image: Add missing image import options

Add support for the following options:

- remote_region
- remote_image_id
- remote_service_interface

In addition, we now return the response to the user.

Change-Id: I7ebb75896002ea8e0eca6617eb407e94050bce65
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2022-11-08 17:05:14 +00:00
parent 5106f2f49f
commit 433d97c40c
6 changed files with 110 additions and 34 deletions

View File

@ -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")
"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 []
@ -119,10 +139,16 @@ class Proxy(_base_proxy.BaseImageProxy):
if not all([image.container_format, image.disk_format]):
raise exceptions.InvalidRequest(
"Both container_format and disk_format are required for "
" importing an image")
"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,

View File

@ -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 = []

View File

@ -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(

View File

@ -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,

View File

@ -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.

View File

@ -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.