diff --git a/openstack/image/v2/_proxy.py b/openstack/image/v2/_proxy.py index f9e2fb314..5d7ca00fb 100644 --- a/openstack/image/v2/_proxy.py +++ b/openstack/image/v2/_proxy.py @@ -50,7 +50,14 @@ class Proxy(_base_proxy.BaseImageProxy): return self._create(_image.Image, **kwargs) def import_image( - self, image, method='glance-direct', uri=None, + self, + image, + method='glance-direct', + *, + uri=None, + remote_region=None, + remote_image_id=None, + remote_service_interface=None, store=None, stores=None, all_stores=None, @@ -67,12 +74,23 @@ class Proxy(_base_proxy.BaseImageProxy): The value can be the ID of a image or a :class:`~openstack.image.v2.image.Image` instance. :param method: - Method to use for importing the image. - A valid value is glance-direct or web-download. + Method to use for importing the image. Not all deployments support + all methods. One of: ``glance-direct`` (default), ``web-download``, + ``glance-download``, or ``copy-image``. Use of ``glance-direct`` + requires the image be first staged. :param uri: Required only if using the web-download import method. This url is where the data is made available to the Image service. + :param remote_region: + The remote glance region to download the image from when using + glance-download. + :param remote_image_id: + The ID of the image to import from the remote glance when using + glance-download. + :param remote_service_interface: + The remote glance service interface to use when using + glance-download :param store: Used when enabled_backends is activated in glance. The value can be the id of a store or a @@ -95,17 +113,19 @@ class Proxy(_base_proxy.BaseImageProxy): the stores where the data has been correctly uploaded. Default is True. - :returns: None + :returns: The raw response from the request. """ image = self._get_resource(_image.Image, image) if all_stores and (store or stores): raise exceptions.InvalidRequest( - "all_stores is mutually exclusive with" - " store and stores") + "all_stores is mutually exclusive with " + "store and stores" + ) if store is not None: if stores: raise exceptions.InvalidRequest( - "store and stores are mutually exclusive") + "store and stores are mutually exclusive" + ) store = self._get_resource(_si.Store, store) stores = stores or [] @@ -118,11 +138,17 @@ class Proxy(_base_proxy.BaseImageProxy): # disk_format are required for using image import process if not all([image.container_format, image.disk_format]): raise exceptions.InvalidRequest( - "Both container_format and disk_format are required for" - " importing an image") + "Both container_format and disk_format are required for " + "importing an image" + ) - image.import_image( - self, method=method, uri=uri, + return image.import_image( + self, + method=method, + uri=uri, + remote_region=remote_region, + remote_image_id=remote_image_id, + remote_service_interface=remote_service_interface, store=store, stores=stores, all_stores=all_stores, diff --git a/openstack/image/v2/image.py b/openstack/image/v2/image.py index 5a79c9c1c..6b71897bb 100644 --- a/openstack/image/v2/image.py +++ b/openstack/image/v2/image.py @@ -298,44 +298,66 @@ class Image(resource.Resource, tag.TagMixin, _download.DownloadMixin): self._translate_response(response, has_body=False) return self - def import_image(self, session, method='glance-direct', uri=None, - store=None, stores=None, all_stores=None, - all_stores_must_succeed=None): + def import_image( + self, + session, + method='glance-direct', + *, + uri=None, + remote_region=None, + remote_image_id=None, + remote_service_interface=None, + store=None, + stores=None, + all_stores=None, + all_stores_must_succeed=None, + ): """Import Image via interoperable image import process""" if all_stores and (store or stores): raise exceptions.InvalidRequest( - "all_stores is mutually exclusive with" - " store and stores") + 'all_stores is mutually exclusive with store and stores' + ) if store and stores: raise exceptions.InvalidRequest( - "store and stores are mutually exclusive." - " Please just use stores.") + 'store and stores are mutually exclusive. stores should be ' + 'preferred.' + ) if store: stores = [store] else: stores = stores or [] url = utils.urljoin(self.base_path, self.id, 'import') - json = {'method': {'name': method}} + data = {'method': {'name': method}} + if uri: - if method == 'web-download': - json['method']['uri'] = uri - else: - raise exceptions.InvalidRequest('URI is only supported with ' - 'method: "web-download"') + if method != 'web-download': + raise exceptions.InvalidRequest( + 'URI is only supported with method: "web-download"' + ) + data['method']['uri'] = uri + + if remote_region and remote_image_id: + if remote_service_interface: + data['method']['glance_service_interface'] = \ + remote_service_interface + data['method']['glance_region'] = remote_region + data['method']['glance_image_id'] = remote_image_id + if all_stores is not None: - json['all_stores'] = all_stores + data['all_stores'] = all_stores if all_stores_must_succeed is not None: - json['all_stores_must_succeed'] = all_stores_must_succeed + data['all_stores_must_succeed'] = all_stores_must_succeed for s in stores: - json.setdefault('stores', []) - json['stores'].append(s.id) + data.setdefault('stores', []) + data['stores'].append(s.id) headers = {} # Backward compat if store is not None: headers = {'X-Image-Meta-Store': store.id} - session.post(url, json=json, headers=headers) + + return session.post(url, json=data, headers=headers) def _consume_header_attrs(self, attrs): self.image_import_methods = [] diff --git a/openstack/tests/unit/image/v2/test_image.py b/openstack/tests/unit/image/v2/test_image.py index e8bbf45cf..4596a4821 100644 --- a/openstack/tests/unit/image/v2/test_image.py +++ b/openstack/tests/unit/image/v2/test_image.py @@ -265,7 +265,7 @@ class TestImage(base.TestCase): def test_import_image(self): sot = image.Image(**EXAMPLE) json = {"method": {"name": "web-download", "uri": "such-a-good-uri"}} - sot.import_image(self.sess, "web-download", "such-a-good-uri") + sot.import_image(self.sess, "web-download", uri="such-a-good-uri") self.sess.post.assert_called_with( 'images/IDENTIFIER/import', headers={}, @@ -293,7 +293,12 @@ class TestImage(base.TestCase): } store = mock.MagicMock() store.id = "ceph_1" - sot.import_image(self.sess, "web-download", "such-a-good-uri", store) + sot.import_image( + self.sess, + "web-download", + uri="such-a-good-uri", + store=store, + ) self.sess.post.assert_called_with( 'images/IDENTIFIER/import', headers={'X-Image-Meta-Store': 'ceph_1'}, @@ -314,7 +319,7 @@ class TestImage(base.TestCase): sot.import_image( self.sess, "web-download", - "such-a-good-uri", + uri="such-a-good-uri", stores=[store], ) self.sess.post.assert_called_with( @@ -335,7 +340,7 @@ class TestImage(base.TestCase): sot.import_image( self.sess, "web-download", - "such-a-good-uri", + uri="such-a-good-uri", all_stores=True, ) self.sess.post.assert_called_with( diff --git a/openstack/tests/unit/image/v2/test_proxy.py b/openstack/tests/unit/image/v2/test_proxy.py index f663c89ee..0775ff787 100644 --- a/openstack/tests/unit/image/v2/test_proxy.py +++ b/openstack/tests/unit/image/v2/test_proxy.py @@ -64,12 +64,18 @@ class TestImage(TestImageProxy): self._verify( "openstack.image.v2.image.Image.import_image", self.proxy.import_image, - method_args=[original_image, "method", "uri"], + method_args=[original_image, "method"], + method_kwargs={ + "uri": "uri", + }, expected_args=[self.proxy], expected_kwargs={ "method": "method", "store": None, "uri": "uri", + "remote_region": None, + "remote_image_id": None, + "remote_service_interface": None, "stores": [], "all_stores": None, "all_stores_must_succeed": None, diff --git a/releasenotes/notes/image-import-proxy-params-f19d8b6166104ebe.yaml b/releasenotes/notes/image-import-proxy-params-f19d8b6166104ebe.yaml new file mode 100644 index 000000000..56b59f4f1 --- /dev/null +++ b/releasenotes/notes/image-import-proxy-params-f19d8b6166104ebe.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + The ``openstack.image.Image.import_image`` method and ``import_image`` + image proxy method now accept the following additional paramters: + + - ``remote_region`` + - ``remote_image_id`` + - ``remote_service_interface`` + + These are required to support the ``glance-download`` image import + method. diff --git a/releasenotes/notes/image-proxy-layer-kwarg-only-arguments-94c9b2033d386160.yaml b/releasenotes/notes/image-proxy-layer-kwarg-only-arguments-94c9b2033d386160.yaml new file mode 100644 index 000000000..882bd8653 --- /dev/null +++ b/releasenotes/notes/image-proxy-layer-kwarg-only-arguments-94c9b2033d386160.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + The signatures of the ``openstack.image.v2.import_image`` has changed. All + arguments except ``image`` and ``method`` are now kwarg-only.