def _mount_instroot(self, base_on = None): parts = self._get_parts() self.__instloop = PartitionedMount(self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, p.label, fsopts = p.fsopts, boot = p.active, align = p.align, part_type = p.part_type) self.__instloop.layout_partitions(self._ptable_format) # Create the disks self.__imgdir = self._mkdtemp() for disk_name, disk in self.__instloop.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "raw") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.SparseLoopbackDisk(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instloop.add_disk(disk_name, disk_obj) self.__instloop.mount() self._create_mkinitrd_config()
def _mount_instroot(self, base_on=None): self.__imgdir = self._mkdtemp() parts = self._get_parts() #create disk for item in self.get_diskinfo(): msger.debug("Adding disk %s as %s/%s-%s.raw" % (item['name'], self.__imgdir, self.name, item['name'])) disk = fs_related.SparseLoopbackDisk( "%s/%s-%s.raw" % (self.__imgdir, self.name, item['name']), item['size']) self.__disks[item['name']] = disk self.__instloop = PartitionedMount(self.__disks, self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, fsopts=p.fsopts, boot=p.active) self.__instloop.mount() self._create_mkinitrd_config()
def do_unpack(cls, srcimg): srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L srcmnt = misc.mkdtemp("srcmnt") disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize) srcloop = PartitionedMount({'/dev/sdb': disk}, srcmnt, skipformat=True) srcloop.add_partition(srcimgsize / 1024 / 1024, "/dev/sdb", "/", "ext3", boot=False) try: srcloop.mount() except errors.MountError: srcloop.cleanup() raise image = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"), "target.img") args = [ 'dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image ] msger.info("`dd` image ...") rc = runner.show(args) srcloop.cleanup() shutil.rmtree(os.path.dirname(srcmnt), ignore_errors=True) if rc != 0: raise errors.CreatorError("Failed to dd") else: return image
def do_unpack(cls, srcimg): srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L srcmnt = misc.mkdtemp("srcmnt") disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize) srcloop = PartitionedMount({'/dev/sdb':disk}, srcmnt, skipformat = True) srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False) try: srcloop.mount() except errors.MountError: srcloop.cleanup() raise image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img") args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image] msger.info("`dd` image ...") rc = runner.show(args) srcloop.cleanup() shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True) if rc != 0: raise errors.CreatorError("Failed to dd") else: return image
def do_unpack(cls, srcimg): img = srcimg imgsize = misc.get_file_size(img) * 1024L * 1024L imgmnt = misc.mkdtemp() disk = fs_related.SparseLoopbackDisk(img, imgsize) imgloop = PartitionedMount({'/dev/sdb': disk}, imgmnt, skipformat=True) imgloop.add_partition(imgsize / 1024 / 1024, "/dev/sdb", "/", "vfat", boot=False) try: imgloop.mount() except errors.MountError: imgloop.cleanup() raise # legacy LiveOS filesystem layout support, remove for F9 or F10 if os.path.exists(imgmnt + "/squashfs.img"): squashimg = imgmnt + "/squashfs.img" else: squashimg = imgmnt + "/LiveOS/squashfs.img" tmpoutdir = misc.mkdtemp() # unsquashfs requires outdir mustn't exist shutil.rmtree(tmpoutdir, ignore_errors=True) misc.uncompress_squashfs(squashimg, tmpoutdir) try: # legacy LiveOS filesystem layout support, remove for F9 or F10 if os.path.exists(tmpoutdir + "/os.img"): os_image = tmpoutdir + "/os.img" else: os_image = tmpoutdir + "/LiveOS/ext3fs.img" if not os.path.exists(os_image): raise errors.CreatorError( "'%s' is not a valid live CD ISO : neither " "LiveOS/ext3fs.img nor os.img exist" % img) imgname = os.path.basename(srcimg) imgname = os.path.splitext(imgname)[0] + ".img" rtimage = os.path.join( tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"), imgname) shutil.copyfile(os_image, rtimage) finally: imgloop.cleanup() shutil.rmtree(tmpoutdir, ignore_errors=True) shutil.rmtree(imgmnt, ignore_errors=True) return rtimage
def _mount_instroot(self, base_on = None): self.__imgdir = self._mkdtemp() parts = self._get_parts() #create disk for item in self.get_diskinfo(): msger.debug("Adding disk %s as %s/%s-%s.raw with size %s bytes" % (item['name'], self.__imgdir, self.name, item['name'], item['size'])) disk = fs_related.SparseLoopbackDisk("%s/%s-%s.raw" % ( self.__imgdir, self.name, item['name']), item['size']) self.__disks[item['name']] = disk self.__instloop = PartitionedMount(self.__disks, self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, fsopts = p.fsopts, boot = p.active, align = p.align) self.__instloop.mount() self._create_mkinitrd_config()
def do_unpack(cls, srcimg): img = srcimg imgsize = misc.get_file_size(img) * 1024L * 1024L imgmnt = misc.mkdtemp() disk = fs_related.SparseLoopbackDisk(img, imgsize) imgloop = PartitionedMount({"/dev/sdb": disk}, imgmnt, skipformat=True) imgloop.add_partition(imgsize / 1024 / 1024, "/dev/sdb", "/", "vfat", boot=False) try: imgloop.mount() except errors.MountError: imgloop.cleanup() raise # legacy LiveOS filesystem layout support, remove for F9 or F10 if os.path.exists(imgmnt + "/squashfs.img"): squashimg = imgmnt + "/squashfs.img" else: squashimg = imgmnt + "/LiveOS/squashfs.img" tmpoutdir = misc.mkdtemp() # unsquashfs requires outdir mustn't exist shutil.rmtree(tmpoutdir, ignore_errors=True) misc.uncompress_squashfs(squashimg, tmpoutdir) try: # legacy LiveOS filesystem layout support, remove for F9 or F10 if os.path.exists(tmpoutdir + "/os.img"): os_image = tmpoutdir + "/os.img" else: os_image = tmpoutdir + "/LiveOS/ext3fs.img" if not os.path.exists(os_image): raise errors.CreatorError( "'%s' is not a valid live CD ISO : neither " "LiveOS/ext3fs.img nor os.img exist" % img ) imgname = os.path.basename(srcimg) imgname = os.path.splitext(imgname)[0] + ".img" rtimage = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"), imgname) shutil.copyfile(os_image, rtimage) finally: imgloop.cleanup() shutil.rmtree(tmpoutdir, ignore_errors=True) shutil.rmtree(imgmnt, ignore_errors=True) return rtimage
def _mount_instroot(self, base_on = None): self.__imgdir = self._mkdtemp() #Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) #list of partitions from kickstart file parts = kickstart.get_partitions(self.ks) #list of disks where a disk is an dict with name: and size disks = [] for i in range(len(parts)): if parts[i].disk: disk = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk specified in partition line of ks file") if not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype specified in partition line of ks file") size = parts[i].size * 1024L * 1024L found = False for j in range(len(disks)): if disks[j]['name'] == disk: disks[j]['size'] = disks[j]['size'] + size found = True break else: found = False if not found: disks.append({ 'name': disk, 'size': size }) #create disk for item in disks: msger.debug("Adding disk %s as %s/%s-%s.raw" % (item['name'], self.__imgdir,self.name, item['name'])) disk = fs_related.SparseLoopbackDisk("%s/%s-%s.raw" % (self.__imgdir,self.name, item['name']),item['size']) self.__disks[item['name']] = disk self.__instloop = PartitionedMount(self.__disks, self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, fsopts = p.fsopts, boot = p.active) self.__instloop.mount() self._create_mkinitrd_config()
class RawImageCreator(BaseImageCreator): """Installs a system into a file containing a partitioned disk image. ApplianceImageCreator is an advanced ImageCreator subclass; a sparse file is formatted with a partition table, each partition loopback mounted and the system installed into an virtual disk. The disk image can subsequently be booted in a virtual machine or accessed with kpartx """ def __init__(self, *args): """Initialize a ApplianceImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, *args) self.__instloop = None self.__imgdir = None self.__disks = {} self.__disk_format = "raw" self._diskinfo = [] self.vmem = 512 self.vcpu = 1 self.checksum = False self.appliance_version = None self.appliance_release = None #self.getsource = False #self.listpkg = False self._dep_checks.extend(["sync", "kpartx", "parted", "extlinux"]) def configure(self, repodata=None): import subprocess def chroot(): os.chroot(self._instroot) os.chdir("/") if os.path.exists(self._instroot + "/usr/bin/Xorg"): subprocess.call(["/bin/chmod", "u+s", "/usr/bin/Xorg"], preexec_fn=chroot) BaseImageCreator.configure(self, repodata) def _get_fstab(self): s = "" for mp in self.__instloop.mountOrder: p = None for p1 in self.__instloop.partitions: if p1['mountpoint'] == mp: p = p1 break s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': "/dev/%s%-d" % (p['disk'], p['num']), 'mountpoint': p['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not p['fsopts'] else p['fsopts'] } if p['mountpoint'] == "/": for subvol in self.__instloop.subvolumes: if subvol['mountpoint'] == "/": continue s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': "/dev/%s%-d" % (p['disk'], p['num']), 'mountpoint': subvol['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not subvol['fsopts'] else subvol['fsopts'] } s += "devpts /dev/pts devpts gid=5,mode=620 0 0\n" s += "tmpfs /dev/shm tmpfs defaults 0 0\n" s += "proc /proc proc defaults 0 0\n" s += "sysfs /sys sysfs defaults 0 0\n" return s def _create_mkinitrd_config(self): #write to tell which modules to be included in initrd mkinitrd = "" mkinitrd += "PROBE=\"no\"\n" mkinitrd += "MODULES+=\"ext3 ata_piix sd_mod libata scsi_mod\"\n" mkinitrd += "rootfs=\"ext3\"\n" mkinitrd += "rootopts=\"defaults\"\n" msger.debug("Writing mkinitrd config %s/etc/sysconfig/mkinitrd" \ % self._instroot) os.makedirs(self._instroot + "/etc/sysconfig/", mode=644) cfg = open(self._instroot + "/etc/sysconfig/mkinitrd", "w") cfg.write(mkinitrd) cfg.close() def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_diskinfo(self): if self._diskinfo: return self._diskinfo #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified in partition line of ks file") size = parts[i].size * 1024L * 1024L found = False for j in range(len(self._diskinfo)): if self._diskinfo[j]['name'] == disk: self._diskinfo[j][ 'size'] = self._diskinfo[j]['size'] + size found = True break else: found = False if not found: self._diskinfo.append({'name': disk, 'size': size}) return self._diskinfo # # Actual implemention # def _mount_instroot(self, base_on=None): self.__imgdir = self._mkdtemp() parts = self._get_parts() #create disk for item in self.get_diskinfo(): msger.debug("Adding disk %s as %s/%s-%s.raw" % (item['name'], self.__imgdir, self.name, item['name'])) disk = fs_related.SparseLoopbackDisk( "%s/%s-%s.raw" % (self.__imgdir, self.name, item['name']), item['size']) self.__disks[item['name']] = disk self.__instloop = PartitionedMount(self.__disks, self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, fsopts=p.fsopts, boot=p.active) self.__instloop.mount() self._create_mkinitrd_config() def _get_required_packages(self): required_packages = BaseImageCreator._get_required_packages(self) if not self.target_arch or not self.target_arch.startswith("arm"): required_packages += ["syslinux", "syslinux-extlinux"] return required_packages def _get_excluded_packages(self): return BaseImageCreator._get_excluded_packages(self) def _get_syslinux_boot_config(self): bootdevnum = None rootdevnum = None rootdev = None for p in self.__instloop.partitions: if p['mountpoint'] == "/boot": bootdevnum = p['num'] - 1 elif p['mountpoint'] == "/" and bootdevnum is None: bootdevnum = p['num'] - 1 if p['mountpoint'] == "/": rootdevnum = p['num'] - 1 rootdev = "/dev/%s%-d" % (p['disk'], p['num']) prefix = "" if bootdevnum == rootdevnum: prefix = "/boot" return (bootdevnum, rootdevnum, rootdev, prefix) def _create_syslinux_config(self): #Copy splash splash = "%s/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg" \ % self._instroot if os.path.exists(splash): shutil.copy(splash, "%s%s/splash.jpg" \ % (self._instroot, "/boot/extlinux")) splashline = "menu background splash.jpg" else: splashline = "" (bootdevnum, rootdevnum, rootdev, prefix) = \ self._get_syslinux_boot_config() options = self.ks.handler.bootloader.appendLine #XXX don't hardcode default kernel - see livecd code syslinux_conf = "" syslinux_conf += "prompt 0\n" syslinux_conf += "timeout 1\n" syslinux_conf += "\n" syslinux_conf += "default vesamenu.c32\n" syslinux_conf += "menu autoboot Starting %s...\n" % self.distro_name syslinux_conf += "menu hidden\n" syslinux_conf += "\n" syslinux_conf += "%s\n" % splashline syslinux_conf += "menu title Welcome to %s!\n" % self.distro_name syslinux_conf += "menu color border 0 #ffffffff #00000000\n" syslinux_conf += "menu color sel 7 #ffffffff #ff000000\n" syslinux_conf += "menu color title 0 #ffffffff #00000000\n" syslinux_conf += "menu color tabmsg 0 #ffffffff #00000000\n" syslinux_conf += "menu color unsel 0 #ffffffff #00000000\n" syslinux_conf += "menu color hotsel 0 #ff000000 #ffffffff\n" syslinux_conf += "menu color hotkey 7 #ffffffff #ff000000\n" syslinux_conf += "menu color timeout_msg 0 #ffffffff #00000000\n" syslinux_conf += "menu color timeout 0 #ffffffff #00000000\n" syslinux_conf += "menu color cmdline 0 #ffffffff #00000000\n" versions = [] kernels = self._get_kernel_versions() for kernel in kernels: for version in kernels[kernel]: versions.append(version) footlabel = 0 for v in versions: shutil.copy( "%s/boot/vmlinuz-%s" % (self._instroot, v), "%s%s/vmlinuz-%s" % (self._instroot, "/boot/extlinux/", v)) syslinux_conf += "label %s%d\n" \ % (self.distro_name.lower(), footlabel) syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) syslinux_conf += "\tkernel vmlinuz-%s\n" % v syslinux_conf += "\tappend ro root=%s quiet vga=current %s\n" \ % (rootdev, options) if footlabel == 0: syslinux_conf += "\tmenu default\n" footlabel += 1 msger.debug("Writing syslinux config %s/boot/extlinux/extlinux.conf" \ % self._instroot) cfg = open(self._instroot + "/boot/extlinux/extlinux.conf", "w") cfg.write(syslinux_conf) cfg.close() def _install_syslinux(self): i = 0 for name in self.__disks.keys(): loopdev = self.__disks[name].device i = i + 1 msger.debug("Installing syslinux bootloader to %s" % loopdev) (bootdevnum, rootdevnum, rootdev, prefix) = \ self._get_syslinux_boot_config() #Set MBR mbrsize = os.stat("%s/usr/share/syslinux/mbr.bin" \ % self._instroot)[stat.ST_SIZE] rc = runner.show([ 'dd', 'if=%s/usr/share/syslinux/mbr.bin' % self._instroot, 'of=' + loopdev ]) if rc != 0: raise MountError("Unable to set MBR to %s" % loopdev) #Set Bootable flag parted = fs_related.find_binary_path("parted") rc = runner.quiet([ parted, "-s", loopdev, "set", "%d" % (bootdevnum + 1), "boot", "on" ]) #XXX disabled return code check because parted always fails to #reload part table with loop devices. Annoying because we can't #distinguish this failure from real partition failures :-( if rc != 0 and 1 == 0: raise MountError("Unable to set bootable flag to %sp%d" \ % (loopdev, (bootdevnum + 1))) #Ensure all data is flushed to disk before doing syslinux install runner.quiet('sync') fullpathsyslinux = fs_related.find_binary_path("extlinux") rc = runner.show( [fullpathsyslinux, "-i", "%s/boot/extlinux" % self._instroot]) if rc != 0: raise MountError("Unable to install syslinux bootloader to %sp%d" \ % (loopdev, (bootdevnum + 1))) def _create_bootconfig(self): #If syslinux is available do the required configurations. if os.path.exists("%s/usr/share/syslinux/" % (self._instroot)) \ and os.path.exists("%s/boot/extlinux/" % (self._instroot)): self._create_syslinux_config() self._install_syslinux() def _unmount_instroot(self): if not self.__instloop is None: self.__instloop.cleanup() def _resparse(self, size=None): return self.__instloop.resparse(size) def _stage_final_image(self): """Stage the final system image in _outdir. write meta data """ self._resparse() msger.debug("moving disks to stage location") for name in self.__disks.keys(): src = "%s/%s-%s.raw" % (self.__imgdir, self.name, name) self._img_name = "%s-%s.%s" % (self.name, name, self.__disk_format) dst = "%s/%s" % (self._outdir, self._img_name) msger.debug("moving %s to %s" % (src, dst)) shutil.move(src, dst) self._write_image_xml() def _write_image_xml(self): imgarch = "i686" if self.target_arch and self.target_arch.startswith("arm"): imgarch = "arm" xml = "<image>\n" name_attributes = "" if self.appliance_version: name_attributes += " version='%s'" % self.appliance_version if self.appliance_release: name_attributes += " release='%s'" % self.appliance_release xml += " <name%s>%s</name>\n" % (name_attributes, self.name) xml += " <domain>\n" # XXX don't hardcode - determine based on the kernel we installed for # grub baremetal vs xen xml += " <boot type='hvm'>\n" xml += " <guest>\n" xml += " <arch>%s</arch>\n" % imgarch xml += " </guest>\n" xml += " <os>\n" xml += " <loader dev='hd'/>\n" xml += " </os>\n" i = 0 for name in self.__disks.keys(): xml += " <drive disk='%s-%s.%s' target='hd%s'/>\n" \ % (self.name,name, self.__disk_format,chr(ord('a')+i)) i = i + 1 xml += " </boot>\n" xml += " <devices>\n" xml += " <vcpu>%s</vcpu>\n" % self.vcpu xml += " <memory>%d</memory>\n" % (self.vmem * 1024) for network in self.ks.handler.network.network: xml += " <interface/>\n" xml += " <graphics/>\n" xml += " </devices>\n" xml += " </domain>\n" xml += " <storage>\n" if self.checksum is True: for name in self.__disks.keys(): diskpath = "%s/%s-%s.%s" \ % (self._outdir,self.name,name, self.__disk_format) disk_size = os.path.getsize(diskpath) meter_ct = 0 meter = progress.TextMeter() meter.start(size=disk_size, text="Generating disk signature for %s-%s.%s" \ % (self.name, name, self.__disk_format)) xml += " <disk file='%s-%s.%s' use='system' format='%s'>\n" \ % (self.name, name, self.__disk_format, self.__disk_format) try: import hashlib m1 = hashlib.sha1() m2 = hashlib.sha256() except: import sha m1 = sha.new() m2 = None f = open(diskpath, "r") while 1: chunk = f.read(65536) if not chunk: break m1.update(chunk) if m2: m2.update(chunk) meter.update(meter_ct) meter_ct = meter_ct + 65536 sha1checksum = m1.hexdigest() xml += " <checksum type='sha1'>%s</checksum>\n" \ % sha1checksum if m2: sha256checksum = m2.hexdigest() xml += " <checksum type='sha256'>%s</checksum>\n" \ % sha256checksum xml += " </disk>\n" else: for name in self.__disks.keys(): xml += " <disk file='%s-%s.%s' use='system' format='%s'/>\n"\ %(self.name, name, self.__disk_format, self.__disk_format) xml += " </storage>\n" xml += "</image>\n" msger.debug("writing image XML to %s/%s.xml" % (self._outdir, self.name)) cfg = open("%s/%s.xml" % (self._outdir, self.name), "w") cfg.write(xml) cfg.close()
class DirectImageCreator(BaseImageCreator): """ Installs a system into a file containing a partitioned disk image. DirectImageCreator is an advanced ImageCreator subclass; an image file is formatted with a partition table, each partition created from a rootfs or other OpenEmbedded build artifact and dd'ed into the virtual disk. The disk image can subsequently be dd'ed onto media and used on actual hardware. """ def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir, kernel_dir, native_sysroot, hdddir, staging_data_dir, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"): """ Initialize a DirectImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, creatoropts, pkgmgr) self.__instimage = None self.__imgdir = None self.__disks = {} self.__disk_format = "direct" self._disk_names = [] self._ptable_format = self.ks.handler.bootloader.ptable self.use_uuid = fstab_entry == "uuid" self.compress_image = compress_image self.bmap_needed = generate_bmap self.oe_builddir = oe_builddir if image_output_dir: self.tmpdir = image_output_dir self.cachedir = "%s/cache" % image_output_dir self.rootfs_dir = rootfs_dir self.bootimg_dir = bootimg_dir self.kernel_dir = kernel_dir self.native_sysroot = native_sysroot self.hdddir = hdddir self.staging_data_dir = staging_data_dir def __write_fstab(self, image_rootfs): """overriden to generate fstab (temporarily) in rootfs. This is called from mount_instroot, make sure it doesn't get called from BaseImage.mount()""" if image_rootfs is None: return None fstab = image_rootfs + "/etc/fstab" if not os.path.isfile(fstab): return None parts = self._get_parts() self._save_fstab(fstab) fstab_lines = self._get_fstab(fstab, parts) self._update_fstab(fstab_lines, parts) self._write_fstab(fstab, fstab_lines) return fstab def _update_fstab(self, fstab_lines, parts): """Assume partition order same as in wks""" for num, p in enumerate(parts, 1): if not p.mountpoint or p.mountpoint == "/" or p.mountpoint == "/boot": continue if self._ptable_format == 'msdos' and num > 3: device_name = "/dev/" + p.disk + str(num + 1) else: device_name = "/dev/" + p.disk + str(num) opts = "defaults" if p.fsopts: opts = p.fsopts fstab_entry = device_name + "\t" + \ p.mountpoint + "\t" + \ p.fstype + "\t" + \ opts + "\t0\t0\n" fstab_lines.append(fstab_entry) def _write_fstab(self, fstab, fstab_lines): fstab = open(fstab, "w") for line in fstab_lines: fstab.write(line) fstab.close() def _save_fstab(self, fstab): """Save the current fstab in rootfs""" shutil.copyfile(fstab, fstab + ".orig") def _restore_fstab(self, fstab): """Restore the saved fstab in rootfs""" if fstab is None: return shutil.move(fstab + ".orig", fstab) def _get_fstab(self, fstab, parts): """Return the desired contents of /etc/fstab.""" f = open(fstab, "r") fstab_contents = f.readlines() f.close() return fstab_contents def set_bootimg_dir(self, bootimg_dir): """ Accessor for bootimg_dir, the actual location used for the source of the bootimg. Should be set by source plugins (only if they change the default bootimg source) so the correct info gets displayed for print_outimage_info(). """ self.bootimg_dir = bootimg_dir def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_disk_names(self): """ Returns a list of physical target disk names (e.g., 'sdb') which will be created. """ if self._disk_names: return self._disk_names #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk_name = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if parts[i].mountpoint and not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified for partition with mountpoint " "'%s' in the ks file") self._disk_names.append(disk_name) return self._disk_names def _full_name(self, name, extention): """ Construct full file name for a file we generate. """ return "%s-%s.%s" % (self.name, name, extention) def _full_path(self, path, name, extention): """ Construct full file path to a file we generate. """ return os.path.join(path, self._full_name(name, extention)) def get_default_source_plugin(self): """ The default source plugin i.e. the plugin that's consulted for overall image generation tasks outside of any particular partition. For convenience, we just hang it off the bootloader handler since it's the one non-partition object in any setup. By default the default plugin is set to the same plugin as the /boot partition; since we hang it off the bootloader object, the default can be explicitly set using the --source bootloader param. """ return self.ks.handler.bootloader.source # # Actual implemention # def _mount_instroot(self, base_on = None): """ For 'wic', we already have our build artifacts and don't want to loop mount anything to install into, we just create filesystems from the artifacts directly and combine them into a partitioned image. We still want to reuse as much of the basic mic machinery though; despite the fact that we don't actually do loop or any other kind of mounting we still want to do many of the same things to prepare images, so we basically just adapt to the basic framework and reinterpret what 'mounting' means in our context. _instroot would normally be something like /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for installing packages, etc. We don't currently need to do that, so we simplify life by just using /var/tmp/wic/build as our workdir. """ parts = self._get_parts() self.__instimage = PartitionedMount(self._instroot) for p in parts: # as a convenience, set source to the boot partition source # instead of forcing it to be set via bootloader --source if not self.ks.handler.bootloader.source and p.mountpoint == "/boot": self.ks.handler.bootloader.source = p.source for p in parts: # need to create the filesystems in order to get their # sizes before we can add them and do the layout. # PartitionedMount.mount() actually calls __format_disks() # to create the disk images and carve out the partitions, # then self.install() calls PartitionedMount.install() # which calls __install_partitition() for each partition # to dd the fs into the partitions. It would be nice to # be able to use e.g. ExtDiskMount etc to create the # filesystems, since that's where existing e.g. mkfs code # is, but those are only created after __format_disks() # which needs the partition sizes so needs them created # before its called. Well, the existing setup is geared # to installing packages into mounted filesystems - maybe # when/if we need to actually do package selection we # should modify things to use those objects, but for now # we can avoid that. fstab = self.__write_fstab(self.rootfs_dir.get("ROOTFS_DIR")) p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) self._restore_fstab(fstab) self.__instimage.add_partition(int(p.size), p.disk, p.mountpoint, p.source_file, p.fstype, p.label, fsopts = p.fsopts, boot = p.active, align = p.align, part_type = p.part_type) self.__instimage.layout_partitions(self._ptable_format) self.__imgdir = self.workdir for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.DiskImage(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instimage.add_disk(disk_name, disk_obj) self.__instimage.mount() def install(self, repo_urls=None): """ Install fs images into partitions """ for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Installing disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) self.__instimage.install(full_path) def configure(self, repodata = None): """ Configure the system image according to kickstart. For now, it just prepares the image to be bootable by e.g. creating and installing a bootloader configuration. """ source_plugin = self.get_default_source_plugin() if source_plugin: self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods) for disk_name, disk in self.__instimage.disks.items(): self._source_methods["do_install_disk"](disk, disk_name, self, self.workdir, self.oe_builddir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) def print_outimage_info(self): """ Print the image(s) and artifacts used, for the user. """ msg = "The new image(s) can be found here:\n" parts = self._get_parts() for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msg += ' %s\n\n' % full_path msg += 'The following build artifacts were used to create the image(s):\n' for p in parts: if p.get_rootfs() is None: continue if p.mountpoint == '/': str = ':' else: str = '["%s"]:' % p.label msg += ' ROOTFS_DIR%s%s\n' % (str.ljust(20), p.get_rootfs()) msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir msg += ' KERNEL_DIR: %s\n' % self.kernel_dir msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot msger.info(msg) def _get_boot_config(self): """ Return the rootdev/root_part_uuid (if specified by --part-type) Assume partition order same as in wks """ rootdev = None root_part_uuid = None parts = self._get_parts() for num, p in enumerate(parts, 1): if p.mountpoint == "/": part = '' if p.disk.startswith('mmcblk'): part = 'p' if self._ptable_format == 'msdos' and num > 3: rootdev = "/dev/%s%s%-d" % (p.disk, part, num + 1) else: rootdev = "/dev/%s%s%-d" % (p.disk, part, num) root_part_uuid = p.part_type return (rootdev, root_part_uuid) def _unmount_instroot(self): if not self.__instimage is None: try: self.__instimage.cleanup() except MountError, err: msger.warning("%s" % err)
def _mount_instroot(self, base_on = None): """ For 'wic', we already have our build artifacts and don't want to loop mount anything to install into, we just create filesystems from the artifacts directly and combine them into a partitioned image. We still want to reuse as much of the basic mic machinery though; despite the fact that we don't actually do loop or any other kind of mounting we still want to do many of the same things to prepare images, so we basically just adapt to the basic framework and reinterpret what 'mounting' means in our context. _instroot would normally be something like /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for installing packages, etc. We don't currently need to do that, so we simplify life by just using /var/tmp/wic/build as our workdir. """ parts = self._get_parts() self.__instimage = PartitionedMount(self._instroot) for p in parts: # as a convenience, set source to the boot partition source # instead of forcing it to be set via bootloader --source if not self.ks.handler.bootloader.source and p.mountpoint == "/boot": self.ks.handler.bootloader.source = p.source for p in parts: # need to create the filesystems in order to get their # sizes before we can add them and do the layout. # PartitionedMount.mount() actually calls __format_disks() # to create the disk images and carve out the partitions, # then self.install() calls PartitionedMount.install() # which calls __install_partitition() for each partition # to dd the fs into the partitions. It would be nice to # be able to use e.g. ExtDiskMount etc to create the # filesystems, since that's where existing e.g. mkfs code # is, but those are only created after __format_disks() # which needs the partition sizes so needs them created # before its called. Well, the existing setup is geared # to installing packages into mounted filesystems - maybe # when/if we need to actually do package selection we # should modify things to use those objects, but for now # we can avoid that. fstab = self.__write_fstab(self.rootfs_dir.get("ROOTFS_DIR")) p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) self._restore_fstab(fstab) self.__instimage.add_partition(int(p.size), p.disk, p.mountpoint, p.source_file, p.fstype, p.label, fsopts = p.fsopts, boot = p.active, align = p.align, part_type = p.part_type) self.__instimage.layout_partitions(self._ptable_format) self.__imgdir = self.workdir for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.DiskImage(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instimage.add_disk(disk_name, disk_obj) self.__instimage.mount()
class RawImageCreator(BaseImageCreator): """Installs a system into a file containing a partitioned disk image. ApplianceImageCreator is an advanced ImageCreator subclass; a sparse file is formatted with a partition table, each partition loopback mounted and the system installed into an virtual disk. The disk image can subsequently be booted in a virtual machine or accessed with kpartx """ def __init__(self, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"): """Initialize a ApplianceImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, creatoropts, pkgmgr) self.__instloop = None self.__imgdir = None self.__disks = {} self.__disk_format = "raw" self._disk_names = [] self._ptable_format = self.ks.handler.bootloader.ptable self.vmem = 512 self.vcpu = 1 self.checksum = False self.use_uuid = fstab_entry == "uuid" self.appliance_version = None self.appliance_release = None self.compress_image = compress_image self.bmap_needed = generate_bmap #self.getsource = False #self.listpkg = False self._dep_checks.extend(["sync", "kpartx", "parted", "extlinux"]) def configure(self, repodata = None): import subprocess def chroot(): os.chroot(self._instroot) os.chdir("/") if os.path.exists(self._instroot + "/usr/bin/Xorg"): subprocess.call(["/bin/chmod", "u+s", "/usr/bin/Xorg"], preexec_fn = chroot) BaseImageCreator.configure(self, repodata) def _get_fstab(self): s = "" for mp in self.__instloop.mountOrder: p = None for p1 in self.__instloop.partitions: if p1['mountpoint'] == mp: p = p1 break if self.use_uuid and p['uuid']: device = "UUID=%s" % p['uuid'] else: device = "/dev/%s%-d" % (p['disk_name'], p['num']) s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': device, 'mountpoint': p['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not p['fsopts'] else p['fsopts']} if p['mountpoint'] == "/": for subvol in self.__instloop.subvolumes: if subvol['mountpoint'] == "/": continue s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': "/dev/%s%-d" % (p['disk_name'], p['num']), 'mountpoint': subvol['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not subvol['fsopts'] else subvol['fsopts']} s += "devpts /dev/pts devpts gid=5,mode=620 0 0\n" s += "tmpfs /dev/shm tmpfs defaults 0 0\n" s += "proc /proc proc defaults 0 0\n" s += "sysfs /sys sysfs defaults 0 0\n" return s def _create_mkinitrd_config(self): """write to tell which modules to be included in initrd""" mkinitrd = "" mkinitrd += "PROBE=\"no\"\n" mkinitrd += "MODULES+=\"ext3 ata_piix sd_mod libata scsi_mod\"\n" mkinitrd += "rootfs=\"ext3\"\n" mkinitrd += "rootopts=\"defaults\"\n" msger.debug("Writing mkinitrd config %s/etc/sysconfig/mkinitrd" \ % self._instroot) os.makedirs(self._instroot + "/etc/sysconfig/",mode=644) cfg = open(self._instroot + "/etc/sysconfig/mkinitrd", "w") cfg.write(mkinitrd) cfg.close() def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_disk_names(self): """ Returns a list of physical target disk names (e.g., 'sdb') which will be created. """ if self._disk_names: return self._disk_names #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk_name = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if parts[i].mountpoint and not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified for partition with mountpoint " "'%s' in the ks file") self._disk_names.append(disk_name) return self._disk_names def _full_name(self, name, extention): """ Construct full file name for a file we generate. """ return "%s-%s.%s" % (self.name, name, extention) def _full_path(self, path, name, extention): """ Construct full file path to a file we generate. """ return os.path.join(path, self._full_name(name, extention)) # # Actual implemention # def _mount_instroot(self, base_on = None): parts = self._get_parts() self.__instloop = PartitionedMount(self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, p.label, fsopts = p.fsopts, boot = p.active, align = p.align, part_type = p.part_type) self.__instloop.layout_partitions(self._ptable_format) # Create the disks self.__imgdir = self._mkdtemp() for disk_name, disk in self.__instloop.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "raw") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.SparseLoopbackDisk(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instloop.add_disk(disk_name, disk_obj) self.__instloop.mount() self._create_mkinitrd_config() def _get_required_packages(self): required_packages = BaseImageCreator._get_required_packages(self) if not self.target_arch or not self.target_arch.startswith("arm"): required_packages += ["syslinux", "syslinux-extlinux"] return required_packages def _get_excluded_packages(self): return BaseImageCreator._get_excluded_packages(self) def _get_syslinux_boot_config(self): rootdev = None root_part_uuid = None for p in self.__instloop.partitions: if p['mountpoint'] == "/": rootdev = "/dev/%s%-d" % (p['disk_name'], p['num']) root_part_uuid = p['partuuid'] return (rootdev, root_part_uuid) def _create_syslinux_config(self): splash = os.path.join(self._instroot, "boot/extlinux") if os.path.exists(splash): splashline = "menu background splash.jpg" else: splashline = "" (rootdev, root_part_uuid) = self._get_syslinux_boot_config() options = self.ks.handler.bootloader.appendLine #XXX don't hardcode default kernel - see livecd code syslinux_conf = "" syslinux_conf += "prompt 0\n" syslinux_conf += "timeout 1\n" syslinux_conf += "\n" syslinux_conf += "default vesamenu.c32\n" syslinux_conf += "menu autoboot Starting %s...\n" % self.distro_name syslinux_conf += "menu hidden\n" syslinux_conf += "\n" syslinux_conf += "%s\n" % splashline syslinux_conf += "menu title Welcome to %s!\n" % self.distro_name syslinux_conf += "menu color border 0 #ffffffff #00000000\n" syslinux_conf += "menu color sel 7 #ffffffff #ff000000\n" syslinux_conf += "menu color title 0 #ffffffff #00000000\n" syslinux_conf += "menu color tabmsg 0 #ffffffff #00000000\n" syslinux_conf += "menu color unsel 0 #ffffffff #00000000\n" syslinux_conf += "menu color hotsel 0 #ff000000 #ffffffff\n" syslinux_conf += "menu color hotkey 7 #ffffffff #ff000000\n" syslinux_conf += "menu color timeout_msg 0 #ffffffff #00000000\n" syslinux_conf += "menu color timeout 0 #ffffffff #00000000\n" syslinux_conf += "menu color cmdline 0 #ffffffff #00000000\n" versions = [] kernels = self._get_kernel_versions() symkern = "%s/boot/vmlinuz" % self._instroot if os.path.lexists(symkern): v = os.path.realpath(symkern).replace('%s-' % symkern, "") syslinux_conf += "label %s\n" % self.distro_name.lower() syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) syslinux_conf += "\tlinux ../vmlinuz\n" if self._ptable_format == 'msdos': rootstr = rootdev else: if not root_part_uuid: raise MountError("Cannot find the root GPT partition UUID") rootstr = "PARTUUID=%s" % root_part_uuid syslinux_conf += "\tappend ro root=%s %s\n" % (rootstr, options) syslinux_conf += "\tmenu default\n" else: for kernel in kernels: for version in kernels[kernel]: versions.append(version) footlabel = 0 for v in versions: syslinux_conf += "label %s%d\n" \ % (self.distro_name.lower(), footlabel) syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) syslinux_conf += "\tlinux ../vmlinuz-%s\n" % v syslinux_conf += "\tappend ro root=%s %s\n" \ % (rootdev, options) if footlabel == 0: syslinux_conf += "\tmenu default\n" footlabel += 1; msger.debug("Writing syslinux config %s/boot/extlinux/extlinux.conf" \ % self._instroot) cfg = open(self._instroot + "/boot/extlinux/extlinux.conf", "w") cfg.write(syslinux_conf) cfg.close() def _install_syslinux(self): for name in self.__disks.keys(): loopdev = self.__disks[name].device # Set MBR mbrfile = "%s/usr/share/syslinux/" % self._instroot if self._ptable_format == 'gpt': mbrfile += "gptmbr.bin" else: mbrfile += "mbr.bin" msger.debug("Installing syslinux bootloader '%s' to %s" % \ (mbrfile, loopdev)) mbrsize = os.stat(mbrfile)[stat.ST_SIZE] rc = runner.show(['dd', 'if=%s' % mbrfile, 'of=' + loopdev]) if rc != 0: raise MountError("Unable to set MBR to %s" % loopdev) # Ensure all data is flushed to disk before doing syslinux install runner.quiet('sync') fullpathsyslinux = fs_related.find_binary_path("extlinux") rc = runner.show([fullpathsyslinux, "-i", "%s/boot/extlinux" % self._instroot]) if rc != 0: raise MountError("Unable to install syslinux bootloader to %s" \ % loopdev) def _create_bootconfig(self): #If syslinux is available do the required configurations. if os.path.exists("%s/usr/share/syslinux/" % (self._instroot)) \ and os.path.exists("%s/boot/extlinux/" % (self._instroot)): self._create_syslinux_config() self._install_syslinux() def _unmount_instroot(self): if not self.__instloop is None: try: self.__instloop.cleanup() except MountError, err: msger.warning("%s" % err)
class RawImageCreator(BaseImageCreator): """Installs a system into a file containing a partitioned disk image. ApplianceImageCreator is an advanced ImageCreator subclass; a sparse file is formatted with a partition table, each partition loopback mounted and the system installed into an virtual disk. The disk image can subsequently be booted in a virtual machine or accessed with kpartx """ img_format = 'raw' def __init__(self, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"): """Initialize a ApplianceImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, creatoropts, pkgmgr) self.__instloop = None self.__imgdir = None self.__disks = {} self.__disk_format = "raw" self._disk_names = [] self._ptable_format = self.ks.handler.bootloader.ptable self.vmem = 512 self.vcpu = 1 self.checksum = False self.use_uuid = fstab_entry == "uuid" self.appliance_version = None self.appliance_release = None self.compress_image = compress_image self.bmap_needed = generate_bmap self._need_extlinux = not kickstart.use_installerfw(self.ks, "bootloader") #self.getsource = False #self.listpkg = False self._dep_checks.extend(["sync", "kpartx", "parted"]) if self._need_extlinux: self._dep_checks.extend(["extlinux"]) def configure(self, repodata = None): import subprocess def chroot(): os.chroot(self._instroot) os.chdir("/") if os.path.exists(self._instroot + "/usr/bin/Xorg"): subprocess.call(["/bin/chmod", "u+s", "/usr/bin/Xorg"], preexec_fn = chroot) BaseImageCreator.configure(self, repodata) def _get_fstab(self): s = "" for mp in self.__instloop.mount_order: p = None for p1 in self.__instloop.partitions: if p1['mountpoint'] == mp: p = p1 break if self.use_uuid and p['uuid']: device = "UUID=%s" % p['uuid'] else: device = "/dev/%s%-d" % (p['disk_name'], p['num']) s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': device, 'mountpoint': p['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not p['fsopts'] else p['fsopts']} if p['mountpoint'] == "/": for subvol in self.__instloop.subvolumes: if subvol['mountpoint'] == "/": continue s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': "/dev/%s%-d" % (p['disk_name'], p['num']), 'mountpoint': subvol['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not subvol['fsopts'] else subvol['fsopts']} s += "devpts /dev/pts devpts gid=5,mode=620 0 0\n" s += "tmpfs /dev/shm tmpfs defaults 0 0\n" s += "proc /proc proc defaults 0 0\n" s += "sysfs /sys sysfs defaults 0 0\n" return s def _create_mkinitrd_config(self): """write to tell which modules to be included in initrd""" mkinitrd = "" mkinitrd += "PROBE=\"no\"\n" mkinitrd += "MODULES+=\"ext3 ata_piix sd_mod libata scsi_mod\"\n" mkinitrd += "rootfs=\"ext3\"\n" mkinitrd += "rootopts=\"defaults\"\n" msger.debug("Writing mkinitrd config %s/etc/sysconfig/mkinitrd" \ % self._instroot) os.makedirs(self._instroot + "/etc/sysconfig/",mode=644) cfg = open(self._instroot + "/etc/sysconfig/mkinitrd", "w") cfg.write(mkinitrd) cfg.close() def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_disk_names(self): """ Returns a list of physical target disk names (e.g., 'sdb') which will be created. """ if self._disk_names: return self._disk_names #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk_name = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if parts[i].mountpoint and not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified for partition with mountpoint " "'%s' in the ks file") self._disk_names.append(disk_name) return self._disk_names def _full_name(self, name, extention): """ Construct full file name for a file we generate. """ return "%s-%s.%s" % (self.name, name, extention) def _full_path(self, path, name, extention): """ Construct full file path to a file we generate. """ return os.path.join(path, self._full_name(name, extention)) # # Actual implemention # def _mount_instroot(self, base_on = None): parts = self._get_parts() self.__instloop = PartitionedMount(self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, p.label, fsopts = p.fsopts, boot = p.active, align = p.align, part_type = p.part_type) self.__instloop.layout_partitions(self._ptable_format) # Create the disks self.__imgdir = self._mkdtemp() for disk_name, disk in self.__instloop.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "raw") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.SparseLoopbackDisk(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instloop.add_disk(disk_name, disk_obj) self.__instloop.mount() self._create_mkinitrd_config() def mount(self, base_on = None, cachedir = None): """ This method calls the base class' 'mount()' method and then creates block device nodes corresponding to the image's partitions in the image itself. Namely, the image has /dev/loopX device corresponding to the entire image, and per-partition /dev/mapper/* devices. We copy these files to image's "/dev" directory in order to enable scripts which run in the image chroot environment to access own raw partitions. For example, this can be used to install the bootloader to the MBR (say, from an installer framework plugin). """ def copy_devnode(src, dest): """A helper function for copying device nodes.""" if not src: return stat_obj = os.stat(src) assert stat.S_ISBLK(stat_obj.st_mode) os.mknod(dest, stat_obj.st_mode, os.makedev(os.major(stat_obj.st_rdev), os.minor(stat_obj.st_rdev))) # os.mknod uses process umask may create a nod with different # permissions, so we use os.chmod to make sure permissions are # correct. os.chmod(dest, stat_obj.st_mode) BaseImageCreator.mount(self, base_on, cachedir) # Copy the disk loop devices for name in self.__disks.keys(): loopdev = self.__disks[name].device copy_devnode(loopdev, self._instroot + loopdev) # Copy per-partition dm nodes os.mkdir(self._instroot + "/dev/mapper", os.stat("/dev/mapper").st_mode) for p in self.__instloop.partitions: copy_devnode(p['mapper_device'], self._instroot + p['mapper_device']) copy_devnode(p['mpath_device'], self._instroot + p['mpath_device']) def unmount(self): """ Remove loop/dm device nodes which we created in 'mount()' and call the base class' 'unmount()' method. """ for p in self.__instloop.partitions: if p['mapper_device']: path = self._instroot + p['mapper_device'] if os.path.exists(path): os.unlink(path) if p['mpath_device']: path = self._instroot + p['mpath_device'] if os.path.exists(path): os.unlink(path) path = self._instroot + "/dev/mapper" if os.path.exists(path): shutil.rmtree(path, ignore_errors=True) for name in self.__disks.keys(): if self.__disks[name].device: path = self._instroot + self.__disks[name].device if os.path.exists(path): os.unlink(path) BaseImageCreator.unmount(self) def _get_required_packages(self): required_packages = BaseImageCreator._get_required_packages(self) if self._need_extlinux: if not self.target_arch or not self.target_arch.startswith("arm"): required_packages += ["syslinux", "syslinux-extlinux"] return required_packages def _get_excluded_packages(self): return BaseImageCreator._get_excluded_packages(self) def _get_syslinux_boot_config(self): rootdev = None root_part_uuid = None for p in self.__instloop.partitions: if p['mountpoint'] == "/": rootdev = "/dev/%s%-d" % (p['disk_name'], p['num']) root_part_uuid = p['partuuid'] return (rootdev, root_part_uuid) def _create_syslinux_config(self): splash = os.path.join(self._instroot, "boot/extlinux") if os.path.exists(splash): splashline = "menu background splash.jpg" else: splashline = "" (rootdev, root_part_uuid) = self._get_syslinux_boot_config() options = self.ks.handler.bootloader.appendLine #XXX don't hardcode default kernel - see livecd code syslinux_conf = "" syslinux_conf += "prompt 0\n" syslinux_conf += "timeout 1\n" syslinux_conf += "\n" syslinux_conf += "default vesamenu.c32\n" syslinux_conf += "menu autoboot Starting %s...\n" % self.distro_name syslinux_conf += "menu hidden\n" syslinux_conf += "\n" syslinux_conf += "%s\n" % splashline syslinux_conf += "menu title Welcome to %s!\n" % self.distro_name syslinux_conf += "menu color border 0 #ffffffff #00000000\n" syslinux_conf += "menu color sel 7 #ffffffff #ff000000\n" syslinux_conf += "menu color title 0 #ffffffff #00000000\n" syslinux_conf += "menu color tabmsg 0 #ffffffff #00000000\n" syslinux_conf += "menu color unsel 0 #ffffffff #00000000\n" syslinux_conf += "menu color hotsel 0 #ff000000 #ffffffff\n" syslinux_conf += "menu color hotkey 7 #ffffffff #ff000000\n" syslinux_conf += "menu color timeout_msg 0 #ffffffff #00000000\n" syslinux_conf += "menu color timeout 0 #ffffffff #00000000\n" syslinux_conf += "menu color cmdline 0 #ffffffff #00000000\n" versions = [] kernels = self._get_kernel_versions() symkern = "%s/boot/vmlinuz" % self._instroot if os.path.lexists(symkern): v = os.path.realpath(symkern).replace('%s-' % symkern, "") syslinux_conf += "label %s\n" % self.distro_name.lower() syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) syslinux_conf += "\tlinux ../vmlinuz\n" if self._ptable_format == 'msdos': rootstr = rootdev else: if not root_part_uuid: raise MountError("Cannot find the root GPT partition UUID") rootstr = "PARTUUID=%s" % root_part_uuid syslinux_conf += "\tappend ro root=%s %s\n" % (rootstr, options) syslinux_conf += "\tmenu default\n" else: for kernel in kernels: for version in kernels[kernel]: versions.append(version) footlabel = 0 for v in versions: syslinux_conf += "label %s%d\n" \ % (self.distro_name.lower(), footlabel) syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) syslinux_conf += "\tlinux ../vmlinuz-%s\n" % v syslinux_conf += "\tappend ro root=%s %s\n" \ % (rootdev, options) if footlabel == 0: syslinux_conf += "\tmenu default\n" footlabel += 1; msger.debug("Writing syslinux config %s/boot/extlinux/extlinux.conf" \ % self._instroot) cfg = open(self._instroot + "/boot/extlinux/extlinux.conf", "w") cfg.write(syslinux_conf) cfg.close() def _install_syslinux(self): for name in self.__disks.keys(): loopdev = self.__disks[name].device # Set MBR mbrfile = "%s/usr/share/syslinux/" % self._instroot if self._ptable_format == 'gpt': mbrfile += "gptmbr.bin" else: mbrfile += "mbr.bin" msger.debug("Installing syslinux bootloader '%s' to %s" % \ (mbrfile, loopdev)) rc = runner.show(['dd', 'if=%s' % mbrfile, 'of=' + loopdev]) if rc != 0: raise MountError("Unable to set MBR to %s" % loopdev) # Ensure all data is flushed to disk before doing syslinux install runner.quiet('sync') fullpathsyslinux = fs_related.find_binary_path("extlinux") rc = runner.show([fullpathsyslinux, "-i", "%s/boot/extlinux" % self._instroot]) if rc != 0: raise MountError("Unable to install syslinux bootloader to %s" \ % loopdev) def _create_bootconfig(self): #If syslinux is available do the required configurations. if self._need_extlinux \ and os.path.exists("%s/usr/share/syslinux/" % (self._instroot)) \ and os.path.exists("%s/boot/extlinux/" % (self._instroot)): self._create_syslinux_config() self._install_syslinux() def _unmount_instroot(self): if not self.__instloop is None: try: self.__instloop.cleanup() except MountError, err: msger.warning("%s" % err)
def do_chroot(cls, target): img = target imgsize = misc.get_file_size(img) * 1024L * 1024L partedcmd = fs_related.find_binary_path("parted") disk = fs_related.SparseLoopbackDisk(img, imgsize) imgmnt = misc.mkdtemp() imgloop = PartitionedMount({'/dev/sdb': disk}, imgmnt, skipformat=True) img_fstype = "ext3" # Check the partitions from raw disk. root_mounted = False partition_mounts = 0 for line in runner.outs([partedcmd, "-s", img, "unit", "B", "print"]).splitlines(): line = line.strip() # Lines that start with number are the partitions, # because parted can be translated we can't refer to any text lines. if not line or not line[0].isdigit(): continue # Some vars have extra , as list seperator. line = line.replace(",", "") # Example of parted output lines that are handled: # Number Start End Size Type File system Flags # 1 512B 3400000511B 3400000000B primary # 2 3400531968B 3656384511B 255852544B primary linux-swap(v1) # 3 3656384512B 3720347647B 63963136B primary fat16 boot, lba partition_info = re.split("\s+", line) size = partition_info[3].split("B")[0] if len(partition_info) < 6 or partition_info[5] in ["boot"]: # No filesystem can be found from partition line. Assuming # btrfs, because that is the only MeeGo fs that parted does # not recognize properly. # TODO: Can we make better assumption? fstype = "btrfs" elif partition_info[5] in ["ext2", "ext3", "ext4", "btrfs"]: fstype = partition_info[5] elif partition_info[5] in ["fat16", "fat32"]: fstype = "vfat" elif "swap" in partition_info[5]: fstype = "swap" else: raise errors.CreatorError( "Could not recognize partition fs type '%s'." % partition_info[5]) if not root_mounted and fstype in [ "ext2", "ext3", "ext4", "btrfs" ]: # TODO: Check that this is actually the valid root partition from /etc/fstab mountpoint = "/" root_mounted = True elif fstype == "swap": mountpoint = "swap" else: # TODO: Assing better mount points for the rest of the partitions. partition_mounts += 1 mountpoint = "/media/partition_%d" % partition_mounts if "boot" in partition_info: boot = True else: boot = False msger.verbose( "Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot)) # TODO: add_partition should take bytes as size parameter. imgloop.add_partition((int)(size) / 1024 / 1024, "/dev/sdb", mountpoint, fstype=fstype, boot=boot) try: imgloop.mount() except errors.MountError: imgloop.cleanup() raise try: chroot.chroot(imgmnt, None, "/bin/env HOME=/root /bin/bash") except: raise errors.CreatorError("Failed to chroot to %s." % img) finally: chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
class DirectImageCreator(BaseImageCreator): """ Installs a system into a file containing a partitioned disk image. DirectImageCreator is an advanced ImageCreator subclass; an image file is formatted with a partition table, each partition created from a rootfs or other OpenEmbedded build artifact and dd'ed into the virtual disk. The disk image can subsequently be dd'ed onto media and used on actual hardware. """ def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir, kernel_dir, native_sysroot, hdddir, staging_data_dir, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"): """ Initialize a DirectImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, creatoropts, pkgmgr) self.__instimage = None self.__imgdir = None self.__disks = {} self.__disk_format = "direct" self._disk_names = [] self._ptable_format = self.ks.handler.bootloader.ptable self.use_uuid = fstab_entry == "uuid" self.compress_image = compress_image self.bmap_needed = generate_bmap self.oe_builddir = oe_builddir if image_output_dir: self.tmpdir = image_output_dir self.cachedir = "%s/cache" % image_output_dir self.rootfs_dir = rootfs_dir self.bootimg_dir = bootimg_dir self.kernel_dir = kernel_dir self.native_sysroot = native_sysroot self.hdddir = hdddir self.staging_data_dir = staging_data_dir def __write_fstab(self, image_rootfs): """overriden to generate fstab (temporarily) in rootfs. This is called from mount_instroot, make sure it doesn't get called from BaseImage.mount()""" if image_rootfs is None: return None fstab = image_rootfs + "/etc/fstab" if not os.path.isfile(fstab): return None parts = self._get_parts() self._save_fstab(fstab) fstab_lines = self._get_fstab(fstab, parts) self._update_fstab(fstab_lines, parts) self._write_fstab(fstab, fstab_lines) return fstab def _update_fstab(self, fstab_lines, parts): """Assume partition order same as in wks""" for num, p in enumerate(parts, 1): if not p.mountpoint or p.mountpoint == "/" or p.mountpoint == "/boot": continue if self._ptable_format == 'msdos' and num > 3: device_name = "/dev/" + p.disk + str(num + 1) else: device_name = "/dev/" + p.disk + str(num) opts = "defaults" if p.fsopts: opts = p.fsopts fstab_entry = device_name + "\t" + \ p.mountpoint + "\t" + \ p.fstype + "\t" + \ opts + "\t0\t0\n" fstab_lines.append(fstab_entry) def _write_fstab(self, fstab, fstab_lines): fstab = open(fstab, "w") for line in fstab_lines: fstab.write(line) fstab.close() def _save_fstab(self, fstab): """Save the current fstab in rootfs""" shutil.copyfile(fstab, fstab + ".orig") def _restore_fstab(self, fstab): """Restore the saved fstab in rootfs""" if fstab is None: return shutil.move(fstab + ".orig", fstab) def _get_fstab(self, fstab, parts): """Return the desired contents of /etc/fstab.""" f = open(fstab, "r") fstab_contents = f.readlines() f.close() return fstab_contents def set_bootimg_dir(self, bootimg_dir): """ Accessor for bootimg_dir, the actual location used for the source of the bootimg. Should be set by source plugins (only if they change the default bootimg source) so the correct info gets displayed for print_outimage_info(). """ self.bootimg_dir = bootimg_dir def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_disk_names(self): """ Returns a list of physical target disk names (e.g., 'sdb') which will be created. """ if self._disk_names: return self._disk_names #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk_name = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if parts[i].mountpoint and not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified for partition with mountpoint " "'%s' in the ks file") self._disk_names.append(disk_name) return self._disk_names def _full_name(self, name, extention): """ Construct full file name for a file we generate. """ return "%s-%s.%s" % (self.name, name, extention) def _full_path(self, path, name, extention): """ Construct full file path to a file we generate. """ return os.path.join(path, self._full_name(name, extention)) def get_default_source_plugin(self): """ The default source plugin i.e. the plugin that's consulted for overall image generation tasks outside of any particular partition. For convenience, we just hang it off the bootloader handler since it's the one non-partition object in any setup. By default the default plugin is set to the same plugin as the /boot partition; since we hang it off the bootloader object, the default can be explicitly set using the --source bootloader param. """ return self.ks.handler.bootloader.source # # Actual implemention # def _mount_instroot(self, base_on=None): """ For 'wic', we already have our build artifacts and don't want to loop mount anything to install into, we just create filesystems from the artifacts directly and combine them into a partitioned image. We still want to reuse as much of the basic mic machinery though; despite the fact that we don't actually do loop or any other kind of mounting we still want to do many of the same things to prepare images, so we basically just adapt to the basic framework and reinterpret what 'mounting' means in our context. _instroot would normally be something like /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for installing packages, etc. We don't currently need to do that, so we simplify life by just using /var/tmp/wic/build as our workdir. """ parts = self._get_parts() self.__instimage = PartitionedMount(self._instroot) for p in parts: # as a convenience, set source to the boot partition source # instead of forcing it to be set via bootloader --source if not self.ks.handler.bootloader.source and p.mountpoint == "/boot": self.ks.handler.bootloader.source = p.source for p in parts: # need to create the filesystems in order to get their # sizes before we can add them and do the layout. # PartitionedMount.mount() actually calls __format_disks() # to create the disk images and carve out the partitions, # then self.install() calls PartitionedMount.install() # which calls __install_partitition() for each partition # to dd the fs into the partitions. It would be nice to # be able to use e.g. ExtDiskMount etc to create the # filesystems, since that's where existing e.g. mkfs code # is, but those are only created after __format_disks() # which needs the partition sizes so needs them created # before its called. Well, the existing setup is geared # to installing packages into mounted filesystems - maybe # when/if we need to actually do package selection we # should modify things to use those objects, but for now # we can avoid that. fstab = self.__write_fstab(self.rootfs_dir.get("ROOTFS_DIR")) p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) self._restore_fstab(fstab) self.__instimage.add_partition(int(p.size), p.disk, p.mountpoint, p.source_file, p.fstype, p.label, fsopts=p.fsopts, boot=p.active, align=p.align, part_type=p.part_type) self.__instimage.layout_partitions(self._ptable_format) self.__imgdir = self.workdir for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.DiskImage(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instimage.add_disk(disk_name, disk_obj) self.__instimage.mount() def install(self, repo_urls=None): """ Install fs images into partitions """ for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Installing disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) self.__instimage.install(full_path) def configure(self, repodata=None): """ Configure the system image according to kickstart. For now, it just prepares the image to be bootable by e.g. creating and installing a bootloader configuration. """ source_plugin = self.get_default_source_plugin() if source_plugin: self._source_methods = pluginmgr.get_source_plugin_methods( source_plugin, disk_methods) for disk_name, disk in self.__instimage.disks.items(): self._source_methods["do_install_disk"]( disk, disk_name, self, self.workdir, self.oe_builddir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) def print_outimage_info(self): """ Print the image(s) and artifacts used, for the user. """ msg = "The new image(s) can be found here:\n" parts = self._get_parts() for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msg += ' %s\n\n' % full_path msg += 'The following build artifacts were used to create the image(s):\n' for p in parts: if p.get_rootfs() is None: continue if p.mountpoint == '/': str = ':' else: str = '["%s"]:' % p.label msg += ' ROOTFS_DIR%s%s\n' % (str.ljust(20), p.get_rootfs()) msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir msg += ' KERNEL_DIR: %s\n' % self.kernel_dir msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot msger.info(msg) def _get_boot_config(self): """ Return the rootdev/root_part_uuid (if specified by --part-type) Assume partition order same as in wks """ rootdev = None root_part_uuid = None parts = self._get_parts() for num, p in enumerate(parts, 1): if p.mountpoint == "/": part = '' if p.disk.startswith('mmcblk'): part = 'p' if self._ptable_format == 'msdos' and num > 3: rootdev = "/dev/%s%s%-d" % (p.disk, part, num + 1) else: rootdev = "/dev/%s%s%-d" % (p.disk, part, num) root_part_uuid = p.part_type return (rootdev, root_part_uuid) def _unmount_instroot(self): if not self.__instimage is None: try: self.__instimage.cleanup() except MountError, err: msger.warning("%s" % err)
def _mount_instroot(self, base_on=None): """ For 'wic', we already have our build artifacts and don't want to loop mount anything to install into, we just create filesystems from the artifacts directly and combine them into a partitioned image. We still want to reuse as much of the basic mic machinery though; despite the fact that we don't actually do loop or any other kind of mounting we still want to do many of the same things to prepare images, so we basically just adapt to the basic framework and reinterpret what 'mounting' means in our context. _instroot would normally be something like /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for installing packages, etc. We don't currently need to do that, so we simplify life by just using /var/tmp/wic/build as our workdir. """ parts = self._get_parts() self.__instimage = PartitionedMount(self._instroot) for p in parts: # as a convenience, set source to the boot partition source # instead of forcing it to be set via bootloader --source if not self.ks.handler.bootloader.source and p.mountpoint == "/boot": self.ks.handler.bootloader.source = p.source for p in parts: # need to create the filesystems in order to get their # sizes before we can add them and do the layout. # PartitionedMount.mount() actually calls __format_disks() # to create the disk images and carve out the partitions, # then self.install() calls PartitionedMount.install() # which calls __install_partitition() for each partition # to dd the fs into the partitions. It would be nice to # be able to use e.g. ExtDiskMount etc to create the # filesystems, since that's where existing e.g. mkfs code # is, but those are only created after __format_disks() # which needs the partition sizes so needs them created # before its called. Well, the existing setup is geared # to installing packages into mounted filesystems - maybe # when/if we need to actually do package selection we # should modify things to use those objects, but for now # we can avoid that. fstab = self.__write_fstab(self.rootfs_dir.get("ROOTFS_DIR")) p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) self._restore_fstab(fstab) self.__instimage.add_partition(int(p.size), p.disk, p.mountpoint, p.source_file, p.fstype, p.label, fsopts=p.fsopts, boot=p.active, align=p.align, part_type=p.part_type) self.__instimage.layout_partitions(self._ptable_format) self.__imgdir = self.workdir for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.DiskImage(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instimage.add_disk(disk_name, disk_obj) self.__instimage.mount()
class RawImageCreator(BaseImageCreator): """Installs a system into a file containing a partitioned disk image. ApplianceImageCreator is an advanced ImageCreator subclass; a sparse file is formatted with a partition table, each partition loopback mounted and the system installed into an virtual disk. The disk image can subsequently be booted in a virtual machine or accessed with kpartx """ def __init__(self, creatoropts=None, pkgmgr=None, compress_image=None): """Initialize a ApplianceImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, creatoropts, pkgmgr) self.__instloop = None self.__imgdir = None self.__disks = {} self.__disk_format = "raw" self._diskinfo = [] self.vmem = 512 self.vcpu = 1 self.checksum = False self.appliance_version = None self.appliance_release = None self.compress_image = compress_image #self.getsource = False #self.listpkg = False self._dep_checks.extend(["sync", "kpartx", "parted", "extlinux"]) def configure(self, repodata = None): import subprocess def chroot(): os.chroot(self._instroot) os.chdir("/") if os.path.exists(self._instroot + "/usr/bin/Xorg"): subprocess.call(["/bin/chmod", "u+s", "/usr/bin/Xorg"], preexec_fn = chroot) BaseImageCreator.configure(self, repodata) def _get_fstab(self): s = "" for mp in self.__instloop.mountOrder: p = None for p1 in self.__instloop.partitions: if p1['mountpoint'] == mp: p = p1 break s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': "/dev/%s%-d" % (p['disk'], p['num']), 'mountpoint': p['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not p['fsopts'] else p['fsopts']} if p['mountpoint'] == "/": for subvol in self.__instloop.subvolumes: if subvol['mountpoint'] == "/": continue s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { 'device': "/dev/%s%-d" % (p['disk'], p['num']), 'mountpoint': subvol['mountpoint'], 'fstype': p['fstype'], 'fsopts': "defaults,noatime" if not subvol['fsopts'] else subvol['fsopts']} s += "devpts /dev/pts devpts gid=5,mode=620 0 0\n" s += "tmpfs /dev/shm tmpfs defaults 0 0\n" s += "proc /proc proc defaults 0 0\n" s += "sysfs /sys sysfs defaults 0 0\n" return s def _create_mkinitrd_config(self): #write to tell which modules to be included in initrd mkinitrd = "" mkinitrd += "PROBE=\"no\"\n" mkinitrd += "MODULES+=\"ext3 ata_piix sd_mod libata scsi_mod\"\n" mkinitrd += "rootfs=\"ext3\"\n" mkinitrd += "rootopts=\"defaults\"\n" msger.debug("Writing mkinitrd config %s/etc/sysconfig/mkinitrd" \ % self._instroot) os.makedirs(self._instroot + "/etc/sysconfig/",mode=644) cfg = open(self._instroot + "/etc/sysconfig/mkinitrd", "w") cfg.write(mkinitrd) cfg.close() def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_diskinfo(self): if self._diskinfo: return self._diskinfo #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified in partition line of ks file") size = parts[i].size * 1024L * 1024L # If we have alignment set for partition we need to enlarge the # drive, so that the alignment changes fits there as well if parts[i].align: size += parts[i].align * 1024L found = False for j in range(len(self._diskinfo)): if self._diskinfo[j]['name'] == disk: self._diskinfo[j]['size'] = self._diskinfo[j]['size'] + size found = True break else: found = False if not found: self._diskinfo.append({ 'name': disk, 'size': size }) return self._diskinfo # # Actual implemention # def _mount_instroot(self, base_on = None): self.__imgdir = self._mkdtemp() parts = self._get_parts() #create disk for item in self.get_diskinfo(): msger.debug("Adding disk %s as %s/%s-%s.raw with size %s bytes" % (item['name'], self.__imgdir, self.name, item['name'], item['size'])) disk = fs_related.SparseLoopbackDisk("%s/%s-%s.raw" % ( self.__imgdir, self.name, item['name']), item['size']) self.__disks[item['name']] = disk self.__instloop = PartitionedMount(self.__disks, self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, fsopts = p.fsopts, boot = p.active, align = p.align) self.__instloop.mount() self._create_mkinitrd_config() def _get_required_packages(self): required_packages = BaseImageCreator._get_required_packages(self) if not self.target_arch or not self.target_arch.startswith("arm"): required_packages += ["syslinux", "syslinux-extlinux"] return required_packages def _get_excluded_packages(self): return BaseImageCreator._get_excluded_packages(self) def _get_syslinux_boot_config(self): bootdevnum = None rootdevnum = None rootdev = None for p in self.__instloop.partitions: if p['mountpoint'] == "/boot": bootdevnum = p['num'] - 1 elif p['mountpoint'] == "/" and bootdevnum is None: bootdevnum = p['num'] - 1 if p['mountpoint'] == "/": rootdevnum = p['num'] - 1 rootdev = "/dev/%s%-d" % (p['disk'], p['num']) prefix = "" if bootdevnum == rootdevnum: prefix = "/boot" return (bootdevnum, rootdevnum, rootdev, prefix) def _create_syslinux_config(self): #Copy splash splash = "%s/usr/lib/anaconda-runtime/syslinux-vesa-splash.jpg" \ % self._instroot if os.path.exists(splash): shutil.copy(splash, "%s%s/splash.jpg" \ % (self._instroot, "/boot/extlinux")) splashline = "menu background splash.jpg" else: splashline = "" (bootdevnum, rootdevnum, rootdev, prefix) = \ self._get_syslinux_boot_config() options = self.ks.handler.bootloader.appendLine #XXX don't hardcode default kernel - see livecd code syslinux_conf = "" syslinux_conf += "prompt 0\n" syslinux_conf += "timeout 1\n" syslinux_conf += "\n" syslinux_conf += "default vesamenu.c32\n" syslinux_conf += "menu autoboot Starting %s...\n" % self.distro_name syslinux_conf += "menu hidden\n" syslinux_conf += "\n" syslinux_conf += "%s\n" % splashline syslinux_conf += "menu title Welcome to %s!\n" % self.distro_name syslinux_conf += "menu color border 0 #ffffffff #00000000\n" syslinux_conf += "menu color sel 7 #ffffffff #ff000000\n" syslinux_conf += "menu color title 0 #ffffffff #00000000\n" syslinux_conf += "menu color tabmsg 0 #ffffffff #00000000\n" syslinux_conf += "menu color unsel 0 #ffffffff #00000000\n" syslinux_conf += "menu color hotsel 0 #ff000000 #ffffffff\n" syslinux_conf += "menu color hotkey 7 #ffffffff #ff000000\n" syslinux_conf += "menu color timeout_msg 0 #ffffffff #00000000\n" syslinux_conf += "menu color timeout 0 #ffffffff #00000000\n" syslinux_conf += "menu color cmdline 0 #ffffffff #00000000\n" versions = [] kernels = self._get_kernel_versions() for kernel in kernels: for version in kernels[kernel]: versions.append(version) footlabel = 0 for v in versions: shutil.copy("%s/boot/vmlinuz-%s" %(self._instroot, v), "%s%s/vmlinuz-%s" % (self._instroot, "/boot/extlinux/", v)) syslinux_conf += "label %s%d\n" \ % (self.distro_name.lower(), footlabel) syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) syslinux_conf += "\tkernel vmlinuz-%s\n" % v syslinux_conf += "\tappend ro root=%s %s\n" \ % (rootdev, options) if footlabel == 0: syslinux_conf += "\tmenu default\n" footlabel += 1; msger.debug("Writing syslinux config %s/boot/extlinux/extlinux.conf" \ % self._instroot) cfg = open(self._instroot + "/boot/extlinux/extlinux.conf", "w") cfg.write(syslinux_conf) cfg.close() def _install_syslinux(self): i = 0 for name in self.__disks.keys(): loopdev = self.__disks[name].device i =i+1 msger.debug("Installing syslinux bootloader to %s" % loopdev) (bootdevnum, rootdevnum, rootdev, prefix) = \ self._get_syslinux_boot_config() #Set MBR mbrsize = os.stat("%s/usr/share/syslinux/mbr.bin" \ % self._instroot)[stat.ST_SIZE] rc = runner.show(['dd', 'if=%s/usr/share/syslinux/mbr.bin' % self._instroot, 'of=' + loopdev]) if rc != 0: raise MountError("Unable to set MBR to %s" % loopdev) #Set Bootable flag parted = fs_related.find_binary_path("parted") rc = runner.quiet([parted, "-s", loopdev, "set", "%d" % (bootdevnum + 1), "boot", "on"]) #XXX disabled return code check because parted always fails to #reload part table with loop devices. Annoying because we can't #distinguish this failure from real partition failures :-( if rc != 0 and 1 == 0: raise MountError("Unable to set bootable flag to %sp%d" \ % (loopdev, (bootdevnum + 1))) #Ensure all data is flushed to disk before doing syslinux install runner.quiet('sync') fullpathsyslinux = fs_related.find_binary_path("extlinux") rc = runner.show([fullpathsyslinux, "-i", "%s/boot/extlinux" % self._instroot]) if rc != 0: raise MountError("Unable to install syslinux bootloader to %sp%d" \ % (loopdev, (bootdevnum + 1))) def _create_bootconfig(self): #If syslinux is available do the required configurations. if os.path.exists("%s/usr/share/syslinux/" % (self._instroot)) \ and os.path.exists("%s/boot/extlinux/" % (self._instroot)): self._create_syslinux_config() self._install_syslinux() def _unmount_instroot(self): if not self.__instloop is None: self.__instloop.cleanup() def _resparse(self, size = None): return self.__instloop.resparse(size) def _stage_final_image(self): """Stage the final system image in _outdir. write meta data """ self._resparse() if self.compress_image: for imgfile in os.listdir(self.__imgdir): if imgfile.endswith('.raw') or imgfile.endswith('bin'): imgpath = os.path.join(self.__imgdir, imgfile) misc.compressing(imgpath, self.compress_image) if self.pack_to: dst = os.path.join(self._outdir, self.pack_to) msger.info("Pack all raw images to %s" % dst) misc.packing(dst, self.__imgdir) else: msger.debug("moving disks to stage location") for imgfile in os.listdir(self.__imgdir): src = os.path.join(self.__imgdir, imgfile) dst = os.path.join(self._outdir, imgfile) msger.debug("moving %s to %s" % (src,dst)) shutil.move(src,dst) self._write_image_xml() def _write_image_xml(self): imgarch = "i686" if self.target_arch and self.target_arch.startswith("arm"): imgarch = "arm" xml = "<image>\n" name_attributes = "" if self.appliance_version: name_attributes += " version='%s'" % self.appliance_version if self.appliance_release: name_attributes += " release='%s'" % self.appliance_release xml += " <name%s>%s</name>\n" % (name_attributes, self.name) xml += " <domain>\n" # XXX don't hardcode - determine based on the kernel we installed for # grub baremetal vs xen xml += " <boot type='hvm'>\n" xml += " <guest>\n" xml += " <arch>%s</arch>\n" % imgarch xml += " </guest>\n" xml += " <os>\n" xml += " <loader dev='hd'/>\n" xml += " </os>\n" i = 0 for name in self.__disks.keys(): xml += " <drive disk='%s-%s.%s' target='hd%s'/>\n" \ % (self.name,name, self.__disk_format,chr(ord('a')+i)) i = i + 1 xml += " </boot>\n" xml += " <devices>\n" xml += " <vcpu>%s</vcpu>\n" % self.vcpu xml += " <memory>%d</memory>\n" %(self.vmem * 1024) for network in self.ks.handler.network.network: xml += " <interface/>\n" xml += " <graphics/>\n" xml += " </devices>\n" xml += " </domain>\n" xml += " <storage>\n" if self.checksum is True: for name in self.__disks.keys(): diskpath = "%s/%s-%s.%s" \ % (self._outdir,self.name,name, self.__disk_format) disk_size = os.path.getsize(diskpath) meter_ct = 0 meter = progress.TextMeter() meter.start(size=disk_size, text="Generating disk signature for %s-%s.%s" \ % (self.name, name, self.__disk_format)) xml += " <disk file='%s-%s.%s' use='system' format='%s'>\n" \ % (self.name, name, self.__disk_format, self.__disk_format) try: import hashlib m1 = hashlib.sha1() m2 = hashlib.sha256() except: import sha m1 = sha.new() m2 = None f = open(diskpath,"r") while 1: chunk = f.read(65536) if not chunk: break m1.update(chunk) if m2: m2.update(chunk) meter.update(meter_ct) meter_ct = meter_ct + 65536 sha1checksum = m1.hexdigest() xml += " <checksum type='sha1'>%s</checksum>\n" \ % sha1checksum if m2: sha256checksum = m2.hexdigest() xml += " <checksum type='sha256'>%s</checksum>\n" \ % sha256checksum xml += " </disk>\n" else: for name in self.__disks.keys(): xml += " <disk file='%s-%s.%s' use='system' format='%s'/>\n"\ %(self.name, name, self.__disk_format, self.__disk_format) xml += " </storage>\n" xml += "</image>\n" msger.debug("writing image XML to %s/%s.xml" %(self._outdir, self.name)) cfg = open("%s/%s.xml" % (self._outdir, self.name), "w") cfg.write(xml) cfg.close()
def _create_usbimg(self, isodir): overlaysizemb = 64 #default #skipcompress = self.skip_compression? fstype = "vfat" homesizemb=0 swapsizemb=0 homefile="home.img" plussize=128 kernelargs=None if fstype == 'vfat': if overlaysizemb > 2047: raise CreatorError("Can't have an overlay of 2048MB or " "greater on VFAT") if homesizemb > 2047: raise CreatorError("Can't have an home overlay of 2048MB or " "greater on VFAT") if swapsizemb > 2047: raise CreatorError("Can't have an swap overlay of 2048MB or " "greater on VFAT") livesize = misc.get_file_size(isodir + "/LiveOS") usbimgsize = (overlaysizemb + \ homesizemb + \ swapsizemb + \ livesize + \ plussize) * 1024 * 1024 disk = fs_related.SparseLoopbackDisk("%s/%s.usbimg" \ % (self._outdir, self.name), usbimgsize) usbmnt = self._mkdtemp("usb-mnt") usbloop = PartitionedMount({'/dev/sdb':disk}, usbmnt) usbloop.add_partition(usbimgsize/1024/1024, "/dev/sdb", "/", fstype, boot=True) usbloop.mount() try: fs_related.makedirs(usbmnt + "/LiveOS") if os.path.exists(isodir + "/LiveOS/squashfs.img"): shutil.copyfile(isodir + "/LiveOS/squashfs.img", usbmnt + "/LiveOS/squashfs.img") else: fs_related.mksquashfs(os.path.dirname(self._image), usbmnt + "/LiveOS/squashfs.img") if os.path.exists(isodir + "/LiveOS/osmin.img"): shutil.copyfile(isodir + "/LiveOS/osmin.img", usbmnt + "/LiveOS/osmin.img") uuid = usbloop.partitions[0]['mount'].uuid label = usbloop.partitions[0]['mount'].fslabel usblabel = "UUID=%s" % (uuid) overlaysuffix = "-%s-%s" % (label, uuid) args = ['cp', "-Rf", isodir + "/isolinux", usbmnt + "/syslinux"] rc = runner.show(args) if rc: raise CreatorError("Can't copy isolinux directory %s" \ % (isodir + "/isolinux/*")) if os.path.isfile("/usr/share/syslinux/isolinux.bin"): syslinux_path = "/usr/share/syslinux" elif os.path.isfile("/usr/lib/syslinux/isolinux.bin"): syslinux_path = "/usr/lib/syslinux" else: raise CreatorError("syslinux not installed : " "cannot find syslinux installation path") for f in ("isolinux.bin", "vesamenu.c32"): path = os.path.join(syslinux_path, f) if os.path.isfile(path): args = ['cp', path, usbmnt + "/syslinux/"] rc = runner.show(args) if rc: raise CreatorError("Can't copy syslinux file " + path) else: raise CreatorError("syslinux not installed: " "syslinux file %s not found" % path) fd = open(isodir + "/isolinux/isolinux.cfg", "r") text = fd.read() fd.close() pattern = re.compile('CDLABEL=[^ ]*') text = pattern.sub(usblabel, text) pattern = re.compile('rootfstype=[^ ]*') text = pattern.sub("rootfstype=" + fstype, text) if kernelargs: text = text.replace("liveimg", "liveimg " + kernelargs) if overlaysizemb > 0: msger.info("Initializing persistent overlay file") overfile = "overlay" + overlaysuffix if fstype == "vfat": args = ['dd', "if=/dev/zero", "of=" + usbmnt + "/LiveOS/" + overfile, "count=%d" % overlaysizemb, "bs=1M"] else: args = ['dd', "if=/dev/null", "of=" + usbmnt + "/LiveOS/" + overfile, "count=1", "bs=1M", "seek=%d" % overlaysizemb] rc = runner.show(args) if rc: raise CreatorError("Can't create overlay file") text = text.replace("liveimg", "liveimg overlay=" + usblabel) text = text.replace(" ro ", " rw ") if swapsizemb > 0: msger.info("Initializing swap file") swapfile = usbmnt + "/LiveOS/" + "swap.img" args = ['dd', "if=/dev/zero", "of=" + swapfile, "count=%d" % swapsizemb, "bs=1M"] rc = runner.show(args) if rc: raise CreatorError("Can't create swap file") args = ["mkswap", "-f", swapfile] rc = runner.show(args) if rc: raise CreatorError("Can't mkswap on swap file") if homesizemb > 0: msger.info("Initializing persistent /home") homefile = usbmnt + "/LiveOS/" + homefile if fstype == "vfat": args = ['dd', "if=/dev/zero", "of=" + homefile, "count=%d" % homesizemb, "bs=1M"] else: args = ['dd', "if=/dev/null", "of=" + homefile, "count=1", "bs=1M", "seek=%d" % homesizemb] rc = runner.show(args) if rc: raise CreatorError("Can't create home file") mkfscmd = fs_related.find_binary_path("/sbin/mkfs." + fstype) if fstype == "ext2" or fstype == "ext3": args = [mkfscmd, "-F", "-j", homefile] else: args = [mkfscmd, homefile] rc = runner.show(args) if rc: raise CreatorError("Can't mke2fs home file") if fstype == "ext2" or fstype == "ext3": tune2fs = fs_related.find_binary_path("tune2fs") args = [tune2fs, "-c0", "-i0", "-ouser_xattr,acl", homefile] rc = runner.show(args) if rc: raise CreatorError("Can't tune2fs home file") if fstype == "vfat" or fstype == "msdos": syslinuxcmd = fs_related.find_binary_path("syslinux") syslinuxcfg = usbmnt + "/syslinux/syslinux.cfg" args = [syslinuxcmd, "-d", "syslinux", usbloop.partitions[0]["device"]] elif fstype == "ext2" or fstype == "ext3": extlinuxcmd = fs_related.find_binary_path("extlinux") syslinuxcfg = usbmnt + "/syslinux/extlinux.conf" args = [extlinuxcmd, "-i", usbmnt + "/syslinux"] else: raise CreatorError("Invalid file system type: %s" % (fstype)) os.unlink(usbmnt + "/syslinux/isolinux.cfg") fd = open(syslinuxcfg, "w") fd.write(text) fd.close() rc = runner.show(args) if rc: raise CreatorError("Can't install boot loader.") finally: usbloop.unmount() usbloop.cleanup() # Need to do this after image is unmounted and device mapper is closed msger.info("set MBR") mbrfile = "/usr/lib/syslinux/mbr.bin" if not os.path.exists(mbrfile): mbrfile = "/usr/share/syslinux/mbr.bin" if not os.path.exists(mbrfile): raise CreatorError("mbr.bin file didn't exist.") mbrsize = os.path.getsize(mbrfile) outimg = "%s/%s.usbimg" % (self._outdir, self.name) args = ['dd', "if=" + mbrfile, "of=" + outimg, "seek=0", "conv=notrunc", "bs=1", "count=%d" % (mbrsize)] rc = runner.show(args) if rc: raise CreatorError("Can't set MBR.")
def _mount_instroot(self, base_on=None): self.__imgdir = self._mkdtemp() #Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) #list of partitions from kickstart file parts = kickstart.get_partitions(self.ks) #list of disks where a disk is an dict with name: and size disks = [] for i in range(len(parts)): if parts[i].disk: disk = parts[i].disk else: raise CreatorError( "Failed to create disks, no --ondisk specified in partition line of ks file" ) if not parts[i].fstype: raise CreatorError( "Failed to create disks, no --fstype specified in partition line of ks file" ) size = parts[i].size * 1024L * 1024L found = False for j in range(len(disks)): if disks[j]['name'] == disk: disks[j]['size'] = disks[j]['size'] + size found = True break else: found = False if not found: disks.append({'name': disk, 'size': size}) #create disk for item in disks: msger.debug("Adding disk %s as %s/%s-%s.raw" % (item['name'], self.__imgdir, self.name, item['name'])) disk = fs_related.SparseLoopbackDisk( "%s/%s-%s.raw" % (self.__imgdir, self.name, item['name']), item['size']) self.__disks[item['name']] = disk self.__instloop = PartitionedMount(self.__disks, self._instroot) for p in parts: self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype, fsopts=p.fsopts, boot=p.active) self.__instloop.mount() self._create_mkinitrd_config()
def _create_usbimg(self, isodir): overlaysizemb = 64 #default #skipcompress = self.skip_compression? fstype = "vfat" homesizemb=0 swapsizemb=0 homefile="home.img" plussize=128 kernelargs=None if fstype == 'vfat': if overlaysizemb > 2047: raise CreatorError("Can't have an overlay of 2048MB or " "greater on VFAT") if homesizemb > 2047: raise CreatorError("Can't have an home overlay of 2048MB or " "greater on VFAT") if swapsizemb > 2047: raise CreatorError("Can't have an swap overlay of 2048MB or " "greater on VFAT") livesize = misc.get_file_size(isodir + "/LiveOS") usbimgsize = (overlaysizemb + \ homesizemb + \ swapsizemb + \ livesize + \ plussize) * 1024L * 1024L disk = fs_related.SparseLoopbackDisk("%s/%s.usbimg" \ % (self._outdir, self.name), usbimgsize) usbmnt = self._mkdtemp("usb-mnt") usbloop = PartitionedMount(usbmnt) usbloop.add_disk('/dev/sdb', disk) usbloop.add_partition(usbimgsize/1024/1024, "/dev/sdb", "/", fstype, boot=True) usbloop.mount() try: fs_related.makedirs(usbmnt + "/LiveOS") if os.path.exists(isodir + "/LiveOS/squashfs.img"): shutil.copyfile(isodir + "/LiveOS/squashfs.img", usbmnt + "/LiveOS/squashfs.img") else: fs_related.mksquashfs(os.path.dirname(self._image), usbmnt + "/LiveOS/squashfs.img") if os.path.exists(isodir + "/LiveOS/osmin.img"): shutil.copyfile(isodir + "/LiveOS/osmin.img", usbmnt + "/LiveOS/osmin.img") if fstype == "vfat" or fstype == "msdos": uuid = usbloop.partitions[0]['mount'].uuid label = usbloop.partitions[0]['mount'].fslabel usblabel = "UUID=%s-%s" % (uuid[0:4], uuid[4:8]) overlaysuffix = "-%s-%s-%s" % (label, uuid[0:4], uuid[4:8]) else: diskmount = usbloop.partitions[0]['mount'] usblabel = "UUID=%s" % diskmount.uuid overlaysuffix = "-%s-%s" % (diskmount.fslabel, diskmount.uuid) args = ['cp', "-Rf", isodir + "/isolinux", usbmnt + "/syslinux"] rc = runner.show(args) if rc: raise CreatorError("Can't copy isolinux directory %s" \ % (isodir + "/isolinux/*")) if os.path.isfile("/usr/share/syslinux/isolinux.bin"): syslinux_path = "/usr/share/syslinux" elif os.path.isfile("/usr/lib/syslinux/isolinux.bin"): syslinux_path = "/usr/lib/syslinux" else: raise CreatorError("syslinux not installed : " "cannot find syslinux installation path") for f in ("isolinux.bin", "vesamenu.c32"): path = os.path.join(syslinux_path, f) if os.path.isfile(path): args = ['cp', path, usbmnt + "/syslinux/"] rc = runner.show(args) if rc: raise CreatorError("Can't copy syslinux file " + path) else: raise CreatorError("syslinux not installed: " "syslinux file %s not found" % path) fd = open(isodir + "/isolinux/isolinux.cfg", "r") text = fd.read() fd.close() pattern = re.compile('CDLABEL=[^ ]*') text = pattern.sub(usblabel, text) pattern = re.compile('rootfstype=[^ ]*') text = pattern.sub("rootfstype=" + fstype, text) if kernelargs: text = text.replace("rd.live.image", "rd.live.image " + kernelargs) if overlaysizemb > 0: msger.info("Initializing persistent overlay file") overfile = "overlay" + overlaysuffix if fstype == "vfat": args = ['dd', "if=/dev/zero", "of=" + usbmnt + "/LiveOS/" + overfile, "count=%d" % overlaysizemb, "bs=1M"] else: args = ['dd', "if=/dev/null", "of=" + usbmnt + "/LiveOS/" + overfile, "count=1", "bs=1M", "seek=%d" % overlaysizemb] rc = runner.show(args) if rc: raise CreatorError("Can't create overlay file") text = text.replace("rd.live.image", "rd.live.image rd.live.overlay=" + usblabel) text = text.replace(" ro ", " rw ") if swapsizemb > 0: msger.info("Initializing swap file") swapfile = usbmnt + "/LiveOS/" + "swap.img" args = ['dd', "if=/dev/zero", "of=" + swapfile, "count=%d" % swapsizemb, "bs=1M"] rc = runner.show(args) if rc: raise CreatorError("Can't create swap file") args = ["mkswap", "-f", swapfile] rc = runner.show(args) if rc: raise CreatorError("Can't mkswap on swap file") if homesizemb > 0: msger.info("Initializing persistent /home") homefile = usbmnt + "/LiveOS/" + homefile if fstype == "vfat": args = ['dd', "if=/dev/zero", "of=" + homefile, "count=%d" % homesizemb, "bs=1M"] else: args = ['dd', "if=/dev/null", "of=" + homefile, "count=1", "bs=1M", "seek=%d" % homesizemb] rc = runner.show(args) if rc: raise CreatorError("Can't create home file") mkfscmd = fs_related.find_binary_path("/sbin/mkfs." + fstype) if fstype == "ext2" or fstype == "ext3": args = [mkfscmd, "-F", "-j", homefile] else: args = [mkfscmd, homefile] rc = runner.show(args) if rc: raise CreatorError("Can't mke2fs home file") if fstype == "ext2" or fstype == "ext3": tune2fs = fs_related.find_binary_path("tune2fs") args = [tune2fs, "-c0", "-i0", "-ouser_xattr,acl", homefile] rc = runner.show(args) if rc: raise CreatorError("Can't tune2fs home file") if fstype == "vfat" or fstype == "msdos": syslinuxcmd = fs_related.find_binary_path("syslinux") syslinuxcfg = usbmnt + "/syslinux/syslinux.cfg" args = [syslinuxcmd, "-d", "syslinux", usbloop.partitions[0]["device"]] elif fstype == "ext2" or fstype == "ext3": extlinuxcmd = fs_related.find_binary_path("extlinux") syslinuxcfg = usbmnt + "/syslinux/extlinux.conf" args = [extlinuxcmd, "-i", usbmnt + "/syslinux"] else: raise CreatorError("Invalid file system type: %s" % (fstype)) os.unlink(usbmnt + "/syslinux/isolinux.cfg") fd = open(syslinuxcfg, "w") fd.write(text) fd.close() rc = runner.show(args) if rc: raise CreatorError("Can't install boot loader.") finally: usbloop.unmount() usbloop.cleanup() # Need to do this after image is unmounted and device mapper is closed msger.info("set MBR") mbrfile = "/usr/lib/syslinux/mbr.bin" if not os.path.exists(mbrfile): mbrfile = "/usr/share/syslinux/mbr.bin" if not os.path.exists(mbrfile): raise CreatorError("mbr.bin file didn't exist.") mbrsize = os.path.getsize(mbrfile) outimg = "%s/%s.usbimg" % (self._outdir, self.name) args = ['dd', "if=" + mbrfile, "of=" + outimg, "seek=0", "conv=notrunc", "bs=1", "count=%d" % (mbrsize)] rc = runner.show(args) if rc: raise CreatorError("Can't set MBR.")
class DirectImageCreator(BaseImageCreator): """ Installs a system into a file containing a partitioned disk image. DirectImageCreator is an advanced ImageCreator subclass; an image file is formatted with a partition table, each partition created from a rootfs or other OpenEmbedded build artifact and dd'ed into the virtual disk. The disk image can subsequently be dd'ed onto media and used on actual hardware. """ def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir, kernel_dir, native_sysroot, hdddir, staging_data_dir, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"): """ Initialize a DirectImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, creatoropts, pkgmgr) self.__instimage = None self.__imgdir = None self.__disks = {} self.__disk_format = "direct" self._disk_names = [] self._ptable_format = self.ks.handler.bootloader.ptable self.use_uuid = fstab_entry == "uuid" self.compress_image = compress_image self.bmap_needed = generate_bmap self.oe_builddir = oe_builddir if image_output_dir: self.tmpdir = image_output_dir self.cachedir = "%s/cache" % image_output_dir self.rootfs_dir = rootfs_dir self.bootimg_dir = bootimg_dir self.kernel_dir = kernel_dir self.native_sysroot = native_sysroot self.hdddir = hdddir self.staging_data_dir = staging_data_dir self.boot_type = "" def __write_fstab(self): """overriden to generate fstab (temporarily) in rootfs. This is called from mount_instroot, make sure it doesn't get called from BaseImage.mount()""" image_rootfs = self.rootfs_dir parts = self._get_parts() fstab = image_rootfs + "/etc/fstab" self._save_fstab(fstab) fstab_lines = self._get_fstab(fstab, parts) self._update_fstab(fstab_lines, parts) self._write_fstab(fstab, fstab_lines) return fstab def _update_fstab(self, fstab_lines, parts): """Assume partition order same as in wks""" for num, p in enumerate(parts, 1): if p.mountpoint == "/" or p.mountpoint == "/boot": continue if self._ptable_format == 'msdos' and num > 3: device_name = "/dev/" + p.disk + str(num + 1) else: device_name = "/dev/" + p.disk + str(num) fstab_entry = device_name + "\t" + p.mountpoint + "\t" + p.fstype + "\tdefaults\t0\t0\n" fstab_lines.append(fstab_entry) def _write_fstab(self, fstab, fstab_lines): fstab = open(fstab, "w") for line in fstab_lines: fstab.write(line) fstab.close() def _save_fstab(self, fstab): """Save the current fstab in rootfs""" shutil.copyfile(fstab, fstab + ".orig") def _restore_fstab(self, fstab): """Restore the saved fstab in rootfs""" shutil.move(fstab + ".orig", fstab) def _get_fstab(self, fstab, parts): """Return the desired contents of /etc/fstab.""" f = open(fstab, "r") fstab_contents = f.readlines() f.close() return fstab_contents def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_disk_names(self): """ Returns a list of physical target disk names (e.g., 'sdb') which will be created. """ if self._disk_names: return self._disk_names #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk_name = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if parts[i].mountpoint and not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified for partition with mountpoint " "'%s' in the ks file") self._disk_names.append(disk_name) return self._disk_names def _full_name(self, name, extention): """ Construct full file name for a file we generate. """ return "%s-%s.%s" % (self.name, name, extention) def _full_path(self, path, name, extention): """ Construct full file path to a file we generate. """ return os.path.join(path, self._full_name(name, extention)) def get_boot_type(self): """ Determine the boot type from fstype and mountpoint. """ parts = self._get_parts() boot_type = "" for p in parts: if p.mountpoint == "/boot": if p.fstype == "msdos": boot_type = "pcbios" else: boot_type = p.fstype return boot_type # # Actual implemention # def _mount_instroot(self, base_on=None): """ For 'wic', we already have our build artifacts and don't want to loop mount anything to install into, we just create filesystems from the artifacts directly and combine them into a partitioned image. We still want to reuse as much of the basic mic machinery though; despite the fact that we don't actually do loop or any other kind of mounting we still want to do many of the same things to prepare images, so we basically just adapt to the basic framework and reinterpret what 'mounting' means in our context. _instroot would normally be something like /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for installing packages, etc. We don't currently need to do that, so we simplify life by just using /var/tmp/wic/build as our workdir. """ parts = self._get_parts() self.__instimage = PartitionedMount(self._instroot) fstab = self.__write_fstab() self.boot_type = self.get_boot_type() if not self.bootimg_dir: if self.boot_type == "pcbios": self.bootimg_dir = self.staging_data_dir elif self.boot_type == "efi": self.bootimg_dir = self.hdddir if self.boot_type == "pcbios": self._create_syslinux_config() elif self.boot_type == "efi": self._create_grubefi_config() else: raise CreatorError( "Failed to detect boot type (no /boot partition?), " "please check your kickstart setting.") for p in parts: if p.fstype == "efi": p.fstype = "msdos" # need to create the filesystems in order to get their # sizes before we can add them and do the layout. # PartitionedMount.mount() actually calls __format_disks() # to create the disk images and carve out the partitions, # then self.install() calls PartitionedMount.install() # which calls __install_partitition() for each partition # to dd the fs into the partitions. It would be nice to # be able to use e.g. ExtDiskMount etc to create the # filesystems, since that's where existing e.g. mkfs code # is, but those are only created after __format_disks() # which needs the partition sizes so needs them created # before its called. Well, the existing setup is geared # to installing packages into mounted filesystems - maybe # when/if we need to actually do package selection we # should modify things to use those objects, but for now # we can avoid that. p.prepare(self.workdir, self.oe_builddir, self.boot_type, self.rootfs_dir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) self.__instimage.add_partition(int(p.size), p.disk, p.mountpoint, p.source_file, p.fstype, p.label, fsopts=p.fsopts, boot=p.active, align=p.align, part_type=p.part_type) self._restore_fstab(fstab) self.__instimage.layout_partitions(self._ptable_format) self.__imgdir = self.workdir for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.DiskImage(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instimage.add_disk(disk_name, disk_obj) self.__instimage.mount() def install(self, repo_urls=None): """ Install fs images into partitions """ for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Installing disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) self.__instimage.install(full_path) def configure(self, repodata=None): """ Configure the system image according to kickstart. For now, it just prepares the image to be bootable by e.g. creating and installing a bootloader configuration. """ if self.boot_type == "pcbios": self._install_syslinux() def print_outimage_info(self): """ Print the image(s) and artifacts used, for the user. """ msg = "The new image(s) can be found here:\n" for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msg += ' %s\n\n' % full_path msg += 'The following build artifacts were used to create the image(s):\n' msg += ' ROOTFS_DIR: %s\n' % self.rootfs_dir msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir msg += ' KERNEL_DIR: %s\n' % self.kernel_dir msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot msger.info(msg) def _get_boot_config(self): """ Return the rootdev/root_part_uuid (if specified by --part-type) Assume partition order same as in wks """ rootdev = None root_part_uuid = None parts = self._get_parts() for num, p in enumerate(parts, 1): if p.mountpoint == "/": if self._ptable_format == 'msdos' and num > 3: rootdev = "/dev/%s%-d" % (p.disk, num + 1) else: rootdev = "/dev/%s%-d" % (p.disk, num) root_part_uuid = p.part_type return (rootdev, root_part_uuid) def _create_syslinux_config(self): hdddir = "%s/hdd/boot" % self.workdir rm_cmd = "rm -rf " + self.workdir exec_cmd(rm_cmd) install_cmd = "install -d %s" % hdddir tmp = exec_cmd(install_cmd) splash = os.path.join(self.workdir, "/hdd/boot/splash.jpg") if os.path.exists(splash): splashline = "menu background splash.jpg" else: splashline = "" (rootdev, root_part_uuid) = self._get_boot_config() options = self.ks.handler.bootloader.appendLine syslinux_conf = "" syslinux_conf += "PROMPT 0\n" timeout = kickstart.get_timeout(self.ks) if not timeout: timeout = 0 syslinux_conf += "TIMEOUT " + str(timeout) + "\n" syslinux_conf += "\n" syslinux_conf += "ALLOWOPTIONS 1\n" syslinux_conf += "SERIAL 0 115200\n" syslinux_conf += "\n" if splashline: syslinux_conf += "%s\n" % splashline syslinux_conf += "DEFAULT boot\n" syslinux_conf += "LABEL boot\n" kernel = "/vmlinuz" syslinux_conf += "KERNEL " + kernel + "\n" if self._ptable_format == 'msdos': rootstr = rootdev else: if not root_part_uuid: raise MountError("Cannot find the root GPT partition UUID") rootstr = "PARTUUID=%s" % root_part_uuid syslinux_conf += "APPEND label=boot root=%s %s\n" % (rootstr, options) msger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg" \ % self.workdir) cfg = open("%s/hdd/boot/syslinux.cfg" % self.workdir, "w") cfg.write(syslinux_conf) cfg.close() def _create_grubefi_config(self): hdddir = "%s/hdd/boot" % self.workdir rm_cmd = "rm -rf %s" % self.workdir exec_cmd(rm_cmd) install_cmd = "install -d %s/EFI/BOOT" % hdddir tmp = exec_cmd(install_cmd) splash = os.path.join(self.workdir, "/EFI/boot/splash.jpg") if os.path.exists(splash): splashline = "menu background splash.jpg" else: splashline = "" (rootdev, root_part_uuid) = self._get_boot_config() options = self.ks.handler.bootloader.appendLine grubefi_conf = "" grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n" grubefi_conf += "default=boot\n" timeout = kickstart.get_timeout(self.ks) if not timeout: timeout = 0 grubefi_conf += "timeout=%s\n" % timeout grubefi_conf += "menuentry 'boot'{\n" kernel = "/vmlinuz" if self._ptable_format == 'msdos': rootstr = rootdev else: if not root_part_uuid: raise MountError("Cannot find the root GPT partition UUID") rootstr = "PARTUUID=%s" % root_part_uuid grubefi_conf += "linux %s root=%s rootwait %s\n" \ % (kernel, rootstr, options) grubefi_conf += "}\n" if splashline: syslinux_conf += "%s\n" % splashline msger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg" \ % self.workdir) cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % self.workdir, "w") cfg.write(grubefi_conf) cfg.close() def _install_syslinux(self): mbrfile = "%s/syslinux/" % self.bootimg_dir if self._ptable_format == 'gpt': mbrfile += "gptmbr.bin" else: mbrfile += "mbr.bin" if not os.path.exists(mbrfile): msger.error( "Couldn't find %s. If using the -e option, do you have the right MACHINE set in local.conf? If not, is the bootimg_dir path correct?" % mbrfile) for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Installing MBR on disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) rc = runner.show( ['dd', 'if=%s' % mbrfile, 'of=%s' % full_path, 'conv=notrunc']) if rc != 0: raise MountError("Unable to set MBR to %s" % full_path) def _unmount_instroot(self): if not self.__instimage is None: try: self.__instimage.cleanup() except MountError, err: msger.warning("%s" % err)
def do_chroot(cls, target, cmd=[]): img = target imgsize = misc.get_file_size(img) * 1024L * 1024L partedcmd = fs_related.find_binary_path("parted") disk = fs_related.SparseLoopbackDisk(img, imgsize) imgmnt = misc.mkdtemp() imgloop = PartitionedMount(imgmnt, skipformat = True) imgloop.add_disk('/dev/sdb', disk) img_fstype = "ext3" msger.info("Partition Table:") partnum = [] for line in runner.outs([partedcmd, "-s", img, "print"]).splitlines(): # no use strip to keep line output here if "Number" in line: msger.raw(line) if line.strip() and line.strip()[0].isdigit(): partnum.append(line.strip()[0]) msger.raw(line) rootpart = None if len(partnum) > 1: rootpart = msger.choice("please choose root partition", partnum) # Check the partitions from raw disk. # if choose root part, the mark it as mounted if rootpart: root_mounted = True else: root_mounted = False partition_mounts = 0 for line in runner.outs([partedcmd,"-s",img,"unit","B","print"]).splitlines(): line = line.strip() # Lines that start with number are the partitions, # because parted can be translated we can't refer to any text lines. if not line or not line[0].isdigit(): continue # Some vars have extra , as list seperator. line = line.replace(",","") # Example of parted output lines that are handled: # Number Start End Size Type File system Flags # 1 512B 3400000511B 3400000000B primary # 2 3400531968B 3656384511B 255852544B primary linux-swap(v1) # 3 3656384512B 3720347647B 63963136B primary fat16 boot, lba partition_info = re.split("\s+",line) size = partition_info[3].split("B")[0] if len(partition_info) < 6 or partition_info[5] in ["boot"]: # No filesystem can be found from partition line. Assuming # btrfs, because that is the only MeeGo fs that parted does # not recognize properly. # TODO: Can we make better assumption? fstype = "btrfs" elif partition_info[5] in ["ext2","ext3","ext4","btrfs"]: fstype = partition_info[5] elif partition_info[5] in ["fat16","fat32"]: fstype = "vfat" elif "swap" in partition_info[5]: fstype = "swap" else: raise errors.CreatorError("Could not recognize partition fs type '%s'." % partition_info[5]) if rootpart and rootpart == line[0]: mountpoint = '/' elif not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]: # TODO: Check that this is actually the valid root partition from /etc/fstab mountpoint = "/" root_mounted = True elif fstype == "swap": mountpoint = "swap" else: # TODO: Assing better mount points for the rest of the partitions. partition_mounts += 1 mountpoint = "/media/partition_%d" % partition_mounts if "boot" in partition_info: boot = True else: boot = False msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot)) # TODO: add_partition should take bytes as size parameter. imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint, fstype = fstype, boot = boot) try: imgloop.mount() except errors.MountError: imgloop.cleanup() raise try: if len(cmd) != 0: cmdline = ' '.join(cmd) else: cmdline = "/bin/bash" envcmd = fs_related.find_binary_inchroot("env", imgmnt) if envcmd: cmdline = "%s HOME=/root %s" % (envcmd, cmdline) chroot.chroot(imgmnt, None, cmdline) except: raise errors.CreatorError("Failed to chroot to %s." %img) finally: chroot.cleanup_after_chroot("img", imgloop, None, imgmnt)
class DirectImageCreator(BaseImageCreator): """ Installs a system into a file containing a partitioned disk image. DirectImageCreator is an advanced ImageCreator subclass; an image file is formatted with a partition table, each partition created from a rootfs or other OpenEmbedded build artifact and dd'ed into the virtual disk. The disk image can subsequently be dd'ed onto media and used on actual hardware. """ def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir, kernel_dir, native_sysroot, hdddir, staging_data_dir, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"): """ Initialize a DirectImageCreator instance. This method takes the same arguments as ImageCreator.__init__() """ BaseImageCreator.__init__(self, creatoropts, pkgmgr) self.__instimage = None self.__imgdir = None self.__disks = {} self.__disk_format = "direct" self._disk_names = [] self._ptable_format = self.ks.handler.bootloader.ptable self.use_uuid = fstab_entry == "uuid" self.compress_image = compress_image self.bmap_needed = generate_bmap self.oe_builddir = oe_builddir if image_output_dir: self.tmpdir = image_output_dir self.cachedir = "%s/cache" % image_output_dir self.rootfs_dir = rootfs_dir self.bootimg_dir = bootimg_dir self.kernel_dir = kernel_dir self.native_sysroot = native_sysroot self.hdddir = hdddir self.staging_data_dir = staging_data_dir self.boot_type = "" def __write_fstab(self): """overriden to generate fstab (temporarily) in rootfs. This is called from mount_instroot, make sure it doesn't get called from BaseImage.mount()""" image_rootfs = self.rootfs_dir parts = self._get_parts() fstab = image_rootfs + "/etc/fstab" self._save_fstab(fstab) fstab_lines = self._get_fstab(fstab, parts) self._update_fstab(fstab_lines, parts) self._write_fstab(fstab, fstab_lines) return fstab def _update_fstab(self, fstab_lines, parts): """Assume partition order same as in wks""" for num, p in enumerate(parts, 1): if p.mountpoint == "/" or p.mountpoint == "/boot": continue if self._ptable_format == 'msdos' and num > 3: device_name = "/dev/" + p.disk + str(num + 1) else: device_name = "/dev/" + p.disk + str(num) fstab_entry = device_name + "\t" + p.mountpoint + "\t" + p.fstype + "\tdefaults\t0\t0\n" fstab_lines.append(fstab_entry) def _write_fstab(self, fstab, fstab_lines): fstab = open(fstab, "w") for line in fstab_lines: fstab.write(line) fstab.close() def _save_fstab(self, fstab): """Save the current fstab in rootfs""" shutil.copyfile(fstab, fstab + ".orig") def _restore_fstab(self, fstab): """Restore the saved fstab in rootfs""" shutil.move(fstab + ".orig", fstab) def _get_fstab(self, fstab, parts): """Return the desired contents of /etc/fstab.""" f = open(fstab, "r") fstab_contents = f.readlines() f.close() return fstab_contents def _get_parts(self): if not self.ks: raise CreatorError("Failed to get partition info, " "please check your kickstart setting.") # Set a default partition if no partition is given out if not self.ks.handler.partition.partitions: partstr = "part / --size 1900 --ondisk sda --fstype=ext3" args = partstr.split() pd = self.ks.handler.partition.parse(args[1:]) if pd not in self.ks.handler.partition.partitions: self.ks.handler.partition.partitions.append(pd) # partitions list from kickstart file return kickstart.get_partitions(self.ks) def get_disk_names(self): """ Returns a list of physical target disk names (e.g., 'sdb') which will be created. """ if self._disk_names: return self._disk_names #get partition info from ks handler parts = self._get_parts() for i in range(len(parts)): if parts[i].disk: disk_name = parts[i].disk else: raise CreatorError("Failed to create disks, no --ondisk " "specified in partition line of ks file") if parts[i].mountpoint and not parts[i].fstype: raise CreatorError("Failed to create disks, no --fstype " "specified for partition with mountpoint " "'%s' in the ks file") self._disk_names.append(disk_name) return self._disk_names def _full_name(self, name, extention): """ Construct full file name for a file we generate. """ return "%s-%s.%s" % (self.name, name, extention) def _full_path(self, path, name, extention): """ Construct full file path to a file we generate. """ return os.path.join(path, self._full_name(name, extention)) def get_boot_type(self): """ Determine the boot type from fstype and mountpoint. """ parts = self._get_parts() boot_type = "" for p in parts: if p.mountpoint == "/boot": if p.fstype == "msdos": boot_type = "pcbios" else: boot_type = p.fstype return boot_type # # Actual implemention # def _mount_instroot(self, base_on = None): """ For 'wic', we already have our build artifacts and don't want to loop mount anything to install into, we just create filesystems from the artifacts directly and combine them into a partitioned image. We still want to reuse as much of the basic mic machinery though; despite the fact that we don't actually do loop or any other kind of mounting we still want to do many of the same things to prepare images, so we basically just adapt to the basic framework and reinterpret what 'mounting' means in our context. _instroot would normally be something like /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for installing packages, etc. We don't currently need to do that, so we simplify life by just using /var/tmp/wic/build as our workdir. """ parts = self._get_parts() self.__instimage = PartitionedMount(self._instroot) fstab = self.__write_fstab() self.boot_type = self.get_boot_type() if not self.bootimg_dir: if self.boot_type == "pcbios": self.bootimg_dir = self.staging_data_dir elif self.boot_type == "efi": self.bootimg_dir = self.hdddir if self.boot_type == "pcbios": self._create_syslinux_config() elif self.boot_type == "efi": self._create_grubefi_config() else: raise CreatorError("Failed to detect boot type (no /boot partition?), " "please check your kickstart setting.") for p in parts: if p.fstype == "efi": p.fstype = "msdos" # need to create the filesystems in order to get their # sizes before we can add them and do the layout. # PartitionedMount.mount() actually calls __format_disks() # to create the disk images and carve out the partitions, # then self.install() calls PartitionedMount.install() # which calls __install_partitition() for each partition # to dd the fs into the partitions. It would be nice to # be able to use e.g. ExtDiskMount etc to create the # filesystems, since that's where existing e.g. mkfs code # is, but those are only created after __format_disks() # which needs the partition sizes so needs them created # before its called. Well, the existing setup is geared # to installing packages into mounted filesystems - maybe # when/if we need to actually do package selection we # should modify things to use those objects, but for now # we can avoid that. p.prepare(self.workdir, self.oe_builddir, self.boot_type, self.rootfs_dir, self.bootimg_dir, self.kernel_dir, self.native_sysroot) self.__instimage.add_partition(int(p.size), p.disk, p.mountpoint, p.source_file, p.fstype, p.label, fsopts = p.fsopts, boot = p.active, align = p.align, part_type = p.part_type) self._restore_fstab(fstab) self.__instimage.layout_partitions(self._ptable_format) self.__imgdir = self.workdir for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Adding disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) disk_obj = fs_related.DiskImage(full_path, disk['min_size']) self.__disks[disk_name] = disk_obj self.__instimage.add_disk(disk_name, disk_obj) self.__instimage.mount() def install(self, repo_urls=None): """ Install fs images into partitions """ for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Installing disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) self.__instimage.install(full_path) def configure(self, repodata = None): """ Configure the system image according to kickstart. For now, it just prepares the image to be bootable by e.g. creating and installing a bootloader configuration. """ if self.boot_type == "pcbios": self._install_syslinux() def print_outimage_info(self): """ Print the image(s) and artifacts used, for the user. """ msg = "The new image(s) can be found here:\n" for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msg += ' %s\n\n' % full_path msg += 'The following build artifacts were used to create the image(s):\n' msg += ' ROOTFS_DIR: %s\n' % self.rootfs_dir msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir msg += ' KERNEL_DIR: %s\n' % self.kernel_dir msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot msger.info(msg) def _get_boot_config(self): """ Return the rootdev/root_part_uuid (if specified by --part-type) Assume partition order same as in wks """ rootdev = None root_part_uuid = None parts = self._get_parts() for num, p in enumerate(parts, 1): if p.mountpoint == "/": if self._ptable_format == 'msdos' and num > 3: rootdev = "/dev/%s%-d" % (p.disk, num + 1) else: rootdev = "/dev/%s%-d" % (p.disk, num) root_part_uuid = p.part_type return (rootdev, root_part_uuid) def _create_syslinux_config(self): hdddir = "%s/hdd/boot" % self.workdir rm_cmd = "rm -rf " + self.workdir exec_cmd(rm_cmd) install_cmd = "install -d %s" % hdddir tmp = exec_cmd(install_cmd) splash = os.path.join(self.workdir, "/hdd/boot/splash.jpg") if os.path.exists(splash): splashline = "menu background splash.jpg" else: splashline = "" (rootdev, root_part_uuid) = self._get_boot_config() options = self.ks.handler.bootloader.appendLine syslinux_conf = "" syslinux_conf += "PROMPT 0\n" timeout = kickstart.get_timeout(self.ks) if not timeout: timeout = 0 syslinux_conf += "TIMEOUT " + str(timeout) + "\n" syslinux_conf += "\n" syslinux_conf += "ALLOWOPTIONS 1\n" syslinux_conf += "SERIAL 0 115200\n" syslinux_conf += "\n" if splashline: syslinux_conf += "%s\n" % splashline syslinux_conf += "DEFAULT boot\n" syslinux_conf += "LABEL boot\n" kernel = "/vmlinuz" syslinux_conf += "KERNEL " + kernel + "\n" if self._ptable_format == 'msdos': rootstr = rootdev else: if not root_part_uuid: raise MountError("Cannot find the root GPT partition UUID") rootstr = "PARTUUID=%s" % root_part_uuid syslinux_conf += "APPEND label=boot root=%s %s\n" % (rootstr, options) msger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg" \ % self.workdir) cfg = open("%s/hdd/boot/syslinux.cfg" % self.workdir, "w") cfg.write(syslinux_conf) cfg.close() def _create_grubefi_config(self): hdddir = "%s/hdd/boot" % self.workdir rm_cmd = "rm -rf %s" % self.workdir exec_cmd(rm_cmd) install_cmd = "install -d %s/EFI/BOOT" % hdddir tmp = exec_cmd(install_cmd) splash = os.path.join(self.workdir, "/EFI/boot/splash.jpg") if os.path.exists(splash): splashline = "menu background splash.jpg" else: splashline = "" (rootdev, root_part_uuid) = self._get_boot_config() options = self.ks.handler.bootloader.appendLine grubefi_conf = "" grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n" grubefi_conf += "default=boot\n" timeout = kickstart.get_timeout(self.ks) if not timeout: timeout = 0 grubefi_conf += "timeout=%s\n" % timeout grubefi_conf += "menuentry 'boot'{\n" kernel = "/vmlinuz" if self._ptable_format == 'msdos': rootstr = rootdev else: if not root_part_uuid: raise MountError("Cannot find the root GPT partition UUID") rootstr = "PARTUUID=%s" % root_part_uuid grubefi_conf += "linux %s root=%s rootwait %s\n" \ % (kernel, rootstr, options) grubefi_conf += "}\n" if splashline: syslinux_conf += "%s\n" % splashline msger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg" \ % self.workdir) cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % self.workdir, "w") cfg.write(grubefi_conf) cfg.close() def _install_syslinux(self): mbrfile = "%s/syslinux/" % self.bootimg_dir if self._ptable_format == 'gpt': mbrfile += "gptmbr.bin" else: mbrfile += "mbr.bin" if not os.path.exists(mbrfile): msger.error("Couldn't find %s. If using the -e option, do you have the right MACHINE set in local.conf? If not, is the bootimg_dir path correct?" % mbrfile) for disk_name, disk in self.__instimage.disks.items(): full_path = self._full_path(self.__imgdir, disk_name, "direct") msger.debug("Installing MBR on disk %s as %s with size %s bytes" \ % (disk_name, full_path, disk['min_size'])) rc = runner.show(['dd', 'if=%s' % mbrfile, 'of=%s' % full_path, 'conv=notrunc']) if rc != 0: raise MountError("Unable to set MBR to %s" % full_path) def _unmount_instroot(self): if not self.__instimage is None: try: self.__instimage.cleanup() except MountError, err: msger.warning("%s" % err)