#! /bin/bash # # Wrapper um cfengine-Konfigurationsdateien zu importieren # und dabei die Reihenfolge der Bearbeitungsschritte definieren # zu koennen. # # Das Problem ist folgendes: # # -- datei: cf.main --- # # import: # cf.task1 # cf.task2 # # -- eof: cf.main --- # -- datei: cf.task1 --- # control: # actionsequence = ( copy shellcommands ) # copy: # $(filetree)/etc/someconf /etc/someconf # shellcommands: # "/etc/init.d/some restart" # -- eof: cf.task1 --- # # -- datei: cf.task2 --- # control: # actionsequence = ( shellcommands copy ) <-- beachte umgekehrte reihenfolge # shellcommands: # "/etc/init.d/other restart" # copy: # /var/run/something.pid /var/backup/something.pid # -- eof: cf.task2 --- # # Wie man sieht, soll task1 _zuerst_ kopieren und _dann_ restarten # Task2 koennte sowas wie ein regelmaessiger restart sein, bei dem # nachher eine Status-Datei gesichert wird # # Was jetzt passiert ist folgendes: # - cfengine macht aus allen Includes ein grosses # - bei der Controlsequence wird immer angehangen # - dh. das resultierende "file" sieht so aus: # # -- datei: --- # control: # actionsequence = ( copy shellcommands shellcommands copy ) # shellcommands: # "/etc/init.d/some restart" # "/etc/init.d/other restart" # copy: # $(filetree)/etc/someconf /etc/someconf # /var/run/something.pid /var/backup/something.pid # -- eof: --- # # Das verursacht folgende Probleme: # - copy und shellcommands werden 2x ausgefuehrt, dabei # ist bei der 2.Ausfuehrung unter Umstaenden noch nicht eine Minute # (default-Wert von "ifelapsed") vergangen, so das beim 2. mal # _garnichts_ gemacht wird # - es ist fraglich ob Task 2 ueberhaupt noch funktioniert # - durch die doppelte Ausfuehrung wird die Bearbeitungszeit erhoeht # - alle Tasks teilen sich einen Namespace fuer Locks, so dass # z.B. "processes" nicht mehr Funktioniert, da SetOptionString nicht # mehr ausgefuehrt wird, da IfElapsed noch nicht vorbei ist # - alle Tasks teilen sich einen Namespace fuer lokale Variablen # wir man sieht, fuehrt dieses Konstrukt bei grossen imports, # wie z.B. im Linuxpool zu teilweise unvorhersehbaren Effekten # # Die Loesung besteht darin, dass cfengine sich selbst per shellcommands # startet, dabei aber ueber "-f" den Name der Taskdatei uebergibt. # Der Hauptvorteil liegt darin, dass mit dieser Vorgehensweise # _EIN_ cfagent-Lauf ausreicht um alle Tasks vollstaendig und in der # gewuenschten Reihenfolge laufen zu lassen. Dabei wirken Locks jetzt # "lokal", d.h. pro Datei in der sie definiert sind. # # Der Nachteil besteht darin, dass man sich jetzt selbst um das # weiterreichen von Variablen und Definierten Klassen kuemmern # muss. # 1. Wie werden "globale" Variable weitergereicht ? # Globale Variable, die z.B. "filetree" werden in einer Extradatei # abgelegt und von jedem Task selbst importiert # 2. Wie werden "lokale" Variable an Subtask weitergereicht ? # Das geht im Moment (noch?) nicht. D.h. Variablen, die in # 30xts/cf.main definiert werden sind fuer Tasks der "Klasse" # 30xts nicht verfuegbar. Das koennte man ueber ein 2. import # realisieren, damit koennen Tasks aber nicht mehr zwischen # Klassen hin und her geschoben werden. (Was ziemlich nuetzlich ist ;-) # 3. Wie werden Classes weitergereicht ? # Moeglichkeit1: Ueber import. Das funktioniert aber nicht. (Warum # auch immer, habe in der cfengine-Doku nichts dazu gefunden) # Moeglichkeit2: Ueber die Environment und den Kommandozeilen-Parameter # "--define" von cfagent. <- genau das macht dieses Skript ;-) # Die definierten Klassen stellt cfengine in der Variable ALLCLASSES # an shellcommands zur Verfuegung, wenn man es mit "--use-env" startet # 4. Aufgrund des kranken Scopings von cfengine, passiert folgendes: # control: # ... # import: # datei-mit-super-variablen # subtaskX # shellcommands: # blabla # # Merkwuerdigerweise, kann man in subtaskX auf die Variablen, die in der # Datei "datei-mit-super-variablen" definiert sind, zugreifen. # An der Stelle "blabla", ist jedoch _keine_ dieser Variablen definiert. # # Dieses Verhalten ist nicht besonders intuitiv, weshalb alle Scripte # die mit import-hack.sh ausgefuehrt werden, nochmal gewrappt werden. # D.h. es wir eine temporaere Datei erzeugt, mit folgendem Inhalt: # import: # # # # Damit sind dann im eigentlichen "Zielscript" alle Variablen wie gewohnt # Verfuegbar... ######################################################################### # Import-Script # - emulates imports, where global variables and classes are inherited, # _but_ actionsequence is script-specific # # written by Jens Braeuer , 14.06.2005 # # WARNING:: before editing, be _SURE_ YOU KNOW WHAT YOU DO and that # YOU UNDERSTOOD THE IMPLEMENTATION OF CFENGINE's # variable and class SCOPES !!! ######################################################################### # ------------------------------------------------------ # binary settings - as needed in Debian Sarge # other system may need modifications RM=/bin/rm CAT=/bin/cat ECHO=/bin/echo CUT=/usr/bin/cut TR=/usr/bin/tr LOGGER="/usr/bin/logger -t import-hack " CFAGENT=/usr/sbin/cfagent TEMPFILE=/bin/tempfile BASENAME=/usr/bin/basename # ------------------------------------------------------ # env settings export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin # ------------------------------------------------------ # default options for cfagent cfopts="--use-env " # this file is imported by generated scripts cfglobal="/etc/cfengine/global-options.conf" # ------------------------------------------------------ # function to filter out hard-classes. these classes can not be defined # via --define, so they _have_to_ be filtered # # WARNING: result is stored in $filterResult # filterResult="" function filterClasses() { filterResult="" case "$1" in 130_149*|ipv4_*|net_iface*|*zrz_TU_Berlin_DE) ;; Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday) ;; January|February|March|April|May|June) ;; July|August|September|November|December) ;; Yr[0-9]*|Day[0-9]*|Hr[0-9]*|Min[0-9]*|Q[1-4]) ;; any|compiled_on*|cfengine_*) ;; debian*) ;; 32_bit|64_bit|i686|i586|i486) ;; linux|linux_*) ;; *) filterResult="--define $1" ;; esac } # ------------------------------------------------------ # print and log the msg, then exit function fatalError() { local msg="$@" ${LOGGER} "${msg}" ${ECHO} "${msg}" exit 1 } # ------------------------------------------------------ # main() # ------------------------------------------------------ # sanity checks filename="${1}" if [ "${filename}" == "" ]; then fatalError "no first argument given" elif [ ! -f "${filename}" ]; then fatalError "file does not exist: ${filename}" elif [ -z "${CFALLCLASSES}" ]; then fatalError "environment-variable CFALLCLASSES not set." fi # get all classes (including hard-classes) from $CFALLCLASSES classes=`${ECHO} ${CFALLCLASSES} | "${TR}" ':' ' '` for cl in ${classes}; do filterClasses "$cl" if [ "${filterResult}" != "" ]; then cfopts="${cfopts} ${filterResult}" fi done # create a temp-wrapper-script, DONT use tempname, this will # f*ck off cfeniges locks-database and make all # ifelapsed-settings non-functional. (you know what you do ? ;-) umask 0077 wrappername=`${BASENAME} "${filename}"` wrappername="/tmp/${wrappername}" if [ -e "${wrappername}" ]; then fatalError "file ${wrappername} already exists. Either this is a bug or the previous run is not finished yet..." fi ${CAT} >"${wrappername}" <