2

Due to a certain legacy setup I'm not allowed to change, some of our DNS zones are file-based rather than database-based, and they're transmitted from the primary to the secondary servers via rsync.

Recently I've been learning to automate various maintenance tasks via ansible playbooks, and the following playbook lets me reload all the auth servers automatically:

---
- hosts: auth
  name: Reload PDNS auth servers
  become: no

  tasks:
   - name: Check if PDNS is running
     service:
       name: pdns
       state: started
   - name: Execute reload command.
     command: /usr/bin/pdns_control reload

That works fine, and given that the reload command causes next to no increased load on the server it counts as "good enough" but my mild OCD gets twitchy and I'd really like to be able to tell it "Only perform the reload if there are any files in /etc/powerdns/bindbackend/zones/ newer than a designated timestamp file."

From what I'm seeing in the documentation I need to look at the when and/or find clauses -- run find and only execute the reload sequence if the number of results is greater than zero -- but I'm not sure how to formulate 'any files older than /tmp/timestamp' in ansible playbook syntax.

Any pointers?

1 Answer 1

2

This is what you're looking for, I think

  - name: Find timestamp
    find:
      paths: /tmp
      patterns: timestamp
    register: my_timestamp

  - name: Timestamp not found. End of host.
    block:
      - debug:
          msg: /tmp/timestamp not found. End of host.
      - meta: end_host
    when: my_timestamp.matched == 0

  - name: Find all zone files
    find:
      paths: "{{ zones_dir }}"
    register: my_zones

  - name: Find zone files newer than timestamp
    set_fact:
      my_zones_newer: "{{ my_zones.files|json_query(query)
                          map('basename')|
                          list }}"
    vars:
      query: "[?mtime > to_number('{{ my_timestamp.files.0.mtime }}')].path"

  - block:
      - name: "Reload {{ my_zones_newer|join(',') }}"
        command: /usr/bin/pdns_control reload
      - name: Touch timestamp
        file:
          state: touch
          path: /tmp/timestamp
    when: my_zones_newer|length > 0


Critical section between "Find all zone files" and "Touch timestamp"

The reload won't be triggered if a zone file is written during this critical section. to solve this problem, set mtime of the file /tmp/timestamp to the value of the newest zone file found to avoid the potential hazard. Change the block

  - block:
      - name: "Reload {{ my_zones_newer|join(',') }}"
        command: /usr/bin/pdns_control reload
      - set_fact:
          my_mtime_max: "{{ my_zones.files|json_query('[].mtime')|max }}"
      - file:
          state: touch
          modification_time: "{{ '%Y%m%d%H%M.%S'|
                                 strftime(my_mtime_max|
                                          float|
                                          round(precision=0, method='ceil')|
                                          int) }}"
          path: /tmp/timestamp
    when: my_zones_newer|length > 0

It seems that the granularity of modification_time_format is the second. See time.strftime. As a result, the variable my_mtime_max must be rounded up method='ceil' which leaves the hazard in this rounded fraction of the second.


Store mtime in timestamp

Robuster solution is to store mtime in the file timestamp. For example,

  - name: Read modification time from /tmp/timestamp (default=0)
    set_fact:
      my_mtime_max: "{{ lookup('pipe', my_command) }}"
    vars:
      my_command: sh -c '[ -e /tmp/timestamp ] && cat /tmp/timestamp || echo 0'

  - name: Find all zone files
    find:
      paths: "{{ zones_dir }}"
    register: my_zones

  - name: Find zone files newer than timestamp
    set_fact:
      my_zones_newer: "{{ my_zones.files|json_query(query)|
                          map('basename')|
                          list }}"
    vars:
      query: "[?mtime > to_number('{{ my_mtime_max }}')].path"

  - block:
      - name: "Reload {{ my_zones_newer|join(',') }}"
        command: /usr/bin/pdns_control reload
      - name: Set mtime of newest zone file to my_mtime_max
        set_fact:
          my_mtime_max: "{{ my_zones.files|json_query('[].mtime')|max }}"
      - name: Store my_mtime_max in /tmp/timestamp
        template:
          src: timestamp.j2
          dest: /tmp/timestamp
    when: my_zones_newer|length > 0
shell> cat timestamp.j2 
{{ my_mtime_max }}
2
  • That looks promising, I'll try it out. Commented May 13, 2020 at 11:28
  • I've updated the answer with the attempt to get rid of the critical section. Commented May 13, 2020 at 11:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .