Фильтрация узлов Ansible с определённым зарегистрированным значением

Вопрос или проблема

Мне нужна помощь, чтобы отфильтровать хосты с определенным предыдущим результатом, чтобы иметь возможность использовать модуль add_host. Кажется, что add_host игнорирует условие “when”, поэтому, вероятно, мне нужно сделать цикл для нужных хостов.

- name: Получить процесс chrome
  ansible.windows.win_powershell:
    script: |
      (get-process chrome -ErrorAction SilentlyContinue).count -gt 0
  register: ps_chrome_running

Теперь мне нужно отфильтровать хосты, где ps_chrome_running.output[0] равно true, и поместить эти хосты в новую группу.

- name: Добавить в список пропуска
  ansible.builtin.add_host:
    name: '{{ item }}'
    groups: 'skip_chrome_actions'
  loop: "{{ hostvars|dict2items| selectattr('ps_chrome_running.output[0]', '==', true)| map(attribute="key") }}"
  delegate_to: localhost
  run_once: true

Я уже пробовал разные методы, но как-то не могу добиться правильного результата.
Может кто-то помочь с фильтром цикла, чтобы получить только список хостов, где ps_chrome_running.output[0] == true?

+1: ps_chrome_running.output[0] уже является boolean

Регистрация выглядит так:

changed: [server1] => {
"changed": true,
"debug": [],
"error": [],
"host_err": "",
"host_out": "",
"information": [],
"invocation": {
    "module_args": {
        "arguments": null,
        "chdir": null,
        "creates": null,
        "depth": 2,
        "error_action": "continue",
        "executable": null,
        "parameters": null,
        "removes": null,
        "script": "(get-process chrome -ErrorAction SilentlyContinue).count -eq 0\n",
        "sensitive_parameters": null
    }
},
"output": [
    true
],
"result": {},
"verbose": [],
"warning": []
}

ОБНОВЛЕНИЕ:
Этот плейбук:

- name: "Получить процессы Chrome"
  hosts: all
  gather_facts: no
  tasks:
    - name: Получить процесс chrome
      ansible.windows.win_powershell:
        script: |
          (get-process chrome -ErrorAction SilentlyContinue).count -gt 0
      register: ps_chrome_running
      # failed_when: ps_chrome_running.output[0]

    - name: Проверить vars1
      ansible.builtin.debug:
        msg: "item : {{ item }}"
      loop: "{{ hostvars | dict2items | json_query('[?value.ps_chrome_running.output[0]].key') }}"
      delegate_to: localhost
      run_once: true
      ignore_errors: true

    - name: Добавить кандидат в хосты
      ansible.builtin.debug:
        msg: "host: {{ item }}"
      loop: "{{ hostvars | dict2items | json_query('[?value.ps_chrome_running.output[0]].key') }}"
      delegate_to: localhost
      run_once: true
      ignore_errors: true

    - name: Добавить в список пропуска
      ansible.builtin.add_host:
        name: '{{ item }}'
        groups: 'skip_chrome_update'
      loop: "{{ hostvars | dict2items | json_query('[?value.ps_chrome_running.output[0]].key') }}"
      delegate_to: localhost
      run_once: true

    - debug:
        var: groups.skip_chrome_actions
      delegate_to: localhost
      run_once: true

- name: Обновить Chrome
  hosts: '!skip_chrome_update'
  gather_facts: no
  tasks:
    - name: Дополнительная задача
      ansible.builtin.debug:
        msg: "должно выполняться на предыдущем успехе"

    - name: Дополнительная задача2
      ansible.windows.win_powershell:
        script: |
          write-output $env:computername

Дает следующий вывод:

PLAY [Получить процессы Chrome] ****************************************************

TASK [Получить процесс chrome] ******************************************************
changed: [Windows1] => {"changed": true, "debug": [], "error": [], "host_err": "", "host_out": "", "information": [], "output": [false], "result": {}, "verbose": [], "warning": []}
changed: [Windows2] => {"changed": true, "debug": [], "error": [], "host_err": "", "host_out": "", "information": [], "output": [false], "result": {}, "verbose": [], "warning": []}

TASK [Проверить vars1] *************************************************************
skipping: [Windows1] => {"skipped_reason": "No items in the list"}

TASK [Добавить кандидат в хосты] ******************************************************
skipping: [Windows1] => {"skipped_reason": "No items in the list"}

TASK [Добавить в список пропуска] ********************************************************
skipping: [Windows1] => {"changed": false, "skipped_reason": "No items in the list"}

