- # Leave 2048 blocks at the end
- new_end = last.end + 2048
-
- if last.mpoint:
- stat = os.statvfs(last.mpoint)
- occupied_blocks = stat.f_blocks - stat.f_bavail
- new_size = (occupied_blocks * stat.f_frsize) // src_dev.sectorSize
-
- # Add 10% just to be on the safe side
- part_end = last.start + (new_size * 11) // 10
- # Alighn to 2048
- part_end = ((part_end + 2047) // 2048) * 2048
- last = last._replace(end=part_end)
- partitions[-1] = last
-
- # Leave 2048 blocks at the end.
- new_end = new_size + 2048
-
- img_disk.setPartitionGeometry(
- img_disk.getPartitionBySector(last.start),
- parted.Constraint(device=img_dev), start=last.start, end=last.end)
- img_disk.commit()
-
- if last.type == parted.PARTITION_LOGICAL:
- # Fix the extended partition
- ext = disk.getExtendedPartition()
-
- img_disk.setPartitionGeometry(ext,
- parted.Constraint(device=img_dev), ext.geometry.start,
- end=last.end)
- img_disk.commit()
-
- # Check if we have the available space on the filesystem hosting /mnt
- # for the image.
- out.output("Examining available space in /mnt ... ", False)
- stat = os.statvfs('/mnt')
- image_size = (new_end + 1) * src_dev.sectorSize
- available = stat.f_bavail * stat.f_frsize
-
- if available <= image_size:
- raise FatalError('Not enough space in /mnt to host the image')
-
- out.success("sufficient")
-
- return img
+ 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):
+
+ filesystem = {}
+ for p in self.disk.partitions:
+ filesystem[p.number] = self._get_mount_options(p.path)
+
+ 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.
+ for p in unmounted:
+ self.out.output('Cloning partition %d ... ' % p.num, False)
+ dd('if=%s' % self.disk.device.path, 'of=%s' % image,
+ 'count=%d' % (p.end - p.start + 1), 'conv=notrunc',
+ 'seek=%d' % p.start, 'skip=%d' % p.start)
+ self.out.success("done")
+
+ loop = str(losetup('-f', '--show', image)).strip()
+ mapped = {}
+ try:
+ for p in mounted:
+ 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 = 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], filesystem[i].mpoint)
+ for i in mapped.keys()])
+ excluded = self._to_exclude()
+
+ rsync = Rsync(self.out)
+
+ # Excluded paths need to be relative to the source
+ for excl in map(lambda p: p[1:], excluded + [image]):
+ rsync.exclude(excl)
+
+ rsync.archive().hard_links().xattrs().sparse().acls()
+ rsync.run('/', target, 'host', 'temporary image')
+
+ # Create missing mountpoints. Since they are mountpoints, we
+ # cannot determine the ownership and the mode of the real
+ # directory. Make them inherit those properties from their
+ # parent dir
+ for excl in excluded:
+ dirname = os.path.dirname(excl)
+ stat = os.stat(dirname)
+ os.mkdir(target + excl)
+ os.chmod(target + excl, stat.st_mode)
+ os.chown(target + excl, stat.st_uid, stat.st_gid)
+
+ # /tmp and /var/tmp are special cases. We exclude then even if
+ # they aren't mountpoints. Restore their permissions.
+ for excl in ('/tmp', '/var/tmp'):
+ if self._is_mpoint(excl):
+ os.chmod(target + excl, 041777)
+ os.chown(target + excl, 0, 0)
+ else:
+ stat = os.stat(excl)
+ os.chmod(target + excl, stat.st_mode)
+ os.chown(target + excl, stat.st_uid, stat.st_gid)
+
+ # We need to replace the old UUID referencies with the new
+ # ones in grub configuration files and /etc/fstab for file
+ # systems that have been recreated.
+ self._replace_uuids(target, new_uuid)
+
+ finally:
+ self._umount_all(target)
+ os.rmdir(target)
+ finally:
+ for dev in mapped.values():
+ self._unmap_partition(dev)
+ losetup('-d', loop)
+
+ def create_image(self, image):
+ """Given an image filename, this method will create an image out of the
+ running system.
+ """
+
+ size = self.disk.device.length * self.disk.device.sectorSize
+
+ # Create sparse file to host the 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, partitions = self._shrink_partitions(image)
+
+ if self.disk.type == 'gpt':
+ old_size = size
+ size = (end_sector + 1) * self.disk.device.sectorSize
+ ptable = GPTPartitionTable(image)
+ size = ptable.shrink(size, old_size)
+ else:
+ # Alighn to 2048
+ end_sector = ((end_sector + 2047) // 2048) * 2048
+ size = (end_sector + 1) * self.disk.device.sectorSize
+
+ # Truncate image to the new size.
+ fd = os.open(image, os.O_RDWR)
+ try:
+ os.ftruncate(fd, size)
+ finally:
+ os.close(fd)
+
+ # Check if the available space is enough to host the image
+ dirname = os.path.dirname(image)
+ self.out.output("Examining available space ...", False)
+ if free_space(dirname) <= size:
+ raise FatalError('Not enough space under %s to host the image' %
+ dirname)
+ self.out.success("sufficient")
+
+ self._create_filesystems(image, partitions)
+
+ return image