【问题标题】:Fetch specific output from stdout_lines and store it in a list从 stdout_lines 获取特定输出并将其存储在列表中
【发布时间】:2019-05-07 21:16:54
【问题描述】:

我正在自动化 F5 BIG IP 配置停用。我需要从执行的任意 bigip 命令中获取池名称。我已将输出的内容存储到寄存器中,并希望获取池名称并将其存储在列表中。

直到从执行命令的输出中获得宽ip信息(包含池名称)为止。

预期输出:-

我可以使用任何正则表达式来获取池信息吗,据我所知,我可以做一个点走来从 JSON 中获取确切的内容。我很确定当有多个池名称时输出不会相同。如果还有多个池,逻辑是什么?

---
- name: Playbook to verify the Wideip and fetch the GTM configuration
  hosts: test-gtm.com
  connection: local
  gather_facts: no

  tasks:

    - name: Verify WideIP Exists
      bigip_command:
        user: admin
        password: admin
        commands: "list gtm wideip a wideip"
        validate_certs: no
      register: output
      delegate_to: localhost

    - debug:
        var: output

    - name: Fetch the Pool name from the WideIP
      set_fact:
        gtm_pool: "{{ output.stdout_lines[0][1].split()[0] }}"

``

Output without the dot walk :-

"stdout_lines": [
            [
                "gtm wideip a test.abc.com {", 
                "    pools {", 
                "        test-pool {", 
                "            order 0", 
                "        }", 
                "    }", 
                "}"
            ]


Whole  debug output :-



        "output": {
        "changed": false, 
        "deprecations": [
            {
                "msg": "Param 'server' is deprecated. See the module docs for more information", 
                "version": 2.9
            }, 
            {
                "msg": "Param 'user' is deprecated. See the module docs for more information", 
                "version": 2.9
            }, 
            {
                "msg": "Param 'password' is deprecated. See the module docs for more information", 
                "version": 2.9
            }, 
            {
                "msg": "Param 'validate_certs' is deprecated. See the module docs for more information", 
                "version": 2.9
            }
        ], 
        "executed_commands": [
            "tmsh -c \\\"list gtm wideip a wideip\\\""
        ], 
        "failed": false, 
        "stdout": [
            "gtm wideip a wideip {\n    pools {\n        test-pool {\n            order 0\n        }\n    }\n}"
        ], 
        "stdout_lines": [
            [
                "gtm wideip a wideip {", 
                "    pools {", 
                "        test-pool {", 
                "            order 0", 
                "        }", 
                "    }", 
                "}"
            ]
        ]
    }
}





Debug output consisting of more than single pool:-

