Pages Menu
TwitterRss
Categories Menu

Posté par le 10 oct 2011 dans Puppet, Tutoriel | 13 commentaires

Utiliser Puppet avec un ENC (External Node Classifier)

Par défaut, la configuration des scénarios à jouer sur les serveurs clients de Puppet se fait au niveau du fichier “nodes.pp”. Cette solution atteint vite ses limites dès lors que vous avez de nombreux clients, paramètres, classes.
En effet, ce fichier devient vite assez dense rendant sa lecture difficile et la porte ouverte aux erreurs qui peuvent rapidement se transformer en actions non désirées sur vos clients ! De même, connaître d’un coup d’oeil l’ensemble des scénarios joués ainsi que leurs paramètres devient compliqué. Puppet propose d’autres solutions comme par exemple utiliser un LDAP ou un External Node Classifier (ENC), ce que nous allons voir dans cet article.
Je vais reprendre ici la définition d’un ENC que vous trouverez sur la documentation de Puppet tout en rajoutant des exemples précis sur l’utilisation de ressources de type “define” avec l’utilisation de tables de hachage en paramètre.


 

ENC ? Ce que vous devez savoir

Un ENC n’est rien d’autre qu’un programme appelé par le puppetmasterd au moment où un client se présente pour obtenir son catalogue. Puppet va alors appeler le programme défini avec pour paramètre le hostname (en fqdn). Ce programme peut être écrit dans le langage de votre choix. Dans votre ENC, vous pouvez utiliser le backend de votre choix comme source de donnée. La seule obligation est de fournir les informations en YAML. Il est également possible d’utiliser Puppet dashboard ou The Foreman comme ENC.

Il y a cependant quelques limites importantes quand on utilise un ENC :

  • Avec un ENC, vous ne pouvez que déclarer des classes (classiques et avec paramètre), définir des variables globales et définir un environnement (bien que visiblement toujours mal supporté à ce jour).
  • Vous ne pouvez donc pas utiliser, en l’état, vos ressources de type “define” !
  • Vous ne pouvez pas déclarer de relations entre vos différentes classes.
 

Le YAML produit par l’ENC

Je vais reprendre les exemples de Puppet en détaillant un peu. Imaginons 4 classes : common, puppet, dns, ntp.
Dans le “nodes.pp”, cela ressemblerait donc à :

node default {
include common
include puppet
include dns
include ntp
}

Votre ENC produira lui le résultat suivant :

classes:
  common:
  puppet:
  dns:
  ntp:

Avec une classe paramétrée

Si vous utilisez une classe paramétrée (Parameterized Classes), on passe de :

class { 'ntp':
    ntpserver => ['0.pool.ntp.org','1.pool.ntp.org']
  }

à :

classes:
    common:
    puppet:
    ntp:
        ntpserver:
                 - 0.pool.ntp.org
                 - 1.pool.ntp.org

Déclarer des variables globales

Il est possible de déclarer uniquement des variables globales (top scope variables). Dans votre “nodes.pp” ou “site.pp” :

$var1 = 1
$var2 = "test"
$var3 = ["a", 1]
$var4 = { 'ma_ref1' => { 'cle1' => 'valeur1', 'cle2' => 'valeur2' },\
          'ma_ref2' => { 'cle1' => 'valeur1', 'cle2' => 'valeur2' } }

Votre ENC affichera le YAML suivant :

parameters:
    var1: 1
    var2: test
    var3:
        - a
        - 1
    var4:
        ma_ref1:
              cle1: valeur1
              cle2: valeur2
        ma_ref2:
              cle1: valeur1
              cle2: valeur2
 

Comment faire pour les ressources “define”

Par défaut, vous indiquez uniquement les classes associées à vos clients. Si vous avez dans votre “nodes.pp” une référence à une ressource de type “define”, vous ne pouvez plus l’utiliser directement avec votre ENC. Pour être clair, imaginons un module “users” et son manifest “create” :

cat /etc/puppet/modules/users/manifests/create.pp
define users::create($user, $path) {
[...]
}

L’utilisation se fait de cette façon dans le “nodes.pp” :

user::create { 'user1':
     user => 'user1',
     path => '/home/user1'
}

user::create { 'user2':
     user => 'user2',
     path => '/home/user2'
}

Il n’y a pas de solution directe (à ce jour) pour reproduire cette fonctionnalité dans l’ENC. Vous ne pouvez pas définir les variables et appeler user::create comme on appelle une classe. Il faudra utiliser la fonction create_resources. Cette fonction est disponible nativement depuis la version 2.7 de Puppet. Pour les versions précédentes, il faudra passer par un module.
Grace à cette fonction, on va finalement utiliser une classe qui aura pour mission de construire dynamiquement les différents appels à user::create via create_resources.

Exemple :

Mon manifest create.pp reste identique. Il n’y a pas de modification à lui apporter. Il a toujours besoin de la variable $user et $path. On va créer une nouvelle classe manage :

cat /etc/puppet/modules/user/manifests/manage.pp
class user::manage ($user_list) inherits user {
  create_resources('user::create',$user_list)
}

On peut maintenant appeler la classe user::manage dans l’ENC. Il faut déclarer la variable user_list nécessaire à la classe user::manage pour finalement fournir à user::create les variables user et path. Le YAML produit ressemblera à :

classes:
   user::manage:
     user_list:
         user1:
           user: user1
           path: /home/user1
         user2:
           user: user2
           path: /home/user2
 

Un exemple en python

Un petit exemple d’un ENC basique en Python.

On obtient finalement la sortie suivante :

# /usr/local/bin/test_enc.py mon_host
classes:
  common: {}
  dns: {}
  ntp:
    ntpserver:
    - 0.pool.ntp.org
    - 1.pool.ntp.org
  puppet: {}
  user::manage:
    user_list:
      user1:
        path: /home/user1
        user: user1
      user2:
        path: /home/user2
        user: user2
parameters: {}

Et pour un serveur différent de mon_host :

# /usr/local/bin/test_enc.py serveur1
classes:
  common: {}
  dns: {}
  ntp:
    ntpserver:
    - 0.pool.ntp.org
    - 1.pool.ntp.org
  puppet: {}
parameters: {}

Vous pouvez également utiliser des classes classiques et des variables globales visibles, donc, au niveau de “parameters” dans votre ENC. Cela fonctionnera de la même façon mais l’inconvénient majeur de cette solution est que votre variable est exposée à toutes les classes. Tant que vous maîtrisez l’ensemble de vos modules et de vos classes, vous pouvez vous assurer de ne pas écraser cette variable. Dès lors que vous utilisez des modules/classes écrites par d’autres personnes, le risque devient beaucoup plus important. C’est une des raisons de l’apparition des Parameterized Classes que je vous encourage, ici, à utiliser.

 

Activer l’ENC

Il ne vous reste plus qu’à indiquer à Puppet d’utiliser votre ENC. Dans votre fichier de configuration /etc/puppet/puppet.conf, rajoutez :

[master]
  node_terminus = exec
  external_nodes = /usr/local/bin/test_enc.py

Relancez ensuite votre Puppet master.

C’est terminé ! Pour aller plus loin, vous pouvez par exemple fournir la liste des classes, des paramètres et les groupes de serveurs via une base de données alimentée par une IHM. L’extraction sera réalisée toujours par votre ENC du langage de votre choix et qui produira le YAML pour Puppet. La gestion de la configuration du serveur client deviendra aussi simple que de choisir au niveau de l’IHM une liste de classes et de paramètres.

