Archivi categoria: Programmazione

Nagios: script bash per monitorare lo stato dei volumi RAID

Partendo dalle considerazioni fatte in questo post, ho deciso di mettere a punto uno script bash da integrare a Nagios, in modo da monitorare lo status dei volumi RAID (e dei dischi fisici annessi) a prescindere dal metodo utilizzato per l’implementazione di tale tecnologia (hardware, fake oppure software).

nagiosDi seguito riporto il suddetto script nella sua interezza:

#!/bin/bash

type=$1

subtype=$2

element=$3

usage="check_raid <--software|--fake|--hardware> [--megaraid|--mpt] [--volume|--physical|--battery]"

if [[ ! -z "$type" && "$type" =~ "software" ]];then
        okswraid=0;
        koswraid=0;
        volumes=`cat /proc/mdstat | grep md | grep active | grep -v inactive | awk '{print $1}' | wc -l`
        if [[ ! -z $volumes ]];then
                for (( v=1; v<=$volumes; v++ ))
                do
                        volume=`cat /proc/mdstat | grep md | grep active | grep -v inactive | awk '{print $1}' | sed -n "$v p"`
                        raidtype=`cat /proc/mdstat | grep md | grep active | grep -v inactive | awk '{print $4}' | sed -n "$v p"`
                        diskno=`cat /proc/mdstat | grep '[[0-9]\/[0-9]]' | awk '{print $3}' | sed -n "$v p"`
                        disksok=`echo $diskno | sed 's/\[//g' | cut -d '/' -f1`
                        diskstotal=`echo $diskno | sed 's/\]//g' | cut -d '/' -f2`
                        if [[ "$disksok" -eq "$diskstotal" ]];then
                                echo "OK: Software RAID volume $volume configured in $raidtype is OK, with $diskno disks UP"
                                ((okswraid++))
                        elif [[ "$disksok" -lt "$diskstotal" ]];then
                                echo "CRITICAL: Software RAID volume $volume configured in $raidtype is CRITICAL, with $diskno disks UP"
                                ((koswraid++))
                        fi
                done

                if [[ $koswraid -eq 0 ]];then
                        exit 0;
                else
                        exit 2;
                fi
        else
                echo "UNKNOWN: No software RAID configured"
                exit 3;
        fi

elif [[ ! -z "$type" && "$type" =~ "fake" ]];then
        bin=`/usr/bin/which dmraid`
        if [[ ! -z $bin ]];then
                result=`$bin -s`
                disksno=`$bin -r | grep -v no | wc -l`
                disksok=`$bin -r | grep ok | wc -l`
                if [[ ! -z "$result" && "$result" =~ "ok" ]];then
                        echo "OK: RAID Status is OK, with $disksok/$disksno disks OK"
                        exit 0;
                elif [[ ! -z "$result" && "$result" =~ "no raid" ]];then
                        echo "UNKNOWN: no fake RAID configured"
                        exit 3;
                else
                        echo "CRITICAL: RAID Status is KO, with $disksok/$disksno disks OK"
                        exit 2;
                fi
        else
                echo "UNKNOWN: no dmraid binary found - please install dmraid"
                exit 3;
        fi

