首页 文章

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

提问于
浏览
1

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

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

Ansible运行时:

$ 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 }}"

EXPECTED RESULT

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

ACTUAL RESULT

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.73中的变量正在用于两个主机,即使10.74.0.71在单独的文件中定义了自己唯一的变量值 . 在具有相同结构和16个路由器的单独的剧本中,它显示了相同的行为...使用组中最后一个路由器的主机变量为所有16个路由器(yikes!)

我've been searching the web for a few hours and have looked through the Ansible docs and a lot of discussion here pertaining to loops, variables, and variable precedence. I haven'弄明白问题是什么 . 我认为最可能的罪魁祸首是我误解了 with_items 操作是如何工作的,但我不知道如何修改任务以确保每个主机具有唯一变量列表所需的结果 . 这可能是bug行为吗?

非常感谢您对此问题的任何帮助!

1 回答

  • 1

    感谢您发布目录输出,这是关键 . 这不是 with_items 's causing you the problems, it'是 group_vars 中的嵌套 host_vars .

    如果将 host_vars 拉出 group_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 并查看 _meta 中的 hostvars 就可以自行确认而无需运行整个游戏手册

相关问题