czwartek, 11 września 2008

Symfony krok 5

W pliku /apps/frontend/config/routing.yml można ustawić która strona zostaje wyświetlona jako "index" naszej aplikacji:


homepage:
url: /
param: { module: question, action: list }

wtorek, 9 września 2008

Symfony user managmet and sessions

Sesje użytkowników są przechowywane w symfony w obiekcie User. Możemy pobrać ten obiekt w dowolnym miejscu za pomocą metody getUser();


class mymoduleActions extends sfActions
{
public function executeFirstPage()
{
$nickname = $this->getRequestParameter('nickname');

// Store data in the user session
$this->getUser()->setAttribute('nickname', $nickname);
}

public function executeSecondPage()
{
// Retrieve data from the user session with a default value
$nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward');
}
}


Oczywiście w tym obiekcie możemy przechowywać obiekty (niezalecane) i zmienne które będą dostępne pomiędzy wywołaniami stron.

Zmienne możemy ustawiać, pobierać i czyścić


class mymoduleActions extends sfActions
{
public function executeRemoveNickname()
{
$this->getUser()->getAttributeHolder()->remove('nickname');
}

public function executeCleanup()
{
$this->getUser()->getAttributeHolder()->clear();
}
}


Do sesji użytkownika możemy dobrać się także z szablonów



Hello, getAttribute('nickname') ?>




Jeśli potrzebujemy przechować informację tylko do kolejnego wywołania, np potwierdzenie zapisania/usunięcia/edycji danych możemy skorzystać z metody setFlash i getFlash. Dane zapisane za pomocą tych metod zostaną usunięte przy kolejnym wywołaniu.
np.

$this->setFlash('attrib', $value);
$value = $this->getFlash('attrib');

// i w szablonach

has('attrib')): ?>
get('attrib') ?>



Ciasteczka

W symfony możemy decydować gdzie zapisane są ciasteczka. Określamy to w pliku apps/myapp/config/factories.yml


all:
storage:
class: sfSessionStorage
param:
session_name: my_cookie_name


Po stronie aplikacji sesje są przechowywane domyślnie w plikach. Możemy to zmienić w pliku apps/myapp/config/factories.yml


all:
storage:
class: sfMySQLSessionStorage
param:
db_table: SESSION_TABLE_NAME # Name of the table storing the sessions
database: DATABASE_CONNECTION # Name of the database connection to use


Czas przechowywania sesji można ustawić w pliku apps/myapp/config/settings.yml


default:
.settings:
timeout: 1800 # Session lifetime in seconds

Symfony krok 4

W niektórych przypadkach potrzebujemy tylko wysłać nagłówek:


public function executeRefresh()
{
$output = '<"title","My basic letter"],["name","Mr Brown">';
$this->getResponse()->setHttpHeader("X-JSON", '('.$output.')');

return sfView::HEADER_ONLY;
}


lub wybrać szablon o dowolnej nazwie


$this->setTemplate('myCustomTemplate');


