Commit b0627b52 authored by Stavros Sachtouris's avatar Stavros Sachtouris

Enrich CLI errors in "cmds.image"

Refs #21
parent 8aa5d91e
...@@ -83,22 +83,23 @@ class Generic(object): ...@@ -83,22 +83,23 @@ class Generic(object):
' kamaki config set cloud.default.token <token>', ' kamaki config set cloud.default.token <token>',
'To get current token:', 'To get current token:',
' kamaki config get cloud.default.token', ' kamaki config get cloud.default.token',
'%s' % ce, '%s %s' % (getattr(ce, 'status', ''), ce)] + CLOUDNAME)
] + CLOUDNAME)
elif ce.status in range(-12, 200) + [302, 401, 500]: elif ce.status in range(-12, 200) + [302, 401, 500]:
raise CLIError('%s' % ce, importance=3) raise CLIError(
'%s %s' % (getattr(ce, 'status', ''), ce),
importance=3)
elif ce.status == 404 and 'kamakihttpresponse' in ce_msg: elif ce.status == 404 and 'kamakihttpresponse' in ce_msg:
client = getattr(self, 'client', None) client = getattr(self, 'client', None)
if not client: if not client:
raise raise
url = getattr(client, 'endpoint_url', '<empty>') url = getattr(client, 'endpoint_url', '<empty>')
raise CLIError('Invalid service URL %s' % url, details=[ raise CLIError('Invalid service URL %s' % url, details=[
'%s' % ce,
'Check if authentication URL is correct', 'Check if authentication URL is correct',
'To check current URL', 'To check current URL',
' kamaki config get cloud.default.url', ' kamaki config get cloud.default.url',
'To set new authentication 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)
raise raise
return _raise return _raise
...@@ -363,24 +364,26 @@ class Image(object): ...@@ -363,24 +364,26 @@ class Image(object):
if image_id and ce.status in (404, 400): if image_id and ce.status in (404, 400):
raise CLIError( raise CLIError(
'No image with id %s found' % image_id, 'No image with id %s found' % image_id,
importance=2, importance=2, details=this.about_image_id + [
details=this.about_image_id + ['%s' % ce]) '%s %s' % (getattr(ce, 'status', ''), ce)])
raise raise
return _raise return _raise
@classmethod @classmethod
def metadata(this, func): def permissions(this, func):
def _raise(self, *args, **kwargs): def _raise(self, *args, **kwargs):
key = kwargs.get('key', None)
try: try:
return func(self, *args, **kwargs) func(self, *args, **kwargs)
except ClientError as ce: except ClientError as ce:
ce_msg = ('%s' % ce).lower() if ce.status in (403, 405):
if ce.status == 404 or (
ce.status == 400 and 'metadata' in ce_msg):
raise CLIError( raise CLIError(
'No properties with key %s in this image' % key, 'Insufficient permissions for this action',
details=['%s' % ce, ]) 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)])
raise raise
return _raise return _raise
......
...@@ -39,7 +39,7 @@ from pydoc import pager ...@@ -39,7 +39,7 @@ from pydoc import pager
from kamaki.cli import command from kamaki.cli import command
from kamaki.cli.cmdtree import CommandTree 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.image import ImageClient
from kamaki.clients.pithos import PithosClient from kamaki.clients.pithos import PithosClient
from kamaki.clients import ClientError from kamaki.clients import ClientError
...@@ -59,16 +59,15 @@ namespaces = [image_cmds, imagecompute_cmds] ...@@ -59,16 +59,15 @@ namespaces = [image_cmds, imagecompute_cmds]
howto_image_file = [ howto_image_file = [
'Kamaki commands to:', 'To get current user id', ' kamaki user info',
' get current user id: kamaki user info', 'To list all containers', ' kamaki container list',
' check available containers: kamaki container list', 'To create a new container', ' kamaki container create CONTAINER',
' create a new container: kamaki container create CONTAINER', 'To list container contents', ' kamaki file list /CONTAINER',
' check container contents: kamaki file list /CONTAINER', 'To upload files', ' kamaki file upload FILE /CONTAINER[/PATH]',
' upload files: kamaki file upload IMAGE_FILE /CONTAINER[/PATH]', 'To register an image',
' register an image:', ' kamaki image register --name=IMAGE_NAME --location=/CONTAINER/PATH']
' 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__) log = getLogger(__name__)
...@@ -86,7 +85,6 @@ class _ImageInit(CommandInit): ...@@ -86,7 +85,6 @@ class _ImageInit(CommandInit):
# Plankton Image Commands # Plankton Image Commands
def load_image_meta(filepath): def load_image_meta(filepath):
""" """
:param filepath: (str) the (relative) path of the metafile :param filepath: (str) the (relative) path of the metafile
...@@ -201,9 +199,8 @@ class image_list(_ImageInit, OptionalOutput, NameFilter, IDFilter): ...@@ -201,9 +199,8 @@ class image_list(_ImageInit, OptionalOutput, NameFilter, IDFilter):
filters[arg] = self[arg] filters[arg] = self[arg]
order = self['order'] order = self['order']
detail = self['detail'] or ( detail = any([self[x] for x in (
self['prop'] or self['prop_like']) or ( 'detail', 'prop', 'prop_like', 'owner', 'owner_name')])
self['owner'] or self['owner_name'])
images = self.client.list_public(detail, filters, order) images = self.client.list_public(detail, filters, order)
...@@ -213,14 +210,23 @@ class image_list(_ImageInit, OptionalOutput, NameFilter, IDFilter): ...@@ -213,14 +210,23 @@ class image_list(_ImageInit, OptionalOutput, NameFilter, IDFilter):
images = self._filter_by_properties(images) images = self._filter_by_properties(images)
images = self._filter_by_id(images) images = self._filter_by_id(images)
images = self._non_exact_name_filter(images) images = self._non_exact_name_filter(images)
for img in [] if self['output_format'] else images:
try:
img['size'] = format_size(img['size'])
except KeyError:
continue
if self['detail'] and not self['output_format']: if self['detail'] and not self['output_format']:
images = self._add_owner_name(images) images = self._add_owner_name(images)
elif detail and not self['detail']: elif detail and not self['detail']:
for img in images: for img in images:
for key in set(img).difference([ for key in set(img).difference([
'id', 'name', 'status', 'container_format', 'id',
'disk_format', 'size']): 'name',
'status',
'container_format',
'disk_format',
'size']):
img.pop(key) img.pop(key)
kwargs = dict(with_enumeration=self['enum']) kwargs = dict(with_enumeration=self['enum'])
if self['limit']: if self['limit']:
...@@ -247,7 +253,14 @@ class image_info(_ImageInit, OptionalOutput): ...@@ -247,7 +253,14 @@ class image_info(_ImageInit, OptionalOutput):
def _run(self, image_id): def _run(self, image_id):
meta = self.client.get_meta(image_id) meta = self.client.get_meta(image_id)
if not self['output_format']: if not self['output_format']:
meta['owner'] += ' (%s)' % self._uuid2username(meta['owner']) try:
meta['owner'] += ' (%s)' % self._uuid2username(meta['owner'])
except KeyError as ke:
log.debug('%s' % ke)
try:
meta['size'] = format_size(meta['size'])
except KeyError as ke:
log.debug('%s' % ke)
self.print_(meta, self.print_dict) self.print_(meta, self.print_dict)
def main(self, image_id): def main(self, image_id):
...@@ -258,7 +271,7 @@ class image_info(_ImageInit, OptionalOutput): ...@@ -258,7 +271,7 @@ class image_info(_ImageInit, OptionalOutput):
@command(image_cmds) @command(image_cmds)
class image_modify(_ImageInit): class image_modify(_ImageInit):
"""Add / update metadata and properties for an image """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( arguments = dict(
...@@ -286,6 +299,7 @@ class image_modify(_ImageInit): ...@@ -286,6 +299,7 @@ class image_modify(_ImageInit):
@errors.Generic.all @errors.Generic.all
@errors.Image.connection @errors.Image.connection
@errors.Image.permissions
@errors.Image.id @errors.Image.id
def _run(self, image_id): def _run(self, image_id):
for mid in (self['member_ID_to_add'] or []): for mid in (self['member_ID_to_add'] or []):
...@@ -362,18 +376,15 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -362,18 +376,15 @@ class image_register(_ImageInit, OptionalOutput):
"""(Re)Register an image file to an Image service """(Re)Register an image file to an Image service
The image file must be stored at a pithos repository 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 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 by the system (e.g., image id).
metadata, called properties. Custom user metadata are termed as "properties".
A register command creates a remote meta file at A register command creates a remote meta file at
/<container>/<image path>.meta /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.
images.
In case of a meta file, runtime arguments for metadata or properties In case of a meta file, runtime arguments for metadata or properties
override meta file settings. override meta file settings.
""" """
container_info_cache = {} container_info_cache = {}
arguments = dict( arguments = dict(
checksum=ValueArgument('Set image checksum', '--checksum'), checksum=ValueArgument('Set image checksum', '--checksum'),
container_format=ValueArgument( container_format=ValueArgument(
...@@ -387,7 +398,7 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -387,7 +398,7 @@ class image_register(_ImageInit, OptionalOutput):
is_public=FlagArgument('Mark image as public', '--public'), is_public=FlagArgument('Mark image as public', '--public'),
size=IntArgument('Set image size in bytes', '--size'), size=IntArgument('Set image size in bytes', '--size'),
metafile=ValueArgument( metafile=ValueArgument(
'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: {...}"}', '{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
('--metafile')), ('--metafile')),
force_upload=FlagArgument( force_upload=FlagArgument(
...@@ -402,15 +413,15 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -402,15 +413,15 @@ class image_register(_ImageInit, OptionalOutput):
uuid=ValueArgument('Custom user uuid', '--uuid'), uuid=ValueArgument('Custom user uuid', '--uuid'),
local_image_path=ValueArgument( local_image_path=ValueArgument(
'Local image file path to upload and register ' '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 )',
'--upload-image-file'), '--upload-image-file'),
progress_bar=ProgressBarArgument( progress_bar=ProgressBarArgument(
'Do not use progress bar', '--no-progress-bar', default=False), 'Do not use progress bar', '--no-progress-bar', default=False),
name=ValueArgument('The name of the new image', '--name'), name=ValueArgument('The name of the new image', '--name'),
pithos_location=PithosLocationArgument( pithos_location=PithosLocationArgument(
'The Pithos+ image location to put the image at. Format: ' 'The Pithos+ image location to put the image at. Format: '
'pithos://USER_UUID/CONTAINER/IMAGE or ' 'pithos://USER_UUID/CONTAINER/IMAGE_PATH or '
'/CONTAINER/IMAGE', '/CONTAINER/IMAGE_PATH',
'--location') '--location')
) )
required = ('name', 'pithos_location') required = ('name', 'pithos_location')
...@@ -458,10 +469,7 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -458,10 +469,7 @@ class image_register(_ImageInit, OptionalOutput):
if pithos and not self['force_upload']: if pithos and not self['force_upload']:
try: try:
pithos.get_object_info(path) pithos.get_object_info(path)
raise CLIError( raise CLIError('File already exists', importance=2, details=[
'File already exists',
importance=2,
details=[
'A remote file /%s/%s already exists' % ( 'A remote file /%s/%s already exists' % (
pithos.container, path), pithos.container, path),
'Use %s to force upload' % self.arguments[ 'Use %s to force upload' % self.arguments[
...@@ -556,8 +564,7 @@ class image_register(_ImageInit, OptionalOutput): ...@@ -556,8 +564,7 @@ class image_register(_ImageInit, OptionalOutput):
'To specify a local file:', 'To specify a local file:',
' %s [pithos://UUID]/CONTAINER[/PATH] %s LOCAL_PATH' % ( ' %s [pithos://UUID]/CONTAINER[/PATH] %s LOCAL_PATH' % (
locator.lvalue, locator.lvalue,
self.arguments['local_image_path'].lvalue) self.arguments['local_image_path'].lvalue)])
])
self.arguments['pithos_location'].setdefault( self.arguments['pithos_location'].setdefault(
'uuid', self.astakos.user_term('id')) 'uuid', self.astakos.user_term('id'))
self._run(self['name'], locator) self._run(self['name'], locator)
...@@ -569,6 +576,7 @@ class image_unregister(_ImageInit): ...@@ -569,6 +576,7 @@ class image_unregister(_ImageInit):
@errors.Generic.all @errors.Generic.all
@errors.Image.connection @errors.Image.connection
@errors.Image.permissions
@errors.Image.id @errors.Image.id
def _run(self, image_id): def _run(self, image_id):
self.client.unregister(image_id) self.client.unregister(image_id)
...@@ -680,6 +688,7 @@ class imagecompute_delete(_CycladesInit): ...@@ -680,6 +688,7 @@ class imagecompute_delete(_CycladesInit):
@errors.Generic.all @errors.Generic.all
@errors.Cyclades.connection @errors.Cyclades.connection
@errors.Image.permissions
@errors.Image.id @errors.Image.id
def _run(self, image_id): def _run(self, image_id):
self.client.delete_image(image_id) self.client.delete_image(image_id)
...@@ -705,6 +714,7 @@ class imagecompute_modify(_CycladesInit): ...@@ -705,6 +714,7 @@ class imagecompute_modify(_CycladesInit):
@errors.Generic.all @errors.Generic.all
@errors.Cyclades.connection @errors.Cyclades.connection
@errors.Image.permissions
@errors.Image.id @errors.Image.id
def _run(self, image_id): def _run(self, image_id):
if self['property_to_add']: 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