| Peter Pentchev | 0be59a3 | 2022-06-06 09:09:57 +0300 | [diff] [blame^] | 1 | commit 58011410d752fb7ceaf44d9c67b73940e47f70f8 | 
|  | 2 | Author: Peter Penchev <openstack-dev@storpool.com> | 
|  | 3 | Date:   Wed Apr 20 16:05:29 2022 +0300 | 
|  | 4 |  | 
|  | 5 | StorPool: implement clone_image(). | 
|  | 6 |  | 
|  | 7 | Change-Id: Idd3b79aa945a7210420a1fe57356a4dbad4f691f | 
|  | 8 |  | 
|  | 9 | diff --git a/cinder/volume/drivers/storpool.py b/cinder/volume/drivers/storpool.py | 
|  | 10 | index 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 |