Christophe Nowicki

July 6, 2008

Zoneminder plugins for Munin

I use ZoneMinder for my CCTV system and Munin for network monitoring.
But there is no ZoneMinder plugins for Munin, so I’ve decided to wrote theses plugins :

  • zm_events : get the number of events catched by monitors ;
  • zm_fps : get the number of frames per seconds ;
  • zm_hourly_events : get the number of events per hours.

You can download theses plugins from the subversion repository :

svn co http://svn.csquad.org/zoneminder_munin/

Enjoy and feel free to report bugs and suggest new graphs.

Filed under: Perl, ZoneMinder — cscm @ 8:30 am

May 30, 2008

Système de synchronisation conditionnel pour PostgreSQL

Il existe des outils tel que SQLSync permettant de synchroniser les données d’une base PostgreSQL. Mais cette outil, ne permet pas de faire une copie partiel des données.
Dans la plus part des cas, il faut programmer la synchronisation soit même, l’objectif de mon article est de vous proposer une solution simple et élégante.

La problèmatique

Des données sensibles se trouvent dans la base principale et il ne faut synchoniser qu’une partie de ces données vers les autres bases.

L’architecture dispose des caractèristiques suivantes :

  • Le schèma est identique sur chaque base ;
  • Les opèrations d’écriture se font seulement sur la base principale ;
  • Les bases sont accessibles en réseau.

La solution

Pour résoudre le problème, j’ai testé plusieurs approches avant de retenir l’utilisation d’un fonction de hachage dont voici le principe :

  • chaque table doit disposer d’un identifiant unique. (champ id de type SERIAL) ;
  • un programme récupère le couple id et la sum de hachage pour chaque table de chaque base qui doit être synchroniseés ;
  • il compare le couple et si celui-ci est diffèrent alors les données sont mise à jour.

Implementation au niveau de la base de données

Le calcul de la somme de hachage se fait un niveau de la base de données à l’aide d’un fonction :

SELECT id,md5 FROM get_table_md5('nom_de_la_table');
 id |               md5
----+----------------------------------
  4 | 9f3bcd2fae528244669613ae0466cc3c
  5 | 1f3bcd2fa24528244669613ae66cc3czd
...
(42 row)

Voici le code de la function PL/Perl:

CREATE LANGUAGE plperl;
CREATE TYPE table_md5 AS (id INTEGER, md5 TEXT);
CREATE OR REPLACE FUNCTION get_table_md5(varchar) RETURNS SETOF table_md5 AS $$
    my ($rv, $status, $nrows, $row);
    # Get Table OID
    $rv = spi_exec_query(\"SELECT c.oid AS oid FROM pg_catalog.pg_class c
        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
        WHERE c.relname LIKE '$_[0]';\");
    $status = $rv->{status};
    $nrows = $rv->{processed};
    return undef if ($nrows != 1);
    my $oid = $rv->{rows}[0]->{oid};
    # Get Table attributs
    $rv = spi_exec_query(\"SELECT attname,atttypid FROM pg_catalog.pg_attribute a
        WHERE a.attrelid = $oid AND a.attnum > 0 AND NOT a.attisdropped\");
    $status = $rv->{status};
    $nrows = $rv->{processed};
    return undef if ($nrows < 1);
    my $atts;
    my $atts_not_null;
    foreach my $rn (0 .. $nrows - 1) {
        if ($rv->{rows}[$rn]->{atttypid} == 16) {
            $atts .= ” (CASE WHEN $rv->{rows}[$rn]->{attname} THEN ‘t’ ELSE ‘f’ END) AS $rv->{rows}[$rn]->{attname}”;
        } else {
            $atts .= ” $rv->{rows}[$rn]->{attname}::text”;
        }
        $atts_not_null .= ” (CASE WHEN $rv->{rows}[$rn]->{attname} IS NULL THEN ‘’ ELSE $rv->{rows}[$rn]->{attname} END)”;
        $atts .= ‘,’ if ($rn != $nrows - 1);
        $atts_not_null .= ‘ || ‘ if ($rn != $nrows - 1);
    }
    # Calc MD5
    my $sql = “SELECT id, MD5($atts_not_null) AS md5 FROM (SELECT $atts FROM $_[0]) AS $_[0];”;
    $rv = spi_exec_query($sql);
    $nrows = $rv->{processed};
    foreach my $rn (0 .. $nrows - 1) {
        return_next({
            id  => $rv->{rows}[$rn]->{id},
            md5 => $rv->{rows}[$rn]->{md5}
        });
    }
    return undef;
$$ LANGUAGE plperl;

Pour utiliser cette fonction vous avez besoin du support du language de programmation PL/Perl dans Postgres, celui-ci se trouve dans le paquet Debian : postgresql-plperl-

La gestion des conditions

Le choix des données à synchroniser se fait individuellement pour chaque table à l’aide de la clause WHERE de la manère suivante :

SELECT id,md5 FROM get_table_md5('nom_de_la_table') WHERE id IN(SELECT id FROM nom_de_la_table WHERE nom_de_la_table.champ LIKE 'sync');
 id |               md5
----+----------------------------------
  1 | fdd56eabd4bb997e453e33f0022d46c1
(1 row)

Implementation du script de synchronisation

La synchronisation des données peut être réalisée à l’aide de n’importe quel language de programmation,
il suffit juste de disposer d’un accèss à la base de données.

Voici un exemple de script écrit par Emmanuel Saracco en PHP5 qui se repose sur les modules PEAR suivants :

  • DB : pour l’accès à la base de donnée.
  • Console_Getopt : pour la gestion de la ligne de commande.

Ce script nécessite un fichier de configuration, en voici un exemple.
Ce fichier contiens la configuration des diffèrentes base de données et les conditions sous-forme de clauses where.

Conclusion

Cette méthode de synchronisation fonctionne, elle est en production chez un client pour une base de données de plus de 100 tables, elle permet de synchronisé le contenu d’une base vers deux autres.
Les avantages de cette méthode sont les suivants :

  • le fait de pouvoir choisir le contenu de la clause where permet de réalisé des régles de synchronisation très complexes ;
  • les performances sont très bonnes car les transfert réseau sont limités ;
  • il est possible d’écrire le script de synchronisation avec n’importe quel language de programmation.
Filed under: Perl, PHP, PostgreSQL — cscm @ 2:30 pm

March 4, 2006

Munin, une alternative simple à Cacti?

Cela fait près d’un an et demi que j’utilise le logiciel Cacti pour monitorer l’activité réseau. Aussi bien dans le cadre de mon activité professionnelle que pour mon usage personnel. Mais j’ai récement découvert une alternative sérieuse : Munin. Celui-ci présente de nombreux avantages par rapport à Cacti et mérite d’être plus connue.

Les faiblesses de Cacti

A première vue, Cacti semble être le logiciel de monitoring réseau parfait, le rendu graphique est vendeur, l’installation et la prise en main est simple. Mes principales reproches concernnent la configuration des graphiques personnalisers, que je trouve beaucoup trop complexe. Effectiffment après avoir configuré les graphiques basiques ( utilisation processeur, mémoire, disque etc… ), il est très difficile d’ajouter ses propres graphiques. Cette partie du logiciel est bien trop complexe, les nombres d’extensions disponibles est trop faible et il faut par conséquent passer beaucoup de temps sur la personnalisation des graphiques. C’est pour cela qu’après avoir installer Munin, je regrette de ne pas l’avoir découvert plutôt, cela m’aurait économisé quelques heures de sommeil précieuses ;0)

Présentation de Munin

munin@home Fonctionnalités:

  • Simplicité
  • Interface web de consutation
  • Architecture client/serveur
  • Support de RRDTool, pour la génération de graphiques
  • Très nombreux plugins disponible
  • Envoi d’alertes vers Nagios
  • Support du protocole SNMP
  • Détection automatique des services présents sur la machine.

Une démonstration des fonctionnalité du logiciel est disponible à l’adresse suivante : http://munin.ping.uio.no/

Installation du serveur

Le serveur est décomposé en deux paquets dans Debian :

  • munin : le serveur de monitoring
  • munin-node : l’agent de monitoring et les diffèrents plugins

Pour l’installation d’un serveur minimal vous avez besoin d’installé le paquet munin (l’installation du paquet munin-node sera nécessaire dans le cas où vous voulez monitorer une machine à l’aide du protocole SNMP).

#aptitude install munin munin-node

L’ensemble des fichiers de configuration se trouve dans le répertoire /etc/munin:

  • munin.conf : configuration du serveur
  • munin-node.conf : configuration de l’agent
  • plugin-conf.d : répertoire des fichiers de configuration des plugins
  • plugins : répertoire des plugins activé ( la liste des plugins disponible se trouve dans /usr/share/munin/plugins)
  • templates : modèles des pages web pour l’interface web

Le processus de récupèration des données est réalisé par la commande munin-cron toutes les 5 minutes :

*/5 * * * * munin if [ -x /usr/bin/munin-cron ]; then /usr/bin/munin-cron; fi

Pour monitorer une machine, il suffit d’ajouter une entrée dans le fichier de configuration du serveur ( munin.conf ) :

[host.domain.name]
address 192.168.42.42 # l’adresse IP ou le nom DNS de la machine

Pour activer l’accés à l’interface web pour visionner les statistiques, il faut rendre le répertoire /var/www/munin accessible à l’aide du serveur Web Apache :

munin:/etc/apache/conf.d# cat munin.conf
<virtualhost munin>
ServerName munin.csquad.lan
ServerAlias munin.csquad.lan

ErrorLog /var/log/apache/munin/error.log
CustomLog /var/log/apache/munin/access.log common

DocumentRoot /var/www/munin
</virtualhost>

Installation des clients

Sur chaque client, il suffit d’installer le paquet munin-node :

#aptitude install munin-node

Modifier les régles d’accès dans le fichier de configuration /etc/munin/munin-node.conf :

allow ^192\.168\.42\.21$ #adresse du serveur

L’ensemble des services disponibles sur la machine sont détectés par la commande munin-node-configure :

# munin-node-configure –suggest | grep yes
cpu | no | yes
df | no | yes
df_inode | no | yes
entropy | no | yes
exim_mailqueue | no | yes
exim_mailstats | no | yes
forks | no | yes
if_ | no | yes +eth0 +eth1
if_err_ | no | yes +eth0 +eth1
interrupts | no | yes

Le paquet debian, active les plugins pour les services détectés en créant les liens dans le répertoire /etc/munin/plugins :

$ ls -l /etc/munin/plugins
lrwxrwxrwx 1 root root 28 Feb 20 18:14 cpu -> /usr/share/munin/plugins/cpu
lrwxrwxrwx 1 root root 27 Feb 20 18:14 df -> /usr/share/munin/plugins/df

Vous pouvez désactiver un plugin en supprimant son lien symbolique et l’activer en créant un lien symbolique :

# ln -s /usr/share/munin/plugins/apache_volume /etc/munin/plugins/

Simple, non?

Filed under: Debian, Perl — cscm @ 9:04 pm

February 11, 2006

Retour d’expérience sur la personnalisation du logiciel libre de gestion d’incident « Request Tracker »

Après plusieurs mois de travail sur la personnalisation de cet outils pour un client, j’ai le plaisir de vous presenter un article qui décrit notre travail.
Mais tout d’abord une rapide présentation du logiciel.

Request Tracker

RT est un logiciel libre de gestion d’incident, en anglais ticketing, il permet la prise en charge des demandes client. Lorsqu’un problème survient chez un de nos clients (plantage serveur, coupure de service, etc…) celui-ci envoie un mail ou bien téléphonne au support pour signaler l’incident.
Cet incident donne lieu à un ticket qui sera pris en charge par le support technique.
Grace à ce système :

  • Les clients peuvent suivre en temps réel, la résolution de l’incident
  • Communiquer avec le support
  • Le support facture les interventions
  • L’ensemble des interventions sont archivées.

Nous utilisons ce système chez Easter-eggs depuis près de 3ans avec succés et nous en sommes très content, comme beaucoup d’autres.
La mise en place de système est assez complexe et nécessite de bonnes compétances en administration système et en programmation Perl ( si vous voulez modifier l’outils pour l’adapter aux besoins spécifiques de votre entreprise).
Pour vous aider dans l’installation de l’outil, un article est paru dans le Linux Magazine France de ce mois-ci (Février 2006), mais celui-ci ne parle pas de la personnalisation de l’outils.

L’application de Suivi Qualité

L’Application de Suivi Qualité développée par Easter-eggs à partir de RT offre la possibilité de :

  • s’interfacer avec l’annuaire LDAP de l’entreprise ;
  • importer les bases de données d’un outil existant ;
  • effectuer des recherches dynamiques simplifiées ;
  • réaliser des imports CSV à partir d’un ERP ;
  • avoir une interface dynamique sans rechargement avec Javascript, méthodologie AJAX;
  • personnaliser les emails ;
  • gérer de manière automatisée les relances des tâches échues ;
  • extraire des statistiques d’activité.

Le retour d’expérience sur la modification

Mon article est disponible au format PDF à l’adresse suivante :

Retour d’expérience sur la personnalisation du logiciel libre de gestion d’incident « Request Tracker »

Filed under: Work, Perl — cscm @ 10:20 am

January 12, 2006

Gestion des exceptions SOAP en Perl avec les module SOAP::Lite et Error

Après une longue journée de travail, j’ai enfin réussi à faire fonctionner correctement le système de gestion d’exception de SOAP::Lite avec le module Error. Tout d’abord quelques explications.

Système de gestion d’exception

Tout programme en exécution peut être sujet à des erreurs, pour lesquels des stratégies de détection et de réparation sont possibles. Ces erreurs ne sont donc pas des bogues des programmes, mais des conditions particulières, on parle aussi de conditions exceptionnelles ou exceptions dans le déroulement normal d’une partie d’un programme.

Module Perl Error

Le module CPAN Error permet de gérér les exceptions en Perl. Il propose une interface objet semblable à celle disponible dans des langages comme Java ou bien C#. Le module repose sur la fonction die du langage.

SOAP::Lite

Le projet SOAP::Lite permet de prendre en charge l’ensemble des protocoles lier aux Web Services (SOAP, WSDL et UDDI). L’ensemble des fonctionnalitées offertes sont extrèment riches et puissantes. Vous pouvez aussi bien faire un serveur qu’un client en Perl et vous interfacer avec des Service écrit dans de nombreaux autres langages.

Entre la théorie et la pratique …

L’ensemble des interactions sont décrites dans les spécifications des protocoles de Web Services. Mais comme présque toujours en informatique, chaque constructeur, langage ou implémentation apporte son lot de spécificitées.

Dans le cas du langage Perl, les deux principaux sont l’absence :

  • de typage
  • d’un système de gestion d’exception

Le permier problème se controurne à l’aide de la déclaration des type dans un fichier WSDL.
Pour le second, j’ai eu un peu plus de mal avant de trouver la solution.

A propos du code source de SOAP::Lite

Ce n’est vraiment pas un exemple à suivre :

  • très peu de commentaires dans le code
  • seul fichier de 5000 lignes
  • le mode de debug trace n’est pas assez verbeux
  • on se demande souvent si l’auteur du module n’a pas coder sous l’imfluence de certaines drogues

L’anomalie de fonctionnement

Lorsqu’une exception est levée à l’aide de la fonction throw dans un module Perl elle créer un objet pour indiquer des informations sur le contexte de l’erreur. L’objet le plus simple est Error::Simple

sub foo {
   throw Error::Simple( \"A simple error\");
}

Il est possible de créer vos propres objets en utilisant l’héritage en utilisant l’objet Error comme base.

La framework SOAP::Lite détourne la fonction die et renvoie une exception SOAP qui contiens un objet. La problème est que cela ne fonctionne pas correctement :

 SOAP Fault :
        Fault String : Application error
        Fault Detail :  {
          ‘Error__Simple’ => {
                             ‘-file’ => ‘Demo.pm’,
                             ‘-text’ => ‘A simple error’,
                             ‘-package’ => ‘Demo’,
                             ‘-line’ => ‘9′
                           }
        };
	
        Fault Actor  : http://localhost:4242/

Le contenu de Fault Detail n’est pas un objet MAIS une table de HASH.
De plus un message d’erreur du côte du serveur vous indique qu’il y’a visiblement un problème avec la serialisation :

 Use of uninitialized value in sprintf at /usr/share/perl5/SOAP/Lite.pm line 814.
Cannot encode 'namesp1:something' element as 'hash'. Will be encoded as 'map' in  stead

Après quelques heures de tatonnement et de recherche dans le code source de SOAP::Lite, j’ai trouver la source du problème.

Les attributs du module Error contiennent tous un préfixe qui fait échouer la serialization :

$VAR1 =  bless( {
        '-file' => '...',
        '-text' => '...',
        '-package' => '...',
        '-line' => '...'
        }, 'BadError')

La fonction résponsable de ce bug est SOAP::Serializer::encode_hash.
Une expression régulier empeche la serialisation d’un caractère ‘-’ dans le flux XML.

J’ai donc déciser de supprimer le caractère ‘-’ dans le nom des attributs à l’aide du boût de code suivant :

sub encode_hash {
	
my($self, $hash, $name, $type, $attr) = @_;
	
  while(my ($k, $v) = each (%$hash))
  {
    if ($k =~ m/^-/)
    {
        $k =~ s/^-//;
        $hash->{$k} = $v;
        delete $hash->{'-'.$k};
    }
  }
	
  if ($self->autotype && grep {!/$SOAP::Constants::ELMASK/o} keys %$hash) {

Et Voilà !

Cela fonctionne enfin correctement :

 SOAP Fault :
        Fault String : Application error
        Fault Detail :  {
                             'detail' => bless ( {
                             '-file' => 'Demo.pm',
                             '-text' => 'A simple error',
                             '-package' => 'Demo',
                             '-line' => '9'
                           }, 'Error::Simple')
        };
	
        Fault Actor  : http://localhost:4242/

Je vais contacter l’auteur du module pour trouver une solution plus propore ;0)
Mais en attendant celle-ci fonctionne correctement.

Filed under: Programmation, Perl — cscm @ 9:20 pm

November 26, 2005

Génération dynamique de fichiers WSDL avec Perl

Dans le cadre de mon travail, je devais rendre accesible via des Web Services un ensemble de modules écrits en Perl.

Pour que les interfaces d’un module soient accessible via SOAP, il faut les décrire à l’aide du langage WSDL.

Le format WSDL est basé sur XML, il est relativement difficile de le comprendre et encore plus de l’écrire à la main.
C’est pour cette raison que la plupart des langages de programmation proposent des outils de géneration automatique.

Le principale problème que l’ont peut rencontrer avec ce genre d’outils est le manque de contrôle.

Pour palier à ce problème, Perl propose une solution remarquable : décrire l’interface à l’aide de commentaires directement dans le code.

Effectivement à l’aide du module Pod::WSDL, il est possible d’extraire les informations nécessaires pour créer le fichier de déscription d’interface.

Par exemple :

=begin WSDL

_IN $bar $string description du paramètre
_RETURN $string
_FAULT Error::Simple Le type d'éxception déclancher en cas d'erreur
_DOC description rapide du fonctionnement de ma fonction

=cut

sub foo ($) {
my $bar = shift;
return $bar;
}

Ensuite, il ne reste plus qu’à charger le module précédant et extraire les informations pour produire le fichier WSDL, à l’aide du bout de code suivant:

use Pod::WSDL;

my $pod = new Pod::WSDL(
source => 'My::Server',
location => 'http://localhost/My/Server',
pretty => 1,
withDocumentation => 1);

print $pod->WSDL;

Celui-ci vous affiche le fichier WSDL, directement sur la sortie standard.

Les fichiers produits par Pod::WSDL sont conformes aux spécifications et fonctionnent parfaitement.
Je les ai testé avec SOAP::Lite et le framework Apache Axis.

Nous avons une jolie solution pour créer des fichiers WSDL directement lors de l’installation de nos modules.

Néanmoins j’ai voulu pousser le concept de génération dynamique un peu plus loin.

Tout simplement parce que mon interface SOAP est séparée des modules qu’elle met à disposition (dans deux paquets différents).

Dans Web Services, il y a la notion de Web. J’ai donc mis à contribution le framework Mason, qui est un outil de template permetant de faire des sites web dynamiques en Perl.

Celui-ci dispose deux fonctionnalités intéressantes :

  • un système de cache ;
  • un système de gestion de page non disponible.

Le système de gestion de page non disponbile, permet de créer des pages web dynamiquement en fonction de l’url demander.

Le fichier dhandler suivant permet de créer dynamiquement un fichier WSDL en fonction du nom du module Perl:

< %once>
use Pod::WSDL;
use strict;
< %once>
< %init>
my $arg = $m->dhandler_arg;
$arg =~ s/\.wsdl//;
my $pod;

eval {
$pod = new Pod::WSDL(
source => $arg,
location => ‘http://localhost/soapEndPoint’,
pretty => 1,
withDocumentation => 0
);
};
if ($@) {
return 404;
}
$m->out($pod->WSDL);
< /%init>

Par exemple pour récupèrer le fichier WSDL pour le module Test, il suffit d’aller sur l’url : http://localhost/Test.wsdl.

L’opèration de géneration est rélativement rapide, mais dans le cas d’un service fortement solicité, il faut mettre en place un système de cache, comme celui décrit dans la documentation de Mason : DATA CACHING

Voilà

Filed under: Programmation, Perl — cscm @ 10:22 pm

Powered by WordPress