用Python解析puppet-api yaml

14 投票
4 回答
4781 浏览
提问于 2025-04-17 07:32

我正在创建一个脚本,需要解析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 个回答

1

我只需要类的部分。所以我写了一个小的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())
1

我觉得问题的关键在于,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写脚本可能会更简单。

24

我给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

撰写回答