带有delegate_to localhost的ansible的lineinfile模块不会将所有数据写入localhost,它只会在localhost上写入1个随机条目

时间:2017-08-16 14:24:39

标签: ansible

我有3个远程VM和1个ansible节点。

我通过ansible shell模块在这些远程VM上运行hostname命令并在 hostname_output 变量中注册该输出来获取某些VM的主机名。

然后我想将这些VM的IP (使用gather_facts收集:True,{{ansible_default_ipv4.address}})添加到主机名中,并将其附加到文件 temp_hostname 在localhost上,因此我将任务委托给localhost。

但问题是,当我在控制台上看到时,lineinfile模块表示在为每个节点执行模块并将其委托给localhost时添加了该行,但是当我检查localhost上的文件时,只显示了1个条目在localhost而不是3。

---
- name: get hostnames of dynamically created VMs
  hosts: all
  remote_user: "{{ remote_user }}"
  gather_facts: True

  tasks:
   - name: save hostname in variable, as this command is executed remotely, and we want the value on the ansible node
     shell: hostname
     register: hostname_output

   - name: writing hostname_output in ansible node in file on ansible node
     lineinfile:
      line: "{{ ansible_default_ipv4.address }} {{ hostname_output.stdout }}"
      dest: temp_hostname
      state: present
     delegate_to: 127.0.0.1

我甚至尝试使用Ansible writing output from multiple task to a single file中指定的复制模块,但也给出了相同的结果,即仅1个条目。

---
- name: get hostnames of dynamically created VMs
  hosts: all
  remote_user: "{{ remote_user }}"
  gather_facts: True

  tasks:
   - name: save hostname in variable, as this command is executed remotely, and we want the value on the ansible node
     shell: hostname
     register: hostname_output

   - name: writing hostname_output in ansible node in file on ansible node
     copy:
        content: "{{ ansible_default_ipv4.address }} {{ hostname_output.stdout }}"
        dest: /volume200gb/sushil/test/code_hostname/temp_hostname
     delegate_to: 127.0.0.1

最后,当我使用带有重定向操作符的shell模块时,它按照我想要的方式工作,即localhost上的3个文件。

---
- name: get hostnames of dynamically created VMs
  hosts: all
  remote_user: "{{ remote_user }}"
  gather_facts: True

  tasks:
   - name: save hostname in variable, as this command is executed remotely, and we want the value on the ansible node
     shell: hostname
     register: hostname_output

   - name: writing hostname_output in ansible node in file on ansible node
     shell: echo -e "{{ ansible_default_ipv4.address }} {{ hostname_output.stdout }}" >> temp_hostname
     delegate_to: 127.0.0.1

我使用命令调用此ansible-playbook get_hostname.yml:

ansible-playbook -i hosts get_hostname.yml --ssh-extra-args =“ - o StrictHostKeyChecking = no”--extra-vars“remote_user = cloud-user”-vvv

我的主机文件是:

10.194.11.86 private_key_file=/root/.ssh/id_rsa
10.194.11.87 private_key_file=/root/.ssh/id_rsa
10.194.11.88 private_key_file=/root/.ssh/id_rsa

我使用的是ansible 2.1.0.0

我只使用默认的ansible.cfg,没有修改

我的问题是为什么lineinfile和copy模块不起作用?我错过了什么或写错了吗

3 个答案:

答案 0 :(得分:0)

使用第一个带有lineinfile的代码块,我得到了不一致的结果。有时我在目标文件中获得所有3个IP和主机名,有时我只得到2.我不确定为什么会这样,但我的猜测是Ansible正在尝试同时保存对文件的更改而且只是一个变化被接受了。

第二个代码块无法正常工作,因为复制将覆盖该文件,除非内容与已存在的内容相匹配。运行的最后一个主机将是目标文件中唯一的IP /主机名。

要解决此问题,您可以循环播放play_hosts(当前播放中的活动主持人)并使用hostvars引用其变量。

- name: writing hostname_output in ansible node in file on ansible node
  lineinfile:
    line: "{{ hostvars[item]['ansible_default_ipv4'].address }} {{ hostvars[item]['hostname_output'].stdout }}"         
    dest: temp_hostname
    state: present
  delegate_to: 127.0.0.1
  run_once: True
  with_items: "{{ play_hosts }}"

或者您可以使用具有相同逻辑的模板

- name: writing hostname_output in ansible node in file on ansible node
  template:
    src: IP_hostname.j2
    dest: temp_hostname
  delegate_to: 127.0.0.1
  run_once: True

IP_hostname.j2

{% for host in play_hosts %}
{{ hostvars[host]['ansible_default_ipv4'].address }} {{ hostvars[host]['hostname_output'].stdout }}
{% endfor %}

答案 1 :(得分:0)

我试图重现你的问题并且它没有发生在我身上,我怀疑这是你的ansible版本的问题,尝试使用最新版本。

话虽如此,我认为您可以使用serial: 1使其工作,这可能是文件锁定的一个问题,我不会在ansible 2.3中看到发生。我还认为,不是使用shell任务来收集主机名,而是可以使用作为安全事实提供的ansible_hostname变量,如果你想要的只是主机名,你还可以避免收集所有事实。具体的任务。最后,它看起来像这样:

---
- name: get hostnames of dynamically created VMs
  hosts: all
  serial: 1  
  remote_user: "{{ remote_user }}"

  tasks:
  - name: Get hostnames
    setup:
      filter: ansible_hostname

  - name: writing hostname_output in ansible node in file on ansible node
    lineinfile:
      line: "{{ ansible_default_ipv4.address }} {{ ansible_hostname }}"
      dest: temp_hostname
      state: present
    delegate_to: 127.0.0.1

答案 2 :(得分:0)

问题就在这里,只有一个文件有多个并发写入。这会导致意想不到的结果:

对此的解决方案是在您的游戏中使用 serial: 1,这会强制您的主机之间非并行执行。

但它可能会成为性能杀手,具体取决于主机数量。

我建议使用另一种解决方案:不是只写入一个文件,每个主机代表团都可以写入自己的文件(此处使用 inventory_hostname 值)。因此,它将没有更多的并发写入。

之后,您可以使用模块 assemble 将所有文件合并为一个。这是一个示例(未经测试):

---
- name: get hostnames of dynamically created VMs
  hosts: all
  remote_user: "{{ remote_user }}"
  gather_facts: True

  tasks:
  - name: save hostname in variable, as this command is executed remotely, and we want the value on the ansible node
    shell: hostname
    register: hostname_output

  - name: deleting tmp folder
    file: path=/tmp/temp_hostname state=absent
    delegate_to: 127.0.0.1
    run_once: true

  - name: create tmp folder
    file: path=/tmp/temp_hostname state=directory
    delegate_to: 127.0.0.1
    run_once: true

  - name: writing hostname_output in ansible node in file on ansible node
    template: path=tpl.j2 dest=/tmp/temp_hostname/{{ inventory_hostname }}
    delegate_to: 127.0.0.1

  - name: assemble hostnames
    assemble: src=/tmp/temp_hostname/ dest=temp_hostname
    delegate_to: '{{ base_rundeck_server }}'
    run_once: true

显然您必须创建 tpl.j2 文件。