diff --git a/etc/templates/initrd b/etc/templates/initrd index 5a6984f4..a0d8d0fd 100644 --- a/etc/templates/initrd +++ b/etc/templates/initrd @@ -1,9 +1,81 @@ -# initrd template file -# -# supported variables: @initrd@ -# @instroot@ +# initrd template + +:makedir + @initrd@/modules + @initrd@/sbin + @initrd@/dev + @initrd@/etc + @initrd@/etc/udev/rules.d + @initrd@/lib/udev/rules.d + @initrd@/proc + @initrd@/selinux + @initrd@/sys + @initrd@/etc/terminfo/{a,b,d,l,s,v,x} + @initrd@/tmp + @initrd@/usr/libexec + @initrd@/usr/@libdir@/NetworkManager + @initrd@/usr/share/dbus-1/system-services + @initrd@/var/cache/hald + @initrd@/var/lib/dbus + @initrd@/var/lib/dhclient + @initrd@/var/lock/rpm + @initrd@/var/run + @initrd@/var/run/dbus + @initrd@/var/run/hald + @initrd@/var/run/NetworkManager + @initrd@/etc/dbus-1/system.d + @initrd@/etc/modprobe.d + @initrd@/etc/NetworkManager/dispatcher.d + @initrd@/@libdir@/dbus-1 + @initrd@/etc/sysconfig/network-scripts + @initrd@/usr/share/PolicyKit/policy + @initrd@/etc/PolicyKit + @initrd@/var/lib/misc + @initrd@/etc/hal/fdi + @initrd@/usr/share/hal/fdi + @initrd@/usr/share/hwdata + @initrd@/etc/rc.d/init.d + @initrd@/usr/sbin + @initrd@/var/run/wpa_supplicant + +:edit + @initrd@/etc/arch text "@buildarch@" + :copy - # comment - @instroot@/bin/bash to @initrd@/bin - @instroot@/bin/mount to @initrd@/bin + @instroot@/etc/passwd to @initrd@/etc + @instroot@/etc/group to @initrd@/etc + @instroot@/etc/nsswitch.conf to @initrd@/etc + +# mount/umount +:copy + @instroot@/bin/mount to @initrd@/sbin/mount + @instroot@/bin/umount to @initrd@/sbin/umount + @instroot@/usr/sbin/mount.nfs to @initrd@/sbin/mount.nfs +:link + @initrd@/sbin/umount.nfs to @initrd@/sbin/mount.nfs + +# udev +:copy + @instroot@/sbin/udevd to @initrd@/sbin + @instroot@/sbin/udevadm to @initrd@/sbin +:link + @initrd@/sbin/udevinfo to @initrd@/sbin/udevadm + @initrd@/sbin/udevsettle to @initrd@/sbin/udevadm + +# bash +:copy + @instroot@/bin/bash to @initrd@/sbin/bash +:link + @initrd@/sbin/sh to @initrd@/sbin/bash +:copy + @instroot@/sbin/consoletype to @initrd@/sbin/consoletype + @instroot@/usr/bin/logger to @initrd@/sbin/logger + +:copy + @instroot@/etc/rc.d/init.d/functions to @initrd@/etc/rc.d/init.d + @instroot@/etc/sysconfig/network-scripts/network-functions* @initrd@/etc/sysconfig/network-scripts + +:link + @initrd@/etc/init.d to /etc/rc.d/init.d + diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py index 200f7227..3488f61b 100644 --- a/src/pylorax/__init__.py +++ b/src/pylorax/__init__.py @@ -1,4 +1,4 @@ -# __init__.py +# pylorax/__init__.py import sys import os diff --git a/src/pylorax/actions/__init__.py b/src/pylorax/actions/__init__.py index eba3a70c..8db8d439 100644 --- a/src/pylorax/actions/__init__.py +++ b/src/pylorax/actions/__init__.py @@ -1,5 +1,10 @@ # pylorax/actions/__init__.py +import logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger('pylorax.actions') + +import sys import os @@ -7,22 +12,28 @@ def getActions(): actions = {} root, actions_dir = os.path.split(os.path.dirname(__file__)) + sys.path.insert(0, root) + modules = set() for filename in os.listdir(os.path.join(root, actions_dir)): if filename.endswith('.py') and filename != '__init__.py': basename, extension = os.path.splitext(filename) - modules.add(os.path.join('pylorax', actions_dir, basename).replace('/', '.')) + modules.add(os.path.join(actions_dir, basename).replace('/', '.')) for module in modules: - print('Loading actions from %s' % module) + logger.debug('loading actions from module %s' % module) imported = __import__(module, globals(), locals(), [module], -1) + try: commands = getattr(imported, 'COMMANDS') except AttributeError: + logger.debug('no actions found') continue else: for command, classname in commands.items(): - print('Loaded: %s' % classname) + logger.debug('loaded: %s' % classname) actions[command] = getattr(imported, classname) + sys.path.pop(0) + return actions diff --git a/src/pylorax/actions/fileactions.py b/src/pylorax/actions/base.py similarity index 52% rename from src/pylorax/actions/fileactions.py rename to src/pylorax/actions/base.py index 30cc7c29..b2e487cf 100644 --- a/src/pylorax/actions/fileactions.py +++ b/src/pylorax/actions/base.py @@ -1,20 +1,79 @@ -# pylorax/actions/fileactions.py - -from pylorax.base import LoraxAction +# pylorax/actions/base.py import os import re -from pylorax.utils.fileutil import cp, mv, touch, edit, replace + +from pylorax.utils.fileutils import cp, mv, touch, edit, replace +# command:action mapping +# maps a template command to an action class +# if you want your new action to be supported, you have to include it in this mapping COMMANDS = { 'copy': 'Copy', 'move': 'Move', 'link': 'Link', 'touch': 'Touch', 'edit': 'Edit', - 'replace': 'Replace' } + 'replace': 'Replace', + 'makedir': 'MakeDir' } +class LoraxAction(object): + """Actions base class. + + To create your own custom action, subclass this class and override the methods you need. + + A valid action has to have a REGEX class variable, which specifies the format of the action + command line, so the needed parameters can be properly extracted from it. + All the work should be done in the execute method, which will be called from Lorax. + At the end, set the success to False, or True depending on the success or failure of your action. + + If you need to install some package prior to executing the action, return an install pattern + with the "install" property. Lorax will get this first, and will try to install the needed + package. + + Don't forget to include a command:action map for your new action in the COMMANDS dictionary. + Action classes which are not in the COMMANDS dictionary will not be loaded. + + You can take a look at some of the builtin actions to get an idea of how to create your + own actions.""" + + + REGEX = r'' # regular expression for extracting the parameters from the command line + + def __init__(self): + if self.__class__ is LoraxAction: + raise TypeError, 'LoraxAction is an abstract class, cannot be used this way' + + self._attrs = {} + self._attrs['success'] = None # success is None, if the action wasn't executed yet + + def __str__(self): + return '%s: %s' % (self.__class__.__name__, self._attrs) + + def execute(self, verbose=False): + """This method is the main body of the action. Put all the "work" stuff in here.""" + raise NotImplementedError, 'execute method not implemented for LoraxAction class' + + @property + def success(self): + """Returns if the action's execution was successful or not.""" + return self._attrs['success'] + + @property + def install(self): + """Returns a pattern that needs to be installed, prior to calling the execute method.""" + return None + + def getDeps(self): + # FIXME hmmm, how can i do this more generic? + return None + + +# +-----------------+ +# | builtin actions | +# +-----------------+ + class Copy(LoraxAction): REGEX = r'^(?P.*?)\sto\s(?P.*?)(\smode\s(?P.*?))?$' @@ -33,9 +92,6 @@ class Copy(LoraxAction): cp(src=self.src, dst=self.dst, mode=self.mode, verbose=verbose) self._attrs['success'] = True - def getDeps(self): - return self._attrs['src'] - @property def src(self): return self._attrs['src'] @@ -50,7 +106,11 @@ class Copy(LoraxAction): @property def install(self): - return self._attrs.get('src') + return self._attrs['src'] + + @property + def getDeps(self): + return self._attrs['src'] class Move(Copy): @@ -68,10 +128,6 @@ class Link(LoraxAction): self._attrs['name'] = kwargs.get('name') self._attrs['target'] = kwargs.get('target') - file = getFileName(self._attrs['name']) - if file: - self._attrs['install'] = file - def execute(self, verbose=False): os.symlink(self.name, self.target) self._attrs['success'] = True @@ -86,7 +142,7 @@ class Link(LoraxAction): @property def install(self): - return self._attrs['install'] + return self._attrs['target'] class Touch(LoraxAction): @@ -120,10 +176,6 @@ class Edit(Touch): else: self._attrs['append'] = False - file = getFileName(self._attrs['filename']) - if file: - self._attrs['install'] = file - def execute(self, verbose=False): edit(filename=self.filename, text=self.text, append=self.append, verbose=verbose) self._attrs['success'] = True @@ -138,7 +190,7 @@ class Edit(Touch): @property def install(self): - return self._attrs['install'] + return self._attrs['filename'] class Replace(Touch): @@ -150,10 +202,6 @@ class Replace(Touch): self._attrs['find'] = kwargs.get('find') self._attrs['replace'] = kwargs.get('replace') - file = getFileName(self._attrs['filename']) - if file: - self._attrs['install'] = file - def execute(self, verbose=False): replace(filename=self.filename, find=self.find, replace=self.replace, verbose=verbose) self._attrs['success'] = True @@ -168,4 +216,27 @@ class Replace(Touch): @property def install(self): - return self._attrs['install'] + return self._attrs['filename'] + + +class MakeDir(LoraxAction): + + REGEX = r'^(?P.*?)(\smode\s(?P.*?))?$' + + def __init__(self, **kwargs): + LoraxAction.__init__(self) + self._attrs['dir'] = kwargs.get('dir') + self._attrs['mode'] = kwargs.get('mode') + + def execute(self, verbose=False): + if not os.path.isdir(self.dir): + os.makedirs(path=self.dir, mode=self.mode) + self._attrs['success'] = True + + @property + def dir(self): + return self._attrs['dir'] + + @property + def mode(self): + return self._attrs['mode'] diff --git a/src/pylorax/base.py b/src/pylorax/base.py index 8eca6bca..56c40f61 100644 --- a/src/pylorax/base.py +++ b/src/pylorax/base.py @@ -3,28 +3,6 @@ import commands -class LoraxAction(object): - - REGEX = r'.*' - - def __init__(self): - if self.__class__ is LoraxAction: - raise TypeError, 'LoraxAction is an abstract class' - - self._attrs = {} - self._attrs['success'] = None - - def __str__(self): - return '%s: %s' % (self.__class__.__name__, self._attrs) - - def execute(self, verbose=False): - raise NotImplementedError, 'execute method not implemented for LoraxAction class' - - @property - def success(self): - return self._attrs['success'] - - def seq(arg): if type(arg) not in (type([]), type(())): return [arg] diff --git a/src/pylorax/config.py b/src/pylorax/config.py index f605a0d7..7a28c93e 100644 --- a/src/pylorax/config.py +++ b/src/pylorax/config.py @@ -87,6 +87,12 @@ class Template(object): lines = f.readlines() f.close() + # check vars + for lineno, line in enumerate(lines, start=1): + for var in re.findall(r'@.*?@', line) + if var not in vars: + raise TemplateError, 'unknown variable "%s" on line %d' % (var, lineno) + active_action = '' in_action = False for lineno, line in enumerate(lines, start=1): diff --git a/src/pylorax/images.py b/src/pylorax/images.py index 392994e8..cd2b0406 100644 --- a/src/pylorax/images.py +++ b/src/pylorax/images.py @@ -30,7 +30,7 @@ class Images(object): pkgs = i.getPkgs() # install needed packages - self.yum.addPackages(['anaconda', 'anaconda-runtime', 'kernel', 'syslinux', 'memtest']) + self.yum.addPackages(['anaconda', 'anaconda-runtime', 'kernel', 'syslinux']) self.yum.addPackages(pkgs) self.yum.install() diff --git a/src/pylorax/initrd.py b/src/pylorax/initrd.py index 830309a9..25a2db7f 100644 --- a/src/pylorax/initrd.py +++ b/src/pylorax/initrd.py @@ -26,7 +26,12 @@ class InitRD(object): 'initrd')) vars = { '@instroot@': self.conf.treedir, - '@initrd@': self.conf.initrddir } + '@initrd@': self.conf.initrddir, + '@libdir@': self.conf.libdir, + '@buildarch@': self.conf.buildarch, + '@confdir@' : self.conf.confdir, + '@datadir@': self.conf.datadir } + self.template = Template() for file in initrd_templates: if os.path.isfile(file): diff --git a/src/pylorax/utils/fileutil.py b/src/pylorax/utils/fileutils.py similarity index 72% rename from src/pylorax/utils/fileutil.py rename to src/pylorax/utils/fileutils.py index d8629de0..154ffa7c 100644 --- a/src/pylorax/utils/fileutil.py +++ b/src/pylorax/utils/fileutils.py @@ -9,16 +9,28 @@ import re def cp(src, dst, mode=None, verbose=False): + errors = [] for name in glob.iglob(src): - __copy(name, dst, verbose=verbose) - if mode: - os.chmod(dst, mode) + rc = __copy(name, dst, verbose=verbose) + if not rc: + errors.append('unable to copy "%s" to "%s"' % (name, dst)) + else: + if mode: + os.chmod(dst, mode) + + return errors def mv(src, dst, mode=None, verbose=False): + errors = [] for name in glob.iglob(src): - __copy(name, dst, verbose=verbose, remove=True) - if mode: - os.chmod(dst, mode) + rc = __copy(name, dst, verbose=verbose, remove=True) + if not rc: + errors.append('unable to move "%s" to "%s"' % (name, dst)) + else: + if mode: + os.chmod(dst, mode) + + return errors def rm(target, verbose=False): if os.path.isdir(target): @@ -30,10 +42,12 @@ def rm(target, verbose=False): print('removing file "%s"' % target) os.unlink(target) + return True + def __copy(src, dst, verbose=False, remove=False): if not os.path.exists(src): sys.stderr.write('cannot stat "%s": No such file or directory\n' % src) - return + return False if os.path.isdir(dst): basename = os.path.basename(src) @@ -42,7 +56,7 @@ def __copy(src, dst, verbose=False, remove=False): if os.path.isdir(src): if os.path.isfile(dst): sys.stderr.write('omitting directory "%s"\n' % src) - return + return False if not os.path.isdir(dst): os.makedirs(dst) @@ -53,7 +67,7 @@ def __copy(src, dst, verbose=False, remove=False): else: if os.path.isdir(dst): sys.stderr.write('cannot overwrite directory "%s" with non-directory\n' % dst) - return + return False try: if verbose: @@ -61,19 +75,26 @@ def __copy(src, dst, verbose=False, remove=False): shutil.copy2(src, dst) except (shutil.Error, IOError) as why: sys.stderr.write('cannot copy "%s" to "%s": %s\n' % (src, dst, why)) + return False else: if remove: if verbose: print('removing "%s"' % src) os.unlink(src) + + return True def touch(filename, verbose=False): if os.path.exists(filename): + if verbose: + print('touching file "%s"' % filename) os.utime(filename, None) return True try: + if verbose: + print('creating file "%s"' % filename) f = open(filename, 'w') except IOError: return False @@ -87,6 +108,8 @@ def edit(filename, text, append=False, verbose=False): mode = 'a' try: + if verbose: + print('editing file "%s"' % filename) f = open(filename, mode) except IOError: return False @@ -96,6 +119,8 @@ def edit(filename, text, append=False, verbose=False): return True def replace(filename, find, replace, verbose=False): + if verbose: + print('replacing "%s" for "%s" in file "%s"' % (find, replace, filename)) fin = fileinput.input(filename, inplace=1) for line in fin: line = re.sub(find, replace, line) diff --git a/src/pylorax/utils/libutil.py b/src/pylorax/utils/libutil.py index ea746e5b..ff89fdc2 100644 --- a/src/pylorax/utils/libutil.py +++ b/src/pylorax/utils/libutil.py @@ -1,4 +1,4 @@ -import sys +# pylorax/utils/libutil.py import os import commands @@ -6,7 +6,7 @@ import re class LDD(object): - def __init__(self, libroot='/'): + def __init__(self, libroot='/lib'): f = open('/usr/bin/ldd', 'r') for line in f.readlines(): line = line.strip() @@ -15,7 +15,14 @@ class LDD(object): break f.close() - self._ldd = '%s --list --library-path %s' % (ld_linux, libroot) + if libroot.endswith('/') and libroot != '/': + libroot = libroot[:-1] + + libpaths = [libroot] + if libroot.endswith('64'): + libpaths.append(libroot[:-2]) + + self._ldd = 'LD_LIBRARY_PATH="%s" %s --list' % (':'.join(libpaths), ld_linux) self._deps = set() def getDeps(self, filename): @@ -46,12 +53,3 @@ class LDD(object): @property def deps(self): return self._deps - - -if __name__ == '__main__': - ldd = LDD(libroot=sys.argv[2]) - ldd.getDeps(sys.argv[1]) - ldd.getLinks() - - for dep in ldd.deps: - print dep diff --git a/src/pylorax/utils/rpmutil.py b/src/pylorax/utils/rpmutils.py similarity index 100% rename from src/pylorax/utils/rpmutil.py rename to src/pylorax/utils/rpmutils.py