diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-06-13 20:50:10 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-06-13 20:50:10 +0000 |
| commit | dfcaa325f7a6925f59123a33bd509ebc6c04bd03 (patch) | |
| tree | bb19559b988c778c2b1323f7f57e8a315e80c74b /nova/virt | |
| parent | e80b33948292634666d0dad0d18bb83a9997acd7 (diff) | |
| parent | e0540dfed1c1276106105aea8d5765356961ef3d (diff) | |
Merge "blueprint lvm-disk-images"
Diffstat (limited to 'nova/virt')
| -rw-r--r-- | nova/virt/disk/api.py | 8 | ||||
| -rw-r--r-- | nova/virt/libvirt/connection.py | 269 | ||||
| -rw-r--r-- | nova/virt/libvirt/imagebackend.py | 255 | ||||
| -rw-r--r-- | nova/virt/libvirt/utils.py | 55 |
4 files changed, 418 insertions, 169 deletions
diff --git a/nova/virt/disk/api.py b/nova/virt/disk/api.py index 9e2e9ffa1..672b5d50e 100644 --- a/nova/virt/disk/api.py +++ b/nova/virt/disk/api.py @@ -108,6 +108,11 @@ def get_image_virtual_size(image): return int(m.group(2)) +def resize2fs(image, check_exit_code=False): + utils.execute('e2fsck', '-fp', image, check_exit_code=check_exit_code) + utils.execute('resize2fs', image, check_exit_code=check_exit_code) + + def extend(image, size): """Increase image to size""" # NOTE(MotoKen): check image virtual size before resize @@ -116,8 +121,7 @@ def extend(image, size): return utils.execute('qemu-img', 'resize', image, size) # NOTE(vish): attempts to resize filesystem - utils.execute('e2fsck', '-fp', image, check_exit_code=False) - utils.execute('resize2fs', image, check_exit_code=False) + resize2fs(image) def bind(src, target, instance_name): diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index fe117159b..d890cffb2 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -71,6 +71,7 @@ from nova.virt.disk import api as disk from nova.virt import driver from nova.virt.libvirt import config from nova.virt.libvirt import firewall +from nova.virt.libvirt import imagebackend from nova.virt.libvirt import imagecache from nova.virt.libvirt import utils as libvirt_utils @@ -271,6 +272,7 @@ class LibvirtDriver(driver.ComputeDriver): self._disk_cachemode = None self.image_cache_manager = imagecache.ImageCacheManager() + self.image_backend = imagebackend.Backend(FLAGS.use_cow_images) @property def disk_cachemode(self): @@ -506,6 +508,34 @@ class LibvirtDriver(driver.ComputeDriver): if os.path.exists(target): shutil.rmtree(target) + #NOTE(bfilippov): destroy all LVM disks for this instance + self._cleanup_lvm(instance) + + def _cleanup_lvm(self, instance): + """Delete all LVM disks for given instance object""" + disks = self._lvm_disks(instance) + if disks: + libvirt_utils.remove_logical_volumes(*disks) + + def _lvm_disks(self, instance): + """Returns all LVM disks for given instance object""" + if FLAGS.libvirt_images_volume_group: + vg = os.path.join('/dev', FLAGS.libvirt_images_volume_group) + if not os.path.exists(vg): + return [] + pattern = '%s_' % instance['name'] + + def belongs_to_instance(disk): + return disk.startswith(pattern) + + def fullpath(name): + return os.path.join(vg, name) + + disk_names = filter(belongs_to_instance, os.listdir(vg)) + disks = map(fullpath, disk_names) + return disks + return [] + def get_volume_connector(self, instance): if not self._initiator: self._initiator = libvirt_utils.get_iscsi_initiator() @@ -681,7 +711,10 @@ class LibvirtDriver(driver.ComputeDriver): # NOTE(vish): assume amis are raw source_format = 'raw' image_format = FLAGS.snapshot_image_format or source_format - if FLAGS.use_cow_images: + use_qcow2 = ((FLAGS.libvirt_images_type == 'default' and + FLAGS.use_cow_images) or + FLAGS.libvirt_images_type == 'qcow2') + if use_qcow2: source_format = 'qcow2' # NOTE(vish): glance forces ami disk format to be ami if base.get('disk_format') == 'ami': @@ -957,8 +990,8 @@ class LibvirtDriver(driver.ComputeDriver): return fpath def _inject_files(self, instance, files, partition): - disk_path = os.path.join(FLAGS.instances_path, - instance['name'], 'disk') + disk_path = self.image_backend.image(instance['name'], + 'disk').path disk.inject_files(disk_path, files, partition=partition, use_cow=FLAGS.use_cow_images) @@ -1071,72 +1104,6 @@ class LibvirtDriver(driver.ComputeDriver): return hasDirectIO @staticmethod - def _cache_image(fn, target, fname, cow=False, size=None, *args, **kwargs): - """Wrapper for a method that creates an image that caches the image. - - This wrapper will save the image into a common store and create a - copy for use by the hypervisor. - - The underlying method should specify a kwarg of target representing - where the image will be saved. - - fname is used as the filename of the base image. The filename needs - to be unique to a given image. - - If cow is True, it will make a CoW image instead of a copy. - - If size is specified, we attempt to resize up to that size. - """ - - # NOTE(mikal): Checksums aren't created here, even if the image cache - # manager is enabled, as that would slow down VM startup. If both - # cache management and checksumming are enabled, then the checksum - # will be created on the first pass of the image cache manager. - - generating = 'image_id' not in kwargs - if not os.path.exists(target): - base_dir = os.path.join(FLAGS.instances_path, FLAGS.base_dir_name) - - if not os.path.exists(base_dir): - libvirt_utils.ensure_tree(base_dir) - base = os.path.join(base_dir, fname) - - @utils.synchronized(fname) - def call_if_not_exists(base, fn, *args, **kwargs): - if not os.path.exists(base): - with utils.remove_path_on_error(base): - fn(target=base, *args, **kwargs) - - if cow or not generating: - call_if_not_exists(base, fn, *args, **kwargs) - elif generating: - # For raw it's quicker to just generate outside the cache - call_if_not_exists(target, fn, *args, **kwargs) - - @utils.synchronized(base) - def copy_and_extend(cow, generating, base, target, size): - if cow: - cow_base = base - if size: - size_gb = size / (1024 * 1024 * 1024) - cow_base += "_%d" % size_gb - if not os.path.exists(cow_base): - with utils.remove_path_on_error(cow_base): - libvirt_utils.copy_image(base, cow_base) - disk.extend(cow_base, size) - libvirt_utils.create_cow_image(cow_base, target) - elif not generating: - libvirt_utils.copy_image(base, target) - # Resize after the copy, as it's usually much faster - # to make sparse updates, rather than potentially - # naively copying the whole image file. - if size: - disk.extend(target, size) - - with utils.remove_path_on_error(target): - copy_and_extend(cow, generating, base, target, size) - - @staticmethod def _create_local(target, local_size, unit='G', fs_format=None, label=None): """Create a blank image of specified size""" @@ -1181,6 +1148,13 @@ class LibvirtDriver(driver.ComputeDriver): instance['name'], fname + suffix) + def image(fname, image_type=FLAGS.libvirt_images_type): + return self.image_backend.image(instance['name'], + fname, suffix, image_type) + + def raw(fname): + return image(fname, image_type='raw') + # ensure directories exist and are writable libvirt_utils.ensure_tree(basepath(suffix='')) @@ -1204,22 +1178,20 @@ class LibvirtDriver(driver.ComputeDriver): if disk_images['kernel_id']: fname = disk_images['kernel_id'] - self._cache_image(fn=libvirt_utils.fetch_image, - context=context, - target=basepath('kernel'), - fname=fname, - image_id=disk_images['kernel_id'], - user_id=instance['user_id'], - project_id=instance['project_id']) + raw('kernel').cache(fn=libvirt_utils.fetch_image, + context=context, + fname=fname, + image_id=disk_images['kernel_id'], + user_id=instance['user_id'], + project_id=instance['project_id']) if disk_images['ramdisk_id']: fname = disk_images['ramdisk_id'] - self._cache_image(fn=libvirt_utils.fetch_image, - context=context, - target=basepath('ramdisk'), - fname=fname, - image_id=disk_images['ramdisk_id'], - user_id=instance['user_id'], - project_id=instance['project_id']) + raw('ramdisk').cache(fn=libvirt_utils.fetch_image, + context=context, + fname=fname, + image_id=disk_images['ramdisk_id'], + user_id=instance['user_id'], + project_id=instance['project_id']) root_fname = hashlib.sha1(str(disk_images['image_id'])).hexdigest() size = instance['root_gb'] * 1024 * 1024 * 1024 @@ -1231,15 +1203,13 @@ class LibvirtDriver(driver.ComputeDriver): if not self._volume_in_mapping(self.default_root_device, block_device_info): - self._cache_image(fn=libvirt_utils.fetch_image, - context=context, - target=basepath('disk'), - fname=root_fname, - cow=FLAGS.use_cow_images, - image_id=disk_images['image_id'], - user_id=instance['user_id'], - project_id=instance['project_id'], - size=size) + image('disk').cache(fn=libvirt_utils.fetch_image, + context=context, + fname=root_fname, + size=size, + image_id=disk_images['image_id'], + user_id=instance['user_id'], + project_id=instance['project_id']) ephemeral_gb = instance['ephemeral_gb'] if ephemeral_gb and not self._volume_in_mapping( @@ -1248,12 +1218,14 @@ class LibvirtDriver(driver.ComputeDriver): fn = functools.partial(self._create_ephemeral, fs_label='ephemeral0', os_type=instance.os_type) - self._cache_image(fn=fn, - target=basepath('disk.local'), - fname="ephemeral_%s_%s_%s" % - ("0", ephemeral_gb, instance.os_type), - cow=FLAGS.use_cow_images, - ephemeral_size=ephemeral_gb) + fname = "ephemeral_%s_%s_%s" % ("0", + ephemeral_gb, + instance.os_type) + size = ephemeral_gb * 1024 * 1024 * 1024 + image('disk.local').cache(fn=fn, + fname=fname, + size=size, + ephemeral_size=ephemeral_gb) else: swap_device = self.default_second_device @@ -1261,12 +1233,14 @@ class LibvirtDriver(driver.ComputeDriver): fn = functools.partial(self._create_ephemeral, fs_label='ephemeral%d' % eph['num'], os_type=instance.os_type) - self._cache_image(fn=fn, - target=basepath(_get_eph_disk(eph)), - fname="ephemeral_%s_%s_%s" % - (eph['num'], eph['size'], instance.os_type), - cow=FLAGS.use_cow_images, - ephemeral_size=eph['size']) + size = eph['size'] * 1024 * 1024 * 1024 + fname = "ephemeral_%s_%s_%s" % (eph['num'], + eph['size'], + instance.os_type) + image(_get_eph_disk(eph)).cache(fn=fn, + fname=fname, + size=size, + ephemeral_size=eph['size']) swap_mb = 0 @@ -1278,11 +1252,11 @@ class LibvirtDriver(driver.ComputeDriver): swap_mb = inst_type['swap'] if swap_mb > 0: - self._cache_image(fn=self._create_swap, - target=basepath('disk.swap'), - fname="swap_%s" % swap_mb, - cow=FLAGS.use_cow_images, - swap_mb=swap_mb) + size = swap_mb * 1024 * 1024 + image('disk.swap').cache(fn=self._create_swap, + fname="swap_%s" % swap_mb, + size=size, + swap_mb=swap_mb) target_partition = None if not instance['kernel_id']: @@ -1297,12 +1271,11 @@ class LibvirtDriver(driver.ComputeDriver): if config_drive_id: fname = config_drive_id - self._cache_image(fn=libvirt_utils.fetch_image, - target=basepath('disk.config'), - fname=fname, - image_id=config_drive_id, - user_id=instance['user_id'], - project_id=instance['project_id'],) + raw('disk.config').cache(fn=libvirt_utils.fetch_image, + fname=fname, + image_id=config_drive_id, + user_id=instance['user_id'], + project_id=instance['project_id']) elif config_drive: label = 'config' with utils.remove_path_on_error(basepath('disk.config')): @@ -1360,10 +1333,10 @@ class LibvirtDriver(driver.ComputeDriver): if any((key, net, metadata, admin_password)): if config_drive: # Should be True or None by now. - injection_path = basepath('disk.config') + injection_path = raw('disk.config').path img_id = 'config-drive' else: - injection_path = basepath('disk') + injection_path = image('disk').path img_id = instance.image_ref for injection in ('metadata', 'key', 'net', 'admin_password'): @@ -1511,16 +1484,16 @@ class LibvirtDriver(driver.ComputeDriver): "rootfs") guest.add_device(fs) else: - if FLAGS.use_cow_images: - driver_type = 'qcow2' - else: - driver_type = 'raw' - if image_meta and image_meta.get('disk_format') == 'iso': root_device_type = 'cdrom' else: root_device_type = 'disk' + def disk_info(name, suffix=''): + image = self.image_backend.image(instance['name'], + name, suffix) + return image.libvirt_info(root_device_type) + if FLAGS.libvirt_type == "uml": ephemeral_disk_bus = "uml" elif FLAGS.libvirt_type == "xen": @@ -1529,23 +1502,13 @@ class LibvirtDriver(driver.ComputeDriver): ephemeral_disk_bus = "virtio" if rescue: - diskrescue = config.LibvirtConfigGuestDisk() - diskrescue.source_type = "file" - diskrescue.source_path = os.path.join(FLAGS.instances_path, - instance['name'], - "disk.rescue") - diskrescue.driver_format = driver_type + diskrescue = disk_info('disk', '.rescue') diskrescue.driver_cache = self.disk_cachemode diskrescue.target_dev = self.default_root_device diskrescue.target_bus = ephemeral_disk_bus guest.add_device(diskrescue) - diskos = config.LibvirtConfigGuestDisk() - diskos.source_type = "file" - diskos.source_path = os.path.join(FLAGS.instances_path, - instance['name'], - "disk") - diskos.driver_format = driver_type + diskos = disk_info('disk') diskos.driver_cache = self.disk_cachemode diskos.target_dev = self.default_second_device diskos.target_bus = ephemeral_disk_bus @@ -1555,14 +1518,8 @@ class LibvirtDriver(driver.ComputeDriver): block_device_info) if not ebs_root: - diskos = config.LibvirtConfigGuestDisk() - diskos.source_type = "file" - diskos.source_device = root_device_type - diskos.driver_format = driver_type + diskos = disk_info('disk') diskos.driver_cache = self.disk_cachemode - diskos.source_path = os.path.join(FLAGS.instances_path, - instance['name'], - "disk") diskos.target_dev = root_device if root_device_type == "cdrom": diskos.target_bus = "ide" @@ -1580,14 +1537,8 @@ class LibvirtDriver(driver.ComputeDriver): ephemeral_device = self.default_second_device if ephemeral_device is not None: - disklocal = config.LibvirtConfigGuestDisk() - disklocal.source_type = "file" - disklocal.source_device = root_device_type - disklocal.driver_format = driver_type + disklocal = disk_info('disk.local') disklocal.driver_cache = self.disk_cachemode - disklocal.source_path = os.path.join(FLAGS.instances_path, - instance['name'], - "disk.local") disklocal.target_dev = ephemeral_device disklocal.target_bus = ephemeral_disk_bus guest.add_device(disklocal) @@ -1603,14 +1554,8 @@ class LibvirtDriver(driver.ComputeDriver): for eph in driver.block_device_info_get_ephemerals( block_device_info): - diskeph = config.LibvirtConfigGuestDisk() - diskeph.source_type = "block" - diskeph.source_device = root_device_type - diskeph.driver_format = driver_type + diskeph = disk_info(_get_eph_disk(eph)) diskeph.driver_cache = self.disk_cachemode - diskeph.source_path = os.path.join(FLAGS.instances_path, - instance['name'], - _get_eph_disk(eph)) diskeph.target_dev = block_device.strip_dev( eph['device_name']) diskeph.target_bus = ephemeral_disk_bus @@ -1618,13 +1563,8 @@ class LibvirtDriver(driver.ComputeDriver): swap = driver.block_device_info_get_swap(block_device_info) if driver.swap_is_usable(swap): - diskswap = config.LibvirtConfigGuestDisk() - diskswap.disk_type = "file" - diskswap.driver_format = driver_type + diskswap = disk_info('disk.swap') diskswap.driver_cache = self.disk_cachemode - diskswap.source_path = os.path.join(FLAGS.instances_path, - instance['name'], - "disk.swap") diskswap.target_dev = block_device.strip_dev( swap['device_name']) diskswap.target_bus = ephemeral_disk_bus @@ -1632,13 +1572,8 @@ class LibvirtDriver(driver.ComputeDriver): elif (inst_type['swap'] > 0 and not self._volume_in_mapping(swap_device, block_device_info)): - diskswap = config.LibvirtConfigGuestDisk() - diskswap.disk_type = "file" - diskswap.driver_format = driver_type + diskswap = disk_info('disk.swap') diskswap.driver_cache = self.disk_cachemode - diskswap.source_path = os.path.join(FLAGS.instances_path, - instance['name'], - "disk.swap") diskswap.target_dev = swap_device diskswap.target_bus = ephemeral_disk_bus guest.add_device(diskswap) diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py new file mode 100644 index 000000000..d1a6afe6a --- /dev/null +++ b/nova/virt/libvirt/imagebackend.py @@ -0,0 +1,255 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Grid Dynamics +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import abc +import contextlib +import os + +from nova import flags +from nova.openstack.common import cfg +from nova.openstack.common import excutils +from nova import utils +from nova.virt.disk import api as disk +from nova.virt.libvirt import config +from nova.virt.libvirt import utils as libvirt_utils + +__imagebackend_opts = [ + cfg.StrOpt('libvirt_images_type', + default='default', + help='VM Images format. Acceptable values are: raw, qcow2, lvm,' + ' default. If default is specified,' + ' then use_cow_images flag is used instead of this one.'), + cfg.StrOpt('libvirt_images_volume_group', + default=None, + help='LVM Volume Group that is used for VM images, when you' + ' specify libvirt_images_type=lvm.'), + cfg.BoolOpt('libvirt_sparse_logical_volumes', + default=False, + help='Create sparse logical volumes (with virtualsize)' + ' if this flag is set to True.'), + ] + +FLAGS = flags.FLAGS +FLAGS.register_opts(__imagebackend_opts) + + +class Image(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __init__(self, instance, name, suffix): + """Image initialization. + + :instance: Instance name. + :name: Image name. + :suffix: Suffix for image name. + """ + pass + + @abc.abstractmethod + def create_image(self, prepare_template, base, size, *args, **kwargs): + """Create image from template. + + Contains specific behavior for each image type. + + :prepare_template: function, that creates template. + Should accept `target` argument. + :base: Template name + :size: Size of created image in bytes + """ + pass + + @abc.abstractmethod + def libvirt_info(self, device_type): + """Get `LibvirtConfigGuestDisk` filled for this image. + + :device_type: Device type for this image. + """ + pass + + def cache(self, fn, fname, size=None, *args, **kwargs): + """Creates image from template. + + Ensures that template and image not already exists. + Ensures that base directory exists. + Synchronizes on template fetching. + + :fn: function, that creates template. + Should accept `target` argument. + :fname: Template name + :size: Size of created image in bytes (optional) + """ + @utils.synchronized(fname) + def call_if_not_exists(target, *args, **kwargs): + if not os.path.exists(target): + fn(target=target, *args, **kwargs) + + if not os.path.exists(self.path): + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + libvirt_utils.ensure_tree(base_dir) + base = os.path.join(base_dir, fname) + + self.create_image(call_if_not_exists, base, size, + *args, **kwargs) + + +class Raw(Image): + def __init__(self, instance, name, suffix): + if not suffix: + suffix = '' + self.path = os.path.join(FLAGS.instances_path, + instance, name + suffix) + + def libvirt_info(self, device_type): + info = config.LibvirtConfigGuestDisk() + info.source_type = 'file' + info.source_device = device_type + info.driver_format = 'raw' + info.source_path = self.path + return info + + def create_image(self, prepare_template, base, size, *args, **kwargs): + @utils.synchronized(base) + def copy_raw_image(base, target, size): + libvirt_utils.copy_image(base, target) + if size: + disk.extend(target, size) + + generating = 'image_id' not in kwargs + if generating: + #Generating image in place + prepare_template(target=self.path, *args, **kwargs) + else: + prepare_template(target=base, *args, **kwargs) + with utils.remove_path_on_error(self.path): + copy_raw_image(base, self.path, size) + + +class Qcow2(Raw): + def libvirt_info(self, device_type): + info = config.LibvirtConfigGuestDisk() + info.source_type = 'file' + info.source_device = device_type + info.driver_format = 'qcow2' + info.source_path = self.path + return info + + def create_image(self, prepare_template, base, size, *args, **kwargs): + @utils.synchronized(base) + def copy_qcow2_image(base, target, size): + qcow2_base = base + if size: + size_gb = size / (1024 * 1024 * 1024) + qcow2_base += '_%d' % size_gb + if not os.path.exists(qcow2_base): + with utils.remove_path_on_error(qcow2_base): + libvirt_utils.copy_image(base, qcow2_base) + disk.extend(qcow2_base, size) + libvirt_utils.create_cow_image(qcow2_base, target) + + prepare_template(target=base, *args, **kwargs) + with utils.remove_path_on_error(self.path): + copy_qcow2_image(base, self.path, size) + + +class Lvm(Image): + @staticmethod + def escape(fname): + return fname.replace('_', '__') + + def libvirt_info(self, device_type): + info = config.LibvirtConfigGuestDisk() + info.source_type = 'block' + info.source_device = device_type + info.driver_format = 'raw' + info.source_path = self.path + return info + + def __init__(self, instance, name, suffix): + if not suffix: + suffix = '' + if not FLAGS.libvirt_images_volume_group: + raise RuntimeError(_('You should specify' + ' libvirt_images_volume_group' + ' flag to use LVM images.')) + self.vg = FLAGS.libvirt_images_volume_group + self.lv = '%s_%s' % (self.escape(instance), + self.escape(name + suffix)) + self.path = os.path.join('/dev', self.vg, self.lv) + self.sparse = FLAGS.libvirt_sparse_logical_volumes + + def create_image(self, prepare_template, base, size, *args, **kwargs): + @utils.synchronized(base) + def create_lvm_image(base, size): + base_size = disk.get_image_virtual_size(base) + resize = size > base_size + size = size if resize else base_size + libvirt_utils.create_lvm_image(self.vg, self.lv, + size, sparse=self.sparse) + cmd = ('dd', 'if=%s' % base, 'of=%s' % self.path, 'bs=4M') + utils.execute(*cmd, run_as_root=True) + if resize: + disk.resize2fs(self.path) + + generated = 'ephemeral_size' in kwargs + + #Generate images with specified size right on volume + if generated and size: + libvirt_utils.create_lvm_image(self.vg, self.lv, + size, sparse=self.sparse) + with self.remove_volume_on_error(self.path): + prepare_template(target=self.path, *args, **kwargs) + else: + prepare_template(target=base, *args, **kwargs) + with self.remove_volume_on_error(self.path): + create_lvm_image(base, size) + + @contextlib.contextmanager + def remove_volume_on_error(self, path): + try: + yield + except Exception: + with excutils.save_and_reraise_exception(): + libvirt_utils.remove_logical_volumes(path) + + +class Backend(object): + def __init__(self, use_cow): + self.BACKEND = { + 'raw': Raw, + 'qcow2': Qcow2, + 'lvm': Lvm, + 'default': Qcow2 if use_cow else Raw + } + + def image(self, instance, name, + suffix=None, image_type=None): + """Constructs image for selected backend + + :instance: Instance name. + :name: Image name. + :suffix: Suffix for image name (optional). + :image_type: Image type. + Optional, is FLAGS.libvirt_images_type by default. + """ + if not image_type: + image_type = FLAGS.libvirt_images_type + image = self.BACKEND.get(image_type) + if not image: + raise RuntimeError(_('Unknown image_type=%s') % image_type) + return image(instance, name, suffix) diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index 735957b29..363c692aa 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -90,6 +90,61 @@ def create_cow_image(backing_file, path): 'backing_file=%s' % backing_file, path) +def create_lvm_image(vg, lv, size, sparse=False): + """Create LVM image. + + Creates a LVM image with given size. + + :param vg: existing volume group which should hold this image + :param lv: name for this image (logical volume) + :size: size of image in bytes + :sparse: create sparse logical volume + """ + free_space = volume_group_free_space(vg) + + def check_size(size): + if size > free_space: + raise RuntimeError(_('Insufficient Space on Volume Group %(vg)s.' + ' Only %(free_space)db available,' + ' but %(size)db required' + ' by volume %(lv)s.') % locals()) + + if sparse: + preallocated_space = 64 * 1024 * 1024 + check_size(preallocated_space) + if free_space < size: + LOG.warning(_('Volume group %(vg)s will not be able' + ' to hold sparse volume %(lv)s.' + ' Virtual volume size is %(size)db,' + ' but free space on volume group is' + ' only %(free_space)db.') % locals()) + + cmd = ('lvcreate', '-L', '%db' % preallocated_space, + '--virtualsize', '%db' % size, '-n', lv, vg) + else: + check_size(size) + cmd = ('lvcreate', '-L', '%db' % size, '-n', lv, vg) + execute(*cmd, run_as_root=True, attempts=3) + + +def volume_group_free_space(vg): + """Return available space on volume group in bytes. + + :param vg: volume group name + """ + out, err = execute('vgs', '--noheadings', '--nosuffix', + '--units', 'b', '-o', 'vg_free', vg, + run_as_root=True) + return int(out.strip()) + + +def remove_logical_volumes(*paths): + """Remove one or more logical volume.""" + if paths: + lvremove = ('lvremove', '-f') + paths + execute(*lvremove, attempts=3, run_as_root=True) + + def get_disk_size(path): """Get the (virtual) size of a disk image |
