From owner-p4-projects@FreeBSD.ORG Wed Aug 22 22:32:09 2007 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id BA1FB16A41A; Wed, 22 Aug 2007 22:32:09 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 4E91616A418 for ; Wed, 22 Aug 2007 22:32:09 +0000 (UTC) (envelope-from ivoras@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 3D19E13C457 for ; Wed, 22 Aug 2007 22:32:09 +0000 (UTC) (envelope-from ivoras@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id l7MMW8EH090880 for ; Wed, 22 Aug 2007 22:32:09 GMT (envelope-from ivoras@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id l7MMW8TN090877 for perforce@freebsd.org; Wed, 22 Aug 2007 22:32:08 GMT (envelope-from ivoras@FreeBSD.org) Date: Wed, 22 Aug 2007 22:32:08 GMT Message-Id: <200708222232.l7MMW8TN090877@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to ivoras@FreeBSD.org using -f From: Ivan Voras To: Perforce Change Reviews Cc: Subject: PERFORCE change 125568 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Aug 2007 22:32:10 -0000 http://perforce.freebsd.org/chv.cgi?CH=125568 Change 125568 by ivoras@ivoras_finstall on 2007/08/22 22:31:40 Started work on post-install system configuration Affected files ... .. //depot/projects/soc2007/ivoras_finstall/installer/finstall.py#13 edit .. //depot/projects/soc2007/ivoras_finstall/installer/glade/createuser.glade#1 add .. //depot/projects/soc2007/ivoras_finstall/installer/glade/hostroot.glade#1 add .. //depot/projects/soc2007/ivoras_finstall/installer/text/createuser.txt#1 add .. //depot/projects/soc2007/ivoras_finstall/installer/text/hostroot.txt#1 add .. //depot/projects/soc2007/ivoras_finstall/pybackend/freebsd.py#8 edit .. //depot/projects/soc2007/ivoras_finstall/pybackend/systoolengine.py#11 edit Differences ... ==== //depot/projects/soc2007/ivoras_finstall/installer/finstall.py#13 (text+ko) ==== @@ -23,7 +23,7 @@ # finstall Installer import os,sys,time -import logging +import logging, base64, random from types import MethodType from xmlrpclib import ServerProxy import gobject, gtk, gtk.gdk, gtk.glade @@ -45,7 +45,13 @@ { "tile" : "nparts" }, { "tile" : "ndefaultfs" }, { "tile" : "nverify" }, - { "tile" : "ninstall", "glade" : "installprogress.glade" } + { "tile" : "ninstall", "glade" : "installprogress.glade" }, + { "tile" : "hostroot" }, + { "tile" : "createuser" }, + { "tile" : "netconf" }, + { "tile" : "x11conf" }, + { "tile" : "soundconf" }, + { "tile" : "nfinish" } ] @@ -93,7 +99,7 @@ if "on_load" in self.tile_handlers: try: if not self.tile_handlers["on_load"](): - logging.error("On_Load refused by %s, but it's not implemented" % tile_name) + logging.error("On_Load refused by %s, but it's ignored (not implemented yet)" % tile_name) except: logging.exception("Error executing on_load handler for %s" % tile_name) @@ -317,8 +323,6 @@ /var /home and a symlink from /usr/ports to /usr/local/ports. - We'll not use the traditional BSD layout of bsdlabels (a=root, b=swap), except - for the unfortunately special "c" label. """ if self["radio_ufs"].get_active(): @@ -359,7 +363,7 @@ # Calculate the partitioning scheme var_size = min(2048, int(self.trackdata["drive_size"] * 0.1)) - usr_size = min(15*1024, int(self.trackdata["drive_size"] * 0.3)) + usr_size = min(10*1024, int(self.trackdata["drive_size"] * 0.5)) if fs in ("UFS+SU", "UFS+GJ", "Ext2"): # "Normal" file systems, unix-like @@ -527,6 +531,7 @@ self.next_progress_advert() self.trackdata["part_job"] = self.server.StartPartitionJob(self.trackdata["new_parts"]) gobject.timeout_add(500, self.part_progress) + return True def add_install_list(self, msg): @@ -559,7 +564,7 @@ self._show_message(result[-1000:], "Error during backend function (part_job)") return False self["progressbar"].set_fraction(float(pcnt) / 100) - if pcnt == 100: + if pcnt == 100 and self.server.QueryIsJobFinished(self.trackdata["part_job"]): result = self.server.QueryJobResult(self.trackdata["part_job"]) self.server.DismantleJob(self.trackdata["part_job"]) del self.trackdata["part_job"] @@ -567,7 +572,7 @@ self.trackdata["install_job"] = self.server.StartInstallJob(self.trackdata["new_parts"]) self["progressbar"].set_fraction(0) self.add_install_list("Installing FreeBSD base system...") - gobject.timeout_add(1000, self.install_progress) + gobject.timeout_add(2000, self.install_progress) self.next_progress_advert() return False return True @@ -582,13 +587,119 @@ self._show_message(result[-1000:], "Error during backend function (install_job)") return False self["progressbar"].set_fraction(float(pcnt) / 100) - if pcnt > 0 and pcnt % 15 == 0: + if pcnt > 0 and pcnt % 25 == 0: + self.next_progress_advert() + if pcnt == 100 and self.server.QueryIsJobFinished(self.trackdata["install_job"]): + self.server.DismantleJob(self.trackdata["install_job"]) + del self.trackdata["install_job"] + # Start a new job - install packages + self.trackdata["pkginstall_job"] = self.server.StartPkgInstallJob(self.trackdata["new_parts"]) + self["progressbar"].set_fraction(0) + self.add_install_list("Installing packages...") + gobject.timeout_add(2000, self.pkginstall_progress) + self.next_progress_advert() + return False + return True + + + def pkginstall_progress(self): + try: + pcnt = self.server.QueryJobProgress(self.trackdata["pkginstall_job"]) + except Exception, e: + code, result = self.server.QueryJobError(self.trackdata["pkginstall_job"]) + print code, result + self._show_message(result[-1000:], "Error during backend function (install_job)") + return False + self["progressbar"].set_fraction(float(pcnt) / 100) + if pcnt > 0 and pcnt % 25 == 0: self.next_progress_advert() - if pcnt == 100: + if pcnt == 100 and self.server.QueryIsJobFinished(self.trackdata["pkginstall_job"]): + self.server.DismantleJob(self.trackdata["pkginstall_job"]) + del self.trackdata["pkginstall_job"] + logging.info("PkgInstall finished") + self._load_tile("hostusername") + return False + return True + + + def hostroot_on_load(self): + self._load_label(self["label2"], "hostroot.txt") + return True + + + def on_button_generate_clicked(self, evt): + seq = "" + for x in xrange(8): + seq += chr(random.randint(0, 255)) + str = base64.b64encode(seq) + password = str[0:8] + self._show_message("Your generated password is:\n\n"+password+"\n\nPLEASE REMEMBER IT. DO NOT WRITE DOWN YOUR PASSWORDS.", "Generated random password") + self["entry_password"].set_text(password) + self["entry_confirm_password"].set_text(password) + + + def hostroot_on_next(self): + if self["entry_password"].get_text() != self["entry_confirm_password"].get_text(): + self._show_message("Passwords do not match! Please enter the same password\nin the Password and Confirm password fields", "Password mismatch") + return False + code, result = self.server.SetHostName(self["entry_hostname"].get_text()) + if code != 0: + self._show_message(result, "Error") + return False + code, result = self.server.SetUserPassword("root", self["entry_password"].get_text()) + return True + + + def createuser_on_load(self): + self._load_label(self["label2"], "createuser.txt") + return True + + + def createuser_on_next(self): + if self["entry_password"].get_text() != self["entry_confirm_password"].get_text(): + self._show_message("Passwords do not match! Please enter the same password\nin the Password and Confirm password fields", "Password mismatch") + return False + username = self["entry_username"].get_text() + if username.find(" ") == -1: + self._show_message("Invalid username. No whitespace, please.", "Error") + return False + code, result = self.server.AddUser(username, self["entry_user"].get_text(), self["entry_password"].get_text()) + if code != 0: + self._show_message(result, "Error") return False return True + def netconf_on_load(self): + pass + + + def netconf_on_next(self): + pass + + + def x11conf_on_load(self): + pass + + + def x11conf_on_next(self): + pass + + + def soundconf_on_load(self): + pass + + + def soundconf_on_next(self): + pass + + + def nfinish_on_load(self): + pass + + + def nfinish_on_next(self): + pass my_dir = os.path.split(sys.argv[0])[0] ==== //depot/projects/soc2007/ivoras_finstall/pybackend/freebsd.py#8 (text+ko) ==== @@ -26,6 +26,8 @@ import re import xmldict +live_root = "/" + cmd_sysctl = "/sbin/sysctl" cmd_geom = "/sbin/geom" cmd_mount = "/sbin/mount" @@ -39,6 +41,7 @@ cmd_kldload = "/sbin/kldload" cmd_mke2fs = "/usr/local/sbin/mke2fs" cmd_boot0cfg = "/usr/sbin/boot0cfg" +cmd_pw = "/usr/sbin/pw" file_dmesg = "/var/run/dmesg.boot" @@ -62,15 +65,18 @@ def get_cmd_output(name): + """Executes a command and returns its output.""" return os.popen(name).read().strip() def get_dmesg(): + """Return the contents of the dmesg file.""" global file_dmesg return [x.strip() for x in file(file_dmesg, "r").readlines()] def get_geom_xml(): + """Returns XMLDict object containing the GEOM XML tree.""" return xmldict.buildxmldict(get_sysctl("kern.geom.confxml", True)) @@ -102,6 +108,10 @@ def guess_fs_last_mount(dev, fs_type="UFS"): + """ + Guess where the file system (device) was last mounted on in the + file system tree. + """ global cmd_file if not fs_type in ("UFS2", "UFS"): # For now, we only know how to handle UFS return None @@ -129,6 +139,7 @@ def geom_sector_size(dev): + """Find out sector size of the device.""" xml = get_geom_xml() for cls in xml["mesh"]["class"]: if "geom" in cls: @@ -144,9 +155,13 @@ """ Convenience function that executes the command specified by the cmd argument and returns its exit status and its output from both stdout - and stderr. Optionally, a simple string can be send to the process' - stdin. + and stderr. Optionally, a simple string can be send to the process' stdin. + If the cmd starts with "/" (as it should), it will be prepended with + global variable live_root, which should point to a FreeBSD live tree. """ + global live_root + if cmd.startswith("/") and live_root != "/": + cmd = "%s/%d" % (live_root, cmd) logging.info("Executing %s" %cmd) p = popen2.Popen4(cmd) if input != None: @@ -163,6 +178,9 @@ def create_fdisk_partition(dev, index, offset_sectors, size_sectors, flags=[]): + """ + Create a fdisk partition on the device. + """ global cmd_fdisk, cmd_boot0cfg temp_fname, f = make_temp_script() # Create the script @@ -199,6 +217,7 @@ def create_bsdlabel_partition(dev, index, offset_sectors, size_sectors, flags=[], fs_type="4.2BSD"): + """Create a bsdlabel partition on the device.""" global cmd_bsdlabel, bsdlabel_map output = "" if "init" in flags: @@ -238,7 +257,7 @@ f.write("\n") f.close() code, output = exec_cmd("%s -R %s %s" % (cmd_bsdlabel, dev, script_fname)) - #os.unlink(script_fname) + os.unlink(script_fname) if code != 0: return (code, output, None) # Verify the result & fetch the last_offset @@ -286,6 +305,7 @@ def mount(dev, mount, fs_type, mkdir_mount=True): + """Mounts a file system / device on a directory.""" global cmd_mount if not dev.startswith("/dev/"): dev = "/dev/"+dev @@ -303,9 +323,56 @@ def umount(mount): + """Umounts a mount point""" global cmd_umount return exec_cmd("%s %s" % (cmd_umount, mount)) + +def fstab_line(dev, mount, fs_type): + """Generates a line for the fstab file""" + if fs_type == "ZFS": + return None + if fs_type == "swap": + mount = "none" + opt = "sw" + dump = fpass = 0 + else: + opt = "rw" + if mount == "/": + dump = fpass = 1 + else: + dump = fpass = 2 + if fs_type in ("UFS", "UFS+SU"): + fs_type = "ufs" + elif fs_type == "UFS+GJ": + fs_type = "ufs" + dev = dev + ".journal" + opt += ",async" + elif fs_type == "Ext2": + fs_type = "ext2fs" + + return "%s\t\%s\t%s\t%s\t%s\t%s" % (dev, mount, fs_type, opt, dump, fpass) + + +def chpasswd(user_name, password, etc_dir="/etc"): + """Changes the user's password. The user must exist.""" + global cmd_pw + return exec_cmd("%s -V %s usermod %s -h 0" % (cmd_pw, etc_dir, user_name), password) + + +def useradd(user_name, user, password, shell, groups=[], etc_dir="/etc"): + """Adds a user and sets his default shell and member groups""" + global cmd_pw + guess_root = os.path.split(etc_dir)[0] + if guess_root == "/": + guess_root = "" + guess_home = "%s/home" % guess_root + if len(groups) != 0: + return exec_cmd("%s -V %s useradd %s -m -s %s -G %s -c \"%s\" -b %s" % (cmd_pw, etc_dir, user_name, shell, ",".join(groups), user, guess_home)) + else: + return exec_cmd("%s -V %s useradd %s -m -s %s -c \"%s\" -b %s" % (cmd_pw, etc_dir, user_name, shell, user, guess_home)) + + if __name__ == "__main__": xml = get_geom_xml() for cls in xml["mesh"]["class"]: ==== //depot/projects/soc2007/ivoras_finstall/pybackend/systoolengine.py#11 (text+ko) ==== @@ -22,7 +22,7 @@ # SysToolD Engine: implements SysToolD XML-RPC methods import os, sys, shutil -import re +import re, time import logging, warnings from threading import Thread, Lock from StringIO import StringIO @@ -33,8 +33,10 @@ def logexception(func): - """Method call decorator that routes exceptions to the logger - before passing them on""" + """ + Method call decorator that routes exceptions to the logger + before passing them on. + """ def call(*args, **kwds): try: return func(*args, **kwds) @@ -45,8 +47,10 @@ def tolist(e): - """Coalesce argument to list (if the argument is not list, - wrap it in a list of one element).""" + """ + Coalesce argument to list (if the argument is not list, + wrap it in a list of one element). + """ if type(e) == type([]): return e else: @@ -54,11 +58,17 @@ class SysToolJobException(Exception): + """Error in a SysToolJob""" pass class SysToolJob(Thread): - """A generic asynchronous SysTool job""" + """A generic asynchronous SysTool job, with common methods""" + + INSTALL_ROOT = "/dist" + BASE_MAPTREE_FILE = "/install/base.maptree" + USRLOCAL_MAPTREE_FILE = "/install/usrlocal.maptree" + VARDBPKG_MAPTREE_FILE = "/install/vardbpkg.maptree" def __init__(self): Thread.__init__(self) @@ -70,7 +80,93 @@ def _calc_percent(self, i, total): return int((float(i+1) / total) * 100) + def MountPartitions(self, part_spec, install_root): + """ + Mount partitions from part_spec. Returnes a (code, result) tuple. + """ + for part in part_spec: + if part["fs"] == None or part["fs"] == "swap": + continue + if part["mount"] == "/": + mount = install_root + else: + mount = "%s/%s" % (install_root, part["mount"]) + code, result = freebsd.mount(part["name"], mount, part["fs"]) + if code != 0: + return (code, result) + return (0, None) + def UmountPartitions(self, part_spec, install_root, force=False): + """ + Unmount partitions from part_spec. If force parameter is True, errors + from umount are ignored, otherwise, a (code,result) pair is returned. + """ + for part in reversed(part_spec): + if part["fs"] == None or part["fs"] == "swap": + continue + if part["mount"] == "/": + mount = install_root + else: + mount = "%s/%s" % (install_root, part["mount"]) + code, result = freebsd.umount(mount) + if code != 0 and not force: + return (code, result) + return (0, None) + + def InstallFromMapTree(self, maptree_filename, install_root, from_root): + """ + The method will process the records in maptree and copy the files found + there from from_root to install_root. Since this operation normally takes + a long time, the method updates self.percent_complete. + """ + if from_root == "/": + from_root = "" + maptree_size = os.path.getsize(maptree_filename) + f = file(maptree_filename, "r") + for line in f: + self.percent_complete = self._calc_percent(f.tell(), maptree_size) + rec = line.strip().split("|") + rec_file = rec[-1] + dest_file = "%s/%s" % (install_root, rec_file) + src_file = "%s/%s" % (from_root, rec_file) + if rec[0] == "F": + if not os.path.exists(src_file): + logging.info("File not found: "+src_file) + continue + logging.info("copy %s to %s" % (src_file, dest_file)) + shutil.copyfile(src_file, dest_file) + shutil.copystat(src_file, dest_file) + elif rec[0] == "D": + if not os.path.exists(dest_file): + os.mkdir(dest_file) + elif rec[0] == "H": + if os.path.exists(dest_file): + os.unlink(dest_file) + try: + os.link("%s/%s" % (install_root, rec[1]), dest_file) + except: + logging.error("Cannot hardlink '%s/%s' to '%s'" % (from_root, rec[1], dest_file)) + try: + shutil.copyfile("/%s" % rec[1], dest_file) + except: + logging.error("Cannot replace hardlink from '%s/%s' to '%s' with file copy" % (from_root, rec[1], dest_file)) + elif rec[0] == "L": + # symlinks are a special case - they don't need to exist and they can be + # embarrasingly relative + if os.path.exists(dest_file): + if os.path.islink(dest_file): + os.unlink(dest_file) + os.symlink("%s" % rec[1], dest_file) + else: + os.symlink("%s" % rec[1], dest_file) + else: + self.error = 1 + self.result = "Unknown record type: "+line + break + f.close() + + + class PartitionJob(SysToolJob): """ A partitioning SysTool job. This one accept a list of partitions @@ -163,6 +259,15 @@ break if self.result == None: self.result = buf.getvalue() + if self.error == None: + # mount partitions now + install_root = self.INSTALL_ROOT + if not os.path.exists(install_root): + os.mkdir(install_root) + code, result = self.MountPartitions(self.part_spec, install_root) + if code != 0: + self.error = code + self.result = result self.finished = True del self.part_spec @@ -170,104 +275,113 @@ class InstallJob(SysToolJob): """ The install job. The job progress is: - - Mount partitions to a temporary tree - - Parse mtree record and use it as a list of files to install + - Mount partitions from part_spec to the temporary tree + - Parse maptree record and use it as a list of files to install + - Generate /etc/fstab from the part_spec + """ + def __init__(self, part_spec): + SysToolJob.__init__(self) + self.part_spec = part_spec + + def run(self): + install_root = self.INSTALL_ROOT + maptree_filename = self.BASE_MAPTREE_FILE + if not os.path.exists(maptree_filename): + self.error = 1 + self.result = "No "+maptree_filename + self.finished = True + return + try: + self.InstallFromMapTree(maptree_filename, install_root, "/") + except Exception, e: + self.error = 1 + self.result = str(e) + if self.error == None: + # create fstab + f = file("%s/etc/fstab" % install_root, "w+") + f.write("# Generated by finstall on %s\n" % time.strftime("%c")) + f.write("# Device\tMountpoint\tFStype\tOptions\tDump\tPass\n") + for part in self.part_spec: + if part["mount"] != None: + line = freebsd.fstab_line(part["name"], part["mount"], part["fs"]) + if line != None: + f.write("%s\n" % line) + f.close() + + self.finished = True + + +class PkgInstallJob(SysToolJob): + """ + This job simply copies /usr/local and /var/pkg trees to the install + destination tree. Thus, it's not suitable for all types of packages (those + with post-install scripts that do non-trivial stuff) but will work ok + for most. + The main benefit of this is that it's way faster than going through + pkg_create -b + pkg_install sequence. """ def __init__(self, part_spec): SysToolJob.__init__(self) self.part_spec = part_spec def run(self): - install_root = "/dist" - if not os.path.exists(install_root): - os.mkdir(install_root) - buf = StringIO() - # mount partitions - for part in self.part_spec: - if part["fs"] == None or part["fs"] == "swap": - continue - if part["mount"] == "/": - mount = install_root - else: - mount = "%s/%s" % (install_root, part["mount"]) - code, result = freebsd.mount(part["name"], mount, part["fs"]) - buf.write(result) - if code != 0: - self.error = code - self.result = buf.getvalue() - self.finished = True - return - maptree_filename = "/maptree.txt" + install_root = self.INSTALL_ROOT + maptree_filename = self.VARDBPKG_MAPTREE_FILE + if not os.path.exists(maptree_filename): + self.error = 1 + self.result = "No "+maptree_filename + self.finished = True + return + try: + self.InstallFromMapTree(maptree_filename, "%s/var/db/pkg" % install_root, "/var/db/pkg") + except Exception, e: + self.error = 1 + self.result = str(e) + self.finished = True + return + maptree_filename = self.USRLOCAL_MAPTREE_FILE if not os.path.exists(maptree_filename): self.error = 1 self.result = "No "+maptree_filename self.finished = True return - maptree_size = os.path.getsize(maptree_filename) - f = file(maptree_filename, "r") - for line in f: - self.percent_complete = self._calc_percent(f.tell(), maptree_size) - rec = line.strip().split("|") - rec_file = rec[-1] - dest_file = "%s/%s" % (install_root, rec_file) - src_file = "/%s" % rec_file - if rec[0] == "F": - if not os.path.exists(src_file): - logging.info("File not found: "+src_file) - continue - shutil.copyfile(src_file, dest_file) - shutil.copystat(src_file, dest_file) - logging.info("copy %s to %s" % (src_file, dest_file)) - elif rec[0] == "D": - if not os.path.exists(dest_file): - os.mkdir(dest_file) - elif rec[0] == "H": - if os.path.exists(dest_file): - os.unlink(dest_file) - try: - os.link("%s/%s" % (install_root, rec[1]), dest_file) - except: - logging.error("Cannot hardlink '/%s' to '%s'" % (rec[1], dest_file)) - try: - shutil.copyfile("/%s" % rec[1], dest_file) - except: - logging.error("Cannot replace hardlink from '/%s' to '%s' with file copy" % (rec[1], dest_file)) - elif rec[0] == "L": - if os.path.exists(dest_file): - if os.path.islink(dest_file): - os.unlink(dest_file) - os.symlink("/%s" % rec[1], dest_file) - else: - os.symlink("/%s" % rec[1], dest_file) - else: - self.error = 1 - self.result = "Unknown record type: "+line - break - for part in reversed(self.part_spec): - if part["fs"] == None or part["fs"] == "swap": - continue - if part["mount"] == "/": - mount = install_root - else: - mount = "%s/%s" % (install_root, part["mount"]) - code, result = freebsd.umount(mount) - if code != 0: - self.error = 1 - if self.result != None: - self.result += "\n"+result - else: - self.result = result + try: + self.InstallFromMapTree(maptree_filename, "%s/usr/local" % install_root, "/usr/local") + except Exception, e: + self.error = 1 + self.result = str(e) + self.finished = True + return + self.finished = True + + +class PostInstallJob(SysToolJob): + """ + This job finishes the installation - unmounts the temporary tree, etc. + """ + def __init__(self, part_spec, force): + SysToolJob.__init__(self) + self.part_spec = part_spec + self.force = force + + def run(self): + code, result = self.UmountPartitions(self.part_spec, self.INSTALL_ROOT, force) + if code != 0: + self.error = code + self.result = result self.finished = True - + class SysToolEngine: def __init__(self): - self.root_dest = "" # Config file / "new" root, sans final slash + self.root_dest = "" # "new" root, sans final slash self.root_live = "" # Live file system root (for binaries!) self.job_list = [] self.job_list_lock = Lock() warnings.simplefilter('ignore', RuntimeWarning) + self.SetDestRoot("/dist") + self.SetLiveRoot("/") def GetId(self): @@ -281,35 +395,44 @@ def SetDestRoot(self, root_dir): - """Sets internal root directory for installation and config tasks - (so Get/SetConf can be used with alternative root)""" + """ + Sets internal root directory for installation and config tasks + (so Get/SetConf can be used with alternative root) + """ self.root_conf = root_dir + SysToolJob.INSTALL_ROOT = root_dir return True def SetLiveRoot(self, root_dir): """Notifies SysTool Engine where to expect utility binaries.""" self.root_live = root_dir + freebsd.live_root = root_dir return True - def GetConf(self, name): + def GetConf(self, name, default=None): """Returns configuration variable from /etc/rc.conf""" cf = ConfFile("%s/etc/rc.conf" % self.root_dest) - val = cf.GetConf(name, None) - return val + return cf.GetConf(name, default) def SetConf(self, name, value, description): """Sets configuration variable to /etc/rc.conf""" cf = ConfFile("%s/etc/rc.conf" % self.root_dest) - cf.SetConf(name, value, description) - return True + return cf.SetConf(name, value, description) + + + @logexception + def GetShells(self): + """Returns a list of acceptable shells""" + return [x.strip() for x in file("%s/etc/shells" % self.root_dest, "r") if len(x) > 1 and x[0] == "/"] @logexception def GetDrives(self): - """Returns information on drives the kernel knows about. + """ + Returns information on drives the kernel knows about. Examines "kern.drives" sysctl. This is NOT the list of valid GEOM leaves which can be mounted, but a list of found hardware. @@ -323,7 +446,8 @@ "name": "ACME MegaDrive", "mediasize": 300000 # (MB) } - }""" + } + """ drive_list = freebsd.get_sysctl("kern.disks", True).split(" ") dmesg = freebsd.get_dmesg() drive_dict = {} @@ -360,7 +484,8 @@ @logexception def GetDrivePartitions(self, drive): - """Returns a list of leaf partitions created on the drive. + """ + Returns a list of leaf partitions created on the drive. ARGUMENTS: drive : The drive to inspect RETURN VALUE: @@ -377,7 +502,8 @@ "fs_type" : "UFS2", "fs_last_mount", "/usr" } - }""" + } + """ parts = {} used_parts = [] geomxml = freebsd.get_geom_xml() @@ -438,8 +564,10 @@ def GetMountPoints(self): - """Returns a list of dictionaries containing information - about currently mounted file systems""" + """ + Returns a list of dictionaries containing information + about currently mounted file systems + """ raw_mounts = freebsd.get_cmd_output("%s -p" % freebsd.cmd_mount).split("\n") mounts = [] for m in raw_mounts: @@ -461,6 +589,7 @@ physmem = int(freebsd.get_sysctl("hw.realmem")) return int(physmem / (1024*1024)) + # Jobs ####################################################################### @logexception def StartPartitionJob(self, part_spec): @@ -492,6 +621,20 @@ @logexception + def StartPkgInstallJob(self, part_spec): + """ + Starts the install job. Returns an integer job_id. + """ + self.job_list_lock.acquire() + job = PkgInstallJob(part_spec) + self.job_list.append(job) + job_id = len(self.job_list) + job.start() + self.job_list_lock.release() + return job_id + + + @logexception def QueryJobProgress(self, job_id): """ Queries the progress of a job, returns percent complete or None @@ -509,6 +652,18 @@ @logexception + def QueryIsJobFinished(self, job_id): + """ + Queries whether the job has finished. Testing for job progress percent == 100 + is not enough. + """ + self.job_list_lock.acquire() + job = self.job_list[job_id-1] + self.job_list_lock.release() + return job.finished + + + @logexception def QueryJobResult(self, job_id): """ Queries the result of a job, if the job is finished. Returns @@ -545,4 +700,32 @@ self.job_list_lock.release() return True +# System configuration ######################################################## + + @logexception + def SetHostName(self, host_name): + if self.SetConf("hostname", host_name, "Host name"): + return (0, None) + else: + return (1, "Cannot set host name") + + + @logexception + def GetHostName(self): + host_name = self.GetConf("hostname") + if host_name == None: + return "amnesiac" + else: + return host_name + + + @logexception + def SetUserPassword(self, user_name, password): + return freebsd.chpasswd(user_name, password, "%s/etc" % self.root_dest) + + + @logexception + def AddUser(self, user_name, user, password, shell, groups): + return freebsd.useradd(user_name, user, password, shell, groups, "%s/etc" % self.root_dest) +