Bcfg2 : la gestion de conf du bout des doigts

Il n'y a pas si longtemps que ça, je vous avais parlé d'un gestionnaire de conf, Puppet, qui s'appuyait sur son propre langage descriptif et jouait avec du Ruby. Après quelques essais ma foi assez pertinents, je me suis aperçue que Puppet était somme toute un peu lent : pour 3 packages (certes, il y avait du java dans le tas) et 2 conf, plus de 20 minutes étaient nécessaires pour mettre à niveau les nodes. Un autre fâcheux défaut est que, par défaut, il agrège toutes les instructions dans un même espace, sans tenir compte de la séquence selon laquelle ils sont définis. Pour le coup, les résultats sont pour le moins inattendus, surtout si on est dans un cas de figure où l'on doit passer une instruction (exemple : définition des dépôts officiels) avant une autre (installation de package). Au final, l'organisation des manifests finit, par défaut en un gigantesque mashup d'instructions. Pas trop sympa quand, naif, on n'y a pas pensé et passé des heures à tout bien organiser. Le seul moyen d'échapper à ce mécanisme est de passer par de lourdes définitions de dépendances. Il était temps de faire jouer AlternativeTo... Et de tomber sur bcfg2, un gestionnaire de conf s'appuyant sur du python ! Retour sur une conf aux petits oignons !

Principes de fonctionnement

Dans l'absolu, le fonctionnement de bcfg2 est assez similaire à celui de Puppet  : on a un agent qui interroge le serveur, et exécute -ou pas- les modifications. Après, étant un peu flemmarde et n'étant pas trop pour la copie illégitime de contenu, je vous renvoie sur le fonctionnement détaillé au poil du projet !

Un peu de technique

Installation

Comme d'habitude, on partira des sources : fraîches, dispo, et surtout dernière version :)
$ cd /usr/local/src
$ wget ftp://ftp.mcs.anl.gov/pub/bcfg/bcfg2-1.1.2.tar.gz
$ tar zxvf bcfg2-1.1.2.tar.gz
$ python setup.py build
$ python setup.py install
$ bcfg2-admin init
Store bcfg2 configuration in [/etc/bcfg2.conf]:
Location of bcfg2 repository [/var/lib/bcfg2]:
Input password used for communication verification (without echoing; leave blank for a random):
What is the server's hostname [bcfg2-server]:
Input the server location [https://bcfg2-server:6789]:
Input base Operating System for clients:
1: Redhat/Fedora/RHEL/RHAS/Centos
2: SUSE/SLES
3: Mandrake
4: Debian
5: Ubuntu
6: Gentoo
7: FreeBSD
: 5
Generating a 2048 bit RSA private key
.......+++
........+++
writing new private key to '/etc/bcfg2.key'
-----
Signature ok
subject=/C=US/ST=Illinois/L=Argonne/CN=bcfg2-server.k-tux.com
Getting Private key
Repository created successfuly in /var/lib/bcfg2
Bon, certificat un peu faussé parce que K-Tux, dans l'Illinois, c'est un peu tiré par les cheveux, mais bon, en s'en contentera :)

Configuration du service

La configuration du serveur en lui-même (je veux dire par là, le paramétrage du service, hein, pas celui concernant les nodes qui vont le solliciter) se situe tout simplement sous /etc et se compose de 3 éléments : la conf, la clé et le certificat en lui-même.
bcfg2-server $ ls /etc/bc*
bcfg2.conf  bcfg2.crt   bcfg2.key
Un petit start du service pour se rendre compte du truc :
bcfg2-server $ bcfg2-server start
Handled 14 events in 0.017s
service available at https://bcfg2-server.k-tux.com:6789
serving bcfg2-server at https://bcfg2-server.k-tux.com:6789
serve_forever() [start]
...Qui reste en foreground, mais c'est pas plus mal pour voir ce qui se passe ! La première conf est celle du Hello World : tester le service en local. Notre serveur sera donc, pour cette fois-ci du moins, également notre node. On lance donc la partie cliente, avec les options qui vont bien : -q (query) -v (verbose) -n (dry-run mode)
bcfg2-server $ bcfg2 -q -v -n
Loaded tool drivers:
Action     Chkconfig  POSIX      RPMng

Phase: initial
Correct entries:        0
Incorrect entries:      0
Total managed entries:  0
Unmanaged entries:      2754

Phase: final
Correct entries:        0
Incorrect entries:      0
Total managed entries:  0
Unmanaged entries:      2754
C'est un peu violent de se prendre 2754 Unmanaged entries dans la tronche, mais pas d'erreur, le serveur nous renvoie tout simplement un :
Generated config for bcfg2-server.k-tux.com in 0.001s
Client bcfg2-server.k-tux.com reported state clean
En clair, il nous reste 2754 packages à configurer.

Configuration du client en local

Il faut savoir, et c'est intéressant car c'est là que vous allez bosser, que la configuration des paramétrages pour les nodes tapant sur votre bcfg2-server se situe par défaut sous /var/lib/bcfg2. Là-dedans, vous avez tout une arborescence qui a été mise en place lors de l'installation :
bcfg2-server $ ls -lsaR  /var/lib/bcfg2
/var/lib/bcfg2:
total 40
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 48 root root 4096 2011-07-06 16:14 ..
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 Base
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 Bundler
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 Cfg
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 etc
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 Metadata
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 Pkgmgr
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 Rules
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 SSHbase

/var/lib/bcfg2/Base:
total 8
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..

/var/lib/bcfg2/Bundler:
total 8
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..

/var/lib/bcfg2/Cfg:
total 8
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..

/var/lib/bcfg2/etc:
total 8
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..

/var/lib/bcfg2/Metadata:
total 16
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..
4 -rw-r--r--  1 root root  111 2011-07-06 16:14 clients.xml
4 -rw-r--r--  1 root root  354 2011-07-06 16:14 groups.xml

/var/lib/bcfg2/Pkgmgr:
total 8
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..

/var/lib/bcfg2/Rules:
total 8
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..

/var/lib/bcfg2/SSHbase:
total 8
4 drwxr-xr-x  2 root root 4096 2011-07-06 16:14 .
4 drwxr-xr-x 10 root root 4096 2011-07-06 16:14 ..
Et par défaut, et bien vous avez vraiment peu de choses :) En  fait, seuls /var/lib/bcfg2/Metadata/clients.xml et groups.xml sont définis. Soit dit en passant, ce sont ces fichiers qui vont permettre d'associer un profil à un node, le profil pouvant ensuite se moduler à l'infini, par le biais de Bundle et d'autres subtilités. Jetons-y un oeil, pour le coup :
bcfg2-server $ cat /var/lib/bcfg2/Metadata/clients.xml
<Clients version="3.0">
<Client profile="basic" pingable="Y" pingtime="0" name="bcfg2-server.k-tux.com"/>
</Clients>
Ici, notre client (qui est aussi le serveur) est associé au profil par défaut; "basic". pinguable et pingtime parlent d'eux même. Pour ce qui est de groups.xml, c'est la même trame : une série de <Group> sont définis, par défaut vides et fonction de la distribution du client.
bcfg2-server $ cat /var/lib/bcfg2/Metadata/groups.xml
<Groups version='3.0'>
<Group profile='true' public='true' default='true' name='basic'>
<Group name='ubuntu'/>
</Group>
<Group name='ubuntu'/>
<Group name='debian'/>
<Group name='freebsd'/>
<Group name='gentoo'/>
<Group name='redhat'/>
<Group name='suse'/>
<Group name='mandrake'/>
<Group name='solaris'/>
</Groups>

A noter que 2 types de groupes existent : les publics, qui sont en fait les entrées par lesquelles les clients accèdent à leur catalogue, et les autres, qui ne sont pas directement visibles par les clients mais servent à mieux organiser les catégories.

Jouer avec les conf

Le premier jeu va consister à mettre à jour une conf sur un client.Les conf, les fichiers texte e, général sont toujours stockés dans /var/lib/bcfg2/Cfg. Pour jouer donc, tapons et modifions d'abord nos jolis xml :
bcfg2-server:/var/lib/bcfg2 $ cat Metadata/groups.xml
<Groups version='3.0'>
<Group profile='true' public='true' default='true' name='basic'>
<Group name='ubuntu'/>
</Group>
<Group name='debian'/>
<Group name='ubuntu'>
<Bundle name='release'/>
</Group>
</Groups>

/var/lib/bcfg2 $ cat Bundler/release.xml
<Bundle name='release' version='2.0'>
<Path name="/etc/ubuntu-release"/>
</Bundle>
Bien sûr, il faut tout de même mettre la conf dans un endroit précis, distinct du système (que se passerait-il si ce ne sont pas les mêmes OS ?). Pour cela, il suffit de créer un dir portant le path et le nom de la conf sous /var/lib/bcfg2/Cfg/. Par exemple, ici, on va :
bcfg2-server $ mkdir -p /var/lib/bcfg2/Cfg/etc/ubuntu-release/
bcfg2-server $ cp /etc/ubuntu-release /var/lib/bcfg2/Cfg/etc/ubuntu-release/
Enfantin, n'est-ce pas ? Le plus embêtant, c'est que j'ai dû redémarrer le service pour qu'il prenne en compte les changements.
bcfg2-server $ bcfg2-server start
service available at https://bcfg2.k-tux.com:6789
serving bcfg2-server at https://bcfg2.k-tux.com:6789
serve_forever() [start]
Handled 30 events in 0.020s
Lançons le client,qui dispose de son ubuntu-release correctement paramétré :
bcfg2-server $ bcfg2 -q -v -n
Loaded tool drivers:
Action     Chkconfig  POSIX      RPMng

Phase: initial
Correct entries:        1
Incorrect entries:      0
Total managed entries:  1
Unmanaged entries:      2754

Phase: final
Correct entries:        1
Incorrect entries:      0
Total managed entries:  1
Unmanaged entries:      2754
Et cette fois, qui a volontairement le ubuntu-release tronqué :
bcfg2-server $ bcfg2 -q -v -n
Loaded tool drivers:
Action     Chkconfig  POSIX      RPMng

Phase: initial
Correct entries:        0
Incorrect entries:      1
Total managed entries:  1
Unmanaged entries:      2759
Le dry-run mode ne faisant rien de spécial sur le système, il se contente de nous broncher une Incorrect entry. Virons le dry-run et voyons si la conf redescend bien comme il faut !
bcfg2-server $ bcfg2 -q -v
Loaded tool drivers:
Action     Chkconfig  POSIX      RPMng

Phase: initial
Correct entries:        0
Incorrect entries:      1
Total managed entries:  1
Unmanaged entries:      2759

Installing ConfigFile /etc/ubuntu-release
The Following Bundles have been modified:
release

Phase: final
Correct entries:        1
Incorrect entries:      0
Total managed entries:  1
Unmanaged entries:      2759
Clair, net, concis : notre conf est bien redescendue ! C'est bien joli tout ça, mais tant qu'à y être, autant voir les choses en grand :) Passons donc en multi clients !

Client - Serveur distincts

Notre client, lui, n'est plus sous ubuntu comme le serveur mais sur une autre machine , en Debian 6.0.1. En réalité, que se passe-t-il qui est différent de notre mode client=serveur ? Et bien, rien de spécial, si ce n'est qu'on a rajouté une entrée dans le clients.xml.
bcfg2-server $ /var/lib/bcfg2 $ cat Metadata/clients.xml
<Clients version="3.0">
<Client profile="basic" pingable="Y" pingtime="0" name="bcgf2-server.k-tux.com"/>
<Client profile="deb" pingable="Y" pingtime="0" name="bcfg2cli-deb.k-tux.com"/>
</Clients>
Notre groups.xml est aussi enrichi, puisque l'OS est différent et qu'il existe pile poil la bonne catégorie pour ce type de machine : bcfg2-server<Groups version='3.0'> <Group profile='true' public='true' default='true' name='basic'> <Group name='ubuntu'/> </Group> <Group profile='true' public='true' default='true' name='deb'> <Group name='debian'/> </Group> <Group name='ubuntu'> <Bundle name='release'/> </Group> <Group name='debian'/> </Groups> Au niveau du client, c'est un peu plus palpable :
bcfg2cli-deb $ apt-get update && apt-get upgrade
bcfg2cli-deb $ apt-get install bcfg2
Creating config file /etc/bcfg2.conf with new version
Forcément, la conf par défaut n'est pas valide, puisque pour fonctionner doit d'ailleurs directement taper sur le serveur (logique). Avant, on avait donc ça :
bcfg2cli-deb $ cat /etc/bcfg2.conf
[communication]
protocol = xmlrpc/ssl
password = foobat
# certificate = /etc/bcfg2.key
# key = /etc/bcfg2.key

[components]
bcfg2 = https://localhost:6789
Et après modification du general password et du serveur sur lequel taper :
bcfg2cli-deb $ cat /etc/bcfg2.conf
[communication]
protocol = xmlrpc/ssl
password = sF9Xlpuv
# certificate = /etc/bcfg2.key
# key = /etc/bcfg2.key

[components]
bcfg2 = https://bcfg2-server.k-tux.com:6789
On lance le client :
bcfg2cli-deb $ bcfg2 -qvn
No ca is specified. Cannot authenticate the server with SSL.
No ca is specified. Cannot authenticate the server with SSL.
Loaded tool drivers:
APT      Action   DebInit  POSIX

Phase: initial
Correct entries:        0
Incorrect entries:      0
Total managed entries:  0
Unmanaged entries:      365

Phase: final
Correct entries:        0
Incorrect entries:      0
Total managed entries:  0
Unmanaged entries:      365

