Cfengine (1) è uno strano sistema di amministrazione di elaboratori Unix, la cui importanza si apprende solo con il tempo e con l'utilizzo. Il suo scopo è quello di facilitare l'amministrazione di tali sistemi operativi, soprattutto quando si dispone di un gruppo eterogeneo di questi su diversi elaboratori. Questi capitoli dedicati a Cfengine non pretendono di esaurire l'argomento, cercando piuttosto di semplificare il suo apprendimento che poi può essere approfondito leggendo la documentazione originale.
A prima vista, si può intendere Cfengine come l'interprete di un linguaggio molto evoluto. In questo capitolo si introduce l'uso specifico dell'eseguibile cfengine, il cui scopo è interpretare un file di configurazione, ovvero il suo script, agendo di conseguenza.
Per funzionare, l'eseguibile cfengine richiede la presenza di un file di configurazione, che eventualmente può essere trasformato in script, se ciò può essere conveniente. La comprensione, anche elementare, del modo in cui si configura questo programma, è la chiave per capire a cosa può servire in generale Cfengine.
Il file di configurazione di cfengine ha una struttura speciale, in cui però si possono inserire commenti, preceduti dal simbolo #, e righe vuote o bianche. In particolare, a proposito dei commenti, se questi si collocano alla fine di una direttiva, devono essere staccati da questa con uno o più spazi orizzontali.
Le direttive del file di configurazione vanno inserite all'interno di sezioni ed eventualmente all'interno di classi. In altri termini, il file di configurazione si articola in sezioni, che possono contenere direttive o scomporsi in classi, che a loro volta contengono le direttive. Come si intende, la suddivisione in classi è facoltativa, ma si tratta comunque di una caratteristica fondamentale di Cfengine, in quanto consente di selezionare le direttive da prendere in considerazione in base all'appartenenza o meno dell'elaboratore alle classi stesse.
Dal momento che il problema non è semplice da esporre, conviene iniziare subito con un esempio che possa essere verificato senza troppi problemi anche da un utente comune:
|
Se questo file si chiama cfengine.conf
e si trova nella directory corrente, qualunque essa sia, se non è stata impostata la variabile di ambiente CFINPUTS, si può avviare l'interpretazione di tale file semplicemente avviando l'eseguibile cfengine:
$
cfengine
[Invio]
Quello che si ottiene è soltanto la creazione del collegamento simbolico /var/tmp/altra
che punta in realtà alla directory /tmp/
.
Se il file di configurazione fosse collocato altrove, eventualmente con un'altra denominazione, si potrebbe ottenere lo stesso risultato con il comando seguente, dove il nome del file viene aggiunto nella riga di comando:
$
cfengine -f file_di_configurazione
[Invio]
Infine, per realizzare uno script dalla configurazione, basta inserire all'inizio una riga simile a quella seguente (ammesso che l'eseguibile si trovi effettivamente in /usr/bin/
):
|
In altri termini, lo script completo dell'esempio precedente sarebbe:
|
La variabile di ambiente CFINPUTS serve per definire un percorso di ricerca per il file di configurazione. In generale, se si utilizza l'opzione -f specificando un percorso assoluto, a partire dalla radice (qualcosa che inizia con /), si tratta esattamente di quel file, altrimenti, se è disponibile la variabile CFINPUTS, questa viene preposta al nome del file indicato. Per esempio, il comando
$
cfengine -f prova
[Invio]
fa riferimento precisamente al file di configurazione $CFINPUTS/prova
, ovvero al file ./prova
se la variabile CFINPUTS non è disponibile.
Quando non si indica il file di configurazione, si fa implicitamente riferimento al nome cfengine.conf
. In tal caso si tratta precisamente di $CFINPUTS/cfengine.conf
, ovvero del file ./cfengine.conf
in mancanza della variabile CFINPUTS.
Cfengine è un sistema molto potente, i cui script definiscono operazioni molto complesse con poche direttive. Di fronte a direttive distruttive occorre essere sicuri del risultato che si ottiene effettivamente. Per verificare cosa farebbe Cfengine con la configurazione stabilita, senza eseguire realmente la cosa, si può usare l'opzione -n, abbinata a -v: la prima simula l'esecuzione; la seconda mostra nel dettaglio cosa succede o cosa dovrebbe succedere.
Finché non si è sicuri del proprio script o della propria configurazione, occorre ricordare di fare tutte le prove utilizzando l'opzione -n. |
Realizzando uno script con questo intento, basta modificare la prima riga nel modo seguente:
|
Le direttive del file di configurazione vanno inserite all'interno di sezioni, che a loro volta possono suddividersi in classi. Le sezioni rappresentano dei tipi di azione e i loro nomi sono già stabiliti.
sezione_ovvero_tipo_di_azione: definizione_della_classe:: direttiva_o_azione ... ... |
Negli esempi visti fino a questo punto, sono state mostrate le sezioni control e links. Nella sezione control è stata inserita la direttiva actionsequence, che ha l'aspetto di un assegnamento a una variabile:
|
Le direttive, ovvero le istruzioni che possono apparire all'interno di classi o di sezioni non suddivise in classi, possono occupare una o più righe, senza bisogno di simboli di continuazione e senza bisogno di simboli per la conclusione delle istruzioni stesse.
In questo caso particolare, si tratta di assegnare uno o più nomi, che rappresentano altrettante sezioni, alla sequenza di esecuzione. In pratica, la direttiva dell'esempio stabilisce che deve essere eseguita la sezione links. Se non venisse specificata in questo modo, la sezione links non verrebbe presa in considerazione. Pertanto, la configurazione seguente non produrrebbe alcunché:
|
Il prossimo esempio dovrebbe chiarire definitivamente questo particolare. Si osservi il fatto che si vuole eseguire prima la sezione tidy e poi la sezione links, anche se l'ordine in cui sono mostrate poi le sezioni è inverso.
|
In questo caso, la sezione tidy serve a programmare la cancellazione di file e directory. Per la precisione, la direttiva che si vede cancella tutti i file e le directory a partire da /var/tmp/
, purché la data di accesso sia trascorsa da almeno 30 giorni. Si osservi anche l'opzione recurse=inf, che richiede una ricorsione infinita nelle sottodirectory. In condizioni normali, questa ricorsione non dovrebbe attraversare i collegamenti simbolici, mentre per ottenere tale comportamento occorrerebbe aggiungere l'opzione -l. Pertanto, anche se dovesse esistere già il collegamento simbolico /var/tmp/altra
, che punta a /tmp/
, questa directory non verrebbe scandita se non richiesto espressamente.
Le classi sono la caratteristica fondamentale di Cfengine, perché consentono di distinguere le direttive di una sezione in base a una sottoclassificazione che serve a selezionare un gruppo ristretto di elaboratori. In pratica, consente di indicare direttive differenti in base alla «classificazione» a cui appartengono gli elaboratori presi in considerazione. Si osservi l'esempio seguente:
|
Anche se poco significativo, l'esempio è abbastanza semplice e dovrebbe permettere di comprendere il senso della distinzione in classi. In questo caso, la sezione links si articola in due classi, denominate linux_2.2.15 e linux_2.2.16. Se viene usato questo file in un elaboratore con un sistema GNU/Linux avente un kernel 2.2.15, si ottiene il collegamento simbolico /var/tmp/altra
, mentre con un kernel 2.2.16 si otterrebbero due collegamenti simbolici: /var/tmp/altre
e /var/tmp/altri
. Naturalmente, questa operazione può non avere molto significato in generale, ma l'esempio serve a mostrare la possibilità di indicare direttive diverse in base alla classe a cui appartiene l'elaboratore.
La classe serve principalmente a individuare il sistema operativo (nel caso di GNU/Linux si tratta del nome del kernel), in modo da cambiare azione in funzione delle consuetudini di ogni ambiente. In questo caso, volendo selezionare un sistema GNU/Linux senza specificare la versione del kernel sarebbe stato sufficiente indicare la classe linux. Tuttavia, come si vede nell'esempio, esistono delle classi più dettagliate che permettono di raggiungere anche altre caratteristiche. Per conoscere quali sono le classi valide nell'elaboratore che si utilizza in un certo momento, basta il comando seguente:
$
cfengine -p -v
[Invio]
A titolo di esempio, ecco cosa potrebbe comparire:
|
Le classi disponibili sono quindi quelle elencate nell'insieme Defined Classes. Si può osservare che è accessibile anche una classe con il nome della distribuzione GNU/Linux (in questo caso è Debian), oltre agli indirizzi IP abbinati all'interfaccia di rete.
Le classi non sono necessariamente nomi singoli; possono essere delle espressioni composte da più nomi di classe, uniti tra loro attraverso operatori booleani opportuni. Prima di arrivare a descrivere questo, è bene riassumere le classi più comuni e vedere come si possono definire delle classi nuove. Una classe elementare può essere:
la parola chiave any, che rappresenta tutti gli elaboratori;
il nome del sistema operativo o del kernel, assieme a una serie di varianti che includono altre caratteristiche dell'architettura del sistema;
il nome finale dell'elaboratore (senza il dominio eventuale a cui appartiene);
il nome che identifica una componente del tempo (giorno, ora, minuto, ecc.), come si vede nella tabella u84.10;
il nome di un gruppo di classi definito per comodità dell'utilizzatore;
il nome di una classe libera definito per comodità dell'utilizzatore.
|
Si può definire un gruppo di classi attraverso la sezione classes o groups, in cui le direttive servono per definire delle classi nuove raggruppando più classi preesistenti:
classes:|groups: gruppo_di_classi = ( classe_1 classe_2... ) ... |
Per esempio, la dichiarazione seguente serve a raggruppare in due classi nuove le ore del mattino e le ore della sera, supponendo che ciò possa avere un significato pratico di qualche tipo:
|
Inoltre si possono definire delle classi in base al risultato soddisfacente di un programma o di uno script. In altri termini, se un programma restituisce Vero, questo fatto può essere preso in considerazione come motivo valido per generare una classe. L'esempio seguente crea la classe miashell se è presente il file /bin/bash
oppure il file /bin/zsh
:
|
Si possono dichiarare anche delle classi fittizie, il cui significato si può comprendere solo in un secondo momento. Queste classi fittizie si dichiarano nella sezione control, con la direttiva addclasses:
control: ... addclasses = ( classe_fittizia... ) ... |
L'esempio seguente crea due classi fittizie, denominate bianchi e rossi:
|
Avendo più chiaro in mente cosa possa essere una classe elementare, si può iniziare a descrivere la definizione di espressioni legate alle classi. Le espressioni in questione sono booleane, dal momento che le classi, di per sé, rappresentano degli insiemi di elaboratori. In questo senso, la logica booleana si intende correttamente come la logica degli insiemi. Gli operatori di queste espressioni sono elencati nella tabella u84.14.
|
Per esempio, per indicare una classe complessiva che rappresenta indifferentemente un elaboratore con sistema operativo GNU/Linux o GNU/Hurd, si può usare l'espressione linux|hurd. In pratica, si scrive così:
|
Per indicare una classe che rappresenti tutti gli elaboratori che non abbiano un sistema operativo GNU/Linux, si potrebbe usare l'espressione !linux, ovvero:
|
A questo punto diventa più facile comprendere il senso delle classi fittizie che si possono dichiarare con la direttiva addclasses. Si osservi l'esempio seguente:
|
L'espressione any.primo si avvera solo quando la classe elementare primo è stata dichiarata come nell'esempio; infatti, any è sempre vera. In questo modo, anche se l'esempio non richiederebbe tanta raffinatezza, basterebbe controllare la dichiarazione della direttiva addclasses per abilitare o meno la classe sottostante. In altri termini, è facile modificare un file di configurazione che richiama in più punti la classe fittizia primo, modificando solo una riga di codice nella sezione control.
Il controllo sulla definizione di classi fittizie può avvenire anche al di fuori del file di configurazione attraverso le opzioni -Dclasse_fittizia e -Nclasse_fittizia. Nel primo caso, si ottiene la dichiarazione di una classe fittizia, mentre nel secondo si ottiene l'eliminazione di una classe già dichiarata nel file di configurazione. Per esempio, il comando seguente serve ad annullare l'effetto della dichiarazione della classe fittizia primo, dell'esempio precedente.
$
cfengine -Nprimo -f prova.conf
[Invio]
Cfengine gestisce le variabili di ambiente, oltre ad altre variabili, in modo simile a quanto fanno le shell. Queste variabili vengono espanse usando una delle due notazioni seguenti:
$(nome_variabile) ${nome_variabile} |
Per la precisione, le variabili di Cfengine possono essere state ereditate dall'ambiente, possono essere state definite nella sezione control, oppure possono essere variabili predefinite di Cfengine. L'esempio seguente mostra la dichiarazione della variabile percorso nella sezione control:
|
Si intuisce che potrebbe essere più interessante dichiarare la variabile in questione all'interno di classi diverse, in modo da aggiornare automaticamente il percorso di conseguenza. L'esempio seguente mostra due classi inventate, bianco e nero, che non esistono in realtà:
|
Si può osservare in particolare che la direttiva actionsequence, non appartenendo ad alcuna classe, viene presa sempre in considerazione.
Le variabili predefinite di Cfengine sono tali perché sono gestite automaticamente e servono a rendere disponibili delle informazioni, oppure perché servono a definire delle informazioni specifiche. In altri termini, le prime vanno solo lette, mentre le altre vanno impostate opportunamente se richiesto. La tabella u84.20 mostra le variabili destinate alla sola lettura, mentre la tabella u84.21 mostra le variabili da impostare.
|
Per quanto riguarda la variabile domain, se questa non viene impostata espressamente, occorre considerare che potrebbe trattarsi del dominio che compone il nome dell'elaboratore, ovvero ciò che si legge e si imposta con il comando hostname dei sistemi Unix. In pratica, se il nome dell'elaboratore è stato impostato senza l'aggiunta del dominio di appartenenza, questa variabile restituisce probabilmente la stringa undefined.domain. Lo stesso discorso vale per la variabile fqhost: se non si dispone del dominio finale nel nome restituito da hostname, si ottiene una cosa simile a nome.undefined.domain.
|
In generale, i nomi delle variabili sono distinti anche in base all'uso di maiuscole e minuscole; tuttavia, le variabili predefinite possono essere usate con qualunque combinazione di lettere maiuscole e minuscole. |
Esiste anche un altro gruppo di variabili speciali, in sola lettura, definite per facilitare l'inserimento di caratteri speciali all'interno di stringhe, quando non è possibile fare altrimenti. Queste variabili sono elencate nella tabella u84.22.
|
Le stringhe sono delimitate indifferentemente attraverso apici doppi e singoli, potendo usare anche gli apici singoli inversi. In pratica, si possono usare le forme seguenti:
"stringa" 'stringa' `stringa` |
Il significato è lo stesso e l'espansione delle variabili avviene in tutti i casi nello stesso modo. Disponendo di diversi tipi di delimitatori, è più facile includere questi simboli nelle stringhe stesse. In questo senso va considerato il fatto che non esistono sequenze di escape; al massimo si possono usare le variabili predefinite per la rappresentazione di caratteri particolari.
Le stringhe sono utilizzabili solo in contesti particolari, precisamente la definizione di valori da assegnare a una variabile dichiarata nella sezione control e i comandi di shell nella sezione shellcommands (che non è ancora stata mostrata).
Le variabili possono essere intese come contenenti un elenco di sottostringhe. In questi casi, la loro espansione può richiedere una valutazione ulteriore. Tutto ha inizio dalla variabile interna split, che normalmente contiene il carattere :. In questo senso, si osservi l'esempio seguente:
|
Assegnando alla variabile elenco la stringa primo:secondo:terzo, si ottiene l'indicazione di un elenco di tre sottostringhe: primo, secondo e terzo. A questo punto, la direttiva contenuta nella sezione tidy, si traduce nella cancellazione dei file /var/tmp/primo
, /var/tmp/secondo
e /var/tmp/terzo
. Volendo cambiare il simbolo di separazione delle sottostringhe si agisce nella variabile split, come si vede nell'esempio seguente, che ottiene lo stesso risultato.
|
Naturalmente, si può ottenere l'espansione di variabili del genere solo nei contesti in cui questo può avere significato.
In contesti ben determinati, si possono indicare delle espressioni regolari. Cfengine utilizza le espressioni regolari ERE secondo le convenzioni GNU. Sono disponibili gli operatori riassunti nella tabella u84.25.
|
Le espressioni regolari GNU includono anche le classi di caratteri (nella forma [:nome:], come prescrive lo standard POSIX, mentre mancano i simboli di collazione e le classi di equivalenza..
«a2» 2013.11.11 --- Copyright © Daniele Giacomini -- appunti2@gmail.com http://informaticalibera.net