È giunto il momento di dire a Nagios cosa controllare. Dobbiamo quindi dirgli:
Tutte queste informazioni sono rappresentate per mezzo di oggetti, definiti da un insieme di dichiarazioni "define", racchiuse fra parentesi graffe e contenenti un numero variabile di direttive, una per riga, in forma chiave/valore. Le chiavi sono separate dai valori per mezzo di spazi ed è possibile specificare più valori separandoli con delle virgole; l'indentazione, all'interno delle dichiarazioni, è ammessa.
Ricapitolando, la sintassi della dichiarazione di un oggetto può essere rappresentata così:
define oggetto {
chiave-1 valore-1
chiave-2 valore-2,valore-3,...
[...]
chiave-n valore-n
}
Le definizioni degli oggetti possono essere suddivise in un numero arbitrario di file: basta ricordarsi di elencarli tutti nel file di configurazione principale usando le direttive cfg_file e/o cfg_dir.
La dichiarazione timeperiod consente di specificare, per ogni giorno della settimana, uno o più intervalli di tempo in cui eseguire determinati controlli e/o notificare determinate persone. Gli intervalli di tempo non possono superare la mezzanotte e i giorni esclusi vanno semplicemente saltati.
In questo esempio, tutte le definizioni di timeperiod sono raggruppate in un file chiamato timeperiods.cfg all'interno della directory /var/www/etc/nagios/.
# La seguente definizione di timeperiod comprende le normali ore lavorative. Le
# direttive 'timeperiod_name' e 'alias' sono obbligatorie. Si noti che i giorni
# del weekend sono semplicemente omessi
define timeperiod {
timeperiod_name workhours
alias Work Hours
monday 09:00-18:00
tuesday 09:00-18:00
wednesday 09:00-18:00
thursday 09:00-18:00
friday 09:00-18:00
}
# Il seguente timeperiod comprende tutto il tempo fuori dalle normali ore
# lavorative. Gli intervalli di tempo fra le 18 e le 9 devono essere spezzati
# in due intervalli per non essere a cavallo di mezzanotte
define timeperiod {
timeperiod_name nonworkhours
alias Non-Work Hours
sunday 00:00-24:00
monday 00:00-09:00,18:00-24:00
tuesday 00:00-09:00,18:00-24:00
wednesday 00:00-09:00,18:00-24:00
thursday 00:00-09:00,18:00-24:00
friday 00:00-09:00,18:00-24:00
saturday 00:00-24:00
}
# La maggior parte dei controlli girera' continuamente
define timeperiod {
timeperiod_name always
alias Every Hour Every Day
sunday 00:00-24:00
monday 00:00-24:00
tuesday 00:00-24:00
wednesday 00:00-24:00
thursday 00:00-24:00
friday 00:00-24:00
saturday 00:00-24:00
}
# Il timeperiod perfetto per quando non si vuole essere seccati dalle notifiche
# (ad es. durante i test)
define timeperiod {
timeperiod_name never
alias No Time is a Good Time
}
# Alcune eccezioni alle normali schedulazioni settimanali (vd. la documentazione
# per ulteriori esempi)
define timeperiod {
timeperiod_name exceptions
alias Some random dates
2008-12-15 00:00-24:00 ; 15 Dicembre 2008
friday 3 00:00-24:00 ; Terzo venerdì di ogni mese
february -1 00:00-24:00 ; Ultimo giorno di Febbraio ogni anno
march 20 - june 21 00:00-24:00 ; Primavera
day 1 - 15 00:00-24:00 ; Prima metà di ogni mese
2008-01-01 / 7 00:00-24:00 ; Ogni 7 giorni a partire dal 1/1/2008
}
Il passo successivo è dire a Nagios come effettuare i vari controlli e le notifiche; lo facciamo definendo una serie di oggetti command che specificano i comandi che Nagios dovrà eseguire.
Le definizioni dei comandi sono formate da coppie di nomi e righe di comando (entrambi obbligatori) e possono contenere delle macro. Come detto prima, le macro sono variabili, racchiuse fra "$", che verranno sostituite con il rispettivo valore subito prima dell'esecuzione del comando; le macro consentono di mantenere le definizioni dei comandi generiche e leggibili. Un piccolo esempio renderà tutto più chiaro.
Si supponga di voler monitorare un web server con indirizzo IP "1.2.3.4"; si potrebbe definire un comando come il seguente:
define command {
command_name check-http
command_line /usr/local/libexec/nagios/check_http -I 1.2.3.4
}
Questa definizione è corretta e raggiungerà perfettamente il suo scopo. Ma cosa succede se si decide in un secondo tempo di monitorare un altro server web? Sarebbe una buona idea definire un nuovo comando, quasi identico? Pare decisamente più efficiente approfittare delle macro e scrivere un comando generico come:
define command {
command_name check-http
command_line $USER1$/check_http -I $HOSTADDRESS$
}
e lasciare a Nagios la responsbilità di sostituire la macro $HOSTADDRESS$ con l'indirizzo IP corretto, ricavato dalla definizione dell'host (vd. sotto). Come si ricorderà dal capitolo precedente, la macro $USER1$ contiene il percorso della directory dei plugin.
E ora complichiamo ancora un po' le cose! Cosa succede se vogliamo che Nagios verifichi la disponibilità di una certa URL su ogni server web? Questa URL può variare da server a server, quindi ora dobbiamo definire un comando che sia ancora generico, ma allo stesso tempo specifico per ogni server! Può sembrare contraddittorio, eppure, ancora una volta, Nagios risolve questo problema per mezzo delle macro: infatti le macro $ARGn$ (dove n è un numero fra 1 e 32 compresi) fungono da segnaposto per argomenti (specifici per ogni servizio) che verranno indicati più avanti, all'interno delle definizioni dei servizi (vd. sotto per ulteriori dettagli). Quindi, la definizione del comando diventerebbe:
define command {
command_name check-http
command_line $USER1$/check_http -I $HOSTADDRESS$ -u $ARG1$
}
Oltre a quelle appena viste, Nagios fornisce parecchie altre macro utilissime; la documentazione le elenca tutte, insieme al loro contesto di validità. Di seguito, un esempio di definizioni di comandi.
################################################################################
# Comandi di notifica #
# Non ci sono plugin di notifica standard; quindi i comandi di notifica sono #
# generalmente script personali o semplici righe di comando #
################################################################################
define command {
command_name host-notify-by-email
command_line $USER1$/host_notify_by_email.sh $CONTACTEMAIL$
}
define command {
command_name notify-by-email
command_line $USER1$/notify_by_email.sh $CONTACTEMAIL$
}
define command {
command_name host-notify-by-SMS
command_line /usr/local/bin/sendsms $ADDRESS1$ "Nagios: Host $HOSTNAME$ ($HOSTADDRESS$)is in state: $HOSTSTATE$"
}
define command {
command_name notify-by-SMS
command_line /usr/local/bin/sendsms $ADDRESS1$ "Nagios: Service $SERVICEDESC$ on $HOSTALIAS$ is in state: $SERVICESTATE$"
}
################################################################################
# Comandi di controllo #
# I plugin ufficiali di Nagios dovrebbero soddisfare le esigenze piu' comuni #
# per il monitoraggio di host e servizi. Comunque, se non dovessero bastare, #
# vedremo a breve come scrivere plugin personali #
################################################################################
define command {
command_name check-host-alive
command_line $USER1$/check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 1
}
define command {
command_name check-ssh
command_line $USER1$/check_ssh $HOSTADDRESS$
}
define command {
command_name check-http
command_line $USER1$/check_http -I $HOSTADDRESS$ -u $ARG1$
}
define command {
command_name check-smtp
command_line $USER1$/check_smtp -H $HOSTADDRESS$
}
define command {
command_name check-imap
command_line $USER1$/check_imap -H $HOSTADDRESS$
}
define command {
command_name check-dns
command_line $USER1$/check_dns -s $HOSTADDRESS$ -H $ARG1$ -a $ARG2$
}
define command {
command_name check-mysql
command_line $USER1$/check_mysql -H $HOSTADDRESS -u $USER2$ -p $USER3$
}
[...]
Gli oggetti contact consentono di specificare le persone che devono essere avvisate automaticamente al verificarsi delle condizioni di allarme. I contatti vengono prima definiti individualmente e poi raggruppati in oggetti contactgroup, per semplificarne la gestione.
Per la prima volta, nelle prossime definizioni, si farà riferimento a oggetti definiti in precedenza. In particolare, i valori delle direttive host_notification_period e service_notification_period devono essere oggetti timeperiod; e i valori delle direttive host_notification_command e service_notification_command devono essere oggetti command.
define contact {
# Nome breve per identificare il contatto
contact_name john
# Nome esteso o descrizione
alias John Doe
# Abilita le notifiche per questo contatto
host_notifications_enabled 1
service_notifications_enabled 1
# Timeperiod durante i quali il contatto puo' essere notificato in caso di
# problemi o di recovery di un host o un servizio
host_notification_period always
service_notification_period always
# Stati degli host per i quali questo contatto vuole essere notificato
# (d=down, u=unreachable, r=recovery, f=flapping, n=none)
host_notification_options d,u,r
# Stati dei servizi per i quali questo contatto vuole essere notificato
# (w=warning, c=critical, u=unknown, r=recovery, f=flapping, n=none)
service_notification_options w,u,c,r
# Comandi usati per notificare questo contatto in caso di problemi o recovery
# di host o servizi
host_notification_commands host-notify-by-email,host-notify-by-SMS
service_notification_commands notify-by-email,notify-by-SMS
# Indirizzo email di questo contatto
email jdoe@kernel-panic.it
# Nagios fornisce 6 direttive 'address' (da address1 a address6) per
# specificare altri 'indirizzi' per il contatto (ad es. il numero di cellulare
# per le notifiche via SMS)
address1 xxx-xxx-xxxx
# Consenti a questo contatto di passare comandi esterni a Nagios dai CGI
can_submit_commands 1
}
# Il prossimo contatto e' diviso in due, per consentire diverse opzioni di
# notifica a seconda del timeperiod
define contact {
contact_name danix@work
alias Daniele Mazzocchio
host_notifications_enabled 1
service_notifications_enabled 1
host_notification_period workhours
service_notification_period workhours
host_notification_options d,u,r
service_notification_options w,u,c,r
host_notification_commands host-notify-by-email
service_notification_commands notify-by-email
email danix@kernel-panic.it
can_submit_commands 1
}
define contact {
contact_name danix@home
alias Daniele Mazzocchio
host_notifications_enabled 1
service_notifications_enabled 1
host_notification_period nonworkhours
service_notification_period nonworkhours
host_notification_options d,u
service_notification_options c
host_notification_commands host-notify-by-email,host-notify-by-SMS
service_notification_commands notify-by-email,notify-by-SMS
email danix@kernel-panic.it
address1 xxx-xxx-xxxx
can_submit_commands 1
}
[...]
# Tutti i contatti amministrativi sono raggruppati nel contactgroup 'Admins'
define contactgroup {
contactgroup_name Admins
alias Nagios Administrators
members danix@work,danix@home,john
}
[...]
Siamo finalmente giunti a uno degli aspetti più importanti della configurazione di Nagios; la definizione degli host (server, workstation, dispositivi di rete, ecc.) che vogliamo monitorare. Questo ci porterà a parlare di una delle funzionalità più potenti della configurazione di Nagios: l'ereditarietà degli oggetti. Si noti che, anche se ne parliamo ora per la prima volta, l'ereditarietà si applica a tutti gli oggetti di Nagios; tuttavia, è proprio nella definizione di host e servizi che offre i maggiori vantaggi.
In effetti, configurare un host richiede l'impostazione di un sacco di parametri; e il valore di questi parametri sarà normalmente lo stesso per la maggior parte degli host. Senza ereditarietà, questo significherebbe sprecare un sacco di tempo scrivendo e riscrivendo gli stessi parametri per poi ritrovarsi dei file di configurazione confusi, sovrappeso e ingestibili.
Ma per fortuna Nagios è abbastanza furbo da risparmiarci un sacco di fatica, consentendo di definire speciali oggetti template, le cui proprietà possono essere "ereditate" da altri oggetti senza doverle riscrivere. Di seguito un breve esempio di come viene creato un template:
define host {
name generic-host-template # Nome del template
check_command check-host-alive
check_period always
max_check_attempts 5
notification_options d,u,r
register 0 # Non registrarlo!
}
Come si vede, la definizione di un template è praticamente identica alla definizione di un normale oggetto. Le uniche differenze sono:
Per creare un host effettivo a partire da un template, occorre semplicemente specificare il nome del template come valore della direttiva use e assicurarsi che tutti i campi obbligatori siano ereditati o impostati esplicitamente:
define host {
host_name hostname
use generic-host-template
alias alias
address x.x.x.x
}
Bene, adesso passiamo dalla teoria alla pratica e definiamo un paio di template per i nostri server. Si noti che il secondo eredita dal primo; Nagios consente infatti più livelli di template.
# Questo e' il template per tutti gli host in LAN
define host {
# Nome del template
name generic-lan-host
# Comando da usare per controllare lo stato dell'host
check_command check-host-alive
# Contactgroup da notificare in caso di problemi (o recovery) di questo host
contact_groups Admins
# Abilita i check attivi
active_checks_enabled 1
# Timeperiod durante il quale effettuare i check attivi per questo host
check_period always
# Numero di volte che Nagios ripetera' un controllo che ritorna uno stato non OK
max_check_attempts 3
# Abilita il gestore degli eventi
event_handler_enabled 1
# Abilita il calcolo dei dati di performance
process_perf_data 1
# Abilita il salvataggio delle informazioni di stato fra restart del programma
retain_status_information 1
# Abilita il salvataggio delle informazioni non di stato fra restart del programma
retain_nonstatus_information 1
# Abilita le notifiche
notifications_enabled 1
# Intervallo di tempo (in minuti) fra notifiche consecutive che il server e'
# ancora giu' o non raggiungibile
notification_interval 120
# Timeperiod durante il quale le notifiche riguardanti questo host vanno spedite
notification_period always
# Stati dell'host per i quali le notifiche dovrebbero essere spedite (d=down,
# u=unreachable, r=recovery, f=flapping, n=none)
notification_options d,u,r
# Non registrare questa definizione: e' solo un template, non un host effettivo
register 0
}
# GLi host in DMZ ereditano tutti gli attributi dal generic-lan-host per mezzo
# della direttiva 'use'. L'unica differenza e' che Nagios deve passare dai
# firewall interni (CARP) per raggiungere i server in DMZ, rendendo quindi
# necessaria la direttiva 'parents'
define host {
name generic-dmz-host
# La direttiva 'use' specifica il nome di un oggetto template da cui questo host
# eredita le proprieta'
use generic-lan-host
# Questa direttiva specifica gli host lungo la strada fra il server di
# monitoraggio e l'host remoto (maggiori dettagli qui)
parents fw-int
# Anche questo e' un template
register 0
}
Ora possiamo approfittare dei nostri template per definire gli host in poche righe.
# Configurazione per l'host dns1.lan.kernel-panic.it
define host {
use generic-lan-host
host_name dns1
alias LAN primary master name server
address 172.16.0.161
# Informazioni estese (totalmente facoltative)
notes This is the internal primary master name server (Bind 9.3.4)
# URL con maggiori informazioni su questo host
notes_url http://www.kernel-panic.it/openbsd/dns/
# Immagine associata a questo host nello status CGI; le immagini devono essere in
# /var/www/nagios/images/logos/
icon_image dns.png
# Stringa usata nel tag 'alt' della icon_image
icon_image_alt [dns]
# Immagine associata a questo host nel CGI statusmap
statusmap_image dns.gd2
}
# Configurazione per l'host mail.kernel-panic.it
define host {
use generic-dmz-host
host_name mail
alias Mail server
address 172.16.240.150
notes This is the Postfix mail server (with IMAP(S) and web access)
notes_url http://www.kernel-panic.it/openbsd/mail/
icon_image mail.png
icon_image_alt [Mail]
statusmap_image mail.gd2
}
# Configurazione per l'host proxy.kernel-panic.it
define host {
use generic-dmz-host
host_name proxy
alias Proxy server
address 172.16.240.151
notes This is the Squid proxy server
notes_url http://www.kernel-panic.it/openbsd/proxy/
icon_image proxy.png
icon_image_alt [Proxy]
statusmap_image proxy.gd2
}
[...]
# Configurazione per l'host fw-int.kernel-panic.it
define host {
use generic-lan-host
host_name fw-int
alias Internal firewalls' CARP address
address 172.16.0.202
notes Virtual CARP address of the internal firewalls
notes_url http://www.kernel-panic.it/openbsd/carp/
icon_image fw.png
icon_image_alt [FW]
statusmap_image fw.gd2
}
# Configurazione per l'host mickey.kernel-panic.it
define host {
use generic-lan-host
host_name mickey
alias Internal Firewall #1
address 172.16.0.200
notes Internal firewall (first node of a two-nodes CARP cluster)
notes_url http://www.kernel-panic.it/openbsd/carp/
icon_image fw.png
icon_image_alt [FW]
statusmap_image fw.gd2
}
[...]
Gli host possono essere raggruppati con la dichiarazione hostgroup, che non ha effetto sul monitoraggio, ma semplicemente consente di visualizzare gli host a gruppi nei CGI.
# Server DNS
define hostgroup {
hostgroup_name DNS
alias Domain Name Servers
members dns1,dns2,dns3,dns4
notes Our internal Domain Name Servers, running Bind 9.4.2-P2
}
# Firewall
define hostgroup {
hostgroup_name firewalls
alias CARP Firewalls
members mickey,minnie,donald,daisy,fw-int,fw-ext
notes Our CARP-enabled firewalls (both virtual and physical addresses)
}
# Server web
define hostgroup {
hostgroup_name WWW
alias Web Servers
members www1,www2
notes Our corporate web servers, running Apache 1.3
}
La configurare dei servizi da monitorare è molto simile a quella degli host: l'ereditarietà può farci risparmiare un sacco di tempo ed è possibile raggruppare i servizi con la dichiarazione (facoltativa) servicegroup. Di seguito la definizione del nostro servizio template:
define service {
# Nome del template
name generic-service
# Normalmente i servizi non sono volatili
is_volatile 0
# Contactgroup da notificare in caso di problemi (o recovery) di questo servizio
contact_groups Admins
# Abilita i check attivi
active_checks_enabled 1
# Timeperiod durante il quale effettuare i check attivi per questo servizio
check_period always
# Intervallo di tempo (in minuti) fra check "regolari", cioe' check che
# avvengono quando il servizio e' in stato OK o quando e' in stato non OK ma
# e' gia' stato controllato max_check_attempts volte
normal_check_interval 5
# Intervallo di tempo (in minuti) fra controlli non regolari
retry_check_interval 1
# Numero di volte che Nagios ripetera' un controllo che ritorna uno stato non OK
max_check_attempts 3
# Abilita la parallelizzazione dei controlli per migliorare le performance
parallelize_check 1
# Abilita i check passivi
passive_checks_enabled 1
# Abilita il gestore degli eventi
event_handler_enabled 1
# Abilita il calcolo dei dati di performance
process_perf_data 1
# Abilita il salvataggio delle informazioni di stato fra restart del programma
retain_status_information 1
# Abilita il salvataggio delle informazioni non di stato fra restart del programma
retain_nonstatus_information 1
# Abilita le notifiche
notifications_enabled 1
# Intervallo di tempo (in minuti) fra notifiche consecutive che il servizio e'
# ancora in stato non-OK
notification_interval 120
# Timeperiod durante il quale le notifiche per questo servizio vanno spedite
notification_period always
# Stati del servizio per i quali le notifiche dovrebbero essere spedite (c=critical,
# w=warning, u=unknown, r=recovery, f=flapping, n=none)
notification_options w,u,c,r
register 0
}
Ora, prima di passare alla definizione dei servizi, dobbiamo terminare la nostra discussione su come passare ai comandi argomenti specifici per servizio, tramite le macro $ARGn$. Come si ricorderà, queste macro fungono da segnaposto: vengono sostituite con l'ennesimo argomento passato al comando nella definizione del servizio; ad esempio, una definizione di comando come la seguente si aspetta di ricevere due argomenti:
define command {
command_name some-command
command_line $USER1$/check_something $ARG1$ $ARG2$
}
Di conseguenza, per controllare un servizio tramite questo comando, dovremo assegnare alla variabile check_command una stringa contenente lo short name del comando, seguito dagli argomenti, il tutto separato da caratteri "!". Ad es.:
define service {
service_description some-service
check_command some-command!arg-1!arg-2
[...]
}
Procediamo quindi alla definizione dei servizi effettivi:
# Servizio di secure shell
define service {
use generic-service
service_description SSH
# Short name degli host che eseguono questo servizio. Se un servizio gira su
# tutti gli host, si puo' usare il carattere jolly '*'
host_name *
check_command check-ssh
# Questa direttiva e' una possibile alternativa all'uso della direttiva members
# nelle definizioni dei service group
servicegroups ssh-services
# Informazioni estese
notes Availability of the SSH daemon
notes_url http://www.openssh.org/
icon_image ssh.png
icon_image_alt [SSH]
}
# Servizi web
define service {
use generic-service
service_description WWW
host_name www1,www2
check_command check-http!/index.html
notes Availability of the corporate web sites
notes_url http://www.apache.org/
icon_image www.png
icon_image_alt [WWW]
}
define service {
use generic-service
service_description WWW
host_name mail
check_command check-http!/webmail/index.html
notes Availability of the web access to the mail server
notes_url http://www.squirrelmail.org/
icon_image www.png
icon_image_alt [WWW]
}
[...]
Come gli host, anche i servizi possono essere raggruppati per mezzo della direttiva servicegroup:
define servicegroup {
servicegroup_name www-services
alias Web Services
# La direttiva 'members' richiede una lista, separata da virgole, di coppie
# di host e servizi, ad es. 'host1,servizio1,host2,servizio2,...'
members www1,WWW,www2,WWW,mail,WWW
}
define servicegroup {
servicegroup_name dns-services
alias Domain Name Service
members dns1,DNS,dns2,DNS,dns3,DNS,dns4,DNS
}
# I membri del seguente servicegroup sono specificati dalla direttiva
# 'serviecegroups' nella definizione del servizio 'SSH'
define servicegroup {
servicegroup_name ssh-services
alias Secure Shell Service
}
[...]
Bene, il grosso del lavoro è fatto: l'ultimo passo è la configurazione dell'interfaccia web e poi saremo finalmente in grado di mettere Nagios al lavoro!