sabato 18 aprile 2009

I servizi di OpenSolaris (SMF) parte #1

Era da tempo che volevo parlare di questa interessantissima caratteristica di OpenSolaris (ma presente anche in Solaris 10): i Service Management Facility. Grazie agli SMF il sistema è in grado di gestire l'avvio di tutti i servizi al momento del boot, il loro corretto arresto in fase di shutdown ed anche la loro ripartenza in caso di problemi.
Quest'ultima caratteristica rende gli SMF unici ma non è la sola a rappresentare un punto di vantaggio al sistema, infatti nel descrittore dei servizi è possibile definire una serie di dipendenze che legano tra di loro i servizi stessi. Indicheremo ad esempio da quali servizi dipende un nostro servizio, quindi essi precederanno il nostro nella fase di avvio, oppure il nostro servizio non potrà essere avviato se prima non verranno fatte partire le sue dipendenze.

Visto che in questi giorni mi sono cimentato nella realizzazione di un SMF per gestire il Sun Java System Web Server 7 che usiamo qui in azienda vorrei condividere con coi questa esperienza, analizzando passo passo le varie sezioni che costituiscono il servizio.

Il servizio SMF viene descritto tramite un file XML (il manifesto), come punto di partenza potete analizzare e prendere spunto da quelli forniti di base con OpenSolaris, memorizzati in /var/svc/manifest/, ecco come è composto il mio:
<?xml version="1.0"?>
<!DOCTYPE service_bundle
SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type='manifest' name='webserver7'>
<service
name='network/webserver7'
type='service'
version='1'>
<single_instance />

questa prima parte è puramente descrittiva, dove scegliamo un nome per il nostro "manifesto" (io ho scelto webserver7) e il nome che andrà ad identificare in maniera univoca il nostro servizio nel sistema (il nome "parziale" sarà svc:/network/webserver7 a cui successivamente aggiungeremo le varie istanze), questo identificatore univoco lo troverete spesso indicato nella documentazione e nelle man pages con la sigla FMRI (ovvero Fault Management Resource Identifier).
<dependency
name='multi-user-server'
grouping='optional_all'
type='service'
restart_on='none'>
<service_fmri value='svc:/milestone/multi-user-server' />
</dependency>

questa seconda sezione troviamo l'unica dipendenza che ho definito, ovvero che il sistema sia stato avviato in modalità multi-user-server. In realtà è possibile definire meglio le dipendenze, ad esempio avrei potuto mettere due dipendenze di base: il "montaggio" dei dischi locali e la partenza dei servizi di rete, dichiarando inoltre che se quest'ultima venisse riavviata anche il mio servizio doveva essere riavviato.
<exec_method
type='method'
name='start'
exec='/lib/svc/method/webserver %m %i %{webserver/home} %{webserver/user}'
timeout_seconds='60'>
</exec_method>

<exec_method
type='method'
name='restart'
exec='/lib/svc/method/webserver %m %i %{webserver/home} %{webserver/user}'
timeout_seconds='60'>
</exec_method>

<exec_method
type='method'
name='stop'
exec='/lib/svc/method/webserver %m %i %{webserver/home} %{webserver/user}'
timeout_seconds='60'>
</exec_method>

queste tre direttive rapprensentano il cuore del servizio SMF in fase di realizzazione, infatti descrivono come far partire, arrestare o riavviare la nostra applicazione; tutti e tre richiamano lo stesso script di shell (sempre da me sviluppato) passando tre paramenti: il primo (%m) rapprensenta il metodo che si sta eseguendo (start, stop o restart), seguito da %i che rappresenta il nome dell'istanza (che vedremo successivamente) e per concludere due proprietà del servizio webserver/home e webserver/user.
<property_group name="webserver" type="application">
<propval name="home" type="astring" value="/sun/webserver7" override="true"/>
<propval name="user" type="astring" value="webservd" override="true"/>
</property_group>

come anticipato il nostro servizio fa uso di due proprietà passandole allo script di gestione visto prima, grazie ad esse l'utilizzatore potrà personalizzarle secondo la sua specifica installazione (essendo state dichiarate tutte e due con la proprietà ovverride settata su true), comunque vedremo come impostare dei valori differenti successivamente.
<instance name='https-myhost' enabled='false'>
<property_group name="instance" type="application">
<propval name="name" type="astring" value="https-myhost" override="false"/>
</property_group>
</instance>

