""" # Copyright 1999-2005 Gentoo Foundation # This source code is distributed under the terms of version 2 of the GNU # General Public License as published by the Free Software Foundation, a copy # of which can be found in the main directory of this project. Gentoo Linux Installer $Id: GLIArchitectureTemplate.py,v 1.297 2006/09/15 16:37:11 agaffney Exp $ The ArchitectureTemplate is largely meant to be an abstract class and an interface (yes, it is both at the same time!). The purpose of this is to create subclasses that populate all the methods with working methods for that architecture. The only definitions that are filled in here are architecture independent. """ import GLIUtility, GLILogger, os, string, sys, shutil, re, time import GLIPortage from GLIException import * import Partitioning import gettext MEGABYTE = 1024 * 1024 LOGFILE = '/var/log/install.log' COMPILE_LOGFILE = '/tmp/compile_output.log' #Start Gettext Support try: gettext.install('gli-gtk', './languages') translator = gettext.translation('gli-gtk', './languages') _ = translator.gettext except: _ = gettext.gettext #End Gettext Support class ArchitectureTemplate: ## # Initialization of the ArchitectureTemplate. Called from some other arch template. # @param install_profile=None An Install Profile # @param client_controller=None Client Controller. not same as configuration. def __init__(self,install_profile=None, client_controller=None): self._install_profile = install_profile self._cc = client_controller # This will get used a lot, so it's probably # better to store it in a variable than to call # this method 100000 times. self._chroot_dir = self._install_profile.get_root_mount_point() self._logger = GLILogger.Logger(LOGFILE) self._compile_logfile = COMPILE_LOGFILE self._debug = self._install_profile.get_verbose() self._portage = GLIPortage.GLIPortage(self._chroot_dir, self._logger, self._debug, self._cc, self._compile_logfile) # This will cleanup the logfile if it's a dead link (pointing # to the chroot logfile when partitions aren't mounted, else # no action needs to be taken if os.path.islink(self._compile_logfile) and not os.path.exists(self._compile_logfile): os.unlink(self._compile_logfile) # cache the list of successfully mounted devices and swap devices here self._mounted_devices = [] self._swap_devices = [] # These must be filled in by the subclass. _steps is a list of # functions, that will carry out the installation. They must be # in order. # # For example, self._steps might be: [preinstall, stage1, stage2, stage3, postinstall], # where each entry is a function (with no arguments) that carries out the desired actions. # Of course, steps will be different depending on the install_profile self._architecture_name = "generic" self._install_steps = { 'do_recommended_partitioning': _("Automatically partition the drive"), 'mount_local_partitions': _("Mount local partitions"), 'mount_network_shares': _("Mount network (NFS) shares"), 'unpack_stage_tarball': _("Unpack stage tarball"), 'update_config_files': _("Updating config files"), 'configure_make_conf': _("Configure /etc/make.conf"), 'prepare_chroot': _("Preparing chroot"), 'install_portage_tree': _("Syncing the Portage tree"), 'stage1': _("Performing bootstrap"), 'stage2': _("Performing 'emerge system'"), 'set_root_password': _("Set the root password"), 'set_timezone': _("Setting timezone"), 'emerge_kernel_sources': _("Emerge kernel sources"), 'build_kernel': _("Building kernel"), 'install_distcc': _("Install distcc"), 'install_mta': _("Installing MTA"), 'install_logging_daemon': _("Installing system logger"), 'install_cron_daemon': _("Installing Cron daemon"), 'install_filesystem_tools': _("Installing filesystem tools"), 'setup_network_post': _("Configuring post-install networking"), 'install_bootloader': _("Configuring and installing bootloader"), 'setup_and_run_bootloader': _("Setting up and running bootloader"), # 'update_config_files': "Re-Updating config files", #second run. 'set_users': _("Add additional users."), 'install_packages': _("Installing additional packages."), # services for startup need to come after installing extra packages # otherwise some of the scripts will not exist. 'set_services': _("Setting up services for startup"), 'run_post_install_script': _("Running custom post-install script"), 'finishing_cleanup': _("Cleanup and unmounting local filesystems."), 'install_failed_cleanup' : _("Cleaning up after a failed install") } ## # Returns the steps and their comments in an array def get_install_steps(self): return self._install_steps ## # Tells the frontend something # @param type type of data # @param data the data itself. usually a number. def notify_frontend(self, type, data): self._cc.addNotification(type, data) # It is possible to override these methods in each Arch Template. # It might be necessary to do so, if the arch needs something 'weird'. ######################## Private Functions ######################## ## # Private function to add a /etc/init.d/ script to the given runlevel in the chroot environement # @param script_name the script to be added # @param runlevel="default" the runlevel to add to def _add_to_runlevel(self, script_name, runlevel="default"): if not GLIUtility.is_file(self._chroot_dir + '/etc/init.d/' + script_name): #raise GLIException("RunlevelAddError", 'fatal', '_add_to_runlevel', "Failure adding " + script_name + " to runlevel " + runlevel + "!") #This is not a fatal error. If the init script is important it will exist. self._logger.log("ERROR! Failure adding " + script_name + " to runlevel " + runlevel + " because it was not found!") if self._debug: self._logger.log("DEBUG: running rc-update add " + script_name + " " + runlevel + " in chroot.") status = GLIUtility.spawn("rc-update add " + script_name + " " + runlevel, display_on_tty8=True, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(status): #raise GLIException("RunlevelAddError", 'fatal', '_add_to_runlevel', "Failure adding " + script_name + " to runlevel " + runlevel + "!") #Again, an error here will not prevent a new system from booting. But it is important to log the error. self._logger.log("ERROR! Could not add " + script_name + " to runlevel " + runlevel + ". returned a bad status code.") else: self._logger.log("Added "+script_name+" to runlevel "+runlevel) ## # Private Function. Will return a list of packages to be emerged for a given command. Not yet used. # @param cmd full command to run ('/usr/portage/scripts/bootstrap.sh --pretend' or 'emerge -p system') def _get_packages_to_emerge(self, cmd): if self._debug: self._logger.log("DEBUG: _get_packages_to_emerge() called with '%s'" % cmd) return GLIUtility.spawn(cmd + r" 2>/dev/null | grep -e '\[ebuild' | sed -e 's:\[ebuild .\+ \] ::' -e 's: \[.\+\] ::' -e 's: \+$::'", chroot=self._chroot_dir, return_output=True)[1].strip().split("\n") ## # Private Function. Will emerge a given package in the chroot environment. # @param package package to be emerged # @param binary=False defines whether to try a binary emerge (if GRP this gets ignored either way) # @param binary_only=False defines whether to only allow binary emerges. def _emerge(self, package, binary=True, binary_only=False): #Error checking of this function is to be handled by the parent function. # self._logger.log("_emerge() called with: package='%s', binary='%s', binary_only='%s', grp_install='%s'" % (package, str(binary), str(binary_only), str(self._install_profile.get_grp_install()))) # now short-circuit for GRP if self._install_profile.get_grp_install(): cmd="emerge -k " + package # now normal installs else: if binary_only: cmd="emerge -K " + package elif binary: cmd="emerge -k " + package else: cmd="emerge " + package self._logger.log("Calling emerge: "+cmd) return GLIUtility.spawn(cmd, display_on_tty8=True, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True) ## # Private Function. Will edit a config file and insert a value or two overwriting the previous value # (actually it only just comments out the old one) # @param filename file to be edited # @param newvalues a dictionary of VARIABLE:VALUE pairs # @param delimeter='=' what is between the key and the value # @param quotes_around_value=True whether there are quotes around the value or not (ex. "local" vs. localhost) # @param only_value=False Ignore the keys and output only a value. # @param create_file=True Create the file if it does not exist. def _edit_config(self, filename, newvalues, delimeter='=', quotes_around_value=True, only_value=False,create_file=True): # don't use 'file' as a normal variable as it conflicts with the __builtin__.file newvalues = newvalues.copy() if self._debug: self._logger.log("DEBUG: _edit_config() called with " + str(newvalues)+" and flags: "+delimeter + "quotes: "+str(quotes_around_value)+" value: "+str(only_value)) if GLIUtility.is_file(filename): f = open(filename) contents = f.readlines() f.close() elif create_file: contents = [] else: raise GLIException("NoSuchFileError", 'notice','_edit_config',filename + ' does not exist!') for key in newvalues.keys(): newline = "" if key == "SPACER": newline = "\n" elif key == "COMMENT": newline = '# ' + newvalues[key] + "\n" elif newvalues[key] == "##comment##" or newvalues[key] == "##commented##": newline = '#' + key + delimeter + '""' + "\n" else: if quotes_around_value: newvalues[key] = '"' + newvalues[key] + '"' #Only the printing of values is required. if only_value: newline = newvalues[key] + "\n" else: newline = key + delimeter + newvalues[key] + "\n" add_at_line = len(contents) for i in range(len(contents)): if newline == contents[i]: break if contents[i].startswith(key + delimeter): contents[i] = "#" + contents[i] add_at_line = i + 1 else: contents.insert(add_at_line, newline) if self._debug: self._logger.log("DEBUG: Contents of file "+filename+": "+str(contents)) f = open(filename,'w') f.writelines(contents) f.flush() f.close() self._logger.log("Edited Config file "+filename) ## # Configures /etc/fstab on the new envorinment def _configure_fstab(self): newfstab = "" mounts = self._install_profile.get_mounts() for mount in mounts: if mount['type'] in ("swap", "linux-swap"): newfstab += mount['devnode']+"\t none swap sw 0 0\n" continue #Stop here and move on. if not mount['mountopts'].strip(): mount['mountopts'] = "defaults" if mount['mountpoint']: if not GLIUtility.is_file(self._chroot_dir+mount['mountpoint']): if self._debug: self._logger.log("DEBUG: making mountpoint: "+mount['mountpoint']) exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mount['mountpoint']) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("MkdirError", 'fatal','configure_fstab', "Making the mount point failed!") newfstab += mount['devnode']+"\t "+mount['mountpoint']+"\t "+mount['type']+"\t "+mount['mountopts']+"\t\t " #Now figure out the last 2 numbers depending on the mountpoint. if mount['mountpoint'] == "/boot": newfstab += "1 2\n" elif mount['mountpoint'] == "/": newfstab += "0 1\n" else: newfstab += "0 0\n" newfstab += "none /proc proc defaults 0 0\n" newfstab += "none /dev/shm tmpfs defaults 0 0\n" if GLIUtility.is_device("/dev/cdroms/cdrom0"): newfstab += "/dev/cdroms/cdrom0 /mnt/cdrom auto noauto,user 0 0\n" for netmount in self._install_profile.get_network_mounts(): if netmount['type'] == "nfs": newfstab += netmount['host'] + ":" + netmount['export'] + "\t" + netmount['mountpoint'] + "\tnfs\t" + netmount['mountopts'] + "\t0 0\n" file_name = self._chroot_dir + "/etc/fstab" try: if self._debug: self._logger.log("DEBUG: backing up original fstab") shutil.move(file_name, file_name + ".OLDdefault") except: self._logger.log("ERROR: could not backup original fstab.") if self._debug: self._logger.log("DEBUG: Contents of new fstab: "+newfstab) f = open(file_name, 'w') f.writelines(newfstab) f.close() self._logger.log("fstab configured.") ##################################################################### ##################################################################### ########################## Installation Functions ################### ## # Will automatically partition the hard drive if both paramaters # in the install_profile are correctly set. This in turn will # appropriately set the mountpoints for the rest of the install. # This function is to be called only for fully-automated installs. def do_recommended_partitioning(self): drive = self._install_profile.get_do_recommended_partitioning() #returns drive name. second_check = self._install_profile.get_yes_iam_sure() if drive and second_check: try: self._logger.log("Creating the drive object for drive %s for do_recommended" % drive) driveobject = Partitioning.Device(drive, self._architecture_name, self._install_profile) self._logger.log("Running do_recommended!") driveobject.do_recommended() self._logger.log("do_recommended complete! mountpoints should now be set.") except: raise GLIException("DoRecommendedError", 'fatal','do_recommended_partitioning', "An Error occurred while trying to run do_recommended!") ## # Will grab partition info from the profile and mount all partitions with a specified mountpoint (and swap too) def mount_local_partitions(self): #Temporary hack here to update the debug value. self._debug = self._install_profile.get_verbose() mounts = self._install_profile.get_mounts() mount_progress = 1 self.notify_frontend("progress", (float(mount_progress) / (len(mounts)+1), _("Generating mount list"))) parts_to_mount = {} for mount in mounts: Partitioning.wait_for_device_node(mount['devnode']) mountpoint = mount['mountpoint'] mountopts = mount['mountopts'] partition_type = mount['type'] if partition_type in ("linux-swap", "swap"): mount_progress += 1 self.notify_frontend("progress", (float(mount_progress) / (len(mounts)+1), _("Activating swap on ") + mount['devnode'])) ret = GLIUtility.spawn("swapon " + mount['devnode']) if not GLIUtility.exitsuccess(ret): self._logger.log("ERROR! : Could not activate swap (" + mount['devnode'] + ")!") else: self._swap_devices.append(mount['devnode']) continue if not mountpoint: continue #Skip this. Probably should be an error. but should NEVER happen. if mountopts: mountopts = "-o " + mountopts + " " if partition_type: if partition_type == "fat32" or partition_type == "fat16": partition_type = "vfat" partition_type = "-t " + partition_type + " " else: #Unknown partition type!!! self._logger.log("ERROR! : Unknown partition type for mountpoint %s which is device %s" % (mountpoint, mount['devnode'])) parts_to_mount[mountpoint] = (mountopts, partition_type, mount['devnode']) sorted_list = parts_to_mount.keys() sorted_list.sort() if not GLIUtility.is_file(self._chroot_dir): if self._debug: self._logger.log("DEBUG: making the chroot dir") exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("MkdirError", 'fatal','mount_local_partitions', "Making the ROOT mount point failed!") else: self._logger.log("Created root mount point") for i, mountpoint in enumerate(sorted_list): mountopts = parts_to_mount[mountpoint][0] partition_type = parts_to_mount[mountpoint][1] partition = parts_to_mount[mountpoint][2] mount_progress += 1 self.notify_frontend("progress", (float(mount_progress) / (len(mounts)+1), _("Mounting %(partition)s at %(mountpoint)s") % {'partition' : partition, 'mountpoint' : mountpoint})) if not GLIUtility.is_file(self._chroot_dir + mountpoint): if self._debug: self._logger.log("DEBUG: making mountpoint: "+mountpoint) exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mountpoint) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("MkdirError", 'fatal','mount_local_partitions', "Making the mount point failed!") else: self._logger.log("Created mountpoint " + mountpoint) if self._debug: self._logger.log("DEBUG: running mount "+ partition_type + mountopts + partition + " " + self._chroot_dir + mountpoint) ret = GLIUtility.spawn("mount " + partition_type + mountopts + partition + " " + self._chroot_dir + mountpoint, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(ret): raise GLIException("MountError", 'fatal','mount_local_partitions','Could not mount a partition') self._mounted_devices.append(mountpoint) mount_files = os.listdir(self._chroot_dir + mountpoint) for mount_file in mount_files: if os.path.isdir(self._chroot_dir + mountpoint + '/' + mount_file) and mount_file != "lost+found": raise GLIException("MountError", "fatal", "mount_local_partition", "The partition %s (mounted at %s) already has files! The installer only supports installing to a blank filesystem" % (partition, mountpoint)) # double check in /proc/mounts # This current code doesn't work and needs to be fixed, because there is a case that it is needed for - robbat2 #ret, output = GLIUtility.spawn('awk \'$2 == "%s" { print "Found" }\' /proc/mounts | head -n1' % (self._chroot_dir + mountpoint), display_on_tty8=True, return_output=True) #if output.strip() != "Found": # raise GLIException("MountError", 'fatal','mount_local_partitions','Could not mount a partition (failed in double-check)') self._logger.log("Mounted mountpoint: " + mountpoint) if self._debug: self._logger.log("DEBUG: mounted_devices is %s" % str(self._mounted_devices)) ## # Mounts all network shares to the local machine def mount_network_shares(self): """ it'll be much easier than mount_local_partitions make sure /etc/init.d/portmap is started then mount each one: mount -t nfs -o : """ nfsmounts = self._install_profile.get_network_mounts() for netmount in nfsmounts: if netmount['type'] == "NFS" or netmount['type'] == "nfs": mountopts = netmount['mountopts'] if mountopts: mountopts = "-o " + mountopts host = netmount['host'] export = netmount['export'] mountpoint = netmount['mountpoint'] if not GLIUtility.is_file(self._chroot_dir + mountpoint): exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mountpoint) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("MkdirError", 'fatal','mount_network_shares', "Making the mount point failed!") else: if self._debug: self._logger.log("DEBUG: mounting nfs mount") ret = GLIUtility.spawn("mount -t nfs " + mountopts + " " + host + ":" + export + " " + self._chroot_dir + mountpoint, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(ret): raise GLIException("MountError", 'fatal','mount_network_shares','Could not mount an NFS partition') else: self._logger.log("Mounted netmount at mountpoint: " + mountpoint) self._mounted_devices.append(mountpoint) else: self._logger.log("Netmount type " + netmount['type'] + " not supported...skipping " + netmount['mountpoint']) ## # Unpacks the stage tarball that has been specified in the profile (it better be there!) def unpack_stage_tarball(self): if not os.path.isdir(self._chroot_dir): if self._debug: self._logger.log("DEBUG: making the chroot dir:"+self._chroot_dir) os.makedirs(self._chroot_dir) if self._install_profile.get_install_stage() == 3 and self._install_profile.get_dynamic_stage3(): # stage3 generation code here try: systempkgs = self._portage.get_system_packages() except: raise GLIException("CreateStage3Error", "fatal", "unpack_stage_tarball", "Could not generate list of system packages") # Pre-create /lib (and possibly /lib32 and /lib64) for libdir in ("/lib", "/usr/lib"): if os.path.islink(libdir) and os.readlink(libdir).endswith("64"): if self._debug: self._logger.log("DEBUG: unpack_stage_tarball(): precreating " + libdir + "64 dir and " + libdir + " -> lib64 symlink because glibc/portage sucks") if not GLIUtility.exitsuccess(GLIUtility.spawn("mkdir -p " + self._chroot_dir + libdir + "64 && ln -sf lib64 " + self._chroot_dir + libdir)): raise GLIException("CreateStage3Error", "fatal", "unpack_stage_tarball", "Could not precreate " + libdir + "64 dir and " + libdir + " -> lib64 symlink") syspkglen = len(systempkgs) for i, pkg in enumerate(systempkgs): pkg = pkg.strip() self.notify_frontend("progress", (float(i) / (syspkglen+1), _("Copying ") + pkg + " (" + str(i+1) + "/" + str(syspkglen) + ")")) self._portage.copy_pkg_to_chroot(pkg, True, ignore_missing=True) self.notify_frontend("progress", (float(syspkglen) / (syspkglen+1), _("Finishing"))) GLIUtility.spawn("cp /etc/make.conf " + self._chroot_dir + "/etc/make.conf") # Remove warning from make.conf GLIUtility.spawn("sed -i '/^##/d' " + self._chroot_dir + "/etc/make.conf") GLIUtility.spawn("ln -s `readlink /etc/make.profile` " + self._chroot_dir + "/etc/make.profile") GLIUtility.spawn("cp -f /etc/inittab.old " + self._chroot_dir + "/etc/inittab") # Nasty, nasty, nasty hack because vapier is a tool for tmpfile in ("/etc/passwd", "/etc/group", "/etc/shadow"): GLIUtility.spawn("grep -ve '^gentoo' " + tmpfile + " > " + self._chroot_dir + tmpfile) chrootscript = r""" #!/bin/bash source /etc/make.conf export LDPATH="/usr/lib/gcc-lib/${CHOST}/$(cd /usr/lib/gcc-lib/${CHOST} && ls -1 | head -n 1)" ldconfig $LDPATH gcc-config 1 env-update source /etc/profile modules-update [ -f /usr/bin/binutils-config ] && binutils-config 1 source /etc/profile cd /dev rm console null tty1 mknod console c 5 1 mknod null c 1 3 mknod tty1 c 4 1 mkdir -m 755 /mnt mkdir -m 700 /mnt/cdrom mkdir -m 700 /mnt/floppy [ -f /usr/sbin/create-cracklib-dict ] && /usr/sbin/create-cracklib-dict /usr/share/dict/* > /dev/null # [ -f /lib/udev-state/devices.tar.bz2 ] && tar -C /dev -xjf /lib/udev-state/devices.tar.bz2 mkdir /etc/portage echo "portdbapi.auxdbmodule = cache.metadata_overlay.database" > /etc/portage/modules """ script = open(self._chroot_dir + "/tmp/extrastuff.sh", "w") script.write(chrootscript) script.close() GLIUtility.spawn("chmod 755 /tmp/extrastuff.sh && /tmp/extrastuff.sh", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) GLIUtility.spawn("rm -rf /var/tmp/portage/* /usr/portage /tmp/*", chroot=self._chroot_dir) self.notify_frontend("progress", (1, _("Done"))) if not GLIUtility.is_file("/etc/make.conf"): raise GLIException("CreateStage3Error", "fatal", "unpack_stage_tarball", "Something went wrong during stage3 generation, /etc/make.conf doesn't exist on the new system! ") self._logger.log("Stage3 was generated successfully") else: self._logger.log("Fetching and unpacking tarball: "+self._install_profile.get_stage_tarball_uri()) GLIUtility.fetch_and_unpack_tarball(self._install_profile.get_stage_tarball_uri(), self._chroot_dir, temp_directory=self._chroot_dir, keep_permissions=True, cc=self._cc) if not GLIUtility.is_file("/etc/make.conf"): raise GLIException("CreateStage3Error", "fatal", "unpack_stage_tarball", "Something went wrong during stage3 generation, /etc/make.conf doesn't exist on the new system! ") self._logger.log(self._install_profile.get_stage_tarball_uri()+" was fetched and unpacked.") ## # This runs etc-update and then re-overwrites the files by running the configure_*'s to keep our values. def update_config_files(self): "Runs etc-update (overwriting all config files), then re-configures the modified ones" # Run etc-update overwriting all config files if self._debug: self._logger.log("DEBUG: update_config_files(): running: "+'echo "-5" | chroot '+self._chroot_dir+' etc-update') status = GLIUtility.spawn('echo "-5" | chroot '+self._chroot_dir+' etc-update', display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(status): self._logger.log("ERROR! : Could not update the config files!") else: self._configure_fstab() etc_files = self._install_profile.get_etc_files() for etc_file in etc_files: # Skip entries with blank filenames if not etc_file: continue if self._debug: self._logger.log("DEBUG: update_config_files(): updating config file: "+etc_file) if isinstance(etc_files[etc_file], dict): self._edit_config(self._chroot_dir + "/etc/" + etc_file, etc_files[etc_file]) else: for entry in etc_files[etc_file]: # Skip blank entries if not entry: continue self._edit_config(self._chroot_dir + "/etc/" + etc_file, { "0": entry }, only_value=True) self._logger.log("Config files updated using etc-update. make.conf/fstab/rc.conf restored.") ## # Configures the new /etc/make.conf def configure_make_conf(self): # Get make.conf options make_conf = self._install_profile.get_make_conf() # For each configuration option... filename = self._chroot_dir + "/etc/make.conf" # self._edit_config(filename, {"COMMENT": "GLI additions ===>"}) for key in make_conf.keys(): # Add/Edit it into make.conf self._edit_config(filename, {key: make_conf[key]}) # self._edit_config(filename, {"COMMENT": "<=== End GLI additions"}) self._logger.log("Make.conf configured") # now make any directories that emerge needs, otherwise it will fail # this must take place before ANY calls to emerge. # otherwise emerge will fail (for PORTAGE_TMPDIR anyway) # defaults first # this really should use portageq or something. PKGDIR = '/usr/portage/packages' PORTAGE_TMPDIR = '/var/tmp' PORT_LOGDIR = None PORTDIR_OVERLAY = None # now other stuff if 'PKGDIR' in make_conf: PKGDIR = make_conf['PKGDIR'] if 'PORTAGE_TMPDIR' in make_conf: PORTAGE_TMPDIR = make_conf['PORTAGE_TMPDIR'] if 'PORT_LOGDIR' in make_conf: PORT_LOGDIR = make_conf['PORT_LOGDIR'] if 'PORTDIR_OVERLAY' in make_conf: PORTDIR_OVERLAY = make_conf['PORTDIR_OVERLAY'] if self._debug: self._logger.log("DEBUG: making PKGDIR if necessary: "+PKGDIR) GLIUtility.spawn("mkdir -p " + self._chroot_dir + PKGDIR, logfile=self._compile_logfile, append_log=True) if self._debug: self._logger.log("DEBUG: making PORTAGE_TMPDIR if necessary: "+PORTAGE_TMPDIR) GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORTAGE_TMPDIR, logfile=self._compile_logfile, append_log=True) if PORT_LOGDIR != None: if self._debug: self._logger.log("DEBUG: making PORT_LOGDIR if necessary: "+PORT_LOGDIR) GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORT_LOGDIR, logfile=self._compile_logfile, append_log=True) if PORTDIR_OVERLAY != None: if self._debug: self._logger.log("DEBUG: making PORTDIR_OVERLAY if necessary "+PORTDIR_OVERLAY) GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORTDIR_OVERLAY, logfile=self._compile_logfile, append_log=True) ## # Prepares the Chroot environment by copying /etc/resolv.conf and mounting proc and dev def prepare_chroot(self): # Copy resolv.conf to new env try: if self._debug: self._logger.log("DEBUG: copying /etc/resolv.conf over.") shutil.copy("/etc/resolv.conf", self._chroot_dir + "/etc/resolv.conf") except: pass if self._debug: self._logger.log("DEBUG: mounting proc") if not os.path.isdir(self._chroot_dir+"/proc"): if self._debug: self._logger.log("DEBUG: making the proc dir in chroot") os.makedirs(self._chroot_dir+"/proc") ret = GLIUtility.spawn("mount -t proc none "+self._chroot_dir+"/proc") if not GLIUtility.exitsuccess(ret): raise GLIException("MountError", 'fatal','prepare_chroot','Could not mount /proc') else: self._mounted_devices.append("/proc") bind_mounts = [ '/dev' ] uname = os.uname() if uname[0] == 'Linux' and uname[2].split('.')[1] == '6': bind_mounts.append('/sys') if self._debug: self._logger.log("DEBUG: bind-mounting " + ", ".join(bind_mounts)) for mount in bind_mounts: if not os.path.isdir(self._chroot_dir+mount): if self._debug: self._logger.log("DEBUG: making the %s dir in chroot" % mount) os.makedirs(self._chroot_dir+mount) ret = GLIUtility.spawn('mount -o bind %s %s%s' % (mount,self._chroot_dir,mount)) if not GLIUtility.exitsuccess(ret): raise GLIException("MountError", 'fatal','prepare_chroot','Could not mount '+mount) else: self._mounted_devices.append(mount) if self._debug: self._logger.log("DEBUG: copying logfile to new system!") GLIUtility.spawn("mv " + self._compile_logfile + " " + self._chroot_dir + self._compile_logfile + " && ln -s " + self._chroot_dir + self._compile_logfile + " " + self._compile_logfile) self._logger.log("Chroot environment ready.") ## # This will get/update the portage tree. If you want to snapshot or mount /usr/portage use "custom". def install_portage_tree(self): # Check the type of portage tree fetching we'll do # If it is custom, follow the path to the custom tarball and unpack it sync_type = self._install_profile.get_portage_tree_sync_type() if sync_type == "snapshot" or sync_type == "custom": # Until this is finalized # Get portage tree info portage_tree_snapshot_uri = self._install_profile.get_portage_tree_snapshot_uri() if portage_tree_snapshot_uri: # Fetch and unpack the tarball if self._debug: self._logger.log("DEBUG: grabbing custom snapshot uri: "+portage_tree_snapshot_uri) GLIUtility.fetch_and_unpack_tarball(portage_tree_snapshot_uri, self._chroot_dir + "/usr/", self._chroot_dir + "/", cc=self._cc) self._logger.log("Portage tree install was custom.") if sync_type == "sync": if self._debug: self._logger.log("DEBUG: starting emerge sync") exitstatus = GLIUtility.spawn("emerge sync", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus): self._logger.log("ERROR! Could not sync the portage tree using emerge sync. Falling back to emerge-webrsync as a backup.") sync_type = "webrsync" else: self._logger.log("Portage tree sync'd") # If the type is webrsync, then run emerge-webrsync if sync_type == "webrsync": if self._debug: self._logger.log("DEBUG: starting emerge webrsync") exitstatus = GLIUtility.spawn("emerge-webrsync", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("EmergeWebRsyncError", 'fatal','install_portage_tree', "Failed to retrieve portage tree using webrsync!") self._logger.log("Portage tree sync'd using webrsync") # Otherwise, spit out a message because its probably a bad thing. #else: # self._logger.log("NOTICE! No valid portage tree sync method was selected. This will most likely result in a failed installation unless the tree is mounted.") ## # Stage 1 install -- bootstraping the system # If we are doing a stage 1 install, then bootstrap def stage1(self): if self._install_profile.get_install_stage() == 1: self._logger.mark() self._logger.log("Starting bootstrap.") pkgs = self._get_packages_to_emerge("/usr/portage/scripts/bootstrap.sh --pretend") if self._debug: self._logger.log("DEBUG: Packages to emerge: "+str(pkgs)+". Now running bootstrap.sh") exitstatus = GLIUtility.spawn("env-update && source /etc/profile && /usr/portage/scripts/bootstrap.sh", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("Stage1Error", 'fatal','stage1', "Bootstrapping failed!") self._logger.log("Bootstrap complete.") ## # Stage 2 install -- emerge -e system # If we are doing a stage 1 or 2 install, then emerge system def stage2(self): if self._install_profile.get_install_stage() in [ 1, 2 ]: self._logger.mark() self._logger.log("Starting emerge system.") pkgs = self._get_packages_to_emerge("emerge -p system") #currently quite the useless if self._debug: self._logger.log("DEBUG: Packages to emerge: "+str(pkgs)+"/ Now running emerge --emptytree system") # exitstatus = self._emerge("--emptytree system") exitstatus = GLIUtility.spawn("env-update && source /etc/profile && emerge -e system", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("Stage2Error", 'fatal','stage2', "Building the system failed!") self._logger.log("Emerge system complete.") ## # Sets the root password def set_root_password(self): if self._debug: self._logger.log("DEBUG: set_root_password(): running: "+ 'echo \'root:' + self._install_profile.get_root_pass_hash() + '\' | chroot '+self._chroot_dir+' chpasswd -e') status = GLIUtility.spawn('echo \'root:' + self._install_profile.get_root_pass_hash() + '\' | chroot '+self._chroot_dir+' chpasswd -e') if not GLIUtility.exitsuccess(status): raise GLIException("SetRootPasswordError", 'fatal', 'set_root_password', "Failure to set root password!") self._logger.log("Root Password set on the new system.") ## # Sets the timezone for the new environment def set_timezone(self): # Set symlink if os.access(self._chroot_dir + "/etc/localtime", os.W_OK): if self._debug: self._logger.log("DEBUG: /etc/localtime already exists, removing it so it can be symlinked") GLIUtility.spawn("rm "+self._chroot_dir + "/etc/localtime") if self._debug: self._logger.log("DEBUG: running ln -s ../usr/share/zoneinfo/" + self._install_profile.get_time_zone() + " /etc/localtime") GLIUtility.spawn("ln -s ../usr/share/zoneinfo/" + self._install_profile.get_time_zone() + " /etc/localtime", chroot=self._chroot_dir) if self._debug: self._logger.log("DEBUG: Setting TIMEZONE to %s in /etc/conf.d/clock" % self._install_profile.get_time_zone()) self._edit_config(self._chroot_dir + "/etc/conf.d/clock", {"TIMEZONE": self._install_profile.get_time_zone()}) if not (self._install_profile.get_time_zone() == "UTC"): if self._debug: self._logger.log("DEBUG: timezone was not UTC, setting CLOCK to local. This may be overwritten later.") self._edit_config(self._chroot_dir + "/etc/conf.d/clock", {"CLOCK":"local"}) self._logger.log("Timezone set.") ## # Fetches desired kernel sources, unless you're using a livecd-kernel in which case it does freaky stuff. def emerge_kernel_sources(self): kernel_pkg = self._install_profile.get_kernel_source_pkg() self._logger.log("Starting emerge_kernel, package is %s" % kernel_pkg) # if kernel_pkg: # Special case, no kernel installed if kernel_pkg == "none": return # Special case, livecd kernel elif kernel_pkg == "livecd-kernel": if self._debug: self._logger.log("DEBUG: starting livecd-kernel setup") self.notify_frontend("progress", (0, _("Determining files to copy"))) GLIUtility.spawn(r"find /var/db/pkg -type f -name CONTENTS | xargs awk '/^obj \/lib\/modules\// { print $2; }' > /tmp/kernel_modules") files = GLIUtility.spawn('ls -1 --color=no /boot/System* /boot/kernel* /boot/initr* $(for i in $(find "/lib/modules/" -type f); do grep --quiet "^${i}$" /tmp/kernel_modules || echo ${i}; done); rm /tmp/kernel_modules 2>/dev/null', return_output=True)[1].strip().split("\n") if not files: raise GLIException("LiveCDKernelError", 'fatal','emerge_kernel_sources', "Could not determine files to copy for livecd-kernel") self.notify_frontend("progress", (0.2, _("Copying kernel, initramfs, and modules"))) if not GLIUtility.exitsuccess(GLIUtility.spawn("cp -p --parents %s %s/" % (" ".join(files), self._chroot_dir), logfile=self._compile_logfile, append_log=True)): raise GLIException("LiveCDKernelError", 'fatal','emerge_kernel_sources', "Could not copy kernel, initramfs, or modules") self.notify_frontend("progress", (0.4, _("Generating module dependencies"))) GLIUtility.spawn("depmod -a", logfile=self._compile_logfile, append_log=True) self.notify_frontend("progress", (0.6, _("Gathering portage configuration"))) portage_info = {} for line in GLIUtility.spawn(r"emerge --info 2>/dev/null | grep '^[A-Z0-9_]\+=' | sed -e 's:\" \([A-Z]\):\"\n\1:g'", chroot=self._chroot_dir, return_output=True)[1].strip().split("\n"): parts = line.split("=", 1) portage_info[parts[0]] = parts[1].strip("'\"") arch = portage_info["ACCEPT_KEYWORDS"].split(" ")[0].strip("~") values = { 'CATEGORY': "sys-kernel", 'CBUILD': portage_info['CHOST'], 'CFLAGS': portage_info['CFLAGS'], 'CHOST': portage_info['CHOST'], 'CONTENTS': "", # 'COUNTER': "0", 'CXXFLAGS': portage_info.get("CXXFLAGS", ""), 'DEPEND': "", 'DESCRIPTION': "LiveCD kernel as installed by GLI", 'EAPI': "0", 'FEATURES': "", 'HOMEPAGE': "http://www.gentoo.org/proj/en/releng/installer/", 'INHERITED': "eutils", 'KEYWORDS': arch, 'LICENSE': "", 'NEEDED': "", 'PDEPEND': "", 'PF': "livecd-kernel-1", 'PROVIDE': "virtual/alsa", 'RDEPEND': "", 'SLOT': "0", 'USE': arch } self.notify_frontend("progress", (0.8, _("Creating VDB entry for livecd-kernel"))) vdbdir = self._chroot_dir + "/var/db/pkg/sys-kernel/livecd-kernel-1" os.mkdir(vdbdir) for tmpfile in values: fileout = open(vdbdir + "/" + tmpfile, "w") fileout.write(values[tmpfile] + "\n") fileout.close() fileout = open(vdbdir + "/livecd-kernel-1.ebuild", "w") fileout.write("inherit eutils\n\nDESCRIPTION=\"LiveCD kernel as installed by GLI\"\nHOMEPAGE=\"http://www.gentoo.org/proj/en/releng/installer/\"\nSRC_URI=\nLICENSE=\nSLOT=0\nKEYWORDS=\"%s\"\nIUSE=\nDEPEND=\nPROVIDE=virtual/alsa\n" % arch) fileout.close() counter = self._portage.update_counter() counter_f = open(vdbdir + "/COUNTER", "w") counter_f.write(str(counter)) counter_f.close() GLIUtility.spawn("echo | bzip2 > %s/environment.bz2" % vdbdir) self.notify_frontend("progress", (1, _("Done copying livecd-kernel to chroot"))) if self._install_profile.get_kernel_bootsplash(): self._logger.log("Bootsplash enabled for livecd-kernel...this is currently broken, so we're skipping the package install") # self._logger.log("Bootsplash enabled...emerging necessary packages") # self._portage.emerge(["splashutils", "splash-themes-livecd"]) # Extra modules from kernelpkgs.txt...disabled until I can figure out why it sucks # try: # kernpkgs = open("/usr/livecd/kernelpkgs.txt", "r") # pkgs = "" # for line in kernpkgs.readlines(): # pkgs += line.strip() + " " # kernpkgs.close() # except: # raise GLIException("EmergeColdplugError", 'fatal','build_kernel', "Could not read kernelpkgs.txt") # exitstatus = self._emerge(pkgs) # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("EmergeExtraKernelModulesError", 'fatal','build_kernel', "Could not emerge extra kernel packages") # self._logger.log("Extra kernel packages emerged.") # normal case else: exitstatus = self._portage.emerge(kernel_pkg, self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("EmergeKernelSourcesError", 'fatal','emerge_kernel_sources',"Could not retrieve kernel sources!") try: os.stat(self._chroot_dir + "/usr/src/linux") except: kernels = os.listdir(self._chroot_dir+"/usr/src") if self._debug: self._logger.log("DEBUG: no /usr/src/linux found. found kernels: "+str(kernels)) found_a_kernel = False counter = 0 while not found_a_kernel: if (len(kernels[counter]) > 6) and (kernels[counter][0:6]=="linux-"): if self._debug: self._logger.log("DEBUG: found one. linking it. running: ln -s /usr/src/"+kernels[counter]+ " /usr/src/linux in the chroot.") exitstatus = GLIUtility.spawn("ln -s /usr/src/"+kernels[counter]+ " /usr/src/linux",chroot=self._chroot_dir) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("EmergeKernelSourcesError", 'fatal','emerge_kernel_sources',"Could not make a /usr/src/linux symlink") found_a_kernel = True else: counter = counter + 1 self._logger.log("Kernel sources:"+kernel_pkg+" emerged and /usr/src/linux symlinked.") ## # Builds the kernel using genkernel or regularly if given a custom .config file in the profile def build_kernel(self): self._logger.mark() self._logger.log("Starting build_kernel") build_mode = self._install_profile.get_kernel_build_method() # No building necessary if using the LiveCD's kernel/initrd # or using the 'none' kernel bypass if self._install_profile.get_kernel_source_pkg() in ["livecd-kernel","none"]: if self._debug: self._logger.log("DEBUG: using "+self._install_profile.get_kernel_source_pkg()+ " so skipping this function.") return # Get the uri to the kernel config kernel_config_uri = self._install_profile.get_kernel_config_uri() # is there an easier way to do this? if self._debug: self._logger.log("DEBUG: running command: awk '/^PATCHLEVEL/{print $3}' /usr/src/linux/Makefile in chroot.") ret, kernel_major = GLIUtility.spawn("awk '/^PATCHLEVEL/{print $3}' /usr/src/linux/Makefile",chroot=self._chroot_dir,return_output=True) # 6 == 2.6 kernel, 4 == 2.4 kernel kernel_major = int(kernel_major) if self._debug: self._logger.log("DEBUG: kernel major version is: "+str(kernel_major)) #Copy the kernel .config to the proper location in /usr/src/linux if kernel_config_uri != '': try: if self._debug: self._logger.log("DEBUG: grabbing kernel config from "+kernel_config_uri+" and putting it in "+self._chroot_dir + "/var/tmp/kernel_config") GLIUtility.get_uri(kernel_config_uri, self._chroot_dir + "/var/tmp/kernel_config") except: raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not copy kernel config!") # the && stuff is important so that we can catch any errors. kernel_compile_script = "#!/bin/bash\n" kernel_compile_script += "cp /var/tmp/kernel_config /usr/src/linux/.config && " kernel_compile_script += "cd /usr/src/linux && " # required for 2.[01234] etc kernels if kernel_major in [0,1,2,3,4]: kernel_compile_script += " yes 'n' | make oldconfig && make symlinks && make dep" # not strictly needed, but recommended by upstream else: #elif kernel_major in [5,6]: kernel_compile_script += "make prepare" # bypass to install a kernel, but not compile it if build_mode == "none": return # this mode is used to install kernel sources, and have then configured # but not actually build the kernel. This is needed for netboot # situations when you have packages that require kernel sources # to build. elif build_mode == "prepare-only": if self._debug: self._logger.log("DEBUG: writing kernel script with contents: "+kernel_compile_script) f = open(self._chroot_dir+"/var/tmp/kernel_script", 'w') f.writelines(kernel_compile_script) f.close() #Build the kernel if self._debug: self._logger.log("DEBUG: running: chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script") exitstatus1 = GLIUtility.spawn("chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script") if self._debug: self._logger.log("DEBUG: running: /var/tmp/kernel_script in chroot.") exitstatus2 = GLIUtility.spawn("/var/tmp/kernel_script", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus1): raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not handle prepare-only build! died on chmod.") if not GLIUtility.exitsuccess(exitstatus2): raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not handle prepare-only build! died on running of kernel script.") #i'm sure i'm forgetting something here. #cleanup exitstatus = GLIUtility.spawn("rm -f "+self._chroot_dir+"/var/tmp/kernel_script "+self._chroot_dir+"/var/tmp/kernel_config") #it's not important if this fails. self._logger.log("prepare-only build complete") # Genkernel mode, including custom kernel_config. Initrd always on. elif build_mode == "genkernel": if self._debug: self._logger.log("DEBUG: build_kernel(): starting emerge genkernel") exitstatus = self._portage.emerge("genkernel", self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("EmergeGenKernelError", 'fatal','build_kernel', "Could not emerge genkernel!") self._logger.log("Genkernel emerged. Beginning kernel compile.") # Null the genkernel_options genkernel_options = "" # If the uri for the kernel config is not null, then if kernel_config_uri != "": if self._debug: self._logger.log("DEBUG: build_kernel(): getting kernel config "+kernel_config_uri) GLIUtility.get_uri(kernel_config_uri, self._chroot_dir + "/var/tmp/kernel_config") genkernel_options = genkernel_options + " --kernel-config=/var/tmp/kernel_config" # Decide whether to use bootsplash or not if self._install_profile.get_kernel_bootsplash(): genkernel_options = genkernel_options + " --gensplash" else: genkernel_options = genkernel_options + " --no-gensplash" # Run genkernel in chroot #print "genkernel all " + genkernel_options self.notify_frontend("progress", (0, _("Compiling kernel. Please be patient!"))) if self._debug: self._logger.log("DEBUG: build_kernel(): running: genkernel all " + genkernel_options + " in chroot.") exitstatus = GLIUtility.spawn("genkernel all " + genkernel_options, chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build kernel!") # exitstatus = self._emerge("hotplug") # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("EmergeHotplugError", 'fatal','build_kernel', "Could not emerge hotplug!") # self._logger.log("Hotplug emerged.") # exitstatus = self._portage.emerge("coldplug", self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("EmergeColdplugError", 'fatal','build_kernel', "Could not emerge coldplug!") self._logger.log("Coldplug emerged. Now they should be added to the default runlevel.") # self._add_to_runlevel("hotplug") # self._add_to_runlevel("coldplug", runlevel="boot") if self._install_profile.get_kernel_bootsplash(): self._logger.log("Bootsplash enabled...emerging necessary packages") self._portage.emerge(["splashutils", "splash-themes-livecd"], self._install_profile.get_grp_install()) self._logger.log("Genkernel complete.") elif build_mode == "custom": #CUSTOM CONFIG kernel_compile_script += " && make && make modules && make modules_install" #Ok now that it's built, copy it to /boot/kernel-* for bootloader code to find it if self._architecture_name == "x86": kernel_compile_script += " && cp /usr/src/linux/arch/i386/boot/bzImage /boot/kernel-custom\n" elif self._architecture_name == "amd64": kernel_compile_script += " && cp /usr/src/linux/arch/x86_64/boot/bzImage /boot/kernel-custom\n" elif self._architecture_name == "ppc": kernel_compile_script += " && cp /usr/src/linux/vmlinux /boot/kernel-custom\n" if self._debug: self._logger.log("DEBUG: build_kernel(): writing custom kernel script: "+kernel_compile_script) f = open(self._chroot_dir+"/var/tmp/kernel_script", 'w') f.writelines(kernel_compile_script) f.close() #Build the kernel if self._debug: self._logger.log("DEBUG: build_kernel(): running: chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script") exitstatus1 = GLIUtility.spawn("chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script") if self._debug: self._logger.log("DEBUG: build_kernel(): running: /var/tmp/kernel_script in chroot") exitstatus2 = GLIUtility.spawn("/var/tmp/kernel_script", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus1): raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build custom kernel! died on chmod.") if not GLIUtility.exitsuccess(exitstatus2): raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build custom kernel! died on running of kernel script.") #i'm sure i'm forgetting something here. #cleanup exitstatus = GLIUtility.spawn("rm -f "+self._chroot_dir+"/var/tmp/kernel_script "+self._chroot_dir+"/var/tmp/kernel_config") #it's not important if this fails. if self._install_profile.get_kernel_bootsplash(): self._logger.log("Bootsplash enabled...emerging necessary packages") self._portage.emerge(["splashutils", "splash-themes-livecd"], self._install_profile.get_grp_install()) self._logger.log("Custom kernel complete") ## # Installs and starts up distccd if the user has it set, so that it will get used for the rest of the install def install_distcc(self): if self._install_profile.get_install_distcc(): if self._debug: self._logger.log("DEBUG: install_distcc(): we ARE installing distcc") if self._debug: self._logger.log("DEBUG: install_distcc(): running: USE='-*' emerge --nodeps sys-devel/distcc in chroot.") exitstatus = GLIUtility.spawn("USE='-*' emerge --nodeps sys-devel/distcc", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) if not GLIUtility.exitsuccess(exitstatus): self._logger.log("ERROR! : Could not emerge distcc!") else: self._logger.log("distcc emerged.") ## # Installs mail MTA. Does not put into runlevel, as this is not simple with MTAs. def install_mta(self): # Get MTA info mta_pkg = self._install_profile.get_mta_pkg() if mta_pkg: # Emerge MTA if self._debug: self._logger.log("DEBUG: install_mta(): installing mta: "+mta_pkg) exitstatus = self._portage.emerge(mta_pkg, self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("MTAError", 'fatal','install_mta', "Could not emerge " + mta_pkg + "!") self._logger.log("MTA installed: "+mta_pkg) else: installpackages = self._install_profile.get_install_packages() if installpackages: for pkg in installpackages: if pkg in ['esmtp', 'exim', 'msmtp', 'nbsmtp', 'nullmailer', 'sendmail', 'ssmtp', 'xmail']: self._logger.log("Found an mta in the package list: "+pkg+". Installing early.") exitstatus = self._portage.emerge(pkg, self._install_profile.get_grp_install()) self._logger.log("MTA installed.") break # We only want to install one ## # Installs and sets up logging daemon on the new system. adds to runlevel too. def install_logging_daemon(self): # Get loggin daemon info logging_daemon_pkg = self._install_profile.get_logging_daemon_pkg() if logging_daemon_pkg: # Emerge Logging Daemon if self._debug: self._logger.log("DEBUG: install_logging_daemon: emerging "+logging_daemon_pkg) exitstatus = self._portage.emerge(logging_daemon_pkg, self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("LoggingDaemonError", 'fatal','install_logging_daemon', "Could not emerge " + logging_daemon_pkg + "!") # Add Logging Daemon to default runlevel # After we find the name of it's initscript # This current code is a hack, and should be better. initscript = logging_daemon_pkg[(logging_daemon_pkg.find('/')+1):] if self._debug: self._logger.log("DEBUG: install_logging_daemon: adding "+initscript+" to runlevel") self._add_to_runlevel(initscript) self._logger.log("Logging daemon installed: "+logging_daemon_pkg) ## # Installs and sets up cron package. def install_cron_daemon(self): # Get cron daemon info cron_daemon_pkg = self._install_profile.get_cron_daemon_pkg() if cron_daemon_pkg: if cron_daemon_pkg in ("none", "None"): self._logger.log("Skipping installation of cron daemon") else: # Emerge Cron Daemon if self._debug: self._logger.log("DEBUG: install_cron_daemon: emerging "+cron_daemon_pkg) exitstatus = self._portage.emerge(cron_daemon_pkg, self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # raise GLIException("CronDaemonError", 'fatal', 'install_cron_daemon', "Could not emerge " + cron_daemon_pkg + "!") # Add Cron Daemon to default runlevel # After we find the name of it's initscript # This current code is a hack, and should be better. initscript = cron_daemon_pkg[(cron_daemon_pkg.find('/')+1):] if self._debug: self._logger.log("DEBUG: install_cron_daemon: adding "+initscript+" to runlevel") self._add_to_runlevel(initscript) # If the Cron Daemon is not vixie-cron, run crontab if cron_daemon_pkg != "vixie-cron": if self._debug: self._logger.log("DEBUG: install_cron_daemon: running: crontab /etc/crontab in chroot.") exitstatus = GLIUtility.spawn("crontab /etc/crontab", chroot=self._chroot_dir, display_on_tty8=True) if not GLIUtility.exitsuccess(exitstatus): raise GLIException("CronDaemonError", 'fatal', 'install_cron_daemon', "Failure making crontab!") self._logger.log("Cron daemon installed and configured: "+cron_daemon_pkg) ## # This will parse the partitions looking for types that require fstools and emerge them if found. def install_filesystem_tools(self): "Installs and sets up fstools" # Get the list of file system tools to be installed mounts = self._install_profile.get_mounts() # don't use an array, use a set instead filesystem_types = [] for mount in mounts: partition_type = mount['type'].lower() if mount['mountpoint'] and partition_type not in filesystem_types: filesystem_types.append(partition_type) package_list = [] for filesystem in filesystem_types: if filesystem == 'xfs': package_list.append('sys-fs/xfsprogs') elif filesystem == 'reiserfs': package_list.append('sys-fs/reiserfsprogs') elif filesystem == 'jfs': package_list.append('sys-fs/jfsutils') elif filesystem == 'ntfs': package_list.append('sys-fs/ntfsprogs') elif filesystem in ['fat','vfat', 'msdos', 'umsdos']: package_list.append('sys-fs/dosfstools') elif filesystem == 'hfs': # should check with the PPC guys on this package_list.append('sys-fs/hfsutils') package_list.append('sys-fs/hfsplusutils') #else: # should be code here for every FS type! failed_list = [] for package in package_list: if self._debug: self._logger.log("DEBUG: install_filesystem_tools(): emerging "+package) exitstatus = self._portage.emerge(package, self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # self._logger.log("ERROR! : Could not emerge "+package+"!") # failed_list.append(package) # else: self._logger.log("FileSystemTool "+package+" was emerged successfully.") # error checking is important! if len(failed_list) > 0: raise GLIException("InstallFileSystemToolsError", 'warning', 'install_filesystem_tools', "Could not emerge " + failed_list + "!") ## # Installs and sets up pcmcia-cs if selected in the profile IS THIS USED AT ALL???? def install_pcmcia_cs(self): if self._debug: self._logger.log("DEBUG: install_pcmcia_cs(): emerging pcmcia-cs") exitstatus = self._portage.emerge("pcmcia-cs", self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # self._logger.log("ERROR! : Could not emerge pcmcia-cs!") # Add pcmcia-cs to the default runlevel # else: self._add_to_runlevel('pcmcia') self._logger.log("PCMCIA_CS emerged and configured.") ## # Sets up the network for the first boot def setup_network_post(self): if self._debug: self._logger.log("DEBUG: setup_network_post(): starting network configuration") # Get hostname, domainname and nisdomainname hostname = self._install_profile.get_hostname() domainname = self._install_profile.get_domainname() nisdomainname = self._install_profile.get_nisdomainname() # Write the hostname to the hostname file #open(self._chroot_dir + "/etc/hostname", "w").write(hostname + "\n") self._edit_config(self._chroot_dir + "/etc/conf.d/hostname", {"HOSTNAME": hostname}) # Write the domainname to the nisdomainname file if domainname: #open(self._chroot_dir + "/etc/dnsdomainname", "w").write(domainname + "\n") self._edit_config(self._chroot_dir + "/etc/conf.d/domainname", {"DNSDOMAIN": domainname}) self._add_to_runlevel("domainname") # Write the nisdomainname to the nisdomainname file if nisdomainname: #open(self._chroot_dir + "/etc/nisdomainname", "w").write(nisdomainname + "\n") self._edit_config(self._chroot_dir + "/etc/conf.d/domainname", {"NISDOMAIN": nisdomainname}) self._add_to_runlevel("domainname") # # EDIT THE /ETC/HOSTS FILE # # The address we are editing is 127.0.0.1 hosts_ip = "127.0.0.1" # If the hostname is localhost if hostname == "localhost": # If a domainname is set if domainname: hosts_line = hostname + "." + domainname + "\t" + hostname else: hosts_line = hostname # If the hostname is not localhost else: # If a domainname is set if domainname: hosts_line = hostname + "." + domainname + "\t" + hostname + "\tlocalhost" else: hosts_line = "localhost\t" + hostname # Write to file self._edit_config(self._chroot_dir + "/etc/hosts", {hosts_ip: hosts_line}, delimeter='\t', quotes_around_value=False) # # SET DEFAULT GATEWAY # # Get default gateway default_gateway = self._install_profile.get_default_gateway() # If the default gateway exists, add it if default_gateway: default_gateway_string = '( "default via ' + default_gateway[1] + '" )' if self._debug: self._logger.log("DEBUG: setup_network_post(): found gateway. adding to confing. "+default_gateway_string) self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"routes_"+default_gateway[0]: default_gateway_string}, quotes_around_value=False) # # SET RESOLV INFO # # Get dns servers dns_servers = self._install_profile.get_dns_servers() # Clear the list resolv_output = [] # If dns servers are set if dns_servers: # Parse each dns server for dns_server in dns_servers: # Add the server to the output resolv_output.append("nameserver " + dns_server +"\n") # If the domainname is set, then also output it if domainname: resolv_output.append("search " + domainname + "\n") # Output to file if self._debug: self._logger.log("DEBUG: setup_network_post(): writing resolv.conf with contents: " + str(resolv_output)) resolve_conf = open(self._chroot_dir + "/etc/resolv.conf", "w") resolve_conf.writelines(resolv_output) resolve_conf.close() # # PARSE INTERFACES # # Fetch interfaces interfaces = self._install_profile.get_network_interfaces() emerge_dhcp = False # Parse each interface for interface in interfaces.keys(): if self._debug: self._logger.log("DEBUG: setup_network_post(): configuring interface: "+ interface) # Set what kind of interface it is interface_type = interface[:3] # Check to see if there is a startup script for this interface, if there isn't link to the proper script try: os.stat(self._chroot_dir + "/etc/init.d/net." + interface) except: if self._debug: self._logger.log("DEBUG: setup_network_post(): /etc/init.d/net." + interface + " didn't exist, symlinking it.") os.symlink("net." + interface_type + "0", self._chroot_dir + "/etc/init.d/net." + interface) # If we are going to load the network at boot... #if interfaces[interface][2]: #THIS FEATURE NO LONGER EXISTS # Add it to the default runlevel if self._debug: self._logger.log("DEBUG: setup_network_post(): adding net."+interface+" to runlevel.") self._add_to_runlevel("net."+interface) # moved a bit <-- for indentation # # ETHERNET # if interface_type == "eth": # # STATIC IP # # If the post-install device info is not None, then it is a static ip addy if interfaces[interface][0] != "dhcp": ip = interfaces[interface][0] broadcast = interfaces[interface][1] netmask = interfaces[interface][2] # aliases = interfaces[interface][1][3] # alias_ips = [] # alias_broadcasts = [] # alias_netmasks = [] # Write the static ip config to /etc/conf.d/net self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"config_" + interface: '( "' + ip + " netmask " + netmask + " broadcast " + broadcast + '" )'}, quotes_around_value=False) # If aliases are set # if aliases: # Parse aliases to format alias info # for alias in aliases: # alias_ips.append(alias[0]) # alias_broadcasts.append(alias[1]) # alias_netmasks.append(allias[2]) # Once the alias info has been gathered, then write it out # Alias ips first # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "alias_" + interface, string.join(alias_ips)) # Alias broadcasts next # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "broadcast_" + interface, string.join(alias_broadcasts)) # Alias netmasks last # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "netmask_" + interface, string.join(alias_netmasks)) # # DHCP IP # else: dhcpcd_options = interfaces[interface][1] if not dhcpcd_options: dhcpcd_options = "" self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"config_" + interface: '( "dhcp" )'}, quotes_around_value=False) if dhcpcd_options: self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"dhcpcd_" + interface: dhcpcd_options}) emerge_dhcp = True if emerge_dhcp: if self._debug: self._logger.log("DEBUG: setup_network_post(): emerging dhcpcd.") exitstatus = self._portage.emerge("dhcpcd", self._install_profile.get_grp_install()) # if not GLIUtility.exitsuccess(exitstatus): # self._logger.log("ERROR! : Could not emerge dhcpcd!") # else: self._logger.log("dhcpcd emerged.") ## # This is a stub function to be done by the individual arch. I don't think it's even needed here. # but it's nice having it just incase. def install_bootloader(self): "THIS FUNCTION MUST BE DONE BY THE INDIVIDUAL ARCH" pass ## # Sets up the new users for the system def set_users(self): # Loop for each user for user in self._install_profile.get_users(): # Get values from the tuple username = user[0] password_hash = user[1] groups = user[2] shell = user[3] home_dir = user[4] uid = user[5] comment = user[6] options = [ "-m", "-p '" + password_hash + "'" ] # If the groups are specified if groups: # If just one group is listed as a string, make it a list if groups == str: groups = [ groups ] # If only 1 group is listed if len(groups) == 1: options.append("-G " + groups[0]) # If there is more than one group elif len(groups) > 1: options.append('-G "' + string.join(groups, ",") + '"') # Attempt to add the group (will return success when group exists) for group in groups: if not group: continue # Add the user if self._debug: self._logger.log("DEBUG: set_users(): adding user to groups with (in chroot): "+'groupadd -f ' + group) exitstatus = GLIUtility.spawn('groupadd -f ' + group, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True, display_on_tty8=True) if not GLIUtility.exitsuccess(exitstatus): self._logger.log("ERROR! : Failure to add group " + group+" and it wasn't that the group already exists!") # If a shell is specified if shell: options.append("-s " + shell) # If a home dir is specified if home_dir: options.append("-d " + home_dir) # If a UID is specified if uid: options.append("-u " + str(uid)) # If a comment is specified if comment: options.append('-c "' + comment + '"') # Add the user if self._debug: self._logger.log("DEBUG: set_users(): adding user with (in chroot): "+'useradd ' + string.join(options) + ' ' + username) exitstatus = GLIUtility.spawn('useradd ' + string.join(options) + ' ' + username, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True, display_on_tty8=True) if not GLIUtility.exitsuccess(exitstatus): self._logger.log("ERROR! : Failure to add user " + username) # raise GLIException("AddUserError", 'warning', 'set_users', "Failure to add user " + username) else: self._logger.log("User " + username + " was added.") ## # Installs a list of packages specified in the profile. Will install any extra software! # In the future this function will lead to better things. It may even wipe your ass for you. def install_packages(self): installpackages = self._install_profile.get_install_packages() if installpackages: # pkglist = self._portage.get_deps(" ".join(installpackages)) # if self._debug: self._logger.log("install_packages(): pkglist is " + str(pkglist)) # for i, pkg in enumerate(pkglist): # if self._debug: self._logger.log("install_packages(): processing package " + pkg) # self.notify_frontend("progress", (float(i) / len(pkglist), "Emerging " + pkg + " (" + str(i) + "/" + str(len(pkglist)) + ")")) # if not self._portage.get_best_version_vdb("=" + pkg): # status = self._emerge("=" + pkg) # if not GLIUtility.exitsuccess(status): # raise GLIException("ExtraPackagesError", "fatal", "install_packages", "Could not emerge " + pkg + "!") # else: # try: # self._portage.copy_pkg_to_chroot(pkg) # except: # raise GLIException("ExtraPackagesError", "fatal", "install_packages", "Could not emerge " + pkg + "!") self._portage.emerge(installpackages, self._install_profile.get_grp_install()) if GLIUtility.is_file(self._chroot_dir + "/etc/X11"): # Copy the xorg.conf from the LiveCD if they installed xorg-x11 exitstatus = GLIUtility.spawn("cp /etc/X11/xorg.conf " + self._chroot_dir + "/etc/X11/xorg.conf") if not GLIUtility.exitsuccess(exitstatus): self._logger.log("Could NOT copy the xorg configuration from the livecd to the new system!") else: self._logger.log("xorg.conf copied to new system. X should be ready to roll!") if GLIUtility.is_file(self._chroot_dir + "/etc/X11/gdm/gdm.conf"): GLIUtility.spawn("cp -f /etc/X11/gdm/gdm.conf.old " + self._chroot_dir + "/etc/X11/gdm/gdm.conf") if GLIUtility.is_file(self._chroot_dir + "/etc/X11/gdm/custom.conf"): GLIUtility.spawn("cp -f /etc/X11/gdm/custom.conf.old " + self._chroot_dir + "/etc/X11/gdm/custom.conf") ## # Will set the list of services to runlevel default. This is a temporary solution! def set_services(self): services = self._install_profile.get_services() for service in services: if service: runlevel = "default" if "|" in service: service, runlevel = service.split("|", 1) self._add_to_runlevel(service, runlevel) ## # Will execute any arbritraially defined script here for post-install customization. def run_post_install_script(self): if self._install_profile.get_post_install_script_uri(): try: if self._debug: self._logger.log("DEBUG: run_post_install_script(): getting script: "+self._install_profile.get_post_install_script_uri()) GLIUtility.get_uri(self._install_profile.get_post_install_script_uri(), self._chroot_dir + "/var/tmp/post-install") if self._debug: self._logger.log("DEBUG: run_post_install_script(): running: chmod a+x /var/tmp/post-install && /var/tmp/post-install in chroot") GLIUtility.spawn("chmod a+x /var/tmp/post-install && /var/tmp/post-install", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True) except: raise GLIException("RunPostInstallScriptError", 'fatal', 'run_post_install_script', "Failed to retrieve and/or execute post-install script") ## # This function will handle the various cleanup tasks as well as unmounting the filesystems for reboot. def finishing_cleanup(self): #These are temporary until I come up with a nicer idea. #get rid of the compile_output file so the symlink doesn't get screwed up. #we copy the log over to the new system. install_logfile = LOGFILE try: if self._debug: self._logger.log("DEBUG: finishing_cleanup(): copying logfile over to new system's root.") shutil.copy(install_logfile, self._chroot_dir + install_logfile) except: if self._debug: self._logger.log("DEBUG: finishing_cleanup(): ERROR! could not copy logfile over to /root.") #Now we're done logging as far as the new system is concerned. GLIUtility.spawn("cp /tmp/installprofile.xml " + self._chroot_dir + "/root/installprofile.xml") #Unmount mounted fileystems in preparation for reboot #mounts = GLIUtility.spawn(r"mount | sed -e 's:^.\+ on \(.\+\) type .\+$:\1:' | grep -e '^" + self._chroot_dir + "' | sort -r", return_output=True)[1].split("\n") if self._debug: self._logger.log("DEBUG: mounted_devices is %s" % str(self._mounted_devices)) mounted_devices = self._mounted_devices mounted_devices.sort() mounted_devices.reverse() for mount in mounted_devices: if self._debug: self._logger.log("DEBUG: finishing_cleanup(): running: umount -l " + mount) ret = GLIUtility.spawn("umount -l " + self._chroot_dir + mount) if not GLIUtility.exitsuccess(ret): self._logger.log("ERROR! : Could not unmount mountpoint %s" % mount) # now turn off all swap as well. # we need to find the swap devices for swap_device in self._swap_devices: if self._debug: self._logger.log("DEBUG: finishing_cleanup(): running: swapoff "+swap_device) ret = GLIUtility.spawn("swapoff "+swap_device) if not GLIUtility.exitsuccess(ret): self._logger.log("ERROR! : Could not deactivate swap ("+swap_device+")!") GLIUtility.spawn("rm /tmp/compile_output.log && rm " + install_logfile) ## # This function should only be called in the event of an install failure. It performs # general cleanup to prepare the system for another installer run. def install_failed_cleanup(self): steps = len(self._mounted_devices) + len(self._swap_devices) + 2 if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): running: mv /tmp/compile_output.log /tmp/compile_output.log.failed") self.notify_frontend("progress", (float(1) / (steps), _("Moving compile output logfile"))) GLIUtility.spawn("cp " + self._compile_logfile + " " + self._compile_logfile + ".failed && rm " + self._compile_logfile) self.notify_frontend("progress", (float(2) / (steps), _("Moving install logfile"))) GLIUtility.spawn("mv " + LOGFILE + " " + LOGFILE + ".failed") cur_step = 2 if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): gathering mounts to unmount") #mounts = GLIUtility.spawn(r"mount | sed -e 's:^.\+ on \(.\+\) type .\+$:\1:' | grep -e '^" + self._chroot_dir + "' | sort -r", return_output=True)[1].split("\n") if self._debug: self._logger.log("DEBUG: mounted_devices is %s" % str(self._mounted_devices)) mounted_devices = self._mounted_devices mounted_devices.sort() mounted_devices.reverse() for mount in mounted_devices: cur_step += 1 self.notify_frontend("progress", (float(cur_step) / (steps), _("Unmounting ") + mount)) if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): running: umount -l " + mount) ret = GLIUtility.spawn("umount -l " + self._chroot_dir + mount) if not GLIUtility.exitsuccess(ret): self._logger.log("ERROR! : Could not unmount mountpoint %s" % mount) # now turn off all swap as well. # we need to find the swap devices for swap_device in self._swap_devices: cur_step += 1 self.notify_frontend("progress", (float(cur_step) / (steps), _("Deactivating swap on ") + swap_device)) if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): running: swapoff "+swap_device) ret = GLIUtility.spawn("swapoff "+swap_device) if not GLIUtility.exitsuccess(ret): self._logger.log("ERROR! : Could not deactivate swap ("+swap_device+")!")