
Я пытаюсь извлечь имена хостов и IP-адреса из списка хостов, введенного пользователем, и отправить эту информацию на центральный сервер. Основная проблема, с которой я сталкиваюсь, заключается в том, что количество хостов может значительно различаться. Например, при первом запуске пользователь может ввести 1 имя хоста, при втором запуске ввести 30, а при следующем — 5. Я хочу иметь возможность использовать один плейбук, вводит ли пользователь 1 или 100 хостов.
Имена хостов собираются с помощью запроса «дополнительной переменной» при запуске шаблона Ansible Tower:
client_hosts: 'host1,host2'
которые затем упоминаются в книге пьес:
- name: Gather client information
hosts:
- "{{ client_hosts | default(omit) }}"
tasks:
- name: Grab client hostname
shell: cat /etc/hostname
register: client_hostname
- name: Grab client IP address
shell: hostname -i | sed -n '1 p'
register: client_ip
и далее по схеме я хочу добавить эти IP-адреса и имена хостов в файл на определенном центральном сервере (имя хоста сервера не меняется):
- name: Update server
hosts: central.server
tasks:
- name: Update client host list
lineinfile:
path: /path/to/file
line: "{{ hostvars['client_hosts']['client_ip'] }} - {{ hostvars['client_hosts']['client_hostname'] }}"
Вышеприведенный пример отлично работает для одного хоста, но как мне выполнить цикл регистрации переменных, когда указано более одного хоста (например, client_hostname[1,2,*]?), и обновить сервер этими значениями, когда я заранее не знаю, сколько хостов будет введено?
решение1
Этот вариант использования состоит из трех частей: 1) Управление инвентарем, 2) Сборимя_хоста_клиентаиклиент_ipи 3) Отчет на центральный сервер.
1. Управляйте запасами
Существует множество вариантов управления запасами и сбораимя_хоста_клиентаиклиент_ip. Например, используйтеДобавление диапазонов хостовесли вы планируете создать сотни хостов с простыми именами, например host001, hosts002, ..., host999
. Например, в инвентаре ниже добавьтецентральный_сервер, localhost для простоты и сто хостов в группетест
shell> cat inventory/01-hosts
central_server ansible_host=localhost
[test]
host[001:100]
[test:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/usr/local/bin/python3.8
Кратко протестируйте инвентарь
- hosts: all
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts|length
run_once: true
дать сокращенный
ansible_play_hosts|length: '101'
Выполните команду ниже, если вы хотите отобразить полный инвентарь.
shell> ansible-inventory -i inventory --list --yaml
Тогда есть много вариантов, как выбрать хостов. СмотретьШаблоны: нацеливание на хосты и группы. Например,пределинвентарь для определенных хостов или групп. Протестируйте его с помощью простого плейбука ниже
shell> cat pb1.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: inventory_hostname
давать
shell> ansible-playbook -i inventory pb1.yml -l host001,host002
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host001] =>
inventory_hostname: host001
ok: [host002] =>
inventory_hostname: host002
PLAY RECAP ***********************************************************************************
host001: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host002: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Используйте плагин инвентаризациипостроенныйесли вы хотите ограничить инвентарь для большей группы хостов. См.
shell> ansible-doc -t inventory constructed
Например, дополнительная переменнаяколичество_хостовиспользуется в примере ниже для создания группымоя группавключающие хосты, ограниченные значением этой переменной
shell> cat inventory/02-constructed.yml
plugin: constructed
strict: true
use_extra_vars: true
compose:
my_group_count: count_hosts|default(0)
groups:
my_group: inventory_hostname[-2:]|int < my_group_count|int
Попробуй это
shell> ansible-playbook -i inventory pb.yml -e count_hosts=10 -l my_group
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host001] =>
ansible_play_hosts|length: '11'
PLAY RECAP ***********************************************************************************
host001: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Выполните команду ниже, если вы хотите отобразить полный инвентарь.
shell> ansible-inventory -i inventory -e count_hosts=10 --list --yaml
С помощьюпостроенныйплагин, вы можете создавать сложные условия. Например, ограничить хосты определенным интервалом и создать группумоя_группа2
shell> cat inventory/02-constructed.yml
plugin: constructed
strict: true
use_extra_vars: true
compose:
my_group_count: count_hosts|default(0)
my_group_start: start_hosts|default(0)
my_group_stop: stop_hosts|default(0)
groups:
my_group1: inventory_hostname[-2:]|int < my_group_count|int
my_group2: inventory_hostname[-2:]|int >= my_group_start|int and
inventory_hostname[-2:]|int < my_group_stop|int
Попробуй это
shell> ansible-playbook -i inventory pb1.yml -e start_hosts=10 -e stop_hosts=15 -l my_group2
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host010] =>
inventory_hostname: host010
ok: [host011] =>
inventory_hostname: host011
ok: [host012] =>
inventory_hostname: host012
ok: [host013] =>
inventory_hostname: host013
ok: [host014] =>
inventory_hostname: host014
...
2. Собратьимя_хоста_клиентаиклиент_ip
Вы можете использовать любой модульнастраиватьили собирать факты самостоятельно. Из-за портативностинастраиватьследует отдать предпочтение.
См. модульнастраиватьо том, как собирать факты об удаленных хостах. Например, нижеприведенный плейбук собирает факты омашинаисетьи создает переменныеимя_хоста_клиентаиклиент_ip
shell> cat pb2.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
- debug:
var: client_hostname
- debug:
var: ansible_default_ipv4
- debug:
var: ansible_all_ipv4_addresses
- set_fact:
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- debug:
var: client_ip
давать
shell> ansible-playbook -i inventory -l host011 pb2.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host011]
TASK [set_fact] ******************************************************************************
ok: [host011]
TASK [debug] *********************************************************************************
ok: [host011] =>
client_hostname: test_11
TASK [debug] *********************************************************************************
ok: [host011] =>
ansible_default_ipv4: {}
TASK [debug] *********************************************************************************
ok: [host011] =>
ansible_all_ipv4_addresses:
- 10.1.0.61
TASK [set_fact] ******************************************************************************
ok: [host011]
TASK [debug] *********************************************************************************
ok: [host011] =>
client_ip: 10.1.0.61
PLAY RECAP ***********************************************************************************
host011: ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Структура и формат фактов могут различаться в разных операционных системах.
Вы можете собирать факты самостоятельно. Например, ниже приведена инструкция.
shell> cat pb3.yml
- hosts: all
gather_facts: false
tasks:
- name: Grab client hostname
command: cat /etc/hostname
register: out
- set_fact:
client_hostname: "{{ out.stdout }}"
- debug:
var: client_hostname
- name: Grab client IP address
shell: hostname -i | sed -n '1 p'
register: out
- set_fact:
client_ip: "{{ out.stdout|split|last }}"
- debug:
var: client_ip
дайте запустить на Linux
shell> ansible-playbook -i inventory -l central_server pb3.yml
PLAY [all] ***********************************************************************************
TASK [Grab client hostname] ******************************************************************
changed: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [debug] *********************************************************************************
ok: [central_server] =>
client_hostname: central_server
TASK [Grab client IP address] ****************************************************************
changed: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [debug] *********************************************************************************
ok: [central_server] =>
client_ip: 10.1.0.22
PLAY RECAP ***********************************************************************************
central_server: ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Вывод утилит может различаться в зависимости от операционных систем.
3) Отчет на центральный сервер
Используйте Jinja для создания структуры. Запустите задачу один раз и делегируйте еецентральный_сервер
shell> cat pb4.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- copy:
dest: /tmp/test_host_ip.txt
content: |
{% for host in ansible_play_hosts %}
{{ hostvars[host]['client_hostname'] }} - {{ hostvars[host]['client_ip'] }}
{% endfor %}
run_once: true
delegate_to: central_server
давать
shell> ansible-playbook -i inventory -l host011,host013 pb4.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host013]
ok: [host011]
TASK [set_fact] ******************************************************************************
ok: [host011]
ok: [host013]
TASK [copy] **********************************************************************************
changed: [host011 -> central_server(localhost)]
PLAY RECAP ***********************************************************************************
host011: ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host013: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Пособие создало файл вцентральный_сервер
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
Используйте модульlineinfileесли вы хотите добавить строки в файл. Нижеприведенный плейбук является идемпотентным
shell> cat pb5.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- lineinfile:
path: /tmp/test_host_ip.txt
line: |-
{{ hostvars[item]['client_hostname'] }} - {{ hostvars[item]['client_ip'] }}
loop: "{{ ansible_play_hosts }}"
run_once: true
delegate_to: central_server
При повторном запуске на одних и тех же хостах никаких изменений не произойдет.
shell> ansible-playbook -i inventory -l host011,host013 pb5.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host011]
ok: [host013]
TASK [set_fact] ******************************************************************************
ok: [host011]
ok: [host013]
TASK [lineinfile] ****************************************************************************
ok: [host011 -> central_server(localhost)] => (item=host011)
ok: [host011 -> central_server(localhost)] => (item=host013)
PLAY RECAP ***********************************************************************************
host011: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host013: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Если сценарий запускается на новом хосте(ах), в файл будут добавлены новые строки.
shell> ansible-playbook -i inventory -l central_server pb5.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [lineinfile] ****************************************************************************
changed: [central_server] => (item=central_server)
PLAY RECAP ***********************************************************************************
central_server: ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Новая строка была добавлена в файл
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
central_server - 10.1.0.184
решение2
Вы можете пройти по переменной client_hosts, используя директиву with_items в вашем плейбуке. Затем вы можете ссылаться на каждый отдельный хост, используя переменную item в цикле.
Вот пример того, как можно изменить схему действий для работы с несколькими хостами:
- name: Gather client information
hosts: "{{ client_hosts | default(omit) }}"
tasks:
- name: Grab client hostname and IP address
shell: |
hostname -i | sed -n '1 p' > /tmp/client_ip
cat /etc/hostname > /tmp/client_hostname
register: gather_client_info
become: true
- name: Set client hostname and IP address as variables
set_fact:
client_hostname: "{{ hostvars[item]['gather_client_info'].stdout_lines[1] }}"
client_ip: "{{ hostvars[item]['gather_client_info'].stdout_lines[0] }}"
with_items: "{{ client_hosts | default(omit) }}"
- name: Update server
hosts: central.server
tasks:
- name: Update client host list
lineinfile:
path: /path/to/file
line: "{{ client_ip }} - {{ client_hostname }}"
with_items: "{{ client_hosts | default(omit) }}"
Этот сценарий будет проходить по каждому хосту в переменной client_hosts и собирать имя хоста и IP-адрес с помощью модуля оболочки. Затем он установит эти значения как переменные с помощью модуля set_fact. Наконец, он снова пройдет по переменной client_hosts и использует модуль lineinfile для обновления файла на центральном сервере с именем хоста и IP-адресом для каждого хоста.
Надеюсь это поможет.