Ansible 动态清单脚本 - 奇怪的行为
Ansible dynamic inventory script - odd behaviour
我正在尝试根据 JSON 输出为 ansible 创建一个基本的动态清单脚本。我是 jq 的新手,但我遇到了一个问题,即 ansible v2.9.14 和 2.9.15 上的动态脚本不喜欢输出,但是如果我将输出发送到一个文件然后 运行 Ansible针对文件中的输出,ansible 有效。
事情是这样的:
动态清单脚本输出:
{
"all": {
"hosts": {
"ip-172-31-39-30.eu-west-1.compute.internal": null,
"ip-172-31-44-224.eu-west-1.compute.internal": null,
"ip-172-31-42-6.eu-west-1.compute.internal": null,
"ip-172-31-32-68.eu-west-1.compute.internal": null,
}
}
}
Ansible 运行 和错误:
$ ansible -i ./dynamic1.sh all -m ping -u ubuntu
[WARNING]: * Failed to parse /home/ubuntu/dynamic1.sh with script plugin: failed to parse executable inventory script results from /home/ubuntu/dynamic1.sh:
Expecting property name enclosed in double quotes: line 8 column 5 (char 242)
[WARNING]: * Failed to parse /home/ubuntu/dynamic1.sh with ini plugin: /home/ubuntu/dynamic1.sh:2: Expected key=value host variable assignment, got: {
[WARNING]: Unable to parse /home/ubuntu/dynamic1.sh as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
现在,如果我将动态脚本输出到一个文件,然后再次 运行 ansible,它就可以工作了:
$ ./dynamic1.sh > output.json
$ cat output.json
{
"all": {
"hosts": {
"ip-172-31-39-30.eu-west-1.compute.internal": null,
"ip-172-31-44-224.eu-west-1.compute.internal": null,
"ip-172-31-42-6.eu-west-1.compute.internal": null,
"ip-172-31-32-68.eu-west-1.compute.internal": null,
}
}
}
$ ansible -i output.json all -m ping -u ubuntu
[DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host ip-172-31-42-6.eu-west-1.compute.internal should use /usr/bin/python3, but is using /usr/bin/python for
backward compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in version 2.12. Deprecation
warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
ip-172-31-42-6.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
ip-172-31-39-30.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ip-172-31-32-68.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ip-172-31-44-224.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
所以它有效...
这是dynamic1.sh的内容。我知道会有更好的方法来做到这一点,但我只需要一个基于 ansible 可以使用的 JSON 输出中的匹配变量的服务器列表。
$ cat dynamic1.sh
#!/bin/bash
echo "{"
echo " \"all\": {"
echo " \"hosts\": {"
curl --silent -X GET https://url.com/api/servers -H "Authorization: Token $token" -H "Content-Type: text/json" -H "Accept:application/json" | jq -r '.Result.servers[] | select(.ansible_local.local.local_facts.instance_type | tostring | contains("t2.micro")) | (.ansible_fqdn+"\": null,")' | sed 's/^/"/g'
echo " }"
echo " }"
echo "}"
任何人都可以帮助我解释为什么 ansible 接受文件而不接受脚本的输出吗?
与 Ansible inventory format, the inventory plugin script.py 相反,期望属性 hosts 是一个 list(例如 hosts :[ host1, host2, host3 ]) 不是 字典 (例如 hosts:{ host, host2, host3 })。
清单插件 yaml.py 与主机字典一起使用
JSON(或 YAML,因为 JSON 是 YAML 的子集)清单工作正常
shell> cat hosts.json
{
"all": {
"hosts": {
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-32-68.eu-west-1.compute.internal"
}
}
}
shell> ansible-inventory -i hosts.json --list -vvv
...
Parsed /scratch/tmp/hosts.json inventory source with yaml plugin
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"ip-172-31-32-68.eu-west-1.compute.internal",
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal"
]
}
}
但是,脚本提供的相同文件将失败
shell> cat hosts.sh
#!/bin/bash
cat hosts.json
shell> ansible-inventory -i hosts.sh --list -vvv
...
Parsed /scratch/tmp/hosts.sh inventory source with script plugin
[WARNING]: Failed to parse /scratch/tmp/hosts.sh with script plugin: You
defined a group 'all' with bad data for the host list: {'hosts': {'ip-172-31-39-30.eu-
west-1.compute.internal': None, 'ip-172-31-44-224.eu-west-1.compute.internal': None,
'ip-172-31-42-6.eu-west-1.compute.internal': None, 'ip-172-31-32-68.eu-
west-1.compute.internal': None}}
...
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
}
}
清单插件 script.py 与主机列表一起工作
清单插件 script.py 在属性主机是 list
时按预期工作
shell> cat hosts.json
{
"all": {
"hosts": [
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-32-68.eu-west-1.compute.internal"
]
}
}
shell> ansible-inventory -i hosts.sh --list -vvv
...
Parsed /scratch/tmp/hosts.sh inventory source with script plugin
{
"_meta": {
...
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"ip-172-31-32-68.eu-west-1.compute.internal",
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal"
]
}
}
备注
- 脚本 hosts.sh 未正确实现,仅用于此示例。引用自 script.py:
description:
- The source provided must be an executable that returns Ansible inventory JSON
- The source must accept C(--list) and C(--host ) as arguments.
C(--host) will only be used if no C(_meta) key is present.
我正在尝试根据 JSON 输出为 ansible 创建一个基本的动态清单脚本。我是 jq 的新手,但我遇到了一个问题,即 ansible v2.9.14 和 2.9.15 上的动态脚本不喜欢输出,但是如果我将输出发送到一个文件然后 运行 Ansible针对文件中的输出,ansible 有效。
事情是这样的:
动态清单脚本输出:
{
"all": {
"hosts": {
"ip-172-31-39-30.eu-west-1.compute.internal": null,
"ip-172-31-44-224.eu-west-1.compute.internal": null,
"ip-172-31-42-6.eu-west-1.compute.internal": null,
"ip-172-31-32-68.eu-west-1.compute.internal": null,
}
}
}
Ansible 运行 和错误:
$ ansible -i ./dynamic1.sh all -m ping -u ubuntu
[WARNING]: * Failed to parse /home/ubuntu/dynamic1.sh with script plugin: failed to parse executable inventory script results from /home/ubuntu/dynamic1.sh:
Expecting property name enclosed in double quotes: line 8 column 5 (char 242)
[WARNING]: * Failed to parse /home/ubuntu/dynamic1.sh with ini plugin: /home/ubuntu/dynamic1.sh:2: Expected key=value host variable assignment, got: {
[WARNING]: Unable to parse /home/ubuntu/dynamic1.sh as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
现在,如果我将动态脚本输出到一个文件,然后再次 运行 ansible,它就可以工作了:
$ ./dynamic1.sh > output.json
$ cat output.json
{
"all": {
"hosts": {
"ip-172-31-39-30.eu-west-1.compute.internal": null,
"ip-172-31-44-224.eu-west-1.compute.internal": null,
"ip-172-31-42-6.eu-west-1.compute.internal": null,
"ip-172-31-32-68.eu-west-1.compute.internal": null,
}
}
}
$ ansible -i output.json all -m ping -u ubuntu
[DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host ip-172-31-42-6.eu-west-1.compute.internal should use /usr/bin/python3, but is using /usr/bin/python for
backward compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in version 2.12. Deprecation
warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
ip-172-31-42-6.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
ip-172-31-39-30.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ip-172-31-32-68.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ip-172-31-44-224.eu-west-1.compute.internal | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
所以它有效...
这是dynamic1.sh的内容。我知道会有更好的方法来做到这一点,但我只需要一个基于 ansible 可以使用的 JSON 输出中的匹配变量的服务器列表。
$ cat dynamic1.sh
#!/bin/bash
echo "{"
echo " \"all\": {"
echo " \"hosts\": {"
curl --silent -X GET https://url.com/api/servers -H "Authorization: Token $token" -H "Content-Type: text/json" -H "Accept:application/json" | jq -r '.Result.servers[] | select(.ansible_local.local.local_facts.instance_type | tostring | contains("t2.micro")) | (.ansible_fqdn+"\": null,")' | sed 's/^/"/g'
echo " }"
echo " }"
echo "}"
任何人都可以帮助我解释为什么 ansible 接受文件而不接受脚本的输出吗?
与 Ansible inventory format, the inventory plugin script.py 相反,期望属性 hosts 是一个 list(例如 hosts :[ host1, host2, host3 ]) 不是 字典 (例如 hosts:{ host, host2, host3 })。
清单插件 yaml.py 与主机字典一起使用
JSON(或 YAML,因为 JSON 是 YAML 的子集)清单工作正常
shell> cat hosts.json
{
"all": {
"hosts": {
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-32-68.eu-west-1.compute.internal"
}
}
}
shell> ansible-inventory -i hosts.json --list -vvv
...
Parsed /scratch/tmp/hosts.json inventory source with yaml plugin
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"ip-172-31-32-68.eu-west-1.compute.internal",
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal"
]
}
}
但是,脚本提供的相同文件将失败
shell> cat hosts.sh
#!/bin/bash
cat hosts.json
shell> ansible-inventory -i hosts.sh --list -vvv
...
Parsed /scratch/tmp/hosts.sh inventory source with script plugin
[WARNING]: Failed to parse /scratch/tmp/hosts.sh with script plugin: You defined a group 'all' with bad data for the host list: {'hosts': {'ip-172-31-39-30.eu- west-1.compute.internal': None, 'ip-172-31-44-224.eu-west-1.compute.internal': None, 'ip-172-31-42-6.eu-west-1.compute.internal': None, 'ip-172-31-32-68.eu- west-1.compute.internal': None}} ...
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
}
}
清单插件 script.py 与主机列表一起工作
清单插件 script.py 在属性主机是 list
时按预期工作shell> cat hosts.json
{
"all": {
"hosts": [
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-32-68.eu-west-1.compute.internal"
]
}
}
shell> ansible-inventory -i hosts.sh --list -vvv
...
Parsed /scratch/tmp/hosts.sh inventory source with script plugin
{
"_meta": {
...
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"ip-172-31-32-68.eu-west-1.compute.internal",
"ip-172-31-39-30.eu-west-1.compute.internal",
"ip-172-31-42-6.eu-west-1.compute.internal",
"ip-172-31-44-224.eu-west-1.compute.internal"
]
}
}
备注
- 脚本 hosts.sh 未正确实现,仅用于此示例。引用自 script.py:
description: - The source provided must be an executable that returns Ansible inventory JSON - The source must accept C(--list) and C(--host ) as arguments. C(--host) will only be used if no C(_meta) key is present.