elif [[ ! -z "$type" && "$type" =~ "hardware" ]];then
        okraid=0;
        oksmart=0;
        koraid=0;
        kosmart=0;
        if [[ ! -z "$subtype" && "$subtype" =~ "--megaraid" ]];then
                bin=`/usr/bin/which MegaCli64`
                if [[ ! -z $bin ]];then
                        if [[ ! -z "$element" && "$element" =~ "--volume" ]];then
                                result=`$bin -LDinfo -Lall -aALL | grep State | awk '{print $3}'`
                                if [[ ! -z "$result" && $result =~ "Optimal" ]];then
                                        echo "OK: RAID Volume state is $result"
                                        exit 0;
                                else
                                        echo "CRITICAL: RAID Volume state is $result"
                                        exit 2;
                                fi
                        elif [[ ! -z "$element" && "$element" =~ "--physical" ]];then
                                diskno=`$bin -PDList -aALL | grep "S.M.A.R.T alert" | wc -l`
                                for (( d=1; d<=$diskno; d++ ))
                                do
                                        result=`$bin -PDList -aALL | grep "Firmware state" | sed -n "$d p" | awk '{print $3}' | sed 's/,//g'`
                                        if [[ ! -z "$result" && $result =~ "Online" ]];then
                                                echo "RAID Status for Physical Disk number $d is OK"
                                                ((okraid++));
                                        else
                                                echo "RAID Status for Physical Disks number $d is KO"
                                                ((koraid++));
                                        fi
                                done
                                for (( d=1; d<=$diskno; d++ ))
                                do
                                        result=`$bin -PDList -aALL | grep "S.M.A.R.T alert" | sed -n "$d p" | awk '{print $8}'`
                                        if [[ ! -z "$result" && $result =~ "No" ]];then
                                                echo "S.M.A.R.T Status for Physical Disk number $d is OK"
                                                ((oksmart++));
                                        else
                                                echo "S.M.A.R.T. Status for Physical Disks number $d is KO"
                                                ((kosmart++));
                                        fi
                                done
                                if [[ $koraid -eq 0 && $kosmart -eq 0 ]];then
                                        echo "OK: RAID and S.M.A.R.T Status for all Physical Disks is OK"
                                        exit 0;
                                elif [[ $koraid -eq 0 && $kosmart -ne 0 ]];then
                                        echo "CRITICAL: S.M.A.R.T Status for some Physical Disks is KO"
                                        exit 2;
                                elif [[ $koraid -ne 0 && "$kosmart" -eq 0 ]];then
                                        echo "CRITICAL: RAID Status for some Physical Disks is KO"
                                        exit 2;
                                elif [[ $koraid -ne 0 && $kosmart -ne 0 ]];then
                                        echo "CRITICAL: RAID and S.M.A.R.T Status for some Physical Disks is KO"
                                        exit 2;
                                fi
                        elif [[ ! -z "$element" && "$element" =~ "--battery" ]];then
                                result=`$bin -AdpBbuCmd -aAll | grep "Battery State" | awk '{print $3}'`
                                if [[ ! -z "$result" && $result =~ "OK" ]];then
                                        echo "OK: RAID Controller Battery state is OK"
                                        exit 0;
                                else
                                        echo "CRITICAL: RAID Controller Battery state is $result"
                                        exit 2;
                                fi
                        else
                                echo "UNKNOWN: please specify the element to check"
                                echo $usage;
                                exit 3;
                        fi
                else
                        echo "UNKNOWN: No MegaCli64 binary found - please install MegaCli64"
                        exit 3;
                fi

        elif [[ ! -z "$subtype" && "$subtype" =~ "mpt" ]];then
                modprobe mptctl
                bin=`/usr/bin/which mpt-status`
                bin2=`/usr/bin/which lspci`
                bin3=`/usr/bin/which daemonize`
                if [[ ! -z $bin ]];then
                        if [[ ! -z $bin2 ]];then
                                controller_status=`lspci | grep MPT`
                                if [[ ! -z $controller_status ]];then
                                        if [[ ! -z $bin3 ]];then
                                                controller=`$bin -p | grep id | awk '{print $3}' | sed 's/id=//g' | sed 's/,//g'`
                                                if [[ ! -z $controller ]];then
                                                        result=`$bin -i $controller | grep OPTIMAL`
                                                        if [[ ! -z "$result" ]];then
                                                                echo "OK: RAID Status is OPTIMAL"
                                                                exit 0;
                                                        else
                                                                echo "CRITICAL: RAID Status is DEGRADED"
                                                                exit 2;
                                                        fi
                                                else
                                                        echo "UNKNOWN: MPT Controller found but no RAID configured";
                                                        exit 3;
                                                fi
                                        else
                                                echo "UNKNOWN: No daemonize binary found - please install daemonize";
                                                exit 3;
                                        fi
                                else
                                        echo "UNKNOWN: Unable to find RAID Controller";
                                        exit 3;
                                fi
                        else
                                echo "UNKNOWN: No lspci binary found - please install lspci";
                                exit 3;
                        fi
                else
                        echo "UNKNOWN: No mpt-status binary found - please install mpt-status"
                        exit 3;
                fi

        else
                echo "UNKNOWN: please specify the RAID Controller type"
                echo $usage
                exit 3;
        fi
else
        echo "UNKNOWN: please specify the RAID type"
        echo $usage
        exit 3;
fi
exit 0

Lo usage parla chiaro: il primo argomento identifica, per l’appunto, la tecnologia RAID utilizzata sul sistema target. Il secondo ed il terzo argomento, invece, dovranno essere specificati solo nel caso in cui si abbia a che fare con un RAID di tipo hardware. Nella fattispecie, essi rappresentano, rispettivamente, la tipologia di chipset utilizzata dal controller e l’oggetto di interesse della nostra query, ovvero il volume, i dischi fisici oppure la batteria (tale parametro ha senso solo se il chipset è di tipo LSI MegaRAID).

Configurazione di Nagios

Come al solito, il primo step consiste nel definire un comando che utilizzi lo script (in gergo plugin) riportato in precedenza:

# 'check_local_raid' command definition
define command{
        command_name    check_local_raid
        command_line    $USER1$/check_raid $ARG1$ $ARG2$ $ARG3$
        }

tali direttive andranno opportunamente inserite all’interno del file /etc/nagios/objects/commands.cfg.

Successivamente si potrà procedere con la definizione del servizio che si occuperà del monitoraggio vero e proprio, da aggiungere alla configurazione dell’host target, in questo caso /etc/nagios/object/locahost.cfg:

define service{
        use                             local-service         ; Name of service template to use
        host_name                       localhost
        service_description             RAID Status
        check_command                   check_local_raid!--software
        }

A questo punto non ci rimane che ricaricare la configurazione di Nagios per rendere effettive le suddette modifiche:

[root@linuxbox ~]# service nagios reload

ed abbiamo finito.

Alla prossima.

CentOS 6 e PHP 5.3.3: cifratura del codice sorgente mediante BLENC (Blowfish Encoder for PHP source scripts)

A quanti di voi sarà capitato di dover cifrare del codice PHP scritto di vostro pugno, magari perchè contenente informazioni sensibili, quali, ad esempio, username e password di accesso al database?

Ebbene, un metodo semplice per ottenere quanto sopra consiste nell’installare il modulo PHP BLENC (che è ancora in versione beta – qui trovate il manuale). Tale operazione può essere effettuata in modo semiautomatico utilizzando il tool pecl presente sulla nostra macchina.

PHPPrima di procedere, è necessario verificare che il suddetto applicativo sia già installato, digitando il comando:

[root@linuxbox ~]# which pecl

il cui output dovrebbe essere:

/usr/bin/pecl

Successivamente, potremo procedere con l’installazione vera e propria del modulo BLENC, lanciando il comando:

[root@linuxbox ~]# pecl install -f blenc

A questo punto potremo creare il file blenc.ini da posizionare all’interno della dir /etc/php.d, il cui contenuto dovrà  essere simile al seguente:

