如何将 ansible-playbook --limit 与 IP 地址而不是主机名一起使用?
How to use ansible-playbook --limit with an IP address, rather than a hostname?
我们的 INI 样式库存如下所示:
foo-host ansible_host=1.2.3.4 some_var=bla
bar-host ansible_host=5.6.7.8 some_var=blup
我可以使用主机别名将剧本 运行 限制为单个主机:
$ ansible-playbook playbook.yml --limit foo-host
但我无法通过在 ansible_host
变量中提及主机的 IP 地址来限制 运行:
$ ansible-playbook playbook.yml --limit 1.2.3.4
ERROR! Specified hosts and/or --limit does not match any hosts
我想这样做的原因是 Ansible 是由只知道 IP 地址但不知道别名的外部系统触发的。
有没有办法让它工作?修改 IP 地址(例如 ip_1_2_3_4
)是可以接受的。
我考虑过的事情:
把它倒过来,通过 IP 地址识别所有主机:
1.2.3.4 some_var=bla
5.6.7.8 some_var=blup
但现在我们不能再使用漂亮的主机别名了,库存文件的可读性也变差了。
在常规清单之后编写 运行 的自定义清单脚本,并创建一个类似 ip_1_2_3_4
的组,仅包含该单个主机,因此我们可以使用 --limit ip_1_2_3_4
.但是无法从库存脚本访问以前加载的库存,所以我不知道要创建哪些组。
使用 group_by
module 动态创建新组。但是因为这是一个任务,所以只有在 --limit
已经确定没有主机匹配该模式之后,它才是 运行,而此时 Ansible 只是放弃而不是 运行 group_by
任务了。
Q: Playbook running a script running a playbook... it would work, but it's a bit hacky
答:FWIW。可以使用 json_query 并避免脚本。例如
- hosts: all
gather_facts: false
tasks:
- set_fact:
my_host: "{{ hostvars|dict2items|json_query(query)|first }}"
vars:
query: "[?value.ansible_host == '{{ my_host_ip }}' ].key"
run_once: true
- add_host:
hostname: "{{ my_host }}"
groups: my_group
run_once: true
- hosts: my_group
gather_facts: false
tasks:
- debug:
var: inventory_hostname
Q: "Unfortunately I'm running ansible-playbook from AWX, so no wrapper scripts allowed."
A: 运行 剧本中的脚本是可能的。例如下面的剧本
- hosts: all
gather_facts: false
tasks:
- set_fact:
my_host: "{{ lookup('pipe', playbook_dir ~ '/script.sh ' ~ my_host_ip) }}"
delegate_to: localhost
run_once: true
- add_host:
hostname: "{{ my_host }}"
groups: my_group
run_once: true
- hosts: my_group
gather_facts: false
tasks:
- debug:
var: inventory_hostname
给予
$ ansible-playbook -e 'my_host_ip=10.1.0.53' play.yml
PLAY [all] ********************
TASK [set_fact] ***************
ok: [test_01 -> localhost]
TASK [add_host] ***************
changed: [test_01]
PLAY [my_group] ***************
TASK [debug] ************
ok: [test_03] => {
"inventory_hostname": "test_03"
}
(让脚本只打印主机名。)
Q: I can limit a playbook run to a single host by using the host alias:
$ ansible-playbook playbook.yml --limit foo-host
But I can't limit the run by mentioning the host's IP address from the ansible_host variable
$ ansible-playbook playbook.yml --limit 1.2.3.4
答:ansible-inventory
和jq都可以解析主机。例如脚本
#!/bin/bash
my_host="$(ansible-inventory --list | jq '._meta.hostvars | to_entries[] | select (.value.ansible_host=="'""'") | .key')"
my_host="$(echo $my_host | sed -e 's/^"//' -e 's/"$//')"
echo host: $my_host
ansible-playbook -l $my_host play.yml
有库存
test_01 ansible_host=10.1.0.51
test_02 ansible_host=10.1.0.52
test_03 ansible_host=10.1.0.53
并与 playbook.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: inventory_hostname
给予
$ ./script.bash 10.1.0.53
host: test_03
PLAY [all] **********
TASK [debug] ************
ok: [test_03] => {
"inventory_hostname": "test_03"
}
仍然欢迎更好的解决方案,但目前我正在使用一个小型库存插件来实现它,该插件(与库存脚本相反) 可以访问之前添加的库存:
plugins/inventory/ip_based_groups.py
import os.path
import re
from ansible.plugins.inventory import BaseInventoryPlugin
from ansible.inventory.group import Group
PATH_PLACEHOLDER = 'IP_BASED_GROUPS'
IP_RE = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
class InventoryModule(BaseInventoryPlugin):
'''
This inventory plugin does not create any hosts, but just adds groups based
on IP addresses. For each host whose ansible_host looks like an IPv4
address (e.g. 1.2.3.4), a corresponding group is created by prefixing the
IP address with 'ip_' and replacing dots by underscores (e.g. ip_1_2_3_4).
Use it by putting the literal string IP_BASED_GROUPS at the end of the list
of inventory sources.
'''
NAME = 'ip_based_groups'
def verify_file(self, path):
return self._is_path_placeholder(path)
def parse(self, inventory, loader, path, cache=True):
if not self._is_path_placeholder(path):
return
for host_name, host in inventory.hosts.items():
ansible_host = host.vars.get('ansible_host', '')
if self._is_ip_address(ansible_host):
group = 'ip_' + ansible_host.replace('.', '_')
inventory.add_group(group)
inventory.add_host(host_name, group)
def _is_path_placeholder(self, path):
return os.path.basename(path) == PATH_PLACEHOLDER
def _is_ip_address(self, s):
return bool(IP_RE.match(s))
ansible.cfg
[defaults]
# Load plugins from these directories.
inventory_plugins = plugins/inventory
# Directory that contains all inventory files, and placeholder to create
# IP-based groups.
inventory = inventory/,IP_BASED_GROUPS
[inventory]
# Enable our custom inventory plugin.
enable_plugins = ip_based_groups, host_list, script, auto, yaml, ini, toml
我们的 INI 样式库存如下所示:
foo-host ansible_host=1.2.3.4 some_var=bla
bar-host ansible_host=5.6.7.8 some_var=blup
我可以使用主机别名将剧本 运行 限制为单个主机:
$ ansible-playbook playbook.yml --limit foo-host
但我无法通过在 ansible_host
变量中提及主机的 IP 地址来限制 运行:
$ ansible-playbook playbook.yml --limit 1.2.3.4
ERROR! Specified hosts and/or --limit does not match any hosts
我想这样做的原因是 Ansible 是由只知道 IP 地址但不知道别名的外部系统触发的。
有没有办法让它工作?修改 IP 地址(例如 ip_1_2_3_4
)是可以接受的。
我考虑过的事情:
把它倒过来,通过 IP 地址识别所有主机:
1.2.3.4 some_var=bla 5.6.7.8 some_var=blup
但现在我们不能再使用漂亮的主机别名了,库存文件的可读性也变差了。
在常规清单之后编写 运行 的自定义清单脚本,并创建一个类似
ip_1_2_3_4
的组,仅包含该单个主机,因此我们可以使用--limit ip_1_2_3_4
.但是无法从库存脚本访问以前加载的库存,所以我不知道要创建哪些组。使用
group_by
module 动态创建新组。但是因为这是一个任务,所以只有在--limit
已经确定没有主机匹配该模式之后,它才是 运行,而此时 Ansible 只是放弃而不是 运行group_by
任务了。
Q: Playbook running a script running a playbook... it would work, but it's a bit hacky
答:FWIW。可以使用 json_query 并避免脚本。例如
- hosts: all
gather_facts: false
tasks:
- set_fact:
my_host: "{{ hostvars|dict2items|json_query(query)|first }}"
vars:
query: "[?value.ansible_host == '{{ my_host_ip }}' ].key"
run_once: true
- add_host:
hostname: "{{ my_host }}"
groups: my_group
run_once: true
- hosts: my_group
gather_facts: false
tasks:
- debug:
var: inventory_hostname
Q: "Unfortunately I'm running ansible-playbook from AWX, so no wrapper scripts allowed."
A: 运行 剧本中的脚本是可能的。例如下面的剧本
- hosts: all
gather_facts: false
tasks:
- set_fact:
my_host: "{{ lookup('pipe', playbook_dir ~ '/script.sh ' ~ my_host_ip) }}"
delegate_to: localhost
run_once: true
- add_host:
hostname: "{{ my_host }}"
groups: my_group
run_once: true
- hosts: my_group
gather_facts: false
tasks:
- debug:
var: inventory_hostname
给予
$ ansible-playbook -e 'my_host_ip=10.1.0.53' play.yml
PLAY [all] ********************
TASK [set_fact] ***************
ok: [test_01 -> localhost]
TASK [add_host] ***************
changed: [test_01]
PLAY [my_group] ***************
TASK [debug] ************
ok: [test_03] => {
"inventory_hostname": "test_03"
}
(让脚本只打印主机名。)
Q: I can limit a playbook run to a single host by using the host alias:
$ ansible-playbook playbook.yml --limit foo-host
But I can't limit the run by mentioning the host's IP address from the ansible_host variable
$ ansible-playbook playbook.yml --limit 1.2.3.4
答:ansible-inventory
和jq都可以解析主机。例如脚本
#!/bin/bash
my_host="$(ansible-inventory --list | jq '._meta.hostvars | to_entries[] | select (.value.ansible_host=="'""'") | .key')"
my_host="$(echo $my_host | sed -e 's/^"//' -e 's/"$//')"
echo host: $my_host
ansible-playbook -l $my_host play.yml
有库存
test_01 ansible_host=10.1.0.51
test_02 ansible_host=10.1.0.52
test_03 ansible_host=10.1.0.53
并与 playbook.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: inventory_hostname
给予
$ ./script.bash 10.1.0.53
host: test_03
PLAY [all] **********
TASK [debug] ************
ok: [test_03] => {
"inventory_hostname": "test_03"
}
仍然欢迎更好的解决方案,但目前我正在使用一个小型库存插件来实现它,该插件(与库存脚本相反) 可以访问之前添加的库存:
plugins/inventory/ip_based_groups.py
import os.path
import re
from ansible.plugins.inventory import BaseInventoryPlugin
from ansible.inventory.group import Group
PATH_PLACEHOLDER = 'IP_BASED_GROUPS'
IP_RE = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
class InventoryModule(BaseInventoryPlugin):
'''
This inventory plugin does not create any hosts, but just adds groups based
on IP addresses. For each host whose ansible_host looks like an IPv4
address (e.g. 1.2.3.4), a corresponding group is created by prefixing the
IP address with 'ip_' and replacing dots by underscores (e.g. ip_1_2_3_4).
Use it by putting the literal string IP_BASED_GROUPS at the end of the list
of inventory sources.
'''
NAME = 'ip_based_groups'
def verify_file(self, path):
return self._is_path_placeholder(path)
def parse(self, inventory, loader, path, cache=True):
if not self._is_path_placeholder(path):
return
for host_name, host in inventory.hosts.items():
ansible_host = host.vars.get('ansible_host', '')
if self._is_ip_address(ansible_host):
group = 'ip_' + ansible_host.replace('.', '_')
inventory.add_group(group)
inventory.add_host(host_name, group)
def _is_path_placeholder(self, path):
return os.path.basename(path) == PATH_PLACEHOLDER
def _is_ip_address(self, s):
return bool(IP_RE.match(s))
ansible.cfg
[defaults]
# Load plugins from these directories.
inventory_plugins = plugins/inventory
# Directory that contains all inventory files, and placeholder to create
# IP-based groups.
inventory = inventory/,IP_BASED_GROUPS
[inventory]
# Enable our custom inventory plugin.
enable_plugins = ip_based_groups, host_list, script, auto, yaml, ini, toml