可变的嵌套dict变量

时间:2018-08-26 19:48:05

标签: dictionary ansible

使用DHCP IP部署VM之后,我想获取IP并将其附加到guest虚拟机字典中。

对于第一个VM(testvm2),代码将按预期执行,并更新testvm2 VM的tempip变量。

但是对于第二个VM(testvm1),它将使用第二个VM(testvm1)的IP更新第一个VM(testvm2)的tempip变量,并使用代码更新第二个VM(testvm1)的tempip变量'{{tempip_reg.stdout_lines}}'

有人可以向我解释为什么会这样吗? 我会很感激的。

我复制了所有相关代码,并在下面输出:

宾客字典:

---
guests:
  testvm1:
    mem: 512
    cpus: 1
    clone: template-centos
    vmid: 102
    tempip:

  testvm2:
    mem: 1536
    cpus: 2
    clone: template-centos
    vmid: 102
    tempip:

启动任务的Ansible Playbook:

---
- name: Provision VMs
  hosts: infra
  become: true
  vars_files:
    - group_vars/vms.yml
    - group_vars/vars.yml
  tasks:
    - include_tasks: roles/tasks/provision-tasks.yml
      with_dict: "{{ guests }}"

任务:

- name: Run proxmox-get-ip-linux.sh script to register DHCP IP of VM
  script: proxmox-get-ip-linux.sh "{{ item.key }}"
  register: tempip_reg

- name: temporary IP of VM "{{ item.key }}"
  debug:
    var: tempip_reg

- name: current host in item.key
  set_fact:
    current_host: "{{ item.key }}"

- name: current_host variable set to
  debug:
    var: current_host

- name: append item.value.tempip with the DHCP IP of the VM registered in 
 last task
  set_fact:
    guests: "{{ guests|combine({ current_host: {'tempip': '{{ tempip_reg.stdout_lines }}' }}, recursive=True) }}"

- name: temporary IP of "{{ item.key }}"
  debug: var=guests

生成第一个VM:

"tempip_reg": {
    "stdout": "192.168.1.21\r\n",
    "stdout_lines": [
        "192.168.1.21"
    }

"current_host": "testvm2"

"guests": {
    "testvm1": {
        "clone": "template-centos",
        "cpus": 1,
        "ip": "192.168.1.60",
        "mem": 512,
        "tempip": null,
        "vmid": 102
    },
    "testvm2": {
        "clone": "template-centos",
        "cpus": 2,
        "ip": "192.168.1.61",
        "mem": 1536,
        "tempip": [
            "192.168.1.21"
        ],
        "vmid": 102
    }
}

结果第二个虚拟机:

"tempip_reg": {
    "stdout": "192.168.1.22\r\n",
    "stdout_lines": [
        "192.168.1.22"
    }

    "current_host": "testvm1"

"guests": {
    "testvm1": {
        "clone": "template-centos",
        "cpus": 1,
        "ip": "192.168.1.60",
        "mem": 512,
        "tempip": "{{ tempip_reg.stdout_lines }}",
        "vmid": 102
    },
    "testvm2": {
        "clone": "template-centos",
        "cpus": 2,
        "ip": "192.168.1.61",
        "mem": 1536,
        "tempip": [
            "192.168.1.22"
        ],
        "vmid": 102
    }
}

1 个答案:

答案 0 :(得分:0)

TL; DR

使用Ansible代码,您正在尝试实现Ansible已经为您完成的工作。

您的尝试与内置功能叠加在一起,您会得到不确定的结果。


说明:

  • 您的代码的主要问题是用with_dict: "{{ guests }}"声明的完全不必要的循环,这导致该文件包含4次。

    它运行4次是因为您更改了guests字典,该字典在包含的任务文件内循环。

    实际上,您得到的结果看起来像是不确定的结果。

  • 第二个问题很简单:您始终用字符串tempip替换{{ tempip_reg.stdout_lines }}的值。

  • 现在,由于在动态更改的字典上进行了不必要的with_dict循环,并且Jinja2使用了惰性变量求值,因此先前迭代中的字符串被解释为模板,并在随后的迭代中获得不正确的值迭代。

    最后一次迭代使字符串{{ tempip_reg.stdout_lines }}保持不变。

  • 您还定义并打印了两个不同的事实。


您应该做什么:

  • 您根本不应声明任意迭代。 Ansible为所有主机本身实现一个循环。也就是说,如果您声明任务:

    - include_tasks: roles/tasks/provision-tasks.yml
    

    该文件将包含在infra组中的每个主机上(在您的示例中是两次)。

  • 您似乎希望拥有一个数据结构的单个副本,其中包含每个VM的更新值。

    同时,您创建一个事实,它是为每个主机分别维护的单独数据对象。

    因此,您应该参考并修改(combine)一个事实-例如,您可以在localhost上进行修改。


您应该这样构造代码:

---
- name: Provision VMs
  hosts: infra
  become: true
  vars_files:
    - group_vars/vms.yml
    - group_vars/vars.yml
  tasks:
    - include_tasks: roles/tasks/provision-tasks.yml
    - debug:
        var: hostvars['localhost'].guests

provision-tasks.yml

- set_fact:
    guests: "{{ guests|combine({ current_host: {'tempip': tempip_reg.stdout_lines }}, recursive=True) }}"
  delegate_to: localhost

这将为您带来以下结果:

"hostvars['localhost'].guests": {
    "testvm1": {
        "clone": "template-centos",
        "cpus": 1,
        "ip": "192.168.1.60",
        "mem": 512,
        "tempip": [
            "192.168.1.21"
        ],
        "vmid": 102
    },
    "testvm2": {
        "clone": "template-centos",
        "cpus": 2,
        "ip": "192.168.1.61",
        "mem": 1536,
        "tempip": [
            "192.168.1.22"
        ],
        "vmid": 102
    }
}

最后,在上述播放中,您在错误的上下文中使用了group_varsroles/tasks目录。我保留了完整的路径,它们将适用于上面的代码,但是基本上,您永远不要以这种方式使用它们,因为它们再次在Ansible中具有特殊的意义和处理。