summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-07-27 21:04:08 +0000
committerGerrit Code Review <review@openstack.org>2017-07-27 21:04:08 +0000
commit083931f87f3499744f9ccf33693e54a2658655f5 (patch)
tree19e6d1afeb0dc56dbba03d4783dbcc0c8da197e0
parent07076b26ab5c6287b6a276776da770647ee50922 (diff)
parentc0753bdde6c8af65b606e96a72c7cfc015ab51c8 (diff)
downloadpython-glanceclient-083931f87f3499744f9ccf33693e54a2658655f5.tar.gz
Merge "Add image import features to client"
-rw-r--r--glanceclient/v2/images.py32
-rw-r--r--glanceclient/v2/shell.py103
2 files changed, 133 insertions, 2 deletions
diff --git a/glanceclient/v2/images.py b/glanceclient/v2/images.py
index 8f8072e..d95ebca 100644
--- a/glanceclient/v2/images.py
+++ b/glanceclient/v2/images.py
@@ -218,20 +218,48 @@ class Controller(object):
return utils.IterableWithLength(body, content_length), resp
@utils.add_req_id_to_object()
- def upload(self, image_id, image_data, image_size=None):
+ def upload(self, image_id, image_data, image_size=None, u_url=None):
"""Upload the data for an image.
:param image_id: ID of the image to upload data for.
:param image_data: File-like object supplying the data to upload.
:param image_size: Unused - present for backwards compatibility
+ :param u_url: Upload url to upload the data to.
"""
- url = '/v2/images/%s/file' % image_id
+ url = u_url or '/v2/images/%s/file' % image_id
hdrs = {'Content-Type': 'application/octet-stream'}
body = image_data
resp, body = self.http_client.put(url, headers=hdrs, data=body)
return (resp, body), resp
@utils.add_req_id_to_object()
+ def get_import_info(self):
+ """Get Import info from discovery endpoint."""
+ url = '/v2/info/import'
+ resp, body = self.http_client.get(url)
+ return body, resp
+
+ @utils.add_req_id_to_object()
+ def stage(self, image_id, image_data):
+ """Upload the data to image staging.
+
+ :param image_id: ID of the image to upload data for.
+ :param image_data: File-like object supplying the data to upload.
+ """
+ url = '/v2/images/%s/stage' % image_id
+ return self.upload(image_id,
+ image_data,
+ u_url=url)
+
+ @utils.add_req_id_to_object()
+ def image_import(self, image_id, method='glance-direct'):
+ """Import Image via method."""
+ url = '/v2/images/%s/import' % image_id
+ data = {'import_method': method}
+ resp, body = self.http_client.post(url, data=data)
+ return body, resp
+
+ @utils.add_req_id_to_object()
def delete(self, image_id):
"""Delete an image."""
url = '/v2/images/%s' % image_id
diff --git a/glanceclient/v2/shell.py b/glanceclient/v2/shell.py
index 9ce6f96..87285e4 100644
--- a/glanceclient/v2/shell.py
+++ b/glanceclient/v2/shell.py
@@ -89,6 +89,56 @@ def do_image_create(gc, args):
utils.print_image(image)
+@utils.schema_args(get_image_schema, omit=['created_at', 'updated_at', 'file',
+ 'checksum', 'virtual_size', 'size',
+ 'status', 'schema', 'direct_url',
+ 'locations', 'self'])
+@utils.arg('--property', metavar="<key=value>", action='append',
+ default=[], help=_('Arbitrary property to associate with image.'
+ ' May be used multiple times.'))
+@utils.arg('--file', metavar='<FILE>',
+ help=_('Local file that contains disk image to be uploaded '
+ 'during creation. Alternatively, the image data can be '
+ 'passed to the client via stdin.'))
+@utils.arg('--progress', action='store_true', default=False,
+ help=_('Show upload progress bar.'))
+@utils.on_data_require_fields(DATA_FIELDS)
+def do_image_create_via_import(gc, args):
+ """EXPERIMENTAL: Create a new image via image import."""
+ schema = gc.schemas.get("image")
+ _args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()]
+ fields = dict(filter(lambda x: x[1] is not None and
+ (x[0] == 'property' or
+ schema.is_core_property(x[0])),
+ _args))
+
+ raw_properties = fields.pop('property', [])
+ for datum in raw_properties:
+ key, value = datum.split('=', 1)
+ fields[key] = value
+
+ file_name = fields.pop('file', None)
+ if file_name is not None and os.access(file_name, os.R_OK) is False:
+ utils.exit("File %s does not exist or user does not have read "
+ "privileges to it" % file_name)
+ import_methods = gc.images.get_import_info().get('import-methods')
+ if file_name and (not import_methods or
+ 'glance-direct' not in import_methods.get('value')):
+ utils.exit("No suitable import method available for direct upload, "
+ "please use image-create instead.")
+ image, resp = gc.images.create(**fields)
+ try:
+ if utils.get_data_file(args) is not None:
+ args.id = image['id']
+ args.size = None
+ do_image_stage(gc, args)
+ args.from_create = True
+ do_image_import(gc, args)
+ image = gc.images.get(args.id)
+ finally:
+ utils.print_image(image)
+
+
@utils.arg('id', metavar='<IMAGE_ID>', help=_('ID of image to update.'))
@utils.schema_args(get_image_schema, omit=['id', 'locations', 'created_at',
'updated_at', 'file', 'checksum',
@@ -269,6 +319,16 @@ def do_explain(gc, args):
utils.print_list(schema.properties, columns, formatters)
+def do_import_info(gc, args):
+ """Print import methods available from Glance."""
+ try:
+ import_info = gc.images.get_import_info()
+ except exc.HTTPNotFound:
+ utils.exit('Target Glance does not support Image Import workflow')
+ else:
+ utils.print_dict(import_info)
+
+
@utils.arg('--file', metavar='<FILE>',
help=_('Local file to save downloaded image data to. '
'If this is not specified and there is no redirection '
@@ -325,6 +385,49 @@ def do_image_upload(gc, args):
gc.images.upload(args.id, image_data, args.size)
+@utils.arg('--file', metavar='<FILE>',
+ help=_('Local file that contains disk image to be uploaded.'
+ ' Alternatively, images can be passed'
+ ' to the client via stdin.'))
+@utils.arg('--size', metavar='<IMAGE_SIZE>', type=int,
+ help=_('Size in bytes of image to be uploaded. Default is to get '
+ 'size from provided data object but this is supported in '
+ 'case where size cannot be inferred.'),
+ default=None)
+@utils.arg('--progress', action='store_true', default=False,
+ help=_('Show upload progress bar.'))
+@utils.arg('id', metavar='<IMAGE_ID>',
+ help=_('ID of image to upload data to.'))
+def do_image_stage(gc, args):
+ """Upload data for a specific image to staging."""
+ image_data = utils.get_data_file(args)
+ if args.progress:
+ filesize = utils.get_file_size(image_data)
+ if filesize is not None:
+ # NOTE(kragniz): do not show a progress bar if the size of the
+ # input is unknown (most likely a piped input)
+ image_data = progressbar.VerboseFileWrapper(image_data, filesize)
+ gc.images.stage(args.id, image_data, args.size)
+
+
+@utils.arg('--import-method', metavar='<METHOD>', default='glance-direct',
+ help=_('Import method used for Image Import workflow. '
+ 'Valid values can be retrieved with import-info command '
+ 'and the default "glance-direct" is used with '
+ '"image-stage".'))
+@utils.arg('id', metavar='<IMAGE_ID>',
+ help=_('ID of image to import.'))
+def do_image_import(gc, args):
+ try:
+ gc.images.image_import(args.id, args.import_method)
+ except exc.HTTPNotFound:
+ utils.exit('Target Glance does not support Image Import workflow')
+ else:
+ if not getattr(args, 'from_create', False):
+ image = gc.images.get(args.id)
+ utils.print_image(image)
+
+
@utils.arg('id', metavar='<IMAGE_ID>', nargs='+',
help=_('ID of image(s) to delete.'))
def do_image_delete(gc, args):