Add glance image import support

Interoperable image import process are introduced in the Image API v2.6. It
mainly allow image importing from an external url and let Image Service
download it by itself without sending binary data at image creation.

This commit will add a method import_image in both Image resource and proxy.

I also add a simple create_image proxy function for creating an image without
uploading it's data, as it is not required in glance API.

Change-Id: Idee9bea3f1f5db412e0ecd2105adff316aef4c4b
Story: 2005085
Task: 29671
This commit is contained in:
Victor Coutellier 2019-02-25 15:53:00 +01:00 committed by Monty Taylor
parent c2b75dc6ea
commit de8a882ce7
8 changed files with 139 additions and 3 deletions

View File

@ -32,6 +32,20 @@ Create an image by uploading its data and setting its attributes.
Full example: `image resource create`_
Create Image via interoperable image import process
---------------------------------------------------
Create an image then use interoperable image import process to download data
from a web URL.
For more information about the image import process, please check
`interoperable image import`_
.. literalinclude:: ../examples/image/import.py
:pyobject: import_image
Full example: `image resource import`_
.. _download_image-stream-true:
Downloading an Image with stream=True
@ -76,6 +90,8 @@ Delete an image.
Full example: `image resource delete`_
.. _image resource create: http://git.openstack.org/cgit/openstack/openstacksdk/tree/examples/image/create.py
.. _image resource import: http://git.openstack.org/cgit/openstack/openstacksdk/tree/examples/image/import.py
.. _image resource delete: http://git.openstack.org/cgit/openstack/openstacksdk/tree/examples/image/delete.py
.. _image resource list: http://git.openstack.org/cgit/openstack/openstacksdk/tree/examples/image/list.py
.. _image resource download: http://git.openstack.org/cgit/openstack/openstacksdk/tree/examples/image/download.py
.. _interoperable image import: https://docs.openstack.org/glance/latest/admin/interoperable-image-import.html

View File

@ -17,6 +17,8 @@ Image Operations
.. autoclass:: openstack.image.v2._proxy.Proxy
.. automethod:: openstack.image.v2._proxy.Proxy.create_image
.. automethod:: openstack.image.v2._proxy.Proxy.import_image
.. automethod:: openstack.image.v2._proxy.Proxy.upload_image
.. automethod:: openstack.image.v2._proxy.Proxy.download_image
.. automethod:: openstack.image.v2._proxy.Proxy.update_image

38
examples/image/import.py Normal file
View File

@ -0,0 +1,38 @@
# 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 examples.connect import EXAMPLE_IMAGE_NAME
"""
Create resources with the Image service.
For a full guide see
http://developer.openstack.org/sdks/python/openstacksdk/user/guides/image.html
"""
def import_image(conn):
print("Import Image:")
# Url where glance can download the image
uri = 'https://download.cirros-cloud.net/0.4.0/' \
'cirros-0.4.0-x86_64-disk.img'
# Build the image attributes and import the image.
image_attrs = {
'name': EXAMPLE_IMAGE_NAME,
'disk_format': 'qcow2',
'container_format': 'bare',
'visibility': 'public',
}
image = conn.image.create_image(**image_attrs)
conn.image.import_image(image, method="web-download", uri=uri)

View File

@ -33,9 +33,38 @@ _INT_PROPERTIES = ('min_disk', 'min_ram', 'size', 'virtual_size')
class Proxy(_base_proxy.BaseImageProxy):
def import_image(self, image, method='glance-direct', uri=None):
"""Import data to an existing image
Interoperable image import process are introduced in the Image API
v2.6. It mainly allow image importing from an external url and let
Image Service download it by itself without sending binary data at
image creation.
:param image: 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.
:param uri: Required only if using the web-download import method.
This url is where the data is made available to the Image
service.
:returns: None
"""
image = self._get_resource(_image.Image, image)
# as for the standard image upload function, container_format and
# 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")
image.import_image(self, method=method, uri=uri)
def upload_image(self, container_format=None, disk_format=None,
data=None, **attrs):
"""Upload a new image from attributes
"""Create and upload a new image from attributes
.. warning:
This method is deprecated - and also doesn't work very well.

View File

@ -249,6 +249,18 @@ class Image(resource.Resource, resource.TagMixin):
headers={"Content-Type": "application/octet-stream",
"Accept": ""})
def import_image(self, session, method='glance-direct', uri=None):
"""Import Image via interoperable image import process"""
url = utils.urljoin(self.base_path, self.id, 'import')
json = {'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"')
session.post(url, json=json)
def download(self, session, stream=False):
"""Download the data contained in an image"""
# TODO(briancurtin): This method should probably offload the get

View File

@ -239,6 +239,23 @@ class TestImage(base.TestCase):
'images/IDENTIFIER/tags/%s' % tag,
)
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")
self.sess.post.assert_called_with(
'images/IDENTIFIER/import',
json=json
)
def test_import_image_with_uri_not_web_download(self):
sot = image.Image(**EXAMPLE)
self.assertRaises(exceptions.InvalidRequest,
sot.import_image,
self.sess,
"glance-direct",
"such-a-good-uri")
def test_upload(self):
sot = image.Image(**EXAMPLE)

View File

@ -29,11 +29,26 @@ class TestImageProxy(test_proxy_base.TestProxyBase):
super(TestImageProxy, self).setUp()
self.proxy = _proxy.Proxy(self.session)
def test_image_create_no_args(self):
def test_image_import_no_required_attrs(self):
# container_format and disk_format are required attrs of the image
existing_image = image.Image(id="id")
self.assertRaises(exceptions.InvalidRequest,
self.proxy.import_image,
existing_image)
def test_image_import(self):
original_image = image.Image(**EXAMPLE)
self._verify("openstack.image.v2.image.Image.import_image",
self.proxy.import_image,
method_args=[original_image, "method", "uri"],
expected_kwargs={"method": "method",
"uri": "uri"})
def test_image_upload_no_args(self):
# container_format and disk_format are required args
self.assertRaises(exceptions.InvalidRequest, self.proxy.upload_image)
def test_image_create(self):
def test_image_upload(self):
# NOTE: This doesn't use any of the base class verify methods
# because it ends up making two separate calls to complete the
# operation.

View File

@ -0,0 +1,7 @@
---
features:
- Add ability to create image without upload data at the same time
- Add support for interoperable image import process as introduced in the
Image API v2.6 at [1]
[1]https://developer.openstack.org/api-ref/image/v2/index.html#interoperable-image-import