Seite wählen

Ansible

Kapitel 2 Erste Aufgaben

Im Kapitel: Ansible Schritt für Schritt wurde Plan und Ziel beschrieben. Im Kapitel Unser erstes Playbook  wurde nun die Grundlage geschaffen, unsere Aufgaben über Ansible zu verwalten und auszuführen. In diesem Kapitel werden allgemeine administravie Schritte vorgenommen um die Server bereitzustelln sowie Update Wartungsarbeiten angelegt. Hierfür nutzen wir unsere 1. Rollen.

Hinweis

Beispiele und Code auf: Gitlab

Bitte beachten:

In dieser Dokumentenserie  gehen wir von Zielumgebung Ubuntu 22.04.2 und Ansible 2.10 aus. Vereinzelt gehen wir auf Opensuse Leap 15.4 ein. Bei anderen Distributionen und Versionsstände können nachfolgende Beispiele u.U. nicht funktionieren.

Rollen:

Wie bereits im Kapitel: Ansible Schritt für Schritt erklärt: Ist eine Rolle eine Zusammenfassung von aufgaben und Tätigkeiten. Dies werden wir hier nochmals genauer planen und überlegen ob und in welchem Zusammenhang eine Rolle Sinn macht oder ob man Aufgaben in Tasks auftrennt.

Vorplanung:

Was haben wir vor? Welche Schritte warten auf uns?

Folgende Aufgaben erwarten uns:

Update:

Zunächst wird das Zielsystem in der Grundinstallation mit Updates versehen. Dieser Schritt stellt schon eine wiederholbare Aufgabe dar. Systeme regelmässig zu aktualisieren gehört wohl zu den permanten Aufgaben in einem Unternehmen und IT Betrieb. Somit könnte man hieraus eine eigene Rolle erstellen. Rolle: update-systems.  Je nachdem wie heteregon die Systemlandschaft ist, macht es durchaus Sinn um die Komplexität aus den einzelnen Rollen und Tasks zu reduzieren. Somit könnte sich diese Rolle ausschliesslich um die Aufgabe: Systeme aktuell zu halten, kümmern und sich auf die Unterschiede der jeweiligen Distributionen und deren Paketmanager konzentrieren.

Beim Update kommen folgende Aufgaben auf uns zu:

  • Repository Daten aktualisieren, so das lokal die neuesten Versionen bekannt sind.
  • Updates einspielen
  • Prüfen ob ein Neustart erforderlich ist (Kernel Update)
  • ggf. Dienste neu starten.
    • Ich hatte mal das Ziel zu ermitteln, welche Dienst einen Neustart benötigen. Die Rückmeldung aus dem JSON Format zu parsen.. ich habs aufgegeben. Ich weiss, welche Dienste auf den Host laufen und starte bzw. reloade diese pauschal nach einem update.
  • Achja, die unterschiede zwischen den Distributionen behandeln.

 

Benutzernalage:

Für eine einheitliche Systemlandschaft und zur Vermeidung von Berechigungsprobleme nach einem z.B. Backup Restore aus einem anderen Knoten, halten wir UID und GROUP ID auf allen Systemen identisch. Daher legen wir diese Benutzer vor dem Anlegen der von Anwendungen an.

Beispiel: Der Mailserver nutzt die System Benutzer:

  • mail
  • postfix
  • vmail
  • vscan

Für die Datenbank, mysql oder postgres sowie einige weitere

Für den Webserver z.B. OpenSuse: wwwrun Gruppe www, Ubuntu: www-data

Diese Benutzer legen wir an, definieren das Homedir.

Dateisystem: 

Mit der Benutzeranlage wiederum muss u.U. zusätzlich ein Homedir als Filesystem angelegt werden. Unsere Host, wir später nochmals genau erklärt, sind alle identsicht aufgebaut und verwenden LVM. Das macht im Betrieb vieles einfacher. Dazu aber später mehr.

Bevor also ein Benutzer angelegt wird, muss eventuell dessen Homedir angelegt werden. Typisch wäre wohl der Benutzer mysql mit seinem Data Dir. 

Unabhängig von den Benutzer macht es natürlich auch Sinn: /var/log /tmp usw. ein eigenes Filesystem zu geben.

Anwendungsinstallation:

Selbverständlgich installieren wir auch gleich alle notwendigen Anwendungen, welche wir auf allen Systemen erwarten bzw. benötigen. Backup Tools, Monitoring Tools und sonstige Werkzeuge und Scripte. Da dies sehr individuell ist, belasse ich es im Projekt auf einige wenige Beispiele:

 Fazit:

Für dieses Kapitel sind folende Dateien und Ordner notwendig:

  • Eine Rolle: basissystem
    • Wir könnten es weiter aufteilen, aber das machen wir ggf. später Jetzt ist es noch überschaubar
  • Vars / Listen für:
    • Benutzer mit Attributen.
      • Benutzername
      • Gruppenname
      • UID
      • GID
      • Gruppenzugehörigkeit
      • Homedir
        • Location: z.B. /var/lib/mysql
        • Filesystem z.b: xfs, ext4 etc.
        • Grösse: 
      • Login shell
      • Flag: Um ein eigenes Volume/Filesystem zu erstellen, sofern gewünscht. 
    • Filesystem / System Info (Wir werden es global setzen oder über die Hosts definieren)
      • Volume Gruppe:
      • lvm_enables: yes/no
    • Inventar (existiert bereits)
      • Ergänzung als Rolle
      • ggf. Ergänzung um LVM Parameter.

Taks zur Ausführung:

  • Repository Update
    • Unterscheidung zwischen den Betriebsystemen
  • Installation der Updates
    • Ebenfalls mit Unterscheidung zwischen den Betriebsystemen
  • Neustart der Systeme und Dienste
    • Sofern erforderlich
  • Anlegen der Filesysteme
    • Partitionierung, bei uns legen wir eine Logical Volume an.
    • Formatierung
    • Mounten
    • Eintrag in der /etc/fstab
  • Anlegen Systembenutzer
    • UID,GID 
    • Random Passwort
    • Homedir festlegen
    • Shell festlegen z.B. /bin/false damit kein Remote Login möglich ist.

Ich hoffe, ich habe nichts vergessen. Wenn doch, dann stellen wir es weiter unten im Dokument fest.

Systeme und Spezifikation:

In dieser Dokumentenreihe kommen im Grunde folgende Konstelationen zusasmmen:

Für heute betrachten wir nur den Unterschied:

  • Betriebsystem
    • Ubuntu
    • OpenSuSe

Bei der Hardware:

  • Hardware
    • Vituelle Maschine
      • LVM, ja / nein
    • Physischer Server
      • LVM, ja/ nein

In den folgenden Kapiteln wird es noch detailierter spezifiziert. Für unser zweites Playbook reicht es jedoch aus.

Stand heute stehen 3 Systeme zur Verfügung:

  • Physischer Server
    • cube01: Ubuntu mit LVM
    • mx1: OpenSUSE mit LVM
  • Virtuelle Server
    • mx5 Ubuntu ohne LVM

Das genügt um die Anforderung oben durch zu spielen:

Legen wir also los.

Rolle

basissystem

Um eine Rolle anzulegen bietet uns Ansible das passende Werkzeug:

ansible-galaxy

Auf dem Ansible Host wechseln wir zunächst ins Verzeichnis „ANSIBLE_HOME“ nach /opt/ansible-playbook.

In einer globalen Konfiguration legen wir zunächst die Konfiguration fest, wo ansible die Rollen suchen soll. (per default immer im Unterordner roles)

Dateiname: ${ANSIBLE_HOME}/ansible.cfg

[defaults]
roles_path = /opt/ansible-playbook/roles

Diese Konfigurationsdatei werden wir im Laufe des Projektes noch weiter ergänzen. Somit legen wir sie schon jetzt an.

Beim nachfolgenden Anlegen der Rolle wird diese Konfiguration jedoch nicht genutzt!
Wir legen also zunächst roles_path an, wechseln in das Verzeichnis und erstellen die Rolle.

mkdir /opt/ansible-playbook/roles;
cd /opt/ansible-playbook/roles;
ansible-galaxy role init basissystem;

Somit existiert nun ein Ordner basissystem mit dem Inhalt:

+- README.md
- defaults
+ - main.yml
- handlers
+ - main.yml
- meta
+ - main.yml
- tasks
+ - main.yml
- tests
+ - test.yml
+ - inventory
- vars
+ - main.yml

Ansible Basics

