NOM
RackMan::Manual - Fonctionnement et extension de RackMan
DESCRIPTION
Ce document décrit le fonctionnement de RackMan et les mécanismes pour étendre le logiciel par l'ajout de nouveaux formats et rôles.
RackMan est un ensemble de modules destinés à utiliser les données provenant d'une base RackTackes, afin de générer les fichiers de configuraton correspond pour différents logiciels. Sa principale interface est la commande rack(1).
CONFIGURATION
La configuration de RackMan même est gérée au travers du module RackMan::Config
, qui permet la surcharge de chaque paramètre du fichier général rack.conf par la valeur définie dans un éventuel fichier rack.local.conf propre à chaque équipement. De plus certaines valeurs (par exemple %name%
) sont interprétées à la volée pour être remplacées par le nom de l'équipement en cours.
Exemples
Pour récupérer le paramètre [general]/timezone
défini ainsi dans le fichier de configuration :
[general]
timezone = Europe/Paris
il suffit du code suivant
my $config = RackMan::Config->new(-file => "rack.conf");
my $timezone = $config->val("general", "timezone");
Dans le cas d'un paramètre qui doit typiquement être surchargé pour chaque équipement, il est attendu que le chemin vers les configurations personnalisées doit défini avec le marque %name%
, ainsi, dans rack.conf :
[general]
path = configs/%name%
et dans configs/squeak.infra/rack.local.conf :
[device:server:hp_proliant]
ilo_password = squeeee
admin_password = en4bl3d
license_key = 482173267218579
Il faut associer l'objet RackMan::Device
correspondant à l'équipement en cours de traitement à l'objet RackMan::Config
afin qu'il puisse résoudre le nom :
my $rackdev = RackMan->device("squeak.infra");
$config->set_current_rackobject($rackdev);
my $ilo_pwd = $config->val("device:server:hp_proliant", "ilo_password");
ARCHITECTURE
Le but de RackMan étant de générer des fichiers de configuration, il est centré autour de la notion d'équipement au sens générique du terme, représenté par la classe RackMan::Device
. Celle-ci récupère à la demande les informations à propos de l'équipement depuis la base RackTables, au travers de l'ORM DBIx::Class
, instancié sous la forme des modules RackTables::Schema
.
Lors de l'instanciation de l'object, on compose le rôle correspondant au type de l'équipement (PDU, Switch, Server) afin d'une part de définir par la méthode formats()
les formats de configuration associés à cet équipement (Bacula, Cacti, DHCP, Kickstart, LDAP, Nagios), et d'autre part de spécialiser l'objet par la composition d'un second rôle sachant dialoguer avec le matériel pour le configurer.
API POUR UN FORMAT
Un format est une classe qui doit simplement fournir une méthode de classe write()
qui est en charge de réaliser l'opération de configuration, quelle qu'elle soit. Cette méthode recevra en argument un hashref avec les clés suivantes :
rackman
- l'objectRackMan
rackdev
- l'objectRackMan::Device
correspondant à l'équipement en cours de traitementverbose
- un booléen indiquant s'il faut être verbeux ou pas
Exemple : squelette d'un format
package RackMan::Format::Whatever;
use strict;
use RackMan::File;
use constant {
CONFIG_SECTION => "format:whatever",
DEFAULT_PATH => "/etc/whatever/hosts",
};
#
# write()
# -----
sub write {
my ($class, $args) = @_;
# récupération des arguments
my $rackdev = $args->{rackdev};
my $rackman = $args->{rackman};
# objet RackMan::File pour gérer le fichier à créer
my $file = RackMan::File->new;
# récupération du nom de l'équipement
my $name = $rackdev->object_name;
# génération du contenu du fichier proprement dit
$file->add_content(...);
# positionnement du nom et du chemin du fichier
$file->name("$name.conf");
$file->path($rackman->config->val(CONFIG_SECTION, "path", DEFAULT_PATH));
# écriture du fichier sur disque, en utilisant un SCM pour
# le versionnement
my $scm = $rackman->get_scm({ path => $file->path });
$scm->update;
$file->write;
$scm->add($file->name);
$scm->commit($file->name, "generated by $class");
}
API POUR UN RÔLE D'ÉQUIPEMENT
Un rôle d'équipement est un rôle (Moose::Role
) qui est composé sur une instance de RackMan::Device
depuis le rôle de type d'équipement (voir la méthode specialise()
). Il doit fournir les méthodes write_config()
, diff_config()
et push_config()
qui sont les implémentations respectives des actions write
, diff
et push
. Ces méthodes d'objet recevront en argument un hashref avec les clés suivantes :
rackman
- l'objectRackMan
verbose
- un booléen indiquant s'il faut être verbeux ou pas
Il peut fournir une méthode tmpl_params()
qui renvoie un hash avec des paramètres additionnels pour RackMan::Template
.
Exemple : squelette d'un rôle d'équipement
package RackMan::Device::HardwareType::HardwareModel;
use Moose::Role;
use RackMan::File;
use RackMan::Utils;
use namespace::autoclean;
use constant {
CONFIG_SECTION => "device:hw_type:hw_model",
CONFIG_FILENAME => "",
};
#
# write_config()
# ------------
sub write_config {
my ($self, $args) = @_;
# récupération des arguments
my $rackman = $args->{rackman};
# génération du fichier de configuration depuis la base de données
my $config = $self->fetch_from_database($args);
# positionnement du nom et du chemin du fichier
$config->name(CONFIG_FILENAME);
$config->path($rackman->config->val(general => "path"));
# écriture du fichier sur disque, en utilisant un SCM pour
# le versionnement
my $scm = $rackman->get_scm({ path => $config->path });
$scm->update;
$config->write;
$scm->add($config->name);
$scm->commit($config->name, "generated by ".__PACKAGE__);
}
#
# diff_config()
# -----------
sub diff_config {
my ($self, $args) = @_;
# récupération des arguments
my $rackman = $args->{rackman};
# récupération de la configuration courante de l'équipement
my $current = $self->fetch_from_device($args);
# génération du fichier de configuration depuis la base de données
my $expected = $self->fetch_from_database($args);
# calcul des différences entre les deux fichiers
my @cfg_current = split $/, $current->content;
my @cfg_expected = split $/, $expected->content;
my @diff = diff_lines(\@cfg_current, \@cfg_expected);
# affichage du résultat
print @diff;
# positionnement du statut du processus
RackMan->set_status(1) if @diff;
}
#
# push_config()
# -----------
sub push_config {
my ($self, $args) = @_;
# récupération des arguments
my $rackman = $args->{rackman};
my $path = $rackman->config->val(general => "path");
my $config = RackMan::File->new;
# lecture du fichier de configuration tel qu'il a été
# écrit sur disque par write_config()
$config->name(CONFIG_FILENAME);
$config->path($path);
$config->read;
# transfert de la configuration à l'équipement
$self->store_to_device($args, $config);
}
#
# fetch_from_database()
# -------------------
sub fetch_from_database {
my ($self, $args) = @_;
my $rackman = $args->{rackman};
my $config = RackMan::File->new;
# génération du fichier de configuration à partir des
# informations de l'équipement dans RackTables.
# le fichier peut être construit morceau par morceau à
# coups d'appels à $config->add_content(...)
return $config
}
#
# fetch_from_device()
# -----------------
sub fetch_from_device {
my ($self, $args) = @_;
my $rackman = $args->{rackman};
my $config = RackMan::File->new;
# discussion avec le périphérique pour récupérer sa
# configuration, et la stocker dans l'objet $config
return $config
}
#
# store_to_device()
# ---------------
sub store_to_device {
my ($self, $args) = @_;
my $rackman = $args->{rackman};
my $config = RackMan::File->new;
# discussion avec le périphérique pour récupérer sa
# configuration, et la stocker dans l'objet $config
return $config
}
MODULES UTILITAIRES
RackMan est fourni avec des modules offrant des abstractions de haut niveau pour gérer certaines tâches.
RackMan::File
Ce module offre une abstraction de fichier, afin de gérer facilement le contenu et l'emplacement d'un fichier.
use RackMan::File;
my $file = RackMan::File->new(name => "lipsum.txt");
$file->add_content("Lorem ipsum dolor sit amet");
$file->write;
RackMan::SCM
Ce module offre une abstraction de SCM, pour effectuer les opérations basiques quel que soit l'outil sous-jacent. Il est conseillé d'utiliser la méthode get_scm()
d'un objet RackMan
pour avoir le SCM attendu par l'utilisateur.
my $scm = $rackman->get_scm();
$scm->add($path);
$scm->commit($path, "added $path");
Combiné avec un object RackMan::File
:
my $file = RackMan::File->new({ name => ..., path => ... });
$file->add_content(...);
my $scm = $rackman->get_scm({ path => $file->path });
$scm->update;
$file->write;
$scm->add($file->name);
$scm->commit($file->name, "added ".$file->name);
RackMan::Template
Ce module est un petit système de templating s'appuyant sur HTML::Template
et HTML::Template::Filter::TT2
. Rien de bien sophistiqué, mais c'est néanmoins utile.
use RackMan::Template;
my $tmpl = RackMan::Template->new(filename => "dhcp.tmpl");
$tmpl->param(
host_name => "zeruel", host_ipaddr => "192.168.0.43",
host_macaddr => "78:2B:CB:B3:E7:F4", gateway => "192.168.0.254",
);
print $tmpl->output;
avec un fichier dhcp.tmpl comme suit
host [% host_name %] {
option routers [% gateway %];
hardware ethernet [% host_macaddr %];
fixed-address [% host_ipaddr %];
}
RackMan::Utils
Ce module contient des fonctions utilitaires, pour le moment seulement la fonction diff_lines()
, qui sert à déterminer et à fournir un diff au format unifié entre deux sources de données.
use File::Slurp;
use RackMan::Utils;
my @old_conf = read_file(...);
my @new_conf = read_file(...);
my @diff = diff_lines(\@old_conf, \@new_conf);
AUTEUR
Sébastien Aperghis-Tramoni (sebastien@aperghis.net)