Commit b0627b52 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Enrich CLI errors in "cmds.image"

Refs #21
parent 8aa5d91e
......@@ -83,22 +83,23 @@ class Generic(object):
' kamaki config set cloud.default.token <token>',
'To get current token:',
' kamaki config get cloud.default.token',
'%s' % ce,
'%s %s' % (getattr(ce, 'status', ''), ce)] + CLOUDNAME)
elif ce.status in range(-12, 200) + [302, 401, 500]:
raise CLIError('%s' % ce, importance=3)
raise CLIError(
'%s %s' % (getattr(ce, 'status', ''), ce),
elif ce.status == 404 and 'kamakihttpresponse' in ce_msg:
client = getattr(self, 'client', None)
if not client:
url = getattr(client, 'endpoint_url', '<empty>')
raise CLIError('Invalid service URL %s' % url, details=[
'%s' % ce,
'Check if authentication URL is correct',
'To check current URL',
' kamaki config get cloud.default.url',
'To set new authentication URL',
' kamaki config set cloud.default.url'] + CLOUDNAME)
' kamaki config set cloud.default.url',
'%s %s' % (getattr(ce, 'status', ''), ce)] + CLOUDNAME)
return _raise
......@@ -363,24 +364,26 @@ class Image(object):
if image_id and ce.status in (404, 400):
raise CLIError(
'No image with id %s found' % image_id,
details=this.about_image_id + ['%s' % ce])
importance=2, details=this.about_image_id + [
'%s %s' % (getattr(ce, 'status', ''), ce)])
return _raise
def metadata(this, func):
def permissions(this, func):
def _raise(self, *args, **kwargs):
key = kwargs.get('key', None)
return func(self, *args, **kwargs)
func(self, *args, **kwargs)
except ClientError as ce:
ce_msg = ('%s' % ce).lower()
if ce.status == 404 or (
ce.status == 400 and 'metadata' in ce_msg):
if ce.status in (403, 405):
raise CLIError(
'No properties with key %s in this image' % key,
details=['%s' % ce, ])
'Insufficient permissions for this action',
importance=2, details=[
'To see the owner of an image',
' kamaki image info IMAGE_ID',
'To see image file permissions',
' kamaki file info IMAGE_LOCATION --sharing',
'%s %s' % (getattr(ce, 'status', ''), ce)])
return _raise
......@@ -39,7 +39,7 @@ from pydoc import pager
from kamaki.cli import command
from kamaki.cli.cmdtree import CommandTree
from kamaki.cli.utils import filter_dicts_by_dict
from kamaki.cli.utils import filter_dicts_by_dict, format_size
from kamaki.clients.image import ImageClient
from kamaki.clients.pithos import PithosClient
from kamaki.clients import ClientError
......@@ -59,16 +59,15 @@ namespaces = [image_cmds, imagecompute_cmds]
howto_image_file = [
'Kamaki commands to:',
' get current user id: kamaki user info',
' check available containers: kamaki container list',
' create a new container: kamaki container create CONTAINER',
' check container contents: kamaki file list /CONTAINER',
' upload files: kamaki file upload IMAGE_FILE /CONTAINER[/PATH]',
' register an image:',
'To get current user id', ' kamaki user info',
'To list all containers', ' kamaki container list',
'To create a new container', ' kamaki container create CONTAINER',
'To list container contents', ' kamaki file list /CONTAINER',
'To upload files', ' kamaki file upload FILE /CONTAINER[/PATH]',
'To register an image',
' kamaki image register --name=IMAGE_NAME --location=/CONTAINER/PATH']
about_image_id = ['To see a list of available image ids: /image list']
about_image_id = ['To list all images', ' kamaki image list']
log = getLogger(__name__)
......@@ -86,7 +85,6 @@ class _ImageInit(CommandInit):
# Plankton Image Commands
def load_image_meta(filepath):
:param filepath: (str) the (relative) path of the metafile
......@@ -201,9 +199,8 @@ class image_list(_ImageInit, OptionalOutput, NameFilter, IDFilter):
filters[arg] = self[arg]
order = self['order']
detail = self['detail'] or (
self['prop'] or self['prop_like']) or (
self['owner'] or self['owner_name'])
detail = any([self[x] for x in (
'detail', 'prop', 'prop_like', 'owner', 'owner_name')])
images = self.client.list_public(detail, filters, order)
......@@ -213,14 +210,23 @@ class image_list(_ImageInit, OptionalOutput, NameFilter, IDFilter):
images = self._filter_by_properties(images)
images = self._filter_by_id(images)
images = self._non_exact_name_filter(images)
for img in [] if self['output_format'] else images:
img['size'] = format_size(img['size'])
except KeyError:
if self['detail'] and not self['output_format']:
images = self._add_owner_name(images)
elif detail and not self['detail']:
for img in images:
for key in set(img).difference([
'id', 'name', 'status', 'container_format',
'disk_format', 'size']):
kwargs = dict(with_enumeration=self['enum'])
if self['limit']:
......@@ -247,7 +253,14 @@ class image_info(_ImageInit, OptionalOutput):
def _run(self, image_id):
meta = self.client.get_meta(image_id)
if not self['output_format']:
meta['owner'] += ' (%s)' % self._uuid2username(meta['owner'])
except KeyError as ke:
log.debug('%s' % ke)
meta['size'] = format_size(meta['size'])
except KeyError as ke:
log.debug('%s' % ke)
self.print_(meta, self.print_dict)
def main(self, image_id):
......@@ -258,7 +271,7 @@ class image_info(_ImageInit, OptionalOutput):
class image_modify(_ImageInit):
"""Add / update metadata and properties for an image
The original image preserves the values that are not affected
Preserves values not explicitly modified
arguments = dict(
......@@ -286,6 +299,7 @@ class image_modify(_ImageInit):
def _run(self, image_id):
for mid in (self['member_ID_to_add'] or []):
......@@ -362,18 +376,15 @@ class image_register(_ImageInit, OptionalOutput):
"""(Re)Register an image file to an Image service
The image file must be stored at a pithos repository
Some metadata can be set by user (e.g., disk-format) while others are set
only automatically (e.g., image id). There are also some custom user
metadata, called properties.
by the system (e.g., image id).
Custom user metadata are termed as "properties".
A register command creates a remote meta file at
/<container>/<image path>.meta
Users may download and edit this file and use it to re-register one or more
Users may download and edit this file and use it to re-register.
In case of a meta file, runtime arguments for metadata or properties
override meta file settings.
container_info_cache = {}
arguments = dict(
checksum=ValueArgument('Set image checksum', '--checksum'),
......@@ -387,7 +398,7 @@ class image_register(_ImageInit, OptionalOutput):
is_public=FlagArgument('Mark image as public', '--public'),
size=IntArgument('Set image size in bytes', '--size'),
'Load metadata from a json-formated file <img-file>.meta :'
'Load metadata from a json-formated file IMAGE_FILE.meta :'
'{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
......@@ -402,15 +413,15 @@ class image_register(_ImageInit, OptionalOutput):
uuid=ValueArgument('Custom user uuid', '--uuid'),
'Local image file path to upload and register '
'(still need target file in the form /container/remote-path )',
'(still need target file in the form /CONTAINER/REMOTE-PATH )',
'Do not use progress bar', '--no-progress-bar', default=False),
name=ValueArgument('The name of the new image', '--name'),
'The Pithos+ image location to put the image at. Format: '
required = ('name', 'pithos_location')
......@@ -458,10 +469,7 @@ class image_register(_ImageInit, OptionalOutput):
if pithos and not self['force_upload']:
raise CLIError(
'File already exists',
raise CLIError('File already exists', importance=2, details=[
'A remote file /%s/%s already exists' % (
pithos.container, path),
'Use %s to force upload' % self.arguments[
......@@ -556,8 +564,7 @@ class image_register(_ImageInit, OptionalOutput):
'To specify a local file:',
' %s [pithos://UUID]/CONTAINER[/PATH] %s LOCAL_PATH' % (
'uuid', self.astakos.user_term('id'))
self._run(self['name'], locator)
......@@ -569,6 +576,7 @@ class image_unregister(_ImageInit):
def _run(self, image_id):
......@@ -680,6 +688,7 @@ class imagecompute_delete(_CycladesInit):
def _run(self, image_id):
......@@ -705,6 +714,7 @@ class imagecompute_modify(_CycladesInit):
def _run(self, image_id):
if self['property_to_add']:
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment