X-Git-Url: https://code.grnet.gr/git/snf-image-creator/blobdiff_plain/e6f134b3ed30a6c6e5808ae0e5198cf94c49b457..f3845095c7f8802c4dd40d7679e14831d7f70b59:/image_creator/bundle_volume.py diff --git a/image_creator/bundle_volume.py b/image_creator/bundle_volume.py index 5b94c1c..bd418ce 100644 --- a/image_creator/bundle_volume.py +++ b/image_creator/bundle_volume.py @@ -33,9 +33,7 @@ import os import re -import uuid import tempfile -import time from collections import namedtuple import parted @@ -43,14 +41,15 @@ import parted from image_creator.rsync import Rsync from image_creator.util import get_command from image_creator.util import FatalError +from image_creator.util import try_fail_repeat findfs = get_command('findfs') -truncate = get_command('truncate') dd = get_command('dd') dmsetup = get_command('dmsetup') losetup = get_command('losetup') mount = get_command('mount') umount = get_command('umount') +blkid = get_command('blkid') MKFS_OPTS = { 'ext2': ['-F'], @@ -67,9 +66,11 @@ MKFS_OPTS = { } -class BundleVolume(): +class BundleVolume(object): + """This class can be used to create an image out of the running system""" def __init__(self, out, meta): + """Create an instance of the BundleVolume class.""" self.out = out self.meta = meta @@ -218,7 +219,7 @@ class BundleVolume(): image_disk.setPartitionGeometry( image_disk.getPartitionBySector(last.start), parted.Constraint(device=image_disk.device), - start=last.start, end=last.end) + start=last.start, end=part_end) image_disk.commit() # Parted may have changed this for better alignment @@ -227,7 +228,7 @@ class BundleVolume(): partitions[-1] = last # Leave 2048 blocks at the end. - new_end = new_size + 2048 + new_end = part_end + 2048 if last.type == parted.PARTITION_LOGICAL: # Fix the extended partition @@ -238,6 +239,7 @@ class BundleVolume(): ext.geometry.start, end=last.end) image_disk.commit() + image_dev.destroy() return new_end def _map_partition(self, dev, num, start, end): @@ -256,8 +258,7 @@ class BundleVolume(): if not os.path.exists(dev): return - dmsetup('remove', dev.split('/dev/mapper/')[1]) - time.sleep(0.1) + try_fail_repeat(dmsetup, 'remove', dev.split('/dev/mapper/')[1]) def _mount(self, target, devs): @@ -276,10 +277,10 @@ class BundleVolume(): mpoints.sort() for mpoint in reversed(mpoints): - umount(mpoint) + try_fail_repeat(umount, mpoint) def _to_exclude(self): - excluded = ['/tmp'] + excluded = ['/tmp', '/var/tmp'] local_filesystems = MKFS_OPTS.keys() + ['rootfs'] for entry in self._read_fstable('/proc/mounts'): if entry.fs in local_filesystems: @@ -311,15 +312,38 @@ class BundleVolume(): return map(lambda d: d + "/*", excluded) + def _replace_uuids(self, target, new_uuid): + + files = ['/etc/fstab', + '/boot/grub/grub.cfg', + '/boot/grub/menu.lst', + '/boot/grub/grub.conf'] + + orig = dict(map(lambda p: (p.number, blkid('-s', 'UUID', '-o', + 'value', p.path).stdout.strip()), self.disk.partitions)) + + for f in map(lambda f: target + f, files): + + if not os.path.exists(f): + continue + + with open(f, 'r') as src: + lines = src.readlines() + with open(f, 'w') as dest: + for line in lines: + for i, uuid in new_uuid.items(): + line = re.sub(orig[i], uuid, line) + dest.write(line) + def _create_filesystems(self, image): - partitions = self._get_partitions(parted.Disk(parted.Device(image))) - filesystems = {} + filesystem = {} for p in self.disk.partitions: - filesystems[p.number] = self._get_mount_options(p.path) + filesystem[p.number] = self._get_mount_options(p.path) - unmounted = filter(lambda p: filesystems[p.num] is None, partitions) - mounted = filter(lambda p: filesystems[p.num] is not None, partitions) + partitions = self._get_partitions(parted.Disk(parted.Device(image))) + unmounted = filter(lambda p: filesystem[p.num] is None, partitions) + mounted = filter(lambda p: filesystem[p.num] is not None, partitions) # For partitions that are not mounted right now, we can simply dd them # into the image. @@ -335,18 +359,21 @@ class BundleVolume(): i = p.num mapped[i] = self._map_partition(loop, i, p.start, p.end) + new_uuid = {} # Create the file systems for i, dev in mapped.iteritems(): - fs = filesystems[i].fs + fs = filesystem[i].fs self.out.output('Creating %s filesystem on partition %d ... ' % (fs, i), False) get_command('mkfs.%s' % fs)(*(MKFS_OPTS[fs] + [dev])) self.out.success('done') + new_uuid[i] = blkid('-s', 'UUID', '-o', 'value', dev + ).stdout.strip() target = tempfile.mkdtemp() try: absmpoints = self._mount(target, - [(mapped[i], filesystems[i].mpoint) for i in mapped.keys()] + [(mapped[i], filesystem[i].mpoint) for i in mapped.keys()] ) exclude = self._to_exclude() + [image] rsync = Rsync('/', target, @@ -354,6 +381,8 @@ class BundleVolume(): msg = "Copying host files into the image" rsync.archive().run(self.out, msg) + self._replace_uuids(target, new_uuid) + finally: self._umount_all(target) os.rmdir(target) @@ -362,16 +391,22 @@ class BundleVolume(): self._unmap_partition(dev) losetup('-d', loop) - def create_image(self): - - image = '/mnt/%s.diskdump' % uuid.uuid4().hex + def create_image(self, image): + """Given an image filename, this method will create an image out of the + running system. + """ - disk_size = self.disk.device.getLength() * self.disk.device.sectorSize + size = self.disk.device.getLength() * self.disk.device.sectorSize # Create sparse file to host the image - truncate("-s", "%d" % disk_size, image) + fd = os.open(image, os.O_WRONLY | os.O_CREAT) + try: + os.ftruncate(fd, size) + finally: + os.close(fd) self._create_partition_table(image) + end_sector = self._shrink_partitions(image) # Check if the available space is enough to host the image @@ -387,6 +422,15 @@ class BundleVolume(): self._create_filesystems(image) + # Truncate image to the new size. I counldn't find a better way to do + # this. It seems that python's high level functions work in a different + # way. + fd = os.open(image, os.O_RDWR) + try: + os.ftruncate(fd, size) + finally: + os.close(fd) + return image # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :