#!/usr/bin/env python # $Id: gen-updatesimg 1011 2008-03-25 18:42:36Z mfrisch $ # # Copyright (C) 2007 Platform Computing Inc # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # # # This script will create the updates.img for the Kusu Boot Media for RHEL # import re import os import subprocess import sys import tempfile import shutil import string import commands # This is the list of components found in src/kits/base/packages that are # required to build the kusu installer runtime KUSU_RUNTIME = ['kusu-autoinstall', 'kusu-boot', 'kusu-buildkit', 'python-cheetah', 'kusu-core', 'kusu-createrepo', 'kusu-driverpatch', 'kusu-hardware', 'kusu-installer', 'python-IPy', 'kusu-kitops', 'kusu-md5crypt', 'kusu-networktool', 'primitive', 'kusu-path', 'python-sqlite2', 'kusu-repoman', 'python-sqlalchemy', 'thttpd', 'kusu-ui', 'kusu-util'] SLES_PKGS = ['libnewt0_52', 'newt', 'python-newt', 'python', 'slang', 'python-xml', 'grub', 'mkinitrd', 'coreutils', 'parted', 'pyparted', 'rpm', 'rpm-python', 'yast2-python-bindings', 'sqlite', 'python-elementtree', 'libreiserfs', 'inst-source-utils', 'perl', 'insserv' ] CURRENT_PKGS = [] def getKusuTrunk(): """Returns the SVN trunk for Kusu/Emerald""" script = sys.argv[0] bindir = os.path.dirname(script) trunk = os.path.normpath(bindir + '/..') return trunk def getRPM(arg, dirname, names): """Returns the binary RPM""" li = [] if '.svn' in names: names.remove('.svn') for pkg in KUSU_RUNTIME: li.extend([name for name in names if name.startswith(pkg) and name.endswith('.rpm')]) if li: for l in li: CURRENT_PKGS.append(os.path.abspath(os.path.join(dirname,l))) def getAnacondaScript(distro, version, arch=None): """Returns the 'faux' anaconda script from the SVN sources.""" if not arch: archP = subprocess.Popen("arch",shell=True,stdout=subprocess.PIPE) arch = archP.stdout.read().strip(os.linesep) # collapse all ix86 to i386. if re.compile('i[3456]86').match(arch): arch = 'i386' trunk = getKusuTrunk() distspath = 'src/dists/%(distro)s/%(version)s/%(arch)s' % {'distro':distro, 'version':version, 'arch':arch} anacondaScript = os.path.abspath(os.path.join(trunk, os.path.join(distspath,'updates.img/anaconda'))) return anacondaScript def getThttpdRunScript(distro, version, arch=None): """Returns the thttpd-run.sh script from the SVN sources.""" if not arch: archP = subprocess.Popen("arch",shell=True,stdout=subprocess.PIPE) arch = archP.stdout.read().strip(os.linesep) # collapse all ix86 to i386. if re.compile('i[3456]86').match(arch): arch = 'i386' trunk = getKusuTrunk() distspath = 'src/dists/%(distro)s/%(version)s/%(arch)s' % {'distro':distro, 'version':version, 'arch':arch} thttpdRunScript = os.path.abspath(os.path.join(trunk, os.path.join(distspath,'updates.img/thttpd-run.sh'))) return thttpdRunScript def getYastScript(distro, version, arch=None): """Returns the 'faux' anaconda script from the SVN sources.""" if not (distro == 'sles' or distro == 'opensuse'): return None if not arch: archP = subprocess.Popen("arch",shell=True,stdout=subprocess.PIPE) arch = archP.stdout.read().strip(os.linesep) # collapse all ix86 to i386. if re.compile('i[3456]86').match(arch): arch = 'i386' trunk = getKusuTrunk() distspath = 'src/dists/%(distro)s/%(version)s/%(arch)s' % {'distro':distro, 'version':version, 'arch':arch} yastScript = os.path.abspath(os.path.join(trunk, os.path.join(distspath,'updates.img/yast'))) return yastScript def getRealYastScript(distro, version, arch=None): """Returns the 'faux' anaconda script from the SVN sources.""" if not (distro == 'sles' or distro == 'opensuse'): return None if not arch: archP = subprocess.Popen("arch",shell=True,stdout=subprocess.PIPE) arch = archP.stdout.read().strip(os.linesep) # collapse all ix86 to i386. if re.compile('i[3456]86').match(arch): arch = 'i386' trunk = getKusuTrunk() distspath = 'src/dists/%(distro)s/%(version)s/%(arch)s' % {'distro':distro, 'version':version, 'arch':arch} yastScript = os.path.abspath(os.path.join(trunk, os.path.join(distspath,'updates.img/yast.real'))) return yastScript def getRebootScript(distro, version, arch=None): """Returns the 'faux' anaconda script from the SVN sources.""" if not (distro == 'sles' or distro == 'opensuse'): return None if not arch: archP = subprocess.Popen("arch",shell=True,stdout=subprocess.PIPE) arch = archP.stdout.read().strip(os.linesep) # collapse all ix86 to i386. if re.compile('i[3456]86').match(arch): arch = 'i386' trunk = getKusuTrunk() distspath = 'src/dists/%(distro)s/%(version)s/%(arch)s' % {'distro':distro, 'version':version, 'arch':arch} rebootScript = os.path.abspath(os.path.join(trunk, os.path.join(distspath,'updates.img'))) if not os.path.exists(os.path.join(rebootScript,'reboot')): # Custom reboot cp = subprocess.Popen(['make'], cwd=rebootScript, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = cp.communicate() if cp.returncode: raise IOError, stderr return os.path.join(rebootScript,'reboot') def generateUpdatesImg(updatesdir, updatesimgpath): """ Generate the updates.img file. updatesdir points to the directory containing the contents that should be included in the updates.img file. """ packExt2FS(updatesdir, updatesimgpath, 120000) def packExt2FS(dirname, rootimgpath, size=5000): """Packs the directory dir into an ext2fs rootimgpath. The size of the image can be passed as well. """ if not os.path.exists(dirname): raise OSError if os.getuid() <> 0: raise OSError, "Operation requires root access!" # nuke existing rootimgpath if existing if os.path.exists(rootimgpath): os.remove(rootimgpath) rootimgpath = os.path.abspath(rootimgpath) # create scratchdir scratchdir = tempfile.mkdtemp('emerald-') tmprootdir = tempfile.mkdtemp(dir=scratchdir) # create a block device, format it, mount it cwd = scratchdir ddP = subprocess.Popen("dd of=%s if=/dev/zero bs=1024 count=%s" % (rootimgpath,size),shell=True,cwd=scratchdir) ddP.communicate() mke2fsP = subprocess.Popen("mke2fs -F %s" % rootimgpath,shell=True,cwd=scratchdir) mke2fsP.communicate() mountP = subprocess.Popen("mount -o loop %s %s" % (rootimgpath,tmprootdir),shell=True,cwd=scratchdir) mountP.communicate() # copy the contents over to the newly made ext2fs filesystem cmd = '(find . | cpio -mpdu %s) >/dev/null 2>&1' cpioP = subprocess.Popen(cmd % tmprootdir,shell=True,cwd=dirname,stdout=subprocess.PIPE) cpioP.wait() # housecleaning umountP = subprocess.Popen("umount %s" % tmprootdir,shell=True,cwd=scratchdir) umountP.communicate() if os.path.exists(tmprootdir): shutil.rmtree(tmprootdir) if os.path.exists(scratchdir): shutil.rmtree(scratchdir) if __name__ == '__main__': archP = subprocess.Popen("arch",shell=True,stdout=subprocess.PIPE) arch = archP.stdout.read().strip(os.linesep) # collapse all ix86 to i386. if re.compile('i[3456]86').match(arch): arch = 'i386' # get the base kit RPMS directory #basekitRPMSdir = os.path.join(getKusuTrunk(),'src/kits/base/RPMS/noarch') #os.path.walk(basekitRPMSdir,getRPM,None) #basekitRPMSdir = os.path.join(getKusuTrunk(),'src/kits/base/RPMS/' + arch) #os.path.walk(basekitRPMSdir,getRPM,None) if os.path.exists('/etc/fedora-release'): dist = 'fedora' distP = subprocess.Popen("[[ `head -n 1 /etc/fedora-release` =~ '[0-9]+' ]]; echo $BASH_REMATCH",shell=True,stdout=subprocess.PIPE) distver = distP.stdout.read().strip(os.linesep) elif os.path.exists('/etc/redhat-release'): distP = subprocess.Popen("head -n 1 /etc/redhat-release | awk '{print $1}'",shell=True,stdout=subprocess.PIPE) dist = distP.stdout.read().strip(os.linesep) if re.compile('^Red').match(dist): dist = 'rhel' elif re.compile('^Scientific').match(dist): dist = 'scientificlinux' elif re.compile('^CentOS').match(dist): dist = 'centos' distP = subprocess.Popen("[[ `head -n 1 /etc/redhat-release` =~ '[0-9]+' ]]; echo $BASH_REMATCH",shell=True,stdout=subprocess.PIPE) distver = distP.stdout.read().strip(os.linesep) elif os.path.exists('/etc/SuSE-release'): distP = subprocess.Popen("head -n 1 /etc/SuSE-release | awk '{print $1}'",shell=True,stdout=subprocess.PIPE) dist = distP.stdout.read().strip(os.linesep) if re.compile('^SUSE').match(dist): dist = 'sles' elif re.compile('^openSUSE').match(dist): dist = 'opensuse' distP = subprocess.Popen("grep VERSION /etc/SuSE-release | awk -F' = ' '{print $2}' | sed 's/\.//'",shell=True,stdout=subprocess.PIPE) distver = distP.stdout.read().strip(os.linesep) if 'KUSU_BUILD_DIST' in os.environ.keys(): dist = os.environ['KUSU_BUILD_DIST'] if 'KUSU_BUILD_DISTVER' in os.environ.keys(): distver = os.environ['KUSU_BUILD_DISTVER'] print 'KUSU_DIST=%s' % dist print 'KUSU_DISTVER=%s' % distver if dist == 'sles' or dist == 'opensuse': KUSU_RUNTIME = KUSU_RUNTIME + SLES_PKGS # get the base kit RPMS directory basekitRPMSdir = os.path.join(getKusuTrunk(),'src/kits/base/RPMS/noarch') os.path.walk(basekitRPMSdir,getRPM,None) if arch == 'i386' and (dist == 'sles' or dist == 'opensuse'): basekitRPMSdir = os.path.join(getKusuTrunk(),'src/kits/base/RPMS/i586') os.path.walk(basekitRPMSdir,getRPM,None) basekitRPMSdir = os.path.join(getKusuTrunk(),'src/kits/base/RPMS/i686') os.path.walk(basekitRPMSdir,getRPM,None) else: basekitRPMSdir = os.path.join(getKusuTrunk(),'src/kits/base/RPMS/' + arch) os.path.walk(basekitRPMSdir,getRPM,None) if len(CURRENT_PKGS) <> len(KUSU_RUNTIME): #print 'There are missing packages from the Base kit. Please rebuild the Base kit!' tmplst = KUSU_RUNTIME[:] NEW_CURRENT_PKGS = [] for i in CURRENT_PKGS: n = string.join(string.split(os.path.basename(i), '-')[:-2], '-') if n in tmplst: NEW_CURRENT_PKGS.append(i) tmplst.remove(n) CURRENT_PKGS = NEW_CURRENT_PKGS[:] if len(tmplst) > 0: print 'There are missing packages from the Base kit. Please rebuild the Base kit!' print 'Missing packages:' for i in tmplst: print ' %s' % i sys.exit(-1) # create a temp holding directory tdir = tempfile.mkdtemp(prefix='emerald-') # get a handle to /dev/null for redirecting stderr err = open('/dev/null','w') for pkg in CURRENT_PKGS: rpm2cpioP = subprocess.Popen('rpm2cpio %s' % pkg, shell=True, cwd=tdir, stdout=subprocess.PIPE, stderr=err) cpioP = subprocess.Popen('cpio -id', shell=True, cwd=tdir,stdin=rpm2cpioP.stdout,stdout=subprocess.PIPE, stderr=err) cpioP.wait() # fix the missing __init__.py initpy = os.path.join(tdir,'opt/kusu/lib/python/kusu/__init__.py') f = open(initpy,'w') f.close() thttpdRunScript = getThttpdRunScript(dist, distver) if not os.path.exists(thttpdRunScript): print "Unable to get thttpd-run.sh script from SVN source tree! Please ensure the source tree is complete!" sys.exit(-1) # copy the thttpd-run.sh script shutil.copy(thttpdRunScript, tdir) os.chmod(os.path.join(tdir,'thttpd-run.sh'),0755) if dist == 'sles' or dist == 'opensuse': yastScript = getYastScript(dist, distver, arch) if not os.path.exists(yastScript): print "Unable to get 'faux' yast script from SVN source tree! Please ensure the source tree is complete!" sys.exit(-1) # copy the 'faux' yast script cp = subprocess.Popen(['cp', yastScript, os.path.abspath(os.path.join(tdir,'sbin/yast'))], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = cp.communicate() if cp.returncode: raise IOError, stderr os.chmod(os.path.join(tdir,'sbin/yast'),0755) #realYastScript = getRealYastScript(dist, distver, arch) realYastScript = os.path.abspath(os.path.join(os.path.dirname(yastScript),'yast.real')) if not os.path.exists(realYastScript): print "Unable to get 'real' yast script from SVN source tree! Please ensure the source tree is complete!" print "%s" % realYastScript sys.exit(-1) # copy the 'real' real script cp = subprocess.Popen(['cp', realYastScript, os.path.abspath(os.path.join(tdir,'sbin/yast.real'))], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = cp.communicate() if cp.returncode: raise IOError, stderr os.chmod(os.path.join(tdir,'sbin/yast.real'),0755) detectNetCardScript = os.path.abspath(os.path.join(os.path.dirname(yastScript),'detect_network_cards.py')) if not os.path.exists(detectNetCardScript): print "Unable to get detect_network_cards.py script from SVN source tree! Please ensure the source tree is complete!" print "%s" % detectNetCardScript sys.exit(-1) # copy the 'real' real script cp = subprocess.Popen(['cp', detectNetCardScript, os.path.abspath(os.path.join(tdir,'detect_network_cards.py'))], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = cp.communicate() if cp.returncode: raise IOError, stderr os.chmod(os.path.join(tdir,'detect_network_cards.py'),0755) rebootScript = getRebootScript(dist, distver, arch) if not os.path.exists(rebootScript): print "Unable to get reboot script from SVN source tree! Please ensure the source tree is complete!" sys.exit(-1) # copy the reboot script cp = subprocess.Popen(['cp', rebootScript, os.path.abspath(os.path.join(tdir,'sbin/reboot'))], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = cp.communicate() if cp.returncode: raise IOError, stderr # remove parted and partprobe. os.remove(os.path.abspath(os.path.join(tdir, 'usr/sbin/parted'))) os.remove(os.path.abspath(os.path.join(tdir, 'usr/sbin/partprobe'))) else: anacondaScript = getAnacondaScript(dist, distver, arch) if not os.path.exists(anacondaScript): print "Unable to get 'faux' anaconda script from SVN source tree! Please ensure the source tree is complete!" sys.exit(-1) # copy the 'faux' anaconda script shutil.copy(anacondaScript, tdir) os.chmod(os.path.join(tdir,'anaconda'),0755) generateUpdatesImg(tdir,'./updates.img') # remove temp holding directory shutil.rmtree(tdir,ignore_errors=True) err.close() sys.exit(0)