Struktur, Aufbau und Abfolge:

Nochmals Theorie:

Wenn wir eine Rolle nutzen, so wird zunächst roles/basissystem/tasks/main.yml referenziert und ausgeführt.

Ebenfalls verhält es sich mit den anderen Ordner und Dateien. Ohne weitere Angabe würde vars/main.yml, defaults/main.yml usw. referenziert und verarbeitet.

Wie in den Kapiteln zuvor angeprochen: Die aufgaben in Basissystem könnte man auch un mehrere Rollen aufteilen. Eine Rolle für systemupdate, eine für die Installation von Pakete.

In diesem Beispiel habe ich folgendes überlegt:

Was ist der Zustand, den ich ganz am Anfang erwarte?

Wie sollen die Systeme einheitlich aussehen? Welche Benutzer sollen existieren, welche Filesysteme, welche Programme?

Dieser Zustand, ist für mich die Vorgabe an ein basisystem.

Damit sind wir nun exakt in der Theminologie von yaml und Bezug ansible.

YAML

YAML ist also die Definition eines Zustandes und der Weg dahin. Somit enthält es nicht nur Attribute sondern auch Anweisungen. Mit der Möglichkeit Bedingungen zu formulieren wird es daher auch eine wenig Programmiersprache.

Ansible

Ansible ist das Werkzeug, welches die gewünschten Zustände herstellt. Über Galaxy und die Community werden Module / Plugins bereit gestellt um die unterschiedlichsten Aufgaben zur Herstellung eines Zustandes zu ermöglichen. Diese sind in Python geschrieben.

Ansible Rollen

der Einstieg:

Wie oben beschrieben: die tasks/main.yml ist der Einstiegspunkt an dem nun weiter gemacht wird.

Aus dem Beispiel vom Vortag ziehen wir den -task mit der debug Meldung um und führen im Playbook ANSIBLE_HOME/main.yml die Verwendung der Rolle basissystem ein.

So sieht nun unser Playbook aus: 

---
# main play file
- name: Apply basissystem role
hosts: basissystem
roles:
- basissystem

Das ist der Play der Rolle roles/basissystems/tasks/main.yml


# tasks file for basissystem
– name: Unser erste Aufgabe
debug:
msg: ‚Hallo Welt‘

Die Ausführung des Playbooks liefert das identische Ergebnis wie gestern.

Der Unterschied liegt nun darin, dasss wir eine Rolle genutzt haben statt die Anweisungen in einer Datei dem Plabooks zu starten. Hierbei haben wir den Play von Tasks getrennt.

Beachtet diese Thermonolgie:

Play: regelt die Einteilung und Zuweisung.

Tasks: Stellen definierte Zustände her.

Diese Unterscheidung ist nicht ganz unwichtig. In Plays stehen manche Optionen und Funktionen nicht zur Verfügung, wiederum in den Taks selbst können keine Rollen definiert werden. Im Kontext „- name“ was lediglich eine Bezeichnung eines Objekts / Ablaufschritt ist, ist nicht immer klar, handelt es sich nun um einen Task oder Play? Wenn wir ehrlich sind, die Zuweisung an Hosts, ist irgendwie auch als Task zu verstehen. 😉 Verwirrung prefekt? 

Als Play sind immer die Einteilung bzw. Aufteilung und Selektion des Inventars zu verstehen. Dazu gehört die Zuordnung von Rollen.

Tasks sind wiederum die Schritte, welche einen definierten Zustand herstellen. Entweder als Liste im Objekt task: untergeordnet oder in der task/main.yml aufführt. In dieser Datei ist die Unterordnung als Tasks nicht mehr notwendig. Dies muss max. im Playbook selbst gemacht werden.

Das Grundgerüst steht nun. Der gewünschte Zustand kann nun definiert und umgesetzt werden.

Rolle basissystem

Zur Erinnerung: Folgendes soll erreicht werden:

  • System aktualisieren, aktuelle Updates und Patche einspielen
    • Neustart, sofern erforderlich.
  • Anwendungen installieren
    • Ubuntu
    • OpenSuSe
  • Benutzer anlegen
    • Filesystem für den Benutzer
    • System Benutzer mit vorgegener UID /GID
    • Login shell festlegen

 

Diese 3 Hautpaufgaben schauen wir uns genauer an.