No ca is specified. Cannot authenticate the server with SSL.
Au niveau du serveur, il existe des outils permettant de bien visualiser ce que l'on a, sans forcément rentrer dans le xml. Un peu comme... bcfg2-info :
bcfg2-server $ bcfg2-info "clients"
Handled 31 events in 0.012s
cli Client          | Profile
============================
bcfg2cli-deb   | deb
bcfg2-server  | basic

Installer des packages via bcfg2

Forcément, la conf ne fait pas tout, et on aimerait -moi en tout cas- pouvoir monter un noeud qui aurait été victime d'une installation très basique pour en faire un noeud fonctionnel, prêt à brasser du code. Contrairement à son petit nom qui sonne un peu restrictif somme toute, bcfg2 peut, comme Puppet, prendre en charge cette opération, aussi facilement que pour la partie configuration. Attaquons de suite par un exemple on ne peut plus simple ! Je rappelle que le noeud en question rentre dans le profil deb qui fait appel au group debian. Pour pouvoir accéder à la fonctionnalité installation / gestion des packages, il faut retoucher la conf de bcfg2 et rajouter le plugin Packages au bon endroit. Votre conf deviendra donc :
bcfg2-server $ cat /etc/bcfg2.conf

[server]
repository = /var/lib/bcfg2
plugins = Base,Bundler,Cfg,Metadata,Pkgmgr,Packages,Rules,SSHbase

[statistics]
sendmailpath = /usr/lib/sendmail
database_engine = sqlite3
# 'postgresql', 'mysql', 'mysql_old', 'sqlite3' or 'ado_mssql'.
database_name =
# Or path to database file if using sqlite3.
#<repository>/etc/brpt.sqlite is default path if left empty
database_user =
# Not used with sqlite3.
database_password =
# Not used with sqlite3.
database_host =
# Not used with sqlite3.
database_port =
# Set to empty string for default. Not used with sqlite3.
web_debug = True

[communication]
protocol = xmlrpc/ssl
password = sF9Xlpuv
certificate = /etc/bcfg2.crt
key = /etc/bcfg2.key
ca = /etc/bcfg2.crt

[components]
bcfg2 = https://bcfg2-server.k-tux.com:6789
Il faut également créer le répertoire et la configuration qu'il contiendra et qui permettra d'indiquer les dépôts sur lesquels chercher les packages. Dans ma config.xml, comme c'est une Debian Squeeze à qui je m'adresse, les dépôts sont spécifiques. Donc à modifier pour aligner sur vos noeuds.
bcfg2-server:/var/lib/bcfg2 $ mkdir /var/lib/bcfg2/Packages
bcfg2-server:/var/lib/bcfg2 $ vim Packages/config.xml

