shell tools – parametric commands with xargs

Sometime is necessary to execute commands several times changing only some parameters.

In most of the cases you can use a for or a while loop:

for FILE in $(ls -1); do echo ${FILE}; done 

This form isn’t particularly robust because of the unmanaged spaces in the file names but anyway it is really common because of its simplicity.

Another option is to use the -exec option of the find command.

find . -type f -exec echo '{}' \;

This form is a really powerful way of looping but can be used only on list of files

In some cases neither of the previous forms can be used. Let see an example: you have to read a file (ei: an apache access log) elaborate the row in some way and execute a command for each row. In a case like this find isn’t an option and the for loop is inconvenient at the best because you have to put much more logic in the variable definition then in the true loop. Moreover if you have a big file to manage you couldn’t have enough memory.

It would be much better to elaborate on line at time: xargs is the tool that permits us to proceed in this way.

Let’s look at an example: suppose I want to loop through a log file and search for the served pages that doesn’t contains a word. I’ll have to read the file, probably select some lines, extract the information required to execute a curl, execute the curl and examine the result.
I can do this in a single line using xargs without involving intermediate files.

cat  20150608_access.log |grep "linuxandcompany" |awk -F"GET " '{print $2}' |awk -F" HTTP/" '{print $1}' | xargs -I {} sh -c 'curl -s "http://www.linuxandcompany.it{}" | grep "pippo" > /dev/null;  [ $? -ne 0 ] && echo "{}"'

The first part is really basic: the cat command is used to read through a log file. Then I use a filter (grep) to select some lines (the ones related to this website).

To extract the searched information (URI) I use the two times the awk command.

At this point I have a pipe with a list of URI. The function of xargs is just to get these values, substitute them in a parametrized command and execute the result.

In this example I have to execute several commands at each iteration. xargs doesn’t permit this but a simply trick will help us: we choose a shell as command and execute a script inside it.

Just to complete the description of the example the curl retrieves the web page from internet, the grep command checks for the searched value, and the result is thrown in the trash. This because I’m interested only in the return value of the grep command.

The return value of the grep is checked and if the word isn’t found the URI is written in the output.

Gestione dei Log con syslog

Una necessità primaria dei sistemi di produzione è la raccolta e la gestione dei log.

Sempre di più i log vengono generati in enorme quantità e quando l’infrastruttura cresce oltre un certo livello diventa fondamentale passare ad un sistema che ne gestisca la centralizzazione.

Su Linux generalmente si usa una qualche versione del demone syslog (su ubuntu ad esempio ci sono tre opzioni dsyslog, rsyslog, syslog-ng) a questo scopo. Questi sistemi si occupano sia della gestione locale dei log sia della loro eventuale centralizzazione e mettono a disposizione strumenti per filtrare le informazioni più importanti o per integrarsi con sistemi di monitoraggio come nagios o con sistemi di reportistica come logstash. La scelta di un file come repository finale dei dati non è obbligata ma questi sistemi permettono di passare i dati a molti altri tipi di repository.

Farò riferimento per i dettagli a syslog-ng.

Conviene immaginare questi sistemi come a gestori di flussi di informazioni nei quali ci sono dei punti di entrata (un server apache ad esempio), delle regole di elaborazione più o meno complesse e dei punti di uscita (tipicamente un file, altri server syslog nei sistemi centralizzati, database etc..). La configurazione rispecchia questa prospettiva.

Una volta definiti delle opzioni generali si definisco le sorgenti dei log. Questa ad esempio raccoglie le informazioni che tipicamente finiscono sui log standard di sistema.

source s_src { unix-dgram("/dev/log"); internal();
             file("/proc/kmsg" program_override("kernel"));
};

Altre tipiche sorgenti di dati saranno delle porte in ascolto su protocolli udp o tcp

source s_udp_apache {
        udp(ip(0.0.0.0) port(8515));
};
source s_tcp_apache {
        tcp(ip(0.0.0.0) port(8515));
};