; Enable blenc extension module
extension=blenc.so

All’interno del file /etc/php.ini, utilizzando la entry blenc.key_file, possiamo definire il percorso in cui trovare le chiavi per decifrare gli scrip criptati, ovvero:

[blenc]

blenc.key_file = /usr/local/etc/blenckeys

Ora passiamo alla creazione del file encrypt.php, il cui contenuto dovrà essere:

<?php

$file_to_crypt=file_get_contents("security.php");

$key=blenc_encrypt($file_to_crypt, "connect.php");

$key_file = ini_get('blenc.key_file');

file_put_contents($key_file, $key."\n", FILE_APPEND);

?>

il quale ci consentirà di cifrare gli scrip di nostro interesse.

In particolare, security.php contiene il codice sorgente che vogliamo criptare, connect.php sarà il nostro file cifrato e la chiave generata mediante la funzione blenc_encrypt() verrà salvata all’interno dell’apposito file /usr/local/etc/blenckeys, ricavato mediante il parsing della direttiva blenc.key_file presente in /etc/php.ini.

Da notare che all’interno della funzione file_put_contents(), il secondo argomento contiene il carattere \n (newline), poichè per il corretto funzionamento del modulo è necessario che per ogni riga sia presente una sola chiave (dove ogni chiave si riferisce univocamente ad un file cifrato). Tale “accortezza” non è presente all’interno del manuale ufficiale.

Eseguiamo, quindi, il file encrypt.php da linea di comando:

[root@linuxbox ~]# php encrypt.php

il quale genererà il file cifrato connect.php.

Come ultimo step riavviamo httpd:

[root@linuxbox ~]# service httpd reload

ed abbiamo finito.

Alla prossima.

NRPE_NT e Nagios: script PowerShell per il controllo dell’uptime su Windows Server 2008 R2

Tenere sotto controllo l’uptime dei nostri server è molto importante, soprattutto se si ha a che fare con macchine in produzione. Per ciò che concerne i sistemi operativi di casa Microsoft (sia client che server), esiste un comando che può tornarci molto utile allo scopo, ovvero:

net stats srv

il quale, basandosi sul servizio Server di Windows, colleziona e fornisce tutta una serie di informazioni associate alla nostra macchina, tra cui, per l’appunto, il suo tempo di attività:

Statistics since 7/11/2016 9:21:08 AM

powershellAffinchè il suddetto task possa essere effettuato in automatico dal nostro NMS (Nagios), è necessario utilizzare uno scrip (get_uptime.ps1, creato da me allo scopo) da integrare al servizio NRPE_NT. Per ragioni di semplicità e comodità ho deciso di avvalermi di Windows PowerShell per la stesura dello stesso, il cui contenuto è il seguente:

$critical = $Args[0]
$warning = $Args[1]

if ($warning -and $critical) #both variable are defined and not empty
{
    if ($critical -lt $warning)
    {
        $os = Get-WmiObject win32_operatingsystem
        $uptime = (Get-Date) - ($os.ConvertToDateTime($os.lastbootuptime))
        $minutes_to_seconds = $Uptime.Minutes*60
        $hours_to_seconds = $Uptime.Hours*3600
        $days_to_seconds = $Uptime.Days*86400
        $uptime_in_seconds = $minutes_to_seconds + $hours_to_seconds + $days_to_seconds
        if ($uptime_in_seconds -gt $critical -and $uptime_in_seconds -gt $warning)
        {
            $Display = "OK: System Uptime is " + $Uptime.Days + " days, " + $Uptime.Hours + " hours, " + $Uptime.Minutes + " minutes"
            Write-Output $Display
            exit 0
        }
        elseif ($uptime_in_seconds -gt $critical -and $uptime_in_seconds -le $warning)
        {
            $Display = "WARNING: System Uptime is " + $Uptime.Days + " days, " + $Uptime.Hours + " hours, " + $Uptime.Minutes + " minutes"
            Write-Output $Display
            exit 1
        }
        else
        {
            $Display = "CRITICAL: System Uptime is " + $Uptime.Days + " days, " + $Uptime.Hours + " hours, " + $Uptime.Minutes + " minutes"
            Write-Output $Display
            exit 2
        }
    }
    else
    {
        $Usage = "Usage: .\get_uptime.ps1 <critical threshold in seconds> <warning threshold in seconds>"
        $Error1 = "Warning threshold must be greater then the critical one"
        Write-Output $Usage
        Write-Output $Error1
        exit 3
    }
}
else
{
    $Usage = "Usage: .\get_uptime.ps1 <critical threshold in seconds> <warning threshold in seconds>"
    $Error2 = "Warning threshold and critical threshold must be defined"
    Write-Output $Usage
    Write-Output $Error2
    exit 3
}

In soldoni, il suddetto scrip riceve due argomenti da linea di comando, ovvero la soglia critica e quella di warning, espresse entrambe in secondi. Ovviamente, trattandosi di tempo di attività, la prima deve essere strettamente minore della seconda.

Configurazione di NRPE_NT

Salviamo tale scrip all’interno della directory C:\nrpe\Plugins\V2 e successivamente editiamo il file C:\nrpe\Plugins\V2\V2_nrpe_commands.cfg, aggiungendo la seguente direttiva:

# =================================
# Windows System Uptime checks
# =================================

command[get_system_uptime]=cmd.exe /c echo C:\nrpe\Plugins\V2\get_uptime.ps1 "$ARG1$" "$ARG2$" | powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -command -

