Commit 7e18cd26 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Restore backwards compatibility for image register

Refs: Bug #4276

New format:

/image register Name container:path/to/image

Old format:

/image register Name pithos://user-uuid/container/path/to/image

Now, the old format is also recognized. It is planed to be depricated in a
future version, though

Also, uniformize guides and doc responses for image register
parent 4ea0d462
......@@ -225,7 +225,7 @@ Batch-create servers
cyclades_endpoints = user.get_endpoints('compute')
cyclades_endpoints = user.get_service_endpoints('compute')
CYCLADES_URL = cyclades_endpoints['publicURL']
cyclades = CycladesClient(CYCLADES_URL, TOKEN)
......@@ -255,7 +255,7 @@ Batch-create 4 servers of the same kind
cyclades_endpoints = user.get_endpoints('compute')
cyclades_endpoints = user.get_service_endpoints('compute')
CYCLADES_URL = cyclades_endpoints['publicURL']
cyclades = CycladesClient(CYCLADES_URL, TOKEN)
......@@ -282,10 +282,10 @@ Register a banch of pre-uploaded images
astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
USER_UUID = astakos.user_term('uuid')
PITHOS_URL = astakos.get_endpoints('object-store')['publicURL']
PITHOS_URL = astakos.get_service_endpoints('object-store')['publicURL']
IMAGE_URL = astakos.get_endpoints('image')['publicURL']
IMAGE_URL = astakos.get_service_endpoints('image')['publicURL']
plankton = ImageClient(IMAGE_URL, TOKEN)
for img in pithos.list_objects():
......@@ -157,7 +157,8 @@ def command(cmd_tree, prefix='', descedants_depth=1):
'No commend in %s (acts as cmd description)' % cls.__name__)
cmd_tree.add_command(cls_name, cls.description, cls)
cls_name, cls.description, cls, cls.long_description)
return cls
return wrap
......@@ -40,12 +40,15 @@ class Command(object):
subcommands = {}
help = ' '
def __init__(self, path, help=' ', subcommands={}, cmd_class=None):
def __init__(
self, path,
help=' ', subcommands={}, cmd_class=None, long_help=''):
assert path, 'Cannot initialize a command without a command path'
self.path = path = help or ''
self.subcommands = dict(subcommands) if subcommands else {}
self.cmd_class = cmd_class or None
self.long_help = '%s' % (long_help or '')
def name(self):
......@@ -109,9 +112,10 @@ class Command(object):
class CommandTree(object):
def __init__(self, name, description=''):
def __init__(self, name, description='', long_description=''): = name
self.description = description
self.long_description = '%s' % (long_description or '')
self.groups = dict()
self._all_commands = dict()
......@@ -119,7 +123,9 @@ class CommandTree(object):
for group in groups_to_exclude:
self.groups.pop(group, None)
def add_command(self, command_path, description=None, cmd_class=None):
def add_command(
self, command_path,
description=None, cmd_class=None, long_description=''):
terms = command_path.split('_')
cmd = self.groups[terms[0]]
......@@ -139,6 +145,7 @@ class CommandTree(object):
cmd = new_cmd
cmd.cmd_class = cmd_class or None = description or None
cmd.long_help = long_description or cmd.long_help
def find_best_match(self, terms):
"""Find a command that best matches a given list of terms
......@@ -45,8 +45,9 @@ class Command(TestCase):
(None, '', 'cmd'),
(None, '', 'Some help'),
(None, '', {}, dict(cmd0a=None, cmd0b=None)),
(None, command_tree.Command('cmd_cmd0'))):
path, help, subcommands, cmd_class = args
(None, command_tree.Command('cmd_cmd0')),
(None, 'long description')):
path, help, subcommands, cmd_class, long_help = args
cmd = command_tree.Command(*args)
except Exception as e:
......@@ -57,6 +58,7 @@ class Command(TestCase):
self.assertEqual(, help or '')
self.assertEqual(cmd.subcommands, subcommands or {})
self.assertEqual(cmd.cmd_class, cmd_class or None)
self.assertEqual(cmd.long_help, long_help or '')
def test_name(self):
for path in ('cmd', 'cmd_cmd0', 'cmd_cmd0_cmd1', '', None):
......@@ -402,29 +402,40 @@ class image_meta_delete(_init_image, _optional_output_cmd):
class image_register(_init_image, _optional_json):
"""(Re)Register an image"""
"""(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.
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
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'),
checksum=ValueArgument('Set image checksum', '--checksum'),
'set container format',
'Set container format',
disk_format=ValueArgument('set disk format', '--disk-format'),
#owner=ValueArgument('set image owner (admin only)', '--owner'),
disk_format=ValueArgument('Set disk format', '--disk-format'),
owner_name=ValueArgument('Set user uuid by user name', '--owner-name'),
'add property in key=value form (can be repeated)',
'Add property (user-specified metadata) in key=value form'
'(can be repeated)',
('-p', '--property')),
is_public=FlagArgument('mark image as public', '--public'),
size=IntArgument('set image size', '--size'),
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 :'
'{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
'Store remote metadata object, even if it already exists',
('-f', '--force')),
'Overide remote metadata file', ('-f', '--force')),
'Do not store metadata in remote meta file',
......@@ -511,7 +522,7 @@ class image_register(_init_image, _optional_json):
'No image file location provided',
importance=2, details=[
'An image location is needed. Image location format:',
' pithos://<user-id>/<container>/<path>',
' <container>:<path>',
' where an image file at the above location must exist.'
] + howto_image_file)
......@@ -521,11 +532,36 @@ class image_register(_init_image, _optional_json):
ae, 'Invalid image location format',
importance=1, details=[
'Valid image location format:',
' pithos://<user-id>/<container>/<img-file-path>'
' <container>:<img-file-path>'
] + howto_image_file)
def _old_location_format(location):
prefix = 'pithos://'
if location.startswith(prefix):
uuid, sep, rest = location[len(prefix):].partition('/')
container, sep, path = rest.partition('/')
return (uuid, container, path)
except Exception as e:
raiseCLIError(e, 'Invalid location format', details=[
'Correct location format:', ' <container>:<image path>'])
return ()
def _mine_location(self, container_path):
uuid = self['uuid'] or self._get_user_id()
old_response = self._old_location_format(container_path)
if old_response:
return old_response
uuid = self['uuid'] or (self._username2uuid(self['owner_name']) if (
self['owner_name']) else self._get_user_id())
if not uuid:
if self['owner_name']:
raiseCLIError('No user with username %s' % self['owner_name'])
raiseCLIError('Failed to get user uuid', details=[
'For details on current user:',
' /user whoami',
'To authenticate a new user through a user token:',
' /user authenticate <token>'])
if self['container']:
return uuid, self['container'], container_path
container, sep, path = container_path.partition(':')
......@@ -92,6 +92,8 @@ def run(auth_base, cloud, parser, _help):
if _help or not cmd.is_command:
if getattr(cmd, 'long_help', False):
print 'Details:\n', cmd.long_help
......@@ -122,7 +122,7 @@ class RequestManager(Logged):
url += _encode(path[1:] if path.startswith('/') else path)
delim = '?'
for key, val in params.items():
val = _encode(val)
val = _encode(u'%s' % val)
url += '%s%s%s' % (delim, key, ('=%s' % val) if val else '')
delim = '&'
parsed = urlparse(url)
......@@ -32,10 +32,8 @@
# or implied, of GRNET S.A.
from logging import getLogger
from json import dumps
from kamaki.clients import Client, ClientError
from kamaki.clients.utils import path4url
class AstakosClient(Client):
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