utili sia per la costruzione di sistemi gerarchici che permettano la centralizzazione dei log sia come sistema semplice per separare su molti canali i log.
Tra le possibili sorgenti ci sono anche i file

source s_apache_access {file("/var/log/apache2/access.log" log_fetch_limit(100) log_iw_size(1000)); };

utili ad esempio per raccogliere i log anche di sistemi che hanno solo questa.

Il secondo elemento chiave della configurazione sono le destinazioni dei flussi di log. Anche in questo caso è possibile inviare i log su di un file locale, su un canale tcp o udp nel caso di sistemi gerarchici per la gestione di log.

destination d_auth { file("/var/log/auth.log"); };
destination d_udp_system { udp(vip-syslog.de.prod port(8514));};
destination d_tcp_system { tcp(vip-syslog.de.prod port(8514));};

Ci sono molte possibili altre destinazioni tra cui stream unix, o mongodb.

Il terzo elemento della configurazione è filter. Come il nome lascia intuire questo elemento serve a definire delle regole per selezionare un sottoinsieme dellerighe in arrivo. L’elemento tradizionale per suddividere i log è la selezione della facility, ma filter permette di agire su svariati elementi tra cui l’host di invio, il livello del messggio (notice, warn, error…) appositi tag assegnati alla riga di log o anche regular expression sul contenuto.

filter f_err { level(err); };
filter f_mail { facility(mail) and not filter(f_debug); };

I vari elementi della configurazione devono essere poi combinati con un’ultimo elemento della configurazione:

log { source(s_src); filter(f_auth); destination(d_auth); };
log { source(s_apache_extranet); destination(d_apache_extranet); flags(flow_control); };

La combinazione di questi elementi permette una grande flessibilità nella gestione del log. Tornerò su questo tema per approfondirne il filtraggio ed altre opzioni.

Cambiare sfondo periodicamente con Ubuntu Unity

Stranamente Ubuntu Unity non prevede un meccanismo per cambiare lo sfondo periodicamente selezionando delle immagini da una cartella come invece è possibile fare su praticamente ogni altro desktop environment.

A questo è comunque possibile ovviare con facilità sfruttando il comando
gsettings
che permette di gestire da linea di comando molte delle impostazioni dell’interfaccia grafica.

Il comando per cambaire lo sfondo è molto semplice

gsettings set org.gnome.desktop.background picture-uri "file://<path to file>"

e potete testarlo con un’immagine a vostra scelta.

Il secondo obiettivo che ci eravamo posti è quello scegliere un’immagine casualmente un’immagine da una cartella. Si può fare in modo molto semplice con gli strumenti della shell unix. In primo luogo usiamo find per farci dare l’elenco dei file con i path assoluti, utilizziamo poi sort per mischiare casualmente l’eleco; sort, infatti, con l’opzione
-R restituisce un’ordinamento casuale. Usiamo infine
head
per selezionare il primo elemento della lista.

find <absolute path to image directory> |sort -R |head -1

Per combinare le due cose utiliziamo le variabili della shell

export SFONDO="$(find <absolute path to image directory>|sort -R |head -1)"; gsettings set org.gnome.desktop.background picture-uri \"file://${SFONDO}\"

L’ultimo cosa che ci rimane da fare è rendere periodica questa operazione. Per farlo useremo ovviamente
crontab
ma il comando così comè non funzionerebbe perché gsettings, per funzionare correttamente utilizza le variabili di ambiente e crontab crea un ambiente “anomalo” in cui molte di queste variabili o non sono definite o hanno valori specifici.

Per il nostro script ci serve il valore di
DBUS_SESSION_BUS_ADDRESS
. Possiamo vederne il valore con un semplice echo

echo $DBUS_SESSION_BUS_ADDRESS

