# $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