
Estoy intentando obtener nombres de host y direcciones IP de una lista de hosts ingresada por el usuario y enviar esa información a un servidor central. El principal problema con el que me encuentro es que la cantidad de hosts puede variar considerablemente. Por ejemplo, en la primera ejecución, un usuario puede ingresar 1 nombre de host, en la segunda ejecución ingresar 30 y en la siguiente ingresar 5. Quiero poder usar un único libro de jugadas ya sea que un usuario ingrese 1 o 100 hosts.
Los nombres de host se recopilan a través de un mensaje de "variable adicional" cuando se ejecuta una plantilla de Ansible Tower:
client_hosts: 'host1,host2'
a los que luego se hace referencia en el libro de jugadas:
- 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
y más abajo en el libro de jugadas, quiero agregar esas IP + nombres de host a un archivo en un servidor central específico (el nombre de host del servidor no cambia):
- 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'] }}"
Lo anterior funciona bien para un solo host, pero ¿cómo puedo recorrer el registro de variables cuando se especifica más de un host (por ejemplo, client_hostname[1,2,*]?) y actualizar el servidor con esos valores cuando no sé cómo ¿Cuántos hosts se ingresarán con anticipación?
Respuesta1
Este caso de uso consta de tres partes: 1) Gestionar el inventario, 2) Recopilarnombre_host_clienteyip_clientey 3) Informar al servidor central.
1. Gestionar el inventario
Hay muchas opciones sobre cómo gestionar el inventario y recopilarnombre_host_clienteyip_cliente. Por ejemplo, utiliceAgregar rangos de hostssi planea crear cientos de hosts con nombres simples como host001, hosts002, ..., host999
. Por ejemplo, en el inventario siguiente agregueservidor_central, localhost por simplicidad y cien hosts en el grupoprueba
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
Pruebe brevemente el inventario
- hosts: all
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts|length
run_once: true
dar resumido
ansible_play_hosts|length: '101'
Ejecute el siguiente comando si desea mostrar el inventario completo
shell> ansible-inventory -i inventory --list --yaml
Luego hay muchas opciones sobre cómo seleccionar los hosts. VerPatrones: segmentación por hosts y grupos. Por ejemplo,límiteel inventario a hosts o grupos particulares. Pruébelo con el sencillo manual a continuación
shell> cat pb1.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: inventory_hostname
dar
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
Utilice el complemento de inventarioconstruidosi desea limitar el inventario a un grupo más grande de hosts. Ver
shell> ansible-doc -t inventory constructed
Por ejemplo, la variable adicionalcontar_hostsse utiliza en el siguiente ejemplo para crear el grupomi grupoque comprende hosts limitados por el valor de esta variable
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
Pruébalo
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
Ejecute el siguiente comando si desea mostrar el inventario completo
shell> ansible-inventory -i inventory -e count_hosts=10 --list --yaml
Con la ayuda delconstruidocomplemento, puede crear condiciones complejas. Por ejemplo, limite los hosts a un intervalo particular y cree el grupomi_grupo2
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
Pruébalo
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. Recogernombre_host_clienteyip_cliente
Puedes usar el móduloconfiguracióno recopile los hechos por su cuenta. Por la portabilidadconfiguracióndebería ser preferible.
Ver el móduloconfiguraciónsobre cómo recopilar datos sobre hosts remotos. Por ejemplo, el siguiente manual recopila datos sobre elmáquinayredy crea las variablesnombre_host_clienteyip_cliente
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
dar
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
La estructura y el formato de los datos pueden diferir entre los sistemas operativos.
Puede recopilar los hechos por su cuenta. Por ejemplo, el siguiente libro de jugadas
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
dar ejecución en 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
El resultado de las utilidades puede diferir entre los sistemas operativos.
3) Informar al servidor central
Usa Jinja para crear la estructura. Ejecute la tarea una vez y deléguela alservidor_central
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
dar
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
El libro de jugadas creó el archivo enservidor_central
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
Utilice el móduloarchivo de líneasi desea agregar líneas al archivo. El siguiente libro de jugadas es idempotente.
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
No habrá cambios cuando lo ejecute repetidamente en los mismos hosts.
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
Se agregarán nuevas líneas al archivo si el libro de jugadas se ejecuta en nuevos hosts.
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
La nueva línea se agregó al archivo.
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
Respuesta2
Puede recorrer la variable client_hosts usando la directiva with_items en su libro de jugadas. Luego puede hacer referencia a cada host individual utilizando la variable item en el bucle.
A continuación se muestra un ejemplo de cómo puede modificar su libro de estrategias para manejar múltiples hosts:
- 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) }}"
Este manual recorrerá cada host en la variable client_hosts y recopilará el nombre de host y la dirección IP utilizando el módulo shell. Luego establecerá estos valores como variables usando el módulo set_fact. Finalmente, recorrerá la variable client_hosts nuevamente y usará el módulo lineinfile para actualizar el archivo en el servidor central con el nombre de host y la dirección IP de cada host.
Espero que esto ayude.