In particolare, stiamo definendo il comando get_system_uptime che si avvarrà di get_uptime.ps1 per l’individuazione del tempo di attività.

Occorre precisare che l’esecuzione dello scrip non può avvenire richiamando direttamente l’eseguibile powershell.exe, ma deve prima passare per cmd.exe (il cui output, mediante pipe, viene dato in pasto a PowerShell grazie alla direttiva -command –, dove il finale rappresenta “tutto ciò che proviene dal pipe”). Inoltre, si rende indispensabile bypassare l’executionpolicy di PowerShell, consentendo ad NRPE_NT di lanciare get_uptime.ps1 senza restrizioni.

Infine, riavviamo NRPE_NT per rendere effettive le suddette modifiche:

C:\Users\Administrator> net stop nrpe_nt
C:\Users\Administrator> net start nrpe_nt

Configurazione di Nagios

Per prima cosa è necessario creare un apposito comando all’interno del file /etc/nagios/object/commands.cfg:

# nagios-Windows-uptime check command definition

define command {
        command_name    check_Windows_uptime
        command_line    /usr/lib64/nagios/plugins/check_nrpe -H $HOSTADDRESS$ -t $ARG1$ -c get_system_uptime -a $ARG2$ $ARG3$
        }

Successivamente, all’interno del file che rappresenta l’host da monitorare, è necessario aggiungere un servizio specifico che si avvale del suddetto comando:

define service{
        use                             generic-service         ; Name of service template to use
        host_name                       Windows-Server
        service_descripion             query Windows System Uptime for Microsoft Windows Machine
        check_command                   check_Windows_uptime!30!60!900
        }

Ricarichiamo la configurazione di Nagios:

[root@linuxbox ~]# service nagios reload

ed abbiamo finito.

Alla prossima.

NRPE_NT: script powershell per la modifica della direttiva allowed_hosts in nrpe.cfg

Scenario

Sistema di monitoraggio (Nagios) connesso ad Internet mediante indirizzo IP pubblico di tipo dinamico. Esso deve tenere sotto controllo, tramite opportune query NRPE, alcuni servizi attivi su di un server remoto (Windows 2008 R2), raggiungibile mediante FQDN ed IP statico.

powershellProblema

Per evitare che eventuali client non autorizzati possanno connettersi al servizio NRPE_NT in ascolto sul server remoto (porta 5666/TCP), è possibile definire, all’interno del suo file di configurazione (nrpe.cfg), gli indirizzi IP a cui è consentito eseguire le query. Ad esempio, mediante la direttiva allowed_hosts, siamo in grado di dichiarare la seguente regola:

allowed_hosts=79.37.83.59

ed ecco, per l’appunto, dov’è il problema: essendo il suddetto indirizzo definito in modo statico, nel momento in cui, per un motivo o per un altro, Nagios dovesse cambiare il proprio IP pubblico, le query NRPE fallirebbero miseramente (check timeout).

Soluzione

A questo punto è necessario fare una premessa: esiste una soluzione più pulita di quella che sto per riportare, che consiste, fondamentalmente, nell’uso di NSClient++ abbinato ad NRPE_NT, grazie ai quali è possibile realizzare un meccanismo di autenticazione client/server mediante lo scambio di certificati X.509 (in tal caso la direttiva allowed_host sarebbe del tutto superflua poichè autenticazione e confidenzialità verrebbero garantite mediante i suddetti certificati).

Per ciò che concerne, invece, la soluzione da me individuata, essa consiste nella realizzazione di uno scrip powershell che “interroga” l’FQDN di Nagios ogni 5 minuti, verificando che il suo indirizzo IP pubblico sia diverso da quello presente nel file di configurazione di NRPE. In tal caso lo scrip modificherà l’IP definito nella direttiva allowed_hosts.

Di seguito riporto il contenuto dello scrip:

$IP = nslookup nagios.vostrodominio.com | Select-String -Pattern '\d{1,3}(\.\d{1,3}){3}' | Select-String -notmatch 'IP del server da monitorare'

$PublicIP = $IP.tostring().Split(" ")[-1]

$OldIP = (Get-Content C:\nrpe\bin\nrpe.cfg) | Select-String 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}'

$OldPublicIP = $OldIP.tostring().Split("=")[-1]

$data = Get-Date;

if ($PublicIP -ne $OldPublicIP -and $PublicIP)
{

    (Get-Content C:\nrpe\bin\nrpe.cfg) -replace 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}', "allowed_hosts=$(echo $PublicIP)" | Set-Content C:\nrpe\bin\nrpe.cfg

    net stop nrpe_nt

    Start-Sleep -s 5

    net start nrpe_nt

    echo "$data NRPE_NT configuration has been updated. The new authorized Public IP is $PublicIP"

    exit 0;
}
else
{
    echo "$data Nothing to do. Exiting..."

    exit 0;
}

Per prima cosa ho individuato l’indirizzo IP pubblico di Nagios mediante una query di tipo A effettuata mediante nslookup:

$IP = nslookup nagios.vostrodominio.com | Select-String -Pattern '\d{1,3}(\.\d{1,3}){3}' | Select-String -notmatch 'IP del server da monitorare'

$PublicIP = $IP.tostring().Split(" ")[-1]

Ovviamente ho dovuto formattare l’output relativo alla suddetta query, identificando, tramite espressione regolare, le stringhe che riportano gli indirizzi IP coinvolti, escludendo successivamente quella che contiene l’IP del server da monitorare. Inoltre, è stato necessario salvare (dopo opportuna conversione) l’indirizzo IP di Nagios all’interno della variabile $PublicIP.

