Puppet : outillage et plugins
-
Avoir un serveur Puppet scalable, à jour et qui tourne comme une horloge c’est parfait.
Ça facilite la vie de beaucoup de monde dès que l’on connaît un peu son fonctionnement. Cependant, il y a des moments où des utilisateurs ne connaissent pas mais doivent intervenir sur les machines (oui … la vie est dure).
Et bien qu’au début cela peut être amusant d’observer quelqu’un modifier son fichier 10 fois avant de se demander pourquoi ses modifications ne sont pas conservées, à la longue ça l’est beaucoup moins. Du coup, une problématique se pose : Comment une personne ne parlant pas Puppet couramment peut savoir ce qui est géré par puppet ?
La réponse simple : il modifie son fichier et lance un run … si ça change c’est que c’est géré par puppet. Étonnamment ça ne séduit pas les masses.
La réponse dont on va parler : mettre à disposition des utilisateurs une commande permettant de lister les ressources gérées par Puppet et accessoirement ajouter 2-3 infos utiles.
Pour ne pas ajouter un script en plus ce qui serait bien serait de pouvoir ajouter des actions à la commande ‘puppet’.
Quelque chose du type :
puppet list file
Cela lève donc une autre question (oui … encore … ) : Comment ajouter des commandes à puppet ?
En fouillant un peu sur le site de puppetlabs, une page semble intéressante : http://puppetlabs.com/faces/comment-page-1
Les faces … ok… Je n’ai pas forcément trouvé beaucoup de docs (bon ok je n’ai pas cherché très longtemps non plus…), j’ai préféré regarder un peu comment sont fait les sous commandes existantes.
Le point positif: cela peut prendre la forme d’un module et de ce fait, ne nécessite pas de modifier du code de puppet, ou encore d’ajouter des fichiers qui seraient supprimés à la prochaine mise à jour.
Création d’une application…
Allez, créons notre sous commande list.
On va donc commencer par déclarer une sous commande. Pour cela il faut créer un fichier mon_module/lib/puppet/application/list.rb.
require 'puppet/application/face_base' class Puppet::Application::List < Puppet::Application::FaceBase end
Bon … rien de bien complexe … il suffit de mettre le nom de l’application où il faut … le reste ne bouge pas. Voyons ce que ça donne :
root@debtest2:~# puppet help Usage: puppet <subcommand> [options] <action> [options] Available subcommands: agent The puppet agent daemon apply Apply Puppet manifests locally [...] kick Remotely control puppet agent ! list ! Subcommand unavailable due to error. Check error logs. man Display Puppet manual pages. [...]
Donc il y a du progrès on voit bien notre commande. Mais qui est en erreur : normal nous n’avons pas défini de face.
Faisons le maintenant.
… et des commandes qui vont avec
Pour cela rien de sorcier : on va créer un fichier dans mon_module/lib/puppet/face/ qui se nomme comme la commande list.rb.
require 'puppet/face' Puppet::Face.define(:list, '0.0.1') do summary "View resources managed by puppet." description <<-'EOT' This subcommand provides a command line interface to list resources (File, Services, Package) managed by puppet and its associated manifest. It also list changed resources during the last run. EOT end
À ce stade nous avons … fait de la doc. On lie notre face à l’application via la méthode define, on donne un résumé de ce que fait la commande et une description plus longue. Ces informations sont accessibles via
puppet help
ou via le man généré de la commandepuppet man list
.Par contre, si on lance la commande ou si on souhaite avoir des informations sur les actions possibles, Puppet nous insulte car nous n’avons rien défini… C’est de bonne guerre.
Définissons alors une action avec quelques options (alors oui … je vais remettre tout le code car je n’aime pas les posts avec des extraits de code que l’on ne sait pas où mettre … ) :
require 'puppet/face' require 'puppet/util/terminal' Puppet::Face.define(:list, '0.0.1') do extend Puppet::Util::Colors summary "View resources managed by puppet." description <<-'EOT' This subcommand provides a command line interface to list resources (File, Services, Package) managed by puppet and its associated manifest. It also list changed resources during the last run. EOT action :all do summary 'List all type of resources' arguments "[<file>]" description <<-EOT Do not limit output for a specific type of resource EOT option "--changed" do summary "Only show changed resources" default_to { false } end option "--[no-]prefix" do summary "Do not show resource type at the begining of the line" default_to { true } end output = [] when_invoked do |*args| options = args.pop options[:path] = args[0] end when_rendering :console do |output| render_output(output) end end end
Encore une fois, c’est relativement explicite, on fait un bloc action avec le nom que l’on souhaite lui donner (ici all), un résumé, une description complète, les arguments si nécessaire et les options si besoin. Dans cette action on va déclarer un block
when_invoked
qui va faire le traitement voulu et un blocwhen_rendering
pour l’affichage (ici pour la console). Et voilà… nous avons une nouvelle sous commande, certes elle ne renvoie rien mais le bon côté c’est qu’elle ne renvoie pas d’erreur non plus…Détails utiles
Concernant les arguments et les options, pour définir un argument optionnel on le met entre crochets comme c’est le cas ici, sinon il sera obligatoire.
Pour les options c’est un peu pareil : entre crochets pour les arguments optionnels, rien pour les arguments obligatoires. On notera aussi la syntaxe
--[no-]prefix
qui permet de définir l’option--prefix
et--no-prefix
(et qui sera un booléen).Avec ce squelette, il ne reste plus qu’à faire le traitement et nous avons notre commande pour nos utilisateurs profanes.
Dans le traitement (que je ne détaillerai pas ici), nous allons parser le catalogue, la liste des ressources et également le rapport du dernier run.
Concernant l’affichage, il semble nécessaire de retourner une chaîne de caractères pour obtenir un affichage correct.
La méthode
render_output
est faite main pour retourner les informations sous forme de chaîne depuis un hash. Ce n’est pas une méthode de puppet.Résultat:
Voici notre résultat :
[root] >> puppet list all File:/etc/apt/apt.conf.d/02periodic common::updates.pp File:/etc/apt/apt.conf.d/50unattended-upgrades common::updates.pp [...] Service:mcollective mcollective::service.pp Service:postfix services::postfix.pp Service:puppet services::puppetclient.pp Service:rsyslog services::rsyslog.pp [...] Package:debian-archive-keyring common::packages.pp Package:debsecan common::security_debian.pp Package:dhcp_client common::packages.pp
en filtrant :
[root] >> puppet list all mcollective File:/etc/default/mcollective mcollective::agent_debian.pp File:/etc/mcollective/facts.yaml mcollective::agent_debian.pp File:/etc/mcollective/server.cfg mcollective::agent_debian.pp [...] Service:mcollective mcollective::service.pp Package:mcollective mcollective::agent_debian.pp
Lister les changements du dernier run :
[root] >> puppet list all --changed Service:mcollective (changed) mcollective::service.pp
L’aide :
[root] >> puppet help list USAGE: puppet list <action> This subcommand provides a command line interface to list resources (File, Services, Package) managed by puppet and its associated manifest. It also list changed resources during the last run. OPTIONS: --render-as FORMAT - The rendering format to use. --verbose - Whether to log verbosely. --debug - Whether to log debug information. ACTIONS: all List all type of resources file List all resources of type File package List all resources of type package service List all resources of type Service See 'puppet man list' or 'man puppet-list' for full help.
[root] >> puppet help list all USAGE: puppet list all [--changed] [--[no-]prefix] [<file>] Do not limit output for a specific type of resource OPTIONS: --render-as FORMAT - The rendering format to use. --verbose - Whether to log verbosely. --debug - Whether to log debug information. --changed - Only show changed resources --[no-]prefix - Do not show resource type at the begining of the line See 'puppet man list' or 'man puppet-list' for full help.
Cela ressemble beaucoup au script puppet-ls mais ce dernier utilise un catalogue au format yaml, et chez nous c’est du json.
Vers l’infini et au-delà…
Cette commande permet également de faire un alias pour lister les fichiers gérer par puppet dans le répertoire courant (nommé pls chez nous) :
(!)[root] [~] >> pls File:/root/.ssh common::ssh.pp File:/root/.ssh/authorized_keys common::ssh.pp (!)[root] [~] >> alias [...] alias pls='puppet list file $(pwd) | grep "File:$(pwd)"'
Et au passage un prompt indiquant si le dossier courant ou un fichier de ce dossier est géré par puppet en affichant un (!).
Et voilà, nos utilisateurs sont contents, du coup ils nous laissent tranquille, ce qui nous laisse plus de temps pour tenter de conquérir le monde.
Ah au passage une implémentation de ce dont il est question ici est disponible sur github. N’étant pas développeur pour deux sous, il est sûrement possible de l’améliorer mais pour autant ça fonctionne (et en prime il y a le prompt dans les files).
Voilà voilà … amusez-vous bien.