# vim: set sw=4 sts=4 et : # Copyright: 2008 Gentoo Foundation # Author(s): Nirbheek Chauhan # License: GPL-2 # # Immortal lh! # import os, shutil, subprocess, re import os.path as osp from .. import config, sync from time import strftime class PristineChroot(object): """ Pristine Chroot """ def __init__(self, stage_filename): """ @param stage: Filename of Stage tarball which has the chroot @type stage: string """ # stage-my.tar.bz2 -> stage-my self.dir = osp.join(config.CHROOT_DIR, stage_filename[:stage_filename.rfind('.tar')]) self.stage = stage_filename self.tar_args = (osp.join(config.STAGE_DIR, self.stage), self.dir) def _extract_stage(self): os.makedirs(self.dir) # Replace with a jobuild.sh unpack later. subprocess.check_call('tar xf "%s" -C "%s"' % self.tar_args, shell=True) os.mkdir(self.dir+'/usr/portage') def check(self, fix=False): """Verify that the pristine is intact""" if config.VERBOSE: print "Cross-checking with stage tarball \"%s\"" % self.stage process = subprocess.Popen('tar jfd "%s" -C "%s"' % self.tar_args, stderr=subprocess.PIPE, shell=True) if config.VERBOSE: print "PID: %s" % process.pid output = process.communicate()[1] if output.count('No such file or directory') > 0: print "Chroot is no longer pristine." regex = re.compile(r'tar: (.*?):.*') output = regex.sub(r'"\1"', output) if config.VERBOSE: print "The following files are missing:" print output if not fix: return False print "Fixing..." tar_args = self.tar_args + output.replace('\n', ' ') subprocess.check_call('tar xf \"%s\" -C \"%s\" %s' % tar_args, shell=True) print "Chroot is once again pristine :)" return True def setup(self): """Extract the chroot if required""" if osp.exists(self.dir): print "Pristine ready." return True if config.VERBOSE: print "Extracting from stage...", self._extract_stage() print "Pristine ready." return True def reset(self): """Re-extract the chroot from the tarball""" if config.VERBOSE: print "Removing existing chroot..." shutil.rmtree(self.dir) if config.VERBOSE: print "Extracting %s" % self.stage self._extract_stage() print "Pristine ready." class WorkChroot(object): """ Chroot where all the action happens """ def __init__(self, jobdir, stage_file): """ @param job: Job to be run inside the chroot @type job: L{autotua.Job} """ self.pristine = PristineChroot(stage_file) self.jobdir = jobdir self.chrootdir = osp.join(self.jobdir, 'chroot') # Hmmmm. Maybe all this should be in a module of it's own. def _clean_mounts(self): # /proc/mounts is more reliable than mtab (which is what `mount` uses) mounts = open('/proc/mounts', 'r').read() regex = re.compile(r'%s/[^ ]+' % self.chrootdir.replace(' ', r'\\040')) for mount in regex.findall(mounts): mount = mount.replace(r'\040(deleted)', '').replace(r'\040', ' ') subprocess.check_call('umount "%s"' % mount, shell=True) def _bind(self, src, dest, ro=True): """ Bind mount src onto dest inside self.chrootdir Mount read-only by default """ if not dest.startswith('/'): dest = '/'+dest dest = self.chrootdir+dest options = 'bind' if ro: options += ',ro' subprocess.check_call('mount -o %s "%s" "%s"' % (options, src, dest), shell=True) def _setup_mounts(self): for dir in ['/dev', '/sys', '/proc']: self._bind(dir, dir) # FIXME: else: Fetch portage tarball and use. if config.PORTAGE_DIR: if not osp.isdir(config.PORTAGE_DIR): print "\"%s\" is not a directory, cannot mount" % config.PORTAGE_DIR else: self._bind(config.PORTAGE_DIR, '/usr/portage') if config.DISTFILES_DIR and not os.path.samefile(config.DISTFILES_DIR, config.PORTAGE_DIR+'/distfiles'): if not osp.isdir(config.DISTFILES_DIR): print "\"%s\" is not a directory, cannot mount" % config.DISTFILES_DIR else: self._bind(config.DISTFILES_DIR, '/usr/portage/distfiles', ro=False) self._bind(config.AUTOTUA_DIR+'/bin', config.CHAUTOTUA_DIR+'/bin') self._bind(self.jobdir+'/jobtage', config.CHAUTOTUA_DIR+'/jobtage') def setup(self): """ Clean existing mounts, if any rsync from PristineChroot Mount stuff. """ if not self.pristine.setup(): return False # Tidy up incase we screwed up last time self.tidy() # self.pristine.dir/ => rsync *contents* to self.chrootdir print "Preparing Work Chroot..." sync.Syncer(uri=self.pristine.dir+"/", destdir=self.chrootdir, scheme='rsync-nc').sync() for dir in ['bin', 'jobfiles', 'jobtage', 'src']: os.makedirs('%s/%s/%s' % (self.chrootdir, config.CHAUTOTUA_DIR, dir)) self._setup_mounts() print "Work Chroot ready." def tidy(self): """ Cleanup when done. """ self._clean_mounts() if osp.isdir(self.chrootdir+config.CHAUTOTUA_DIR): if not osp.isdir(self.chrootdir+config.CHAUTOTUA_DIR+'-old'): os.makedirs(self.chrootdir+config.CHAUTOTUA_DIR+'-old') shutil.move(self.chrootdir+config.CHAUTOTUA_DIR, self.chrootdir+config.CHAUTOTUA_DIR+'-old/autotua-'+strftime('%Y%m%d%H%M%S'))