commit 7e18cd26b12ec22d78b61f4408047c87ee7c096f
Author: Stavros Sachtouris <saxtouri@admin.grnet.gr>
Date:   Mon Sep 9 15:15:53 2013 +0300

    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

diff --git a/docs/developers/clients-api.rst b/docs/developers/clients-api.rst
index ed2aad0..0beb1f8 100644
--- a/docs/developers/clients-api.rst
+++ b/docs/developers/clients-api.rst
@@ -225,7 +225,7 @@ Batch-create servers
 
     user = AstakosClient(AUTHENTICATION_URL, TOKEN)
 
-    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
 
     user = AstakosClient(AUTHENTICATION_URL, TOKEN)
 
-    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']
     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)
 
     for img in pithos.list_objects():
diff --git a/kamaki/cli/__init__.py b/kamaki/cli/__init__.py
index 2ba89bf..1842da4 100644
--- a/kamaki/cli/__init__.py
+++ b/kamaki/cli/__init__.py
@@ -157,7 +157,8 @@ def command(cmd_tree, prefix='', descedants_depth=1):
                 'No commend in %s (acts as cmd description)' % cls.__name__)
         _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 wrap
 
diff --git a/kamaki/cli/command_tree/__init__.py b/kamaki/cli/command_tree/__init__.py
index 24215ab..394712a 100644
--- a/kamaki/cli/command_tree/__init__.py
+++ b/kamaki/cli/command_tree/__init__.py
@@ -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
         self.help = help or ''
         self.subcommands = dict(subcommands) if subcommands else {}
         self.cmd_class = cmd_class or None
+        self.long_help = '%s' % (long_help or '')
 
     @property
     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=''):
         self.name = 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('_')
         try:
             cmd = self.groups[terms[0]]
@@ -139,6 +145,7 @@ class CommandTree(object):
                 cmd = new_cmd
         cmd.cmd_class = cmd_class or None
         cmd.help = 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
diff --git a/kamaki/cli/command_tree/test.py b/kamaki/cli/command_tree/test.py
index 5964bd7..5929a3b 100644
--- a/kamaki/cli/command_tree/test.py
+++ b/kamaki/cli/command_tree/test.py
@@ -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
             try:
                 cmd = command_tree.Command(*args)
             except Exception as e:
@@ -57,6 +58,7 @@ class Command(TestCase):
             self.assertEqual(cmd.help, 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):
diff --git a/kamaki/cli/commands/image.py b/kamaki/cli/commands/image.py
index 4f459b9..3adfd2f 100644
--- a/kamaki/cli/commands/image.py
+++ b/kamaki/cli/commands/image.py
@@ -402,29 +402,40 @@ class image_meta_delete(_init_image, _optional_output_cmd):
 
 @command(image_cmds)
 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 = {}
 
     arguments = dict(
-        checksum=ValueArgument('set image checksum', '--checksum'),
+        checksum=ValueArgument('Set image checksum', '--checksum'),
         container_format=ValueArgument(
-            'set container format',
+            'Set container format',
             '--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'),
         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')),
-        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'),
         metafile=ValueArgument(
             'Load metadata from a json-formated file <img-file>.meta :'
             '{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
             ('--metafile')),
         metafile_force=FlagArgument(
-            'Store remote metadata object, even if it already exists',
-            ('-f', '--force')),
+            'Overide remote metadata file', ('-f', '--force')),
         no_metafile_upload=FlagArgument(
             'Do not store metadata in remote meta file',
             ('--no-metafile-upload')),
@@ -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)
         try:
@@ -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)
 
+    @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):
-        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(':')
diff --git a/kamaki/cli/one_command.py b/kamaki/cli/one_command.py
index eed456f..1e597df 100644
--- a/kamaki/cli/one_command.py
+++ b/kamaki/cli/one_command.py
@@ -92,6 +92,8 @@ def run(auth_base, cloud, parser, _help):
 
     if _help or not cmd.is_command:
         parser.parser.print_help()
+        if getattr(cmd, 'long_help', False):
+            print 'Details:\n', cmd.long_help
         print_subcommands_help(cmd)
         exit(0)
 
diff --git a/kamaki/clients/__init__.py b/kamaki/clients/__init__.py
index ea57ca7..bc27f0b 100644
--- a/kamaki/clients/__init__.py
+++ b/kamaki/clients/__init__.py
@@ -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)
diff --git a/kamaki/clients/astakos/__init__.py b/kamaki/clients/astakos/__init__.py
index 4801980..c3ebb8c 100644
--- a/kamaki/clients/astakos/__init__.py
+++ b/kamaki/clients/astakos/__init__.py
@@ -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):
