It can't delete snapshots yet because Vitastor layer merge isn't implemented yet. You can only delete volumes with all snapshots. This will be fixed in the near future.
288 lines
14 KiB
Diff
288 lines
14 KiB
Diff
diff --git a/nova/virt/image/model.py b/nova/virt/image/model.py
|
|
index 971f7e9c07..70ed70d5e2 100644
|
|
--- a/nova/virt/image/model.py
|
|
+++ b/nova/virt/image/model.py
|
|
@@ -129,3 +129,22 @@ class RBDImage(Image):
|
|
self.user = user
|
|
self.password = password
|
|
self.servers = servers
|
|
+
|
|
+
|
|
+class VitastorImage(Image):
|
|
+ """Class for images in a remote Vitastor cluster"""
|
|
+
|
|
+ def __init__(self, name, etcd_address = None, etcd_prefix = None, config_path = None):
|
|
+ """Create a new Vitastor image object
|
|
+
|
|
+ :param name: name of the image
|
|
+ :param etcd_address: etcd URL(s) (optional)
|
|
+ :param etcd_prefix: etcd prefix (optional)
|
|
+ :param config_path: path to the configuration (optional)
|
|
+ """
|
|
+ super(RBDImage, self).__init__(FORMAT_RAW)
|
|
+
|
|
+ self.name = name
|
|
+ self.etcd_address = etcd_address
|
|
+ self.etcd_prefix = etcd_prefix
|
|
+ self.config_path = config_path
|
|
diff --git a/nova/virt/images.py b/nova/virt/images.py
|
|
index 5358f3766a..ebe3d6effb 100644
|
|
--- a/nova/virt/images.py
|
|
+++ b/nova/virt/images.py
|
|
@@ -41,7 +41,7 @@ IMAGE_API = glance.API()
|
|
|
|
def qemu_img_info(path, format=None):
|
|
"""Return an object containing the parsed output from qemu-img info."""
|
|
- if not os.path.exists(path) and not path.startswith('rbd:'):
|
|
+ if not os.path.exists(path) and not path.startswith('rbd:') and not path.startswith('vitastor:'):
|
|
raise exception.DiskNotFound(location=path)
|
|
|
|
info = nova.privsep.qemu.unprivileged_qemu_img_info(path, format=format)
|
|
@@ -50,7 +50,7 @@ def qemu_img_info(path, format=None):
|
|
|
|
def privileged_qemu_img_info(path, format=None, output_format='json'):
|
|
"""Return an object containing the parsed output from qemu-img info."""
|
|
- if not os.path.exists(path) and not path.startswith('rbd:'):
|
|
+ if not os.path.exists(path) and not path.startswith('rbd:') and not path.startswith('vitastor:'):
|
|
raise exception.DiskNotFound(location=path)
|
|
|
|
info = nova.privsep.qemu.privileged_qemu_img_info(path, format=format)
|
|
diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py
|
|
index f9475776b3..51573fe41d 100644
|
|
--- a/nova/virt/libvirt/config.py
|
|
+++ b/nova/virt/libvirt/config.py
|
|
@@ -1060,6 +1060,8 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
|
|
self.driver_iommu = False
|
|
self.source_path = None
|
|
self.source_protocol = None
|
|
+ self.source_query = None
|
|
+ self.source_config = None
|
|
self.source_name = None
|
|
self.source_hosts = []
|
|
self.source_ports = []
|
|
@@ -1186,7 +1188,8 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
|
|
elif self.source_type == "mount":
|
|
dev.append(etree.Element("source", dir=self.source_path))
|
|
elif self.source_type == "network" and self.source_protocol:
|
|
- source = etree.Element("source", protocol=self.source_protocol)
|
|
+ source = etree.Element("source", protocol=self.source_protocol,
|
|
+ query=self.source_query, config=self.source_config)
|
|
if self.source_name is not None:
|
|
source.set('name', self.source_name)
|
|
hosts_info = zip(self.source_hosts, self.source_ports)
|
|
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
|
|
index 391231c527..34dc60dcdd 100644
|
|
--- a/nova/virt/libvirt/driver.py
|
|
+++ b/nova/virt/libvirt/driver.py
|
|
@@ -179,6 +179,7 @@ VOLUME_DRIVERS = {
|
|
'local': 'nova.virt.libvirt.volume.volume.LibvirtVolumeDriver',
|
|
'fake': 'nova.virt.libvirt.volume.volume.LibvirtFakeVolumeDriver',
|
|
'rbd': 'nova.virt.libvirt.volume.net.LibvirtNetVolumeDriver',
|
|
+ 'vitastor': 'nova.virt.libvirt.volume.vitastor.LibvirtVitastorVolumeDriver',
|
|
'nfs': 'nova.virt.libvirt.volume.nfs.LibvirtNFSVolumeDriver',
|
|
'smbfs': 'nova.virt.libvirt.volume.smbfs.LibvirtSMBFSVolumeDriver',
|
|
'fibre_channel': 'nova.virt.libvirt.volume.fibrechannel.LibvirtFibreChannelVolumeDriver', # noqa:E501
|
|
@@ -385,10 +386,10 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
# This prevents the risk of one test setting a capability
|
|
# which bleeds over into other tests.
|
|
|
|
- # LVM and RBD require raw images. If we are not configured to
|
|
+ # LVM, RBD, Vitastor require raw images. If we are not configured to
|
|
# force convert images into raw format, then we _require_ raw
|
|
# images only.
|
|
- raw_only = ('rbd', 'lvm')
|
|
+ raw_only = ('rbd', 'lvm', 'vitastor')
|
|
requires_raw_image = (CONF.libvirt.images_type in raw_only and
|
|
not CONF.force_raw_images)
|
|
requires_ploop_image = CONF.libvirt.virt_type == 'parallels'
|
|
@@ -775,12 +776,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
# Some imagebackends are only able to import raw disk images,
|
|
# and will fail if given any other format. See the bug
|
|
# https://bugs.launchpad.net/nova/+bug/1816686 for more details.
|
|
- if CONF.libvirt.images_type in ('rbd',):
|
|
+ if CONF.libvirt.images_type in ('rbd', 'vitastor'):
|
|
if not CONF.force_raw_images:
|
|
msg = _("'[DEFAULT]/force_raw_images = False' is not "
|
|
- "allowed with '[libvirt]/images_type = rbd'. "
|
|
+ "allowed with '[libvirt]/images_type = rbd' or 'vitastor'. "
|
|
"Please check the two configs and if you really "
|
|
- "do want to use rbd as images_type, set "
|
|
+ "do want to use rbd or vitastor as images_type, set "
|
|
"force_raw_images to True.")
|
|
raise exception.InvalidConfiguration(msg)
|
|
|
|
@@ -2603,6 +2604,16 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
if connection_info['data'].get('auth_enabled'):
|
|
username = connection_info['data']['auth_username']
|
|
path = f"rbd:{volume_name}:id={username}"
|
|
+ elif connection_info['driver_volume_type'] == 'vitastor':
|
|
+ volume_name = connection_info['data']['name']
|
|
+ path = 'vitastor:image='+volume_name.replace(':', '\\:')
|
|
+ for k in [ 'config_path', 'etcd_address', 'etcd_prefix' ]:
|
|
+ if k in connection_info['data']:
|
|
+ kk = k
|
|
+ if kk == 'etcd_address':
|
|
+ # FIXME use etcd_address in qemu driver
|
|
+ kk = 'etcd_host'
|
|
+ path += ":"+kk+"="+connection_info['data'][k].replace(':', '\\:')
|
|
else:
|
|
path = 'unknown'
|
|
raise exception.DiskNotFound(location='unknown')
|
|
@@ -2827,8 +2838,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
|
|
image_format = CONF.libvirt.snapshot_image_format or source_type
|
|
|
|
- # NOTE(bfilippov): save lvm and rbd as raw
|
|
- if image_format == 'lvm' or image_format == 'rbd':
|
|
+ # NOTE(bfilippov): save lvm and rbd and vitastor as raw
|
|
+ if image_format == 'lvm' or image_format == 'rbd' or image_format == 'vitastor':
|
|
image_format = 'raw'
|
|
|
|
metadata = self._create_snapshot_metadata(instance.image_meta,
|
|
@@ -2899,7 +2910,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
expected_state=task_states.IMAGE_UPLOADING)
|
|
|
|
# TODO(nic): possibly abstract this out to the root_disk
|
|
- if source_type == 'rbd' and live_snapshot:
|
|
+ if (source_type == 'rbd' or source_type == 'vitastor') and live_snapshot:
|
|
# Standard snapshot uses qemu-img convert from RBD which is
|
|
# not safe to run with live_snapshot.
|
|
live_snapshot = False
|
|
@@ -4099,7 +4110,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
# cleanup rescue volume
|
|
lvm.remove_volumes([lvmdisk for lvmdisk in self._lvm_disks(instance)
|
|
if lvmdisk.endswith('.rescue')])
|
|
- if CONF.libvirt.images_type == 'rbd':
|
|
+ if CONF.libvirt.images_type == 'rbd' or CONF.libvirt.images_type == 'vitastor':
|
|
filter_fn = lambda disk: (disk.startswith(instance.uuid) and
|
|
disk.endswith('.rescue'))
|
|
rbd_utils.RBDDriver().cleanup_volumes(filter_fn)
|
|
@@ -4356,6 +4367,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
# TODO(mikal): there is a bug here if images_type has
|
|
# changed since creation of the instance, but I am pretty
|
|
# sure that this bug already exists.
|
|
+ if CONF.libvirt.images_type == 'vitastor':
|
|
+ return 'vitastor'
|
|
return 'rbd' if CONF.libvirt.images_type == 'rbd' else 'raw'
|
|
|
|
@staticmethod
|
|
@@ -4764,10 +4777,10 @@ class LibvirtDriver(driver.ComputeDriver):
|
|
finally:
|
|
# NOTE(mikal): if the config drive was imported into RBD,
|
|
# then we no longer need the local copy
|
|
- if CONF.libvirt.images_type == 'rbd':
|
|
+ if CONF.libvirt.images_type == 'rbd' or CONF.libvirt.images_type == 'vitastor':
|
|
LOG.info('Deleting local config drive %(path)s '
|
|
- 'because it was imported into RBD.',
|
|
- {'path': config_disk_local_path},
|
|
+ 'because it was imported into %(type).',
|
|
+ {'path': config_disk_local_path, 'type': CONF.libvirt.images_type},
|
|
instance=instance)
|
|
os.unlink(config_disk_local_path)
|
|
|
|
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
|
|
index da2a6e8b8a..52c02e72f1 100644
|
|
--- a/nova/virt/libvirt/utils.py
|
|
+++ b/nova/virt/libvirt/utils.py
|
|
@@ -340,6 +340,10 @@ def find_disk(guest: libvirt_guest.Guest) -> ty.Tuple[str, ty.Optional[str]]:
|
|
disk_path = disk.source_name
|
|
if disk_path:
|
|
disk_path = 'rbd:' + disk_path
|
|
+ elif not disk_path and disk.source_protocol == 'vitastor':
|
|
+ disk_path = disk.source_name
|
|
+ if disk_path:
|
|
+ disk_path = 'vitastor:' + disk_path
|
|
|
|
if not disk_path:
|
|
raise RuntimeError(_("Can't retrieve root device path "
|
|
@@ -354,6 +358,8 @@ def get_disk_type_from_path(path: str) -> ty.Optional[str]:
|
|
return 'lvm'
|
|
elif path.startswith('rbd:'):
|
|
return 'rbd'
|
|
+ elif path.startswith('vitastor:'):
|
|
+ return 'vitastor'
|
|
elif (os.path.isdir(path) and
|
|
os.path.exists(os.path.join(path, "DiskDescriptor.xml"))):
|
|
return 'ploop'
|
|
diff --git a/nova/virt/libvirt/volume/vitastor.py b/nova/virt/libvirt/volume/vitastor.py
|
|
new file mode 100644
|
|
index 0000000000..0256df62c1
|
|
--- /dev/null
|
|
+++ b/nova/virt/libvirt/volume/vitastor.py
|
|
@@ -0,0 +1,75 @@
|
|
+# Copyright (c) 2021+, Vitaliy Filippov <vitalif@yourcmc.ru>
|
|
+#
|
|
+# 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.
|
|
+
|
|
+from os_brick import exception as os_brick_exception
|
|
+from os_brick import initiator
|
|
+from os_brick.initiator import connector
|
|
+from oslo_log import log as logging
|
|
+
|
|
+import nova.conf
|
|
+from nova import utils
|
|
+from nova.virt.libvirt.volume import volume as libvirt_volume
|
|
+
|
|
+
|
|
+CONF = nova.conf.CONF
|
|
+LOG = logging.getLogger(__name__)
|
|
+
|
|
+
|
|
+class LibvirtVitastorVolumeDriver(libvirt_volume.LibvirtBaseVolumeDriver):
|
|
+ """Driver to attach Vitastor volumes to libvirt."""
|
|
+ def __init__(self, host):
|
|
+ super(LibvirtVitastorVolumeDriver, self).__init__(host, is_block_dev=False)
|
|
+
|
|
+ def connect_volume(self, connection_info, instance):
|
|
+ pass
|
|
+
|
|
+ def disconnect_volume(self, connection_info, instance):
|
|
+ pass
|
|
+
|
|
+ def get_config(self, connection_info, disk_info):
|
|
+ """Returns xml for libvirt."""
|
|
+ conf = super(LibvirtVitastorVolumeDriver, self).get_config(connection_info, disk_info)
|
|
+ conf.source_type = 'network'
|
|
+ conf.source_protocol = 'vitastor'
|
|
+ conf.source_name = connection_info['data'].get('name')
|
|
+ conf.source_query = connection_info['data'].get('etcd_prefix') or None
|
|
+ conf.source_config = connection_info['data'].get('config_path') or None
|
|
+ conf.source_hosts = []
|
|
+ conf.source_ports = []
|
|
+ addresses = connection_info['data'].get('etcd_address', '')
|
|
+ if addresses:
|
|
+ if not isinstance(addresses, list):
|
|
+ addresses = addresses.split(',')
|
|
+ for addr in addresses:
|
|
+ if addr.startswith('https://'):
|
|
+ raise NotImplementedError('Vitastor block driver does not support SSL for etcd communication yet')
|
|
+ if addr.startswith('http://'):
|
|
+ addr = addr[7:]
|
|
+ addr = addr.rstrip('/')
|
|
+ if addr.endswith('/v3'):
|
|
+ addr = addr[0:-3]
|
|
+ p = addr.find('/')
|
|
+ if p > 0:
|
|
+ raise NotImplementedError('libvirt does not support custom URL paths for Vitastor etcd yet. Use /etc/vitastor/vitastor.conf')
|
|
+ p = addr.find(':')
|
|
+ port = '2379'
|
|
+ if p > 0:
|
|
+ port = addr[p+1:]
|
|
+ addr = addr[0:p]
|
|
+ conf.source_hosts.append(addr)
|
|
+ conf.source_ports.append(port)
|
|
+ return conf
|
|
+
|
|
+ def extend_volume(self, connection_info, instance, requested_size):
|
|
+ raise NotImplementedError
|