diff --git a/etc/modules b/etc/modules/modules similarity index 100% rename from etc/modules rename to etc/modules/modules diff --git a/etc/alpha/packages b/etc/packages/alpha/packages similarity index 100% rename from etc/alpha/packages rename to etc/packages/alpha/packages diff --git a/etc/i386/packages b/etc/packages/i386/packages similarity index 100% rename from etc/i386/packages rename to etc/packages/i386/packages diff --git a/etc/ia64/packages b/etc/packages/ia64/packages similarity index 100% rename from etc/ia64/packages rename to etc/packages/ia64/packages diff --git a/etc/packages b/etc/packages/packages similarity index 100% rename from etc/packages rename to etc/packages/packages diff --git a/etc/ppc/packages b/etc/packages/ppc/packages similarity index 100% rename from etc/ppc/packages rename to etc/packages/ppc/packages diff --git a/etc/ppc64/packages b/etc/packages/ppc64/packages similarity index 100% rename from etc/ppc64/packages rename to etc/packages/ppc64/packages diff --git a/etc/s390/packages b/etc/packages/s390/packages similarity index 100% rename from etc/s390/packages rename to etc/packages/s390/packages diff --git a/etc/s390x/packages b/etc/packages/s390x/packages similarity index 100% rename from etc/s390x/packages rename to etc/packages/s390x/packages diff --git a/etc/sparc/packages b/etc/packages/sparc/packages similarity index 100% rename from etc/sparc/packages rename to etc/packages/sparc/packages diff --git a/etc/x86_64/packages b/etc/packages/x86_64/packages similarity index 100% rename from etc/x86_64/packages rename to etc/packages/x86_64/packages diff --git a/etc/templates/initrd b/etc/templates/initrd index a0d8d0fd..48e34e7b 100644 --- a/etc/templates/initrd +++ b/etc/templates/initrd @@ -1,81 +1,76 @@ # 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 +# create required directories +makedir @initrd@/modules +makedir @initrd@/sbin +makedir @initrd@/dev +makedir @initrd@/etc +makedir @initrd@/etc/udev/rules.d +makedir @initrd@/lib/udev/rules.d +makedir @initrd@/proc +makedir @initrd@/selinux +makedir @initrd@/sys +makedir @initrd@/etc/terminfo/a +makedir @initrd@/etc/terminfo/b +makedir @initrd@/etc/terminfo/d +makedir @initrd@/etc/terminfo/l +makedir @initrd@/etc/terminfo/s +makedir @initrd@/etc/terminfo/v +makedir @initrd@/etc/terminfo/x +makedir @initrd@/tmp +makedir @initrd@/usr/libexec +makedir @initrd@/usr/@libdir@/NetworkManager +makedir @initrd@/usr/share/dbus-1/system-services +makedir @initrd@/var/cache/hald +makedir @initrd@/var/lib/dbus +makedir @initrd@/var/lib/dhclient +makedir @initrd@/var/lock/rpm +makedir @initrd@/var/run +makedir @initrd@/var/run/dbus +makedir @initrd@/var/run/hald +makedir @initrd@/var/run/NetworkManager +makedir @initrd@/etc/dbus-1/system.d +makedir @initrd@/etc/modprobe.d +makedir @initrd@/etc/NetworkManager/dispatcher.d +makedir @initrd@/@libdir@/dbus-1 +makedir @initrd@/etc/sysconfig/network-scripts +makedir @initrd@/usr/share/PolicyKit/policy +makedir @initrd@/etc/PolicyKit +makedir @initrd@/var/lib/misc +makedir @initrd@/etc/hal/fdi +makedir @initrd@/usr/share/hal/fdi +makedir @initrd@/usr/share/hwdata +makedir @initrd@/etc/rc.d/init.d +makedir @initrd@/usr/sbin +makedir @initrd@/var/run/wpa_supplicant -:edit - @initrd@/etc/arch text "@buildarch@" +# set the buildarch +edit @initrd@/etc/arch text "@buildarch@" +# copy etc +copy @instroot@/etc/passwd to @initrd@/etc +copy @instroot@/etc/group to @initrd@/etc +copy @instroot@/etc/nsswitch.conf to @initrd@/etc -:copy - @instroot@/etc/passwd to @initrd@/etc - @instroot@/etc/group to @initrd@/etc - @instroot@/etc/nsswitch.conf to @initrd@/etc +# copy mount/umount +copy @instroot@/bin/mount to @initrd@/sbin/mount +copy @instroot@/bin/umount to @initrd@/sbin/umount +copy @instroot@/sbin/mount.nfs to @initrd@/sbin/mount.nfs +link @initrd@/sbin/umount.nfs to mount.nfs -# 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 +# copy udev +copy @instroot@/sbin/udevd to @initrd@/sbin +copy @instroot@/sbin/udevadm to @initrd@/sbin +link @initrd@/sbin/udevinfo to udevadm +link @initrd@/sbin/udevsettle to udevadm -# 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 +# copy bash +copy @instroot@/bin/bash to @initrd@/sbin/bash +link @initrd@/sbin/sh to bash +copy @instroot@/sbin/consoletype to @initrd@/sbin/consoletype +copy @instroot@/usr/bin/logger to @initrd@/sbin/logger -# 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 +copy @instroot@/etc/rc.d/init.d/functions to @initrd@/etc/rc.d/init.d +copy @instroot@/etc/sysconfig/network-scripts/network-functions* to @initrd@/etc/sysconfig/network-scripts +link @initrd@/etc/init.d to /etc/rc.d/init.d diff --git a/share/images/README b/share/images/README new file mode 100644 index 00000000..87c66277 --- /dev/null +++ b/share/images/README @@ -0,0 +1,10 @@ +This directory contains image files that can be used to create media +capable of starting the @PRODUCT@ installation process. + +The boot.iso file is an ISO 9660 image of a bootable CD-ROM. It is useful +in cases where the CD-ROM installation method is not desired, but the +CD-ROM's boot speed would be an advantage. + +To use this image file, burn the file onto CD-R (or CD-RW) media as you +normally would. + diff --git a/share/images/pxeboot/README b/share/images/pxeboot/README new file mode 100644 index 00000000..e9018f8d --- /dev/null +++ b/share/images/pxeboot/README @@ -0,0 +1,7 @@ +The files in this directory are useful for booting a machine via PXE. + +The following files are available: +vmlinuz - the kernel used for the installer +initrd.img - an initrd with support for all install methods and + drivers supported for installation of @PRODUCT@ + diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py index 3488f61b..7427e04e 100644 --- a/src/pylorax/__init__.py +++ b/src/pylorax/__init__.py @@ -7,14 +7,14 @@ import tempfile import time import ConfigParser import re +from errors import LoraxError from config import Container -import utils.rpmutil as rpmutil +from utils.rpmutils import Yum +from utils.fileutils import rm import images -from exceptions import LoraxError - class Config(Container): def __init__(self): @@ -52,50 +52,52 @@ class Lorax(object): # check if we have all required options if not self.conf.repos: - raise LoraxError, 'missing repos' + raise LoraxError, "missing required parameter 'repos'" if not self.conf.outdir: - raise LoraxError, 'missing outdir' + raise LoraxError, "missing required parameter 'outdir'" if not self.conf.product: - raise LoraxError, 'missing product' + raise LoraxError, "missing required parameter 'product'" if not self.conf.version: - raise LoraxError, 'missing version' + raise LoraxError, "missing required parameter 'version'" if not self.conf.release: - raise LoraxError, 'missing release' + raise LoraxError, "missing required parameter 'release'" self.yum = None def run(self): - print('Collecting repos...') + bold = ('\033[1m', '\033[0m') + + print('%sCollecting repos%s' % bold) self.collectRepos() # check if we have at least one valid repository if not self.conf.repo: - sys.stderr.write('ERROR: no valid repository\n') + sys.stderr.write('ERROR: No valid repository\n') sys.exit(1) - print('Initializing directories...') + print('%sInitializing directories%s' % bold) self.initDirs() - print('Initializing yum...') + print('%sInitializing yum%s' % bold) self.initYum() - print('Setting build architecture...') + print('%sSetting build architecture%s' % bold) self.setBuildArch() - print('Writing .treeinfo...') + print('%sWriting .treeinfo%s' % bold) self.writeTreeInfo() - print('Writing .discinfo...') + print('%sWriting .discinfo%s' % bold) self.writeDiscInfo() - print('Preparing the install tree...') + print('%sPreparing the install tree%s' % bold) self.prepareInstRoot() - print('Creating the images...') + print('%sCreating the images%s' % bold) self.makeImages() if self.conf.cleanup: - print('Cleaning up...') + print('%sCleaning up%s' % bold) self.cleanUp() def collectRepos(self): @@ -128,7 +130,7 @@ class Lorax(object): os.makedirs(treedir) cachedir = os.path.join(self.conf.tempdir, 'yumcache') os.makedirs(cachedir) - initrddir = os.path.join(self.conf.tempdir, 'initrd') + initrddir = os.path.join(self.conf.tempdir, 'initrddir') os.makedirs(initrddir) print('Working directories:') @@ -145,8 +147,8 @@ class Lorax(object): try: f = open(yumconf, 'w') - except IOError: - sys.stderr.write('ERROR: Unable to write yum.conf file\n') + except IOError as why: + sys.stderr.write('ERROR: Unable to write yum.conf file: %s\n' % why) sys.exit(1) else: f.write('[main]\n') @@ -179,10 +181,11 @@ class Lorax(object): self.conf.addAttr('yumconf') self.conf.set(yumconf=yumconf) - self.yum = rpmutil.Yum(yumconf=self.conf.yumconf, installroot=self.conf.treedir) + # create the Yum object + self.yum = Yum(yumconf=self.conf.yumconf, installroot=self.conf.treedir) - # remove not needed options - self.conf.delAttr(['repo', 'extrarepos', 'mirrorlist']) + # remove not needed attributes + self.conf.delAttr(['repo', 'extrarepos', 'mirrorlist', 'cachedir']) def setBuildArch(self): unamearch = os.uname()[4] @@ -190,10 +193,11 @@ class Lorax(object): self.conf.addAttr('buildarch') self.conf.set(buildarch=unamearch) - anaconda = self.yum.find('anaconda') + installed, available = self.yum.find('anaconda') try: - self.conf.set(buildarch=anaconda[0].arch) + self.conf.set(buildarch=available[0].arch) except: + # FIXME specify what exceptions can we get here pass # set the libdir @@ -206,7 +210,7 @@ class Lorax(object): def writeTreeInfo(self, discnum=1, totaldiscs=1, packagedir=''): outfile = os.path.join(self.conf.outdir, '.treeinfo') - # don't print anything instead of None if variant is not specified + # don't print anything instead of None, if variant is not specified variant = '' if self.conf.variant: variant = self.conf.variant @@ -232,7 +236,8 @@ class Lorax(object): c.set(section, 'kernel', 'images/pxeboot/vmlinuz') c.set(section, 'initrd', 'images/pxeboot/initrd.img') - # XXX actually create the boot iso somewhere, and set up this attribute + # XXX actually create the boot iso somewhere before calling writeTreeInfo(), + # and set up this attribute properly self.conf.addAttr('bootiso') if self.conf.bootiso: @@ -263,7 +268,7 @@ class Lorax(object): return True def prepareInstRoot(self): - # XXX why do we need this? + # XXX do we need this? os.symlink(os.path.join(os.path.sep, 'tmp'), os.path.join(self.conf.treedir, 'var', 'lib', 'xkb')) @@ -271,19 +276,11 @@ class Lorax(object): i = images.Images(self.conf, self.yum) i.run() - # XXX figure out where to put this - #def copyUpdates(self): - # if self.conf.updates and os.path.isdir(self.conf.updates): - # cp(os.path.join(self.conf.updates, '*'), self.conf.treedir) - # self.conf.delAttr('updates') - def cleanUp(self, trash=[]): for item in trash: - if os.path.isdir(item): - shutil.rmtree(item, ignore_errors=True) - else: - os.unlink(item) + if os.path.exists(item): + rm(item) # remove the whole lorax tempdir if os.path.isdir(self.conf.tempdir): - shutil.rmtree(self.conf.tempdir, ignore_errors=True) + rm(self.conf.tempdir) diff --git a/src/pylorax/actions/__init__.py b/src/pylorax/actions/__init__.py index 8db8d439..9f676439 100644 --- a/src/pylorax/actions/__init__.py +++ b/src/pylorax/actions/__init__.py @@ -1,14 +1,10 @@ # pylorax/actions/__init__.py -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger('pylorax.actions') - import sys import os -def getActions(): +def getActions(verbose=False): actions = {} root, actions_dir = os.path.split(os.path.dirname(__file__)) @@ -21,17 +17,20 @@ def getActions(): modules.add(os.path.join(actions_dir, basename).replace('/', '.')) for module in modules: - logger.debug('loading actions from module %s' % module) + if verbose: + print("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') + if verbose: + print("No actions found") continue else: for command, classname in commands.items(): - logger.debug('loaded: %s' % classname) + if verbose: + print("Loaded: %s" % classname) actions[command] = getattr(imported, classname) sys.path.pop(0) diff --git a/src/pylorax/actions/base.py b/src/pylorax/actions/base.py index b2e487cf..6ad49dd7 100644 --- a/src/pylorax/actions/base.py +++ b/src/pylorax/actions/base.py @@ -65,14 +65,13 @@ class LoraxAction(object): """Returns a pattern that needs to be installed, prior to calling the execute method.""" return None + @property def getDeps(self): # FIXME hmmm, how can i do this more generic? return None -# +-----------------+ -# | builtin actions | -# +-----------------+ +##### builtin actions class Copy(LoraxAction): @@ -85,10 +84,6 @@ class Copy(LoraxAction): self._attrs['mode'] = kwargs.get('mode') def execute(self, verbose=False): - dst_dir = os.path.dirname(self.dst) - if not os.path.isdir(dst_dir): - os.makedirs(dst_dir) - cp(src=self.src, dst=self.dst, mode=self.mode, verbose=verbose) self._attrs['success'] = True @@ -129,7 +124,7 @@ class Link(LoraxAction): self._attrs['target'] = kwargs.get('target') def execute(self, verbose=False): - os.symlink(self.name, self.target) + os.symlink(self.target, self.name) self._attrs['success'] = True @property @@ -230,7 +225,10 @@ class MakeDir(LoraxAction): def execute(self, verbose=False): if not os.path.isdir(self.dir): - os.makedirs(path=self.dir, mode=self.mode) + if self.mode: + os.makedirs(self.dir, mode=int(self.mode)) + else: + os.makedirs(self.dir) self._attrs['success'] = True @property diff --git a/src/pylorax/base.py b/src/pylorax/base.py deleted file mode 100644 index 56c40f61..00000000 --- a/src/pylorax/base.py +++ /dev/null @@ -1,21 +0,0 @@ -# pylorax/base.py - -import commands - - -def seq(arg): - if type(arg) not in (type([]), type(())): - return [arg] - else: - return arg - - -def getConsoleSize(): - err, output = commands.getstatusoutput('stty size') - if not err: - height, width = output.split() - else: - # set defaults - height, width = 24, 80 - - return int(height), int(width) diff --git a/src/pylorax/config.py b/src/pylorax/config.py index 7a28c93e..3bd9064b 100644 --- a/src/pylorax/config.py +++ b/src/pylorax/config.py @@ -2,10 +2,9 @@ import sys import re +from errors import TemplateError -from base import seq - -from exceptions import TemplateError +from utils.rpmutils import seq class Container(object): @@ -24,16 +23,17 @@ class Container(object): def __getitem__(self, attr): self.__checkInternal(attr) + if attr not in self.__dict__: raise AttributeError, "'Container' object has no attribute '%s'" % attr return self.__dict__[attr] def __setattr__(self, attr, value): - raise AttributeError, 'you cannot do that, use addAttr() and set() instead' + raise AttributeError, "you can't do that, use addAttr() and/or set()" def __delattr__(self, attr): - raise AttributeError, 'you cannot do that, use delAttr() instead' + raise AttributeError, "you can't do that, use delAttr()" def addAttr(self, attrs): for attr in filter(lambda attr: attr not in self.__dict__, seq(attrs)): @@ -70,62 +70,54 @@ class Container(object): def __checkInternal(self, attr): if attr.startswith('__'): - raise AttributeError, 'do not mess with internal stuff' + raise AttributeError, "do not mess with internal objects" class Template(object): def __init__(self): self._actions = [] - def parse(self, filename, supported_actions, vars): + def parse(self, filename, supported_actions, variables): try: f = open(filename, 'r') - except IOError: - sys.stdout.write('ERROR: Unable to open the template file\n') + except IOError as why: + sys.stderr.write("ERROR: Unable to open template file '%s': %s\n" % (filename, why)) return False else: lines = f.readlines() f.close() - # check vars + # check template variables 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) + for var in filter(lambda var: var not in variables, re.findall(r'@(.*?)@', line)): + raise TemplateError, "unknown variable '%s' on line %d" % (var, lineno) - active_action = '' - in_action = False + # parse the template for lineno, line in enumerate(lines, start=1): line = line.strip() - if not line or line.startswith('#'): + line, sep, comment = line.partition('#') + if not line: continue - for var, value in vars.items(): - line = re.sub(r'(.*)%s(.*)' % var, '\g<1>%s\g<2>' % value, line) + # expand variables + for var, value in variables.items(): + line = re.sub(r'@%s@' % var, value, line) - if in_action and not line.startswith(':'): - # create the action object - regex = supported_actions[active_action].REGEX - m = re.match(regex, line) - if m: - new_action = supported_actions[active_action](**m.groupdict()) - self._actions.append(new_action) - else: - # didn't match the regex - raise TemplateError, 'invalid action format "%s" on line %d' % (line, lineno) + # get the command + command, line = line.split(None, 1) + if command not in supported_actions: + raise TemplateError, "unknown command '%s' on line %d" % (command, lineno) - if in_action and line.startswith(':'): - in_action = False - - if not in_action and line.startswith(':'): - active_action = line[1:] - - if active_action not in supported_actions: - raise TemplateError, 'unknown action "%s" on line %d' % (active_action, lineno) - else: - in_action = True - continue + # create the action object + regex = supported_actions[command].REGEX + m = re.match(regex, line) + if m: + new_action = supported_actions[command](**m.groupdict()) + self._actions.append(new_action) + else: + # didn't match the regex + raise TemplateError, "invalid command format '%s' on line %d" % (line, lineno) return True diff --git a/src/pylorax/exceptions.py b/src/pylorax/errors.py similarity index 77% rename from src/pylorax/exceptions.py rename to src/pylorax/errors.py index ae402ed2..7b036512 100644 --- a/src/pylorax/exceptions.py +++ b/src/pylorax/errors.py @@ -1,4 +1,4 @@ -# pylorax/exceptions.py +# pylorax/errors.py class LoraxError(Exception): pass diff --git a/src/pylorax/images.py b/src/pylorax/images.py index cd2b0406..ac2eca2e 100644 --- a/src/pylorax/images.py +++ b/src/pylorax/images.py @@ -2,10 +2,86 @@ import sys import os +import commands +import re -from utils.fileutil import cp, mv, rm, touch, replace +import actions +import actions.base +from config import Template -import initrd +from utils.fileutils import cp, mv, rm, touch, edit, replace +from utils.ldd import LDD + + +class InitRD(object): + def __init__(self, config, yum): + self.conf = config + self.yum = yum + + # get supported actions + supported_actions = actions.getActions() + + initrd_templates = [] + initrd_templates.append(os.path.join(self.conf.confdir, 'templates', 'initrd')) + initrd_templates.append(os.path.join(self.conf.confdir, 'templates', self.conf.buildarch, + 'initrd')) + + vars = { 'instroot': self.conf.treedir, + 'initrd': self.conf.initrddir, + 'libdir': self.conf.libdir, + 'buildarch': self.conf.buildarch, + 'confdir' : self.conf.confdir, + 'datadir': self.conf.datadir } + + self.template = Template() + for filename in initrd_templates: + if os.path.isfile(filename): + self.template.parse(filename, supported_actions, vars) + + self._actions = [] + + def getPackages(self): + packages = [] + for action in filter(lambda action: action.install, self.template.actions): + m = re.match(r'%s(.*)' % self.conf.treedir, action.install) + if m: + packages.append(m.group(1)) + + return packages + + def getDeps(self): + ldd = LDD(libroot=os.path.join(self.conf.treedir, self.conf.libdir)) + for action in filter(lambda action: hasattr(action, 'getDeps'), self.template.actions): + ldd.getDeps(action.getDeps) + + # resolve symlinks + ldd.getLinks() + + # add dependencies to actions + for dep in ldd.deps: + kwargs = {} + kwargs['src'] = dep + kwargs['dst'] = re.sub(r'%s(?P.*)' % self.conf.treedir, + '%s\g' % self.conf.initrddir, + dep) + + new_action = actions.base.Copy(**kwargs) + self._actions.append(new_action) + + def processActions(self): + # create the initrd temporary directory if it does not exist + if not os.path.isdir(self.conf.initrddir): + os.makedirs(self.conf.initrddir) + + for action in self.template.actions + self._actions: + action.execute() + + def create(self, dst): + err, output = commands.getstatusoutput('find %s | cpio --quiet -c -o | gzip -9 > %s' % + (self.conf.initrddir, dst)) + + def cleanUp(self): + rm(self.conf.initrddir) class Images(object): @@ -13,6 +89,8 @@ class Images(object): self.conf = config self.yum = yum + self.initrd = InitRD(self.conf, self.yum) + # XXX don't see this used anywhere... maybe in some other script, have to check... #syslinux = os.path.join(self.conf.treedir, 'usr', 'lib', 'syslinux', 'syslinux-nomtools') #if not os.path.isfile(syslinux): @@ -23,45 +101,95 @@ class Images(object): # sys.exit(1) def run(self): - self.prepareBootTree() + bold = ('\033[1m', '\033[0m') - def prepareBootTree(self): - i = initrd.InitRD(self.conf, self.yum) - pkgs = i.getPkgs() + print('%sInstalling needed packages%s' % bold) + self.installPackages() - # install needed packages + print('%sCopying updates%s' % bold) + self.copyUpdates() + + print('%sInitializing output directory%s' % bold) + self.initOutputDirs() + + print('%sPopulating the isolinux directory%s' % bold) + self.populateIsolinuxDir() + + # XXX a lot of other stuff needs to be done here + pass + + print('%sDONE%s' % bold) + + def installPackages(self): + # required packages self.yum.addPackages(['anaconda', 'anaconda-runtime', 'kernel', 'syslinux']) - self.yum.addPackages(pkgs) + + # optional packages from confdir + packages_files = [] + packages_files.append(os.path.join(self.conf.confdir, 'packages', 'packages')) + packages_files.append(os.path.join(self.conf.confdir, 'packages', self.conf.buildarch, + 'packages')) + + packages = set() + for pfile in packages_files: + if os.path.isfile(pfile): + f = open(pfile, 'r') + for line in f.readlines(): + line = line.strip() + + if not line or line.startswith('#'): + continue + + if line.startswith('-'): + packages.discard(line[1:]) + else: + packages.add(line) + + f.close() + + self.yum.addPackages(list(packages)) + + # packages required for initrd image + packages = self.initrd.getPackages() + self.yum.addPackages(packages) + + # install all packages self.yum.install() + def copyUpdates(self): + if self.conf.updates and os.path.isdir(self.conf.updates): + cp(os.path.join(self.conf.updates, '*'), self.conf.treedir) + self.conf.delAttr('updates') + + def initOutputDirs(self): # create the destination directories self.imgdir = os.path.join(self.conf.outdir, 'images') if os.path.exists(self.imgdir): rm(self.imgdir) - self.pxedir = os.path.join(self.imgdir, 'pxeboot') os.makedirs(self.imgdir) + + self.pxedir = os.path.join(self.imgdir, 'pxeboot') os.makedirs(self.pxedir) # write the images/README - f = open(os.path.join(self.imgdir, 'README'), 'w') - f.write('This directory contains image files that can be used to create media\n' - 'capable of starting the %s installation process.\n\n' % self.conf.product) - f.write('The boot.iso file is an ISO 9660 image of a bootable CD-ROM. It is useful\n' - 'in cases where the CD-ROM installation method is not desired, but the\n' - 'CD-ROM\'s boot speed would be an advantage.\n\n') - f.write('To use this image file, burn the file onto CD-R (or CD-RW) media as you\n' - 'normally would.\n') - f.close() + src = os.path.join(self.conf.datadir, 'images', 'README') + dst = os.path.join(self.imgdir, 'README') + cp(src, dst) + replace(dst, r'@PRODUCT@', self.conf.product) # write the images/pxeboot/README - f = open(os.path.join(self.pxedir, 'README'), 'w') - f.write('The files in this directory are useful for booting a machine via PXE.\n\n') - f.write('The following files are available:\n') - f.write('vmlinuz - the kernel used for the installer\n') - f.write('initrd.img - an initrd with support for all install methods and\n') - f.write(' drivers supported for installation of %s\n' % self.conf.product) - f.close() + src = os.path.join(self.conf.datadir, 'images', 'pxeboot', 'README') + dst = os.path.join(self.pxedir, 'README') + cp(src, dst) + replace(dst, r'@PRODUCT@', self.conf.product) + # create the isolinux directory + self.isodir = os.path.join(self.conf.outdir, 'isolinux') + if os.path.exists(self.isodir): + rm(self.isodir) + os.makedirs(self.isodir) + + def populateIsolinuxDir(self): # set up some dir variables for further use anacondadir = os.path.join(self.conf.treedir, 'usr', 'lib', 'anaconda-runtime') bootdiskdir = os.path.join(anacondadir, 'boot') @@ -69,13 +197,7 @@ class Images(object): isolinuxbin = os.path.join(syslinuxdir, 'isolinux.bin') if os.path.isfile(isolinuxbin): - print('Creating the isolinux directory...') - self.isodir = os.path.join(self.conf.outdir, 'isolinux') - if os.path.exists(self.isodir): - rm(self.isodir) - os.makedirs(self.isodir) - - # copy the isolinux.bin to isolinux dir + # copy the isolinux.bin cp(isolinuxbin, self.isodir) # copy the syslinux.cfg to isolinux/isolinux.cfg @@ -86,20 +208,22 @@ class Images(object): replace(isolinuxcfg, r'@PRODUCT@', self.conf.product) replace(isolinuxcfg, r'@VERSION@', self.conf.version) - # copy the grub.conf to isolinux dir + # copy the grub.conf cp(os.path.join(bootdiskdir, 'grub.conf'), self.isodir) - # create the initrd in isolinux dir - i.getDeps() - i.processActions() - i.create(os.path.join(self.isodir, 'initrd.img')) - i.cleanUp() + # XXX do we want this here? + # create the initrd.img + print('Creating the initrd.img') + self.initrd.getDeps() + self.initrd.processActions() + self.initrd.create(os.path.join(self.isodir, 'initrd.img')) + #self.initrd.cleanUp() - # copy the vmlinuz to isolinux dir + # copy the vmlinuz vmlinuz = os.path.join(self.conf.treedir, 'boot', 'vmlinuz-*') cp(vmlinuz, os.path.join(self.isodir, 'vmlinuz')) - # copy the splash files to isolinux dir + # copy the splash files vesasplash = os.path.join(anacondadir, 'syslinux-vesa-splash.jpg') if os.path.isfile(vesasplash): cp(vesasplash, os.path.join(self.isodir, 'splash.jpg')) @@ -111,29 +235,27 @@ class Images(object): splashtools = os.path.join(anacondadir, 'splashtools.sh') splashlss = os.path.join(bootdiskdir, 'splash.lss') if os.path.isfile(splashtools): - os.system('%s %s %s' % (splashtools, - os.path.join(bootdiskdir, 'syslinux-splash.jpg'), - splashlss)) + cmd = '%s %s %s' % (splashtools, + os.path.join(bootdiskdir, 'syslinux-splash.jpg'), + splashlss) + os.system(cmd) if os.path.isfile(splashlss): cp(splashlss, self.isodir) - # copy the .msg files to isolinux dir + # copy the .msg files for file in os.listdir(bootdiskdir): if file.endswith('.msg'): cp(os.path.join(bootdiskdir, file), self.isodir) replace(os.path.join(self.isodir, file), r'@VERSION@', self.conf.version) - # if present, copy the memtest to isolinux dir - # XXX search for it in bootdiskdir or treedir/install/boot ? - #cp(os.path.join(bootdiskdir, 'memtest*'), os.path.join(self.isodir, 'memtest')) + # if present, copy the memtest cp(os.path.join(self.conf.treedir, 'boot', 'memtest*'), - os.path.join(self.isodir, 'memtest')) + os.path.join(self.isodir, 'memtest')) if os.path.isfile(os.path.join(self.isodir, 'memtest')): - f = open(isolinuxcfg, 'a') - f.write('label memtest86\n') - f.write(' menu label ^Memory test\n') - f.write(' kernel memtest\n') - f.write(' append -\n') - f.close() + text = "label memtest86\n" + text = text + " menu label ^Memory test\n" + text = text + " kernel memtest\n" + text = text + " append -\n" + edit(isolinuxcfg, text, append=True) else: - print('No isolinux binary found, skipping isolinux creation') + sys.stderr.write('No isolinux binary found, skipping isolinux creation\n') diff --git a/src/pylorax/initrd.py b/src/pylorax/initrd.py deleted file mode 100644 index 25a2db7f..00000000 --- a/src/pylorax/initrd.py +++ /dev/null @@ -1,84 +0,0 @@ -# pylorax/initrd.py - -import os -import re - -import actions -from config import Template -from utils.libutil import LDD -from utils.fileutil import rm - - -class InitRD(object): - def __init__(self, config, yum): - self.conf = config - self.yum = yum - - if not os.path.isdir(self.conf.initrddir): - os.makedirs(self.conf.initrddir) - - # get supported actions - supported_actions = actions.getActions() - - initrd_templates = [] - initrd_templates.append(os.path.join(self.conf.confdir, 'templates', 'initrd')) - initrd_templates.append(os.path.join(self.conf.confdir, 'templates', self.conf.buildarch, - 'initrd')) - - vars = { '@instroot@': self.conf.treedir, - '@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): - self.template.parse(file, supported_actions, vars) - - self.actions = [] - - def getPkgs(self): - # get needed packages - pkgs = [] - for action in filter(lambda action: hasattr(action, 'install'), self.template.actions): - m = re.match(r'%s(.*)' % self.conf.treedir, action.install) - if m: - pkgs.append(m.group(1)) - - return pkgs - - def getDeps(self): - # get needed dependencies - ldd = LDD(libroot=os.path.join(self.conf.treedir, self.conf.libdir)) - for action in filter(lambda action: hasattr(action, 'getDeps'), self.template.actions): - file = action.getDeps() - ldd.getDeps(file) - - # resolve symlinks - ldd.getLinks() - - # add dependencies to actions - for dep in ldd.deps: - kwargs = {} - kwargs['src'] = dep - kwargs['dst'] = re.sub(r'%s(?P.*)' % self.conf.treedir, - '%s\g' % self.conf.initrddir, - dep) - - new_action = actions.fileactions.Copy(**kwargs) - self.actions.append(new_action) - - def processActions(self): - for action in self.template.actions: - action.execute() - - for action in self.actions: - action.execute() - - def create(self, dst): - os.system('find %s | cpio --quiet -c -o | gzip -9 > %s' % (self.conf.initrddir, dst)) - - def cleanUp(self): - rm(self.conf.initrddir) diff --git a/src/pylorax/utils/libutil.py b/src/pylorax/utils/ldd.py similarity index 100% rename from src/pylorax/utils/libutil.py rename to src/pylorax/utils/ldd.py diff --git a/src/pylorax/utils/rpmutils.py b/src/pylorax/utils/rpmutils.py index 8de29814..ad2bed66 100644 --- a/src/pylorax/utils/rpmutils.py +++ b/src/pylorax/utils/rpmutils.py @@ -1,4 +1,4 @@ -# pylorax/utils/rpmutil.py +# pylorax/utils/rpmutils.py import sys import os @@ -6,6 +6,7 @@ import stat import yum import urlgrabber import shutil +import commands import yum.callbacks import yum.rpmtrans @@ -13,7 +14,23 @@ import yum.rpmtrans from rpmUtils.miscutils import rpm2cpio from cpioarchive import CpioArchive -from pylorax.base import seq, getConsoleSize + +def seq(arg): + if type(arg) not in (type([]), type(())): + return [arg] + else: + return arg + + +def getConsoleSize(): + err, output = commands.getstatusoutput('stty size') + if not err: + height, width = output.split() + else: + # set defaults + height, width = 24, 80 + + return int(height), int(width) class Callback(yum.rpmtrans.SimpleCliCallBack): @@ -74,7 +91,7 @@ class Yum(object): def download(self, packages): for package in seq(packages): - print('Downloading package %s...' % package) + print('Downloading package %s' % package) fn = urlgrabber.urlgrab(package.remote_url) shutil.copy(fn, self.installroot) @@ -82,7 +99,7 @@ class Yum(object): def addPackages(self, patterns): for pattern in seq(patterns): - print('Adding package matching %s...' % pattern) + print('Adding package matching %s' % pattern) try: self.yb.install(name=pattern) except yum.Errors.InstallError: @@ -103,7 +120,7 @@ class Yum(object): self.yb.close() -def extract_rpm(rpmfile, destdir): +def extractRPM(rpmfile, destdir): if not os.path.isdir(destdir): os.makedirs(destdir) @@ -125,7 +142,7 @@ def extract_rpm(rpmfile, destdir): if not os.path.isdir(path): os.makedirs(path) else: - print('Extracting %s...' % entry.name) + print('Extracting %s' % entry.name) dir = os.path.dirname(path) if not os.path.isdir(dir): os.makedirs(dir)