diff --git a/plugins/minimize_size/__init__.py b/plugins/minimize_size/__init__.py index dd6500e..d49b551 100644 --- a/plugins/minimize_size/__init__.py +++ b/plugins/minimize_size/__init__.py @@ -1,10 +1,27 @@ import tasks +def validate_manifest(data, validator, error): + import os.path + schema_path = os.path.join(os.path.dirname(__file__), 'manifest-schema.json') + validator(data, schema_path) + if 'zerofree' in data['plugins']['minimize_size']: + zerofree_schema_path = os.path.join(os.path.dirname(__file__), 'manifest-schema-zerofree.json') + validator(data, zerofree_schema_path) + if data['plugins']['minimize_size'].get('shrink', False) and data['volume']['backing'] != 'vmdk': + error('Can only shrink vmdk images', ['plugins', 'minimize_size', 'shrink']) + + def resolve_tasks(taskset, manifest): taskset.update([tasks.AddFolderMounts, tasks.RemoveFolderMounts, ]) + if 'zerofree' in manifest.plugins['minimize_size']: + taskset.add(tasks.CheckZerofreePath) + taskset.add(tasks.Zerofree) + if manifest.plugins['minimize_size'].get('shrink', False): + taskset.add(tasks.CheckVMWareDMCommand) + taskset.add(tasks.ShrinkVolume) def resolve_rollback_tasks(taskset, manifest, counter_task): diff --git a/plugins/minimize_size/manifest-schema-zerofree.json b/plugins/minimize_size/manifest-schema-zerofree.json new file mode 100644 index 0000000..bd6715d --- /dev/null +++ b/plugins/minimize_size/manifest-schema-zerofree.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Minimize size plugin manifest", + "type": "object", + "properties": { + "volume": { + "type": "object", + "properties": { + "partitions": { + "type": "object", + "properties": { + "type": { "enum": ["none"] } + } + } + } + } + } +} diff --git a/plugins/minimize_size/manifest-schema.json b/plugins/minimize_size/manifest-schema.json new file mode 100644 index 0000000..0d7942c --- /dev/null +++ b/plugins/minimize_size/manifest-schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Minimize size plugin manifest", + "type": "object", + "properties": { + "plugins": { + "type": "object", + "properties": { + "minimize_size": { + "type": "object", + "properties": { + "shrink": { "type": "boolean" }, + "zerofree": { "$ref": "#/definitions/absolute_path" } + } + } + } + } + }, + "definitions": { + "absolute_path": { + "type": "string", + "pattern": "^/[^\\0]+$" + } + } +} diff --git a/plugins/minimize_size/tasks.py b/plugins/minimize_size/tasks.py index 96bfdf2..37798ae 100644 --- a/plugins/minimize_size/tasks.py +++ b/plugins/minimize_size/tasks.py @@ -1,7 +1,10 @@ from base import Task from common import phases -from common.tasks import bootstrap from common.tasks import apt +from common.tasks import bootstrap +from common.tasks import filesystem +from common.tasks import partitioning +from common.tasks import volume import os folders = ['tmp', 'var/lib/apt/lists'] @@ -41,3 +44,58 @@ class RemoveFolderMounts(Task): os.rmdir(info.minimize_size_folder) del info.minimize_size_folder + + +class CheckZerofreePath(Task): + description = 'Checking path to zerofree tool' + phase = phases.preparation + + @classmethod + def run(cls, info): + from common.exceptions import TaskError + import os + zerofree = info.manifest.plugins['minimize_size']['zerofree'] + if not os.path.isfile(zerofree): + raise TaskError('The path `{path}\' does not exist or is not a file'.format(path=zerofree)) + if not os.access(zerofree, os.X_OK): + raise TaskError('The path `{path}\' is not executable'.format(path=zerofree)) + + +# Get zerofree here: http://intgat.tigress.co.uk/rmy/uml/index.html +class Zerofree(Task): + description = 'Zeroing unused blocks on the volume' + phase = phases.volume_unmounting + predecessors = [filesystem.UnmountRoot, partitioning.UnmapPartitions] + successors = [volume.Detach] + + @classmethod + def run(cls, info): + from common.tools import log_check_call + zerofree = info.manifest.plugins['minimize_size']['zerofree'] + log_check_call([zerofree, info.volume.device_path]) + + +class CheckVMWareDMCommand(Task): + description = 'Checking path to vmware-vdiskmanager tool' + phase = phases.preparation + + @classmethod + def run(cls, info): + from common.exceptions import TaskError + import os + vdiskmngr = '/usr/bin/vmware-vdiskmanager' + if not os.path.isfile(vdiskmngr): + raise TaskError('Unable to find vmware-vdiskmanager at `{path}\''.format(path=vdiskmngr)) + if not os.access(vdiskmngr, os.X_OK): + raise TaskError('vmware-vdiskmanager at `{path}\' is not executable'.format(path=vdiskmngr)) + + +class ShrinkVolume(Task): + description = 'Shrinking the volume' + phase = phases.volume_unmounting + predecessors = [volume.Detach] + + @classmethod + def run(cls, info): + from common.tools import log_check_call + log_check_call(['/usr/bin/vmware-vdiskmanager', '-k', info.volume.image_path])