运行 整个剧本只有一次 Ansible 处理程序

Run an Ansible handler only once for the entire playbook

我想 运行 一个处理程序在整个剧本中只出现一次。

我尝试在剧本文件的以下内容中使用 include 语句,但这导致处理程序 运行 多次,每次播放一次:

- name: Configure common config
  hosts: all
  become: true
  vars:
        OE: "{{ ansible_hostname[5] }}"
  roles:
    - { role: common }
  handlers:
    - include: handlers/main.yml

- name: Configure metadata config
  hosts: metadata
  become: true
  vars:
        OE: "{{ ansible_hostname[5] }}"
  roles:
    - { role: metadata }
  handlers:
    - include: handlers/main.yml

这里是handlers/main.yml的内容:

- name: restart autofs
  service:
    name: autofs.service
    state: restarted

这是通知处理程序的任务之一的示例:

- name: Configure automount - /opt/local/xxx in /etc/auto.direct
  lineinfile:
     dest: /etc/auto.direct
     regexp: "^/opt/local/xxx"
     line: "/opt/local/xxx   -acdirmin=0,acdirmax=0,rdirplus,rw,hard,intr,bg,retry=2  nfs_server:/vol/xxx"
  notify: restart autofs

如何让剧本对整个剧本只执行一次处理程序?

我不清楚你的处理程序应该做什么。总之,至于official documentation,handlers

are triggered at the end of each block of tasks in a play, and will only be triggered once even if notified by multiple different tasks [...] As of Ansible 2.2, handlers can also “listen” to generic topics, and tasks can notify those topics as follows:

因此,每个任务块都会通知/执行一次处理程序。 也许您只是在 "all" 目标主机之后保留处理程序而达到了目标,但这似乎并不是对处理程序的干净使用。 .

答案

标题中问题的字面答案是:没有。

剧本是剧本列表。 Playbook 没有命名空间、没有变量、没有状态。所有配置、逻辑和任务都在 plays 中定义。

处理程序是一个具有不同调用计划的任务(不是顺序的,而是有条件的,在播放结束时一次,或由 meta: flush_handlers 任务触发)。

一个处理程序属于一个剧本,而不是一个剧本,并且没有办法在剧本之外触发它(即在剧本的末尾)。


解决方案

无需参考处理程序即可解决问题。

您可以使用 group_by module 根据每个游戏底部的任务结果创建一个 ad-hoc 组。

然后您可以在 playbook 的末尾定义一个单独的 play,在属于上述 ad-hoc 组的目标上重新启动服务。

请参考以下存根的想法:

- hosts: all
  roles:
    # roles declaration
  tasks:
    - # an example task modifying Nginx configuration
      register: nginx_configuration

    # ... other tasks ...

    - name: the last task in the play
      group_by:
        key: hosts_to_restart_{{ 'nginx' if nginx_configuration is changed else '' }}

# ... other plays ...

- hosts: hosts_to_restart_nginx
  gather_facts: no
  tasks:
    - service:
        name: nginx
        state: restarted

可能的解决方案

使用处理程序将主机添加到内存清单。然后添加play到运行只为这些主机重启服务。 看这个例子:

如果任务发生变化,它会通知mark to restart设置事实,主机需要重启服务。

第二个处理程序 add host 很特别,因为 add_host 任务只 运行 整个播放一次,即使在处理程序中,另请参阅 documentation。但是,如果收到通知,它将在标记完成后 运行 从处理程序命令中暗示。 处理程序循环执行任务 运行 的主机,并检查主机服务是否需要重启,如果需要,添加到特殊的 hosts_to_restart 组。

因为事实在所有游戏中都是持久的,请通知受影响主机的第三个处理程序 clear mark

您通过移动处理程序隐藏了很多行以分隔文件并包含它们。

库存文件

10.1.1.[1:10]
[primary]
10.1.1.1
10.1.1.5

test.yml

---

- hosts: all
  gather_facts: no
  tasks:
      - name: Random change to notify trigger
        debug: msg="test"
        changed_when: "1|random == 1"
        notify:
            - mark to restart
            - add host
            - clear mark
  handlers:
      - name: mark to restart
        set_fact: restart_service=true
      - name: add host
        add_host:
            name: "{{item}}"
            groups: "hosts_to_restart"
        when: hostvars[item].restart_service is defined and hostvars[item].restart_service
        with_items: "{{ansible_play_batch}}"
      - name: clear mark
        set_fact: restart_service=false

- hosts: primary
  gather_facts: no
  tasks:
      - name: Change to notify trigger
        debug: msg="test"
        changed_when: true
        notify:
            - mark to restart
            - add host
            - clear mark
  handlers:
      - name: mark to restart
        set_fact: restart_service=true
      - name: add host
        add_host:
            name: "{{item}}"
            groups: "hosts_to_restart"
        when: hostvars[item].restart_service is defined and hostvars[item].restart_service
        with_items: "{{ansible_play_batch}}"
      - name: clear mark
        set_fact: restart_service=false


- hosts: hosts_to_restart
  gather_facts: no
  tasks:
      - name: Restart service
        debug: msg="Service restarted"
        changed_when: true

post_tasks 中触发的处理程序将在 运行 之后触发。处理程序可以设置为 run_once: true.