Analisi di pupazzo-api yaml con Python
Domanda
Sto creando uno script che deve analizzare l'output yaml emesso dal pupazzo.
Quando eseguo una richiesta contro l'esempio https://puppet:8140/production/catalog/my.testserver.no Otterrò indietro uno yaml che assomiglia a qualcosa del tipo:
--- &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:
e così via...Il problema è che quando eseguo un yaml.load(yamlstream), otterrò un errore come:
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 ...
^
Per quanto ne so, questa parte &id001 è supportata in yaml.
C'è un modo per aggirare questo?Posso dire al parser yaml di ignorarli?Ho solo bisogno di un paio di righe dallo stream yaml, forse regex è mio amico qui?Qualcuno ha già eseguito regex di pulizia yaml?
Puoi ottenere l'output yaml con curl come:
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)
Ho anche trovato alcune informazioni a riguardo nella mailinglist dei pupazzi @ http://www.mail-archive.com/puppet-users@googlegroups.com/msg24143.html.Ma non riesco a farlo funzionare correttamente...
Soluzione
Ho inviato un'e-mail a Kirill Simonov, il creatore di PyYAML, per ottenere aiuto per analizzare il file YAML di Puppet.
Ha aiutato volentieri con il seguente codice.Questo codice serve per analizzare il registro Puppet, ma sono sicuro che puoi modificarlo per analizzare altri file YAML Puppet.
L'idea è creare il caricatore corretto per l'oggetto Ruby, quindi PyYAML può leggere i dati successivamente.
Ecco qui:
#!/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
Altri suggerimenti
Credo che il nocciolo della questione sia il fatto che pupazzo utilizza "tag" yaml per ruby-fu, e questo crea confusione nel caricatore Python predefinito.In particolare, PyYAML non ha idea di come costruire un ruby/object:Puppet::Resource::Catalog, il che ha senso, dato che è un oggetto ruby.
Ecco un collegamento che mostra alcuni vari usi dei tag yaml: http://www.yaml.org/spec/1.2/spec.html#id2761292
Ho superato questo problema con un approccio di forza bruta semplicemente facendo qualcosa del tipo:
cat the_yaml | sed 's#\!ruby/object.*$##gm' > cleaner.yaml
ma ora sono bloccato su un problema in cui il blocco *resource_table* confonde PyYAML con le sue chiavi complesse (l'uso di '?' per indicare l'inizio di una chiave complessa, in particolare).
Se trovi un bel modo per aggirare il problema, fammi sapere...ma dato quanto il pupazzo è legato a Ruby, potrebbe essere più semplice realizzare la sceneggiatura direttamente in Ruby.
Avevo solo bisogno della sezione delle lezioni. Così ho finito per creare questa piccola funzione Python per eliminarla ...
Spero sia utile per qualcuno :)
#!/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())
Semplice parser yaml:
with open("file","r") as file:
for line in file:
re= yaml.load('\n'.join(line.split('?')[1:-1]).replace('?','\n').replace('""','\'').replace('"','\''))
# print '\n'.join(line.split('?')[1:-1])
# print '\n'.join(line.split('?')[1:-1]).replace('?','\n').replace('""','\'').replace('"','\'')
print line
print re