Discorso molto simile riguarda l’individuazione dell’IP configurato all’interno di nrpe.cfg, ottenuta mediante l’applet Get-Content:

$OldIP = (Get-Content C:\nrpe\bin\nrpe.cfg) | Select-String 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}'

$OldPublicIP = $OldIP.tostring().Split("=")[-1]

A questo punto è stato possibile procedere con il confronto tra l’IP attuale di Nagios e quello presente nel file di configurazione di NRPE_NT. Nel caso in cui questi differiscano (e la variabile $PublicIP non sia nè vuota, nè nulla), la direttiva allowed_hosts verrà modificata, con il conseguente riavvio del servizio in oggetto:

(Get-Content C:\nrpe\bin\nrpe.cfg) -replace 'allowed_hosts=\d{1,3}(\.\d{1,3}){3}', "allowed_hosts=$(echo $PublicIP)" | Set-Content C:\nrpe\bin\nrpe.cfg

    net stop nrpe_nt

    Start-Sleep -s 5

    net start nrpe_nt

    echo "$data NRPE_NT configuration has been updated. The new authorized Public IP is $PublicIP"

Infine, per eseguire tale scrip in modo automatico, ho creato un task mediante l’applicativo Task Scheduler di Windows, il quale si ripete giornalmente ogni 5 minuti e la cui azione è la seguente:

powershell -command "C:\Users\Administrator\Documents\scrip\nrpe_ip_update.ps1 >> C:\nrpe_ip_update.log"

Da ora in poi le query NRPE da indirizzo IP dinamico non saranno un problema.

A presto.

Overview sui linguaggi di programmazione

Con il passare del tempo si è assistito ad una massiccia evoluzione dei linguaggi di programmazione, che ha portato ad una loro evidente semplificazione, aumentandone allo stesso tempo le potenzialità.

 

linguaggi di programmazione, metodi di programmazione, OOP, procedurale, funzionale, Java, C, C++, LISP, linguaggi di scripting, compilatore

Esistono diversi criteri per differenziare i vari linguaggi di programmazione, tra cui l’ordine cronologico. Parliamo quindi di:

1) linguaggi di prima generazione, come il linguaggio macchina, piuttosto complesso e strettamente dipendente dall’architettura hardware;

2) linguaggi di seconda generazione, tra cui assembly. Tale linguaggio, molto simile al linguaggio macchina, consente al programmatore di turno di ottenere gli stessi risultati dei linguaggi di prima generazione adoperando, però, un minor numero di istruzioni. Inoltre, affinchè tale linguaggio possa essere tradotto in linguaggio macchina è necessario uno strumento apposito, detto assembler (per l’operazione inversa viene invece utilizzato il cosiddetto disassembler);

3) linguaggi di terza generazione: trovano in C, C++ e Java gli elementi più rappresentativi. Essi hanno come elemento predominante il fatto che si avvicinano molto al linguaggio naturale (english like) e dunque sono molto più semplici da utilizzare rispetto ad assembly o allo stesso linguaggio macchina.

4) linguaggi di quarta generazione: in questa categoria rientrano i cosiddetti linguaggi dichiartativi, come SQL, utilizzato come DDL e DML nell’albito dei DBMS.

Un ulteriore elemento distintivo tra i vari linguaggi di programmazione è sicuramente il metodo attraverso il quale vengono tradotti in linguaggio macchina. Parliamo quindi di:

1) linguaggi compilati: ne sono un’esempio C e C++, linguaggi che per essere tradotti il codice macchina (e dunque in eseguibili) necessitano di un compilatore (come gcc e g++);

2) linguaggi interpretati: essi non vengono compilati, ma interpretati in runtime senza che vi sia la necessità di trasformarli in eseguibili. Un esempio di linguaggi interpretati sono i cosiddetti linguaggi di scripting: da bash a perl, da php ad asp, and so on…

3) linguaggi a compilazione intermedia: sono un tipo di linguaggio ibrido, ovvero soggetti ad una prima fase, in cui si assiste ad una sorta di compilazione, ed una seconda fase, in cui avviene l’interpretazione. Un esempio tipico di questa tipologia di linguaggi è Java: in un primo momento il codice viene compilato, generando il cosiddetto bytecode. Successivamente, tale bytecode verrà interpretato da un software apposito (che funge appunto da interprete), ovvero la Java Virtual Machine (JVM).

Infine, un altro criterio per distinguere i vari linguaggi è la tipologia di programmazione di cui si avvalgono. Esistono infatti i seguenti tipi di programmazione:

1) programmazione procedurale: il programma viene realizzato attraverso l’uso di apposite procedure (le cosiddette funzioni) che hanno il compito di suddividere lo stesso in parti più elementari, consentendo allo sviluppatore di risolvere il problema mediante il classico approccio divide et impera. Essi, inoltre, permettono l’uso dei salti incondizionati (aka goto), anche se adoperarli potrebbe causare diversi problemi.

2) programmazione strutturata: simile in tutto e per tutto alla programmazione procedurale a differenza che non consente l’uso dei salti incondizionati;

3) programmazione orientata agli ogetti (OOP – Object Oriented Programming): i massimi rappresentanti di questa tipologia di programmazione sono certamente C++ ed il suo successore, ovvero Java. In questo caso parliamo di classe, caratterizzata da dati (attributi) e funzioni (metodi). Un oggetto non è altro che l’istanza di una classe.

4) programmazione dichiarativa, che a sua volta si suddivide in due tipologie, ovvero:

4a) programmazione funzionale, che basa il suo funzionamento su funzioni matematiche (un linguaggio che si avvale di tale tecnica è LISP, utilizzato soprattutto nell’ambito dell’intelligenza artificiale);

