From 511a44f72ec6cf9dc16799650a15050b230df2f7 Mon Sep 17 00:00:00 2001 From: Brendan Harley Date: Sun, 14 May 2017 21:09:19 +0200 Subject: [PATCH] Add LVM as a disk backend Enables the use of Logical Volumes as disk backends. It uses an existing volume group and has no support for creating a new one. It will not override an existing logical volume and fail gracefully. The lv is created, activated and then mounted as a loop device. The boostraping process is then launched on the loop device. Once the process is completed, the lv is unmounted and desactivated. The created lv will be deleted should the boostraping process fail. The lv must be activated before use. A manifest has been included for testing purposes. --- bootstrapvz/base/fs/__init__.py | 4 +- bootstrapvz/base/manifest-schema.yml | 21 +++++++++- bootstrapvz/common/fs/logicalvolume.py | 38 +++++++++++++++++++ bootstrapvz/common/tasks/logicalvolume.py | 37 ++++++++++++++++++ bootstrapvz/providers/kvm/README.rst | 8 ++++ bootstrapvz/providers/kvm/__init__.py | 15 ++++++-- bootstrapvz/providers/kvm/manifest-schema.yml | 6 ++- manifests/examples/kvm/jessie-lvm.yml | 26 +++++++++++++ 8 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 bootstrapvz/common/fs/logicalvolume.py create mode 100644 bootstrapvz/common/tasks/logicalvolume.py create mode 100644 manifests/examples/kvm/jessie-lvm.yml diff --git a/bootstrapvz/base/fs/__init__.py b/bootstrapvz/base/fs/__init__.py index ec86a5c55..fecc99230 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 170cc2ba0..0209283f4 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 000000000..a3945d78b --- /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 000000000..2092ea002 --- /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 e5f72d443..274edf8dc 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 54be46ac8..e993383d8 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 f59c77b36..e28bf00d4 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 000000000..1acfc35d6 --- /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