Die CFEngine-Sprache besteht aus einigen Bausteinen, die hier anhand von Beispielen erklärt werden. Mit den Beispielen werden die Begriffe Promise, Body, Bundle und Methods deutlich. Hier unser erstes Beispiel, von dem wir wissen oder zumindest schnell erahnen können, was es bewirkt:
Ein Promise wird immer aus Sicht des Agenten gelesen, der auf jedem System läuft, welches von CFEngine verwaltet wird. Der Agent liest das Promise derart: Bin ich ein Redhat-System? Wenn nein, dann interessiert mich alles weitere nicht, fertig hier. (Es wird zum nächsten Promise-Typen bzw. der nächsten Class-Abfrage weitergegangen.) Wenn ja, soll ich mich um Pakete kümmern. Das Webserver-Paket httpd soll präsent sein. Ich soll yum als Paketmanager verwenden. Ich soll die Version 2.4.18 installieren. Was ist nun was?Das gesamte Konstrukt ist ein Promise, ein deklarierter Wunschzustand. redhat:: ist eine Hard-Class. Die harten Klassen werden von CFEngine bei jedem Lauf erkannt. Hard-Classes sind Klassen wie die IP-Adresse, das Betriebssystem samt Patchlevel, die Hardware-Architektur (bspw. x86_64), der Hostname oder auch die Uhrzeit. Hardclasses sind quasi faktische Begebenheiten. Man kann definieren: wenn der Hostname "reporter" und es 6:00 Uhr morgens ist, gebe einen Report von "Guten Morgen" aus. Diese Hard-Classes werden als Entscheider verwendet: „wenn, dann“. Wenn die Klasse redhat zutrifft, wenn es also ein Redhat-System ist, dann geht es weiter, sonst wird alles weitere diese Class betreffende ignoriert. Für diese „wenn, dann“-Entscheidungen wird ein doppel-Doppeltpunkt verwendet:: Neben den Hard-Classes gibt es die Soft-Classes, die man selbst in den Promises definiert; sie lassen sich in Abhängigkeit von dem Resultat eines (anderen) Promises definieren. Angenommen, wir wollen nach der Installation des httpd-Pakets den httpd-Service starten. Dazu fügen wir oben unter "version => „2.4.18“ folgende Zeile ein:
Das führt dazu, dass die Soft-Class "httpd_installed" gesetzt wird, sobald das Paket installiert ist. Promises können verschiedene Rückmeldungen geben:
Es lassen sich über weitere sogenannte body_classes entsprechend der Promise-Rückmeldung Classes definieren. Hier ist ein Link zu den Details, aber wer sich mit dem Grundkonzept von CFEngine vertraut machen möchte, dem sei hier geraten, sich nicht zu früh in allen Details und Möglichkeiten zu ver(w)irren: https://docs.cfengine.com/docs/3.8/reference-standard-library-common.html#results Wir halten fest: Mittels einer body_class wie hier von if_repaired geliefert, erhalten wir eine Soft-Class, wenn das Promise erfolgreich war. Anhand von unserer erstellten Class "httpd_installed" können wir nun entscheiden, den Webservice zu starten:
Der Service wird nur gestartet, wenn die Class httpd_installed gesetzt ist. Ist also das Paket (noch) nicht installiert, wird es die Class (noch) nicht geben und die Anweisungen werden ignoriert. Sobald das Paket installiert ist, wird die Class gesetzt und die Anweisungen beim nächsten Lauf des Agenten ausgeführt.
packages:
Der Paketname „httpd“ ist ein Attribut des Promises bzw. des Promise-Typs. Für verschiedene Promise-Typen (z.B. files, services, processes, packages, commands) gibt es verschiedene Attribute: ein Prozess kann gestartet oder gestoppt werden, ein File nicht; ein File kann man anlegen, ändern oder löschen, einen Service nicht, den kann man installieren, starten oder stoppen. So ergeben sich sinngemäß unterschiedliche Attribute zu den verschiedenen Promise-Typen. Promises werden in Bundles verpackt. In einem Bundle kann es viele Promises geben. Bundles können als Container für Promises betrachtet werden, die zusammengehören; bspw. kann es ein Bundle „Webserver“ geben, in dem es nun ein Package-Promise gibt, um das Paket zu installieren, ein File-Promise, um das korrekte Konfigurationsfile sicherzustellen, ggf. auch noch, um Zertifikate zu kopieren, ein Service-Promise, um sicherzustellen, dass der Webserver immer läuft und ein Pager- oder Mail-Promise, dass uns informiert, wenn der Webserver nicht läuft. methodsDie haben wir in dem einfachen Beispiel nicht, weil sie hier nicht zur Anwendung kommen.
In diesem bundle definieren wir eine Variable "userliste" vom Typ String-List (slist), die fünf User enthält. Mit methods: rufen wir nun ein anderes Bundle "erstelle_user" auf und übergeben unsere userliste als Parameter an dieses Bundle.
Die fünf User in unserer Variablen $(userlist) werden nacheinander als Parameter (user) übergeben; soviele User wir in der Liste haben, so oft wird "erstelle_user" mit dem jeweils nächsten User als Parameter aufgerufen und das Kommando useradd ebenso. "erstelle_user" dient hier als Art Subroutine. Diese Methodik bzw. dieses Feature von CFEngine nennt sich "Implizites Looping". Methods sind sehr mächtig und hilfreich, wenn es darum geht, zahlreiche Aktionen auf einen oder mehrere Parameter durchzuführen. Man muss nicht alle Promises selbst schreiben, für viele Aufgaben, einfache sowie komplexe (bspw. mit Pager-Alarm, Reporting und Monitoring) gibt es frei verfügbare Promises auf dem CFEngine GitHub: https://github.com/cfengine |