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 _monmodule/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 commande puppet 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 bloc when_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.