Ansible playbook для запуска команд только при наличии более новых файлов

Большое спасибо @A.B, который восполнил некоторые недостающие части для меня, особенно в отношении семантики netnsids. Его PoC очень поучителен. Однако важным недостающим элементом в его PoC является то, как соотнести локальный netnsidс его глобально уникальным номером инода сетевого пространства имен, потому что только тогда мы можем однозначно соединить правильные соответствующие пары veth.

Подводя итоги и приведя небольшой пример Python, как собирать информацию программно, не полагаясь на ip netnsи необходимость монтирования :RTNETLINK фактически возвращает netnsid при запросе сетевых интерфейсов. Это атрибут IFLA_LINK_NETNSID, который появляется в информации о ссылке только при необходимости. Если его там нет, то он не нужен --и мы должны предположить, что индекс равноправного узла относится к интерфейсу локальной сети пространства имен -.

Важным уроком, который следует усвоить, является то, что netnsid/ IFLA_LINK_NETSID— это только локально определенное в пространстве имен сети, где вы получили его, когда запрашивали у RTNETLINK информацию о ссылке. netnsidс тем же значением, полученным в другом сетевом пространстве имен, может идентифицировать другое одноранговое пространство имен, поэтому будьте осторожны и не используйте netnsidза пределами своего пространства имен. Но какое уникально идентифицируемое сетевое пространство имен(inodeс номером )соответствует какому netnsid?

Как оказалось, самая последняя версияlsnsпо состоянию на март 2018 года вполне способна отображать правильный netnsidрядом с номером инода сетевого пространства имен! Таким образом, — это способ сопоставления локальных netnsidс инодами пространства имен, но на самом деле это наоборот! И это скорее оракул (со строчной буквой ell ), чем поиск :RTM _GETNSID требуется идентификатор пространства имен сети либо в виде PID, либо FD (в пространство имен сети ), а затем возвращается netnsid. См.https://stackoverflow.com/questions/50196902/retrieving-the-netnsid-of-a-network-namespace-in-pythonдля примера того, как запросить оракул сетевого пространства имен Linux.

Следовательно, вам необходимо перечислить доступные сетевые пространства имен (через /procи/или /var/run/netns),затем для данного vethсетевого интерфейса, присоединенного к сетевому пространству имен, где вы его нашли, запросите netnsidвсе сетевые пространства имен, которые вы перечислили в начале (, потому что вы никогда заранее не знаете, какое есть какое ), и, наконец, сопоставьте netnsidоднорангового узла vethс номером inode пространства имен в соответствии с локальной картой, которую вы создали на шаге 3 после присоединения к пространству имен veth.

import psutil
import os
import pyroute2
from pyroute2.netlink import rtnl, NLM_F_REQUEST
from pyroute2.netlink.rtnl import nsidmsg
from nsenter import Namespace

# phase I: gather network namespaces from /proc/[0-9]*/ns/net
netns = dict()
for proc in psutil.process_iter():
    netnsref= '/proc/{}/ns/net'.format(proc.pid)
    netnsid = os.stat(netnsref).st_ino
    if netnsid not in netns:
        netns[netnsid] = netnsref

# phase II: ask kernel "oracle" about the local IDs for the
# network namespaces we've discovered in phase I, doing this
# from all discovered network namespaces
for id, ref in netns.items():
    with Namespace(ref, 'net'):
        print('inside net:[{}]...'.format(id))
        ipr = pyroute2.IPRoute()
        for netnsid, netnsref in netns.items():
            with open(netnsref, 'r') as netnsf:
                req = nsidmsg.nsidmsg()
                req['attrs'] = [('NETNSA_FD', netnsf.fileno())]
                resp = ipr.nlm_request(req, rtnl.RTM_GETNSID, NLM_F_REQUEST)
                local_nsid = dict(resp[0]['attrs'])['NETNSA_NSID']
            if local_nsid != 2**32-1:
                print('  net:[{}] <--> nsid {}'.format(netnsid, local_nsid))

2
13.05.2020, 10:57
1 ответ

Я думаю, это то, что вы ищете

  - 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


Критический раздел между «Найти все файлы зон» и «Отметка времени касания»

Перезагрузка не будет запущена, если файл зоны будет записан во время этой критической секции. чтобы решить эту проблему, установите mtimeфайла /tmp/timestampна значение самого нового найденного файла зоны, чтобы избежать потенциальной опасности. Изменить блок

  - 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

Кажется, что степень детализации модификации _формата времени _является второй. См. time.strftime . В результате переменная my_mtime_maxдолжна быть округлена в большую сторону method='ceil', что оставляет опасность в этой округленной доле секунды.


Сохранить mtime в метке времени

Надежное решение состоит в том, чтобы сохранить mtimeв файле timestamp. Например,

  - 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
28.04.2021, 23:14

Теги

Похожие вопросы