aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/imager/direct.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/imager/direct.py')
-rw-r--r--scripts/lib/wic/imager/direct.py362
1 files changed, 362 insertions, 0 deletions
diff --git a/scripts/lib/wic/imager/direct.py b/scripts/lib/wic/imager/direct.py
new file mode 100644
index 0000000000..5b12856289
--- /dev/null
+++ b/scripts/lib/wic/imager/direct.py
@@ -0,0 +1,362 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Copyright (c) 2013, Intel Corporation.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# DESCRIPTION
+# This implements the 'direct' image creator class for 'wic'
+#
+# AUTHORS
+# Tom Zanussi <tom.zanussi (at] linux.intel.com>
+#
+
+import os
+import stat
+import shutil
+
+from wic import kickstart, msger
+from wic.utils import fs_related, runner, misc
+from wic.utils.partitionedfs import Image
+from wic.utils.errors import CreatorError, ImageError
+from wic.imager.baseimager import BaseImageCreator
+from wic.utils.oe.misc import *
+from wic.plugin import pluginmgr
+
+disk_methods = {
+ "do_install_disk":None,
+}
+
+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):
+ """
+ Initialize a DirectImageCreator instance.
+
+ This method takes the same arguments as ImageCreator.__init__()
+ """
+ BaseImageCreator.__init__(self, creatoropts)
+
+ self.__image = None
+ self.__disks = {}
+ self.__disk_format = "direct"
+ self._disk_names = []
+ self._ptable_format = self.ks.handler.bootloader.ptable
+
+ self.oe_builddir = oe_builddir
+ if image_output_dir:
+ self.tmpdir = 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 _create, make sure it doesn't get called from
+ BaseImage.create()
+ """
+ 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 _create(self):
+ """
+ For 'wic', we already have our build artifacts - we just create
+ filesystems from the artifacts directly and combine them into
+ a partitioned image.
+ """
+ parts = self._get_parts()
+
+ self.__image = Image()
+
+ 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.
+ # Image.create() actually calls __format_disks() to create
+ # the disk images and carve out the partitions, then
+ # self.assemble() calls Image.assemble() which calls
+ # __write_partitition() for each partition to dd the fs
+ # into the partitions.
+ 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.__image.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.__image.layout_partitions(self._ptable_format)
+
+ self.__imgdir = self.workdir
+ for disk_name, disk in self.__image.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.__image.add_disk(disk_name, disk_obj)
+
+ self.__image.create()
+
+ def assemble(self):
+ """
+ Assemble partitions into disk image(s)
+ """
+ for disk_name, disk in self.__image.disks.items():
+ full_path = self._full_path(self.__imgdir, disk_name, "direct")
+ msger.debug("Assembling disk %s as %s with size %s bytes" \
+ % (disk_name, full_path, disk['min_size']))
+ self.__image.assemble(full_path)
+
+ def finalize(self):
+ """
+ Finalize the disk image.
+
+ For example, prepare 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.__image.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.__image.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 _cleanup(self):
+ if not self.__image is None:
+ try:
+ self.__image.cleanup()
+ except ImageError, err:
+ msger.warning("%s" % err)
+