def testRuntoolCatch0(self): (rc, out) = runner.runtool("echo hello", catch=0) self.assertEqual(0, rc) self.assertEqual('', out) (rc, out) = runner.runtool("echo hello >&2", catch=0) self.assertEqual(0, rc) self.assertEqual('', out)
def testRuntoolCatch3(self): (rc, out) = runner.runtool("echo hello", catch=3) self.assertEqual(0, rc) self.assertEqual("hello\n", out) (rc, out) = runner.runtool("echo hello >&2", catch=2) self.assertEqual(0, rc) self.assertEqual("hello\n", out)
def exec_cmd(cmd_and_args, as_shell=False, catch=3): """ Execute command, catching stderr, stdout Need to execute as_shell if the command uses wildcards """ msger.debug("exec_cmd: %s" % cmd_and_args) args = cmd_and_args.split() msger.debug(args) if (as_shell): rc, out = runner.runtool(cmd_and_args, catch) else: rc, out = runner.runtool(args, catch) out = out.strip() msger.debug("exec_cmd: output for %s (rc = %d): %s" % \ (cmd_and_args, rc, out)) if rc != 0: # We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. This # prevents us from distinguishing real errors based on return # code. msger.debug("WARNING: %s returned '%s' instead of 0" % (args[0], rc)) return (rc, out)
def __format_filesystem(self): if self.skipformat: msger.debug("Skip filesystem format.") return msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) cmdlist = [self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b", str(self.blocksize)] if self.extopts: cmdlist.extend(self.extopts.split()) cmdlist.extend([self.disk.device]) rc, errout = runner.runtool(cmdlist, catch=2) if rc != 0: raise MountError("Error creating %s filesystem on disk %s:\n%s" % (self.fstype, self.disk.device, errout)) if not self.extopts: msger.debug("Tuning filesystem on %s" % self.disk.device) runner.show([self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device]) rc, out = runner.runtool([self.dumpe2fs, '-h', self.disk.device], catch=2) if rc != 0: raise MountError("Error dumpe2fs %s filesystem on disk %s:\n%s" % (self.fstype, self.disk.device, out)) # FIXME: specify uuid in mkfs parameter try: self.uuid = self.__parse_field(out, "Filesystem UUID") except: self.uuid = None
def exec_cmd(cmd_and_args, as_shell = False, catch = 3): """ Execute command, catching stderr, stdout Need to execute as_shell if the command uses wildcards """ msger.debug("exec_cmd: %s" % cmd_and_args) args = cmd_and_args.split() msger.debug(args) if (as_shell): rc, out = runner.runtool(cmd_and_args, catch) else: rc, out = runner.runtool(args, catch) out = out.strip() msger.debug("exec_cmd: output for %s (rc = %d): %s" % \ (cmd_and_args, rc, out)) if rc != 0: # We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. This # prevents us from distinguishing real errors based on return # code. msger.debug("WARNING: %s returned '%s' instead of 0" % (args[0], rc)) return (rc, out)
def get_file_size(filename): """ Return size in MB unit """ cmd = ['du', "-s", "-b", "-B", "1M", filename] rc, duOutput = runner.runtool(cmd) if rc != 0: raise CreatorError("Failed to run: %s" % ' '.join(cmd)) size1 = int(duOutput.split()[0]) cmd = ['du', "-s", "-B", "1M", filename] rc, duOutput = runner.runtool(cmd) if rc != 0: raise CreatorError("Failed to run: %s" % ' '.join(cmd)) size2 = int(duOutput.split()[0]) return max(size1, size2)
def installLocal(self, pkg, po=None, updateonly=False): if not self.ts: self.__initialize_transaction() solvfile = "%s/.solv" % (self.creator.cachedir) rc, out = runner.runtool([fs_related.find_binary_path("rpms2solv"), pkg]) if rc == 0: f = open(solvfile, "w+") f.write(out) f.close() warnmsg = self.repo_manager.loadSolvFile(solvfile , os.path.basename(pkg)) if warnmsg: msger.warning(warnmsg) os.unlink(solvfile) else: msger.warning('Can not get %s solv data.' % pkg) hdr = rpmmisc.readRpmHeader(self.ts, pkg) arch = zypp.Arch(hdr['arch']) if self.creator.target_arch == None: # TODO, get the default_arch from conf or detected from global settings sysarch = zypp.Arch('i686') else: sysarch = zypp.Arch(self.creator.target_arch) if arch.compatible_with (sysarch): pkgname = hdr['name'] self.localpkgs[pkgname] = pkg self.selectPackage(pkgname) msger.info("Marking %s to be installed" % (pkg)) else: msger.warning ("Cannot add package %s to transaction. Not a compatible architecture: %s" % (pkg, hdr['arch']))
def __map_partitions(self): """Load it if dm_snapshot isn't loaded""" load_module("dm_snapshot") for dev in self.disks.keys(): d = self.disks[dev] if d['mapped']: continue msger.debug("Running kpartx on %s" % d['disk'].device ) rc, kpartxOutput = runner.runtool([self.kpartx, "-l", "-v", d['disk'].device]) kpartxOutput = kpartxOutput.splitlines() if rc != 0: raise MountError("Failed to query partition mapping for '%s'" % d['disk'].device) # Strip trailing blank and mask verbose output i = 0 while i < len(kpartxOutput) and kpartxOutput[i][0:4] != "loop": i = i + 1 kpartxOutput = kpartxOutput[i:] # Quick sanity check that the number of partitions matches # our expectation. If it doesn't, someone broke the code # further up if len(kpartxOutput) != d['numpart']: raise MountError("Unexpected number of partitions from kpartx: %d != %d" % (len(kpartxOutput), d['numpart'])) for i in range(len(kpartxOutput)): line = kpartxOutput[i] newdev = line.split()[0] mapperdev = "/dev/mapper/" + newdev loopdev = d['disk'].device + newdev[-1] msger.debug("Dev %s: %s -> %s" % (newdev, loopdev, mapperdev)) pnum = d['partitions'][i] self.partitions[pnum]['device'] = loopdev # grub's install wants partitions to be named # to match their parent device + partition num # kpartx doesn't work like this, so we add compat # symlinks to point to /dev/mapper if os.path.lexists(loopdev): os.unlink(loopdev) os.symlink(mapperdev, loopdev) msger.debug("Adding partx mapping for %s" % d['disk'].device) rc = runner.show([self.kpartx, "-v", "-a", d['disk'].device]) if rc != 0: # Make sure that the device maps are also removed on error case. # The d['mapped'] isn't set to True if the kpartx fails so # failed mapping will not be cleaned on cleanup either. runner.quiet([self.kpartx, "-d", d['disk'].device]) raise MountError("Failed to map partitions for '%s'" % d['disk'].device) d['mapped'] = True
def installLocal(self, pkg, po=None, updateonly=False): if not self.ts: self.__initialize_transaction() solvfile = "%s/.solv" % (self.creator.cachedir) rc, out = runner.runtool( [fs_related.find_binary_path("rpms2solv"), pkg]) if rc == 0: f = open(solvfile, "w+") f.write(out) f.close() warnmsg = self.repo_manager.loadSolvFile(solvfile, os.path.basename(pkg)) if warnmsg: msger.warning(warnmsg) os.unlink(solvfile) else: msger.warning('Can not get %s solv data.' % pkg) hdr = rpmmisc.readRpmHeader(self.ts, pkg) arch = zypp.Arch(hdr['arch']) if self.creator.target_arch == None: # TODO, get the default_arch from conf or detected from global settings sysarch = zypp.Arch('i686') else: sysarch = zypp.Arch(self.creator.target_arch) if arch.compatible_with(sysarch): pkgname = hdr['name'] self.localpkgs[pkgname] = pkg self.selectPackage(pkgname) msger.info("Marking %s to be installed" % (pkg)) else: msger.warning( "Cannot add package %s to transaction. Not a compatible architecture: %s" % (pkg, hdr['arch']))
def __create_subvolume_metadata(self, p, pdisk): if len(self.subvolumes) == 0: return argv = [ self.btrfscmd, "subvolume", "list", pdisk.mountdir ] rc, out = runner.runtool(argv) msger.debug(out) if rc != 0: raise MountError("Failed to get subvolume id from %s', return code: %d." % (pdisk.mountdir, rc)) subvolid_items = out.splitlines() subvolume_metadata = "" for subvol in self.subvolumes: for line in subvolid_items: if line.endswith(" path %s" % subvol["subvol"]): subvolid = line.split()[1] if not subvolid.isdigit(): raise MountError("Invalid subvolume id: %s" % subvolid) subvolid = int(subvolid) opts = subvol["fsopts"].split(",") for opt in opts: if opt.strip().startswith("subvol="): opts.remove(opt) break fsopts = ",".join(opts) subvolume_metadata += "%d\t%s\t%s\t%s\n" % (subvolid, subvol["subvol"], subvol['mountpoint'], fsopts) if subvolume_metadata: fd = open("%s/.subvolume_metadata" % pdisk.mountdir, "w") fd.write(subvolume_metadata) fd.close()
def __format_filesystem(self): if self.skipformat: msger.debug("Skip filesystem format.") return msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) cmdlist = [ self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b", str(self.blocksize), "-U", self.uuid ] if self.extopts: cmdlist.extend(self.extopts.split()) cmdlist.extend([self.disk.device]) rc, errout = runner.runtool(cmdlist, catch=2) if rc != 0: raise MountError("Error creating %s filesystem on disk %s:\n%s" % (self.fstype, self.disk.device, errout)) if not self.extopts: msger.debug("Tuning filesystem on %s" % self.disk.device) runner.show([ self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device ])
def _kpseek(self, device): rc, out = runner.runtool([self.kpartxcmd, '-l', '-v', device]) if rc != 0: raise MountError("Can't query dm snapshot on %s" % device) for line in out.splitlines(): if line and line.startswith("loop"): return True return False
def _loseek(self, device): import re rc, out = runner.runtool([self.losetupcmd, '-a']) if rc != 0: raise MountError("Failed to run 'losetup -a'") for line in out.splitlines(): m = re.match("([^:]+): .*", line) if m and m.group(1) == device: return True return False
class BindChrootMount: """Represents a bind mount of a directory into a chroot.""" def __init__(self, src, chroot, dest=None, option=None): self.root = os.path.abspath(os.path.expanduser(chroot)) self.mount_option = option self.orig_src = self.src = src if os.path.islink(src): self.src = os.readlink(src) if not self.src.startswith('/'): self.src = os.path.abspath( os.path.join(os.path.dirname(src), self.src)) if not dest: dest = self.src self.dest = os.path.join(self.root, dest.lstrip('/')) self.mounted = False self.mountcmd = find_binary_path("mount") self.umountcmd = find_binary_path("umount") def ismounted(self): with open('/proc/mounts') as f: for line in f: if line.split()[1] == os.path.abspath(self.dest): return True return False def mount(self): if self.mounted or self.ismounted(): return try: makedirs(self.dest) except OSError, err: if err.errno == errno.ENOSPC: msger.warning("No space left on device '%s'" % err.filename) return if self.mount_option: cmdline = [self.mountcmd, "-o" ,"bind", "-o", "%s" % \ self.mount_option, self.src, self.dest] else: cmdline = [self.mountcmd, "-o", "bind", self.src, self.dest] rc, errout = runner.runtool(cmdline, catch=2) if rc != 0: raise MountError("Bind-mounting '%s' to '%s' failed: %s" % (self.src, self.dest, errout)) self.mounted = True if os.path.islink(self.orig_src): dest = os.path.join(self.root, self.orig_src.lstrip('/')) if not os.path.exists(dest): os.symlink(self.src, dest)
def release_output(self, config, destdir, release): """ Create release directory and files """ def _rpath(fn): """ release path """ return os.path.join(destdir, fn) outimages = self.outimage # new ks new_kspath = _rpath(self.name + ".ks") with open(config) as fr: with open(new_kspath, "w") as wf: # When building a release we want to make sure the .ks # file generates the same build even when --release= is not used. wf.write(fr.read().replace("@BUILD_ID@", release)) outimages.append(new_kspath) # rename iso and usbimg for f in os.listdir(destdir): if f.endswith(".iso"): newf = f[:-4] + ".img" elif f.endswith(".usbimg"): newf = f[:-7] + ".img" else: continue os.rename(_rpath(f), _rpath(newf)) outimages.append(_rpath(newf)) # generate MANIFEST with open(_rpath("MANIFEST"), "w") as wf: if os.path.exists("/usr/bin/md5sum"): for f in os.listdir(destdir): if f == "MANIFEST": continue if os.path.isdir(os.path.join(destdir, f)): continue rc, md5sum = runner.runtool(["/usr/bin/md5sum", "-b", _rpath(f)]) if rc != 0: msger.warning("Failed to generate md5sum for file %s" % _rpath(f)) else: md5sum = md5sum.lstrip().split()[0] wf.write(md5sum + " " + f + "\n") else: msger.warning("no md5sum tool found, no checksum string in MANIFEST") wf.writelines(os.listdir(destdir)) outimages.append("%s/MANIFEST" % destdir) # Filter out the nonexist file for fp in outimages[:]: if not os.path.exists("%s" % fp): outimages.remove(fp)
def release_output(self, config, destdir, release): """ Create release directory and files """ def _rpath(fn): """ release path """ return os.path.join(destdir, fn) outimages = self.outimage # new ks new_kspath = _rpath(self.name+'.ks') with open(config) as fr: with open(new_kspath, "w") as wf: # When building a release we want to make sure the .ks # file generates the same build even when --release= is not used. wf.write(fr.read().replace("@BUILD_ID@", release)) outimages.append(new_kspath) # rename iso and usbimg for f in os.listdir(destdir): if f.endswith(".iso"): newf = f[:-4] + '.img' elif f.endswith(".usbimg"): newf = f[:-7] + '.img' else: continue os.rename(_rpath(f), _rpath(newf)) outimages.append(_rpath(newf)) # generate MANIFEST with open(_rpath("MANIFEST"), "w") as wf: if os.path.exists("/usr/bin/md5sum"): for f in os.listdir(destdir): if f == "MANIFEST": continue if os.path.isdir(os.path.join(destdir,f)): continue rc, md5sum = runner.runtool(["/usr/bin/md5sum", "-b", _rpath(f)]) if rc != 0: msger.warning("Failed to generate md5sum for file %s" % _rpath(f)) else: md5sum = md5sum.lstrip().split()[0] wf.write(md5sum+" "+ f +"\n") else: msger.warning('no md5sum tool found, no checksum string in MANIFEST') wf.writelines(os.listdir(destdir)) outimages.append("%s/MANIFEST" % destdir) # Filter out the nonexist file for fp in outimages[:]: if not os.path.exists("%s" % fp): outimages.remove(fp)
def __run_parted(self, args): """ Run parted with arguments specified in the 'args' list. """ args.insert(0, self.parted) msger.debug(args) rc, out = runner.runtool(args, catch = 3) out = out.strip() msger.debug("'parted': exitcode: %d, output: %s" % (rc, out)) # We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. This # prevents us from distinguishing real errors based on return # code. return rc, out
def __run_parted(self, args): """ Run parted with arguments specified in the 'args' list. """ args.insert(0, self.parted) msger.debug(args) rc, out = runner.runtool(args, catch=3) out = out.strip() msger.debug("'parted': exitcode: %d, output: %s" % (rc, out)) # We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. This # prevents us from distinguishing real errors based on return # code. return rc, out
def __create_part_to_image(self,device, parttype, fstype, start, size): # Start is included to the size so we need to substract one from the end. end = start+size-1 msger.debug("Added '%s' part at %d of size %d" % (parttype,start,end)) part_cmd = [self.parted, "-s", device, "unit", "s", "mkpart", parttype] if fstype: part_cmd.extend([fstype]) part_cmd.extend(["%d" % start, "%d" % end]) msger.debug(part_cmd) rc, out = runner.runtool(part_cmd, catch=3) out = out.strip() if out: msger.debug('"parted" output: %s' % out) return rc
def do_genchecksum(self, image_name): if not self._genchecksum: return """ Generate md5sum if /usr/bin/md5sum is available """ if os.path.exists("/usr/bin/md5sum"): (rc, md5sum) = runner.runtool(["/usr/bin/md5sum", "-b", image_name]) if rc != 0: msger.warning("Can't generate md5sum for image %s" % image_name) else: pattern = re.compile("\*.*$") md5sum = pattern.sub("*" + os.path.basename(image_name), md5sum) fd = open(image_name + ".md5sum", "w") fd.write(md5sum) fd.close() self.outimage.append(image_name + ".md5sum")
def do_genchecksum(self, image_name): if not self._genchecksum: return """ Generate md5sum if /usr/bin/md5sum is available """ if os.path.exists("/usr/bin/md5sum"): (rc, md5sum) = runner.runtool(["/usr/bin/md5sum", "-b", image_name]) if rc != 0: msger.warning("Can't generate md5sum for image %s" % image_name) else: pattern = re.compile("\*.*$") md5sum = pattern.sub("*" + os.path.basename(image_name), md5sum) fd = open(image_name + ".md5sum", "w") fd.write(md5sum) fd.close() self.outimage.append(image_name+".md5sum")
def __run_parted(self, args): """ Run parted with arguments specified in the 'args' list. """ args.insert(0, self.parted) msger.debug(args) rc, out = runner.runtool(args, catch = 3) out = out.strip() if out: msger.debug('"parted" output: %s' % out) if rc != 0: # We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. This # prevents us from distinguishing real errors based on return # code. msger.debug("WARNING: parted returned '%s' instead of 0" % rc)
def __get_subvolume_id(self, rootpath, subvol): if not self.btrfscmd: self.btrfscmd=find_binary_path("btrfs") argv = [ self.btrfscmd, "subvolume", "list", rootpath ] rc, out = runner.runtool(argv) msger.debug(out) if rc != 0: raise MountError("Failed to get subvolume id from %s', return code: %d." % (rootpath, rc)) subvolid = -1 for line in out.splitlines(): if line.endswith(" path %s" % subvol): subvolid = line.split()[1] if not subvolid.isdigit(): raise MountError("Invalid subvolume id: %s" % subvolid) subvolid = int(subvolid) break return subvolid
def __format_filesystem(self): if self.skipformat: msger.debug("Skip filesystem format.") return msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) cmdlist = [self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b", str(self.blocksize), "-U", self.uuid] if self.extopts: cmdlist.extend(self.extopts.split()) cmdlist.extend([self.disk.device]) rc, errout = runner.runtool(cmdlist, catch=2) if rc != 0: raise MountError("Error creating %s filesystem on disk %s:\n%s" % (self.fstype, self.disk.device, errout)) if not self.extopts: msger.debug("Tuning filesystem on %s" % self.disk.device) runner.show([self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device])
def get_loop_device(losetupcmd, lofile): global DEVICE_PIDFILE_DIR global DEVICE_LOCKFILE import fcntl makedirs(os.path.dirname(DEVICE_LOCKFILE)) fp = open(DEVICE_LOCKFILE, 'w') fcntl.flock(fp, fcntl.LOCK_EX) try: loopdev = None devinst = LoopDevice() # clean up left loop device first clean_loop_devices() # provide an avaible loop device rc, out = runner.runtool([losetupcmd, "--find"]) if rc == 0: loopdev = out.split()[0] devinst.register(loopdev) if not loopdev or not os.path.exists(loopdev): devinst.create() loopdev = devinst.device # setup a loop device for image file rc = runner.show([losetupcmd, loopdev, lofile]) if rc != 0: raise MountError("Failed to setup loop device for '%s'" % lofile) devinst.reg_atexit() # try to save device and pid makedirs(DEVICE_PIDFILE_DIR) pidfile = os.path.join(DEVICE_PIDFILE_DIR, os.path.basename(loopdev)) if os.path.exists(pidfile): os.unlink(pidfile) with open(pidfile, 'w') as wf: wf.write(str(os.getpid())) except MountError, err: raise CreatorError("%s" % str(err))
def get_loop_device(losetupcmd, lofile): global DEVICE_PIDFILE_DIR global DEVICE_LOCKFILE import fcntl makedirs(os.path.dirname(DEVICE_LOCKFILE)) fp = open(DEVICE_LOCKFILE, 'w') fcntl.flock(fp, fcntl.LOCK_EX) try: loopdev = None devinst = LoopDevice() # clean up left loop device first clean_loop_devices() # provide an avaible loop device rc, out = runner.runtool([losetupcmd, "--find"]) if rc == 0 and out: loopdev = out.split()[0] devinst.register(loopdev) if not loopdev or not os.path.exists(loopdev): devinst.create() loopdev = devinst.device # setup a loop device for image file rc = runner.show([losetupcmd, loopdev, lofile]) if rc != 0: raise MountError("Failed to setup loop device for '%s'" % lofile) devinst.reg_atexit() # try to save device and pid makedirs(DEVICE_PIDFILE_DIR) pidfile = os.path.join(DEVICE_PIDFILE_DIR, os.path.basename(loopdev)) if os.path.exists(pidfile): os.unlink(pidfile) with open(pidfile, 'w') as wf: wf.write(str(os.getpid())) except MountError, err: raise CreatorError("%s" % str(err))
def __format_disks(self): msger.debug("Assigning partitions to disks") mbr_sector_skipped = False for n in range(len(self.partitions)): p = self.partitions[n] if not self.disks.has_key(p['disk']): raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint'])) if not mbr_sector_skipped: # This hack is used to remove one sector from the first partition, # that is the used to the MBR. p['size'] -= 1 mbr_sector_skipped = True d = self.disks[p['disk']] d['numpart'] += 1 if d['numpart'] > 3: # Increase allocation of extended partition to hold this partition d['extended'] += p['size'] p['type'] = 'logical' p['num'] = d['numpart'] + 1 else: p['type'] = 'primary' p['num'] = d['numpart'] p['start'] = d['offset'] d['offset'] += p['size'] d['partitions'].append(n) msger.debug("Assigned %s to %s%d at %d at size %d" % (p['mountpoint'], p['disk'], p['num'], p['start'], p['size'])) if self.skipformat: msger.debug("Skipping disk format, because skipformat flag is set.") return for dev in self.disks.keys(): d = self.disks[dev] msger.debug("Initializing partition table for %s" % (d['disk'].device)) rc, out = runner.runtool([self.parted, "-s", d['disk'].device, "mklabel", "msdos"], catch=3) out = out.strip() if out: msger.debug('"parted" output: %s' % out) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition-table for disk '%s'." % (p1.returncode,d['disk'].device)) msger.debug("Creating partitions") for p in self.partitions: d = self.disks[p['disk']] if p['num'] == 5: self.__create_part_to_image(d['disk'].device,"extended",None,p['start'],d['extended']) if p['fstype'] == "swap": parted_fs_type = "linux-swap" elif p['fstype'] == "vfat": parted_fs_type = "fat32" elif p['fstype'] == "msdos": parted_fs_type = "fat16" else: # Type for ext2/ext3/ext4/btrfs parted_fs_type = "ext2" # Boot ROM of OMAP boards require vfat boot partition to have an # even number of sectors. if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat","msdos"] and p['size'] % 2: msger.debug("Substracting one sector from '%s' partition to get even number of sectors for the partition." % (p['mountpoint'])) p['size'] -= 1 ret = self.__create_part_to_image(d['disk'].device,p['type'], parted_fs_type, p['start'], p['size']) if ret != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition '%s' for disk '%s'." % (p1.returncode,p['mountpoint'],d['disk'].device)) if p['boot']: msger.debug("Setting boot flag for partition '%s' on disk '%s'." % (p['num'],d['disk'].device)) boot_cmd = [self.parted, "-s", d['disk'].device, "set", "%d" % p['num'], "boot", "on"] msger.debug(boot_cmd) rc = runner.show(boot_cmd) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.warning("parted returned '%s' instead of 0 when adding boot flag for partition '%s' disk '%s'." % (rc,p['num'],d['disk'].device))
def testRuntoolCatch1(self): (rc, out) = runner.runtool("echo hello", catch=1) self.assertEqual(0, rc) self.assertEqual("hello\n", out)
def __map_partitions(self): """Load it if dm_snapshot isn't loaded. """ load_module("dm_snapshot") for dev in self.disks.keys(): d = self.disks[dev] if d['mapped']: continue msger.debug("Running kpartx on %s" % d['disk'].device) rc, kpartx_output = runner.runtool( [self.kpartx, "-l", "-v", d['disk'].device]) kpartx_output = kpartx_output.splitlines() if rc != 0: raise MountError("Failed to query partition mapping for '%s'" % d['disk'].device) # Strip trailing blank and mask verbose output i = 0 while i < len(kpartx_output) and kpartx_output[i][0:4] != "loop": i = i + 1 kpartx_output = kpartx_output[i:] # Make sure kpartx reported the right count of partitions if len(kpartx_output) != d['numpart']: # If this disk has more than 3 partitions, then in case of MBR # paritions there is an extended parition. Different versions # of kpartx behave differently WRT the extended partition - # some map it, some ignore it. This is why we do the below hack # - if kpartx reported one more partition and the partition # table type is "msdos" and the amount of partitions is more # than 3, we just assume kpartx mapped the extended parition # and we remove it. if len(kpartx_output) == d['numpart'] + 1 \ and d['ptable_format'] == 'msdos' and len(kpartx_output) > 3: kpartx_output.pop(3) else: raise MountError("Unexpected number of partitions from " \ "kpartx: %d != %d" % \ (len(kpartx_output), d['numpart'])) for i in range(len(kpartx_output)): line = kpartx_output[i] newdev = line.split()[0] mapperdev = "/dev/mapper/" + newdev loopdev = d['disk'].device + newdev[-1] msger.debug("Dev %s: %s -> %s" % (newdev, loopdev, mapperdev)) pnum = d['partitions'][i] self.partitions[pnum]['device'] = loopdev self.partitions[pnum]['mapper_device'] = mapperdev # grub's install wants partitions to be named # to match their parent device + partition num # kpartx doesn't work like this, so we add compat # symlinks to point to /dev/mapper if os.path.lexists(loopdev): os.unlink(loopdev) os.symlink(mapperdev, loopdev) msger.debug("Adding partx mapping for %s" % d['disk'].device) rc = runner.show([self.kpartx, "-v", "-sa", d['disk'].device]) if rc != 0: # Make sure that the device maps are also removed on error case. # The d['mapped'] isn't set to True if the kpartx fails so # failed mapping will not be cleaned on cleanup either. runner.quiet([self.kpartx, "-sd", d['disk'].device]) raise MountError("Failed to map partitions for '%s'" % d['disk'].device) for p in self.partitions: if p['mapper_device'] and os.path.islink(p['mapper_device']): p['mpath_device'] = resolve_ref(p['mapper_device']) else: p['mpath_device'] = '' # FIXME: need a better way to fix the latency import time time.sleep(1) if not os.path.exists(mapperdev): # load mapper device if not updated runner.quiet([self.dmsetup, "mknodes"]) # still not updated, roll back if not os.path.exists(mapperdev): runner.quiet([self.kpartx, "-sd", d['disk'].device]) raise MountError("Failed to load mapper devices for '%s'" % d['disk'].device) d['mapped'] = True
def __format_disks(self): msger.debug("Assigning partitions to disks") mbr_sector_skipped = False # Go through partitions in the order they are added in .ks file for n in range(len(self.partitions)): p = self.partitions[n] if not self.disks.has_key(p['disk']): raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint'])) if not mbr_sector_skipped: # This hack is used to remove one sector from the first partition, # that is the used to the MBR. p['size'] -= 1 mbr_sector_skipped = True # Get the disk where the partition is located d = self.disks[p['disk']] d['numpart'] += 1 # alignment in sectors align_sectors = None # if first partition then we need to skip the first sector # where the MBR is located, if the alignment isn't set # See: https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey if d['numpart'] == 1: if p['align'] and p['align'] > 0: align_sectors = p['align'] * 1024 / self.sector_size else: align_sectors = MBR_SECTOR_LEN elif p['align']: # If not first partition and we do have alignment set we need # to align the partition. # FIXME: This leaves a empty spaces to the disk. To fill the # gaps we could enlargea the previous partition? # Calc how much the alignment is off. align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size) # We need to move forward to the next alignment point align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors if align_sectors: if p['align'] and p['align'] > 0: msger.debug( "Realignment for %s%s with %s sectors, original" " offset %s, target alignment is %sK." % (p['disk'], d['numpart'], align_sectors, d['offset'], p['align'])) # p['size'] already converted in secctors if p['size'] <= align_sectors: raise MountError("Partition for %s is too small to handle " "the alignment change." % p['mountpoint']) # increase the offset so we actually start the partition on right alignment d['offset'] += align_sectors if d['numpart'] > 3: # Increase allocation of extended partition to hold this partition d['extended'] += p['size'] p['type'] = 'logical' p['num'] = d['numpart'] + 1 else: p['type'] = 'primary' p['num'] = d['numpart'] p['start'] = d['offset'] d['offset'] += p['size'] d['partitions'].append(n) msger.debug( "Assigned %s to %s%d at Sector %d with size %d sectors " "/ %d bytes." % (p['mountpoint'], p['disk'], p['num'], p['start'], p['size'], p['size'] * self.sector_size)) if self.skipformat: msger.debug( "Skipping disk format, because skipformat flag is set.") return for dev in self.disks.keys(): d = self.disks[dev] msger.debug("Initializing partition table for %s" % (d['disk'].device)) rc, out = runner.runtool( [self.parted, "-s", d['disk'].device, "mklabel", "msdos"], catch=3) out = out.strip() if out: msger.debug('"parted" output: %s' % out) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug( "WARNING: parted returned '%s' instead of 0 when creating partition-table for disk '%s'." % (rc, d['disk'].device)) msger.debug("Creating partitions") for p in self.partitions: d = self.disks[p['disk']] if p['num'] == 5: self.__create_part_to_image(d['disk'].device, "extended", None, p['start'], d['extended']) if p['fstype'] == "swap": parted_fs_type = "linux-swap" elif p['fstype'] == "vfat": parted_fs_type = "fat32" elif p['fstype'] == "msdos": parted_fs_type = "fat16" else: # Type for ext2/ext3/ext4/btrfs parted_fs_type = "ext2" # Boot ROM of OMAP boards require vfat boot partition to have an # even number of sectors. if p['mountpoint'] == "/boot" and p['fstype'] in [ "vfat", "msdos" ] and p['size'] % 2: msger.debug( "Substracting one sector from '%s' partition to get even number of sectors for the partition." % (p['mountpoint'])) p['size'] -= 1 ret = self.__create_part_to_image(d['disk'].device, p['type'], parted_fs_type, p['start'], p['size']) if ret != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug( "WARNING: parted returned '%s' instead of 0 when creating partition '%s' for disk '%s'." % (ret, p['mountpoint'], d['disk'].device)) if p['boot']: msger.debug( "Setting boot flag for partition '%s' on disk '%s'." % (p['num'], d['disk'].device)) boot_cmd = [ self.parted, "-s", d['disk'].device, "set", "%d" % p['num'], "boot", "on" ] msger.debug(boot_cmd) rc = runner.show(boot_cmd) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.warning( "parted returned '%s' instead of 0 when adding boot flag for partition '%s' disk '%s'." % (rc, p['num'], d['disk'].device))
def __format_disks(self): msger.debug("Assigning partitions to disks") mbr_sector_skipped = False # Go through partitions in the order they are added in .ks file for n in range(len(self.partitions)): p = self.partitions[n] if not self.disks.has_key(p['disk']): raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint'])) if not mbr_sector_skipped: # This hack is used to remove one sector from the first partition, # that is the used to the MBR. p['size'] -= 1 mbr_sector_skipped = True # Get the disk where the partition is located d = self.disks[p['disk']] d['numpart'] += 1 # alignment in sectors align_sectors = None # if first partition then we need to skip the first sector # where the MBR is located, if the alignment isn't set # See: https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey if d['numpart'] == 1: if p['align'] and p['align'] > 0: align_sectors = p['align'] * 1024 / self.sector_size else: align_sectors = MBR_SECTOR_LEN elif p['align']: # If not first partition and we do have alignment set we need # to align the partition. # FIXME: This leaves a empty spaces to the disk. To fill the # gaps we could enlargea the previous partition? # Calc how much the alignment is off. align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size) # We need to move forward to the next alignment point align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors if align_sectors: if p['align'] and p['align'] > 0: msger.debug("Realignment for %s%s with %s sectors, original" " offset %s, target alignment is %sK." % (p['disk'], d['numpart'], align_sectors, d['offset'], p['align'])) # p['size'] already converted in secctors if p['size'] <= align_sectors: raise MountError("Partition for %s is too small to handle " "the alignment change." % p['mountpoint']) # increase the offset so we actually start the partition on right alignment d['offset'] += align_sectors if d['numpart'] > 3: # Increase allocation of extended partition to hold this partition d['extended'] += p['size'] p['type'] = 'logical' p['num'] = d['numpart'] + 1 else: p['type'] = 'primary' p['num'] = d['numpart'] p['start'] = d['offset'] d['offset'] += p['size'] d['partitions'].append(n) msger.debug("Assigned %s to %s%d at Sector %d with size %d sectors " "/ %d bytes." % (p['mountpoint'], p['disk'], p['num'], p['start'], p['size'], p['size'] * self.sector_size)) if self.skipformat: msger.debug("Skipping disk format, because skipformat flag is set.") return for dev in self.disks.keys(): d = self.disks[dev] msger.debug("Initializing partition table for %s" % (d['disk'].device)) rc, out = runner.runtool([self.parted, "-s", d['disk'].device, "mklabel", "msdos"], catch=3) out = out.strip() if out: msger.debug('"parted" output: %s' % out) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition-table for disk '%s'." % (rc, d['disk'].device)) msger.debug("Creating partitions") for p in self.partitions: d = self.disks[p['disk']] if p['num'] == 5: self.__create_part_to_image(d['disk'].device,"extended",None,p['start'],d['extended']) if p['fstype'] == "swap": parted_fs_type = "linux-swap" elif p['fstype'] == "vfat": parted_fs_type = "fat32" elif p['fstype'] == "msdos": parted_fs_type = "fat16" else: # Type for ext2/ext3/ext4/btrfs parted_fs_type = "ext2" # Boot ROM of OMAP boards require vfat boot partition to have an # even number of sectors. if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat","msdos"] and p['size'] % 2: msger.debug("Substracting one sector from '%s' partition to get even number of sectors for the partition." % (p['mountpoint'])) p['size'] -= 1 ret = self.__create_part_to_image(d['disk'].device,p['type'], parted_fs_type, p['start'], p['size']) if ret != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition '%s' for disk '%s'." % (ret, p['mountpoint'], d['disk'].device)) if p['boot']: msger.debug("Setting boot flag for partition '%s' on disk '%s'." % (p['num'],d['disk'].device)) boot_cmd = [self.parted, "-s", d['disk'].device, "set", "%d" % p['num'], "boot", "on"] msger.debug(boot_cmd) rc = runner.show(boot_cmd) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.warning("parted returned '%s' instead of 0 when adding boot flag for partition '%s' disk '%s'." % (rc,p['num'],d['disk'].device))
def __format_disks(self): msger.debug("Assigning partitions to disks") mbr_sector_skipped = False for n in range(len(self.partitions)): p = self.partitions[n] if not self.disks.has_key(p['disk']): raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint'])) if not mbr_sector_skipped: # This hack is used to remove one sector from the first partition, # that is the used to the MBR. p['size'] -= 1 mbr_sector_skipped = True d = self.disks[p['disk']] d['numpart'] += 1 if d['numpart'] > 3: # Increase allocation of extended partition to hold this partition d['extended'] += p['size'] p['type'] = 'logical' p['num'] = d['numpart'] + 1 else: p['type'] = 'primary' p['num'] = d['numpart'] p['start'] = d['offset'] d['offset'] += p['size'] d['partitions'].append(n) msger.debug("Assigned %s to %s%d at %d at size %d" % (p['mountpoint'], p['disk'], p['num'], p['start'], p['size'])) if self.skipformat: msger.debug("Skipping disk format, because skipformat flag is set.") return for dev in self.disks.keys(): d = self.disks[dev] msger.debug("Initializing partition table for %s" % (d['disk'].device)) rc, out = runner.runtool([self.parted, "-s", d['disk'].device, "mklabel", "msdos"], catch=3) out = out.strip() if out: msger.debug('"parted" output: %s' % out) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition-table for disk '%s'." % (rc, d['disk'].device)) msger.debug("Creating partitions") for p in self.partitions: d = self.disks[p['disk']] if p['num'] == 5: self.__create_part_to_image(d['disk'].device,"extended",None,p['start'],d['extended']) if p['fstype'] == "swap": parted_fs_type = "linux-swap" elif p['fstype'] == "vfat": parted_fs_type = "fat32" elif p['fstype'] == "msdos": parted_fs_type = "fat16" else: # Type for ext2/ext3/ext4/btrfs parted_fs_type = "ext2" # Boot ROM of OMAP boards require vfat boot partition to have an # even number of sectors. if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat","msdos"] and p['size'] % 2: msger.debug("Substracting one sector from '%s' partition to get even number of sectors for the partition." % (p['mountpoint'])) p['size'] -= 1 ret = self.__create_part_to_image(d['disk'].device,p['type'], parted_fs_type, p['start'], p['size']) if ret != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition '%s' for disk '%s'." % (ret, p['mountpoint'], d['disk'].device)) if p['boot']: msger.debug("Setting boot flag for partition '%s' on disk '%s'." % (p['num'],d['disk'].device)) boot_cmd = [self.parted, "-s", d['disk'].device, "set", "%d" % p['num'], "boot", "on"] msger.debug(boot_cmd) rc = runner.show(boot_cmd) if rc != 0: # NOTE: We don't throw exception when return code is not 0, because # parted always fails to reload part table with loop devices. # This prevents us from distinguishing real errors based on return code. msger.warning("parted returned '%s' instead of 0 when adding boot flag for partition '%s' disk '%s'." % (rc,p['num'],d['disk'].device))
def __map_partitions(self): """Load it if dm_snapshot isn't loaded. """ load_module("dm_snapshot") for dev in self.disks.keys(): d = self.disks[dev] if d['mapped']: continue msger.debug("Running kpartx on %s" % d['disk'].device ) rc, kpartxOutput = runner.runtool([self.kpartx, "-l", "-v", d['disk'].device]) kpartxOutput = kpartxOutput.splitlines() if rc != 0: raise MountError("Failed to query partition mapping for '%s'" % d['disk'].device) # Strip trailing blank and mask verbose output i = 0 while i < len(kpartxOutput) and kpartxOutput[i][0:4] != "loop": i = i + 1 kpartxOutput = kpartxOutput[i:] # Make sure kpartx reported the right count of partitions if len(kpartxOutput) != d['numpart']: # If this disk has more than 3 partitions, then in case of MBR # paritions there is an extended parition. Different versions # of kpartx behave differently WRT the extended partition - # some map it, some ignore it. This is why we do the below hack # - if kpartx reported one more partition and the partition # table type is "msdos" and the amount of partitions is more # than 3, we just assume kpartx mapped the extended parition # and we remove it. if len(kpartxOutput) == d['numpart'] + 1 \ and d['ptable_format'] == 'msdos' and len(kpartxOutput) > 3: kpartxOutput.pop(3) else: raise MountError("Unexpected number of partitions from " \ "kpartx: %d != %d" % \ (len(kpartxOutput), d['numpart'])) for i in range(len(kpartxOutput)): line = kpartxOutput[i] newdev = line.split()[0] mapperdev = "/dev/mapper/" + newdev loopdev = d['disk'].device + newdev[-1] msger.debug("Dev %s: %s -> %s" % (newdev, loopdev, mapperdev)) pnum = d['partitions'][i] self.partitions[pnum]['device'] = loopdev # grub's install wants partitions to be named # to match their parent device + partition num # kpartx doesn't work like this, so we add compat # symlinks to point to /dev/mapper if os.path.lexists(loopdev): os.unlink(loopdev) os.symlink(mapperdev, loopdev) msger.debug("Adding partx mapping for %s" % d['disk'].device) rc = runner.show([self.kpartx, "-v", "-a", d['disk'].device]) if rc != 0: # Make sure that the device maps are also removed on error case. # The d['mapped'] isn't set to True if the kpartx fails so # failed mapping will not be cleaned on cleanup either. runner.quiet([self.kpartx, "-d", d['disk'].device]) raise MountError("Failed to map partitions for '%s'" % d['disk'].device) # FIXME: there is a bit delay for multipath device setup, # wait 10ms for the setup import time time.sleep(10) d['mapped'] = True