<instance name='admin-server' enabled='false'>
<property_group name="instance" type="application">
<propval name="name" type="astring" value="admin-server" override="false"/>
</property_group>
</instance>

queste due sezioni praticamente identiche vanno a definire due istanze del nostro servizio, identificandone ognuna con un nome. Grazie a questa caratteristica risulta molto semplice gestire istanze multiple di uno stesso servizio, con la possibilità ad esempio di definire delle proprietà specifiche per ognuna di esse.
  <template>
<common_name>
<loctext xml:lang='C'>WebServer 7</loctext>
</common_name>
</template>
</service>
</service_bundle>

infine indichiamo una breve descrizione del servizio, qui potremmo inserire anche una serie di link o riferimenti a specifiche man page utili per gestire/utilizzare l'applicazione gestita dal servizio.

Conclusa la fase di creazione del nostro file non resta altro da fare che controllare la sua correttezza con il comando:
# svccfg validate webserver.xml

in caso di presenza di errori ci verranno segnalate le linee non corrette, in caso contrario possiamo passare alla fase di importazione del nostro manifesto con il comando:
# svccfg import webserver.xml

quindi controlliamo la presenza nella lista dei servizi del nostro appena creato:
# svcs webserver7
STATE          STIME    FMRI
disabled       Apr_16   svc:/network/webserver7:admin-server
disabled       Apr_16   svc:/network/webserver7:https-myhost

come vedete tutte e due le istanze sono disabilitate in quanto tutte e due, al momento della loro dichiarazione, avevano l'attributo enabled='false', prima di abilitarle controlliamo le loro proprietà con il comando:
# svcprop webserver7:admin-server
instance/name astring admin-server
general/enabled boolean false
general/single_instance boolean true
webserver/home astring /export/home/www/webserver7
webserver/user astring www
multi-user-server/entities fmri svc:/milestone/multi-user-server
multi-user-server/grouping astring optional_all
multi-user-server/restart_on astring none
multi-user-server/type astring service
startd/duration astring transient
start/exec astring /lib/svc/method/webserver\ %m\ %i\ %{webserver/home}\ %{webserver/user}
start/timeout_seconds count 60
start/type astring method
restart/exec astring /lib/svc/method/webserver\ %m\ %i\ %{webserver/home}\ %{webserver/user}
restart/timeout_seconds count 60
restart/type astring method
stop/exec astring /lib/svc/method/webserver\ %m\ %i\ %{webserver/home}\ %{webserver/user}
stop/timeout_seconds count 60
stop/type astring method
tm_common_name/C ustring WebServer\ 7
restarter/start_pid count 11703
restarter/start_method_timestamp time 1239890611.150876000
restarter/start_method_waitstatus integer 0
restarter/logfile astring /var/svc/log/network-webserver7:admin-server.log
restarter/transient_contract count
restarter/auxiliary_state astring none
restarter/next_state astring none
restarter/state astring disabled
restarter/state_timestamp time 1239890652.156404000
restarter_actions/refresh integer
general_ovr/enabled boolean false

noterete che sono presenti anche numerose altre proprietà di default dei servizi (come ad esempio il file di log utilizzato per memorizzare i messaggi generati durante le operazioni di start/stop), in più ho provveduto a personalizzare le due proprietà webserver/home e webserver/user per rispondere alle specifiche della mia installazione, per fare questo basta digitare:
# svccfg -s webserver7 setprop webserver/home=/export/home/www/webserver7
# svccfg -s webserver7 setprop webserver/user=luca

noterete che ho settato le proprietà omettendo volontatiamente la parte di istanza, essendo queste proprietà definite a livello di servizio (quindi hanno valore per tutte le istanze del webserver), a questo punto possiamo far partire la nostra istanza (e rimarrà attiva fino al prossimo reboot):
# svcadm enable -t webserver7:admin-server

mentre per per farla ripartire anche in caso di reboot della macchina basterà digitare:
# svcadm enable webserver7:admin-server

Appuntamento al prossimo post per l'analisi dello script usato dal servizio.