首页 > 解决方案 > 从 stdout_lines 获取特定输出并将其存储在列表中

问题描述

我正在自动化 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", 
                "        }", 
                "    }", 
                "}"
            ]
        ]

标签: ansible

解决方案


Parsing that output robustly will be tricky, because it is effectively a proprietary serialization format. If there's a way to get the switch to just give you JSON that would make your life easier (I know some Cisco switches have this option).

To do this The Right Way you would want to write a parser that understood the output format. But we can cheat, and make lots of assumptions, and solve this with a couple of regular expressions.

I think that in any case we're going to need to parse this output in a tool other than Ansible. I generally just write a filter module in Python in situations like this, although sometimes "pipe to awk" also works.

For parsing this output, I hacked together the following (and dropped it into filter_plugins/bigip.py adjacent to my playbook):

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

The above defines a get_pool_names filter. If I use it in a playbook like this:

---
- 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 }}"

I get the result:

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

Note that I've made assumptions about the format of the output here, and there's really not much in the way of error checking in the filter. But I think it demonstrates one way of tackling this problem.

Update

Regarding:

while i was trying to pass these pool names to the consecutive playbook i could see it is passing this as "[u\'test1-pool']" . But i just need "test1-pool" . Any suggestions ?

The result of the filter is a list of names. You are trying to treat it as a string. You need to either loop over the result, like this:

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

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

Or refer to individual elements in the list:

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

推荐阅读