<Sources>
<APTSource>
<Group>debian-squeeze</Group>
<URL>http://ftp.de.debian.org/debian/</URL>
<Version>squeeze</Version>
<Component>main</Component>
<Component>multiverse</Component>
<Component>restricted</Component>
<Component>universe</Component>
<Arch>amd64</Arch>
</APTSource>
</Sources>
A présent, il faut indiquer quels packages, et pour qui.
bcfg2-server $ cat /var/lib/bcfg2/Metadata/groups.xml
<Groups version='3.0'>
<Group profile='true' public='true' default='true' name='basic'>
<Group name='ubuntu'/>
</Group>
<Group profile='true' public='true' default='true' name='deb'>
<Group name='debian'/>
</Group>
<Group name='debian'>
<Bundle name='python'/>
</Group>
<Group name='ubuntu'>
<Bundle name='release'/>
</Group>
</Groups>
Voila pour qui. Maintenant, le quoi en question. Pour les packages, on utilise ce que bcfg2 appelle des Bundles, qui permettent de grouper plusieurs définitions pour actions. Le tout étant, bien entendu, stocké à sa place dans /var/lib/bcfg2/Bundler/. Nous avons indiqué un Bundle nommé python, nous allons le mettre en forme :
bcfg2-server $ cat /var/lib/bcfg2/Bundler/python.xml
<Bundle name="python" version="2.0">
<Package name="python-openssl"/>
<Package name="python-pytools"/>
<Package name="python-setuptools"/>
</Bundle>
Le restart du service n'est pas très rassurant :
bcfg2-server:/var/lib/bcfg2 $ bcfg2-server start
Failed to read file /var/lib/bcfg2/Packages/cache/http:@@ftp.de.debian.org@debian@dists@squeeze@multiverse@binary-amd64@Packages.gz
Packages: File read failed; falling back to file download
Packages: Updating http://ftp.de.debian.org/debian/dists/squeeze/main/binary-amd64/Packages.gz
Packages: Updating http://ftp.de.debian.org/debian/dists/squeeze/multiverse/binary-amd64/Packages.gz
Packages: Failed to fetch url http://ftp.de.debian.org/debian/dists/squeeze/multiverse/binary-amd64/Packages.gz. code=404
Packages: Updating http://ftp.de.debian.org/debian/dists/squeeze/restricted/binary-amd64/Packages.gz
Packages: Failed to fetch url http://ftp.de.debian.org/debian/dists/squeeze/restricted/binary-amd64/Packages.gz. code=404
Packages: Updating http://ftp.de.debian.org/debian/dists/squeeze/universe/binary-amd64/Packages.gz
Packages: Failed to fetch url http://ftp.de.debian.org/debian/dists/squeeze/universe/binary-amd64/Packages.gz. code=404
Failed to read file /var/lib/bcfg2/Packages/cache/http:@@ftp.de.debian.org@debian@dists@squeeze@multiverse@binary-amd64@Packages.gz
Failed to update source
Traceback (most recent call last):
File "/usr/lib/python2.6/site-packages/Bcfg2/Server/Plugins/Packages.py", line 120, in setup_data
self.read_files()
File "/usr/lib/python2.6/site-packages/Bcfg2/Server/Plugins/Packages.py", line 406, in read_files
raise
File "/usr/lib/python2.6/site-packages/Bcfg2/Server/Plugins/Packages.py", line 403, in read_files
reader = gzip.GzipFile(fname)
File "/usr/lib/python2.6/gzip.py", line 79, in __init__
fileobj = self.myfileobj = __builtin__.open(filename, mode or 'rb')
IOError: [Errno 2] No such file or directory: '/var/lib/bcfg2/Packages/cache/http:@@ftp.de.debian.org@debian@dists@squeeze@multiverse@binary-amd64@Packages.gz'
The following plugins conflict with Packages;Unloading ['Pkgmgr']
Loading experimental plugin(s): Packages
NOTE: Interfaces subject to change
Handled 32 events in 0.012s
service available at https://bcfg2-server.k-tux.com:6789
serving bcfg2-server at https://bcfg2-server.k-tux.com:6789
serve_forever() [start]
Par contre, le service est opérationel ! Le client en dry-run, nous renvoie :
bcfg2cli-deb $ bcfg2 -qvn
No ca is specified. Cannot authenticate the server with SSL.
No ca is specified. Cannot authenticate the server with SSL.
Loaded tool drivers:
APT      Action   DebInit  POSIX
Package python-openssl not installed
Package python-pytools not installed
Package python-setuptools not installed

Phase: initial
Correct entries:        0
Incorrect entries:      3
Total managed entries:  3
Unmanaged entries:      364

In dryrun mode: suppressing entry installation for:
Package:python-openssl     Package:python-pytools     Package:python-setuptools

Phase: final
Correct entries:        0
Incorrect entries:      3
Package:python-openssl     Package:python-pytools     Package:python-setuptools
Total managed entries:  3
Unmanaged entries:      364

No ca is specified. Cannot authenticate the server with SSL.
Et en mode non dry-run, on a effectivement les installations qui se déroulent comme prévu :
bcfg2cli-deb $ bcfg2 -qv
No ca is specified. Cannot authenticate the server with SSL.
No ca is specified. Cannot authenticate the server with SSL.
Loaded tool drivers:
APT      Action   DebInit  POSIX
Package python-openssl not installed
Package python-pytools not installed
Package python-setuptools not installed

Phase: initial
Correct entries:        0
Incorrect entries:      3
Total managed entries:  3
Unmanaged entries:      364

The Following Bundles have been modified:
python

Phase: final
Correct entries:        3
Incorrect entries:      0
Total managed entries:  3
Unmanaged entries:      364

No ca is specified. Cannot authenticate the server with SSL.

bcfg2cli-deb $ dpkg -s python-openssl
Package: python-openssl
Status: install ok installed [...]
Et voilà ! Il ne reste plus qu'à gérer les 364 entrées restantes, plus celles que vous allez rajouter pour enrichir vos nodes :) Pour ma part, je dois encore paufiner la conf sur la partie authentification par certificat SSL et tant qu'à faire résoudre le pourquoi-qu'il-me-crache-des-erreurs-python-au-start. Mais tout ceci est une autre histoire ! Ah ! Et à toujours garder en tête : garder la conf par défaut est trèèèèèèèèèèèès dangereux (je pense au port 6789 en question) !!! Enjoy !!!
Vus : 1884
Publié par K-Tux : 59