ok: [device.abc.com] => {
    "output": {
        "changed": false, 
        "deprecations": [
            {
                "msg": "Param 'server' is deprecated. See the module docs for more information", 
                "version": 2.9
            }, 
            {
                "msg": "Param 'user' is deprecated. See the module docs for more information", 
                "version": 2.9
            }, 
            {
                "msg": "Param 'password' is deprecated. See the module docs for more information", 
                "version": 2.9
            }, 
            {
                "msg": "Param 'validate_certs' is deprecated. See the module docs for more information", 
                "version": 2.9
            }
        ], 
        "executed_commands": [
            "tmsh -c \\\"list gtm wideip a wideip\\\""
        ], 
        "failed": false, 
        "stdout": [
            "gtm wideip a wideip {\n    description wideip\n    pool-lb-mode topology\n    pools {\n        test1-pool {\n            order 1\n        }\n        test2-pool {\n            order 0\n        }\n    }\n}"
        ], 
        "stdout_lines": [
            [
                "gtm wideip a wideip {", 
                "    description wideip", 
                "    pool-lb-mode topology", 
                "    pools {", 
                "        test1-pool {", 
                "            order 1", 
                "        }", 
                "        test2-pool {", 
                "            order 0", 
                "        }", 
                "    }", 
                "}"
            ]
        ]

【问题讨论】:

  • 您好,欢迎来到 SO。您的问题缺少主要元素。请参阅stackoverflow.com/help/how-to-ask。更准确地说:你的比赛结果是什么(抱歉,我们无法猜测)?你期待什么?您尝试过什么来实现您的目标,什么没有按预期工作(带有详细信息和错误消息)? ...
  • @Harsha Nalore 至少发布 "-debug: var=output" 的输​​出。请关注:How to create a Minimal, Complete, and Verifiable example.
  • @VladimirBotka:我已经粘贴了 stdout_lines 的输出,谢谢!
  • @Harsha Nalore 查看答案。这是你所期望的吗?如果没有将您的期望放入问题中。
  • 您在此处显示的输出不是该 debug 语句的完整输出。您能否显示更多输出,包括结果中的多个池?

标签: ansible


【解决方案1】:

稳健地解析该输出会很棘手,因为它实际上是一种专有的序列化格式。如果有办法让交换机只为您提供 JSON,这将使您的生活更轻松(我知道一些 Cisco 交换机有这个选项)。

要做到这一点,您需要编写一个能够理解输出格式的解析器。但是我们可以作弊,做很多假设,然后用几个正则表达式来解决这个问题。

我认为无论如何我们都需要在 Ansible 以外的工具中解析这个输出。在这种情况下,我通常只是在 Python 中编写一个过滤器模块,尽管有时“管道到awk”也可以。

为了解析此输出,我将以下内容组合在一起(并将其放入与我的剧本相邻的 filter_plugins/bigip.py):

import re

# Recall that "\s+" means "one or more whitespace characters"
re_pools = re.compile('''
gtm \s+ wideip \s+ a \s+ (\S+) \s+ { \s+
(?P<parameters>(\S+ \s+ \S+ \s+)*)
pools \s+ { \s+ (?P<pools>
(?:
\S+ \s+ {  \s+
[^}]* \s+
} \s+
)+ \s+
)
}
''', flags=re.VERBOSE)

re_pool = re.compile('''
(\S+) \s+ { \s+ [^}]* \s+ } \s+
''', flags=re.VERBOSE)


def filter_get_pool_names(v):
    combined = ' '.join(v.splitlines())
    res = re_pools.match(combined)

    if not res or not res.group('pools'):
        pools = []
    else:
        pools = re_pool.findall(res.group('pools'))

    return pools


class FilterModule(object):
    filter_map = {
        'get_pool_names': filter_get_pool_names,
    }

    def filters(self):
        return self.filter_map

上面定义了一个get_pool_names 过滤器。如果我在这样的剧本中使用它:

---
- hosts: localhost
  gather_facts: false
  vars:
    output:
      stdout: ["gtm wideip a test.abc.com {\n    pools {\n        test-pool1 {\n            order 0\n        }\n        test-pool2 {\n            order 1\n        }\n        test-pool3 {\n            order 2\n        }\n    }\n}"]

  tasks:
    - debug:
        msg: "{{ output.stdout.0 | get_pool_names }}"

我得到了结果:

TASK [debug] **********************************************************************************
ok: [localhost] => {
    "msg": [
        "test-pool1", 
        "test-pool2", 
        "test-pool3"
    ]
}

请注意,我在这里对输出的格式做出了假设,过滤器中的错误检查方式实际上并不多。但我认为它展示了解决这个问题的一种方法。

更新

关于:

当我试图将这些池名称传递给连续的剧本时,我可以看到它以 "[u\'test1-pool']" 的形式传递。但我只需要 "test1-pool" 。有什么建议吗?

过滤器的结果是名称列表。您正试图将其视为字符串。您需要循环遍历结果,如下所示:

- set_fact:
    pool_names: "{{ output.stdout.0 | get_pool_names }}"

- debug:
    msg: "processing pool: {{ item }}"
  loop: "{{ pool_names }}"

或引用列表中的单个元素:

- debug:
    msg: "the first pool is {{ pool_names.0 }}"

【讨论】:

  • 我在上述问题中添加了包含多个池的调试输出。请查看相同的内容,谢谢!
  • 我已更新过滤器以忽略 pools 部分之前的无关信息。
  • 我试图使用您提供的正则表达式进行匹配,但它抛出错误“gtm\s+wideip\s+a\s+(\S+)\s+{..\s+(? P (\S+\s\S+\s+)*)".P是什么意思?
  • 如何在尝试使用正则表达式匹配时忽略\n“换行符”?
  • @HarshaNalore,因为这个答案似乎不适合它,所以我发布了对正则表达式 here 的扩展讨论。希望对您有所帮助。
【解决方案2】:

问题中的内容是一系列的行,甚至不是JSON,所以不能简单地反序列化。有效的 JSON 将是

{ "gtm wideip a test.abc.com": { "pools": { "test-pool": { "order": 0 }}}}

例如,如果有这样的数据,下面的播放

vars:
  my_stdout_lines:
    gtm wideip a test.abc.com: 
      pools: 
        test-pool: 
          order: 0
        test-pool1: 
          order: 0
        test-pool2: 
          order: 0
tasks:
  - set_fact:
      my_list: "{{ my_stdout_lines|json_query('*.pools.keys(@)') }}"
  - debug:
      var: my_list

给予(删节):

"my_list": [
    [
        "test-pool2", 
        "test-pool", 
        "test-pool1"
    ]
]

【讨论】:

  • 这不是一个相关的例子:你的 my_stdout_lines 变量的内容是一个字典。问题中的内容是一系列的行,甚至不是JSON,所以不能简单的反序列化成字典。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-29
相关资源
最近更新 更多