diff --git a/bootstrapvz/base/fs/__init__.py b/bootstrapvz/base/fs/__init__.py index ec86a5c..fecc992 100644 --- a/bootstrapvz/base/fs/__init__.py +++ b/bootstrapvz/base/fs/__init__.py @@ -25,13 +25,15 @@ def load_volume(data, bootloader): from bootstrapvz.common.fs.virtualharddisk import VirtualHardDisk from bootstrapvz.common.fs.virtualmachinedisk import VirtualMachineDisk from bootstrapvz.common.fs.folder import Folder + from bootstrapvz.common.fs.logicalvolume import LogicalVolume volume_backing = {'raw': LoopbackVolume, 's3': LoopbackVolume, 'vdi': VirtualDiskImage, 'vhd': VirtualHardDisk, 'vmdk': VirtualMachineDisk, 'ebs': EBSVolume, - 'folder': Folder + 'folder': Folder, + 'lvm': LogicalVolume }.get(data['backing']) # Instantiate the partition map diff --git a/bootstrapvz/base/manifest-schema.yml b/bootstrapvz/base/manifest-schema.yml index 170cc2b..0209283 100644 --- a/bootstrapvz/base/manifest-schema.yml +++ b/bootstrapvz/base/manifest-schema.yml @@ -131,6 +131,12 @@ properties: ^\w+$: {} additionalProperties: false volume: + type: object + oneOf: + - $ref: '#/definitions/standardvolume' + - $ref: '#/definitions/logicalvolume' +definitions: + standardvolume: type: object properties: backing: {type: string} @@ -141,7 +147,20 @@ properties: - $ref: '#/definitions/partition_table' required: [partitions] additionalProperties: false -definitions: + logicalvolume: + type: object + properties: + backing: + enum: [lvm] + volumegroup : {type: string} + logicalvolume: {type: string} + partitions: + type: object + oneOf: + - $ref: '#/definitions/no_partitions' + - $ref: '#/definitions/partition_table' + required: [partitions] + additionalProperties: false absolute_path: type: string pattern: ^/[^\0]+$ diff --git a/bootstrapvz/common/fs/logicalvolume.py b/bootstrapvz/common/fs/logicalvolume.py new file mode 100644 index 0000000..a3945d7 --- /dev/null +++ b/bootstrapvz/common/fs/logicalvolume.py @@ -0,0 +1,38 @@ +from bootstrapvz.base.fs.volume import Volume +from bootstrapvz.common.tools import log_check_call +import os + + +class LogicalVolume(Volume): + + def __init__(self, partitionmap): + super(LogicalVolume, self).__init__(partitionmap) + self.vg = '' + self.lv = '' + + def create(self, volumegroup, logicalvolume): + self.vg = volumegroup + self.lv = logicalvolume + image_path = os.path.join(os.sep, 'dev', self.vg, self.lv) + self.fsm.create(image_path=image_path) + + def _before_create(self, e): + self.image_path = e.image_path + lv_size = str(self.size.bytes.get_qty_in('MiB')) + log_check_call(['lvcreate', '--size', '{mib}M'.format(mib=lv_size), + '--name', self.lv, self.vg]) + + def _before_attach(self, e): + log_check_call(['lvchange', '--activate', 'y', self.image_path]) + [self.loop_device_path] = log_check_call(['losetup', '--show', '--find', '--partscan', self.image_path]) + self.device_path = self.loop_device_path + + def _before_detach(self, e): + log_check_call(['losetup', '--detach', self.loop_device_path]) + log_check_call(['lvchange', '--activate', 'n', self.image_path]) + del self.loop_device_path + self.device_path = None + + def delete(self): + log_check_call(['lvremove', '-f', self.image_path]) + del self.image_path diff --git a/bootstrapvz/common/tasks/logicalvolume.py b/bootstrapvz/common/tasks/logicalvolume.py new file mode 100644 index 0000000..2092ea0 --- /dev/null +++ b/bootstrapvz/common/tasks/logicalvolume.py @@ -0,0 +1,37 @@ +import bootstrapvz.common.tasks.host as host +import bootstrapvz.common.tasks.volume as volume +from bootstrapvz.base import Task +from bootstrapvz.common import phases + + +class AddRequiredCommands(Task): + description = 'Adding commands required for creating and mounting logical volumes' + phase = phases.validation + successors = [host.CheckExternalCommands] + + @classmethod + def run(cls, info): + from bootstrapvz.common.fs.logicalvolume import LogicalVolume + if type(info.volume) is LogicalVolume: + info.host_dependencies['lvcreate'] = 'lvm2' + info.host_dependencies['losetup'] = 'mount' + + +class Create(Task): + description = 'Creating a Logical volume' + phase = phases.volume_creation + successors = [volume.Attach] + + @classmethod + def run(cls, info): + info.volume.create(volumegroup=info.manifest.volume['volumegroup'], + logicalvolume=info.manifest.volume['logicalvolume']) + + +class Delete(Task): + description = 'Deleting a Logical volume' + phase = phases.cleaning + + @classmethod + def run(cls, info): + info.volume.delete() diff --git a/bootstrapvz/providers/kvm/README.rst b/bootstrapvz/providers/kvm/README.rst index e5f72d4..274edf8 100644 --- a/bootstrapvz/providers/kvm/README.rst +++ b/bootstrapvz/providers/kvm/README.rst @@ -6,6 +6,7 @@ virtual images for Linux Kernel-based Virtual Machines. It supports the installation of `virtio kernel modules `__ (paravirtualized drivers for IO operations). +It also supports creating an image with LVM as a disk backend. Manifest settings ----------------- @@ -15,6 +16,9 @@ Provider - ``virtio``: Specifies which virtio kernel modules to install. ``optional`` +- ``logicalvolume``: Specifies the logical volume where the disk image will be built. + ``volumegroup``: Specifies the volume group where the logical volume will be stored. + These options should only be used if ``lvm`` was given as a disk backend. Example: @@ -26,3 +30,7 @@ Example: virtio: - virtio_blk - virtio_net + volume: + backing: lvm + logicalvolume: lvtest + volumegroup: vgtest diff --git a/bootstrapvz/providers/kvm/__init__.py b/bootstrapvz/providers/kvm/__init__.py index 54be46a..e993383 100644 --- a/bootstrapvz/providers/kvm/__init__.py +++ b/bootstrapvz/providers/kvm/__init__.py @@ -1,6 +1,6 @@ from bootstrapvz.common import task_groups import tasks.packages -from bootstrapvz.common.tasks import image, loopback, initd, ssh +from bootstrapvz.common.tasks import image, loopback, initd, ssh, logicalvolume def validate_manifest(data, validator, error): @@ -12,14 +12,20 @@ def resolve_tasks(taskset, manifest): taskset.update(task_groups.get_standard_groups(manifest)) taskset.update([tasks.packages.DefaultPackages, - loopback.AddRequiredCommands, - loopback.Create, initd.InstallInitScripts, ssh.AddOpenSSHPackage, ssh.ShredHostkeys, ssh.AddSSHKeyGeneration, - image.MoveImage, ]) + if manifest.volume.get('logicalvolume', []): + taskset.update([logicalvolume.AddRequiredCommands, + logicalvolume.Create, + ]) + else: + taskset.update([loopback.AddRequiredCommands, + loopback.Create, + image.MoveImage, + ]) if manifest.provider.get('virtio', []): from tasks import virtio @@ -28,3 +34,4 @@ def resolve_tasks(taskset, manifest): def resolve_rollback_tasks(taskset, manifest, completed, counter_task): taskset.update(task_groups.get_standard_rollback_tasks(completed)) + counter_task(taskset, logicalvolume.Create, logicalvolume.Delete) diff --git a/bootstrapvz/providers/kvm/manifest-schema.yml b/bootstrapvz/providers/kvm/manifest-schema.yml index f59c77b..e28bf00 100644 --- a/bootstrapvz/providers/kvm/manifest-schema.yml +++ b/bootstrapvz/providers/kvm/manifest-schema.yml @@ -32,7 +32,11 @@ properties: properties: backing: type: string - enum: [raw] + enum: + - raw + - lvm + logicalvolume: {type: string} + volumegroup: {type: string} partitions: type: object properties: diff --git a/manifests/examples/kvm/jessie-lvm.yml b/manifests/examples/kvm/jessie-lvm.yml new file mode 100644 index 0000000..1acfc35 --- /dev/null +++ b/manifests/examples/kvm/jessie-lvm.yml @@ -0,0 +1,26 @@ +--- +name: debian-lvm-example +provider: + name: kvm +bootstrapper: + workspace: /target +system: + release: jessie + architecture: amd64 + bootloader: grub + charmap: UTF-8 + locale: en_US + timezone: UTC +volume: + backing: lvm + logicalvolume: lvtest + volumegroup: vgtest + partitions: + type: gpt + root: + filesystem: ext4 + size: 1GB +packages: {} +plugins: + root_password: + password: test