
It might be useful to upload image from stdin. Disadvantage is, that it is not possible to calculate checksums and this will prohibit threaded image upload when using swift and tasks. Additionally fix checksum validation during image creation - when create_image is not called through cloud layer - the result might be different, when checksums of existing image match (due to call and return cloud.get_image). Fixing this requires also completing image v1 (proper find). Yeah, lots of tests are affected by that change. Required-by: https://review.opendev.org/#/c/650374 Change-Id: I709d8b48cb7867fd806e2f19781bb84739363843
210 lines
8.4 KiB
Python
210 lines
8.4 KiB
Python
# 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.
|
|
import warnings
|
|
|
|
from openstack.cloud import exc
|
|
from openstack.image import _base_proxy
|
|
from openstack.image.v1 import image as _image
|
|
|
|
|
|
class Proxy(_base_proxy.BaseImageProxy):
|
|
|
|
def _create_image(self, **kwargs):
|
|
"""Create image resource from attributes
|
|
"""
|
|
return self._create(_image.Image, **kwargs)
|
|
|
|
def upload_image(self, **attrs):
|
|
"""Upload a new image from attributes
|
|
|
|
.. warning:
|
|
This method is deprecated - and also doesn't work very well.
|
|
Please stop using it immediately and switch to
|
|
`create_image`.
|
|
|
|
:param dict attrs: Keyword arguments which will be used to create
|
|
a :class:`~openstack.image.v1.image.Image`,
|
|
comprised of the properties on the Image class.
|
|
|
|
:returns: The results of image creation
|
|
:rtype: :class:`~openstack.image.v1.image.Image`
|
|
"""
|
|
warnings.warn("upload_image is deprecated. Use create_image instead.")
|
|
return self._create(_image.Image, **attrs)
|
|
|
|
def _upload_image(
|
|
self, name, filename, data, meta, wait, timeout, **image_kwargs):
|
|
# NOTE(mordred) wait and timeout parameters are unused, but
|
|
# are present for ease at calling site.
|
|
if filename and not data:
|
|
image_data = open(filename, 'rb')
|
|
else:
|
|
image_data = data
|
|
image_kwargs['properties'].update(meta)
|
|
image_kwargs['name'] = name
|
|
|
|
# TODO(mordred) Convert this to use image Resource
|
|
image = self._connection._get_and_munchify(
|
|
'image',
|
|
self.post('/images', json=image_kwargs))
|
|
checksum = image_kwargs['properties'].get(self._IMAGE_MD5_KEY, '')
|
|
|
|
try:
|
|
# Let us all take a brief moment to be grateful that this
|
|
# is not actually how OpenStack APIs work anymore
|
|
headers = {
|
|
'x-glance-registry-purge-props': 'false',
|
|
}
|
|
if checksum:
|
|
headers['x-image-meta-checksum'] = checksum
|
|
|
|
image = self._connection._get_and_munchify(
|
|
'image',
|
|
self.put(
|
|
'/images/{id}'.format(id=image.id),
|
|
headers=headers, data=image_data))
|
|
|
|
except exc.OpenStackCloudHTTPError:
|
|
self.log.debug(
|
|
"Deleting failed upload of image %s", name)
|
|
try:
|
|
self.delete('/images/{id}'.format(id=image.id))
|
|
except exc.OpenStackCloudHTTPError:
|
|
# We're just trying to clean up - if it doesn't work - shrug
|
|
self.log.warning(
|
|
"Failed deleting image after we failed uploading it.",
|
|
exc_info=True)
|
|
raise
|
|
return self._connection._normalize_image(image)
|
|
|
|
def _update_image_properties(self, image, meta, properties):
|
|
properties.update(meta)
|
|
img_props = {}
|
|
for k, v in iter(properties.items()):
|
|
if image.properties.get(k, None) != v:
|
|
img_props['x-image-meta-{key}'.format(key=k)] = v
|
|
if not img_props:
|
|
return False
|
|
self.put(
|
|
'/images/{id}'.format(id=image.id), headers=img_props)
|
|
self._connection.list_images.invalidate(self._connection)
|
|
return True
|
|
|
|
def _existing_image(self, **kwargs):
|
|
return _image.Image.existing(connection=self._connection, **kwargs)
|
|
|
|
def delete_image(self, image, ignore_missing=True):
|
|
"""Delete an image
|
|
|
|
:param image: The value can be either the ID of an image or a
|
|
:class:`~openstack.image.v1.image.Image` instance.
|
|
:param bool ignore_missing: When set to ``False``
|
|
:class:`~openstack.exceptions.ResourceNotFound` will be
|
|
raised when the image does not exist.
|
|
When set to ``True``, no exception will be set when
|
|
attempting to delete a nonexistent image.
|
|
|
|
:returns: ``None``
|
|
"""
|
|
self._delete(_image.Image, image, ignore_missing=ignore_missing)
|
|
|
|
def find_image(self, name_or_id, ignore_missing=True):
|
|
"""Find a single image
|
|
|
|
:param name_or_id: The name or ID of a image.
|
|
:param bool ignore_missing: When set to ``False``
|
|
:class:`~openstack.exceptions.ResourceNotFound` will be
|
|
raised when the resource does not exist.
|
|
When set to ``True``, None will be returned when
|
|
attempting to find a nonexistent resource.
|
|
:returns: One :class:`~openstack.image.v1.image.Image` or None
|
|
"""
|
|
return self._find(_image.Image, name_or_id,
|
|
ignore_missing=ignore_missing)
|
|
|
|
def get_image(self, image):
|
|
"""Get a single image
|
|
|
|
:param image: The value can be the ID of an image or a
|
|
:class:`~openstack.image.v1.image.Image` instance.
|
|
|
|
:returns: One :class:`~openstack.image.v1.image.Image`
|
|
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
|
when no resource can be found.
|
|
"""
|
|
return self._get(_image.Image, image)
|
|
|
|
def images(self, **query):
|
|
"""Return a generator of images
|
|
|
|
:param kwargs query: Optional query parameters to be sent to limit
|
|
the resources being returned.
|
|
|
|
:returns: A generator of image objects
|
|
:rtype: :class:`~openstack.image.v1.image.Image`
|
|
"""
|
|
return self._list(_image.Image, base_path='/images/detail', **query)
|
|
|
|
def update_image(self, image, **attrs):
|
|
"""Update a image
|
|
|
|
:param image: Either the ID of a image or a
|
|
:class:`~openstack.image.v1.image.Image` instance.
|
|
:attrs kwargs: The attributes to update on the image represented
|
|
by ``value``.
|
|
|
|
:returns: The updated image
|
|
:rtype: :class:`~openstack.image.v1.image.Image`
|
|
"""
|
|
return self._update(_image.Image, image, **attrs)
|
|
|
|
def download_image(self, image, stream=False, output=None,
|
|
chunk_size=1024):
|
|
"""Download an image
|
|
|
|
This will download an image to memory when ``stream=False``, or allow
|
|
streaming downloads using an iterator when ``stream=True``.
|
|
For examples of working with streamed responses, see
|
|
:ref:`download_image-stream-true`.
|
|
|
|
:param image: The value can be either the ID of an image or a
|
|
:class:`~openstack.image.v2.image.Image` instance.
|
|
|
|
:param bool stream: When ``True``, return a :class:`requests.Response`
|
|
instance allowing you to iterate over the
|
|
response data stream instead of storing its entire
|
|
contents in memory. See
|
|
:meth:`requests.Response.iter_content` for more
|
|
details. *NOTE*: If you do not consume
|
|
the entirety of the response you must explicitly
|
|
call :meth:`requests.Response.close` or otherwise
|
|
risk inefficiencies with the ``requests``
|
|
library's handling of connections.
|
|
|
|
|
|
When ``False``, return the entire
|
|
contents of the response.
|
|
:param output: Either a file object or a path to store data into.
|
|
:param int chunk_size: size in bytes to read from the wire and buffer
|
|
at one time. Defaults to 1024
|
|
|
|
:returns: When output is not given - the bytes comprising the given
|
|
Image when stream is False, otherwise a :class:`requests.Response`
|
|
instance. When output is given - a
|
|
:class:`~openstack.image.v2.image.Image` instance.
|
|
"""
|
|
|
|
image = self._get_resource(_image.Image, image)
|
|
|
|
return image.download(
|
|
self, stream=stream, output=output, chunk_size=chunk_size)
|