Das OS-Plugin-Konzept von OpenSLX

Viele der von uns geplanten Erweiterungen sollen als Plugins implementiert werden, so dass man sie unabhängig vom Kernsystem (und von anderen Erweiterungen) entwickeln und testen kann. Dies hat u.a. auch den Vorteil, dass zwei Entwickler, die gerade an einer Erweiterung werkeln, sich nicht gegenseitig bei der Integration ihrer Arbeit in das Stage3-Initskript auf den Füßen stehen.

Liste verfügbarer Plugins:

Liste geplanter/in Entwicklung befindlicher Plugins:

  • printer
  • scanner
  • virtualbox (unvollständig, in Entwicklung)
  • qemukvm (unvollständig, in Entwicklung)
  • xen
  • auth (Konfiguration von Benutzern und ihren Homeverzeichnissen inkl. root und PW setzen)

Plugins & Attribute

Damit die Plugin-spezifischen Attribute zuverlässig dem richtigen Plugin zugeordnet werden können, muss bei der Benamung der Attribute darauf geachtet werden, dass jedes der Attribute mit den Präfix '<plugin-name>::' beginnt (denn darüber erfolgt die Zuordnung von Attributen zu Plugins beim Demuxen).

Doch, beim Deinstallieren eines Plugins mittels slxos-plugin werden alle Attribute (Stage1 & Stage3) aus der DB gelöscht. Man kann derzeit zwar Defaultwerte für alle Vendor-OS vorgeben, indem man ein Plugin in das Vendor-OS "<<<default>>>" installiert, allerdings wird dieses Plugin dann auch gleich immer beim Anlegen eines neuen Vendor-OS in dieses hineininstalliert.

Stage1-Attribute

Stage1-Attribute sind dazu da, die Installation des Plugins in das Vendor-OS vom Anwender steuerbar zu machen, d.h. die Werte dieser Attribute bestimmen, welche Varianten des vom Plugin verwalteten Dienstes in dem jeweiligen Vendor-OS (und damit in den darauf basierenden Systemen und Clients) zur Verfügung stehen sollen.

Stage1-Attribute werden zusammen mit dem Vendor-OS in der Datenbank gespeichert und repräsentieren die Installation des Plugins. Anzeigen lassen kann man sich diese Attribute (neben den Stage3-Attributen) mit:

 slxconfig --verbose list-vendor <system>

Stage3-Attribute

Stage3-Attribute eines Plugins haben die Aufgabe, die Konfiguration des Plugins für bestimmte Systeme/Clients vorzunehmen, d.h. die Werte dieser Attribute bestimmen, welche der möglichen Varianten des vom Plugin verwalteten
Dienstes beim Booten eines bestimmten Systems/Clients benutzt wird. Merke: Die möglichen Varianten werden durch Stage1-Attribute festgelegt, die konkrete Auswahl einer bestimmten Variante aus diesen möglichen erfolgt über Stage3-Attribute.

Stage3-Attribute können in der DB jeweils einem Vendor-OS, einem System und/oder einem Client zugeordnet werden. Beim Ermitteln der Konfiguration des Plugins werden sie auf folgende Art und Weise (in absteigender Priorität) zusammengemischt:
  1. Attribute des konkreten Clients
  2. Attribute des konkreten Systems
  3. Attribute des Vendor-OS

D.h. ein beim Client gesetztes Attribut überstimmt alle an anderen Stellen gesetzten Werte dieses Attributs.

Nutzung von Plugins

Jedes OS-Plugin stellt eine bestimmte Erweiterung für alle von OpenSLX unterstützten Vendor-OS bereit. Dies kann z.B. einerseits die Möglichkeit zur Benutzerauthentizierung per LDAP sein, als auch anderseits die automatische Integration und Konfiguration von kommerziellen Xorg-Treibern. Damit die OS-Plugins ihre spezifische Aufgabe umsetzen können, bietet der OpenSLX-Kern grundsätzlichen Support für Plugins an:

Installation (allgemein anpassen)

Der Aufruf zur Installation:

slxos-plugin install <vendor-os> xserver ati=1 nvidia=1
slxos-export export <vendor-os> <exportType>
slxconfig-demuxer
slxconfig change-system <vendor-os> xserver::prefnongpl=1

Achtung: Für die binären Treiber von ATI und/oder NVIDIA (werden im Beispiel aktiviert/installiert) braucht man die Treiber-Archive, die von den jeweiligen Seiten heruntergeladen werden können. Bei Installation des Plugins sollte der genaue Pfad der Dateien mittels slxos-plugin festgelegt werden, da die Treiber sonst nicht geeignet installiert werden können.

Grundvorraussetzung sind auch die Kernel-Headers, damit die Module in die benötigte Kernel-Version eingebaut werden kann.

UPDATE: Zur Zeit werden die Kernel-Module direkt kompiliert und die benötigten Treiber-Bibliotheken von dem jeweiligen Paketsystem der Distribution übernommen. Implementiert ist diese Methode auf Ubuntu-8.10 (sauber) und auf Suse-11.0 (alles wird 2 Mal runtergeladen). Wenn jemand herausfindet, wie man mit zypper (dem Suse-rpm-tool) die rpm's direkt herunterladen kann, sollte bitte eine Mail an eine Liste schicken.

Die Option "xserver::prefnongpl=1" dient dazu, die Treiber in Stage3 zu aktivieren.

Generischer NVidia-Installer

Für eine Reihe von Distributionen steht der vorher beschriebene Weg so nicht zur Verfügung. Dann kann man den NVidia-Treiber direkt von der NVidia-Seite (der Link wird sich im Laufe der Zeit sicherlich ändern) herunterladen und ins Verzeichnis /root/xserver-pkgs legen. Von dort wird er bei der Plugin-Installation kopiert und der Installer aufgerufen. Der Aufruf ist für die Einrichtung der Bibliotheken unproblematisch. Diese finden sich dann im Plugin-Verzeichnis unterhalb von nvidia/usr, Schwieriger wird die Erstellung des Kernel-Moduls nvidia.ko.

Hierzu müssen Kernel-Quellen und Compile-Umgebung im gewünschten Vendor-OS installiert sein. Ebenso sollte man eine Konfiguration passend zum laufenden Kernel haben (zcat /proc/config.gz bei SuSE oder /boot/config-2.6.* bei Ubuntu). Dann:

slxos-setup shell <vendor-os>
cd /usr/src/linux(-version) # je nach Distro
cp pfad-zur-config/kernel-config .config
make scripts
cd /opt/openslx/plugin-repo/xserver
NVidia-.../nvidia-installer (mit angepassten Optionen aus *nvidia-install.sh*)

Wenn alles glatt ging, gibts neben dem usr/ im Unterverzeichnis nvidia/ ein weiteres Verzeichnis modules/ in dem dann eine Datei nvidia.ko auftaucht.

Beispielablauf

Derzeit existiert einerseits ein ausführlich kommentiertes, aber funktional nur beispielhaften OS-Plugin namens 'example', welches mit einem Teil der genannten Mechanismen das "Feature" implementiert, beim Booten des Clients in Stage3 einen Smiley auszugeben, wobei die Richtung des Smileys [ ;-) oder (-; ] über eine Konfigurationsdatei angegeben werden kann. Andererseits gibt es das Plugin desktop, welches eine ganze Reihe von Dateien anlegt in Abhängigkeit verschiedener Variablen anlegt und konfiguriert. Dabei passieren Teile im Stage1 (Perl-Code) und Teile im Stage3 (Shell).