13 commentaires

  1. Sinon il y a LDAP, bien plus simple

  2. Oui, j’en parle dans l’intro. Après plus simple, je sais pas ;-)

  3. Bonjour et merci pour ce document. Il m’éclaire un peu plus sur foreman et puppet. Néamoins j’ai une question car j’aimerais utiliser le module razoredge/network pour automatiser la création d’interfaces réseaux en “bonding”. Il y a une énorme quantité de “define” pour les alias, les bond, les bridges etcc… . Je pensais faire une fonction

    network::manage::alias à laquelle je passerais les éléments nécessaires à la création de l’alias et faire de même pour les bond, bridge etc.. Qu’en pensez vous ?

    Cordialement.

    James

  4. Bonjour,

    Quand vous parlez de fonction, je suppose que vous pensez créer une classe paramétrée network::manage::alias qui aurait un certain nombre de create_resources pour gérer vos bond, bridge & co ?
    En tout cas, je m’y prendrais comme ca oui. Et il n’y a pas d’autres solutions si vous souhaitez utiliser directement ce module via votre ENC.
    Cordialement,
    Rémi

  5. Bonjour Rémi

    L’exemple fournit par le créateur du module, pour ajouter un alias de carte réseau est le suivant :

    # === Sample Usage:
    #
    #   network::alias { ‘eth0:1′:
    #     ensure    => ‘up’,
    #     ipaddress => ’1.2.3.5′,
    #     netmask   => ’255.255.255.0′,
    #   }
    #

    Pour l’inclure dans mon ENC je pensais faire créer un fichier manage.pp dans /etc/puppet/environments/production/modules/network/manifests/  qui contiendrait :

    # cat /etc/puppet/environments/production/modules/network/manifests/manage.pp

    class network::manage::alias ($ensure, $ipaddress, $netmask) inherits network::alias {
    create_resources(‘network::alias’, $ensure, $ipaddress, $netmask)
    } Suis-je sur le bonne voie ?

    James

  6. Bonjour,

    Oui c’est presque ça ;-)
    Sauf que create_resources prend un hash, donc il faudrait plutôt faire un hash du genre (hash déclaré dans ton ENC) :

    $alias = { 'mon_alias1' => { ensure => 'up', ipaddress => '1.2.3.4', netmask => '255.255.255.0' } }

    et l’utiliser avec :

    # cat /etc/puppet/environments/production/modules/network/manifests/manage/alias.pp
    
    class network::manage::alias ($ensure, $alias) {
    create_resources('network::alias', $alias)
    } 
    

    2 petites remarques aussi :

    • si ta classe se nomme network::manage::alias, le chemin du manifest doit être /etc/puppet/environments/production/modules/network/manifests/manage/alias.pp.
    • pour moi le inherits est inutile.

    Voilà, à tester ;-)
    A+

  7. Ah ok. Merci encore pour ton aide.
    Juste encore une question, sans vouloir abuser … Pour la hash, je vois à peu près comment le déclarer mais par contre je n’arrive pas à importer ma nouvelle classe :

    /etc/puppet/environments/production/modules/network/manifests/manage/alias.pp

    dans mon ENC (formeman).  Je ne vois vraiment pas comment faire. Cela dit tu m’as déjà bien aidé donc un grand MERCI pour tout ça ….

     

  8. Ce poste remplace le précédent … j’y corrige quelques erreurs ..

     

    Ah ok. Merci encore pour ton aide.
    Juste encore une question, sans vouloir abuser … Pour le hash, je vois à peu près comment le déclarer mais par contre je n’arrive pas à importer ma nouvelle classe :

    /etc/puppet/environments/production/modules/network/manifests/manage/alias.pp

    dans mon ENC (formeman).  Dans l’onglet

    Puppet Classes -> Import new classes

    Foreman ne voit pas la classe présente dans le fichier manage.pp. Je ne vois vraiment pas comment faire.

    Cela dit tu m’as déjà bien aidé donc un grand MERCI pour tout ….

  9. Désolé mais il y’a longtemps que je n’ai pas utilisé Foreman, donc là je ne peux pas t’aider.
    Au plaisir ;-)

  10. Bonjour Rémi,

    j’ai finalement réussi à importer la classe dans foreman. Lorque je je veux appliquer les modifications sur un noeud, j’obtiens ce message :

    Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Invalid parameter netmask   => ’255.255.255.0′ on node machine1.example.comWarning: Not using cache on failed catalog
    Error: Could not retrieve catalog; skipping run

    Pourtant la définition de la classe alias est comme suit :

    define network::alias (
    $ensure,
    $ipaddress,
    $netmask,
    $gateway = ”,
    $userctl = false
    )

    Tu aurais une piste à me conseiller ?

    Merci.

    James

  11. Salut,

    Là je pense que la déclaration dans Foreman n’est pas bonne : Foreman passe netmask => ’255.255.255.0′ et pas netmask.

    A+

  12. Merci pour ton aide.

  13. C’est bon,  j’ai trouvé. Il fallait que la syntax de mon hash (dans foreman) soit au bon format.

    Par exemple, pour un alias, il faut que le hash soir exactement comme ça :

    em2:1:
    ensure: up
    ipaddress: 2.2.2.2
    netmask: 255.255.255.0

    et non pas

    ‘em2:1′ : {ensure => up, ipaddress => 2.2.2.2, netmask => 255.255.255.0, }

    En tout cas merci ton aide m’a été précieuse.

     

    A bientôt.

     

     

     

Poster une réponse

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *


8 − = six

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">