Theoretisch könnten wir diese in der roles/basissystem/tasks/main.yml formulieren.

Allerdings bringen die beiden unterschiedlichen Distributionen Ubuntu und OpenSuSe ggf. noch weitere Distributionen unterscheide mit sich:

  • Die Tools für die Installation: apt vs. zypper, yum, yast usw.
  • Unterscheidliche Applikationen und Namen für die gleiche Aufgabe
    • Als Monitoring nutze ich icinga2 und rufe Remote schecks über nrpe auf.
      • Ubuntu: Der nrpe Server Dienst heisst: nagios-nrpe-server
      • OpenSuse: nrpe

Weiter geht es später mit der Konfiguration des Netzwerkes. 

Diese Unterschiede nehme ich zum Anlass die Taks in eigenständige task.yml aus zu lagern, sowie die vars Dateien der Anwendungen ebenfalls getrennt zu halten. In der main.yml regle ich damit also nur noch: Für welche Distribution führe ich Tasks nur für jeweilige Distribution aus. Gebe diesem Tasks nur eine Liste an Anwendungen, passend zur Distribution.

Dies hat den Vorteil die Tasks nur auf  wenige Aufgaben zu reduzieren.  Macht es übersichtlich.

die main.yml wiederum hat eine Art „Play“ Rolle. Hier Unterscheide ich zwischen den Betriebsystemen und weiteren Hosts Attributen.

Die Trennung macht auch aus einem anderen Grund Sinn: 

Wenn wir später z.B. Rollen oder andere Übergeordnete Elemente einfügen….. Reihenfolge ändern müssen. 30 Zeilen Code zu ändern, Einrückungen azupassen. Das macht keinen Spass.

Mit der Trennung bleibt es hingegen übersichtlich und wird zwischen Organisation und dem tatsächlichen Doing konsequent getrennt.

Die einzige Frage, wäre? Eigenständige Rolle für die unterschiedlichen Distributionen?

Das ist eine Überlegung wert. Es sind aber aktuell so wenige Unterschiede, dass eine Trennung weniger Sinn macht, jedoch eine doppelte Codepflege erfordert. Für sehr allgemeine Dinge wie Benutzeranlage könnten Taks auch im „ANSIBLE_HOME/tasks“ als allgemeingültige Tasks abgelegt werden. Dies werden wir u.U. später erledigen. Hier fassen wir die Schritte, wie oben definiert als einzelne Tasks yml. Dateien in der Rolle basissystem zusammen und steuern den Aufruf über die main.yml.

Die Entscheidung, wie und wo legen wir die Tasks, vars usw ab. Wie definieren wir die Rolleninhalt. Das will sehr gut überlegt sein. Den einen „richtigen“ Weg gibt es aber selten. Schnell verliert man sich in unübersichtlichen Strukturen und Vielzahl redundanter Scripte und Variablen. In diesem Kapitel werden wir ab und an die Strategie anpassen. Das machen wir mit der neuen Anforderung und Ergänzung. Denn so spielt auch das reale Leben. 

 

Your content goes here. Edit or remove this text inline or in the module Content settings. You can also style every aspect of this content in the module Design settings and even apply custom CSS to this text in the module Advanced settings.

TAKS: Aktualisierung des Repositories

Erstmal aktualieren wir das System mit den neuesten Kernel und Anwendungs Patches. Das ist, was man als schnellstes und erstes haben möchte: Ein aktuelles und abgesichertes System.

  • Zunächst werden die Inhaltsdateien der konfigurierten Software Repositories aktualisiert.
  • Werden die Aktualisierungen installiert.
  • Sofern Notwendig, wird das System neu gestartet.
Als erstes fügen wir für die Unterscheidung des Betriebsystems ein neues Attribut im Inventory  „hosts“ dem host mx5:  hinzu:

ansible_distribution: 

Zusätzlich habe ich einen Host mit Distribution openSUSE hinzu gefügt. 

Hinweis: ansible_distribution müsste hier nicht definiert werden, diese Information und Variable wird über „Gathering Facts“ gesammelt und bereitgestellt. Es ist also nur ein Beispiel, dass man über die Variablen in when Bedingungen Filtern kann.

 