TASK [debug] *******************************************************************
ok: [Windows1 -> localhost] => {
    "groups.skip_chrome_actions": "Переменная не определена!: 'объект dict' не имеет атрибута 'skip_chrome_actions'."
}
[WARNING]: Не удалось найти соответствующий паттерн хоста, игнорируется: skip_chrome_update

PLAY [Обновить Chrome] ***********************************************************

TASK [Дополнительная задача] ***************************************************************
ok: [Windows1] => {
    "msg": "должно выполняться на предыдущем успехе"
}
ok: [Windows2] => {
    "msg": "должно выполняться на предыдущем успехе"
}

TASK [Дополнительная задача2] **************************************************************
changed: [Windows1] => {"changed": true, "debug": [], "error": [], "host_err": "", "host_out": "", "information": [], "output": ["SM835222"], "result": {}, "verbose": [], "warning": []}
changed: [Windows2] => {"changed": true, "debug": [], "error": [], "host_err": "", "host_out": "", "information": [], "output": ["SM834069"], "result": {}, "verbose": [], "warning": []}

PLAY RECAP *********************************************************************
Windows2 : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
Windows1 : ok=5    changed=2    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0

add_host учитывает условие when. ansible-doc add_host указывает на это как ignore_conditional none, что является странной двойной отрицательной формой сказать, что when работает.

Возможно, ваше выражение является строкой, которую необходимо преобразовать в тип boolean. Поскольку непустая строка является истинным значением, что не является тем, что было задумано, если строка “False”. В Ansible Jinja фильтр bool будет истинным, если значение (в нижнем регистре) включает в себя: True, ‘yes’, ‘on’, ‘1’, ‘true’, 1. И ложным в противном случае.

- name: Получить процесс chrome
  ansible.windows.win_powershell:
    script: |
      (get-process chrome -ErrorAction SilentlyContinue).count -gt 0
  register: ps_chrome_running

- name: Добавить в список пропуска
  when: "{{ ps_chrome_running.output[0] | bool | default(False) }}"
  ansible.builtin.add_host:
    name: "{{ inventory_hostname }}"
    groups: "skip_chrome_actions"

В дополнение я добавил по умолчанию false для условия when на случай, если выражение когда-либо не будет определено. Это может считаться опциональным, не каждое выражение Ansible необходимо защищать таким образом.

После вашей неизмененной задачи get-process, add_host может быть выполнено в цикле хоста. Поскольку мы добавляем этот хост, магическая переменная inventory_hostname является его именем. (add_host это плагин действия без кода модуля, они всегда выполняются на localhost)

Используя эту группу в памяти, самый понятный способ — запустить новую игру с целью skip_chrome_actions с паттерном.

Как обычно с инвентарем, вы можете также немного схитрить. inventory_hostnames lookup вернет список хостов, соответствующих некоторому паттерну. Или можно использовать магическую переменную groups, и если имя хоста находится в groups.skip_chrome_actions.

Этот выбор неверен

loop: "{{ hostvars | dict2items | selectattr('ps_chrome_running.output[0]', '==', true) | map(attribute="key") }}"

Из-за фильтра dict2items вам нужно ссылаться на value.ps_chrome_running

loop: "{{ hostvars | dict2items | selectattr('value.ps_chrome_running.output[0]', '==', true) | map(attribute="key") }}"

Вы можете упростить выбор. Если переменная является булевской, сравнение не требуется

loop: "{{ hostvars | dict2items | selectattr('value.ps_chrome_running.output[0]') | map(attribute="key") }}"

Используйте json_query вместо selectattr. Фильтр selectattr остановится с ошибкой, если переменная ps_chrome_running не определена. Это может быть так, когда localhost находится в hostvars. Фильтр json_query спокойно игнорирует отсутствующие переменные. Следующий цикл — лучший вариант

loop: "{{ hostvars | dict2items | json_query('[?value.ps_chrome_running.output[0]].key') }}"

Пример полного плейбука для тестирования

shell> cat pb.yml
- hosts: all

  tasks:

    - debug:
        var: ps_chrome_running.output.0

    - add_host:
        name: "{{ item }}"
        groups: skip_chrome_actions
      loop: "{{ hostvars | dict2items | json_query('[?value.ps_chrome_running.output[0]].key') }}"
      run_once: true

    - debug:
        var: groups.skip_chrome_actions
      run_once: true

дает

shell> ansible-playbook pb.yml -i hosts

PLAY [all] **********************************************************************************************************

TASK [debug] ********************************************************************************************************
ok: [test_01] => 
    ps_chrome_running.output.0: true
