class ChrootInstaller(BaseInstaller): # we may need more __init__ args later def __init__(self, conn): BaseInstaller.__init__(self, conn) self._bootstrapped = False self._install_finished = False self._processes = [ 'ready_target', 'bootstrap', 'mount_target_proc', 'mount_target_sys', 'make_device_entries', 'mount_target_devpts', 'apt_sources_installer', 'ready_base_for_install', 'pre_install', 'install', 'post_install', 'apt_sources_final', 'umount_target_sys', 'umount_target_proc' ] # pre_install is unmapped # post_install is unmapped self._process_map = dict( ready_target=self.create_target_directory, bootstrap=self.bootstrap_target, mount_target_proc=self.mount_target_proc, mount_target_sys=self.mount_target_sys, make_device_entries=self.make_device_entries, mount_target_devpts=self.mount_target_devpts, apt_sources_installer=self.apt_sources_installer, ready_base_for_install=self.ready_base_for_install, install=self.install, apt_sources_final=self.apt_sources_final, umount_target_sys=self.umount_target_sys, umount_target_proc=self.umount_target_proc) # this is only used in the machine installer self.mtypedata = {} # the default script for the chroot installer is None def make_script(self, procname): return None def set_logfile(self, logfile): BaseInstaller.set_logfile(self, logfile) self.log.info('-' * 30) msg = '%s initialized' % self.__class__.__name__ self.log.info(msg) self.log.info('-' * 30) @requires_target_set def set_profile(self, profile): self.installer = ProfileInstaller(self) self.installer.mtypedata.update(self.mtypedata) self.installer.set_profile(profile) self.set_suite(self.installer.suite) @requires_target_exists def _bootstrap_with_tarball(self, suite): suite_path = path(self.defenv.get('installer', 'suite_storage')) filename = '%s.tar.gz' % suite basefile = suite_path / filename taropts = '-xzf' if not basefile.exists(): filename = '%s.tar' % suite basefile = suite_path / filename taropts = '-xf' cmd = 'tar -C %s %s %s' % (self.target, taropts, basefile) # if cmd returns nonzero, runlog will raise an error runlog(cmd) # we need to do certain things here that debootstrap # does for us, like copy /etc/resolv.conf self._bootstrapped = True @requires_target_exists def _bootstrap_with_debootstrap(self, suite): mirror = self.defenv.get('installer', 'http_mirror') cmd = debootstrap(suite, self.target, mirror) # if cmd returns nonzero, runlog will raise an error runlog(cmd) self._bootstrapped = True @requires_suite_set def bootstrap_target(self): if not self.target.exists(): self.target.mkdir() if not self.target.isdir(): raise InstallTargetError, "%s is not a directory" % self.target if self.defenv.getboolean('installer', 'bootstrap_target'): self.log.info('bootstrapping with debootstrap') self._bootstrap_with_debootstrap(self.base_suite) else: self.log.info('bootstrapping with premade tarball') self._bootstrap_with_tarball(self.base_suite) # here we add the apt keys that are needed aptkeys = AptKeyHandler(self.conn) keys = self.defenv.get_list('archive_keys', 'installer') for key in keys: row = aptkeys.get_row(key) filename = self.target / ('%s.key' % key) if filename.exists(): raise RuntimeError, "%s already exists" % filename keyfile = file(filename, 'w') keyfile.write(row.data) keyfile.close() #self.chroot('apt-key add %s.key' % key) self.chroot(['apt-key', 'add', '%s.key' % key]) os.remove(filename) if filename.exists(): raise RuntimeError, "%s wasn't deleted" % filename self.log.info('added key %s (%s) to apt' % (key, row.keyid)) @requires_bootstrap def make_device_entries(self): self.log.info('nothing done for make_device_entries yet') @requires_bootstrap def apt_sources_installer(self): make_sources_list(self.conn, self.target, self.suite) @requires_install_complete def apt_sources_final(self): sourceslist = self.target / 'etc/apt/sources.list' sourceslist_installer = path('%s.installer' % sourceslist) os.rename(sourceslist, sourceslist_installer) make_official_sources_list(self.conn, self.target, self.suite) # this is probably not useful anymore # it still has a purpose in the machine installer - # it sets up the mdadm.conf file with the raid devices it creates # if it creates any. @requires_bootstrap def ready_base_for_install(self): # update the package lists #self.chroot('apt-get -y update') self.chroot(['apt-get', '-y', 'update']) # common method for mounting /proc and /sys # here fs is either 'proc' or 'sys' or 'devpts' def _mount_target_virtfs(self, fs): fstype = dict(proc='proc', sys='sysfs', devpts='devpts') target = self.target / fs if fs == 'devpts': target = self.target / 'dev' / 'pts' if not target.isdir(): target.mkdir() #cmd = 'mount -t %s none %s' % (fstype[fs], target) cmd = ['mount', '-t', fstype[fs], 'none', str(target)] runlog(cmd) def _umount_target_virtfs(self, fs): # work around binfmt-support /proc locking # found this idea while messing around in live-helper target = self.target / fs if fs == 'proc': binfmt_misc = self.target / 'proc/sys/fs/binfmt_misc' status = binfmt_misc / 'status' if status.exists(): self.log.info('Unmounting /proc/sys/fs/binfmt_misc in chroot') #cmd = 'umount %s' % binfmt cmd = ['umount', str(binfmt_misc)] runlog(cmd) if fs == 'devpts': target = self.target / 'dev' / 'pts' #cmd = 'umount %s' % target cmd = ['umount', str(target)] runlog(cmd) def _target_proc_mounted(self): testfile = self.target / 'proc/version' return testfile.isfile() def _target_sys_mounted(self): testdir = self.target / 'sys/kernel' return testdir.isdir() @requires_bootstrap def mount_target_proc(self): self._mount_target_virtfs('proc') @requires_bootstrap def mount_target_sys(self): self._mount_target_virtfs('sys') def mount_target_devpts(self): self._mount_target_virtfs('devpts') @requires_target_proc_mounted def umount_target_proc(self): self._umount_target_virtfs('proc') @requires_target_sys_mounted def umount_target_sys(self): self._umount_target_virtfs('sys') def umount_target_devpts(self): self._umount_target_virtfs('devpts') @requires_target_proc_mounted @requires_target_sys_mounted @requires_installer_set def install(self): self.installer.run_all_processes() self._install_finished = True def log_all_processes_finished(self): self.log.info('-' * 30) self.log.info('%s processes finished' % self.__class__.__name__) self.log.info('-' * 30) def save_logfile_in_target(self): install_log = self.target / 'root/paella/install.log' self.mainlog.filename.copyfile(install_log)
class ChrootInstaller(BaseChrootInstaller): # we may need more __init__ args later def __init__(self, conn): BaseChrootInstaller.__init__(self, conn) self._bootstrapped = False self._install_finished = False self._processes = [ 'ready_target', 'bootstrap', 'mount_target_proc', 'mount_target_sys', 'make_device_entries', 'mount_target_devpts', 'apt_sources_installer', 'ready_base_for_install', 'pre_install', 'install', 'post_install', 'apt_sources_final', 'umount_target_sys', 'umount_target_proc', 'umount_target_devpts' ] # pre_install is unmapped # post_install is unmapped self._process_map = dict(ready_target=self.create_target_directory, bootstrap=self.bootstrap_target, mount_target_proc=self.mount_target_proc, mount_target_sys=self.mount_target_sys, make_device_entries=self.make_device_entries, mount_target_devpts=self.mount_target_devpts, apt_sources_installer=self.apt_sources_installer, ready_base_for_install=self.ready_base_for_install, install=self.install, apt_sources_final=self.apt_sources_final, umount_target_sys=self.umount_target_sys, umount_target_proc=self.umount_target_proc, umount_target_devpts=self.umount_target_devpts ) # this is only used in the machine installer self.machine_data = {} # the default script for the chroot installer is None def make_script(self, procname): return None def set_logfile(self, logfile): BaseInstaller.set_logger(self, filename=logfile) self.log.info('-'*30) msg = '%s initialized' % self.__class__.__name__ self.log.info(msg) self.log.info('-'*30) def set_profile(self, profile): self.check_target_set() self.installer = ProfileInstaller(self) if os.environ.has_key('DEBUG'): self.log.info("ChrootInstaller.machine_data: %s" % self.machine_data) self.installer.machine_data.update(self.machine_data) if os.environ.has_key('DEBUG'): self.log.info("ProfileInstaller.machine_data: %s" % self.installer.machine_data) self.installer.set_profile(profile) self.set_suite(self.installer.suite) def _bootstrap_with_tarball(self, suite): self.check_target_exists() suite_path = path(self.defenv.get('installer', 'suite_storage')) arch = get_architecture() filename = '%s-%s.tar.gz' % (suite, arch) basefile = suite_path / filename taropts = '-xzf' # we normally expect a tar.gz # but we'll check for a plain tar also if not basefile.exists(): filename = '%s-%s.tar' % (suite, arch) basefile = suite_path / filename taropts = '-xf' if not basefile.exists(): # We don't really want to ruin an install # by not having a tarball, so we log a warning # and proceed with a debootstrap. msg = "base tarball not found, reverting to debootstrap" self.log.warn(msg) self._bootstrap_with_debootstrap(suite) else: #cmd = 'tar -C %s %s %s' % (self.target, taropts, basefile) cmd = ['tar', '-C', str(self.target), taropts, str(basefile)] # if cmd returns nonzero, runlog will raise an error runlog(cmd) # we need to do certain things after extraction # that debootstrap does for us, # like copy /etc/resolv.conf to the target. # these things should be done in the # ready_base_for_install process # this is now done in post_process #self._bootstrapped = True def _bootstrap_with_debootstrap(self, suite): self.check_target_exists() mirror = self.defenv.get('installer', 'http_mirror') # debug stuff cmd = debootstrap(suite, self.target, mirror) # if cmd returns nonzero, runlog will raise an error runlog(cmd) # this is now done in post_process #self._bootstrapped = True def bootstrap_target(self): self.check_suite_set() if not self.target.exists(): self.target.mkdir() if not self.target.isdir(): raise InstallTargetError, "%s is not a directory" % self.target if self.defenv.getboolean('installer', 'bootstrap_target'): self.log.info('bootstrapping with debootstrap') self._bootstrap_with_debootstrap(self.base_suite) else: self.log.info('bootstrapping with premade tarball') self._bootstrap_with_tarball(self.base_suite) # here we add the apt keys that are needed # we should probably split this part off into # another process. This step needs to be done # before the ready_base_for_install process, or # at least at the beginning of that process. aptkeys = AptKeyHandler(self.conn) keys = self.defenv.get_list('archive_keys', 'installer') for key in keys: try: row = aptkeys.get_row(key) except NoAptKeyError: msg = "There's no apt key named %s in the database" % key self.log.error(msg) raise UnsatisfiedRequirementsError , msg filename = self.target / ('%s.key' % key) if filename.exists(): msg = "%s already exists" % filename self.log.error(msg) raise RuntimeError , msg keyfile = file(filename, 'w') keyfile.write(row.data) keyfile.close() self.chroot(['apt-key', 'add', '%s.key' % key]) os.remove(filename) if filename.exists(): msg = "%s wasn't deleted" % filename self.log.error(msg) raise RuntimeError , msg self.log.info('added key %s (%s) to apt' % (key, row.keyid)) def make_device_entries(self): self.check_bootstrap() self.log.info('nothing done for make_device_entries yet') def apt_sources_installer(self): self.check_bootstrap() make_sources_list(self.conn, self.target, self.suite) def apt_sources_final(self): self.check_install_complete() sourceslist = self.target / 'etc/apt/sources.list' sourceslist_installer = path('%s.installer' % sourceslist) os.rename(sourceslist, sourceslist_installer) make_official_sources_list(self.conn, self.target, self.suite) def ready_base_for_install(self): """This gets the base that was either debootstrapped or extracted ready to install packages. Since the install hasn't happened yet, replacing files like /etc/resolv.conf and the package lists shouldn't affect anything. the apt_sources_installer process is called right before this one, so there should be an appropriate sources.list to update packages with. """ self.check_bootstrap() # 'copy' /etc/resolv.conf to target resolvconf = file('/etc/resolv.conf').read() target_resolvconf = self.target / 'etc/resolv.conf' target_resolvconf.write_text(resolvconf) # update the package lists self.chroot(['apt-get', '-y', 'update']) # common method for mounting /proc and /sys # here fs is either 'proc' or 'sys' or 'devpts' def _mount_target_virtfs(self, fs): fstype = dict(proc='proc', sys='sysfs', devpts='devpts') target = self.target / fs if fs == 'devpts': target = self.target / 'dev' / 'pts' if not target.isdir(): self.log.info('creating %s' % target) target.mkdir() #cmd = 'mount -t %s none %s' % (fstype[fs], target) cmd = ['mount', '-t', fstype[fs], 'none', str(target)] runlog(cmd) def _umount_target_virtfs(self, fs): self.log.info('running umount for %s' % fs) # work around binfmt-support /proc locking # found this idea while messing around in live-helper target = self.target / fs if fs == 'proc': binfmt_misc = self.target / 'proc/sys/fs/binfmt_misc' status = binfmt_misc / 'status' if status.exists(): self.log.info('Unmounting /proc/sys/fs/binfmt_misc in chroot') #cmd = 'umount %s' % binfmt cmd = ['umount', str(binfmt_misc)] runlog(cmd) # hack to stop mdadm on target mdstat = self.target / 'proc/mdstat' if mdstat.isfile(): mdadm_initscript = self.target / 'etc/init.d/mdadm' if mdadm_initscript.isfile(): self.log.info("Stopping mdadm from running on target.") cmd = ['chroot', self.target, '/etc/init.d/mdadm', 'stop'] runlog(cmd) if fs == 'devpts': target = self.target / 'dev' / 'pts' #cmd = 'umount %s' % target cmd = ['umount', str(target)] runlog(cmd) def _target_proc_mounted(self): testfile = self.target / 'proc/version' return testfile.isfile() def _target_sys_mounted(self): testdir = self.target / 'sys/kernel' return testdir.isdir() def mount_target_proc(self): self.check_bootstrap() self._mount_target_virtfs('proc') def mount_target_sys(self): self.check_bootstrap() self._mount_target_virtfs('sys') def mount_target_devpts(self): self.check_bootstrap() self._mount_target_virtfs('devpts') def umount_target_proc(self): self.check_target_proc_mounted() self._umount_target_virtfs('proc') def umount_target_sys(self): self.check_target_sys_mounted() self._umount_target_virtfs('sys') def umount_target_devpts(self): self._umount_target_virtfs('devpts') def install(self): self.check_target_proc_mounted() self.check_target_sys_mounted() self.check_installer_set() self.installer.run_all_processes() # this is now done in post_process #self._install_finished = True def log_all_processes_finished(self): self.log.info('-'*30) self.log.info('%s processes finished' % self.__class__.__name__) self.log.info('-'*30) def save_logfile_in_target(self): install_log = self.target / 'root/paella/install.log' self.mainlog.filename.copyfile(install_log) # if scripts are hooked into certain processes, we # need to mark those processes as being completed # in order for other processes to be run. def post_process(self, procname): name = self.__class__.__name__ self.log.info('%s(%s) post_process' % (name, procname)) if procname == 'bootstrap': self.log.info('%s marking %s finished' % (name, procname)) self._bootstrapped = True elif procname == 'install': self.log.info('%s marking %s finished' % (name, procname)) self._install_finished = True
class ChrootInstaller(BaseInstaller): # we may need more __init__ args later def __init__(self, conn): BaseInstaller.__init__(self, conn) self._bootstrapped = False self._install_finished = False self._processes = [ 'ready_target', 'bootstrap', 'mount_target_proc', 'mount_target_sys', 'make_device_entries', 'mount_target_devpts', 'apt_sources_installer', 'ready_base_for_install', 'pre_install', 'install', 'post_install', 'apt_sources_final', 'umount_target_sys', 'umount_target_proc' ] # pre_install is unmapped # post_install is unmapped self._process_map = dict(ready_target=self.create_target_directory, bootstrap=self.bootstrap_target, mount_target_proc=self.mount_target_proc, mount_target_sys=self.mount_target_sys, make_device_entries=self.make_device_entries, mount_target_devpts=self.mount_target_devpts, apt_sources_installer=self.apt_sources_installer, ready_base_for_install=self.ready_base_for_install, install=self.install, apt_sources_final=self.apt_sources_final, umount_target_sys=self.umount_target_sys, umount_target_proc=self.umount_target_proc ) # this is only used in the machine installer self.mtypedata = {} # the default script for the chroot installer is None def make_script(self, procname): return None def set_logfile(self, logfile): BaseInstaller.set_logfile(self, logfile) self.log.info('-'*30) msg = '%s initialized' % self.__class__.__name__ self.log.info(msg) self.log.info('-'*30) @requires_target_set def set_profile(self, profile): self.installer = ProfileInstaller(self) self.installer.mtypedata.update(self.mtypedata) self.installer.set_profile(profile) self.set_suite(self.installer.suite) @requires_target_exists def _bootstrap_with_tarball(self, suite): suite_path = path(self.defenv.get('installer', 'suite_storage')) filename = '%s.tar.gz' % suite basefile = suite_path / filename taropts = '-xzf' if not basefile.exists(): filename = '%s.tar' % suite basefile = suite_path / filename taropts = '-xf' cmd = 'tar -C %s %s %s' % (self.target, taropts, basefile) # if cmd returns nonzero, runlog will raise an error runlog(cmd) # we need to do certain things here that debootstrap # does for us, like copy /etc/resolv.conf self._bootstrapped = True @requires_target_exists def _bootstrap_with_debootstrap(self, suite): mirror = self.defenv.get('installer', 'http_mirror') cmd = debootstrap(suite, self.target, mirror) # if cmd returns nonzero, runlog will raise an error runlog(cmd) self._bootstrapped = True @requires_suite_set def bootstrap_target(self): if not self.target.exists(): self.target.mkdir() if not self.target.isdir(): raise InstallTargetError, "%s is not a directory" % self.target if self.defenv.getboolean('installer', 'bootstrap_target'): self.log.info('bootstrapping with debootstrap') self._bootstrap_with_debootstrap(self.base_suite) else: self.log.info('bootstrapping with premade tarball') self._bootstrap_with_tarball(self.base_suite) # here we add the apt keys that are needed aptkeys = AptKeyHandler(self.conn) keys = self.defenv.get_list('archive_keys', 'installer') for key in keys: row = aptkeys.get_row(key) filename = self.target / ('%s.key' % key) if filename.exists(): raise RuntimeError, "%s already exists" % filename keyfile = file(filename, 'w') keyfile.write(row.data) keyfile.close() #self.chroot('apt-key add %s.key' % key) self.chroot(['apt-key', 'add', '%s.key' % key]) os.remove(filename) if filename.exists(): raise RuntimeError, "%s wasn't deleted" % filename self.log.info('added key %s (%s) to apt' % (key, row.keyid)) @requires_bootstrap def make_device_entries(self): self.log.info('nothing done for make_device_entries yet') @requires_bootstrap def apt_sources_installer(self): make_sources_list(self.conn, self.target, self.suite) @requires_install_complete def apt_sources_final(self): sourceslist = self.target / 'etc/apt/sources.list' sourceslist_installer = path('%s.installer' % sourceslist) os.rename(sourceslist, sourceslist_installer) make_official_sources_list(self.conn, self.target, self.suite) # this is probably not useful anymore # it still has a purpose in the machine installer - # it sets up the mdadm.conf file with the raid devices it creates # if it creates any. @requires_bootstrap def ready_base_for_install(self): # update the package lists #self.chroot('apt-get -y update') self.chroot(['apt-get', '-y', 'update']) # common method for mounting /proc and /sys # here fs is either 'proc' or 'sys' or 'devpts' def _mount_target_virtfs(self, fs): fstype = dict(proc='proc', sys='sysfs', devpts='devpts') target = self.target / fs if fs == 'devpts': target = self.target / 'dev' / 'pts' if not target.isdir(): target.mkdir() #cmd = 'mount -t %s none %s' % (fstype[fs], target) cmd = ['mount', '-t', fstype[fs], 'none', str(target)] runlog(cmd) def _umount_target_virtfs(self, fs): # work around binfmt-support /proc locking # found this idea while messing around in live-helper target = self.target / fs if fs == 'proc': binfmt_misc = self.target / 'proc/sys/fs/binfmt_misc' status = binfmt_misc / 'status' if status.exists(): self.log.info('Unmounting /proc/sys/fs/binfmt_misc in chroot') #cmd = 'umount %s' % binfmt cmd = ['umount', str(binfmt_misc)] runlog(cmd) if fs == 'devpts': target = self.target / 'dev' / 'pts' #cmd = 'umount %s' % target cmd = ['umount', str(target)] runlog(cmd) def _target_proc_mounted(self): testfile = self.target / 'proc/version' return testfile.isfile() def _target_sys_mounted(self): testdir = self.target / 'sys/kernel' return testdir.isdir() @requires_bootstrap def mount_target_proc(self): self._mount_target_virtfs('proc') @requires_bootstrap def mount_target_sys(self): self._mount_target_virtfs('sys') def mount_target_devpts(self): self._mount_target_virtfs('devpts') @requires_target_proc_mounted def umount_target_proc(self): self._umount_target_virtfs('proc') @requires_target_sys_mounted def umount_target_sys(self): self._umount_target_virtfs('sys') def umount_target_devpts(self): self._umount_target_virtfs('devpts') @requires_target_proc_mounted @requires_target_sys_mounted @requires_installer_set def install(self): self.installer.run_all_processes() self._install_finished = True def log_all_processes_finished(self): self.log.info('-'*30) self.log.info('%s processes finished' % self.__class__.__name__) self.log.info('-'*30) def save_logfile_in_target(self): install_log = self.target / 'root/paella/install.log' self.mainlog.filename.copyfile(install_log)