Questa variabile permette a gsettings di sapere su quale ambiente grafico andare ad agire e deve essere letta “live” cambiando ad ogni sessione grafica. Per recuperarla bisogna fare un po’ di passi. In primo luogo
pgrep
ci permette di recuperare il PID del processo. Con questa operazione possiamo andare a recuperare il valore della variabile dalle informazioni in proc

export PID=$(pgrep gnome-session)
grep -z DBUS_SESSION_BUS_ADDRESS /proc/${PID}/environ

infine awk ci permette di separare il valore dal nome della variabile

grep -z DBUS_SESSION_BUS_ADDRESS /proc/${PID}/environ|awk -F= '{print $2"="$3}'

Non ci rimane che eseguire
crontab -e
ed inserire una riga con la schedulazione voluta ed i comandi

*/10 * * * *  export PID=$(pgrep gnome-session);export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/${PID}/environ|awk -F= '{print $2"="$3}') ;export SFONDO="$(find <absolute path to image directory> |sort -R |head -1)"; gsettings set org.gnome.desktop.background picture-uri \"file://${SFONDO}\"

L’*/10 sta ad indicare che il comando deve essere eseguito ogni 10 minuti.

glusterfs – introduzione

GlusterFS è un filesystem distribuito.

Supera alcuni dei problemi storici di filesystem di rete come NFS e CIFS. In alcuni casi può essere utilizzato come alternativa ad un NAS; un NFS esportato da un NAS può infatti essere considerato affidabile ma se è un server ad esportare l’NFS, come a volte ci si trova a dover fare per ragioni economiche o perché ci si trova su cloud, difficilmente si potrà fare un grosso affidamento su questo sistema. Non è infatti possibile costruire un completamente ridondato con NFS. GlusterFS invece porta con se nativamente questa funzionalità.

GlusterFs comunque, ha tutta una serie di caratteristiche che lo rendono interessante

  1. E’ nativamente ridondabile ed possibile avere dei mount in HA: non più macchine congelate per un mount nfs che non risponde
  2. I suoi volumi possono essere fatti crescere o possono essere ristretti a caldo sfruttando la possibilità di distribuire i dati su più server o, meglio, su più brick
  3. I dati possono essere ridondati
  4. Si può aumentare la velocità di lettura replicando i dati su più server o quelle di lettura e scrittura sfruttando la funzionalità di striping
  5. Esporta anche con il protocollo NFSv3 per retrocompatibilità, ma è chiaramente meglio sfruttare il client nativo
  6. Ha una interessante funzionalità di replica geografica che permette di mantenere una copia asincrona sfruttabile per disaster recovery, backup o altro

Dopo aver definito dei pool di server affidabili, GlusterFS permette di aggiungere a dei volumi virtuali parti del filesystem dei server coinvolti denominate Brick. Non si lavora quindi con partizioni ma con directory all’interno di partizioni montate sul filesystem. Il tutto avviene in user space.

Il sito ufficiale è http://www.gluster.org/. Da qui si può scaricare l’ultima versione o in alternativa si possono utilizzare i pacchetti presenti in buona parte delle distribuzioni. Attualmente l’ultima versione stabile è la 3.5.2 e si sta lavorando al rilascio della versione 3.6.

Se si vuole una versione aggiornata sul sito ufficiale

http://download.gluster.org/pub/gluster/glusterfs/

è possibile scaricarla.
In alternativa alcune distribuzioni permettono di integrare la procedura nel proprio sistema di pacchetti. Ad esempio su ubuntu

sudo add-apt-repository ppa:semiosis/ubuntu-glusterfs-3.5
sudo aptitude update
sudo aptitude install glusterfs-server glusterfs-client

Il comando base per la gestione di glusterfs è gluster. Questo può essere eseguito direttamente con tutti i parametri come in

# gluster pool list

o, senza parametri, per accedere ad una consolle da cui gestire il cluster gluster:

# gluster
gluster> pool list