4b) programmazione logica, fondata su elementi di logica matematica.

Infine, a prescindere dal tipo di linguaggio e di logica di programmazione che si vuole utilizzare durante lo sviluppo software, è necessario porre particolare attenzione sul concetto di astrazione.

L’astrazione è un’attività svolta quasi automaticamente dalla nostra mente, la quale ci consente di concentrarci solo sui dettagli importanti di un dato problema, tralasciando tutto il resto. Esistono diversi tipi di astrazione:

1) astrazione funzionale: ovvero viene fornita solo l’interfaccia di un dato metodo (o il prototipo di una data funzione), tralasciando i dettagli implementativi della stessa;

2) astrazione dei dati: ovvero vengono considerate solo le informazioni essenziali relative ad una classe (o struttura), evitando quindi di dichiarare attributi che non verranno mai utilizzati;

3) astrazione di sistema: che riguarda l’intero software e lo rende in qualche modo indipendente dalla sua implementazione. Si può affermare che è una diretta conseguenza dell’astrazione dei dati che a sua volta dipende dall’astrazione funzionale.

Alla prossima.

Testing, documentazione e manutenzione del software

Testing

Affichè si possa realizzare del software valido, ovvero funzionante ed ottimizzato, occorre effettuare delle campagne di testing rigorose.

A tal proposito è opportuno precisare che esistono diverse metodologie ed approcci per testare il software creato. Tra gli approcci più gettonati vi è il cosiddetto metodo delle white box e quello delle black box. Nel primo caso i test vengono eseguiti tenendo conto del codice sorgente, oltre che del corretto funzionamento del software lato utente. Nel secondo caso, invece, il software viene visto come una vera e propria scatola nera; per la precisione i test si occupano soltanto di verificarne il corretto funzionamento lato utente, tralasciando completamente l’analisi del codice sorgente.

 

programming.jpg

Quando si analizza un software, alla ricerca di eventuali errori, è necessario tenere a mente alcuni termini ricorrenti, quali:

1) failure: ovvero la discrepenza tra il comportamento effettivo di un dato software e quello previsto;

2) fault: malfunzionamento del software causato da uno o più errori;

3) eccezione: ovvero la tecnica attraverso cui il sistema software può segnalare allo sviluppatore (o all’utente) il verificarsi di un dato errore;

4) bug (alias baco): ovvero un errore di programmazione più o meno grave.

In base ai componenti del software da analizzare, esistono diverse tipologie di testing, ad esempio:

1) unit testing, che esamina il funzionamento di un particolare modulo di cui è dotato il sistema;

2) integration testing (conosciuto anche come system testing), ovvero i test immediatamente successivi all’integrazione di uno o più moduli software;

3) acceptance testing, ovvero i test effettuati lato utente che verificano il corretto funzionamento del software e soprattutto la sua conformità con ciò che è stato decretato dall’analisi dei requisiti.

Tornando a bomba sul concetto di integrazione, occorre precisare che esistono diversi approcci per affrontare tale problematica. Il primo approccio consiste essenzialmente nell’integrare dapprima i moduli più importanti, per poi arrivare a quelli più elementari (approccio top-down). Il secondo approccio è speculare al primo, ovvero si parte dall’integrazione dei moduli più elementari per poi arrivare a quelli più importanti (bottom-up). Infine, vi è il cosiddetto approccio a macchia d’olio: si parte da alcuni moduli e si procede con l’integrazione in base alle necessità.

Per i software di grandi dimensioni spesso viene adoperato un approccio leggermente diverso. In particolare, la fase di testing si articola in due sottofasi, quali:

1) alpha testing, in cui gli stessi sviluppatori (o una cerchia ristretta di dipendenti appartenenti alla medesima ditta che ha creato il software), si occupano di verificare il corretto funzionamento del sistema;

2) beta testing, in cui una ristretta cerchia di utenti (esterni alla ditta che ha sviluppato il software) si occupa di verificare la qualità del sistema creato.

Inoltre, poichè spesso esistono più versioni di uno stesso software, magari rilasciate in periodi di tempo successivi, spesso viene adottata la tecnica del cosiddetto testing di regressione. Più precisamente, viene verificato che tutte le componenti funzionanti nelle versioni precedenti del software continuino a funzionare anche nella nuova versione.

Un’importante operazione da effettuare in fase di testing è l’analisi del codice sorgente. Tale analisi può essere di due tipi:

1) analisi statica;

2) analisi dinamica.

Nel primo caso viene controllato il codice dal punto di vista sintattico (definizione delle strutture, uso corretto dei segni di punteggiatura, ecc.) e lessicale (individuazione degli identificatori, delle parole chiave, ecc.). Viene inoltre fatta una verifica sui tipi di dato assegnato alle variabili (e sulle operazioni che le coinvolgono), oltre all’analisi dell’evoluzione temporale delle variabili stesse (dalla laro assegnazione fino al loro flush).

Per ciò che concerne l’analisi dinamica, le operazioni effettuate vengono identificate con il termine di copertura. I tipi di copertura sono molteplici, ovvero:

1) copertura dei comandi, in cui vengono eseguiti, uno per uno, tutti i comandi definiti all’interno del codice;

2) copertura delle decisioni, in cui, a differenza del caso precedente, si tiene conto anche dei costrutti decisionali (if – then – else);

3) copertura dei cammini, che comprende tutti i possibili cammini del programma (definiti dai costrutti decisionali), considerando anche i cicli e le iterazioni.

Documentazione