all:
vars:
ansible_user='root'
ansible_become=yes
ansible_python_interpreter='/usr/bin/env python3'
hosts:
mx2:
ansible_host: mx2.domain.tld
ansible_distribution: "openSUSE Leap"
mx5:
ansible_host: mx5.domain.tld
ansible_distribution: Ubuntu

 

Danach legen wir die Datei der RollenTask tasks/update-os.yml an und fügen die erforderlichen Fakten ein um ein Update des Betriebsystemes durchzuführen. 


### Opensuse
#

– name: OpenSUSE Update der Paketquellen
  zypper:
    name: ‚*‘
    update_cache: yes
  when: ansible_distribution == „openSUSE Leap“

– name: OpenSUSE Installation der Updates
  zypper:
    name: ‚*‘
    state: latest
  when: ansible_distribution == „openSUSE Leap“

– name: OpenSUSE Prüfen ob Systemneustart erforderlich
    register: linux_reboot_required_file
    stat: path=/do_reboot
    when: ansible_distribution == „openSUSE Leap“

### Ubuntu

– name: Ubuntu Update der Paketquellen
  apt:
    upgrade: „yes“
    update_cache: „yes“
    cache_valid_time: 86400 #One day
  when: ansible_distribution == „Ubuntu“

– name: Check if a reboot is needed on Ubuntu servers
  register: linux_reboot_required_file
  stat: path=/var/run/reboot-required
  when: ansible_distribution == „Ubuntu“

### Beides

– name: Reboot , wenn erforderlich
  reboot:
    msg: „Reboot initiated by Ansible for kernel updates“
    connect_timeout: 5
    reboot_timeout: 120
    pre_reboot_delay: 0
    post_reboot_delay: 30
    test_command: uptime
  when: linux_reboot_required_file.stat.exists

Achja, nicht vergessen: in der main.yml im ANSIBLE_HOME Verzeichnis folgende Zeile ändern:

 

hosts: mx5

nach 

hosts: all

Ausführung

Ich fasse zusammen. Mit dem Aufruf: ansible-playbook -i hosts main.yml wird das Playbook ausgeführt. In der main.yml im ANSIBLE_HOME Verzeichnis wird das Inventory „die Rechner“ ermittelt, eine Rolle zugewiesen sowie die zugewiesene Rolle ausgeführt. In der roles/basissystem/tasks/main.yml organisieren wir, welche logische Arbeitsschritte, / Aufgaben erledigt werden. Die Einzelschritte sind wiederum  in der eingebundenen roles/basissystem/tasks/update-os.yml definiert und berücksichtigen die Unterschiede der jeweiligen Distribution. Sehen wir uns dazu das Ergebnis an: mx2 ist bereits in Betrieb, hatte also relativ aktuelle Versionsstände installiert. mx5 hingegen wurde frisch aufgesetzt. 

Mir war nicht bewusst, dass ansible_distribution für openSUSE „openSUSE Leap“ lautet. Entsprechend habe ich eine debug Meldung eingebaut. Diese findet Ihr im Repository: 

Sehen wir uns nun das Ergebnis an. Der Host: mx2 hat ein Problem. somit ergibt sich auch gleich ein  Fehler. Perfekt für uns. Dazu komme ich gleich.

PLAY [Apply basissystem role] *****************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************
ok: [mx5]
[WARNING]: Platform linux on host mx2 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information.
ok: [mx2]

TASK [basissystem : Debug Ansible Facts] ******************************************************************************************************************************************************************************
ok: [mx2] => {
„msg“: „Fakt openSUSE Leap“
}
ok: [mx5] => {
„msg“: „Fakt Ubuntu“
}

TASK [basissystem : OpenSUSE Update der Paketquellen] *****************************************************************************************************************************************************************
skipping: [mx5]

