Ansible :(意外)hostvars项在任务循环期间始终被组中最后一个主机的值覆盖

时间:2018-10-22 23:22:30

标签: automation ansible ansible-2.x cisco ansible-inventory

我正在尝试编写一本Ansible剧本,该剧本运行一些任务来配置Cisco IOS路由器。其中一些任务需要遍历在主机变量文件级别定义的列表变量。例如,给定一个或多个接口,请在该接口上配置x。或者,在给定一个或多个fvrf的情况下,为每个fvrf配置名称服务器。接口和fvrfs的数量是动态的。在剧本角色中,有多个具有动态列表值的任务。

我遇到的问题是,每个路由器具有唯一值的主机变量总是设置为为组中的最后一个路由器定义的值。对于作为字符串的变量和作为字符串列表的变量,会发生这种情况。换句话说,清单组中最后一个路由器之前的路由器的hostvar总是被为最后一个路由器定义的hostvar覆盖。

可运行时间:

$ ansible --version
ansible 2.7.0
  config file = /opt/ansible/ansible.cfg
  configured module search path = [u'/home/<redacted>/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/<redacted>/mypython/lib/python2.7/site-packages/ansible
  executable location = /home/<redacted>/mypython/bin/ansible
  python version = 2.7.15 (default, Oct 22 2018, 15:22:25) [GCC 4.4.7 20120313 (Red Hat 4.4.7-18)]
(A) (mypython) <redacted>@<redacted_hostname> /opt/ansible
$ ansible-playbook --version
ansible-playbook 2.7.0
  config file = /opt/ansible/ansible.cfg
  configured module search path = [u'/home/<redacted>/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/<redacted>/mypython/lib/python2.7/site-packages/ansible
  executable location = /home/<redacted>/mypython/bin/ansible-playbook
  python version = 2.7.15 (default, Oct 22 2018, 15:22:25) [GCC 4.4.7 20120313 (Red Hat 4.4.7-18)]

目录结构:

+-- ansible.cfg
+-- inventory
|   +-- lab-g2
|   |   +-- group_vars
|   |   |   +-- lab-g2-crs-2900
|   |   |       +-- host_vars
|   |   |       |   +-- 10.74.0.71.yml
|   |   |       |   +-- 10.74.0.73.yml
|   |   |       +-- vars
|   |   +-- inventory
+-- library
+-- playbooks
|   +-- roles -> /opt/ansible/roles
|   +-- set-nameservers.yml
+-- README.md
+-- roles
|    +-- set-nameservers
    |   +-- tasks
    |       +-- main.yml

playbook.yml:

---

- name: CONFIGURE NAMESERVERS ON ROUTER
  hosts: all
  gather_facts: no
  connection: network_cli

  roles:
    - set-nameservers

库存文件:

[lab-g2-crs-2900]
10.74.0.71
10.74.0.73

[all:children]
lab-g2-crs-2900

组变量文件:

---

ansible_connection: network_cli
ansible_network_os: ios

主机变量文件:

10.74.0.71.yml:

fvrf: ["WAN1", "WAN2"]
umbrella_out: ["GigabitEthernet0/0"]

10.74.0.73.yml:

fvrf: ["WAN3", "WAN4"]
umbrella_out: ["GigabitEthernet0/1"]

角色/设置名称服务器/任务/main.yml

---
- name: CONFIGURE NAMESERVERS
  ios_config:
    lines:
      - "ip name-server vrf {{ item }} 208.67.220.220 208.67.222.222"
  with_items: "{{ fvrf }}"

- name: DEBUG
  debug:
    msg: "fvrf name is {{ item }}"
  with_items: "{{ fvrf }}"

- name: CONFIGURE UMBRELLA OUTBOUND INTERFACE
  ios_config:
    lines:
      - "description Outbound umbrella interface"
    parents: interface {{ item }}
  with_items: "{{ umbrella_out }}"

- name: DEBUG
  debug:
    msg: "Outbound Umbrella interface is {{ item }}"
  with_items: "{{ umbrella_out }}"

预期结果

PLAY [CONFIGURE NAMESERVERS ON ROUTER] ***************************************************************************************************************************************

TASK [set-nameservers : CONFIGURE NAMESERVERS] *******************************************************************************************************************************
changed: [10.74.0.73] => (item=WAN3)
changed: [10.74.0.71] => (item=WAN1)
changed: [10.74.0.73] => (item=WAN4)
changed: [10.74.0.71] => (item=WAN2)

TASK [set-nameservers : DEBUG] ***********************************************************************************************************************************************
ok: [10.74.0.71] => (item=WAN3) => {
    "msg": "fvrf name is WAN1"
}
ok: [10.74.0.71] => (item=WAN4) => {
    "msg": "fvrf name is WAN2"
}
ok: [10.74.0.73] => (item=WAN3) => {
    "msg": "fvrf name is WAN3"
}
ok: [10.74.0.73] => (item=WAN4) => {
    "msg": "fvrf name is WAN4"
}

TASK [set-nameservers : CONFIGURE UMBRELLA OUTBOUND INTERFACE] ***************************************************************************************************************
changed: [10.74.0.73] => (item=GigabitEthernet0/0)
changed: [10.74.0.71] => (item=GigabitEthernet0/1)

TASK [set-nameservers : DEBUG] ***********************************************************************************************************************************************
ok: [10.74.0.71] => (item=GigabitEthernet0/1) => {
    "msg": "Outbound Umbrella interface is GigabitEthernet0/0"
}
ok: [10.74.0.73] => (item=GigabitEthernet0/1) => {
    "msg": "Outbound Umbrella interface is GigabitEthernet0/1"
}

PLAY RECAP *******************************************************************************************************************************************************************
10.74.0.71                : ok=4    changed=2    unreachable=0    failed=0
10.74.0.73                : ok=4    changed=2    unreachable=0    failed=0

实际结果

PLAY [CONFIGURE NAMESERVERS ON ROUTER] ***************************************************************************************************************************************

TASK [set-nameservers : CONFIGURE NAMESERVERS] *******************************************************************************************************************************
changed: [10.74.0.73] => (item=WAN3)
changed: [10.74.0.71] => (item=WAN3)
changed: [10.74.0.73] => (item=WAN4)
changed: [10.74.0.71] => (item=WAN4)

TASK [set-nameservers : DEBUG] ***********************************************************************************************************************************************
ok: [10.74.0.71] => (item=WAN3) => {
    "msg": "fvrf name is WAN3"
}
ok: [10.74.0.71] => (item=WAN4) => {
    "msg": "fvrf name is WAN4"
}
ok: [10.74.0.73] => (item=WAN3) => {
    "msg": "fvrf name is WAN3"
}
ok: [10.74.0.73] => (item=WAN4) => {
    "msg": "fvrf name is WAN4"
}

TASK [set-nameservers : CONFIGURE UMBRELLA OUTBOUND INTERFACE] ***************************************************************************************************************
changed: [10.74.0.73] => (item=GigabitEthernet0/1)
changed: [10.74.0.71] => (item=GigabitEthernet0/1)

TASK [set-nameservers : DEBUG] ***********************************************************************************************************************************************
ok: [10.74.0.71] => (item=GigabitEthernet0/1) => {
    "msg": "Outbound Umbrella interface is GigabitEthernet0/1"
}
ok: [10.74.0.73] => (item=GigabitEthernet0/1) => {
    "msg": "Outbound Umbrella interface is GigabitEthernet0/1"
}

PLAY RECAP *******************************************************************************************************************************************************************
10.74.0.71                : ok=4    changed=2    unreachable=0    failed=0
10.74.0.73                : ok=4    changed=2    unreachable=0    failed=0

从输出结果中可以看到,即使10.74.0.71在单独的文件中定义了自己的唯一变量值,主机文件中10.74.0.73的变量也同时用于两个主机。在具有相同结构和16个路由器的另一本剧本中,它表现出相同的行为...对所有16个路由器(组)使用组中最后一个路由器的hostvars(赞!)

我已经在网上搜索了几个小时,并浏览了Ansible文档以及有关循环,变量和变量优先级的大量讨论。我还没有弄清楚问题是什么。我认为最可能的罪魁祸首是我误解了with_items操作的工作方式,但是我不知道如何修改任务以确保每个主机都有唯一变量列表的预期结果。可能是错误行为吗?

在此问题上提供的任何帮助,将不胜感激!

1 个答案:

答案 0 :(得分:1)

感谢发布目录输出,这是关键。不是with_items会给您带来问题,而是host_vars中嵌套的group_vars

如果将host_varsgroup_vars目录中拉出,它将再次开始正常运行。我还没有深入研究分配规则来确切地找到 ,但是在tl; dr上,顶部存储区不是 主机专用的,即使最下面的铲斗,因此:

hostvars["10.74.0.71"] = # the correct thing 
vars["lab-g2-crs-2900"] = {}
for h in ["10.74.0.71", ...etc...]:
   vars["lab-g2-crs-2900"].update(hostvars[h])
# and now the value in the group_vars "masks off" the host-specific one
# because they appear to be applied in reverse-depth-first order

您无需使用ansible-inventory --list并查看hostvars中的_meta即可运行整个剧本,而不用自己确认一下