用Python解析puppet-api yaml
我正在创建一个脚本,需要解析Puppet输出的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清理的正则表达式?
你可以使用curl命令获取yaml输出,像这样:
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。但是我无法让它正常工作……
4 个回答
我只需要类的部分。所以我写了一个小的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())
我觉得问题的关键在于,Puppet使用了yaml的“标签”,而这些标签对默认的Python加载器来说有点困惑。特别是,PyYAML根本不知道怎么构建一个ruby对象,比如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感到困惑,因为它的复杂键(特别是用'? '来表示复杂键的开始)让事情变得复杂。
如果你找到一个好的解决办法,请告诉我……不过考虑到Puppet和ruby的紧密关系,直接用ruby写脚本可能会更简单。
我给PyYAML的创始人Kirill Simonov发了邮件,请他帮忙解析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