Alla base di un buon software c’è sempre una buona documentazione. Per documentazione, oltre al manuale dell’utente, si intende:

1) documentazione del progetto, in cui vengono definite le varie fasi da seguire durante la realizzazione del sistema;

2) documentazione del codice, realizzata principalmente attraverso i cosiddetti commenti, presenti all’interno del codice sorgente, oppure attraverso l’uso di librerie apposite (ad esempio JavaDoc).

Per ciò che concerne l’interazione tra le diverse figure professionali che partecipano allo sviluppo del software (analisti, sviluppatori, ecc.) è di vitale importanza utilizzare alcuni strumenti appositi, quali il documento dei requisiti. Esso ha come scopo anche quello di “regolare” lo scambio di informazioni con i clienti e viene realizzato utilizzando dello pseudocodice, meno soggetto ad errori di interpretazione rispetto al linguaggio naturale.

Altri strumenti molto utili per documentare il comportamento del codice sono gli alberi di decisione, le tabelle di decisione e i diagrammi di flusso (flowchart). Un’alternativa ai diagrammi di flusso è rappresentata dal linguaggio UML, i cui diagrammi principali sono i seguenti:

1) diagramma dei casi d’uso, che ha come scopo quello di rappresentare l’interazione dell’utente con il sistema;

2) diagramma degli oggetti;

3) diagramma delle classi;

4) diagramma di sequenza, che rappresenta le modalità con cui entrano in gioco le diverse componenti del sistema;

5) diagramma di deployment, che indica come i vari oggetti vengono distribuiti sui sistemi hardware;

6) diagramma delle attività, che rappresenta l’ordine di esecuzione delle varie attività dell’applicazione;

7) diagramma di collaborazione, che indica come le varie componenti del sistema software interagiscono tra di loro;

8) diagramma degli stati, che specifica l’evoluzione dello stato dei vari oggetti.

Manutenzione

La manutenzione del software è sicuramente una delle operazioni più critiche in assoluto. In particolare essa si occupa delle varie modifiche, evoluzioni ed aggiornamenti a cui il sistema è soggetto durante il corso della sua vita. A tal proposito, per manutenibilità si intende la caratteristica grazie alla quale un dato software può essere modificato senza impattare sulle performance e la funzionalità dello stesso. A ciò si aggiunge un sostanziale risparmio di risorse, sia in termini di denaro e forza lavoro che in termini di tempo.

Altra caratteristica fondamentale di un software è la riusabilità del codice, garantita anche grazie all’uso delle cosiddette API (Application Programming Interface).

Infine, da non sottovalutare è certamente la portabilità del codice, che lo rende teoricamente “indipendente” dall’architettura hardware e dal sistema operativo su cui lo stesso è installato.

Nelle operazioni di manutenzione e sviluppo software il versioning ricopre un ruolo fondamentale. Esso viene realizzato attraverso dei software appositi, detti Concurrent Versioning System (ad esempio Subversion), che consentono a più sviluppatori di lavorare contemporaneamente sullo stesso codice, tenendo traccia delle modifiche apportate da ciascuno di essi.

Le operazioni che vengono eseguite, in soldoni, sono due:

1) check out, ovvero lo sviluppatore accede in scrittura al codice per poterlo modificare;

2) check in, grazie al quale lo sviluppatore, tramite un commit, salva le modifiche apportate al codice.

Ovviamente i CVS devono gestire la concorrenza, quindi nel caso in cui due svuluppatori tentassero di salvare contemporaneamente le modifiche apportate al codice sorgente, il software di versioning li avviserebbe di quanto sta avvenendo generando un errore opportuno e suggerendo le possibili soluzioni al problema.

Più in generale, una corretta manutenzione del codice prevede le seguenti attività:

1) ispezione del codice sorgente;

2) definizione di regole comuni per la scrittura della documentazione;

3) scrittura di documentazione tecnica allegata al software stesso.

E’ tutto. A presto.

Eclipse Galileo ed il plugin Axis2 code generator

Stamattina ho provato ad installare il plugin Axis2 code generator 1.6.1 sul mio Eclipse Galileo. Peccato che ad installazione completata, accedendo al wizard di eclipse non era presente alcuna voce Axis 2. Cercando un po’ sul Web ho trovato questo 3d:

http://stackoverflow.com/questions/8075585/not-able-to-see-axis2-plugin-for-eclipse

in cui si afferma che sia la versione 1.6.1 che la versione 1.4.* sono bacate e quindi funzionano male (o non funzionano del tutto).

axis2.jpg

Proprio per tale motivo ho scaricato la versione 1.7.0 del plugin in questione, direttamente da questo link:

https://builds.apache.org/job/Axis2/lastStableBuild/org.apache.axis2$axis2.eclipse.codegen.plugin/artifact/org.apache.axis2/axis2.eclipse.codegen.plugin/1.7.0-SNAPSHOT/axis2.eclipse.codegen.plugin-1.7.0-SNAPSHOT.jar

Copiate il *.jar all’interno della directory dropin, riavviate eclipse e tutto funzionerà alla perfezione.

A presto.

PS: la 1.7.0 è una nightly release (versione di test), quindi se qualcosa non dovesse funzionare alla perfezione non preoccupatevi più di tanto.

Compilazione di un sorgente scritto in C che fa uso della libreria mysql.h

Circa un annetto fa ho sviluppato un piccolo client (scritto interamente in linguaggio C) che si interfaccia ad un database Mysql. Ora, per fare in modo che l’interfacciamento con il database possa avvenire, è necessario utilizzare la libreria mysql.h da inserire all’interno del codice sorgente mediante la classica direttiva #include.

