在Ansible中,如何查询hostvars以根据不同键的值从列表项中获取键的特定值?
In Ansible, how to query hostvars to get a specific value of a key from a list item based on the value of a different key?
编辑更新:
我找到了一种方法来实现我想要做的事情,使用 index_of
插件。下面的代码输出我需要的。
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- ansible.builtin.set_fact:
mac_address: "{{ hostvars[inventory_hostname]['interfaces'][int_idx|int]['mac_address'] }}"
vars:
int_name: 'PCI1.1'
int_idx: "{{ lookup('ansible.utils.index_of', hostvars[inventory_hostname]['interfaces'], 'eq', int_name, 'name') }}"
- debug:
var: mac_address
输出:
PLAY [CASPOSR1BDAT003] ***********************************************************************************************************************************************************************************************
TASK [ansible.builtin.set_fact] **************************************************************************************************************************************************************************************
ok: [CASPOSR1BDAT003]
TASK [debug] *********************************************************************************************************************************************************************************************************
ok: [CASPOSR1BDAT003] =>
mac_address: 20:67:7C:00:36:A0
我想做什么:
- 使用 Netbox 动态库存插件(这有效,带回我需要的所有信息)
- 查询特定主机的 hostvars,并获取称为 PCI1.1
的特定接口的 MAC 地址的值
我试过的:
- 将 hostvars 转换为 JSON 并使用 json_query:这没有用,并且查看了 GitHub 上的一些问题,hostvars 不是“普通”字典.无论如何,我已经记录了几个问题 (https://github.com/ansible/ansible/issues/76289 and https://github.com/ansible-collections/community.general/issues/3706)。
- 使用序列循环和条件“何时”获取值 - 这种方法在使用
debug
模块时有效,但仍然不只是返回值
什么有效:
我尝试了以下方法,它按预期输出 mac_address
变量。找到列表的长度,然后条件匹配名称。我确实收到了关于使用 jinja2 模板定界符的警告,但这不是这个问题的目标。
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- debug:
var: hostvars[inventory_hostname]['interfaces'][{{ item }}]['mac_address']
with_sequence: start=0 end="{{ end_at }}"
vars:
- end_at: "{{ (hostvars[inventory_hostname]['interfaces'] | length) - 1 }}"
when: hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
结果是:
TASK [debug] *************************************************************************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found:
hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
skipping: [CASPOSR1BDAT003] => (item=0)
skipping: [CASPOSR1BDAT003] => (item=1)
skipping: [CASPOSR1BDAT003] => (item=2)
skipping: [CASPOSR1BDAT003] => (item=3)
skipping: [CASPOSR1BDAT003] => (item=4)
ok: [CASPOSR1BDAT003] => (item=5) =>
ansible_loop_var: item
hostvars[inventory_hostname]['interfaces'][5]['mac_address']: 20:67:7C:00:36:A0
item: '5'
skipping: [CASPOSR1BDAT003] => (item=6)
skipping: [CASPOSR1BDAT003] => (item=7)
skipping: [CASPOSR1BDAT003] => (item=8)
skipping: [CASPOSR1BDAT003] => (item=9)
我正在尝试使用 set_fact 来存储这个 mac_address
变量,因为我需要以几种不同的方式使用它。但是,我无法在此(或任何其他 hostvars 数据,似乎)上使用 set_fact
。例如,以下内容:
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][item]['mac_address'] }}"
with_sequence: start=0 end="{{ end_at }}"
vars:
- end_at: "{{ (hostvars[inventory_hostname]['interfaces'] | length) - 1 }}"
when: hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
- debug:
var: interfaces
结果:
fatal: [CASPOSR1BDAT003]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'list object' has no attribute '5'
The error appears to be in '/Users/kivlint/Documents/GitHub/vmware-automation/ansible/prepare-pxe.yml': line 19, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
# when: hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
- ansible.builtin.set_fact:
^ here
如果我在其中硬编码数字 5,它工作正常:
TASK [ansible.builtin.set_fact] ******************************************************************************************************************
ok: [CASPOSR1BDAT003]
TASK [debug] *************************************************************************************************************************************
ok: [CASPOSR1BDAT003] =>
interfaces: 20:67:7C:00:36:A0
如果我使用“5”作为任务的变量,它也有效。
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][int_index]['mac_address'] }}"
vars:
- int_index: 5
所以我想知道,这是 set_fact
如何与循环一起工作或不工作的“bug/feature”吗(意思是,同一个循环与 debug
一起工作得很好? 或者我是否需要重新考虑该方法并考虑尝试使用 set_fact
来设置一个带有列表索引的变量(例如上例中的 5)?或者其他什么?
set_fact
使用循环,但不是您期望的方式。
此示例从字典列表构造带循环的列表:
- set_fact:
foo: '{{ foo|d([]) + [item.value] }}'
loop:
- value: 1
- value: 2
基本上,set_fact 的每次执行都会创建一个事实。您可以在 set_fact
的 jinja 表达式中引用相同的事实,但您不能指望它会自动构建列表或类似的东西。
[5](列表的第 6 项)和 ['5'](名为“5”的键)之间存在混淆,
您在错误中看到:错误是:'list object' 没有 属性 '5'.
使用模块调试你没有错误,因为 [{{item}}] 被 [5] 代替而不是 ['5']。它与 set_fact 不同。
这是你必须使用过滤器 int 来澄清情况的原因。
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][item|int]['mac_address'] }}"
with_sequence: start=0 end="{{ end_at }}"
vars:
end_at: "{{ (hostvars[inventory_hostname]['interfaces'] | length) - 1 }}"
when: hostvars[inventory_hostname]['interfaces'][item|int]['name'] == "PCI1.1"
所以我建议你使用 loop 而不是 with_sequence:
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][item]['mac_address'] }}"
loop: "{{ range(0, end_at|int, 1)|list }}"
vars:
end_at: "{{ hostvars[inventory_hostname]['interfaces'] | length }}"
when: hostvars[inventory_hostname]['interfaces'][item]['name'] == "PCI1.1"
您的代码中发生了很多事情,实现您想要的结果比实现它更简单。
首先,不要使用hostvars[inventory_hostname]
;普通变量是属于当前主机的变量,通过 hostvars
会引入一些令人兴奋的出错机会。 hostvars
用于访问属于 other 主机的变量。
其次,使用 Jinja 的内置过滤功能可以避免担心所需项目的索引。
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
vars:
int_name: PCI1.1
mac_address: "{{ interfaces | selectattr('name', 'eq', int_name) | map(attribute='mac_address') | first }}"
tasks:
- debug:
var: mac_address
编辑更新:
我找到了一种方法来实现我想要做的事情,使用 index_of
插件。下面的代码输出我需要的。
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- ansible.builtin.set_fact:
mac_address: "{{ hostvars[inventory_hostname]['interfaces'][int_idx|int]['mac_address'] }}"
vars:
int_name: 'PCI1.1'
int_idx: "{{ lookup('ansible.utils.index_of', hostvars[inventory_hostname]['interfaces'], 'eq', int_name, 'name') }}"
- debug:
var: mac_address
输出:
PLAY [CASPOSR1BDAT003] ***********************************************************************************************************************************************************************************************
TASK [ansible.builtin.set_fact] **************************************************************************************************************************************************************************************
ok: [CASPOSR1BDAT003]
TASK [debug] *********************************************************************************************************************************************************************************************************
ok: [CASPOSR1BDAT003] =>
mac_address: 20:67:7C:00:36:A0
我想做什么:
- 使用 Netbox 动态库存插件(这有效,带回我需要的所有信息)
- 查询特定主机的 hostvars,并获取称为 PCI1.1 的特定接口的 MAC 地址的值
我试过的:
- 将 hostvars 转换为 JSON 并使用 json_query:这没有用,并且查看了 GitHub 上的一些问题,hostvars 不是“普通”字典.无论如何,我已经记录了几个问题 (https://github.com/ansible/ansible/issues/76289 and https://github.com/ansible-collections/community.general/issues/3706)。
- 使用序列循环和条件“何时”获取值 - 这种方法在使用
debug
模块时有效,但仍然不只是返回值
什么有效:
我尝试了以下方法,它按预期输出 mac_address
变量。找到列表的长度,然后条件匹配名称。我确实收到了关于使用 jinja2 模板定界符的警告,但这不是这个问题的目标。
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- debug:
var: hostvars[inventory_hostname]['interfaces'][{{ item }}]['mac_address']
with_sequence: start=0 end="{{ end_at }}"
vars:
- end_at: "{{ (hostvars[inventory_hostname]['interfaces'] | length) - 1 }}"
when: hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
结果是:
TASK [debug] *************************************************************************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found:
hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
skipping: [CASPOSR1BDAT003] => (item=0)
skipping: [CASPOSR1BDAT003] => (item=1)
skipping: [CASPOSR1BDAT003] => (item=2)
skipping: [CASPOSR1BDAT003] => (item=3)
skipping: [CASPOSR1BDAT003] => (item=4)
ok: [CASPOSR1BDAT003] => (item=5) =>
ansible_loop_var: item
hostvars[inventory_hostname]['interfaces'][5]['mac_address']: 20:67:7C:00:36:A0
item: '5'
skipping: [CASPOSR1BDAT003] => (item=6)
skipping: [CASPOSR1BDAT003] => (item=7)
skipping: [CASPOSR1BDAT003] => (item=8)
skipping: [CASPOSR1BDAT003] => (item=9)
我正在尝试使用 set_fact 来存储这个 mac_address
变量,因为我需要以几种不同的方式使用它。但是,我无法在此(或任何其他 hostvars 数据,似乎)上使用 set_fact
。例如,以下内容:
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][item]['mac_address'] }}"
with_sequence: start=0 end="{{ end_at }}"
vars:
- end_at: "{{ (hostvars[inventory_hostname]['interfaces'] | length) - 1 }}"
when: hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
- debug:
var: interfaces
结果:
fatal: [CASPOSR1BDAT003]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'list object' has no attribute '5'
The error appears to be in '/Users/kivlint/Documents/GitHub/vmware-automation/ansible/prepare-pxe.yml': line 19, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
# when: hostvars[inventory_hostname]['interfaces'][{{ item }}]['name'] == "PCI1.1"
- ansible.builtin.set_fact:
^ here
如果我在其中硬编码数字 5,它工作正常:
TASK [ansible.builtin.set_fact] ******************************************************************************************************************
ok: [CASPOSR1BDAT003]
TASK [debug] *************************************************************************************************************************************
ok: [CASPOSR1BDAT003] =>
interfaces: 20:67:7C:00:36:A0
如果我使用“5”作为任务的变量,它也有效。
---
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
tasks:
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][int_index]['mac_address'] }}"
vars:
- int_index: 5
所以我想知道,这是 set_fact
如何与循环一起工作或不工作的“bug/feature”吗(意思是,同一个循环与 debug
一起工作得很好? 或者我是否需要重新考虑该方法并考虑尝试使用 set_fact
来设置一个带有列表索引的变量(例如上例中的 5)?或者其他什么?
set_fact
使用循环,但不是您期望的方式。
此示例从字典列表构造带循环的列表:
- set_fact:
foo: '{{ foo|d([]) + [item.value] }}'
loop:
- value: 1
- value: 2
基本上,set_fact 的每次执行都会创建一个事实。您可以在 set_fact
的 jinja 表达式中引用相同的事实,但您不能指望它会自动构建列表或类似的东西。
[5](列表的第 6 项)和 ['5'](名为“5”的键)之间存在混淆, 您在错误中看到:错误是:'list object' 没有 属性 '5'.
使用模块调试你没有错误,因为 [{{item}}] 被 [5] 代替而不是 ['5']。它与 set_fact 不同。
这是你必须使用过滤器 int 来澄清情况的原因。
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][item|int]['mac_address'] }}"
with_sequence: start=0 end="{{ end_at }}"
vars:
end_at: "{{ (hostvars[inventory_hostname]['interfaces'] | length) - 1 }}"
when: hostvars[inventory_hostname]['interfaces'][item|int]['name'] == "PCI1.1"
所以我建议你使用 loop 而不是 with_sequence:
- ansible.builtin.set_fact:
interfaces: "{{ hostvars[inventory_hostname]['interfaces'][item]['mac_address'] }}"
loop: "{{ range(0, end_at|int, 1)|list }}"
vars:
end_at: "{{ hostvars[inventory_hostname]['interfaces'] | length }}"
when: hostvars[inventory_hostname]['interfaces'][item]['name'] == "PCI1.1"
您的代码中发生了很多事情,实现您想要的结果比实现它更简单。
首先,不要使用hostvars[inventory_hostname]
;普通变量是属于当前主机的变量,通过 hostvars
会引入一些令人兴奋的出错机会。 hostvars
用于访问属于 other 主机的变量。
其次,使用 Jinja 的内置过滤功能可以避免担心所需项目的索引。
- hosts: CASPOSR1BDAT003
connection: local
gather_facts: no
become: false
vars:
int_name: PCI1.1
mac_address: "{{ interfaces | selectattr('name', 'eq', int_name) | map(attribute='mac_address') | first }}"
tasks:
- debug:
var: mac_address