Store image properties after image registration
authorStavros Sachtouris <saxtouri@admin.grnet.gr>
Tue, 21 May 2013 14:08:34 +0000 (17:08 +0300)
committerStavros Sachtouris <saxtouri@admin.grnet.gr>
Tue, 21 May 2013 14:08:34 +0000 (17:08 +0300)
Log of changes:
- Properties are stored in a remote file on Pithos+
- File name is <image-file>.meta
- Feature can be switched off with --no-property-file-upload
- If remote property file exists, registration is aborted
- If remote property file exists, force with -f, --fforce-upload-property-file
- Location is given as container:path
- User id can be aquired internaly with an astakos call

Refs: #3769, #3778

Changelog
kamaki/cli/commands/image.py
kamaki/cli/commands/pithos.py
kamaki/clients/pithos/__init__.py

index 1c5d332..8db8517 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -54,4 +54,7 @@ Features:
 - Add a download_to_string method in pithos client [#3608]
 - Add an upload_from_string method in pithos client [#3608]
 - Add pithos client method create_container [#3756]
+- Store image properties on remote location after image registration [#3769]
+- Add runtime args to image register for forcing or unsettitng property
+    storage [#3769]
 
index cb2782f..639065d 100644 (file)
@@ -39,10 +39,14 @@ from kamaki.cli import command
 from kamaki.cli.command_tree import CommandTree
 from kamaki.cli.utils import print_dict, print_items, print_json
 from kamaki.clients.image import ImageClient
+from kamaki.clients.pithos import PithosClient
+from kamaki.clients.astakos import AstakosClient
+from kamaki.clients import ClientError
 from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
 from kamaki.cli.argument import IntArgument
 from kamaki.cli.commands.cyclades import _init_cyclades
 from kamaki.cli.commands import _command_init, errors, _optional_output_cmd
+from kamaki.cli.errors import raiseCLIError
 
 
 image_cmds = CommandTree(
@@ -262,26 +266,77 @@ class image_register(_init_image):
         #    ('-u', '--update')),
         json_output=FlagArgument('Show results in json', ('-j', '--json')),
         property_file=ValueArgument(
-            'Load properties from a json-formated file. Contents:'
+            'Load properties from a json-formated file <img-file>.meta :'
             '{"key1": "val1", "key2": "val2", ...}',
-            ('--property-file'))
+            ('--property-file')),
+        prop_file_force=FlagArgument(
+            'Store remote property object, even it already exists',
+            ('-f', '--force-upload-property-file')),
+        no_prop_file_upload=FlagArgument(
+            'Do not store properties in remote property file',
+            ('--no-property-file-upload')),
+        container=ValueArgument(
+            'Remote image container', ('-C', '--container')),
+        fileowner=ValueArgument(
+            'UUID of the user who owns the image file', ('--fileowner'))
     )
 
+    def _get_uuid(self):
+        uuid = self['fileowner'] or self.config.get('image', 'fileowner')
+        if uuid:
+            return uuid
+        atoken = self.client.token
+        user = AstakosClient(self.config.get('user', 'url'), atoken)
+        return user.term('uuid')
+
+    def _get_pithos_client(self, uuid, container):
+        purl = self.config.get('file', 'url')
+        ptoken = self.client.token
+        return PithosClient(purl, ptoken, uuid, container)
+
+    def _store_remote_property_file(self, pclient, remote_path, properties):
+        return pclient.upload_from_string(
+            remote_path, _validate_image_props(properties, return_str=True))
+
+    def _get_container_path(self, container_path):
+        container = self['container'] or self.config.get('image', 'container')
+        if container:
+            return container, container_path
+
+        container, sep, path = container_path.partition(':')
+        if not sep or not container or not path:
+            raiseCLIError(
+                '%s is not a valid pithos remote location' % container_path,
+                details=[
+                    'To set "image" as container and "my_dir/img.diskdump" as',
+                    'the image path, try one of the following as '
+                    'container:path',
+                    '- <image container>:<remote path>',
+                    '    e.g. image:/my_dir/img.diskdump',
+                    '- <remote path> -C <image container>',
+                    '    e.g. /my_dir/img.diskdump -C image'])
+        return container, path
+
     @errors.generic.all
     @errors.plankton.connection
-    def _run(self, name, location):
-        if not location.startswith('pithos://'):
-            account = self.config.get('file', 'account') \
-                or self.config.get('global', 'account')
-            assert account, 'No user account provided'
-            if account[-1] == '/':
-                account = account[:-1]
-            container = self.config.get('file', 'container') \
-                or self.config.get('global', 'container')
-            if not container:
-                location = 'pithos://%s/%s' % (account, location)
-            else:
-                location = 'pithos://%s/%s/%s' % (account, container, location)
+    def _run(self, name, container_path):
+        container, path = self._get_container_path(container_path)
+        uuid = self._get_uuid()
+        prop_path = '%s.meta' % path
+
+        pclient = None if (
+            self['no_prop_file_upload']) else self._get_pithos_client(
+                uuid, container)
+        if pclient and not self['prop_file_force']:
+            try:
+                pclient.get_object_info(prop_path)
+                raiseCLIError('Property file %s: %s already exists' % (
+                    container, prop_path))
+            except ClientError as ce:
+                if ce.status != 404:
+                    raise
+
+        location = 'pithos://%s/%s/%s' % (uuid, container, path)
 
         params = {}
         for key in set([
@@ -301,9 +356,15 @@ class image_register(_init_image):
         printer = print_json if self['json_output'] else print_dict
         printer(self.client.register(name, location, params, properties))
 
-    def main(self, name, location):
+        if pclient:
+            prop_headers = pclient.upload_from_string(
+                prop_path, _validate_image_props(properties, return_str=True))
+            print('Property file location is %s: %s' % (container, prop_path))
+            print('\twith version %s' % prop_headers['x-object-version'])
+
+    def main(self, name, container___path):
         super(self.__class__, self)._run()
-        self._run(name, location)
+        self._run(name, container___path)
 
 
 @command(image_cmds)
index 1e9be0d..e9e1e67 100644 (file)
@@ -1645,7 +1645,7 @@ class file_permissions_set(_file_container_command, _optional_output_cmd):
     """
 
     @errors.generic.all
-    def format_permition_dict(self, permissions):
+    def format_permission_dict(self, permissions):
         read = False
         write = False
         for perms in permissions:
@@ -1666,13 +1666,13 @@ class file_permissions_set(_file_container_command, _optional_output_cmd):
     def _run(self, read, write):
         self._optional_output(self.client.set_object_sharing(
             self.path,
-            read_permition=read, write_permition=write))
+            read_permission=read, write_permission=write))
 
     def main(self, container___path, *permissions):
         super(self.__class__, self)._run(
             container___path,
             path_is_optional=False)
-        (read, write) = self.format_permition_dict(permissions)
+        (read, write) = self.format_permission_dict(permissions)
         self._run(read, write)
 
 
index abd35b4..8aa7ca5 100644 (file)
@@ -1236,23 +1236,23 @@ class PithosClient(PithosRestClient):
 
     def set_object_sharing(
             self, obj,
-            read_permition=False, write_permition=False):
+            read_permission=False, write_permission=False):
         """Give read/write permisions to an object.
 
         :param obj: (str) remote object path
 
-        :param read_permition: (list - bool) users and user groups that get
-            read permition for this object - False means all previous read
+        :param read_permission: (list - bool) users and user groups that get
+            read permission for this object - False means all previous read
             permissions will be removed
 
-        :param write_perimition: (list - bool) of users and user groups to get
-           write permition for this object - False means all previous write
+        :param write_permission: (list - bool) of users and user groups to get
+           write permission for this object - False means all previous write
            permissions will be removed
 
         :returns: (dict) response headers
         """
 
-        perms = dict(read=read_permition or '', write=write_permition or '')
+        perms = dict(read=read_permission or '', write=write_permission or '')
         r = self.object_post(obj, update=True, permissions=perms)
         return r.headers