fatal: [mx2]: FAILED! => {„changed“: false, „cmd“: [„/usr/bin/zypper“, „–quiet“, „–non-interactive“, „–xmlout“, „install“, „–type“, „package“, „–auto-agree-with-licenses“, „–no-recommends“, „–„, „+*“], „msg“: „Zypper run command failed with return code 4.“, „rc“: 4, „stderr“: „“, „stderr_lines“: [], „stdout“: „<?xml version=’1.0′?>\n<stream>\n<prompt id=\“1\“>\n<description>Problem: nothing provides &apos;php4-mysql&apos; needed by the to be installed babel_console-1.0.0-lp154.9.1.noarch\n Solution 1: do not install babel_console-1.0.0-lp154.9.1.noarch\n Solution 2: break babel_console-1.0.0-lp154.9.1.noarch by ignoring some of its dependencies\n</description>\n<text>Choose from above solutions by number or skip, retry or cancel</text>\n<option value=\“1\“ desc=\“Choose solution 1\“/>\n<option value=\“2\“ desc=\“Choose solution 2\“/>\n<option value=\“s\“ desc=\“Skip problem and continue.\“/>\n<option value=\“r\“ desc=\“Retry solving immediately.\“/>\n<option default=\“1\“ value=\“c\“ desc=\“Choose no solution and cancel.\“/>\n<option value=\“d\“ desc=\“Toggle show detailed conflict information.\“/>\n</prompt>\n</stream>\n“, „stdout_lines“: [„<?xml version=’1.0′?>“, „<stream>“, „<prompt id=\“1\“>“, „<description>Problem: nothing provides &apos;php4-mysql&apos; needed by the to be installed babel_console-1.0.0-lp154.9.1.noarch“, “ Solution 1: do not install babel_console-1.0.0-lp154.9.1.noarch“, “ Solution 2: break babel_console-1.0.0-lp154.9.1.noarch by ignoring some of its dependencies“, „</description>“, „<text>Choose from above solutions by number or skip, retry or cancel</text>“, „<option value=\“1\“ desc=\“Choose solution 1\“/>“, „<option value=\“2\“ desc=\“Choose solution 2\“/>“, „<option value=\“s\“ desc=\“Skip problem and continue.\“/>“, „<option value=\“r\“ desc=\“Retry solving immediately.\“/>“, „<option default=\“1\“ value=\“c\“ desc=\“Choose no solution and cancel.\“/>“, „<option value=\“d\“ desc=\“Toggle show detailed conflict information.\“/>“, „</prompt>“, „</stream>“]}

TASK [basissystem : OpenSUSE Installation der Updates] ****************************************************************************************************************************************************************
skipping: [mx5]

TASK [basissystem : OpenSUSE Prüfen ob Systemneustart erforderlich] ***************************************************************************************************************************************************
skipping: [mx5]

TASK [basissystem : Ubuntu Update der Paketquellen] *******************************************************************************************************************************************************************
ok: [mx5]

TASK [basissystem : Check if a reboot is needed on Ubuntu servers] ****************************************************************************************************************************************************
ok: [mx5]

TASK [basissystem : Reboot , wenn erforderlich] ***********************************************************************************************************************************************************************
skipping: [mx5]

PLAY RECAP ************************************************************************************************************************************************************************************************************
mx2 : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
mx5 : ok=4 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0

Das Ergebnis:

Zunächst sehen wir, alle Tasks wurden abgearbeitet:

bedingt durch den Fehler allerdings wurde die weitere Bearbeitung von mx2 übersprungen. Im nachgang habe ich einen anderen Host gewählt. 

Aber zunächst zum diesem Problem: Die Ausgabe erfolgt im Json Format:

  • „cmd“
    • Damit wird ausgesagt, es wurde ein Kommandozeilen Aufruf gestartet. 
  • „/usr/bin/zypper“
    • Das Kommando, das Paketverwaltungstool bei Opensuse.
  • Es folgen die Optionen. Im Klartext wurde dieser Befehl ausgeführt:
  • /usr/bin/zypper „–quiet“ „–non-interactive“ „–xmlout“ „install“ „–type“ „package“ „–auto-agree-with-licenses“ „–no-recommends“ „–“ „+*“
  • Danach folgt die Rückmeldung
    • Return Code (rc) 4
    • Die Rückmeldung im XML Format.

Ein sehr nützliches Tool hierbei ist: Best YAML to JSON Converter Online (jsonformatter.org)

Hier kann man zum einen yaml nach json übersetzen und json schön formatiert anzeigen lassen!

Die ursprüngliche Ursache spielt für uns keine Rolle, ich erkläre Sie dennoch kurz:

Der Server MX2 hat durch Aktualisierungen und OS Upgrades einzelne veraltete Software Pakete. Diese wiederum haben Abhängigkeiten zu weiteren Paketen, welche nicht mehr verfügbar sind. 

