
Ich versuche, Hostnamen und IP-Adressen aus einer vom Benutzer eingegebenen Hostliste abzurufen und diese Informationen an einen zentralen Server zu senden. Das Hauptproblem, auf das ich stoße, ist, dass die Anzahl der Hosts erheblich variieren kann. Beispielsweise kann ein Benutzer beim ersten Durchlauf 1 Hostnamen eingeben, beim zweiten Durchlauf 30 und beim nächsten Durchlauf 5. Ich möchte ein einzelnes Playbook verwenden können, unabhängig davon, ob ein Benutzer 1 oder 100 Hosts eingibt.
Hostnamen werden über eine „zusätzliche Variable“-Eingabeaufforderung erfasst, wenn eine Ansible Tower-Vorlage ausgeführt wird:
client_hosts: 'host1,host2'
auf die dann im Playbook verwiesen wird:
- 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
und weiter unten im Playbook möchte ich diese IPs + Hostnamen zu einer Datei auf einem bestimmten zentralen Server hinzufügen (der Server-Hostname ändert sich nicht):
- 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'] }}"
Das oben genannte funktioniert für einen einzelnen Host einwandfrei, aber wie kann ich eine Schleife durch die Registrierungsvariablen ausführen, wenn mehr als ein Host angegeben ist (z. B. client_hostname[1,2,*]?), und den Server mit diesen Werten aktualisieren, wenn ich nicht im Voraus weiß, wie viele Hosts eingegeben werden?
Antwort1
Dieser Anwendungsfall besteht aus drei Teilen: 1) Verwalten des Inventars, 2) SammelnClient-HostnameUndClient-IP, und 3) Meldung an den zentralen Server.
1. Verwalten Sie das Inventar
Es gibt viele Möglichkeiten, wie man den Bestand verwalten und sammelnClient-HostnameUndClient-IPVerwenden Sie beispielsweiseHinzufügen von Hostbereichenwenn Sie Hunderte von Hosts mit einfachen Namen wie erstellen möchten host001, hosts002, ..., host999
. Fügen Sie beispielsweise im Inventar unten hinzuzentraler_server, localhost der Einfachheit halber und hundert Hosts in der Gruppeprüfen
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
Kurzer Inventartest
- hosts: all
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts|length
run_once: true
geben gekürzt
ansible_play_hosts|length: '101'
Führen Sie den folgenden Befehl aus, wenn Sie das komplette Inventar anzeigen möchten
shell> ansible-inventory -i inventory --list --yaml
Dann gibt es viele Möglichkeiten, die Hosts auszuwählen. SieheMuster: Zielhosts und -gruppen. Zum Beispiel,Grenzedas Inventar auf bestimmte Hosts oder Gruppen. Testen Sie es mit dem einfachen Playbook unten
shell> cat pb1.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: inventory_hostname
geben
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
Verwenden Sie das Inventar-Pluginkonstruiertwenn Sie die Bestandsaufnahme auf eine größere Gruppe von Hosts beschränken möchten. Siehe
shell> ansible-doc -t inventory constructed
Beispielsweise die zusätzliche VariableAnzahl_Hostswird im folgenden Beispiel verwendet, um die Gruppe zu erstellenmeine Gruppebestehend aus Hosts, begrenzt durch den Wert dieser 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
Probier es aus
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
Führen Sie den folgenden Befehl aus, wenn Sie das komplette Inventar anzeigen möchten
shell> ansible-inventory -i inventory -e count_hosts=10 --list --yaml
Mit Hilfe derkonstruiertPlugin können Sie komplexe Bedingungen erstellen. Beschränken Sie beispielsweise die Hosts auf ein bestimmtes Intervall und erstellen Sie die Gruppemeine_gruppe2
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
Probier es aus
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. SammelnClient-HostnameUndClient-IP
Sie können entweder das Modulaufstellenoder sammeln Sie die Fakten selbst. Aufgrund der Portabilitätaufstellensollte bevorzugt werden.
Zum Modulaufstellenzum Sammeln von Fakten über Remote-Hosts. Das folgende Playbook sammelt beispielsweise Fakten über dieMaschineUndNetzwerkund erstellt die VariablenClient-HostnameUndClient-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
geben
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
Struktur und Format der Fakten können je nach Betriebssystem unterschiedlich sein.
Sie können die Fakten selbst sammeln. Zum Beispiel das folgende Playbook
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
geben läuft unter 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
Die Ausgabe der Dienstprogramme kann je nach Betriebssystem unterschiedlich sein.
3) Melden Sie sich beim zentralen Server
Verwenden Sie Jinja, um die Struktur zu erstellen. Führen Sie die Aufgabe einmal aus und delegieren Sie sie an denzentraler_server
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
geben
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
Das Playbook erstellte die Datei unterzentraler_server
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
Verwenden des ModulsZeilendateiwenn Sie der Datei Zeilen hinzufügen möchten. Das folgende Playbook ist idempotent
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
Es gibt keine Änderungen, wenn Sie es wiederholt auf denselben Hosts ausführen
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
Wenn das Playbook auf einem oder mehreren neuen Hosts ausgeführt wird, werden der Datei neue Zeilen hinzugefügt.
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
Die neue Zeile wurde an die Datei angehängt
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
Antwort2
Sie können die Variable client_hosts mithilfe der Direktive with_items in Ihrem Playbook durchlaufen. Anschließend können Sie mithilfe der Variable item in der Schleife auf jeden einzelnen Host verweisen.
Hier ist ein Beispiel, wie Sie Ihr Playbook ändern können, um mehrere Hosts zu verwalten:
- 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) }}"
Dieses Playbook durchläuft jeden Host in der Variable client_hosts und erfasst den Hostnamen und die IP-Adresse mithilfe des Shell-Moduls. Anschließend legt es diese Werte mithilfe des Moduls set_fact als Variablen fest. Abschließend durchläuft es die Variable client_hosts erneut und aktualisiert mithilfe des Moduls lineinfile die Datei auf dem zentralen Server mit dem Hostnamen und der IP-Adresse für jeden Host.
Hoffe das hilft.