Una volta completata la stesura del codice, è necessario scaricare la libreria che consentirà al nostro client di interrogare il database. Per fare ciò occorrerà utilizzare il comando (su piattaforma *buntu):

nightfly@nightbox:~$ sudo apt-get install libmysqlclient-dev

Installiamo il compilatore per generare l’eseguibile partendo dal sorgente:

nightfly@nightbox:~$ sudo apt-get install gcc

Successivamente, occorrerà compilare il sorgente sfruttando gcc e la libreria mysqlclient:

nightfly@nightbox:~$ sudo gcc -o nome_eseguibile sorgente.c -l mysqlclient

A questo punto possiamo avviare l’eseguibile digitando:

nightfly@nightbox:~$ ./nome_eseguibile

Ecco fatto. A presto.

JADE: piccolo esempio di comunicazione tra agenti

Ecco un piccolo esempio di comunicazione tra agenti JADE.

Agente mittente

import jade.core.Agent;
import jade.core.AID; //libreria necessaria per definire il destinatario
import jade.core.behaviours.*;
import jade.lang.acl.*;

class mioBehaviour extends CyclicBehaviour {
public void action() {
ACLMessage reply = myAgent.receive(); //è l'agente che riceve la risposta
if (reply != null) //appena ricevo una sola risposta non vuota blocco l'agente
{
System.out.println("Ho ricevuto la risposta " + reply.getContent() + " dall'agente " + reply.getSender() );
}
block();
}
}

public class sender extends Agent {
public void setup() {
System.out.println("Agente " +getAID()+ " inizializzato");
ACLMessage msg  = new ACLMessage(ACLMessage.INFORM); //definisco il tipo di messaggio da inviare
msg.setContent("Antani"); //definisco il contenuto del messaggio da inviare
msg.addReceiver( new AID( "destinatario", AID.ISLOCALNAME) ); //destinatario è il nickname che ho assegnato all'agente che riceverà il messaggio
send(msg); //invio il messaggio
System.out.println("Ho inviato il messaggio " + msg.getContent());
addBehaviour(new mioBehaviour()); //ascolto eventuali risposte
}
}

Agente destinatario

import jade.core.Agent;
import jade.core.AID;
import jade.core.behaviours.*;
import jade.lang.acl.*;

class myBehaviour extends CyclicBehaviour {
public void action() {
ACLMessage msg = myAgent.receive(); //è l'agente che riceve la risposta
if (msg != null) //appena ricevo una sola risposta non vuota blocco l'agente
{
System.out.println("Ho ricevuto il messaggio " + msg.getContent() + " dall'agente " + msg.getSender());
ACLMessage reply = msg.createReply();
reply.setPerformative( ACLMessage.INFORM );
reply.setContent("Ricevuto");
myAgent.send(reply);
}
block();
}
}

public class receiver extends Agent{
public void setup() {
System.out.println("Agente " +getAID()+ " inizializzato");
addBehaviour(new myBehaviour());
}
}

Per prima cosa vengono inizializzati i 2 agenti. Successivamente l’agente mittente invia un messaggio all’agente destinatario. Quest’ultimo, alla ricezione del messaggio, elaborerà una risposta da inoltrare all’agente mittente.

Da notare che la ricezione dei messaggi viene gestita mediante dei CyclicBehaviour (ovvero dei behaviour che vengono eseguiti continuamente), proprio per fare in modo che l’agente rimanga in ascolto.

Se l’agente non riceve alcun messaggio il behaviour viene immediatamente arrestato fino alla ricezione del prossimo messaggio non vuoto. Ciò viene implementato attraverso l’istruzione block().

Il codice comunque dovrebbe apparire piuttosto chiaro (sono presenti diversi commenti).

Per ulteriori delucidazioni non esitate a contattarmi. A presto.

C++: classi e puntatori

Ecco un piccolo esempio di come accedere agli attributi ed ai metodi di una classe mediante puntatore:

 Header

class Cliente
{
    public:
    char nome[20];
    char cognome[20];
    char indirizzo[20];
    char sesso;
    int eta;
    void InserisciNome();
    void InserisciCognome();
    void InserisciIndirizzo();
    void InserisciSesso();
    void InserisciEta();
};

Sorgente

#include <iostream.h>
#include "cliente2.h"

void Cliente::InserisciNome()
{
    cout << "Inserisci il nome del cliente" << endl;
    cin >> nome;
}

void Cliente::InserisciCognome()
{
    cout << "Inserisci il cognome del cliente" << endl;
    cin >> cognome;
}

void Cliente::InserisciIndirizzo()
{
    cout << "Inserisci l'indirizzo del cliente" << endl;
    cin >> indirizzo;
}

void Cliente::InserisciSesso() //:: operatore di scope
{
    while((sesso!='m') && (sesso!='f'))
    {
        cout << "Inserisci il sesso del cliente" << endl;
        cin >> sesso;
    }
}

void Cliente::InserisciEta()
{
    cout << "Inserisci l'età del cliente" << endl;
    cin >> eta;
}

void main()
{    

    Cliente *cliente;

    cliente=new Cliente();
    cliente->InserisciNome();
    cliente->InserisciCognome();
    cliente->InserisciIndirizzo();
    cliente->InserisciSesso();
    cliente->InserisciEta();

    cout << "Il cliente da te inserito è " << endl;
    cout << cliente->nome << endl;
    cout << cliente->cognome << endl;
    cout << cliente->indirizzo << endl;
    cout << cliente->sesso << endl;
    cout << cliente->eta << endl;
}

A presto!