ansible - 从 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",
" }",
" }",
"}"
]
]
解决方案
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 }}"
推荐阅读
- react-native - 反应原生平面列表分页启用删除问题
- css - safari 自动建议的样式密码
- java - 不受支持的 JavaFX 配置:类是从“未命名模块 @...”加载的
- c# - 一对扑克牌 C# 的 GetHashCode
- java - Andriod Studio“受信任的证书条目不受密码保护”
- ip-address - 更改 IP 地址后无法通过浏览器访问 Bitnami Stack LimeSurvey 应用程序(托管在 AWS ec2 实例上)
- reactjs - 如何在 reactJS 中添加对 twitch 视频播放器的响应能力
- python - cx_Oracle.DatabaseError: DPI-1047: 找不到 64 位 Oracle 客户端库 oracle 10
- python - 如何告诉 SQLAlchemy 变量“x”中的类型
- javascript - innerHTML 如何在顶部创建新的表格行