In entrambi i casi si otterrà un risultato analogo a

 
UUID					Hostname	State
dee79aaa-6042-4bc6-a17a-27e4944db3c9	localhost	Connected 

che ci informa che il solo localhost è nel pool di server che possono essere coinvolti nella formazione dei volumi.

Si consigli di l’esecuzione di

gluster help

per avere un’idea di quello che è possibile fare.

In un prossimo articolo vedremo alcuni esempi di configurazione.

Howto – virtualizzazione su Linux con KVM

La Virtualizzazione è un tool che torna sicuramente utile se si vogliono testare nuovi daemon e non si hanno a disposizione dei server da utilizzare come laboratorio. Torna utile sia che si vogliano testare altri sistemi operativi dato, che da la possibiltà di farlo senza dedicare a questi dell’hardware e senza le complicazioni del dual boot, sia che si vogliano testare dei demoni visto che permette di farlo in un ambiente isolato.

Virtualizzazione che è oltretutto interessante in se in quanto è anche uno strumento molto utilizzato in ambienti di produzione per quel che permette in termini di scalabilità, affidabilità e ottimizzazione.

Vedremo in questo post i passi necessari per predisporre il proprio PC ad ospitare dei server virtuali e gli step necessari per una prima istallazione.

La virtualizzazione è in linea di principio possibile su qualsiasi processore general-pourpose ma in pratica se si vogliono prestazioni decenti bisogna accertarsi che la propria CPU abbia le relative funzionalità hardware. Per farlo verifichiamo se sui flags della CPU è riportato svm o vmx.

grep svm /proc/cpuinfo
grep vmx /proc/cpuinfo

Andiamo ad installare i pacchetti necessri:

aptitude install qemu-kvm libvirt-bin virtinst

inoltre sarebbe utile il pacchetto virt-manager per avere un’interfaccia grafica di gestione delle macchine virtuali.

aptitude install virt-manager virsh virt-viewer

virt-manager è un’interfaccia grafica che permette di gestire le macchine virtuali sia per crearle sia per gestirne lo stato di avvio, sia per accedere ad una consolle. Ma di fatto permette una gestione molto basilare delle macchine virtuali.

In alternativa si può utilizzare virsh. Ad esempio

virsh -c qemu:///system list

darà la lista dei server attivi
Ma virsh può essere utilizzato anche come consolle lanciandolo senza parametri

$ virsh 
Benvenuti a virsh, il terminale interattivo per la virtualizzazione.

Digitate:  'help' per un aiuto sui comandi
       'quit' per uscire

virsh # help

Ad esempio per avere la lista delle Virtual Machines configurate

virsh 'list --all'

e per avviarne una

virsh start <nome VM>

ci si potrà poi connettere alla consolle ad esempio con

virt-viewer -c qemu:///system <nome VM>

controllo dei processi – supervisord

Parliamo oggi di supervisord, un sistema di controllo dei processi. Questi sistemi hanno la funzione di garantire che altri processi siano costantemente attivi. Possono essere quindi elementi importanti per garantire l’affidabilità di un servizio soprattutto nei casi in cui il daemon da controllare è sviluppato in casa e da, quindi, molta meno affidabilità di un daemon con migliaia di istallazioni.

Il primo passo è, come sempre, istallare supervisord. Lo possiamo fare dal sito del progetto o utlizzando il sistema di pacchetti della propria distribuzione. Su Debian o Ubuntu:

aptitude install supervisor

Ad istallazione effettuata si avrà il demone supervisord e il tool di controllo supervisorctl. Inoltre il demone può essere configurato per rispondere a chiamate html (di default sulla porta 9001) sia per monitoraggio via browser sia per gestione tramite API xml-rpc.

Per prima cosa esaminiamo la configurazione di default che arriva con il pacchetto Ubuntu:

; supervisor config file

[unix_http_server]
file=/var/run/supervisor.sock   ; (the path to the socket file)
chmod=0700                       ; sockef file mode (default 0700)

