blob: 1ceaea78c197b6e63cfacd3ea917e29eb36f23fa [file] [log] [blame]
Peter Pentchev0be59a32022-06-06 09:09:57 +03001commit 58011410d752fb7ceaf44d9c67b73940e47f70f8
2Author: Peter Penchev <openstack-dev@storpool.com>
3Date: Wed Apr 20 16:05:29 2022 +0300
4
5 StorPool: implement clone_image().
6
7 Change-Id: Idd3b79aa945a7210420a1fe57356a4dbad4f691f
8
9diff --git a/cinder/volume/drivers/storpool.py b/cinder/volume/drivers/storpool.py
10index 25c10a100..3c8e7a5cf 100644
11--- a/cinder/volume/drivers/storpool.py
12+++ b/cinder/volume/drivers/storpool.py
13@@ -21,7 +21,9 @@ from oslo_config import cfg
14 from oslo_log import log as logging
15 from oslo_utils import excutils
16 from oslo_utils import importutils
17+from oslo_utils import netutils
18 from oslo_utils import units
19+from oslo_utils import uuidutils
20 import six
21
22 from cinder import context
23@@ -58,6 +60,28 @@ CONF = cfg.CONF
24 CONF.register_opts(storpool_opts, group=configuration.SHARED_CONF_GROUP)
25
26
27+def _extract_cinder_ids(urls):
28+ ids = []
29+ for url in urls:
30+ # The url can also be None and a TypeError is raised
31+ # TypeError: a bytes-like object is required, not 'str'
32+ if not url:
33+ continue
34+ parts = netutils.urlsplit(url)
35+ if parts.scheme == 'cinder':
36+ if parts.path:
37+ vol_id = parts.path.split('/')[-1]
38+ else:
39+ vol_id = parts.netloc
40+ if uuidutils.is_uuid_like(vol_id):
41+ ids.append(vol_id)
42+ else:
43+ LOG.debug("Ignoring malformed image location uri "
44+ "'%(url)s'", {'url': url})
45+
46+ return ids
47+
48+
49 class StorPoolConfigurationInvalid(exception.CinderException):
50 message = _("Invalid parameter %(param)s in the %(section)s section "
51 "of the /etc/storpool.conf file: %(error)s")
52@@ -96,6 +120,7 @@ class StorPoolDriver(driver.VolumeDriver):
53 connector will handle this.
54 - Drop backup_volume()
55 - Avoid data duplication in create_cloned_volume()
56+ - Implement clone_image()
57 """
58
59 VERSION = '2.0.0'
60@@ -200,6 +225,43 @@ class StorPoolDriver(driver.VolumeDriver):
61 except spapi.ApiError as e:
62 raise self._backendException(e)
63
64+ def clone_image(self, context, volume,
65+ image_location, image_meta, image_service):
66+ if (image_meta.get('container_format') != 'bare' or
67+ image_meta.get('disk_format') != 'raw'):
68+ LOG.info("Requested image %(id)s is not in raw format.",
69+ {'id': image_meta.get('id')})
70+ return None, False
71+
72+ LOG.debug('Check whether the image is accessible')
73+ visibility = image_meta.get('visibility', None)
74+ public = (
75+ visibility and visibility == 'public' or
76+ image_meta.get('is_public', False) or
77+ image_meta['owner'] == volume['project_id']
78+ )
79+ if not public:
80+ LOG.warning(
81+ 'The requested image is not accessible by the current tenant'
82+ )
83+ return None, False
84+
85+ LOG.debug('On to parsing %(loc)s', {'loc': repr(image_location)})
86+ direct_url, locations = image_location
87+ urls = list(set([direct_url] + [
88+ loc.get('url') for loc in locations or []
89+ ]))
90+ image_volume_ids = _extract_cinder_ids(urls)
91+ LOG.debug('image_volume_ids %(ids)s', {'ids': repr(image_volume_ids)})
92+
93+ if not image_volume_ids:
94+ LOG.info('No Cinder volumes found to clone')
95+ return None, False
96+
97+ vol_id = image_volume_ids[0]
98+ LOG.info('Cloning volume %(vol_id)s', {'vol_id': vol_id})
99+ return self.create_cloned_volume(volume, {'id': vol_id}), True
100+
101 def create_cloned_volume(self, volume, src_vref):
102 refname = self._attach.volumeName(src_vref['id'])
103 size = int(volume['size']) * units.Gi