6

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

我尝试在 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

我怎样才能让剧本只为整个剧本执行一次处理程序?

4

4 回答 4

13

答案

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

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

Handler 是具有不同调用时间表的任务(不是顺序的,而是有条件的,一次在播放结束时,或者由meta: flush_handlers任务触发)。

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


解决方案

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

您可以使用group_by模块根据每次播放底部的任务结果创建一个临时组。

然后,您可以在 playbook 的末尾定义一个单独的播放,在属于上述 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
于 2017-01-16T09:45:16.073 回答
2

可能的解决方案

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

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

第二个处理程序add host非常特殊,因为add_host即使在处理程序中,任务也只在整个播放过程中运行一次,另见文档。但是如果收到通知,它将在处理程序命令中隐含的标记完成后运行。处理程序遍历运行任务的主机并检查主机服务是否需要重新启动,如果是,则添加到特殊hosts_to_restart组。

因为事实在播放中是持久的,所以通知clear mark受影响主机的第三个处理程序。

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

库存文件

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

测试.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
于 2018-09-09T11:35:37.583 回答
1

触发的处理程序post_tasks将在其他所有内容之后运行。并且处理程序可以设置为run_once: true.

于 2020-12-08T23:27:39.707 回答
0

我不清楚你的处理程序应该做什么。无论如何,至于官方文档,处理程序

在 play 中的每个任务块结束时触发,即使由多个不同的任务通知,也只会触发一次[...] 从 Ansible 2.2 开始,处理程序还可以“监听”通用主题,任务可以通知这些主题如下:

因此,处理程序会为每个任务块通知/执行一次。可能您的目标只是在“所有”目标主机之后保留处理程序,但这似乎不是对处理程序的干净使用。.

于 2017-01-09T21:03:42.950 回答