From de76321da7a507275ff6b2d8d0d5f68a52980ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Sat, 15 Oct 2016 00:04:36 +0200 Subject: [PATCH 01/11] Added resize_raw_disk method in DiskFormatBase Allow to increase the disk geometry of a disk image file in order to create free space on this disk --- kiwi/exceptions.py | 15 ++++++++++++++ kiwi/storage/subformat/base.py | 22 +++++++++++++++++++- test/unit/storage_subformat_base_test.py | 26 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/kiwi/exceptions.py b/kiwi/exceptions.py index be55c7cc..921a2bdc 100644 --- a/kiwi/exceptions.py +++ b/kiwi/exceptions.py @@ -508,6 +508,21 @@ class KiwiRequestError(KiwiError): """ +class KiwiResizeRawDiskError(KiwiError): + """ + Exception raised if an attempt was made to resize the image disk + to a smaller size than the current one. Simply shrinking a disk image + file is not possible without data corruption because the partitions + were setup to use the entire disk geometry as it fits into the file. + A successful shrinking operation would require the filesystems and + the partition table to be reduced which is not done by the provided + simple storage resize method. In addition without the user overwriting + the disk size in the XML setup, kiwi will calculate the minimum + required size in order to store the data. Thus in almost all cases + it will not be possible to store the data in a smaller disk. + """ + + class KiwiResultError(KiwiError): """ Exception raised if the image build result pickle information diff --git a/kiwi/storage/subformat/base.py b/kiwi/storage/subformat/base.py index 4dbc6bca..d76e5544 100644 --- a/kiwi/storage/subformat/base.py +++ b/kiwi/storage/subformat/base.py @@ -20,9 +20,11 @@ import platform from collections import OrderedDict from ...exceptions import ( - KiwiFormatSetupError + KiwiFormatSetupError, + KiwiResizeRawDiskError ) +from ...command import Command from ...defaults import Defaults from ...path import Path from ...logger import log @@ -80,6 +82,24 @@ class DiskFormatBase(object): """ pass + def has_raw_disk(self): + return os.path.exists(self.diskname) + + def resize_raw_disk(self, size_bytes): + """ + Resize raw disk image to specified size + """ + current_byte_size = os.path.getsize(self.diskname) + size_bytes = int(size_bytes) + if size_bytes <= current_byte_size: + raise KiwiResizeRawDiskError( + 'Can not shrink %s disk to %d bytes without data corruption' % + (self.diskname, size_bytes) + ) + Command.run( + ['qemu-img', 'resize', self.diskname, format(size_bytes)] + ) + def create_image_format(self): """ Create disk format diff --git a/test/unit/storage_subformat_base_test.py b/test/unit/storage_subformat_base_test.py index 8e28e39d..e3e252ad 100644 --- a/test/unit/storage_subformat_base_test.py +++ b/test/unit/storage_subformat_base_test.py @@ -63,6 +63,32 @@ class TestDiskFormatBase(object): use_for_bundle=True ) + @raises(KiwiResizeRawDiskError) + @patch('os.path.getsize') + def test_resize_raw_disk_raises_on_shrink_disk(self, mock_getsize): + mock_getsize.return_value = 42 + self.disk_format.resize_raw_disk(10) + + @patch('os.path.getsize') + @patch('kiwi.storage.subformat.base.Command.run') + def test_resize_raw_disk(self, mock_command, mock_getsize): + mock_getsize.return_value = 42 + self.disk_format.resize_raw_disk(1024) + mock_command.assert_called_once_with( + [ + 'qemu-img', 'resize', + 'target_dir/some-disk-image.x86_64-1.2.3.raw', '1024' + ] + ) + + @patch('os.path.exists') + def test_has_raw_disk(self, mock_exists): + mock_exists.return_value = True + assert self.disk_format.has_raw_disk() == True + mock_exists.assert_called_once_with( + 'target_dir/some-disk-image.x86_64-1.2.3.raw' + ) + @patch('kiwi.storage.subformat.base.Path.wipe') @patch('os.path.exists') def test_destructor(self, mock_exists, mock_wipe): From 73f993939c190441e4b320d98ca82e9bd44a7b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Mon, 17 Oct 2016 11:42:33 +0200 Subject: [PATCH 02/11] Add raw format to subformat factory --- kiwi/storage/subformat/__init__.py | 5 +++++ test/unit/storage_subformat_test.py | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/kiwi/storage/subformat/__init__.py b/kiwi/storage/subformat/__init__.py index a446b3ac..2b144ab9 100644 --- a/kiwi/storage/subformat/__init__.py +++ b/kiwi/storage/subformat/__init__.py @@ -22,6 +22,7 @@ from .vhdfixed import DiskFormatVhdFixed from .vmdk import DiskFormatVmdk from .gce import DiskFormatGce from .vdi import DiskFormatVdi +from .base import DiskFormatBase from ...exceptions import ( KiwiDiskFormatSetupError @@ -93,6 +94,10 @@ class DiskFormat(object): return DiskFormatVmdk( xml_state, root_dir, target_dir, custom_args ) + elif name == 'raw': + return DiskFormatBase( + xml_state, root_dir, target_dir + ) else: raise KiwiDiskFormatSetupError( 'Support for %s disk format not implemented' % name diff --git a/test/unit/storage_subformat_test.py b/test/unit/storage_subformat_test.py index 7106cd3c..befa29b4 100644 --- a/test/unit/storage_subformat_test.py +++ b/test/unit/storage_subformat_test.py @@ -78,3 +78,11 @@ class TestDiskFormat(object): xml_state, 'root_dir', 'target_dir', {'adapter_type=controller': None, 'subformat=disk-mode': None} ) + + @patch('kiwi.storage.subformat.DiskFormatBase') + def test_disk_format_base(self, mock_base): + xml_state = mock.Mock() + DiskFormat('raw', xml_state, 'root_dir', 'target_dir') + mock_base.assert_called_once_with( + xml_state, 'root_dir', 'target_dir', + ) From 0825b250307bb8fe465a3950653229bd6f53de3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Mon, 17 Oct 2016 19:18:09 +0200 Subject: [PATCH 03/11] Added manual page for image resize command --- doc/source/manual/image_resize.rst | 34 ++++++++++++++++++++++++++++++ doc/source/manual/kiwi.rst | 1 + 2 files changed, 35 insertions(+) create mode 100644 doc/source/manual/image_resize.rst diff --git a/doc/source/manual/image_resize.rst b/doc/source/manual/image_resize.rst new file mode 100644 index 00000000..39d29f02 --- /dev/null +++ b/doc/source/manual/image_resize.rst @@ -0,0 +1,34 @@ +kiwi image resize +================= + +SYNOPSIS +-------- + +.. program-output:: bash -c "kiwi-ng image resize | awk 'BEGIN{ found=1} /global options:/{found=0} {if (found) print }'" + +DESCRIPTION +----------- + +For disk based images, allow to resize the image to a new disk geometry. +The additional space is free and not in use by the image. In order to +make use of the additional free space a repartition process is required +like it is provided by kiwi's oem boot code. Therefore the resize operation +is useful for oem image builds most of the time. + +OPTIONS +------- + +--root= + + The path to the root directory, if not specified kiwi + searches the root directory in build/image-root below + the specified target directory + +--size= + + New size of the image. The value is either a size in bytes + or can be specified with m=MB or g=GB. Example: 20g + +--target-dir= + + Directory containing the kiwi build results diff --git a/doc/source/manual/kiwi.rst b/doc/source/manual/kiwi.rst index 3632dbec..3e6d98cb 100644 --- a/doc/source/manual/kiwi.rst +++ b/doc/source/manual/kiwi.rst @@ -90,6 +90,7 @@ SERVICES and COMMANDS .. toctree:: + image_resize result_list result_bundle system_prepare From c9c71f4dc581b8b470cf26d6466ebba5fe7d9fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Mon, 17 Oct 2016 19:26:15 +0200 Subject: [PATCH 04/11] Added kiwi image resize command The image resize command allows to resize a disk image and its optional disk format to a new disk geometry --- kiwi/cli.py | 12 ++- kiwi/exceptions.py | 8 ++ kiwi/tasks/image_resize.py | 135 +++++++++++++++++++++++++++ test/unit/cli_test.py | 11 +++ test/unit/tasks_image_resize_test.py | 97 +++++++++++++++++++ 5 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 kiwi/tasks/image_resize.py create mode 100644 test/unit/tasks_image_resize_test.py diff --git a/kiwi/cli.py b/kiwi/cli.py index b4296752..123bcafb 100644 --- a/kiwi/cli.py +++ b/kiwi/cli.py @@ -17,6 +17,12 @@ # """ usage: kiwi -h | --help + kiwi [--profile=...] + [--type=] + [--logfile=] + [--debug] + [--color-output] + image [...] kiwi [--debug] [--color-output] result [...] @@ -43,7 +49,7 @@ global options: help show manual page -global options for service: system +global options for services: image, system --logfile= create a log file containing all log information including debug information even if this is was not requested by the @@ -119,7 +125,9 @@ class Cli(object): :return: service name :rtype: string """ - if self.all_args['system']: + if self.all_args['image']: + return 'image' + elif self.all_args['system']: return 'system' elif self.all_args['result']: return 'result' diff --git a/kiwi/exceptions.py b/kiwi/exceptions.py index 921a2bdc..c40f029c 100644 --- a/kiwi/exceptions.py +++ b/kiwi/exceptions.py @@ -316,6 +316,14 @@ class KiwiHelpNoCommandGiven(KiwiError): """ +class KiwiImageResizeError(KiwiError): + """ + Exception raised if the request to resize a disk image failed. + Reasons could be a missing raw disk reference or a wrong size + specification. + """ + + class KiwiImportDescriptionError(KiwiError): """ Exception raised if the XML description data and scripts could diff --git a/kiwi/tasks/image_resize.py b/kiwi/tasks/image_resize.py new file mode 100644 index 00000000..f6a6ded1 --- /dev/null +++ b/kiwi/tasks/image_resize.py @@ -0,0 +1,135 @@ +# Copyright (c) 2015 SUSE Linux GmbH. All rights reserved. +# +# This file is part of kiwi. +# +# kiwi is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# kiwi 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 kiwi. If not, see +# +""" +usage: kiwi image resize -h | --help + kiwi image resize --target-dir= --size= + [--root=] + kiwi image resize help + +commands: + resize + for disk based images, allow to resize the image to a new + disk geometry. The additional space is free and not in use + by the image. In order to make use of the additional free + space a repartition process is required like it is provided + by kiwi's oem boot code. Therefore the resize operation is + useful for oem image builds most of the time + +options: + --root= + the path to the root directory, if not specified kiwi + searches the root directory in build/image-root below + the specified target directory + + --size= + new size of the image. The value is either a size in bytes + or can be specified with m=MB or g=GB. Example: 20g + + --target-dir= + the target directory to expect image build results +""" +import re +import math + +# project +from .base import CliTask +from ..help import Help +from ..logger import log +from ..storage.subformat import DiskFormat + +from ..exceptions import ( + KiwiImageResizeError +) + + +class ImageResizeTask(CliTask): + """ + Implements resizing of disk images and their disk format + + Attributes + + * :attr:`manual` + Instance of Help + """ + def process(self): + """ + reformats raw disk image and its format to a new disk + geometry using the qemu tool chain + """ + self.manual = Help() + if self._help(): + return + + if self.command_args['--root']: + image_root = self.command_args['--root'] + else: + image_root = ''.join( + [self.command_args['--target-dir'], '/build/image-root'] + ) + + self.load_xml_description( + image_root + ) + + disk_format = self.xml_state.build_type.get_format() + + image_format = DiskFormat( + disk_format or 'raw', self.xml_state, image_root, + self.command_args['--target-dir'] + ) + if not image_format.has_raw_disk(): + raise KiwiImageResizeError( + 'no raw disk image %s found in build results' % + image_format.diskname + ) + + new_disk_size = self._to_bytes(self.command_args['--size']) + + log.info( + 'Resizing raw disk to %d bytes', new_disk_size + ) + image_format.resize_raw_disk(new_disk_size) + if disk_format: + log.info( + 'Creating %s disk format from resized raw disk', disk_format + ) + image_format.create_image_format() + + def _to_bytes(self, size_value): + size_format = '^(\d+)([gGmM]{0,1})$' + size = re.search(size_format, size_value) + if not size: + raise KiwiImageResizeError( + 'unsupported size format %s, must match %s' % + (size_value, size_format) + ) + size_base = int(size.group(1)) + size_unit = size.group(2).lower() + if size_unit == 'g': + return size_base * math.pow(1024, 3) + elif size_unit == 'm': + return size_base * math.pow(1024, 2) + else: + return size_base + + def _help(self): + if self.command_args['help']: + self.manual.show('kiwi::image::resize') + else: + return False + return self.manual diff --git a/test/unit/cli_test.py b/test/unit/cli_test.py index f8366983..1705740f 100644 --- a/test/unit/cli_test.py +++ b/test/unit/cli_test.py @@ -14,6 +14,7 @@ class TestCli(object): 'help': False, '--compat': False, '--type': None, + 'image': False, 'system': True, '-h': False, '--logfile': None, @@ -78,6 +79,16 @@ class TestCli(object): cli = Cli() assert cli.get_servicename() == 'compat' + def test_get_servicename_image(self): + sys.argv = [ + sys.argv[0], + 'image', 'resize', + '--target-dir', 'directory', + '--size', '20g' + ] + cli = Cli() + assert cli.get_servicename() == 'image' + def test_get_servicename_result(self): sys.argv = [ sys.argv[0], diff --git a/test/unit/tasks_image_resize_test.py b/test/unit/tasks_image_resize_test.py new file mode 100644 index 00000000..4b764741 --- /dev/null +++ b/test/unit/tasks_image_resize_test.py @@ -0,0 +1,97 @@ +import sys +import mock + +import kiwi + +from .test_helper import raises + +from kiwi.exceptions import KiwiImageResizeError, KiwiConfigFileNotFound + +from kiwi.tasks.image_resize import ImageResizeTask + + +class TestImageResizeTask(object): + def setup(self): + sys.argv = [ + sys.argv[0], '--type', 'vmx', 'image', 'resize', + '--target-dir', 'target_dir', '--size', '20g', + '--root', '../data/root-dir' + ] + kiwi.tasks.image_resize.Help = mock.Mock( + return_value=mock.Mock() + ) + + self.image_format = mock.Mock() + self.image_format.has_raw_disk = mock.Mock() + self.image_format.diskname = 'some-disk.raw' + kiwi.tasks.image_resize.DiskFormat = mock.Mock( + return_value=self.image_format + ) + + self.task = ImageResizeTask() + + def _init_command_args(self): + self.task.command_args = {} + self.task.command_args['help'] = False + self.task.command_args['resize'] = False + self.task.command_args['--target-dir'] = 'target_dir' + self.task.command_args['--size'] = '42g' + self.task.command_args['--root'] = '../data/root-dir' + + @raises(KiwiConfigFileNotFound) + def test_process_no_root_directory_specified(self): + self.task.command_args['--root'] = None + self.task.process() + + @raises(KiwiImageResizeError) + def test_process_no_raw_disk_found(self): + self._init_command_args() + self.image_format.has_raw_disk.return_value = False + self.task.command_args['resize'] = True + self.task.process() + + @raises(KiwiImageResizeError) + def test_process_unsupported_size_format(self): + self._init_command_args() + self.task.command_args['--size'] = '20x' + self.image_format.has_raw_disk.return_value = True + self.task.command_args['resize'] = True + self.task.process() + + def test_process_image_resize_gb(self): + self._init_command_args() + self.task.command_args['resize'] = True + self.task.process() + self.image_format.resize_raw_disk.assert_called_once_with( + 42 * 1024 * 1024 * 1024 + ) + self.image_format.create_image_format.assert_called_once_with() + + def test_process_image_resize_mb(self): + self._init_command_args() + self.task.command_args['resize'] = True + self.task.command_args['--size'] = '42m' + self.task.process() + self.image_format.resize_raw_disk.assert_called_once_with( + 42 * 1024 * 1024 + ) + self.image_format.create_image_format.assert_called_once_with() + + def test_process_image_resize_bytes(self): + self._init_command_args() + self.task.command_args['resize'] = True + self.task.command_args['--size'] = '42' + self.task.process() + self.image_format.resize_raw_disk.assert_called_once_with( + 42 + ) + self.image_format.create_image_format.assert_called_once_with() + + def test_process_image_resize_help(self): + self._init_command_args() + self.task.command_args['help'] = True + self.task.command_args['resize'] = True + self.task.process() + self.task.manual.show.assert_called_once_with( + 'kiwi::image::resize' + ) From 136b4efa442e044f32610879de707d41b2a41295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Tue, 18 Oct 2016 17:01:58 +0200 Subject: [PATCH 05/11] Update resize_raw_disk method Do not resize the disk if an attempt to resize to the same size was made. Do not fail in this situation but indicate via a bool return value if an action has happened(True) or not(False) --- kiwi/storage/subformat/base.py | 19 +++++++++++++++++-- test/unit/storage_subformat_base_test.py | 7 ++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/kiwi/storage/subformat/base.py b/kiwi/storage/subformat/base.py index d76e5544..2dd0ccef 100644 --- a/kiwi/storage/subformat/base.py +++ b/kiwi/storage/subformat/base.py @@ -83,22 +83,37 @@ class DiskFormatBase(object): pass def has_raw_disk(self): + """ + Check if the base raw disk image exists + + :rtype: bool + """ return os.path.exists(self.diskname) def resize_raw_disk(self, size_bytes): """ - Resize raw disk image to specified size + Resize raw disk image to specified size. If the request + would actually shrink the disk an exception is raised. + If the disk got changed the method returns True, if + the new size is the same as the current size nothing + gets resized and the method returns False + + :param int size: new size in bytes + :rtype: bool """ current_byte_size = os.path.getsize(self.diskname) size_bytes = int(size_bytes) - if size_bytes <= current_byte_size: + if size_bytes < current_byte_size: raise KiwiResizeRawDiskError( 'Can not shrink %s disk to %d bytes without data corruption' % (self.diskname, size_bytes) ) + elif size_bytes == current_byte_size: + return False Command.run( ['qemu-img', 'resize', self.diskname, format(size_bytes)] ) + return True def create_image_format(self): """ diff --git a/test/unit/storage_subformat_base_test.py b/test/unit/storage_subformat_base_test.py index e3e252ad..ad53a3ea 100644 --- a/test/unit/storage_subformat_base_test.py +++ b/test/unit/storage_subformat_base_test.py @@ -73,7 +73,7 @@ class TestDiskFormatBase(object): @patch('kiwi.storage.subformat.base.Command.run') def test_resize_raw_disk(self, mock_command, mock_getsize): mock_getsize.return_value = 42 - self.disk_format.resize_raw_disk(1024) + assert self.disk_format.resize_raw_disk(1024) == True mock_command.assert_called_once_with( [ 'qemu-img', 'resize', @@ -81,6 +81,11 @@ class TestDiskFormatBase(object): ] ) + @patch('os.path.getsize') + def test_resize_raw_disk_same_size(self, mock_getsize): + mock_getsize.return_value = 42 + assert self.disk_format.resize_raw_disk(42) == False + @patch('os.path.exists') def test_has_raw_disk(self, mock_exists): mock_exists.return_value = True From 714d01e4d58b563a0ff18b0619768e1d0ac325a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Tue, 18 Oct 2016 17:10:03 +0200 Subject: [PATCH 06/11] Use format method instead of printf like style --- kiwi/storage/subformat/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kiwi/storage/subformat/base.py b/kiwi/storage/subformat/base.py index 2dd0ccef..e2602496 100644 --- a/kiwi/storage/subformat/base.py +++ b/kiwi/storage/subformat/base.py @@ -105,8 +105,9 @@ class DiskFormatBase(object): size_bytes = int(size_bytes) if size_bytes < current_byte_size: raise KiwiResizeRawDiskError( - 'Can not shrink %s disk to %d bytes without data corruption' % - (self.diskname, size_bytes) + 'shrinking {0} disk to {1} bytes corrupts the image'.format( + self.diskname, size_bytes + ) ) elif size_bytes == current_byte_size: return False From d85060d73ca82d971282620448b9ff4189790c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Tue, 18 Oct 2016 17:20:00 +0200 Subject: [PATCH 07/11] Explicitly bool check for the service name option From docopt we expect a True/False value for the selected service name positional parameter. Thus the code should also make it clear what we expect --- kiwi/cli.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kiwi/cli.py b/kiwi/cli.py index 123bcafb..7c7fb203 100644 --- a/kiwi/cli.py +++ b/kiwi/cli.py @@ -125,13 +125,13 @@ class Cli(object): :return: service name :rtype: string """ - if self.all_args['image']: + if self.all_args.get('image') is True: return 'image' - elif self.all_args['system']: + elif self.all_args.get('system') is True: return 'system' - elif self.all_args['result']: + elif self.all_args.get('result') is True: return 'result' - elif self.all_args['--compat']: + elif self.all_args.get('--compat') is True: return 'compat' else: raise KiwiUnknownServiceName( From 4d817810386150d650e9b3ddb50302766ddf5a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Tue, 18 Oct 2016 17:28:52 +0200 Subject: [PATCH 08/11] Smarter way to calculate bytes from a size unit --- kiwi/tasks/image_resize.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/kiwi/tasks/image_resize.py b/kiwi/tasks/image_resize.py index f6a6ded1..c74c7630 100644 --- a/kiwi/tasks/image_resize.py +++ b/kiwi/tasks/image_resize.py @@ -119,13 +119,8 @@ class ImageResizeTask(CliTask): (size_value, size_format) ) size_base = int(size.group(1)) - size_unit = size.group(2).lower() - if size_unit == 'g': - return size_base * math.pow(1024, 3) - elif size_unit == 'm': - return size_base * math.pow(1024, 2) - else: - return size_base + size_unit = {'g': 3, 'm': 2}.get(size.group(2).lower()) + return size_unit and size_base * math.pow(0x400, size_unit) or size_base def _help(self): if self.command_args['help']: From a149abe8c1e2e1baac728cba351dee86d8e614fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Tue, 18 Oct 2016 17:35:11 +0200 Subject: [PATCH 09/11] Simplify help call --- kiwi/tasks/image_resize.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/kiwi/tasks/image_resize.py b/kiwi/tasks/image_resize.py index c74c7630..220ded0f 100644 --- a/kiwi/tasks/image_resize.py +++ b/kiwi/tasks/image_resize.py @@ -72,8 +72,9 @@ class ImageResizeTask(CliTask): geometry using the qemu tool chain """ self.manual = Help() - if self._help(): - return + + if self.command_args.get('help') is True: + return self.manual.show('kiwi::image::resize') if self.command_args['--root']: image_root = self.command_args['--root'] @@ -121,10 +122,3 @@ class ImageResizeTask(CliTask): size_base = int(size.group(1)) size_unit = {'g': 3, 'm': 2}.get(size.group(2).lower()) return size_unit and size_base * math.pow(0x400, size_unit) or size_base - - def _help(self): - if self.command_args['help']: - self.manual.show('kiwi::image::resize') - else: - return False - return self.manual From de80a1f9b7f13d00de6fb95ee927d472c4d8d703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Tue, 18 Oct 2016 17:47:18 +0200 Subject: [PATCH 10/11] Only resize disk format if required Only resize the disk format if the raw disk has been changed If the size of the raw disk is the same as the requested size just print a message to the user --- kiwi/tasks/image_resize.py | 9 ++++++--- test/unit/tasks_image_resize_test.py | 25 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/kiwi/tasks/image_resize.py b/kiwi/tasks/image_resize.py index 220ded0f..559ce8f7 100644 --- a/kiwi/tasks/image_resize.py +++ b/kiwi/tasks/image_resize.py @@ -72,7 +72,6 @@ class ImageResizeTask(CliTask): geometry using the qemu tool chain """ self.manual = Help() - if self.command_args.get('help') is True: return self.manual.show('kiwi::image::resize') @@ -104,12 +103,16 @@ class ImageResizeTask(CliTask): log.info( 'Resizing raw disk to %d bytes', new_disk_size ) - image_format.resize_raw_disk(new_disk_size) - if disk_format: + resize_result = image_format.resize_raw_disk(new_disk_size) + if disk_format and resize_result is True: log.info( 'Creating %s disk format from resized raw disk', disk_format ) image_format.create_image_format() + elif resize_result is False: + log.info( + 'Raw disk is already at %d bytes', new_disk_size + ) def _to_bytes(self, size_value): size_format = '^(\d+)([gGmM]{0,1})$' diff --git a/test/unit/tasks_image_resize_test.py b/test/unit/tasks_image_resize_test.py index 4b764741..9b0ff189 100644 --- a/test/unit/tasks_image_resize_test.py +++ b/test/unit/tasks_image_resize_test.py @@ -1,9 +1,10 @@ import sys import mock +from mock import call import kiwi -from .test_helper import raises +from .test_helper import raises, patch from kiwi.exceptions import KiwiImageResizeError, KiwiConfigFileNotFound @@ -61,6 +62,7 @@ class TestImageResizeTask(object): def test_process_image_resize_gb(self): self._init_command_args() self.task.command_args['resize'] = True + self.image_format.resize_raw_disk.return_value = True self.task.process() self.image_format.resize_raw_disk.assert_called_once_with( 42 * 1024 * 1024 * 1024 @@ -71,6 +73,7 @@ class TestImageResizeTask(object): self._init_command_args() self.task.command_args['resize'] = True self.task.command_args['--size'] = '42m' + self.image_format.resize_raw_disk.return_value = True self.task.process() self.image_format.resize_raw_disk.assert_called_once_with( 42 * 1024 * 1024 @@ -81,12 +84,32 @@ class TestImageResizeTask(object): self._init_command_args() self.task.command_args['resize'] = True self.task.command_args['--size'] = '42' + self.image_format.resize_raw_disk.return_value = True self.task.process() self.image_format.resize_raw_disk.assert_called_once_with( 42 ) self.image_format.create_image_format.assert_called_once_with() + @patch('kiwi.logger.log.info') + def test_process_image_resize_not_needed(self, mock_log_info): + self._init_command_args() + self.task.command_args['resize'] = True + self.task.command_args['--size'] = '42' + self.image_format.resize_raw_disk.return_value = False + self.task.process() + self.image_format.resize_raw_disk.assert_called_once_with( + 42 + ) + assert mock_log_info.call_args_list == [ + call('Loading XML description'), + call('--> loaded %s', '../data/root-dir/image/config.xml'), + call('--> Selected build type: %s', 'vmx'), + call('--> Selected profiles: %s', 'vmxFlavour'), + call('Resizing raw disk to %d bytes', 42), + call('Raw disk is already at %d bytes', 42) + ] + def test_process_image_resize_help(self): self._init_command_args() self.task.command_args['help'] = True From a3ce96ae7888ceee199fa421efd321cf338c6687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Wed, 19 Oct 2016 11:34:05 +0200 Subject: [PATCH 11/11] Use format method for messages in image_resize There are more places where this cleanup from %x format attributes to the format() method is required. Here it is done in the scope of the image resize task --- kiwi/tasks/image_resize.py | 18 +++++++++++------- test/unit/tasks_image_resize_test.py | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/kiwi/tasks/image_resize.py b/kiwi/tasks/image_resize.py index 559ce8f7..b0d30e10 100644 --- a/kiwi/tasks/image_resize.py +++ b/kiwi/tasks/image_resize.py @@ -94,24 +94,27 @@ class ImageResizeTask(CliTask): ) if not image_format.has_raw_disk(): raise KiwiImageResizeError( - 'no raw disk image %s found in build results' % - image_format.diskname + 'no raw disk image {0} found in build results'.format( + image_format.diskname + ) ) new_disk_size = self._to_bytes(self.command_args['--size']) log.info( - 'Resizing raw disk to %d bytes', new_disk_size + 'Resizing raw disk to {0} bytes'.format(new_disk_size) ) resize_result = image_format.resize_raw_disk(new_disk_size) if disk_format and resize_result is True: log.info( - 'Creating %s disk format from resized raw disk', disk_format + 'Creating {0} disk format from resized raw disk'.format( + disk_format + ) ) image_format.create_image_format() elif resize_result is False: log.info( - 'Raw disk is already at %d bytes', new_disk_size + 'Raw disk is already at {0} bytes'.format(new_disk_size) ) def _to_bytes(self, size_value): @@ -119,8 +122,9 @@ class ImageResizeTask(CliTask): size = re.search(size_format, size_value) if not size: raise KiwiImageResizeError( - 'unsupported size format %s, must match %s' % - (size_value, size_format) + 'unsupported size format {0}, must match {1}'.format( + size_value, size_format + ) ) size_base = int(size.group(1)) size_unit = {'g': 3, 'm': 2}.get(size.group(2).lower()) diff --git a/test/unit/tasks_image_resize_test.py b/test/unit/tasks_image_resize_test.py index 9b0ff189..d9386611 100644 --- a/test/unit/tasks_image_resize_test.py +++ b/test/unit/tasks_image_resize_test.py @@ -106,8 +106,8 @@ class TestImageResizeTask(object): call('--> loaded %s', '../data/root-dir/image/config.xml'), call('--> Selected build type: %s', 'vmx'), call('--> Selected profiles: %s', 'vmxFlavour'), - call('Resizing raw disk to %d bytes', 42), - call('Raw disk is already at %d bytes', 42) + call('Resizing raw disk to 42 bytes'), + call('Raw disk is already at 42 bytes') ] def test_process_image_resize_help(self):