简体   繁体   English

可变的嵌套dict变量

[英]ansible changing nested dict variable

After deployment of a VM with a DHCP IP I would like to get the IP and append it to the guests dictionary. 在使用DHCP IP部署VM之后,我想获取IP并将其附加到guest虚拟机字典中。

For the first VM (testvm2) the code perfoms as expected and updates the tempip variable for the testvm2 VM. 对于第一个VM(testvm2),代码将按预期执行,并更新testvm2 VM的tempip变量。

But with the second VM (testvm1), it updates the tempip variable of the first VM (testvm2) with the IP of the second VM (testvm1), and updates the tempip variable of the second VM (testvm1) with the code of the variable '{{ tempip_reg.stdout_lines }}' 但是对于第二个VM(testvm1),它将使用第二个VM(testvm1)的IP更新第一个VM(testvm2)的tempip变量,并使用变量的代码更新第二个VM(testvm1)的tempip变量'{{tempip_reg.stdout_lines}}'

Can anyone explain to me why this happens? 谁能向我解释为什么会这样? I would appreciate the help. 我会很感激的。

I copied all the relevant code and output below: 我复制了所有相关代码,并在下面输出:

guests dictionary: 来宾字典:

---
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 that starts the task: 启动任务的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 }}"

Ansible Tasks: Ansible任务:

- 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

Result first 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
    }
}

Result 2nd VM: 结果第二个虚拟机:

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

TL;DR TL; DR

Using Ansible code, you are trying to implement what Ansible already does for you. 使用Ansible代码,您正在尝试实现Ansible已经为您完成的工作。

Your attempts superimpose with built-in functionality and you get results which look nondeterministic. 您的尝试与内置功能叠加在一起,您会得到不确定的结果。


Explanation: 说明:

  • The main problem with your code is a completely unnecessary loop declared with with_dict: "{{ guests }}" which causes to include the file 4 times. 您的代码的主要问题是用with_dict: "{{ guests }}"声明的完全不必要的循环,这导致该文件包含4次。

    It runs 4 times because you change the guests dictionary, which it loops over inside the included tasks-file. 它运行4次是因为您更改了guests虚拟机字典,该字典在包含的task-file中循环。

    In effect you get something which looks like an nondeterministic result. 实际上,您得到的东西看起来像是不确定的结果。

  • The second problem is a trivial one: you always replace the value of tempip with a string {{ tempip_reg.stdout_lines }} . 第二个问题很简单:您总是用字符串{{ tempip_reg.stdout_lines }}替换tempip的值。

  • Now, because of the unnecessary with_dict loop over a dictionary which you dynamically change, and because Jinja2 uses lazy variable evaluation, strings from previous iterations are interpreted as templates and get evaluated with incorrect values in subsequent iterations. 现在,由于您可以动态地更改字典上的不必要的with_dict循环,并且由于Jinja2使用了惰性变量求值,因此先前迭代中的字符串被解释为模板,并在后续迭代中获得了不正确的值。

    The last iteration leaves the string {{ tempip_reg.stdout_lines }} intact. 最后一次迭代使字符串{{ tempip_reg.stdout_lines }}保持不变。

  • You also define and print two different facts. 您还定义和打印两个不同的事实。


What you should do: 您应该做什么:

  • You should not declare arbitrary iterations at all. 您根本不应该声明任意迭代。 Ansible implements a loop for all hosts itself. Ansible为所有主机本身实现一个循环。 That is, if you declare a task: 也就是说,如果您声明任务:

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

    the file will be included for each of the hosts in infra group (twice in your example). 该文件将包含在infra组中的每个主机中(在您的示例中为两次)。

  • You seem to want to have a single copy of your data structure with updated values for each VM. 您似乎希望拥有一个数据结构的单个副本,其中包含每个VM的更新值。

    At the same time, you create a fact, which is a separate data object maintained for each host separately. 同时,您创建一个事实,它是为每个主机单独维护的单独的数据对象。

    So you should refer to and modify ( combine ) a single fact - you can do it for example on localhost . 因此,您应该引用和修改( combine )单个事实-例如,可以在localhost


You should structure your code like this: 您应该这样构造代码:

---
- 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

and provision-tasks.yml : provision-tasks.yml

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

This will get you the following result: 这将为您带来以下结果:

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

Finally, in the above play, you used group_vars and roles/tasks directories in wrong context. 最后,在上述播放中,您在错误的上下文中使用了group_varsroles/tasks目录。 I left the paths intact and they will work for the above code, but basically you should never use them this way, because again, they have special meaning and treatment in Ansible. 我保留了完整的路径,它们将适用于上面的代码,但是基本上,您永远不要以这种方式使用它们,因为同样,它们在Ansible中具有特殊的意义和处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM