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