Синтаксический анализ puppet-api yaml с помощью python
Вопрос
Я создаю скрипт, которому нужно проанализировать выходные данные yaml, которые выводит марионетка.
Когда я выполняю запрос, например https://puppet:8140/production/catalog/my.testserver.no Я верну какой-нибудь yaml, который выглядит примерно так:
--- &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:
и так далее...Проблема в том, что когда я выполняю yaml.load (yamlstream), я получаю сообщение об ошибке типа:
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 ...
^
Насколько я знаю, эта часть &id001 поддерживается в yaml.
Есть ли какой-нибудь способ обойти это?Могу ли я сказать анализатору yaml игнорировать их?Мне нужна всего пара строк из потока yaml, может быть, регулярное выражение - мой друг здесь?Кто-нибудь раньше выполнял какие-либо регулярные выражения для очистки yaml?
Вы можете получить вывод yaml с помощью curl, например:
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)
Я также нашел некоторую информацию об этом в списке рассылки puppet @ http://www.mail-archive.com/puppet-users@googlegroups.com/msg24143.html.Но я не могу заставить его работать правильно...
Решение
Я отправил электронное письмо Кириллу Симонову, создателю PyYAML, чтобы получить помощь по разбору файла Puppet YAML.
Он с радостью помог со следующим кодом.Этот код предназначен для анализа журнала Puppet, но я уверен, что вы можете изменить его для анализа другого файла Puppet YAML.
Идея состоит в том, чтобы создать правильный загрузчик для объекта Ruby, после чего PyYAML сможет считывать данные.
Вот и все:
#!/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
Другие советы
Я полагаю, что суть вопроса заключается в том факте, что puppet использует yaml "теги" для ruby-fu, и это сбивает с толку загрузчик python по умолчанию.В частности, PyYAML понятия не имеет, как создать ruby / object:Puppet::Resource::Catalog , что имеет смысл, поскольку это объект ruby.
Вот ссылка, показывающая некоторые различные варианты использования тегов yaml: http://www.yaml.org/spec/1.2/spec.html#id2761292
Я преодолел это с помощью метода грубой силы, просто сделав что-то вроде:
cat the_yaml | sed 's#\!ruby/object.*$##gm' > cleaner.yaml
но теперь я застрял на проблеме, из-за которой блок * resource_table * сбивает PyYAML с толку своими сложными ключами (использование '?' в частности, для обозначения начала сложной клавиши).
Если вы найдете хороший способ обойти это, пожалуйста, дайте мне знать...но учитывая, насколько сильно the hip puppet привязан к ruby, возможно, вам будет проще создать скрипт непосредственно на ruby.
Мне нужен только раздел классов. Итак, я закончил тем, что создал эту маленькую функцию Python, чтобы ее выбросить ...
Надеюсь, это полезно для кого -то :)
#!/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())
Простой ямл анализатор:
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