+# -*- coding: utf-8 -*-
+#
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
-from image_creator.util import FatalError
+from image_creator.util import FatalError, check_guestfs_version
from image_creator.gpt import GPTPartitionTable
from image_creator.os_type import os_cls
class Image(object):
"""The instances of this class can create images out of block devices."""
- def __init__(self, device, output, bootable=True, meta={}):
- """Create a new ImageCreator."""
+ def __init__(self, device, output, **kargs):
+ """Create a new Image instance"""
self.device = device
self.out = output
- self.bootable = bootable
- self.meta = meta
+
+ self.meta = kargs['meta'] if 'meta' in kargs else {}
+ self.sysprep_params = \
+ kargs['sysprep_params'] if 'sysprep_params' in kargs else {}
+
self.progress_bar = None
self.guestfs_device = None
self.size = 0
- self.mounted = False
self.g = guestfs.GuestFS()
+ self.guestfs_enabled = False
+
+ def enable(self):
+ """Enable a newly created Image instance"""
+
+ self.enable_guestfs()
+
+ self.out.output('Inspecting Operating System ...', False)
+ roots = self.g.inspect_os()
+ if len(roots) == 0:
+ raise FatalError("No operating system found")
+ if len(roots) > 1:
+ raise FatalError("Multiple operating systems found."
+ "We only support images with one OS.")
+ self.root = roots[0]
+ self.guestfs_device = self.g.part_to_dev(self.root)
+ self.size = self.g.blockdev_getsize64(self.guestfs_device)
+ self.meta['PARTITION_TABLE'] = \
+ self.g.part_get_parttype(self.guestfs_device)
+
+ self.ostype = self.g.inspect_get_type(self.root)
+ self.distro = self.g.inspect_get_distro(self.root)
+ self.out.success(
+ 'found a(n) %s system' %
+ self.ostype if self.distro == "unknown" else self.distro)
+
+ def enable_guestfs(self):
+ """Enable the guestfs handler"""
+
+ if self.guestfs_enabled:
+ self.out.warn("Guestfs is already enabled")
+ return
+
self.g.add_drive_opts(self.device, readonly=0, format="raw")
# Before version 1.17.14 the recovery process, which is a fork of the
# file descriptors. This can cause problems especially if the parent
# process has opened pipes. Since the recovery process is an optional
# feature of libguestfs, it's better to disable it.
- self.g.set_recovery_proc(0)
- version = self.g.version()
- if version['major'] > 1 or \
- (version['major'] == 1 and (version['minor'] >= 18 or
- (version['minor'] == 17 and
- version['release'] >= 14))):
- self.g.set_recovery_proc(1)
+ if check_guestfs_version(self.g, 1, 17, 14) >= 0:
self.out.output("Enabling recovery proc")
+ self.g.set_recovery_proc(1)
+ else:
+ self.g.set_recovery_proc(0)
#self.g.set_trace(1)
#self.g.set_verbose(1)
- self.guestfs_enabled = False
-
- def enable(self):
- """Enable a newly created ImageCreator"""
-
self.out.output('Launching helper VM (may take a while) ...', False)
# self.progressbar = self.out.Progress(100, "Launching helper VM",
# "percent")
# self.progressbar = None
self.out.success('done')
- self.out.output('Inspecting Operating System ...', False)
- roots = self.g.inspect_os()
- if len(roots) == 0:
- raise FatalError("No operating system found")
- if len(roots) > 1:
- raise FatalError("Multiple operating systems found."
- "We only support images with one OS.")
- self.root = roots[0]
- self.guestfs_device = self.g.part_to_dev(self.root)
- self.size = self.g.blockdev_getsize64(self.guestfs_device)
- self.meta['PARTITION_TABLE'] = \
- self.g.part_get_parttype(self.guestfs_device)
+ def disable_guestfs(self):
+ """Disable the guestfs handler"""
- self.ostype = self.g.inspect_get_type(self.root)
- self.distro = self.g.inspect_get_distro(self.root)
- self.out.success('found a(n) %s system' % self.distro)
+ if not self.guestfs_enabled:
+ self.out.warn("Guestfs is already disabled")
+ return
+
+ self.out.output("Shutting down helper VM ...", False)
+ self.g.sync()
+ # guestfs_shutdown which is the prefered way to shutdown the backend
+ # process was introduced in version 1.19.16
+ if check_guestfs_version(self.g, 1, 19, 16) >= 0:
+ self.g.shutdown()
+ else:
+ self.g.kill_subprocess()
+
+ self.guestfs_enabled = False
+ self.out.success('done')
def _get_os(self):
+ """Return an OS class instance for this image"""
if hasattr(self, "_os"):
return self._os
if not self.guestfs_enabled:
self.enable()
- if not self.mounted:
- do_unmount = True
- self.mount(readonly=True)
- else:
- do_unmount = False
+ cls = os_cls(self.distro, self.ostype)
+ self._os = cls(self, sysprep_params=self.sysprep_params)
- try:
- cls = os_cls(self.distro, self.ostype)
- self._os = cls(self.root, self.g, self.out)
-
- finally:
- if do_unmount:
- self.umount()
+ self._os.collect_metadata()
return self._os
os = property(_get_os)
def destroy(self):
- """Destroy this ImageCreator instance."""
+ """Destroy this Image instance."""
# In new guestfs versions, there is a handy shutdown method for this
try:
#
# self.progressbar.goto((position * 100) // total)
- def mount(self, readonly=False):
- """Mount all disk partitions in a correct order."""
-
- mount = self.g.mount_ro if readonly else self.g.mount
- msg = " read-only" if readonly else ""
- self.out.output("Mounting the media%s ..." % msg, False)
- mps = self.g.inspect_get_mountpoints(self.root)
-
- # Sort the keys to mount the fs in a correct order.
- # / should be mounted befor /boot, etc
- def compare(a, b):
- if len(a[0]) > len(b[0]):
- return 1
- elif len(a[0]) == len(b[0]):
- return 0
- else:
- return -1
- mps.sort(compare)
- for mp, dev in mps:
- try:
- mount(dev, mp)
- except RuntimeError as msg:
- self.out.warn("%s (ignored)" % msg)
-
- self.mounted = True
- self.out.success("done")
-
- def umount(self):
- """Umount all mounted filesystems."""
- self.g.umount_all()
- self.mounted = False
-
def _last_partition(self):
+ """Return the last partition of the image disk"""
if self.meta['PARTITION_TABLE'] not in 'msdos' 'gpt':
msg = "Unsupported partition table: %s. Only msdos and gpt " \
"partition tables are supported" % self.meta['PARTITION_TABLE']
return last_partition
def shrink(self):
- """Shrink the disk.
+ """Shrink the image.
- This is accomplished by shrinking the last filesystem in the
- disk and then updating the partition table. The new disk size
+ This is accomplished by shrinking the last file system of the
+ image and then updating the partition table. The new disk size
(in bytes) is returned.
ATTENTION: make sure unmount is called before shrink
break
if not re.match("ext[234]", fstype):
- self.out.warn("Don't know how to resize %s partitions." % fstype)
+ self.out.warn("Don't know how to shrink %s partitions." % fstype)
return self.size
part_dev = "%s%d" % (self.guestfs_device, last_part['part_num'])
return self.size
def dump(self, outfile):
- """Dumps the content of device into a file.
+ """Dumps the content of the image into a file.
This method will only dump the actual payload, found by reading the
partition table. Empty space in the end of the device will be ignored.