Im Folgenden soll der konkrete Ablauf von 'example' nachvollzogen werden:
  1. Das Plugin 'example' wird im Vendor-OS 'suse-11.1' "installiert" (und aktiviert - das ist der Default, so dass es nicht wirklich notwendig ist. Damit es auch wirklich zur Verfügung steht, ist anschliessend noch ein Export erforderlich. Hier passiert nichts Plugin-spezifisches, alle notwendigen statischen Dateien werden erst jetzt in den Export übertragen.):
    slxos-plugin install suse-11.1 example active=1
    slxos-export export suse-11.1 <export-type>
    
  2. Beim Generieren der Conf-TGZs mittels
    slxconfig-demuxer
    

    wird das Stage1-Perlmodul example.pm erneut geladen und nach der (Default-)Konfiguration für die Clients gefragt. Diese Konfiguration wird zusammen mit dem Stage3-Shellskript des Plugins in das Config-TGZ des Clients übertragen:
    fry:/tmp/xxx # tar tzvf /srv/openslx/tftpboot/client-config/suse-11.1\:nfs/default.tgz
    drwxr-xr-x root/root         0 2007-10-14 18:41:10 initramfs/
    -rw-r--r-- root/root       482 2007-10-14 18:41:10 initramfs/machine-setup
    -rwxr--r-- root/root       317 2007-06-17 11:51:38 initramfs/postinit.local
    drwxr-xr-x root/root         0 2007-10-14 18:41:10 initramfs/plugin-init.d/
    -rwxr-xr-x root/root      1518 2007-10-14 18:41:10 initramfs/plugin-init.d/50_example.sh
    drwxr-xr-x root/root         0 2007-10-14 18:41:10 initramfs/plugin-conf/
    -rw-r--r-- root/root        49 2007-10-14 18:41:10 initramfs/plugin-conf/example.conf
    -rwxr--r-- root/root       230 2007-06-17 11:44:58 initramfs/preinit.local
    drwxr-xr-x root/root         0 2006-09-22 21:08:33 rootfs/
    drwxr-xr-x root/root         0 2006-09-23 17:42:43 rootfs/etc/
    -rw-r----- root/root       597 2006-09-23 17:46:16 rootfs/etc/shadow
    -rw-r--r-- root/root      1264 2006-09-23 17:46:09 rootfs/etc/passwd
    

    Man sieht, dass das Stage3-Runlevelskript des example-Plugins die Priorität 50 erhalten hat, was der Default-Priorität entspricht:
    fry:/tmp/xxx # cat initramfs/plugin-conf/example.conf
    example_active="1" 
    example_preferred_side="left" 
    

    Außerdem kann man der Konfiguration noch entnehmen, dass das Plugin grundsätzlich aktiv ist (d.h. in Stage3 ausgeführt werden soll) und dass man es vorzieht, den Kopf nach links zu neigen, um das Smiley zu lesen ...

Aktivierung/Deaktivierung von Plugins

Merke: Ein Plugin kann einerseits installiert und aktiviert werden:
  • Installieren heißt, es wird ins Vendor-OS kopiert (und installiert dort evtl. weitere Pakete).
  • Aktivieren erfolgt pro System (per Vorgabe ist jedes Plugin nach Installation in ein Vendor-OS in allen Systemen dieses Vendor-OS aktiviert).

Informationen zur Pluginentwicklung

Init-Hooks

Um es einzelnen Plugins zu ermöglichen, direkten Einfluss auf die Abläufe im Stage3-Initskript zu nehmen, gibt es noch das Konzept von Init-Hooks, d.h. das Stage3-Initskript sieht es vor, beim Erreichen von bestimmten Zuständen (Kernelvars ausgewertet, Root-FS gemountet, usw.) sämtliche in einem bestimmten Verzechnis befindliche Shellskripte auszuführen (ein simpler Eventmechanismus). Ein Plugin, welches in den Ablauf des Init-Skriptes eingreifen muss, kann nun eigene Hook-Skripte an die "richtigen" Stellen plazieren, um so die Gelegenheit zu erhalten, auf das jeweilige "Ereignis" zu reagieren.
So benutzt das bootsplash-Plugin diverse solcher Hook-Skripte, um den Bootsplash zu updaten.

Die Liste der existierenden Init-Hooks wird sich wohl noch verändern, aber bislang gibt es erstmal folgende Ereignisse:
  • 00-started
  • 05-have-kernelvars
  • 10-nw-if-config
  • 20-nw-bridge-config
  • 25-have-ip-config
  • 35-have-network-root
  • 40-started-hw-config
  • 50-have-layered-fs
  • 60-have-servconfig
  • 70-before-plugins
  • 80-after-plugins
  • 85-have-initial-boot
  • 90-postinit-done
  • 95-cleanup
  • 99-handing-over

Distrospezifische Funktionen

Da bei der Installation eines Plugins in Stage1 klar ist, um welche Distribution es sich handelt, ist dieses der richtige Ort, das Stage3-Runlevel-Skript zu erzeugen, so denn eines benötigt wird. Dieses erfolgte ursprünglich via:

    my $runlevelScript = $self->{distro}->function(attributes);
    spitFile($file, $runlevelScript);

was sich aber als nicht so praktisch erwies. Deshalb gibt es nun einen abstrakteren Ansatz (Doku bisher nur in r2405):
        * use OpenSLX::DistroUtils;
         1. get initfile object
            my $initfile = newInitFile();
         1. modify the object
            $initfile->addToBlock('head', 'set some variables');
         1. 3rd parameter is setting the priority of the entry
         2. default is 5
            $initfile->addToBlock('head', 'set sth at the very beginning', 1);
            $initfile->addToBlock('start', '/bin/startsomething');
            $initfile->addToBlock('stop', '/bin/stopsomething');
            $initfile->setName('foo');
            ..
         1. get generated content of initfile
            $source = getInitFileForDistro($initfile, 'Ubuntu');

