示例#1
0
    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:
            # Align 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.info("Examining available space ...", False)
        if free_space(dirname) <= size:
            raise FatalError("Not enough space under %s to host the temporary "
                             "image" % dirname)
        self.out.success("sufficient")

        self._create_filesystems(image, partitions)

        return image
示例#2
0
    def shrink(self, silent=False):
        """Shrink the image.

        This is accomplished by shrinking the last file system of the
        image and then updating the partition table. The shrinked device is
        returned.

        ATTENTION: make sure umount is called before shrink
        """
        def get_fstype(partition):
            """Get file system type"""
            device = "%s%d" % (self.guestfs_device, partition['part_num'])
            return self.g.vfs_type(device)

        def is_extended(partition):
            """Returns True if the partition is an extended partition"""
            if self.meta['PARTITION_TABLE'] == 'msdos':
                mbr_id = self.g.part_get_mbr_id(self.guestfs_device,
                                                partition['part_num'])
                return mbr_id in (0x5, 0xf)
            return False

        def part_del(partnum):
            """Delete a partition"""
            self.g.part_del(self.guestfs_device, partnum)

        MB = 2**20

        if self.is_unsupported():
            if not silent:
                self.out.warn("Shrinking is disabled for unsupported images")
            return None

        sector_size = self.g.blockdev_getss(self.guestfs_device)

        last_part = None
        fstype = None
        while True:
            last_part = self._last_partition()
            fstype = get_fstype(last_part)

            if fstype == 'swap':
                self.meta['SWAP'] = "%d:%s" % \
                    (last_part['part_num'],
                     (last_part['part_size'] + MB - 1) // MB)
                part_del(last_part['part_num'])
                continue
            elif is_extended(last_part):
                part_del(last_part['part_num'])
                continue

            # Most disk manipulation programs leave 2048 sectors after the last
            # partition
            new_size = last_part['part_end'] + 1 + 2048 * sector_size
            self.size = min(self.size, new_size)
            break

        if not fstype:
            if not silent:
                self.out.warn("Unable to shrink partition: %s. Reason: "
                              "Could not detect file system." %
                              last_part['part_num'])
            return None

        if not re.match("ext[234]", fstype):
            if not silent:
                self.out.warn("Unable to shrink partition: %s. Reason: "
                              "Don't know how to shrink %s file systems." %
                              (last_part['part_num'], fstype))
            return None

        part_dev = "%s%d" % (self.guestfs_device, last_part['part_num'])
        out = self.g.tune2fs_l(part_dev)
        block_size = int(filter(lambda x: x[0] == 'Block size', out)[0][1])
        old_block_cnt = int(filter(lambda x: x[0] == 'Block count', out)[0][1])

        try:
            if self.check_guestfs_version(1, 15, 17) >= 0:
                self.g.e2fsck(part_dev, forceall=1)
            else:
                self.g.e2fsck_f(part_dev)
        except RuntimeError as e:
            # There is a bug in some versions of libguestfs and a RuntimeError
            # is thrown although the command has successfully corrected the
            # found file system errors.
            if str(e).find('***** FILE SYSTEM WAS MODIFIED *****') == -1:
                raise

        self.g.resize2fs_M(part_dev)

        out = self.g.tune2fs_l(part_dev)
        assert block_size == \
            int(filter(lambda x: x[0] == 'Block size', out)[0][1])
        block_cnt = int(filter(lambda x: x[0] == 'Block count', out)[0][1])

        # Add some extra space for the image to be able to run.
        block_cnt = min(old_block_cnt, block_cnt + 8388608 / block_size)

        start = last_part['part_start'] / sector_size
        end = start + (block_size * block_cnt) / sector_size - 1

        self._resize_partition(end)

        # Enlarge the underlying file system to consume the available space
        self.g.resize2fs(part_dev)

        new_size = (end + 1) * sector_size

        assert new_size <= self.size

        if self.meta['PARTITION_TABLE'] == 'gpt':
            with self.raw_device(readonly=False) as raw:
                ptable = GPTPartitionTable(raw)
                self.size = ptable.shrink(new_size, self.size)
        else:
            self.size = min(new_size + 2048 * sector_size, self.size)

        if not silent:
            self.out.success("Image size is %dMB" %
                             ((self.size + MB - 1) // MB))

        return part_dev
示例#3
0
    def shrink(self, silent=False):
        """Shrink the image.

        This is accomplished by shrinking the last file system of the
        image and then updating the partition table. The shrinked device is
        returned.

        ATTENTION: make sure umount is called before shrink
        """
        def get_fstype(partition):
            """Get file system type"""
            device = "%s%d" % (self.guestfs_device, partition['part_num'])
            return self.g.vfs_type(device)

        def is_logical(partition):
            """Returns True if the partition is a logical partition"""
            return self.meta['PARTITION_TABLE'] == 'msdos' and \
                partition['part_num'] > 4

        def is_extended(partition):
            """Returns True if the partition is an extended partition"""
            if self.meta['PARTITION_TABLE'] == 'msdos':
                mbr_id = self.g.part_get_mbr_id(self.guestfs_device,
                                                partition['part_num'])
                return mbr_id in (0x5, 0xf)
            return False

        def part_add(ptype, start, stop):
            """Add partition"""
            self.g.part_add(self.guestfs_device, ptype, start, stop)

        def part_del(partnum):
            """Delete a partition"""
            self.g.part_del(self.guestfs_device, partnum)

        def part_get_id(partnum):
            """Returns the MBR id of the partition"""
            return self.g.part_get_mbr_id(self.guestfs_device, partnum)

        def part_set_id(partnum, id):
            """Sets the MBR id of the partition"""
            self.g.part_set_mbr_id(self.guestfs_device, partnum, id)

        def part_get_bootable(partnum):
            """Returns the bootable flag of the partition"""
            return self.g.part_get_bootable(self.guestfs_device, partnum)

        def part_set_bootable(partnum, bootable):
            """Sets the bootable flag for a partition"""
            self.g.part_set_bootable(self.guestfs_device, partnum, bootable)

        MB = 2 ** 20

        if self.is_unsupported():
            if not silent:
                self.out.warn("Shrinking is disabled for unsupported images")
            return None

        sector_size = self.g.blockdev_getss(self.guestfs_device)

        last_part = None
        fstype = None
        while True:
            last_part = self._last_partition()
            fstype = get_fstype(last_part)

            if fstype == 'swap':
                self.meta['SWAP'] = "%d:%s" % \
                    (last_part['part_num'],
                     (last_part['part_size'] + MB - 1) // MB)
                part_del(last_part['part_num'])
                continue
            elif is_extended(last_part):
                part_del(last_part['part_num'])
                continue

            # Most disk manipulation programs leave 2048 sectors after the last
            # partition
            new_size = last_part['part_end'] + 1 + 2048 * sector_size
            self.size = min(self.size, new_size)
            break

        if not (fstype or silent):
            self.out.warn(
                "Unable to shrink partition: %s. Reason: "
                "Could not detect file system." % last_part['part_num'])
            return None

        if not (re.match("ext[234]", fstype) or silent):
            self.out.warn(
                "Unable to shrink partition: %s. Reason: "
                "Don't know how to shrink %s file systems." %
                (last_part['part_num'], fstype))
            return None

        part_dev = "%s%d" % (self.guestfs_device, last_part['part_num'])

        try:
            if self.check_guestfs_version(1, 15, 17) >= 0:
                self.g.e2fsck(part_dev, forceall=1)
            else:
                self.g.e2fsck_f(part_dev)
        except RuntimeError as e:
            # There is a bug in some versions of libguestfs and a RuntimeError
            # is thrown although the command has successfully corrected the
            # found file system errors.
            if e.message.find('***** FILE SYSTEM WAS MODIFIED *****') == -1:
                raise

        self.g.resize2fs_M(part_dev)

        out = self.g.tune2fs_l(part_dev)
        block_size = int(filter(lambda x: x[0] == 'Block size', out)[0][1])
        block_cnt = int(filter(lambda x: x[0] == 'Block count', out)[0][1])

        start = last_part['part_start'] / sector_size
        end = start + (block_size * block_cnt) / sector_size - 1

        if is_logical(last_part):
            partitions = self.g.part_list(self.guestfs_device)

            logical = []  # logical partitions
            for partition in partitions:
                if partition['part_num'] < 4:
                    continue
                logical.append({
                    'num': partition['part_num'],
                    'start': partition['part_start'] / sector_size,
                    'end': partition['part_end'] / sector_size,
                    'id': part_get_id(partition['part_num']),
                    'bootable': part_get_bootable(partition['part_num'])
                })

            logical[-1]['end'] = end  # new end after resize

            # Recreate the extended partition
            extended = filter(is_extended, partitions)[0]
            part_del(extended['part_num'])
            part_add('e', extended['part_start'] / sector_size, end)

            # Create all the logical partitions back
            for l in logical:
                part_add('l', l['start'], l['end'])
                part_set_id(l['num'], l['id'])
                part_set_bootable(l['num'], l['bootable'])
        else:
            # Recreate the last partition
            if self.meta['PARTITION_TABLE'] == 'msdos':
                last_part['id'] = part_get_id(last_part['part_num'])

            last_part['bootable'] = part_get_bootable(last_part['part_num'])
            part_del(last_part['part_num'])
            part_add('p', start, end)
            part_set_bootable(last_part['part_num'], last_part['bootable'])

            if self.meta['PARTITION_TABLE'] == 'msdos':
                part_set_id(last_part['part_num'], last_part['id'])

        new_size = (end + 1) * sector_size

        assert new_size <= self.size

        if self.meta['PARTITION_TABLE'] == 'gpt':
            with self.raw_device(readonly=False) as raw:
                ptable = GPTPartitionTable(raw)
                self.size = ptable.shrink(new_size, self.size)
        else:
            self.size = min(new_size + 2048 * sector_size, self.size)

        if not silent:
            self.out.success("Image size is %dMB" %
                             ((self.size + MB - 1) // MB))

        return part_dev
示例#4
0
    def shrink(self, silent=False):
        """Shrink the image.

        This is accomplished by shrinking the last file system of the
        image and then updating the partition table. The shrinked device is
        returned.

        ATTENTION: make sure umount is called before shrink
        """
        def get_fstype(partition):
            """Get file system type"""
            device = "%s%d" % (self.guestfs_device, partition['part_num'])
            return self.g.vfs_type(device)

        def is_extended(partition):
            """Returns True if the partition is an extended partition"""
            if self.meta['PARTITION_TABLE'] == 'msdos':
                mbr_id = self.g.part_get_mbr_id(self.guestfs_device,
                                                partition['part_num'])
                return mbr_id in (0x5, 0xf)
            return False

        def part_del(partnum):
            """Delete a partition"""
            self.g.part_del(self.guestfs_device, partnum)

        MB = 2 ** 20

        if self.is_unsupported():
            if not silent:
                self.out.warn("Shrinking is disabled for unsupported images")
            return None

        sector_size = self.g.blockdev_getss(self.guestfs_device)

        last_part = None
        fstype = None
        while True:
            last_part = self._last_partition()
            fstype = get_fstype(last_part)

            if fstype == 'swap':
                self.meta['SWAP'] = "%d:%s" % \
                    (last_part['part_num'],
                     (last_part['part_size'] + MB - 1) // MB)
                part_del(last_part['part_num'])
                continue
            elif is_extended(last_part):
                part_del(last_part['part_num'])
                continue

            # Most disk manipulation programs leave 2048 sectors after the last
            # partition
            new_size = last_part['part_end'] + 1 + 2048 * sector_size
            self.size = min(self.size, new_size)
            break

        if not fstype:
            if not silent:
                self.out.warn(
                    "Unable to shrink partition: %s. Reason: "
                    "Could not detect file system." % last_part['part_num'])
            return None

        if not re.match("ext[234]", fstype):
            if not silent:
                self.out.warn(
                    "Unable to shrink partition: %s. Reason: "
                    "Don't know how to shrink %s file systems." %
                    (last_part['part_num'], fstype))
            return None

        part_dev = "%s%d" % (self.guestfs_device, last_part['part_num'])
        out = self.g.tune2fs_l(part_dev)
        block_size = int(filter(lambda x: x[0] == 'Block size', out)[0][1])
        old_block_cnt = int(filter(lambda x: x[0] == 'Block count', out)[0][1])

        try:
            if self.check_guestfs_version(1, 15, 17) >= 0:
                self.g.e2fsck(part_dev, forceall=1)
            else:
                self.g.e2fsck_f(part_dev)
        except RuntimeError as e:
            # There is a bug in some versions of libguestfs and a RuntimeError
            # is thrown although the command has successfully corrected the
            # found file system errors.
            if str(e).find('***** FILE SYSTEM WAS MODIFIED *****') == -1:
                raise

        self.g.resize2fs_M(part_dev)

        out = self.g.tune2fs_l(part_dev)
        assert block_size == \
            int(filter(lambda x: x[0] == 'Block size', out)[0][1])
        block_cnt = int(filter(lambda x: x[0] == 'Block count', out)[0][1])

        # Add some extra space for the image to be able to run.
        block_cnt = min(old_block_cnt, block_cnt + 8388608/block_size)

        start = last_part['part_start'] / sector_size
        end = start + (block_size * block_cnt) / sector_size - 1

        self._resize_partition(end)

        # Enlarge the underlying file system to consume the available space
        self.g.resize2fs(part_dev)

        new_size = (end + 1) * sector_size

        assert new_size <= self.size

        if self.meta['PARTITION_TABLE'] == 'gpt':
            with self.raw_device(readonly=False) as raw:
                ptable = GPTPartitionTable(raw)
                self.size = ptable.shrink(new_size, self.size)
        else:
            self.size = min(new_size + 2048 * sector_size, self.size)

        if not silent:
            self.out.success("Image size is %dMB" %
                             ((self.size + MB - 1) // MB))

        return part_dev