# $Id: base.py,v 1.45 2006/09/11 22:46:56 mjk Exp $ # # Manipulate RedHat installer to include Rocks steps. # # @Copyright@ # # Rocks # www.rocksclusters.org # version 4.2.1 (Cydonia) # # Copyright (c) 2006 The Regents of the University of California. All # rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice unmodified and in its entirety, this list of conditions and the # following disclaimer in the documentation and/or other materials provided # with the distribution. # # 3. All advertising and press materials, printed or electronic, mentioning # features or use of this software must display the following acknowledgement: # # "This product includes software developed by the Rocks # Cluster Group at the San Diego Supercomputer Center at the # University of California, San Diego and its contributors." # # 4. Neither the name or logo of this software nor the names of its # authors may be used to endorse or promote products derived from this # software without specific prior written permission. The name of the # software includes the following terms, and any derivatives thereof: # "Rocks", "Rocks Clusters", and "Avalanche Installer". # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # @Copyright@ # # $Log: base.py,v $ # Revision 1.45 2006/09/11 22:46:56 mjk # monkey face copyright # # Revision 1.44 2006/09/08 18:28:02 bruno # fixes: # # - eject the CD 'early' # - handle RHEL/CentOS/SL os disks # # Revision 1.43 2006/08/10 00:09:24 mjk # 4.2 copyright # # Revision 1.42 2006/07/27 00:36:38 bruno # add a new info message that tells the user we are running the rocks # post installation scripts # # give grub-install 60 seconds to run. it actually takes almost 10 seconds on a # dell optiplex gx620, so a 10 second timeout was far to low. # # Revision 1.41 2006/07/26 21:39:29 bruno # capture output from grub-install to help debug grub issues # # Revision 1.40 2006/07/22 02:01:07 bruno # fixupGrup needs to run -- after taking it out, an older sun java workstation # hung at the 'grup loading stage 2' prompt. # # but, users have reported cases where 'grub-install' hangs. this fix gives # grub-install 10 seconds to do its thing -- if it doesn't complete in that # time, we kill it. # # Revision 1.39 2006/07/19 03:00:52 bruno # running grub-install causes some nodes to hang and not reboot after the # installation. let's experiment with removing it. # # Revision 1.38 2006/07/12 22:17:17 bruno # take steps to make sure the rpm database is cleared before we install # packages. this addresses the issue we saw with the initscripts package and # how it wasn't creating all the appropriate links under /etc/rc.d/ # # Revision 1.37 2006/06/26 22:22:51 bruno # be smarter about when to eject the CD # # Revision 1.36 2006/06/22 22:13:26 bruno # cleanup of installclass files # # Revision 1.35 2006/06/12 21:43:13 bruno # can now get CD-based rolls # # Revision 1.34 2006/06/09 22:40:28 bruno # moved 'wget' definition to downloadRoll function # # Revision 1.33 2006/06/08 21:30:48 bruno # first attempt at CD roll install # # Revision 1.32 2006/06/05 17:57:32 bruno # first steps towards 4.2 beta # # Revision 1.31 2006/01/27 22:29:42 bruno # stable (mostly) after integration of new foundation and localization code # # Revision 1.30 2005/12/09 18:58:58 bruno # added --no-floppy flag to fix a problem where some nodes would hang in # grub-install and not reboot # # Revision 1.29 2005/11/30 19:31:32 bruno # software raid fixes # # Revision 1.28 2005/11/20 01:33:45 bruno # more partitioning fixes # # Revision 1.27 2005/10/12 18:08:28 mjk # final copyright for 4.1 # # Revision 1.26 2005/10/10 18:13:35 bruno # more software raid corner case handling # # Revision 1.25 2005/10/07 18:05:39 bruno # more software raid corner case fixes # # Revision 1.24 2005/10/06 18:04:41 bruno # for unknown nodes (i.e., non-rocks nodes), ensure that only the first # disk discovered is partitioned and formatted as rocks default root disk. # # Revision 1.23 2005/10/04 21:19:25 bruno # added 'manual' as a partition directive # # Revision 1.22 2005/09/30 20:52:27 bruno # support for only partitioning the first drive. # # this is the default for frontends. # # for all other nodes, the user can specify the flag # 'force-default-root-disk-only' in between the part XML tags. # # Revision 1.21 2005/09/28 22:46:27 bruno # bug fixes for frontend installs with mixed IDE and SCSI drives including # some of these drives being configured as software RAID(s). # # Revision 1.20 2005/09/19 22:08:35 bruno # partitioning tuning. # # - the frontend and NAS appliance will no longer nuke all partitions before # the partitioning screen # - a NAS appliance always displays the partitioning screen # - added a nas-exports node to make it easier for people to customize # their export directories on NAS appliances # - hardened the software raid partitioning code # # Revision 1.19 2005/09/16 01:02:08 mjk # updated copyright # # Revision 1.18 2005/09/02 00:05:47 bruno # pushing toward 4.1 beta # # Revision 1.17 2005/06/01 03:12:30 bruno # on frontend machines, only partition and format the root disk # # Revision 1.16 2005/05/24 21:21:46 mjk # update copyright, release is not any closer # # Revision 1.15 2005/05/23 23:59:21 fds # Frontend Restore # # Revision 1.14 2005/04/29 05:06:35 bruno # partitioning once again works on i386/x86_64 (instability was caused by # the ia64 checkins). # # Revision 1.13 2005/04/28 21:47:23 bruno # partitioning function updates in order to support itanium. # # itanics need 'parted' as 'sfdisk' only looks at block 0 on a disk and # itanics put their real partition info in block 1 (this is the GPT partitioning # scheme) # # Revision 1.12 2005/04/20 23:52:32 bruno # getRocksFrontendCerts() is not present on non-frontend installs # # Revision 1.11 2005/04/18 23:22:33 fds # Save important files before drives get nuked. # # Revision 1.10 2005/04/14 23:23:06 bruno # don't call grub-install for ia64. this fixes bug 109. # # Revision 1.9 2005/04/14 17:06:39 bruno # fixes for ia64 disk partitioning # # Revision 1.8 2005/04/14 00:23:52 fds # Keep it simple. Less throwing around keys. # # Revision 1.7 2005/04/12 01:50:39 fds # Incorporated new Install classes First and Last # # Revision 1.6 2005/03/31 22:39:25 fds # Secure (key-authenticated) WAN kickstart # # Revision 1.5 2005/03/18 00:14:08 fds # Moved Bruno's Partition classes to their own partition.py file. Added # support functions for USB keys. All nodes run a postAction() now. # # Revision 1.4 2005/03/15 23:54:51 bruno # force-default fixes # # Revision 1.3 2005/03/14 19:27:49 bruno # harden up the posting of partitions to the frontend's database # # Revision 1.2 2005/03/12 00:01:50 bruno # minor checkin # # Revision 1.1 2005/03/01 00:22:25 mjk # moved to base roll # # Revision 1.20 2005/02/14 21:55:01 bruno # support for phil's phartitioning phun # # Revision 1.19 2004/10/20 03:26:04 bruno # put the code back in that was removed in july. # # i'm sure the assumption was that the bootable() code in urls.c in loader made # this code redundant -- this is not the case. # # the bootable() code in urls.c only makes the disks bootable *up to* the # disk that holds the cert. # # for example, in a two disk system, if the first disk holds the cert, then # only the first disk is bootable and the second disk is unbootable. this # disparity confuses anaconda and completely hoses reinstallation of # compute nodes. # # Revision 1.18 2004/10/14 19:39:56 bruno # needed to remove old reference to RocksPartitioningComplete # # Revision 1.17 2004/10/14 00:44:55 bruno # remove ego-centric debug statement # # Revision 1.16 2004/10/04 21:17:27 bruno # force grub-install to run # # give the user the ability to 'force-default' partition on a compute node # # Revision 1.15 2004/08/25 03:17:14 bruno # now have a method to return a disk to a rocks default state. # # this addresses bug 2 # # Revision 1.14 2004/08/23 23:26:01 bruno # we now have user-settable timezones and an ntp server. # # this address bug 6. # # Revision 1.13 2004/07/28 23:17:46 fds # Revision 1.10 was not tested. # # Revision 1.12 2004/07/26 22:52:47 fds # Rocks is done. # # Revision 1.11 2004/07/16 20:13:59 bruno # the real fix for bug 11. # # now can handle the double quote. # # Revision 1.10 2004/07/16 11:41:01 bruno # added code to deal with XML escape characters as well as correctly # dealing with single and double quotes. this fixes bug 11. # # also, restructured some code to include a new method 'defaultPartitioning'. # this will enable the use of the 'part' tag with the value 'force-default' # in order to ignore the .rocks-release file and force the default partitioning # on a node. # # Revision 1.9 2004/05/14 21:17:05 bruno # new fix for preserving file systems over reinstalls # # now works for software raid # # Revision 1.8 2004/04/28 17:35:54 bruno # support for variabled-sized partitions root and swap partitions # # Revision 1.7 2004/04/07 23:43:59 bruno # you say import, i say imoort # # Revision 1.6 2004/04/07 23:32:26 bruno # make root 6 GB # # Revision 1.5 2004/03/25 03:15:42 bruno # touch 'em all! # # update version numbers to 3.2.0 and update copyrights # # Revision 1.4 2004/03/19 03:25:13 bruno # can now do software raid # # Revision 1.3 2004/03/16 22:10:33 mjk # fix profile paths for netstg2 # # Revision 1.2 2004/03/11 21:41:38 fds # Adopt mason's new rocks-dist capabilities. Allows a much cleaner WAN # kickstart. Also added the ability to choose the distro name on central. # # Revision 1.1 2004/03/08 21:01:22 mjk # *** empty log message *** # # Revision 1.39 2004/02/12 00:36:38 fds # WAN Kickstart # # Revision 1.38 2004/02/09 16:49:52 bruno # root partition default now 8 GB # # Revision 1.37 2004/02/06 02:32:48 fds # Much closer to WAN Kickstart. # # Revision 1.36 2004/02/03 02:00:21 fds # Closer. # # Revision 1.35 2004/01/30 20:46:06 fds # Indents and tweaks. Added correct rocks-dist copycd for wan rolls with new # --cdrom option. # # Revision 1.34 2004/01/29 21:29:23 fds # Moved RocksDoInstall classes to install.py # # Revision 1.33 2004/01/07 22:13:21 bruno # change default file system type to ext3 # # Revision 1.32 2003/12/18 18:47:44 fds # Fixed small typo which prevented correct x86_64 operation. # # Revision 1.31 2003/12/15 22:08:03 bruno # fixes # # Revision 1.30 2003/12/10 20:04:33 bruno # more partitioning # # Revision 1.28 2003/11/14 19:23:23 bruno # now can install metarolls # # Revision 1.27 2003/11/13 02:54:02 fds # Fixed opteron copycd problem. # # Revision 1.26 2003/11/12 02:33:30 bruno # for rolls, install a variants of a package (e.g., put an i586 package on # an i686 node) # # Revision 1.25 2003/11/04 00:23:49 bruno # no need to install packages from Rocks Base -- anaconda already did that # # Revision 1.24 2003/11/03 22:29:16 bruno # tweaks for enterprise 3 # # Revision 1.23 2003/10/29 22:08:48 fds # Removed step that was hanging up compute # node kickstarts. We need packages installed in this step and languagesupport too if # we want to have real language support. # # Revision 1.22 2003/10/21 16:31:01 bruno # needed to move 'import rpm' into doLocal # # Revision 1.21 2003/10/21 15:47:23 bruno # taroon tweaks # # Revision 1.20 2003/10/16 17:09:20 bruno # final change for taroon # # Revision 1.19 2003/10/15 22:19:47 bruno # fixes for taroon # # Revision 1.18 2003/09/24 17:08:45 fds # Bruno's changes for RH 9 # # Revision 1.17 2003/08/15 22:34:46 mjk # 3.0.0 copyright # # Revision 1.16 2003/08/05 02:17:43 bruno # re-added the check to see if roll packages should be installed # # Revision 1.15 2003/07/30 16:23:47 bruno # changes for ia64 # # Revision 1.14 2003/07/23 23:15:34 bruno # subtle fix # # Revision 1.13 2003/07/16 20:10:41 bruno # changed to new 'file' tag # # Revision 1.12 2003/07/15 18:53:21 bruno # support for installing meta packages during the roll installation # part of the install # # Revision 1.11 2003/07/12 00:52:31 bruno # only install rolls if a roll CD has been inserted # # Revision 1.10 2003/07/10 19:50:42 bruno # roll RPMS are now in RedHat/RPMS # # Revision 1.9 2003/07/10 15:36:38 bruno # had to add import rocks.file back in # # Revision 1.8 2003/07/09 23:48:25 bruno # touch up # # Revision 1.7 2003/07/07 23:19:02 bruno # added path to rocks.file # # Revision 1.6 2003/07/07 21:07:26 bruno # small fix on roll prompt verbage # # Revision 1.5 2003/07/07 20:27:15 bruno # roll enablers # # Revision 1.4 2003/05/22 16:36:35 mjk # copyright # # Revision 1.3 2003/04/28 19:54:13 mjk # more installclass fixes # # Revision 1.2 2003/04/25 18:15:09 bruno # first pass at adding all the new rocks config screens # # Revision 1.1 2003/04/24 16:59:41 mjk # - add order tags # - edge and order tags can have children # This just make the graph look nicer, no functional change # - added include directory # - moved install class code into include directory # - dependecies enforced via topological sort # - weight attributes are dead # - long live order tags # - the 'gen' attribute is currently ignored. This will be used to support # other graph ordering requirements (e.g. testing, cfengine, ...) # import sys import os import os.path import string import tempfile import isys import shutil from userauth_text import * from kickstart import * from text import * from product import * from dispatch import installSteps import crypt import md5 class RocksCustomKickstart(KickstartBase): rocksPartition = None def setSteps(self, dispatch): # # the text-based installer uses stepToClasses, and # the gui-based installer used stepToClass. # from gui import stepToClass log('ROCKS:RocksCustomKickstart:setSteps') KickstartBase.setSteps(self, dispatch) self.dispatch = dispatch # # skip the steps that are not relevant to a rocks install # dispatch.skipStep("welcome", permanent = 1) dispatch.skipStep("language", permanent = 1) dispatch.skipStep("keyboard", permanent = 1) dispatch.skipStep("bootloader", permanent = 1) dispatch.skipStep("firewall", permanent = 1) dispatch.skipStep("languagesupport", permanent = 1) dispatch.skipStep("timezone", skip = 1) dispatch.skipStep("package-selection", permanent = 1) dispatch.skipStep("checkdeps", permanent = 1) dispatch.skipStep("dependencies", permanent = 1) dispatch.skipStep("handleX11pkgs", permanent = 1) dispatch.skipStep("videocard", permanent = 1) dispatch.skipStep("monitor", permanent = 1) dispatch.skipStep("xcustom", permanent = 1) dispatch.skipStep("confirminstall", permanent = 1) dispatch.skipStep("selectlangpackages", permanent = 1) dispatch.skipStep("autopartition", permanent = 1) dispatch.skipStep("accounts", permanent = 1) dispatch.skipStep("partitionmethod", skip = 1) dispatch.skipStep("partitionmethodsetup", skip = 1) dispatch.skipStep("partition", skip = 1) dispatch.skipStep("network", permanent = 1) dispatch.skipStep("migratefilesystems", skip = 1) # # make sure the bootloader gets installed # self.id.bootloader.useGrubVal = 1 dispatch.skipStep("instbootloader", 0) # # set the partitioning steps # if self.id.partitions.useAutopartitioning == 1: dispatch.skipStep("autopartitionexecute", skip = 0) else: # # manual partitioning # dispatch.skipStep("partitionmethodsetup", skip = 0) dispatch.skipStep("partition", skip = 0) dispatch.skipStep("fdisk", skip = 0) # # set up the call that will update the frontend's database # with the partition info from this node # index = 0 for key in installSteps: if key[0] == "enablefilesystems": break index = index + 1 installSteps[index] = ("enablefilesystems", RocksTurnOnFilesystems, ("dir", "id.fsset", "id.diskset", "id.partitions", "id.upgrade", "instPath", "id.methodstr")) dispatch.skipStep("enablefilesystems", skip = 0) # # make sure we can get the comps file and hdlist files # correctly # dispatch.skipStep("readcomps", skip = 1) index = 0 for key in installSteps: if key[0] == "dopostaction": break index = index + 1 installSteps[index] = ("dopostaction", RocksDoPostAction, ("id", "instPath", "intf")) dispatch.skipStep("dopostaction", skip = 0) # # finish up # dispatch.skipStep("complete", permanent = 1) index = 0 for key in installSteps: if key[0] == "methodcomplete": break index = index + 1 installSteps[index] = ("methodcomplete", RocksMethodComplete, ("method", )) dispatch.skipStep("methodcomplete", skip = 0) # # call the rocks custom configuration windows # # redefine red hat's 'betanag' window to be the # rocks configuration window(s) # index = 0 for key in installSteps: if key[0] == "betanag": break index = index + 1 installSteps[index] = ("rockswindows", ("id.configFileData",)) # # the browser is called in the rockswindows step and the # purpose of running the browser is to generate a # /tmp/site.xml file. if the file already exists, then # there is no need to run the browser # if os.path.exists('/tmp/site.xml'): dispatch.skipStep("rockswindows", permanent = 1) else: dispatch.skipStep("rockswindows", skip = 0) stepToClass["rockswindows"] = ("ksclass", "RocksWelcomeWindowGUI") # # setup call our install classes # index = 0 for key in installSteps: if key[0] == "preinstallconfig": break index = index + 1 installSteps[index] = ("preinstallconfig", RocksPreInstall, ("method", "id", "intf", "instPath", "dir")) dispatch.skipStep("preinstallconfig", skip = 0) return def skipStep(self, stepToSkip, skip = 1, permanent = 0): self.dispatch.skipStep(stepToSkip, skip, permanent) # # write the site-specific XML file # def writeSiteHeader(self, file): file.write('\n') def writeSiteFooter(self, file): file.write('\n') def writeSiteVar(self, file, name, value): import xml.sax.saxutils # # need this because kpp yacks when it gets a null value # if not name: name = ' ' if not value: value = ' ' # # make sure the name and value are XML safe # xmlname = xml.sax.saxutils.escape(name) xmlvalue = xml.sax.saxutils.escape(value) # # make sure the quotes are escaped # xmlname = string.replace(xmlname, '"', '\\"') xmlvalue = string.replace(xmlvalue, '"', '\\"') xmlname = string.replace(xmlname, "'", "\\'") xmlvalue = string.replace(xmlvalue, "'", "\\'") xmlname = string.replace(xmlname, "`", "\\`") xmlvalue = string.replace(xmlvalue, "`", "\\`") file.write('\n' % (xmlname, xmlvalue)) # # regenerate the kickstart file and reparse the post section # def regenerateKickstart(self): f = open('/tmp/ks-rocks.debug', 'a') f.write('regenerateKickstart:enter\n') f.close() ksfile = '/tmp/ks-regen.cfg' cwd = os.getcwd() os.chdir('/export/profiles') f = open('/tmp/ks-rocks.debug', 'a') f.write('regenerateKickstart:cwd (%s)\n' % (os.getcwd())) f.close() os.system('unset PYTHONPATH ; ' \ + ' /opt/rocks/sbin/kpp root | ' \ + ' /opt/rocks/sbin/kgen --postonly > %s ' % (ksfile)) os.chdir(cwd) KickstartBase.readKickstart(self, self.id, ksfile) def initializeRocksPartition(self, id, nukeit=1): nodepartinfo = self.rocksPartition.getNodePartInfo() # # NOTE: dbpartinfo is initialized in the kickstart file by # kickstart.cgi # # # for each physical disk, try to match the partitions from # a disk to the rows in the database # i = nodepartinfo # print 'nodepartinfo %s' % (nodepartinfo) # print 'dbpartinfo %s' % (dbpartinfo) # # make sure to walk through the disks in the same order # as presented by the kernel # disks = [] for d in self.rocksPartition.getDisks(): if d in nodepartinfo.keys(): disks.append(d) log('disks:1:%s' % (disks)) for nodedisk in disks: # print 'nodedisk %s' % (nodedisk) # # if all the physical partitions for a drive match # those for a common device in the database # (e.g., 'hda'), then we've successfully matched a # physical drive to the rows in the database # for dbdisk in dbpartinfo.keys(): # print 'dbdisk %s' % (dbdisk) if self.rocksPartition.compareDiskInfo( dbpartinfo[dbdisk], nodepartinfo[nodedisk]): # # tell anaconda to save the existing # partitions and only reformat the # root partition (if present) # self.rocksPartition.addPartitions(id, nodepartinfo[nodedisk], format = 0) del i[nodedisk] del dbpartinfo[dbdisk] break; nodepartinfo = i # # software raid corner case # # there is a case where the node may not have a previously # installed software raid, but the database has a software # raid entry for this node (e.g., when a drive is replaced) # # in this case, add the software raid disk to the list of # disks # disks = [] for d in self.rocksPartition.getDisks(): if d in nodepartinfo.keys(): disks.append(d) if dbpartinfo.has_key('md'): found = 0 for d in nodepartinfo.keys(): if len(d) > 1 and d[0:2] == 'md': found = 1 break if not found: disks.append('md') log('disks:2:%s' % (disks)) for nodedisk in disks: # # if we're here, then at least one physical disk # was not matched to partition rows in the database # log('ROCKS:initializeRocksPartition:nodedisk %s' \ % (nodedisk)) if dbpartinfo.has_key(nodedisk): # # there is set of partition entries in the # database for this physical disk. in this # case, the database is the authority, so # partition the drive according to the # database's specification # self.rocksPartition.partitionDrive(nodedisk, dbpartinfo[nodedisk]) if i.has_key(nodedisk): del i[nodedisk] log('ROCKS:using db partitions for ' + 'disk:%s ' % (nodedisk)) self.rocksPartition.addPartitions(id, dbpartinfo[nodedisk], format = 1) elif nodepartinfo.has_key(nodedisk): if self.rocksPartition.isRocksDisk( \ nodepartinfo[nodedisk]) \ or \ self.rocksPartition.isLinuxSoftwareRaidPartition(nodepartinfo[nodedisk]): # # there is no entry in the database # for this physical disk, but this # drive was previously partitioned by # rocks # # or there was a linux software raid # partition on the drive. in this case # add all partitions on the drive. # NOTE: if non-linux partitions are # on the drive, then this will pass # non-linux partitions to anaconda # # in this case, just try to reconnect # the file systems on the disk # log('ROCKS:using node partitions ' \ 'for disk: %s' % (nodedisk)) self.rocksPartition.addPartitions(id, nodepartinfo[nodedisk], format = 0) elif nodedisk != 'md' and nukeit == 1: # # don't try to autopartion software # raid devices # # # the final case --- this drive is # unknown. rocks has never partitioned # the drive and there are no entries # in the database for it # # if there is user-supplied custom # partitioning info, the drive will # be partitioned per that spec. # otherwise, the drive will get the # default rocks partitioning. this # occurs above in definePartition # # in either case, remove all # partitions from the drive # log('ROCKS:nuking disk:%s' \ % (nodedisk)) self.rocksPartition.nukePartitions( nodedisk, nodepartinfo[nodedisk]) self.rocksPartition.addUnknownDisk( nodedisk) # # flush all disk partition data out to the disks # id.diskset.savePartitions() arch = os.uname()[4] if arch != 'ia64': # # for non-ia64 nodes, for some reason, the second # call to 'savePartitions' causes the kernel to # re-read the partitions from all the drives # id.diskset.savePartitions() return def initializePartitioning(self, id): self.rocksPartition = RocksPartition(self, id, fstype, '/mnt/runtime/usr/sbin/sfdisk', '/mnt/runtime/usr/sbin/e2label', '/upgrade/etc/fstab') return def definePartition(self, id, args): import string # # this function is called for every directive # log('ROCKS:definePartition:enter:args:%s' % (args)) if 'manual' in args: # # force manual partitioning and return # id.partitions.useAutopartitioning = 0 return # # not manually partitioning, so setup the installer for # auto partitioning # id.partitions.useAutopartitioning = 1 # # initialize the partition structures # doinit = 0 if self.rocksPartition == None: doinit = 1 self.initializePartitioning(id) # # Save files we need from the old rocks disk before we # nuke its contents. # self.rocksPartition.saveFile('/etc/fstab', '/upgrade/etc/fstab') try: getRocksFrontendCerts() except: # # this occurs on a non-frontend install # pass root_disk_only = 0 if 'force-default-root-disk-only' in args: root_disk_only = 1 args = [] # # nukeall set to 0 will only remove the partitions # from the first detected hard disk # nukeall = 0 self.rocksPartition.nukeDiskPartitions(nukeall) elif 'force-default' in args: args = [] # # force all disks to become rocks default disks # nukeall = 1 self.rocksPartition.nukeDiskPartitions(nukeall) elif doinit: # # but, if the user supplied partitioning info, # give the authority to initializeRocksPartition() # to remove partitions as it sees fit # # otherwise, don't allow initializeRocksPartition() # to remove any partitions # if len(args) > 0: nukeall = 1 else: nukeall = 0 self.initializeRocksPartition(id, nukeall) # # before continuing, stop the raid # # do this because we started the raid in order to # read info from it (see initializeRocksPartition()). # for normal anaconda, the raid is not started before # it is configured, so let's return to that state # self.rocksPartition.stopRaid() if len(args) > 0: # # the user supplied partitioning info # mntpoint = '' disk = '' for i in range(0, len(args)): if args[i][0] == '/': mntpoint = args[i] elif args[i][0:len('--ondisk')] == '--ondisk': a = string.split(args[i], '=') if len(a) > 1: disk = a[1] else: disk = args[i+1] if disk != '' and \ disk in self.rocksPartition.getUnknownDisks(): log('ROCKS:definePartition:args:%s' % (args)) KickstartBase.definePartition(self, id, args) self.rocksPartition.mountpoints.append(mntpoint) else: # # determine if this should be configured # as a default root disk, or default # data disk # for disk in self.rocksPartition.getUnknownDisks(): log('ROCKS:definePartition:unknown:%s' % (disk)) if self.rocksPartition.isRootDiskConfigured(): if not root_disk_only: self.rocksPartition.defaultDataDisk(disk, id) else: self.rocksPartition.defaultRootDisk( disk, id) # # catch the case where this is a non-rocks node and # a root partition has not been assigned. # if this was a rocks node, then the old '/' would # have been discovered. # # in this case, force the first disk to be a default # rocks root disk. # if not self.rocksPartition.isRootDiskConfigured(): self.definePartition(id, [ 'force-default-root-disk-only' ]) return def defineRaid(self, id, args): import string log('ROCKS:defineRaid:enter:args:%s' % (args)) mntpoint = '' raid_device = '' raid_level = '' for i in range(0, len(args)): if args[i][0] == '/' or args[i] == 'swap': mntpoint = args[i] elif args[i][0:len('--device')] == '--device': a = string.split(args[i], '=') if len(a) > 1: raid_device = a[1] else: raid_device = args[i+1] elif args[i][0:len('--level')] == '--level': a = string.split(args[i], '=') if len(a) > 1: raid_level = a[1] else: raid_level = args[i+1] if mntpoint in self.rocksPartition.mountpoints: # # this mountpoint as already been configured (due to # a pre-existing partition. # # in this case, just return # return # # if we are here, then we didn't find the requested # configuration already configured on the node. # # just take the user-specified description # log('ROCKS:defineRaid:exit:args:%s' % (args)) KickstartBase.defineRaid(self, id, args) self.rocksPartition.mountpoints.append(mntpoint) return def doInteractive(self, id, args): """Extends KickstartBase to read distribution name for wide-area installs.""" KickstartBase.doInteractive(self, id, args) (args, extra) = isys.getopt(args, '', ['dist=', 'rolls=']) distname = "rocks-dist" rollstr = "" rolls = [] for key, val in args: if key == "--dist": distname = val elif key == "--rolls": rollstr = val #id.rocks.distname = distname if rollstr: for r in eval(rollstr): # Rolls is a list of Roll objects rolls.append(apply(Roll, r)) #id.rocks.rolls = rolls # (fds) I'm being a bastard here. Because setting a # kickstart directive called 'rocks' is too dirty (see # kickstart.py:readKickstart()) I am overriding the # 'interactive' handler to do my work. I know we may # not be interactive now... so force it when necessary. if rollstr: self.interactive = 0 def postAction(self, rootPath, serial): """This method is called by anaconda when it wants to run the post installation scripts.""" # # Call all other post install scripts # orig_pythonpath = os.environ['PYTHONPATH'] os.environ['PYTHONPATH'] = '' KickstartBase.postAction(self, rootPath, serial) os.environ['PYTHONPATH'] = orig_pythonpath return def __init__(self, file, serial): self.partition = RocksPartition(0, 0, '', '', '/mnt/runtime/usr/sbin/e2label', '/tmp/etc/fstab') # # need to copy the kickstart file to another location # because there is a bug in the __init__ method below # that removes the kickstart file after parsing the # installclass section # rocksfile = file + ".rocks" os.link(file, rocksfile) KickstartBase.__init__(self, rocksfile, serial) return from kickstart import * class CustomKickstart(RocksCustomKickstart): def __init__(self, file, serial): # # need to copy the kickstart file to another location # because there is a bug in the __init__ method below # that removes the kickstart file after parsing the # installclass section # rocksfile = file + ".rocks" os.link(file, rocksfile) RocksCustomKickstart.__init__(self, rocksfile, serial) return import rocks.roll import rocks.installcgi import rocks.media import rocks.file def checkCD(intf, media, diskname): # # check that it is the right roll CD # found_disk = 'false' while found_disk == 'false': diskid = media.getId() if diskname == diskid: found_disk = 'true' if found_disk == 'false': media.ejectCD() intf.messageWindow(_("Install Roll"), _("Put Roll disk") + " '%s' " % (diskname) + _("in the drive\n")) return def downloadRoll(intf, roll): (rollname, rollversion, rollarch, rollurl, diskid) = roll w = intf.waitWindow(_("Downloading"), _("Downloading Roll") + " '%s' " % (rollname)) # # test if this roll is in rocks format # isrocksroll = 1 u = string.split(rollurl, '/') if len(u) > 2 and u[2] == '127.0.0.1': # # all CDs and DVDs will have the loopback IP address as the # the host name, so let's see if a specific directory exists # that will indicate to us if this is a rocks roll or # a or 'foreign' roll # p = os.path.join('/mnt/cdrom', rollname, rollversion, rollarch) if not os.path.exists(p): isrocksroll = 0 path = os.path.join(rollname, rollversion, rollarch) localpath = os.path.join( '/mnt/sysimage/export/home/install/rolls', path) if isrocksroll: url = '%s' % os.path.join(rollurl, path) else: # # this is not a rocks roll, so append the keywords 'RedHat' # and 'RPMS' onto the local directory name. this allows us # to use CentOS and Scientific Linux CDs # localpath = os.path.join(localpath, 'RedHat', 'RPMS') # # change the url to point to the RPMS directory # cdtree = rocks.file.Tree('/mnt/cdrom') dirpath = '' for dir in cdtree.getDirs(): d = string.split(dir, '/') if d[-1] == 'RPMS': dirpath = dir break url = os.path.join('http://127.0.0.1/mnt/cdrom', dirpath) cutdirs = len(string.split(url[7:], '/')) if isrocksroll: # # for rolls in rocks format, make sure we copy all the # files from the roll (e.g., 'RedHat' and 'base' directories) # this is useful for the kernel roll. # cutdirs -= 1 os.system('mkdir -p %s' % localpath) os.chdir(localpath) if os.path.exists('/tmp/updates/rocks/bin/wget'): wget = '/tmp/updates/rocks/bin/wget' else: wget = '/usr/bin/wget' cmd = '%s -m -nv -np -nH --cut-dirs=%d %s' \ % (wget, cutdirs, url) cmd += ' >> /tmp/wget.debug' os.system(cmd) w.pop() return def RocksGetRolls(intf): # # download the selected rolls # # # if there isn't a /tmp/rolls.xml file, then there is are no rolls # to fetch -- so return # media = rocks.media.Media() if not os.path.exists('/tmp/rolls.xml'): if media.mounted(): media.ejectCD() return # # get the roll list by parsing /tmp/rolls.xml # generator = rocks.roll.Generator() generator.parse('/tmp/rolls.xml') cwd = os.getcwd() # # get all the CD-based rolls first # diskids = [] for roll in generator.rolls: (name, version, arch, url, diskid) = roll if diskid != '' and diskid not in diskids: diskids.append(diskid) diskids.sort() for d in diskids: # # ask the user to put the right media in the bay # checkCD(intf, media, d) # # then, for each selected roll on this disk, copy it # for roll in generator.rolls: (name, version, arch, url, diskid) = roll if diskid == d: downloadRoll(intf, roll) if media.mounted(): media.ejectCD() # # now get all the network rolls # for roll in generator.rolls: (rollname, rollversion, rollarch, rollurl, diskid) = roll if diskid != '': continue downloadRoll(intf, roll) os.chdir(cwd) # # rebuild the distro # w = intf.waitWindow(_("Rocks-Dist"), _("Rebuilding the Distribution...")) rootdir = '/mnt/sysimage/export/home/install' path = '' try: path = os.environ['PATH'] except: pass # # need this path so rocks-dist uses the 'rpm' binary from the rocks # path and not the 'busybox' rpm # os.environ['PATH'] = '/mnt/runtime/rocks/bin:' + path installcgi = rocks.installcgi.InstallCGI(rootdir) installcgi.createPopt() installcgi.rebuildDistro(generator.rolls) # # make sure the link to the repository is correct for the package # installation portion of the install # arch = os.uname()[4] if arch in ['i386', 'i486', 'i586', 'i686']: arch = 'i386' os.system('umount /mnt/cdrom') os.system('rm /mnt/cdrom/RedHat') dir = '/mnt/sysimage/export/home/install/rocks-dist/lan/%s/RedHat' \ % (arch) os.system('ln -s %s /mnt/cdrom/RedHat' % (dir)) w.pop() return def RocksReadComps(intf, method, id): # # before reading the packages, see if there are any rolls to fetch # RocksGetRolls(intf) # # now call the native function # from packages import readPackages readPackages(intf, method, id) return def RocksPreInstall(method, id, intf, instPath, dir): RocksReadComps(intf, method, id) # # now call the native function # from packages import doPreInstall doPreInstall(method, id, intf, instPath, dir) return def RocksTurnOnFilesystems(dir, thefsset, diskset, partitions, upgrade, instPath, baseUrl): from packages import turnOnFilesystems rocksdiskset = RocksDiskSet(diskset, thefsset, baseUrl) turnOnFilesystems(dir, thefsset, rocksdiskset, partitions, upgrade, instPath) # # mark all the disks as 'seen' by rocks # rocksdiskset.rocksDiskStamp() # # if this is an itanium, we need to push the partition data out # once more, as the 'fs-type' field is set only after all the # partitions have been formatted. # arch = os.uname()[4] if arch == 'ia64': rocksdiskset.savePartitions() # # after all the partitions have been formatted, make sure the RPM # database is clean. if the package installation function uses # the 'old' database (the one from the previous installation), then # it may think some packages are already installed and not install # them # rpmdir = '/mnt/sysimage/var/lib/rpm' for root, dirs, files in os.walk(rpmdir, topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) # # now create an empty directory for the rpm database # try: os.mkdir(rpmdir, 0755) except: pass return def rocksCmd(cmd): """A convenience function that sets up the Rocks python environment, and runs a cmd in the root partition of the target disk. Returns the exit code of the command. Will only work after core Linux packages have been installed.""" pythonpath = 'export PYTHONPATH=$PYTHONPATH:/opt/rocks/lib/python' file = open('/tmp/rocks-commands.debug', 'a') fullcmd = 'chroot /mnt/sysimage /bin/bash -c ' \ + ' "%s; %s" ' % (pythonpath, cmd) rc = os.system(fullcmd) file.write('%s\n' % fullcmd) file.close() return rc def setPartitionMethod(partitions, instclass): if instclass.partitionscheme == 'manual': partitions.useAutopartitioning = 0 # # set up the appropriate steps for manual partitioning # instclass.skipStep("partitionmethodsetup", skip = 0) instclass.skipStep("partition", skip = 0) instclass.skipStep("fdisk", skip = 0) else: partitions.useAutopartitioning = 1 return class RocksPartitionMethod: def __call__(self, screen, partitions, instclass): setPartionMethod(partitions, instclass) return INSTALL_OK def RocksDoPostAction(id, instPath, intf): w = intf.waitWindow(_("Post Scripts"), _("Running Rocks Post Installation Scripts...")) id.instClass.postAction(instPath, flags.serial) w.pop() return def fixupGrub(): arch = os.uname()[4] if arch != 'ia64': # # if grub is the bootloader, make sure it is correctly # installed # file = open('/mnt/sysimage/tmp/grub-install', 'w') file.write('#/bin/bash\n' + '/sbin/grub-install --no-floppy ' + "`awk -F= '/^#boot/ { print $2 }' " + '/boot/grub/grub.conf` > ' + '/tmp/grub-install.debug 2>&1') file.close() os.system('chmod a+x /mnt/sysimage/tmp/grub-install') rocksCmd('/tmp/grub-install') os.unlink('/mnt/sysimage/tmp/grub-install') return def RocksMethodComplete(method): # # give the function fixupGrup() 60 seconds to complete # import time import sys pid = os.fork() if pid == 0: fixupGrub() sys.exit(0) else: time.sleep(60) os.kill(pid, 9) os.system('killall -9 minilogd') return