[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
childlogdir=/var/log/supervisor            ; ('AUTO' child log dir, default $TEMP)

; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL  for a unix socket

; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.

[include]
files = /etc/supervisor/conf.d/*.conf

Come avviene in moltissimi casi i parametri di configurazione sono raggruppati in paragrafi.

  • [unix_http_server]: contiene i parametri necessari a definire il socket unix per le connessioni a supervisord.
  • [supervisord]: configurazione vera e propria del daemon; file di log, file di lock.
  • [rpcinterface:supervisor]: serve ad abilitare l’interfaccia rpc
  • [supervisorctl]: parametri passati usati al client; ad esempio come connettersi al server se non specificato altrimenti
  • [include]: un elenco di file da includere. Tipicamente uno per ogni tipo di daemon da controllare con le specifiche direttive.

Come prima cosa attiviamo l’interfaccia web abilitando un’utenza ed una password. Aggiungiamo o modifichiamo quindi il paragrafo inet_http_server con i seguenti valori:

[inet_http_server]
port = 127.0.0.1:9001
username = user
password = 123

e riavviamo il daemon

/etc/init.d/supervisord restart

Possiamo ora accedere all’interfaccia web connettendoci in localhost sulla porta 9001 ed inserendo i dati dell’account quando richiesti.

Creiamo  ora uno script che utilizzeremo come daemon di test e configuriamo supervisord per gestirlo.

Editiamo quindi un file denominato test_supervisord.sh  con il nostro editor di riferimento ed inseriamoci il seguente codice:

#!/bin/sh
while [ true ]; do 
	ls -lh /tmp/
	rm /tmp/pippo
	touch /tmp/pippo
	sleep 5
done

cambiamo poi i permessi per renderlo eseguibile

chmod 700 test_supervisord.sh

Creiamo ora la configurazione su supervisord per dirgli di gestire questo daemon. Andremo ad inserire nel file /etc/supervisor/conf.d/test.conf il seguente contenuto

[program:test_supervisord]
command=/tmp/test_supervisord.sh
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/test.err
stdout_logfile=/var/log/supervisor/test.log

dove, in un apposito paragrafo specifichiamo

  • [program:test_supervisord]: il nome del daemon da riportare sui vari report
  • command=/tmp/test_supervisord.sh: il comando da eseguire
  • autostart=true: se deve partire automaticamente all’avvio
  • autorestart=true: se deve essere riavviato in caso di interruzione
  • stderr_logfile=/var/log/supervisor/test.err: file di log in cui viene registrato l’output del daemon
  • stdout_logfile=/var/log/supervisor/test.log file di log in cui vengono riportato lo standard error del daemon

I parametri specificabili sono molti altri. Ad esempio

  • user: permette di specificare l’utente con cui il daemon viene eseguito
  • environment: accetta coppie chiave-valore che vengono trasformate in variabili di ambiente per l’esecuzione del daemon.

Per rendere attiva la nuova configurazione si può procedere con uno tra

kill -HUP "PID"
supervisorctl reread && supervisorctl update

o anche attraverso l’interfaccia web visto che l’abbiamo attivata.

supervisorctl rimane comunque l’interfaccia di eccellenza di questo sistema.

Al suo avvio presenta l’elenco dei demoni configurati ed il loro stato. Ed il comando help mostra cosa può essere fatto all’interno di questa command line.

root@nb:/etc/supervisor/conf.d# supervisorctl 
test_supervisord                 RUNNING    pid 8443, uptime 0:03:40
supervisor> help

default commands (type help ):
=====================================
add    clear  fg        open  quit    remove  restart   start   stop  update 
avail  exit   maintail  pid   reload  reread  shutdown  status  tail  version

supervisor>

stop, start, restart e status serviranno ad esempio a gestire i vari daemon. reread e update li abbiamo già visti in azione. Quit permette di lasciare questa shell.