ok: [test_02] => 
    ps_chrome_running.output.0: true
ok: [test_03] => 
    ps_chrome_running.output.0: false

TASK [add_host] *****************************************************************************************************
changed: [test_01] => (item=test_01)
changed: [test_01] => (item=test_02)

TASK [debug] ********************************************************************************************************
ok: [test_01] => 
    groups.skip_chrome_actions:
    - test_01
    - test_02

PLAY RECAP **********************************************************************************************************
test_01: ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
test_02: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
test_03: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Учитывая дерево

shell> tree .
.
├── ansible.cfg
├── hosts
├── host_vars
│   ├── test_01.yml
│   ├── test_02.yml
│   └── test_03.yml
└── pb.yml
shell> cat ansible.cfg 
[defaults]
gathering = explicit
inventory = $PWD/hosts
callback_result_format = yaml

[connection]
pipelining = true
shell> cat hosts 
test_01
test_02
test_03
shell> cat host_vars/test_01.yml 
ps_chrome_running:
  output:
    - true

shell> cat host_vars/test_02.yml 
ps_chrome_running:
  output:
    - true

shell> cat host_vars/test_03.yml 
ps_chrome_running:
  output:
    - false

Ответ или решение

Вопрос о том, как отфильтровать хосты с определенным зарегистрированным значением в Ansible, является актуальной задачей для специалистов по ИТ, особенно при управлении большими сетями серверов. Атрибуты и их фильтрация в Ansible позволяет автоматизировать администрирование серверов и управлять конфигурациями более эффективно. Давайте подробно разберем эту проблему, применяя метод TEA (Теория, Пример, Применение).

Теория

Фильтрация хостов на основе зарегистрированных значений — это подход, который используется для управления группами хостов в зависимости от состояния или результатов выполненных находок. В Ansible зарегистрированные переменные сохраняют данные о выполнении задач, которые могут использоваться в последующих задачах для принятия решений. Основной задачей является обработка данных с помощью фильтров Jinja2 и модулей Ansible, таких как add_host, чтобы создать новые группы хостов на основе определенных критериев.

Модуль ansible.windows.win_powershell позволяет запускать PowerShell-скрипты на Windows-узлах, регистрируя резильтаты выполнения скриптов в переменных. Эта информация впоследствии используется для принятия решения о добавлении хоста в определённую группу.

Пример

В предложенном сценарии (playbook) выполняется следующая последовательность команд:

  1. Выполнение PowerShell-скрипта на всех хостах:

    - name: Get chrome process
      ansible.windows.win_powershell:
        script: |
          (get-process chrome -ErrorAction SilentlyContinue).count -gt 0
      register: ps_chrome_running

    Здесь скрипт проверяет, запущен ли процесс Chrome, и сохраняет результат (true или false) в ps_chrome_running.output[0].

  2. Фильтрация и добавление в группу:

    - name: Put on skip list
      ansible.builtin.add_host:
        name: '{{ item }}'
        groups: 'skip_chrome_update'
      loop: "{{ hostvars
        | dict2items
        | json_query('[?value.ps_chrome_running.output[0]].key') }}"
      delegate_to: localhost
      run_once: true

    Этот код фильтрует хосты, у которых ps_chrome_running.output[0] равно true, и добавляет их в группу skip_chrome_update.

Важный момент здесь — использование фильтра json_query, который корректно обрабатывает отсутствующие переменные, избегая ошибок. Это особенность делает полученное решение более стабильным.

Применение

Применяя описанные выше методы, специалисты могут эффективно управлять серверной инфраструктурой. Например, администраторы могут автоматически группировать хосты на основе установленных процессов или других системных показателей и целенаправленно выполнять обновления или обходить изменения на специфических узлах.

Следуя этому шаблону, можно настроить выполнение других задач с учетом состояния системы. Например, задача по обновлению программного обеспечения может автоматически игнорировать хосты с критическими процессами, минимизируя риски отключения важных сервисов.

В результате такой подход не только автоматизирует и упрощает администрирование серверов, но и значительно повышает надежность и управляемость операций, позволяя быстрее реагировать на изменения в инфраструктуре.

Заключение

Таким образом, умение правильно фильтровать хосты и управлять ними на основании зарегистрированных данных открывает широкие возможности для оптимизации и автоматизации в Ansible. Знание и практическое применение таких методик позволяет минимизировать ручной труд, а также существенно улучшает управляемость и масштабируемость систем.

Оцените материал
Добавить комментарий

Капча загружается...