Ansible-Schleife durch variable Anzahl von Hostvars

Ansible-Schleife durch variable Anzahl von Hostvars

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.

verwandte Informationen