Source code for hammers.osrest.glance

"""
Glance API shims. See `Glance HTTP API
<https://developer.openstack.org/api-ref/image/v2/index.html>`_
"""

import requests
import textwrap


[docs]def images(auth, query=None): """ Retrieves all images, filtered by `query`, if provided. Doesn't support pagination. Don't request too many. For querying, accepts a dictionary. If the value is a non-string iterable, the key is repeated in the query with each element in the iterable. """ response = requests.get( url=auth.endpoint('image') + '/v2/images',# + query, params=query, headers={'X-Auth-Token': auth.token}, ) response.raise_for_status() images = response.json()['images'] return images
[docs]def image(auth, id=None, name=None): """ Looks up image by `id` or `name`. If `name` is not unique given the scope of authentication (e.g. private image owned by someone else may be hidden), an error is raised. """ if id is None and name is None: raise ValueError('must specify name or id') if id is None: matches = images(auth, query={'name': name}) if len(matches) < 1: raise RuntimeError('no images found with name "{}"'.format(name)) elif len(matches) > 1: raise RuntimeError('multiple images ({}) found with name "{}"'.format(len(matches), name)) id = matches[0]['id'] response = requests.get( url=auth.endpoint('image') + '/v2/images/{}'.format(id), headers={'X-Auth-Token': auth.token}, ) response.raise_for_status() image = response.json() return image
# def image_create(auth, name, *, disk_format='qcow2', container_format='bare', visibility='private', extra=None): # <py2 kwargs compat>
[docs]def image_create(auth, name, **kwargs): """Creates empty image entry, ready to be filled by an upload command. If provided, `extra` is a mapping to set custom properties.""" disk_format = kwargs.get('disk_format', 'qcow2') container_format = kwargs.get('container_format', 'bare') visibility = kwargs.get('visibility', 'private') extra = kwargs.get('extra', None) # </py2 kwargs compat> if extra is None: extra = {} data = { 'name': name, 'disk_format': disk_format, 'container_format': container_format, 'visibility': visibility, # **extra, } data.update(extra) response = requests.post( url=auth.endpoint('image') + '/v2/images', headers={ 'X-Auth-Token': auth.token, }, json=data, ) response.raise_for_status() return response.json()
[docs]def image_delete(auth, id): """Deletes image by ID""" response = requests.delete( url=auth.endpoint('image') + '/v2/images/{}'.format(id), headers={ 'X-Auth-Token': auth.token, }, ) response.raise_for_status() return response
def image_tag(auth, id, tag): response = requests.put( url=auth.endpoint('image') + '/v2/images/{}/tags/{}'.format(id, tag), headers={'X-Auth-Token': auth.token}, ) response.raise_for_status() def image_untag(auth, id, tag): response = requests.delete( url=auth.endpoint('image') + '/v2/images/{}/tags/{}'.format(id, tag), headers={'X-Auth-Token': auth.token}, ) response.raise_for_status() #def image_properties(auth, id, *, add=None, remove=None, replace=None): # <py2 kwargs compat>
[docs]def image_properties(auth, id, **kwargs): """ Add/remove/replace properties on the image. Some standard properties can be modified (name, visibility), some can't (id, checksum), but custom fields can be whatever. :param mapping add: properties to add :param iterable remove: properties to delete :param mapping replace: properties to replace by key """ add = kwargs.get('add', None) remove = kwargs.get('remove', None) replace = kwargs.get('replace', None) # </py2 kwargs compat> patch = [] if add is not None: for key, value in add.items(): patch.append({'op': 'add', 'path': '/{}'.format(key), 'value': value}) if remove is not None: for key in remove: patch.append({'op': 'remove', 'path': '/{}'.format(key)}) if replace is not None: for key, value in replace.items(): patch.append({'op': 'replace', 'path': '/{}'.format(key), 'value': value}) response = requests.patch( url=auth.endpoint('image') + '/v2/images/{}'.format(id), headers={ 'X-Auth-Token': auth.token, 'Content-Type': 'application/openstack-images-v2.1-json-patch', # subset of full JSON patch }, json=patch, ) response.raise_for_status() return response.json()
[docs]def image_upload_curl(auth, id, filepath): """Generates an cURL command to upload an image file at `filepath` to be associated with the Glance image. Includes authentication header, so is stateless and can be run most anywhere.""" return textwrap.dedent('''\ curl -i -X PUT -H "X-Auth-Token: {token}" \ -H "Content-Type: application/octet-stream" \ -H "Connection: keep-alive" \ -T "{filepath}" \ {url}'''.format( token=auth.token, filepath=filepath, url=auth.endpoint('image') + '/v2/images/{}/file'.format(id), ))
[docs]def image_download_curl(auth, id, filepath=None): """Generates a cURL command to download an image file to `filepath`. If `filepath` is not provided, dumps to ``~/<image name>.img``. The request is authenticated, so can be run most anywhere.""" if filepath is None: image = glance_image(auth, id) filepath = '~/{}.img'.format(image['name']) return textwrap.dedent('''\ curl -D /dev/stdout -X GET -H "X-Auth-Token: {token}" \ -H "Connection: keep-alive" \ {url} \ -o {filepath}'''.format( token=auth.token, url=auth.endpoint('image') + '/v2/images/{}/file'.format(id), filepath=filepath, ))
# backwards compatible, hack-namespaced. __all__ = [ 'glance_images', 'glance_image', 'glance_image_create', 'glance_image_delete', 'glance_image_tag', 'glance_image_untag', 'glance_image_properties', 'glance_image_upload_curl', 'glance_image_download_curl', ] glance_images = images glance_image = image glance_image_create = image_create glance_image_delete = image_delete glance_image_tag = image_tag glance_image_untag = image_untag glance_image_properties = image_properties glance_image_upload_curl = image_upload_curl glance_image_download_curl = image_download_curl