Przydatnymi metodami są także forward, redirect i forward404 (nie można znaleźć strony. Dzięki nim możemy przejść do innej akcji w innym module lub załadować dowolną stronę.


$this->forward('otherModule', 'index');

$this->redirect('otherModule/index');
$this->redirect('http://www.google.com/');

/**
* szablon do strony 404 znajduje się w $sf_symfony_data_dir/modules/default/
* możemy także nadpisać akcje error404 w dowolnym module, oraz dołączyć do modułu
*/
forward404();
/**
* istnieje tez kilka funkcji pomocniczych które ułatwią nam pracę
*/
forwardIf();
forwardUnless();
forward404If();
forward404Unless();
redirectIf();
redirectUnless();

$this->forward404If(!$article);


Jeśli mamy czynność którą musimy powtórzyć przed / po wykonaniu każdej akcji możemy utworzyć metody preExecute() i postExecute()

public function preExecute()
{
// The code inserted here is executed at the beginning of each action call
...
}

public function executeIndex()
{
...
}

public function executeList()
{
...
$this->myCustomMethod(); // Methods of the action class are accessible
}

public function postExecute()
{
// The code inserted here is executed at the end of each action call
...
}


Funkcje pomocne przy wykonywaniu akcji:


Name Function Sample Output
Request Information
getMethod() Request method Returns sfRequest::GET or sfRequest::POST constants
getMethodName() Request method name 'POST'
getHttpHeader('Server') Value of a given HTTP header 'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'
getCookie('foo') Value of a named cookie 'bar'
isXmlHttpRequest()* Is it an Ajax request? true
isSecure() Is it an SSL request? true
Request Parameters
hasParameter('foo') Is a parameter present in the request? true
getParameter('foo') Value of a named parameter 'bar'
getParameterHolder()->getAll() Array of all request parameters
URI-Related Information
getUri() Full URI 'http://localhost/myapp_dev.php/mymodule/myaction'
getPathInfo() Path info '/mymodule/myaction'
getReferer()** Referrer 'http://localhost/myapp_dev.php/'
getHost() Host name 'localhost'
getScriptName() Front controller path and name 'myapp_dev.php'
Client Browser Information
getLanguages() Array of accepted languages Array( [0] => fr [1] => fr_FR [2] => en_US [3] => en )
getCharsets() Array of accepted charsets Array( [0] => ISO-8859-1 [1] => UTF-8 [2] => * )
getAcceptableContentTypes() Array of accepted content types Array( [0] => text/xml [1] => text/html



Przykłady wykorzystania:

pobieranie parametrów:

class mymoduleActions extends sfActions
{
public function executeIndex()
{
$hasFoo = $this->getRequest()->hasParameter('foo');
$hasFoo = $this->hasRequestParameter('foo'); // Shorter version
$foo = $this->getRequest()->getParameter('foo');
$foo = $this->getRequestParameter('foo'); // Shorter version
}
}


upload plików:


class mymoduleActions extends sfActions
{
public function executeUpload()
{
if ($this->getRequest()->hasFiles())
{
foreach ($this->getRequest()->getFileNames() as $uploadedFile)
{
$fileName = $this->getRequest()->getFileName($uploadedFile);
$fileSize = $this->getRequest()->getFileSize($uploadedFile);
$fileType = $this->getRequest()->getFileType($uploadedFile);
$fileError = $this->getRequest()->hasFileError($uploadedFile);
$uploadDir = sfConfig::get('sf_upload_dir');
$this->getRequest()->moveFile($uploadedFile, $uploadDir.'/'.$fileName);
}
}
}
}

niedziela, 7 września 2008

Symfony krok 3

Uruchomienie kodu symfony z lini poleceń:



define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/..'));
define('SF_APP', 'myapp');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG', false);

require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');

// add code here


Front controller to coś co przyjmuje zapytania i interpretuje je. Można dodać nowy kontroler, ale mnie interesują tylko dwa kontrolery, które już są zdefiniowane. Mianowicie:


http://localhost/index.php/mymodule/index
http://localhost/mymodule/index

i

http://localhost/myapp_dev.php/mymodule/index


Nazewnictwo klas akcji

Nazwa klasy musi zaczynać się od nazwy modułu z małej litery i kończyć się na Actions. Klasa musi być umieszczona w katalogu pps/myapp/modules/mymodule/actions/ a plik musi nazywać się actions.class.php

Każda metoda przynosząca nowe akcje musi zaczynać się od execute, a cała klasa musi dziedziczyć po sfActions.


class mymoduleActions extends sfActions
{
public function executeIndex()
{

}
}


Nazwa wywołania url jest wzięta z nazwy funkcji w ten sposób że jeśli utworzymy funkcję executeList poprawnym url dla tej akcji jest

http://localhost/myapp_dev.php/mymodule/list
lub
http://localhost/mymodule/list

Możemy również tworzyć całe klasy dla poszczególnych akcji. Wtedy musimy dziedziczyć z klasy sfAction i tworzyć klasy o nazwie NazwaAkcjiAction.

np.
myapp/modules/mymodule/actions/indexAction.class.php


class indexAction extends sfAction
{
public function execute()
{
...
}
}


myapp/modules/mymodule/actions/listAction.class.php


class listAction extends sfAction
{
public function execute()
{
...
}
}


Pobieranie informacji o wywołaniu:


class mymoduleActions extends sfActions
{
public function executeIndex()
{
// Retrieving request parameters
$password = $this->getRequestParameter('password');

// Retrieving controller information
$moduleName = $this->getModuleName();
$actionName = $this->getActionName();

// Retrieving framework core objects
$request = $this->getRequest();
$userSession = $this->getUser();
$response = $this->getResponse();
$controller = $this->getController();
$context = $this->getContext();

// Setting action variables to pass information to the template
$this->setVar('foo', 'bar');
$this->foo = 'bar'; // Shorter version

}
}


Templates

Jeśli akcję zakończymy return sfView::SUCCESS;, lub jeśli nie podamy return do wywołania zostanie dołączony szablon actionNameSuccess.php. Możemy także zwrócić sfView::ERROR wtedy zostanie dołączony szablon actionNameError.php, ogólnie symfony szuka w szablonach pliku zwróconego prze return 'MyResult' + prefix nazwa akcji.

Można też:


return sfView::NONE;


i


$this->getResponse()->setContent("Hello, World!");

return sfView::NONE;


lub


return $this->renderText("Hello, World!");

sobota, 6 września 2008

Symfony krok 2

Definicja bazy danych jest przechowywana w pliku config/schema.yml lub config/schema.xml.
Przykładowy plik xml może wyglądać następująco:


<?xml version="1.0" encoding="UTF-8"?>
<database name="propel" defaultIdMethod="native" noxsd="true">
<table name="ask_question" phpName="Question">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="title" type="longvarchar" />
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
<column name="updated_at" type="timestamp" />
</table>

<table name="ask_answer" phpName="Answer">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="question_id" type="integer" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
</table>

<table name="ask_user" phpName="User">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="nickname" type="varchar" size="50" />
<column name="first_name" type="varchar" size="100" />
<column name="last_name" type="varchar" size="100" />
<column name="created_at" type="timestamp" />
</table>

<table name="ask_interest" phpName="Interest">
<column name="question_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="created_at" type="timestamp" />
</table>

<table name="ask_relevancy" phpName="Relevancy">
<column name="answer_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_answer">
<reference local="answer_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="score" type="integer" />
<column name="created_at" type="timestamp" />
</table>

</database>


Jeśli chcemy wygenerować plik konfiguracyjny z istniejącej bazy danych wystarczy w pliku config/propel.ini
wyedytować linie:

propel.database.createUrl = mysql://marian@localhost/
propel.database.url = mysql://marian@localhost/askeet

i wydać komendę:

$ symfony propel-build-schema

Dla mnie najprzyjemniejszym narzędziem go graficznego tworzenia baz danych jest wtyczka Clay do Eclipse.
Aby wygenerować klasy reprezentujące tabele w bazie danych (na podstawie pliku xml lub yml) wystarczy wydać polecenie:

$ symfony propel-build-model

Aby symfony mogło połączyć się do bazy danych trzeba wyedytować plik config/database.yml (nie mylić z konfiguracją pluginu propel):

all:
propel:
class: sfPropelDatabase
param:
dsn: mysql://marian@localhost/askeet

! Ważna uwaga: w plikach yml trzeba używać spacji zamiast tabulatorów.

Jeśli nie mamy gotowej bazy danych możemy wygenerować skrytp sql z konfiguracji schema.xml lub schema.yml:

$ symfony propel-build-sql
$ mysql -u youruser -p askeet <> lub
$ symfony propel-insert-sql

Kolejnym krokiem jest wygenerowanie formularzy do operacji CRUD (miło szybko zobaczyć coś działającego):

// ciekawe że trzeba wykonać ten krok w symfony 1.1 (w askeet tutorial ten krok jest pominięty gdyż tutorial traktuje o symfony 1.0)
$ symfony propel:build-forms

// no i właściwy proces generowania
$ symfony propel-generate-crud frontend question Question

czwartek, 4 września 2008

System ratownictwa PCK - możliwości usprawnień

Od dłuższego czasu część mojego wolnego czasu poświęcam na wolontariat w grupie ratownictwa PCK w Opolu. Szkolenia dotyczące klęsk żywiołowych, jak i rozmowy z ratownikami dłużej pracującymi w tej formacji przyniosły mi kilka pytań co do możliwości usprawnienia pracy ratowników przy pomocy nowoczesnej technologi.

Jak wygląda akcja ratownicza w obecnym systemie.
W wypadku niespodziewanego kataklizmu czy wypadku zaczyna się od zebrania jak największej ilości informacji o zdarzeniu (jego miejscu, ilości poszkodowanych, warunkach panujących na miejscu zdarzenia itp).
Począwszy od tego miejsca można by wprowadzić usprawnienia które przyśpieszyły by ten proces.
Najważniejsza według mnie jest łączność pomiędzy ratownikami (tymi zbierającymi informacje jak i niosącymi doraźną pomoc poszkodowanym) a kierownictwem akcji ratowniczej. Owszem takowa już istnieje (łączność radiowa na odpowiednio przydzielonych do tego kanałach radiowych) lecz polega ona tylko na głosowym informowaniu kierownictwa. Według mnie powinna istnieć możliwość przesyłania do kierownictwa informacji o położeniu które ciężko opisać słownie.

W terenie gdzie wynikła jakaś katastrofa (np. rozległych budynkach, czy całych miejscowościach) gdzie potrzebna jest pomoc na większym obszarze robione są mapki z rozłożeniem poszkodowanych, obiektów, miejsc niebezpiecznych itp. Problem polega na tym że informacje przynoszone do "centrum dowodzenia" nie są najdokładniejsze. No bo nie każdy jest w stanie prawidłowo określić odległości i w dodatku w czytelny sposób nanieść je na kartkę papieru. Dlaczego nie wykorzystać do tego celu technologi GPS?

Przypuśćmy że w każdej grupie ratowniczej wysyłanej w teren znalazła by się osoba posiadająca odbiornik GPS, mogący w dowolnym momencie połączyć się do "centrum dowodzenia" i wysłać informacje o bieżącej lokalizacji ratowników. Idąc o krok dalej powinna istnieć możliwość zaznaczenia przez ratownika lokalizacji budynków, poszkodowanych i niebezpiecznych miejsc przy pomocy GPS i przesłania ich do kierownictwa akcji. Dane od różnych grup mogły by być połączone w centralnym komputerze i wyświetlone kierującemu akcją. Dało by mu to przybliżony obraz sytuacji i możliwość podjęcia decyzji o kierunku dalszych działań (np. gdzie wysłać więcej ratowników, ile przygotować środków potrzebnych do kontynuacji akcji itp).

Wiedza, marzenia, pomysły

To niesamowite ile pomysłów chodzi mi po głowie.
Część przychodzi sama z siebie, gdy odpoczywam, słucham muzyki, czy pracuje.
Inne wpadają gdy rozwiązuję jakiś problem czy rozważam usprawnienie jakiejś pracy.
Wszystko to kłębi się w głowie i dojrzewa, część zapisuję, rozważam i szukam sposobów realizacji. Szkoda że nie mam wystarczającej wiedzy w niektórych dziedzinach aby zacząć pracę nad ich realizacją. Szukamy wyjścia - może kolejne studia, np. automatyka i robotyka. A może wystarczy przeczytać kilka książek o rozważanych problemach, lub zostać słuchaczem na interesujących wykładach?

Na pewno zacznę publikować "owoce" moich przemyśleń. A nuż ktoś podpowie mi jak najlepiej zabrać się do ich realizacji.

Ostatnio spotkałem się z opinią że pracujemy i tworzymy po to aby osiągać z tego wymierne zyski. W moim przypadku chyba nie do końca o to chodzi. Samo tworzenie jest dla mnie ogromną frajdą.

Więcej usystematyzowanej wiedzy - to jest rozwiązanie.

środa, 3 września 2008

Nazwa dla nowego projektu...

niby nic trudnego, wystarczy przegrepować słownik (np z kurnikowego słownika) wyrażeniem regularnym

może tak:


marian@miranda:~/programming/bazy> grep -E '^s.*p.*j.*r.*;' thesaurus-1.5.utf8.txt
spisek;tajne porozumienie;zmowa


hmmm, raczej nie o to mi chodziło - chyba pozostanę przy dłuższej nazwie :)

wtorek, 2 września 2008

symfony i SuSe

Instalacja.

Najprościej skorzystać z PEAR (jest dostępny w repozytorium yast)
Następnie wystarczy wydać komendę:

> pear channel-discover pear.symfony-project.com
> pear remote-list -c symfony
> pear install symfony/symfony

Tworzenie projektu:

> mkdir myproject
> cd myproject
> symfony generate:app frontend
> symfony generate:project myproject
> symfony generate:module frontend content

Ponadto aby testować stronę z dowolnym url musimy dodać wpis do virtualnych hostów.
W suse wystarczy dodać plik z rozszerzeniem .conf do katalogu /etc/apache2/vhosts.d


<virtualhost>
ServerName przykladowa-aplikacja.pl
DocumentRoot "/home/marian/przykladowa-aplikacja/web"
DirectoryIndex index.php
Alias /sf "/usr/share/php5/PEAR/data/symfony/web/sf"
<directory>
AllowOverride All
Allow from All
</directory>
<directory>
AllowOverride All
Allow from All
</directory>
</virtualhost>


Musimy również dodać do /etc/hosts

127.0.0.1 przykladowa-aplikacja.pl

poniedziałek, 18 sierpnia 2008

Replikacja mysql

Replikacja pozwala na stworzenie "lustrzanego odbicia" bazy (baz) danych, w czsie rzeczywistym (albo prawie rzeczywistym). Zreplikowaną bazę danych (slave) możemy wykorzystać jako backup (który w każdym momencie możemy wykorzystać jako podstawową bazę danych). Konfiguracja serwera slave polega na wpisaniu do pliku konfiguracyjnego hostu na którym znajduje się baza mysql działająca jako master, loginu i hasła użytkownika który ma prawo do replikacji oraz (opcjonalnie) bazwę bazy danych którą chcemy replikować.

Po stronie serwera mysql działającego jako master, należy założyć konto dla użytkownika który będzie wykorzystywany jako slave.

CREATE USER slave_user;
GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'slave_host' IDENTIFIED BY 'slave_pass';
FLUSH PRIVILEGES;

W obu serwerach trzeba róznież ustawić server-id, który jest unikalnym identyfikatorem serwera w naszej sieci (najlepiej ustawić 1 dla master i kolejn0 (2,3...n) dla serwerów slave).

Pozostała konfiguracja polega na ustawieniu punktu od którego slave ma czytać zmiany w bazie danych master.

/* pobranie informacji o nazwie pliku logów oraz ostatniej pozycji w tym pliku - uruchamiamy na bazie master */

SHOW MASTER STATUS;

/* ustawiamy z którego pliku logów, oraz której pozycji ma czytać slave */
/* jeśli binlog jest pusty to trzeba podać slave-owi pusty string '' jako plik i pozycje */
CHANGE MASTER TO MASTER_HOST='master_host_name', MASTER_USER='slave_user', MASTER_PASSWORD='slave_pass', MASTER_LOG_FILE='recorded_log_file_name', MASTER_LOG_POS=recorded_log_position;

Ostatnią czynnością jest uruchomienie slave-a:

START SLAVE;

Można również startować selektywnie wątki replikacji:

START SLAVE SQL_THREAD;
START SLAVE IO_THREAD;

Jeśli nie jesteśmy pewni czy replikacja działa możemy sprawdzić jej stan na slave-ie za pomocą polecenia:

SHOW SLAVE STATUS\G

\G na końcu poda nam wynik w czytelniejszy sposób niż bez niego.

Aby zatrzymać działającego slave-a wystarczy wydać polecenie:

STOP SLAVE;

Czasami zachodzi potrzeba uruchomienia serwera mysq, skonfigurowanego jako slave z wyłączeniem opcji replikacji. Możemy to zrobić uruchamiając serwer mysql z opcją "--skip-slave"

mysqld --skip-slave

piątek, 15 sierpnia 2008

Walka ze sprzętem - sun xfire 2200 i karty ethernetowe nvidia



Nasze ostatnie problemy z serwerami (które wymusiły na nas wyjazd do serwerowni w Warszawskim ATM) były związane z usterką kart sieciowych opartych na chipisetach nvidi. Przy zwiększonym ruchu po interfejsie sieciowym wynikłym np. przez zwiększonym ruchu pomiędzy mysql-em a apachem interfejs przestawał odbierać i wysyłać dane, a po pewnym czasie nawet zawieszał system i powodował kernel panic. Obsługa techniczna atm twierdziła oczywiście że jesteśmy pierwszymi klientami którzy raportuja takie problemy z sun xfire 2200 i linuxem (może ze względu że chyba nikt nie miał połączonych ze sobą bezpośrednio 3 serwerów bez switch-a). Problem powtarzał się ze zmienionym kernelem i nowym modułem forcedeth.
W logach przy generowaniu dużego ruchu sieciowego (przez nc lub spc) pojawiał się wpis: "eth0: too many iterations (6) in nv_nic_irq."
Rozwiązaniem problemu okazało się załadowanie modułu forcedeth z opcją max_interrupt_work=25


localhost:/# modprobe forcedeth max_interrupt_work=25

w opisach znalezionych w internecie ta liczba była mniejsza, lecz metodą prób i błędów doszliśmy to 25. Nie wiem jednak czy rozwiązanie to wystarczy na długo i czy sytuacja nie powtórzy się. Najlepszym wyjściem będzie zainstalowanie switcha i użycie pozostałego interfejsu sieciowego opartego na chipisecie broadcom-u - co niebawem uczynimy.

Problemy z amfphp i drupalem


Amfphp w połączeniu z drupalem daje całkiem duże możliwości. Niestety moduł amfphp do drupala czasami sprawia spore kłopoty.
Najczęstszym powodem kłopotów z komunikacją flasha z amfphp jest źle ustawiony gateway (dla samego amfphp jest to www.example.com/amfphp/gateway.php , a dla zintegrowanego amfphp z drupalem www.example.com/services/amfphp), lub brak dodania uprawnień do korzystania z services. Niestety w moim przypadku wszystko było skonfigurowane poprawnie a flash nadal nie odbierał danych z serwisu. W analizatorze ruchu sieciowego wireshark pakiety z informacjami dochodziły do systemu a flash ich nie odbierał (lub odbierał a nie potrafił zinterpretować). Aby ułatwić sobie przeglądanie pakietów z wireshark wystarczy wstawić filtr ip.src == ip serwera || ip.dst == ip serwera.

Problemem było to że z jakiegoś powodu drupal do każdej strony dodawał znak nowej lińji, co powodowało że dodawał znak nowej lińji również do xml-a z odpowiedzią wywołania metody amfphp. Można to najlepiej przetestować w pythonie:

#! /usr/bin/env python
import xmlrpclib, math

server = xmlrpclib.Server("http://www.example.pl/xmlrpc.php")
print server.node.load(135730,[])

jeśli ten sktrypt zwróci
xml.parsers.expat.ExpatError: xml declaration not at start of external entity: line 2, column 0
to znaczy że mamy doczynienia z uszkodzoną odpowiedzią z serwera.

Oczywiście skrypt ten komunikuje sie z drupalem za pośrednictwej xmlrpc, co wymaga włączenie serwera xmlrpc, oraz serwisu node_service w modułach.

Rozwiązanie problemu znaku nowej lińji znalazełem na stronach http://drupal.org/node/18265 i http://drupal.org/node/18265.

wystarczy dodać do includes/bootstrap.inc

function drupal_page_header() {
$clean = ob_get_contents();
ob_end_clean();

Wszystko było by fajnie gdybym wiedział dlaczego drupal zachowuje się w ten sposób. Dziwne bo ta sama instalacja drupala w innej lokalizacji (na serwerze testowym) działa w najlepsze, a na serwerze produkcyjnym znalazłem w/w błąd.

Funkcja drupal_page_header() w pliku includes/bootstrap.inc służy do generowania nagłówków.

string ob_get_contents ( void )

Gets the contents of the output buffer without clearing it.

bool ob_end_clean ( void )

This function discards the contents of the topmost output buffer and turns off this output buffering. If you want to further process the buffer's contents you have to call ob_get_contents() before ob_end_clean() as the buffer contents are discarded when ob_end_clean() is called.

niedziela, 10 sierpnia 2008

ssh-agent - czyli bezpieczne logowanie prawie bez hasła

Każdy kto pracował na zdalnych maszynach wie że ciągłe wpisywanie długich i skomplikowanych haseł nie należy do przyjemności. Jak chociaż trochę ułatwić sobie życie nie denerwując Andrzeja?
Na stronie http://susewiki.org/index.php?title=Using_SSH-agent znalazłem opis który nie pomógł mi bardzo przy procedurze tworzenia i dodawania kluczy ale zwrócił uwagę na prawa dostępu do katalogu .ssh i authorized_keys, jeśli katalog nie jest 700 a plik 600 całość po prostu nie zadziała.
miranda$ ssh-keygen -t dsa
.
.
.
miranda$ scp ~/.ssh/id_dsa.pub marian@remote.domain.net

remote$ mkdir ~/.ssh
remote$ chmod 700 ~/.ssh
remote$ cat ~/id_dsa.pub >> ~/.ssh/authorized_keys
remote$ chmod 600 ~/.ssh/authorized_keys
remote$ rm ~/id_dsa.pub

miranda$ ssh-agent

teraz należy dodać zmienne środowiskowe

miranda$ ssh-add

Warszawa Smyczkowa 9, Grochowska ATM, czyli kolejny odcinek walki z serwerami

Trochę daleko ten hotel....




Wyświetl większą mapę

mysql-proxy


Bardzo przydatny program. Generalnie jest to proxy które stoi pomiędzy serwerem mysql (mysqld) a klientem (mysql, php, python). Służy m.in. do analizy kodu sql wysyłanego do serwera, jego zmianie, filtrowaniu (gdy np. napiszemy CREAT zamiast CREATE skrypt lua może wykryć taką sytuację i poprawić kod), oraz do pisania rozmaitych macr.

http://www.oreillynet.com/pub/a/databases/2007/07/12/getting-started-with-mysql-proxy.html

Dzięki niemu możliwy jest load balancing ruchu pomiędzy klientami a nodami clustra mysql lub serwerem master/slave.

Opcje które umożliwią nam uruchomienie load-balancing-u to:

--proxy-read-only-backend-addresses= address:port of the remote slave-server (default: not set)
--proxy-backend-addresses= address:port of the remote backend-servers (default: 127.0.0.1:3306)

Szkoda tylko że nie ma prostego sposobu na załadowanie kilku takich skryptów, bo wraz z uruchomieniem demona mysql-proxy należy podać ścieżkę do skryptu który chcemy załadować.

$ mysql-proxy --proxy-lua-script=first_example.lua -D

Napisałem "prostego" bo znalazłem skrypt lua który umożliwia ładowanie kolejnych skryptów przez klienta mysql ( http://datacharmer.blogspot.com/2007/11/multiple-scripts-in-mysql-proxy.html). Wymieniony skrypt znajduje się także w katalogu /usr/share/doc/packages/mysql-proxy/examples/ ( SUSE 10.3). Czy nie powinno być opcji w uruchomieniu mysql-proxy która pozwoli na załadowanie kilku skryptów przy starcie demona?


Bardzo przydatna według mnie jest opcja wywoływania zewnętrznych programów przy pomocy skryptów lua. Przykładowy sktypt z możliwością uruchomienia dowolnej aplikacji shell-owej znajduje sie na forge.mysql.com (http://forge.mysql.com/tools/tool.php?id=79). W połączeniu z event schedulerem (http://dev.mysql.com/doc/refman/5.1/en/events.html) daje to nowe możliwości, możliwości które prawdopodobnie będę miał okazje wykorzystać.