Somit habe ich ein anderes OpenSUSE System zum Test eingebunden Host: mx1. Wie ihr wisst, stolpert man in einer Heterogenen Umgebung ständig durch Probleme durch Unterscheide in Versionstände und Distributionen. So ist es auch hier.

Neues Problem:

PLAY [Apply basissystem role] *****************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************************
ok: [mx5]
[WARNING]: Platform linux on host mx1 is using the discovered Python interpreter at /usr/bin/python3.6, but future installation of another Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information.
ok: [mx1]

TASK [basissystem : Debug Ansible Facts] ******************************************************************************************************************************************************************************
ok: [mx1] => {
„msg“: „Fakt openSUSE Leap“
}
ok: [mx5] => {
„msg“: „Fakt Ubuntu“
}

TASK [basissystem : OpenSUSE Update der Paketquellen] *****************************************************************************************************************************************************************
skipping: [mx5]
ok: [mx1]

TASK [basissystem : OpenSUSE Installation der Updates] ****************************************************************************************************************************************************************
skipping: [mx5]
ok: [mx1]

TASK [basissystem : OpenSUSE Prüfen ob Systemneustart erforderlich] ***************************************************************************************************************************************************
skipping: [mx5]
ok: [mx1]

TASK [basissystem : Ubuntu Update der Paketquellen] *******************************************************************************************************************************************************************
skipping: [mx1]
ok: [mx5]

TASK [basissystem : Check if a reboot is needed on Ubuntu servers] ****************************************************************************************************************************************************
skipping: [mx1]
ok: [mx5]

TASK [basissystem : Debug] ********************************************************************************************************************************************************************************************
ok: [mx1] => {
„linux_reboot_required_file“: {
„changed“: false,
„skip_reason“: „Conditional result was False“,
„skipped“: true
}
}
ok: [mx5] => {
„linux_reboot_required_file“: {
„changed“: false,
„failed“: false,
„stat“: {
„exists“: false
}
}
}

TASK [basissystem : Reboot , wenn erforderlich] ***********************************************************************************************************************************************************************
fatal: [mx1]: FAILED! => {„msg“: „The conditional check ‚linux_reboot_required_file is defined and linux_reboot_required_file.stat.exists and linux_reboot_required_file.stat.exists == True‘ failed. The error was: error while evaluating conditional (linux_reboot_required_file is defined and linux_reboot_required_file.stat.exists and linux_reboot_required_file.stat.exists == True): ‚dict object‘ has no attribute ’stat’\n\nThe error appears to be in ‚/opt/ansible-playbook/roles/basissystem/tasks/update-os.yml‘: line 46, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Reboot , wenn erforderlich\n ^ here\n“}
skipping: [mx5]

PLAY RECAP ************************************************************************************************************************************************************************************************************
mx1 : ok=6 changed=0 unreachable=0 failed=1 skipped=2 rescued=0 ignored=0
mx5 : ok=5 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0

Da der Fehler nicht sofort offensichtlich war, wurde die update-os.yml um einen Tasks debug erweitert:Hier machen sich die Unterschiede zur Distribution und gelegentlichen Updates bemerkbar.

Als ich mit dem Umbau angefangen hatte, klappt die when Bedingung noch fehlerfrei. Bei der Prüfung, ob eine Semaphore (Signal Datei / Flag) existiert welche die Notwendigkeit eines Reboots signalisiert, liefert openSUSE und Ubuntu unterschiedliche Werte zurück:

ok: [mx1] => {
"linux_reboot_required_file": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
ok: [mx5] => {
"linux_reboot_required_file": {
"changed": false,
"failed": false,
"stat": {
"exists": false
}
}
}

Dieser Unterschied wird nun in der when Bedingung berücksichtigt.

when: linux_reboot_required_file is defined and linux_reboot_required_file.stat.exists|default(false) and linux_reboot_required_file.stat.exists == True

Diese Ergänzung: linux_reboot_required_file.stat.exists|default(false)

Setzt den Status (stat),  wenn dieser wie bei mx1 nicht existiert und setzt diesen auf false.

Existiert die /do_reboot Semaphore auf mx1 wird der Task zum Reboot ausgeführt.

Damit ist Kapitel 2 Abschlossen. Alle Änderungen und anpassungen findet Ihr hier: Gitlab Kapitel 2