2011-12-02 4 views
14

Je crée un script qui doit analyser la sortie yaml que la marionnette génère.Analyse de marionnette-api yaml avec python

Quand je demande une agains ne exemple https: // marionnettes: 8140/production/catalogue/my.testserver.no je récupérer une partie de YAML qui ressemble à:

--- &id001 !ruby/object:Puppet::Resource::Catalog 
    aliases: {} 
    applying: false 
    classes: 
    - s_baseconfig 
    ... 
    edges: 
    - &id111 !ruby/object:Puppet::Relationship 
     source: &id047 !ruby/object:Puppet::Resource 
     catalog: *id001 
     exported: 

et ainsi sur ... Le problème est quand je fais un yaml.load (yamlstream), je reçois une erreur comme:

yaml.constructor.ConstructorError: could not determine a constructor for the tag '!ruby/object:Puppet::Resource::Catalog' 
in "<string>", line 1, column 5: 
    --- &id001 !ruby/object:Puppet::Reso ... 
    ^

Pour autant que je sache, cette partie & ID001 est pris en charge en YAML.

Y a-t-il un moyen de contourner ce problème? Puis-je dire au parseur de yaml de les ignorer? Je n'ai besoin que de quelques lignes du flux yaml, peut-être que regex est mon ami ici? Quelqu'un a-t-il déjà effectué des regex de nettoyage de yaml?

Vous pouvez obtenir la sortie de YAML avec boucle comme:

curl --cert /var/lib/puppet/ssl/certs/$(hostname).pem --key /var/lib/puppet/ssl/private_keys/$(hostname).pem --cacert /var/lib/puppet/ssl/certs/ca.pem -H 'Accept: yaml' https://puppet:8140/production/catalog/$(hostname) 

Je trouve également quelques informations à ce sujet dans la liste de diffusion de marionnettes @http://www.mail-archive.com/[email protected]/msg24143.html. Mais je ne peux pas le faire fonctionner correctement ...

Répondre

23

J'ai envoyé Kirill Simonov, le créateur de PyYAML, pour obtenir de l'aide pour analyser Fichier YAML de marionnettes.

Il a volontiers aidé avec le code suivant. Ce code est pour l'analyse du journal de marionnettes, mais je suis sûr que vous pouvez le modifier pour analyser d'autres fichiers Puppet YAML.

L'idée est de créer le chargeur correct pour l'objet Ruby, puis PyYAML peut lire les données après cela.

va ici:

#!/usr/bin/env python 

import yaml 

def construct_ruby_object(loader, suffix, node): 
    return loader.construct_yaml_map(node) 

def construct_ruby_sym(loader, node): 
    return loader.construct_yaml_str(node) 

yaml.add_multi_constructor(u"!ruby/object:", construct_ruby_object) 
yaml.add_constructor(u"!ruby/sym", construct_ruby_sym) 


stream = file('201203130939.yaml','r') 
mydata = yaml.load(stream) 
print mydata 
+0

Je n'ai pas encore eu l'occasion de l'essayer, mais cela semble très prometteur et je pense que c'est exactement ce dont j'ai besoin. Je vais essayer de changer mon code actuel pour quelque chose comme ça au lieu de prendre le code yaml incomparable. Merci! – xeor

1

Je crois que le point crucial est que marionnette utilise des "tags" yaml pour ruby-fu, ce qui crée de la confusion avec le chargeur python par défaut. En particulier, PyYAML n'a aucune idée de comment construire un ruby ​​/ object: Puppet :: Resource :: Catalogue, ce qui est logique, puisque c'est un objet ruby.

est ici un lien montrant des différentes utilisations des balises YAML: http://www.yaml.org/spec/1.2/spec.html#id2761292

J'ai obtenu après cela dans une approche force brute en faisant simplement quelque chose comme:

cat the_yaml | sed 's#\!ruby/object.*$##gm' > cleaner.yaml 

mais maintenant je suis bloqué sur un problème où le bloc * resource_table * confond PyYAML avec ses clés complexes (l'utilisation de '?' pour indiquer le début d'une clé complexe, en particulier). Si vous trouvez un bon moyen de contourner cela, s'il vous plaît laissez-moi savoir ... mais étant donné que la marionnette à la hanche est liée à rubis, il peut être plus facile de faire script directement en rubis.

+0

Merci pour l'info. Je vais essayer lundi. Comme il n'y a pas beaucoup d'informations dont j'ai réellement besoin, je pourrais probablement être capable de me déshabiller beaucoup .. Ruby n'est pas une option, désolé ..: =) Commentera sur cette question si je découvre quelque chose d'intelligent. – xeor

1

J'avais seulement besoin de la section classes. Donc, j'ai fini par créer cette petite fonction python pour dépouiller dehors ...

Espoir pour quelqu'un de son USEFULL :)

#!/usr/bin/env python 

import re 

def getSingleYamlClass(className, yamlList): 
    printGroup = False 
    groupIndent = 0 
    firstInGroup = False 
    output = '' 

    for line in yamlList: 
     # Count how many spaces in the beginning of our line 
     spaceCount = len(re.findall(r'^[ ]*', line)[0]) 
     cleanLine = line.strip() 

     if cleanLine == className: 
      printGroup = True 
      groupIndent = spaceCount 
      firstInGroup = True 

     if printGroup and (spaceCount > groupIndent) or firstInGroup: 
      # Strip away the X amount of spaces for this group, so we get valid yaml 
      output += re.sub(r'^[ ]{%s}' % groupIndent, '', line) + '\n' 
      firstInGroup = False # Reset this 
     else: 
      # End of our group, reset 
      groupIndent = 0 
      printGroup = False 

    return output 

getSingleYamlClass('classes:', open('puppet.yaml').readlines())