Stage 3

Für Stage3 distrospezifische Skripte (beispielsweise angepasste Runlevelskripten) - diese werden bei der Installation des Plugins an eine einheitliche Stelle kopiert - kann man einen Blick auf das desktop Plugin werfen. Hierzu lässt sich folgender Ansatz verwenden:
  • Entsprechendes Skript in Stage1 (Distro ist hier bekannt) kopieren und
    _XX_plugin.sh_ nur mit
    ..??
    

    füllen. Ein Beispiel ist in Changeset r2012 zu sehen.

Stage 4

Stage4-Skripte (init-Skripte) kann man in Stage3 ablegen (sofern vendorOS je nach Stage3 Konfiguration verschiedene init-Skripte verwendet). Hier gibt es die Variable ${D_INITDIR}. Beispiel:

cp /mnt/opt/openslx/plugin-repo/vmware/${vmware_kind}/vmware.init \
      /mnt/etc/${D_INITDIR}/vmware-env

Für die Aktivierung gibt es in Stage3 einen eigenen Befehl rrlinker.

Die zu kopierende Datei (hier vmware.init) ist distributionsspezifisch, da in Stage1 bekannt ist um welche Distribution es sich handelt und diese dementsprechend dort angepasst abgelegt wird.

Testen und Fehlersuche

Zum Ausprobieren, ob ein bestimmtes Perl-Modul (z.B. Plugin) funktioniert (dazu muss man sich im Pfad mit allen Plugins befinden ~/os-plugins/plugins):

perl -I /opt/openslx/lib/ -cw desktop/OpenSLX/Distro/Suse.pm

Pakete im Vendor-OS nachinstallieren

Im Stage1 kann man Pakete nachinstallieren - sofern diese Standardbestandteil der Distribution sind. Dies geschieht z.B. im Desktop-Plugin. Dieser Abschnitt dient lediglich als Hinweis, dass dies möglich ist und an welcher Stelle man den Ablauf nachschlagen kann.

Innerhalb des Desktop Plugins - dekstop.pm:

sub installationPhase
{
    my $self = shift;
    my $info = shift;

    ...

    $self->_installRequiredPackages();

}

sub _installRequiredPackages
{
    my $self  = shift;

    my $engine = $self->{'os-plugin-engine'};

    # Test ob Gnome installiert ist. Falls nicht, so wird
    # installGNOME aus der spezifischen Distributionsdatei ausgefuehrt
    if ($self->{'gnome'} && !$self->{distro}->isGNOMEInstalled()) {
        $self->{distro}->installGNOME();
    }
    ....

    return 1;
}

Ablauf eines Installationsaufrufes (z.B. installGNOME()) anhand der aufgerufenen Funktionen nach zu verfolgen. Interessant ist in diesem Beispiel auch die vorherige Überprüfung isGNOMEInstalled(). Neben der Gnome Umgebung können von diesem Plugin auch andere Pakete installiert werden. Je nach Bedarf kann aufgrund von distributionsspezifischen Funktionen die Betrachtung eines zu installierenden Paketes nahegelegt werden.

Die Frage die noch offen bleibt ist, in welchen Fällen man dies anwenden sollte.

Abbruchwerte der Pluginstallation innerhalb von plugin.pm

Manchmal kann es vorkommen, das man die Installation eines Plugins abbrechen will. Z.B. wenn bestimmte Dateien oder Pakete fehlen und auch nicht installierbar sind.

Ein "exit 1" in preInstallationPhase() fuehrt dazu, das das Plugin als nicht installiert gelistet wird (dies ist auch in der entsprechenden perldoc, die preInstallationPhase() beschreibt dokumentiert.

Zu installationPhase() wurde auf openslx-devel@ folgendes geschrieben:

> If I do a "exit 1" in installationPhase(), the plugin won't get
> installed. But in "slxos-plugin list-installed <vendorOS>" the plugin is
> still listed as installed.

The underlying problem here is that installationPhase() is being invoked 
inside the chroot. In order to do so and be able to leave the chroot later, 
the main process forks a child, which enters the chroot and invokes 
installationPhase(). The parent process waits for the child to exit and then 
continues (still outside of the chroot).

> Which exit value do I need in installationPhase() so the plugin won't
> show up as installed in slxos-plugin?

I think you should use die("message") instead of printing an error and then 
invoke exit(). The death message should then be propagated back to the parent 
process correctly, causing the parent to stop, too.