Apply option outputs to image commands + renames
authorStavros Sachtouris <saxtouri@admin.grnet.gr>
Mon, 20 May 2013 14:54:34 +0000 (17:54 +0300)
committerStavros Sachtouris <saxtouri@admin.grnet.gr>
Mon, 20 May 2013 14:54:34 +0000 (17:54 +0300)
- Add image.add_member missing content-length header
- Rename image-[add|del]member commands to members-[add|delete]
- Remove update option from image-register
- In image-compute split properties to properties-list and properties-get
- Add optional output to methods
 * image: unregister, members add/delete/set
 * image compute: delete, properties delete
- Transliterate methods to list-get-set-delete command groups:
 * image: members, member
 * image compute: properties

Refs :#3756 #3732

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

index 023c9ad..1c5d332 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -6,6 +6,7 @@ Bug Fixes:
 - Shell can manage all valid command line arguments [#3716]
 - Restore 2nd level command syntax in shell [#3736]
 - Allow copy of deleted objects by refering to older version [#3737]
+- Add image.add_member missing content-length header
 
 Changes:
 
@@ -19,13 +20,23 @@ Changes:
     This operation was implemented by accident, due to the symetry between
     move and copy
 - Rename file-meta commands to file-metadata
-- Add optional output for file methods [#3756, #3732]:
+- Rename image-[add|del]member commands to members-[add|delete]
+- Remove update option from imagre-register
+- In image-compute split properties to properties-list and properties-get
+- Add optional output to methods[#3756, #3732]:
+    - file:
     mkdir, touch, create, move, copy, move, append, truncate, overwrite,
     manifest, upload, delete, purge, unpublish, permissions set/delete, info,
     metadata set/delete, containerlimit set, versioning set, group set/delete,
     upload, overwrite
-- Transliterate permissions, versioning, group and metadata methods to
-    get-set-delete command groups
+    - image:
+    unregister, members add/delete/set
+    -image compute:
+    delete, properties delete
+- Transliterate methods to list-get-set-delete command groups:
+    - file: permissions, versioning, group and metadata
+    - image: members, member
+    - image compute: properties
 
 Features:
 
index 8f1f5f8..4c2d54d 100644 (file)
 
 from kamaki.cli import command
 from kamaki.cli.command_tree import CommandTree
-from kamaki.cli.utils import print_dict, print_items
+from kamaki.cli.utils import print_dict, print_items, print_json
 from kamaki.clients.image import ImageClient
 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
+from kamaki.cli.commands import _command_init, errors, _optional_output_cmd
 
 
 image_cmds = CommandTree(
@@ -104,7 +104,8 @@ class image_list(_init_image):
         more=FlagArgument(
             'output results in pages (-n to set items per page, default 10)',
             '--more'),
-        enum=FlagArgument('Enumerate results', '--enumerate')
+        enum=FlagArgument('Enumerate results', '--enumerate'),
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
     )
 
     def _filtered_by_owner(self, detail, *list_params):
@@ -146,8 +147,11 @@ class image_list(_init_image):
             images = self._filtered_by_owner(detail, filters, order)
         else:
             images = self.client.list_public(detail, filters, order)
-        images = self._filtered_by_name(images)
 
+        if self['json_output']:
+            print_json(images)
+            return
+        images = self._filtered_by_name(images)
         if self['more']:
             print_items(
                 images,
@@ -171,12 +175,16 @@ class image_meta(_init_image):
     - image os properties (os, fs, etc.)
     """
 
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
+
     @errors.generic.all
     @errors.plankton.connection
     @errors.plankton.id
     def _run(self, image_id):
-        image = self.client.get_meta(image_id)
-        print_dict(image)
+        printer = print_json if self['json_output'] else print_dict
+        printer(self.client.get_meta(image_id))
 
     def main(self, image_id):
         super(self.__class__, self)._run()
@@ -200,9 +208,10 @@ class image_register(_init_image):
             ('-p', '--property')),
         is_public=FlagArgument('mark image as public', '--public'),
         size=IntArgument('set image size', '--size'),
-        update=FlagArgument(
-            'update existing image properties',
-            ('-u', '--update'))
+        #update=FlagArgument(
+        #    'update existing image properties',
+        #    ('-u', '--update')),
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
     )
 
     @errors.generic.all
@@ -232,11 +241,9 @@ class image_register(_init_image):
             params[key] = self[key]
 
             properties = self['properties']
-        if self['update']:
-            self.client.reregister(location, name, params, properties)
-        else:
-            r = self.client.register(name, location, params, properties)
-            print_dict(r)
+
+        printer = print_json if self['json_output'] else print_dict
+        printer(self.client.register(name, location, params, properties))
 
     def main(self, name, location):
         super(self.__class__, self)._run()
@@ -244,14 +251,14 @@ class image_register(_init_image):
 
 
 @command(image_cmds)
-class image_unregister(_init_image):
+class image_unregister(_init_image, _optional_output_cmd):
     """Unregister an image (does not delete the image file)"""
 
     @errors.generic.all
     @errors.plankton.connection
     @errors.plankton.id
     def _run(self, image_id):
-        self.client.unregister(image_id)
+        self._optional_output(self.client.unregister(image_id))
 
     def main(self, image_id):
         super(self.__class__, self)._run()
@@ -259,45 +266,64 @@ class image_unregister(_init_image):
 
 
 @command(image_cmds)
-class image_members(_init_image):
-    """Get image members"""
+class image_shared(_init_image):
+    """List images shared by a member"""
+
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
 
     @errors.generic.all
     @errors.plankton.connection
-    @errors.plankton.id
-    def _run(self, image_id):
-        members = self.client.list_members(image_id)
-        print_items(members)
+    def _run(self, member):
+        r = self.client.list_shared(member)
+        if self['json_output']:
+            print_json(r)
+        else:
+            print_items(r, title=('image_id',))
 
-    def main(self, image_id):
+    def main(self, member):
         super(self.__class__, self)._run()
-        self._run(image_id=image_id)
+        self._run(member)
 
 
 @command(image_cmds)
-class image_shared(_init_image):
-    """List images shared by a member"""
+class image_members(_init_image):
+    """Manage members. Members of an image are users who can modify it"""
+
+
+@command(image_cmds)
+class image_members_list(_init_image):
+    """List members of an image"""
+
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
 
     @errors.generic.all
     @errors.plankton.connection
-    def _run(self, member):
-        images = self.client.list_shared(member)
-        print_items(images)
+    @errors.plankton.id
+    def _run(self, image_id):
+        members = self.client.list_members(image_id)
+        if self['json_output']:
+            print_json(members)
+        else:
+            print_items(members, title=('member_id',), with_redundancy=True)
 
-    def main(self, member):
+    def main(self, image_id):
         super(self.__class__, self)._run()
-        self._run(member)
+        self._run(image_id=image_id)
 
 
 @command(image_cmds)
-class image_addmember(_init_image):
+class image_members_add(_init_image, _optional_output_cmd):
     """Add a member to an image"""
 
     @errors.generic.all
     @errors.plankton.connection
     @errors.plankton.id
     def _run(self, image_id=None, member=None):
-            self.client.add_member(image_id, member)
+            self._optional_output(self.client.add_member(image_id, member))
 
     def main(self, image_id, member):
         super(self.__class__, self)._run()
@@ -305,14 +331,14 @@ class image_addmember(_init_image):
 
 
 @command(image_cmds)
-class image_delmember(_init_image):
+class image_members_delete(_init_image, _optional_output_cmd):
     """Remove a member from an image"""
 
     @errors.generic.all
     @errors.plankton.connection
     @errors.plankton.id
     def _run(self, image_id=None, member=None):
-            self.client.remove_member(image_id, member)
+            self._optional_output(self.client.remove_member(image_id, member))
 
     def main(self, image_id, member):
         super(self.__class__, self)._run()
@@ -320,14 +346,14 @@ class image_delmember(_init_image):
 
 
 @command(image_cmds)
-class image_setmembers(_init_image):
+class image_members_set(_init_image, _optional_output_cmd):
     """Set the members of an image"""
 
     @errors.generic.all
     @errors.plankton.connection
     @errors.plankton.id
     def _run(self, image_id, members):
-            self.client.set_members(image_id, members)
+            self._optional_output(self.client.set_members(image_id, members))
 
     def main(self, image_id, *members):
         super(self.__class__, self)._run()
@@ -352,7 +378,8 @@ class image_compute_list(_init_cyclades):
         more=FlagArgument(
             'output results in pages (-n to set items per page, default 10)',
             '--more'),
-        enum=FlagArgument('Enumerate results', '--enumerate')
+        enum=FlagArgument('Enumerate results', '--enumerate'),
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
     )
 
     def _make_results_pretty(self, images):
@@ -364,6 +391,9 @@ class image_compute_list(_init_cyclades):
     @errors.cyclades.connection
     def _run(self):
         images = self.client.list_images(self['detail'])
+        if self['json_output']:
+            print_json(images)
+            return
         if self['detail']:
             self._make_results_pretty(images)
         if self['more']:
@@ -382,11 +412,18 @@ class image_compute_list(_init_cyclades):
 class image_compute_info(_init_cyclades):
     """Get detailed information on an image"""
 
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
+
     @errors.generic.all
     @errors.cyclades.connection
     @errors.plankton.id
     def _run(self, image_id):
         image = self.client.get_image_details(image_id)
+        if self['json_output']:
+            print_json(image)
+            return
         if 'metadata' in image:
             image['metadata'] = image['metadata']['values']
         print_dict(image)
@@ -397,14 +434,14 @@ class image_compute_info(_init_cyclades):
 
 
 @command(image_cmds)
-class image_compute_delete(_init_cyclades):
+class image_compute_delete(_init_cyclades, _optional_output_cmd):
     """Delete an image (WARNING: image file is also removed)"""
 
     @errors.generic.all
     @errors.cyclades.connection
     @errors.plankton.id
     def _run(self, image_id):
-        self.client.delete_image(image_id)
+        self._optional_output(self.client.delete_image(image_id))
 
     def main(self, image_id):
         super(self.__class__, self)._run()
@@ -413,32 +450,65 @@ class image_compute_delete(_init_cyclades):
 
 @command(image_cmds)
 class image_compute_properties(_init_cyclades):
-    """Get properties related to OS installation in an image"""
+    """Manage proeprties related to OS installation in an image"""
+
+
+@command(image_cmds)
+class image_compute_properties_list(_init_cyclades):
+    """List all image properties"""
+
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
+
+    @errors.generic.all
+    @errors.cyclades.connection
+    @errors.plankton.id
+    def _run(self, image_id):
+        printer = print_json if self['json_output'] else print_dict
+        printer(self.client.get_image_metadata(image_id))
+
+    def main(self, image_id):
+        super(self.__class__, self)._run()
+        self._run(image_id=image_id)
+
+
+@command(image_cmds)
+class image_compute_properties_get(_init_cyclades):
+    """Get an image property"""
+
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
 
     @errors.generic.all
     @errors.cyclades.connection
     @errors.plankton.id
     @errors.plankton.metadata
     def _run(self, image_id, key):
-        r = self.client.get_image_metadata(image_id, key)
-        print_dict(r)
+        printer = print_json if self['json_output'] else print_dict
+        printer(self.client.get_image_metadata(image_id, key))
 
-    def main(self, image_id, key=''):
+    def main(self, image_id, key):
         super(self.__class__, self)._run()
         self._run(image_id=image_id, key=key)
 
 
 @command(image_cmds)
-class image_compute_addproperty(_init_cyclades):
-    """Add an OS-related property to an image"""
+class image_compute_properties_add(_init_cyclades):
+    """Add a property to an image"""
+
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
 
     @errors.generic.all
     @errors.cyclades.connection
     @errors.plankton.id
     @errors.plankton.metadata
     def _run(self, image_id, key, val):
-        r = self.client.create_image_metadata(image_id, key, val)
-        print_dict(r)
+        printer = print_json if self['json_output'] else print_dict
+        printer(self.client.create_image_metadata(image_id, key, val))
 
     def main(self, image_id, key, val):
         super(self.__class__, self)._run()
@@ -446,33 +516,41 @@ class image_compute_addproperty(_init_cyclades):
 
 
 @command(image_cmds)
-class image_compute_setproperty(_init_cyclades):
-    """Update an existing property in an image"""
+class image_compute_properties_set(_init_cyclades):
+    """Add / update a set of properties for an image
+    proeprties must be given in the form key=value, e.v.
+    /image compute properties set <image-id> key1=val1 key2=val2
+    """
+    arguments = dict(
+        json_output=FlagArgument('Show results in json', ('-j', '--json'))
+    )
 
     @errors.generic.all
     @errors.cyclades.connection
     @errors.plankton.id
-    @errors.plankton.metadata
-    def _run(self, image_id, key, val):
-        metadata = {key: val}
-        r = self.client.update_image_metadata(image_id, **metadata)
-        print_dict(r)
-
-    def main(self, image_id, key, val):
+    def _run(self, image_id, keyvals):
+        metadata = dict()
+        for keyval in keyvals:
+            key, val = keyval.split('=')
+            metadata[key] = val
+        printer = print_json if self['json_output'] else print_dict
+        printer(self.client.update_image_metadata(image_id, **metadata))
+
+    def main(self, image_id, *key_equals_value):
         super(self.__class__, self)._run()
-        self._run(image_id=image_id, key=key, val=val)
+        self._run(image_id=image_id, keyvals=key_equals_value)
 
 
 @command(image_cmds)
-class image_compute_delproperty(_init_cyclades):
-    """Delete a property of an image"""
+class image_compute_properties_delete(_init_cyclades, _optional_output_cmd):
+    """Delete a property from an image"""
 
     @errors.generic.all
     @errors.cyclades.connection
     @errors.plankton.id
     @errors.plankton.metadata
     def _run(self, image_id, key):
-        self.client.delete_image_metadata(image_id, key)
+        self._optional_output(self.client.delete_image_metadata(image_id, key))
 
     def main(self, image_id, key):
         super(self.__class__, self)._run()
index 57e326d..889ddeb 100644 (file)
@@ -233,7 +233,8 @@ class ComputeClient(ComputeRestClient):
         """
         :param image_id: (str)
         """
-        self.images_delete(image_id)
+        r = self.images_delete(image_id)
+        return r.headers
 
     def get_image_metadata(self, image_id, key=''):
         """
@@ -280,4 +281,5 @@ class ComputeClient(ComputeRestClient):
         :param key: (str) metadatum key
         """
         command = path4url('meta', key)
-        self.images_delete(image_id, command)
+        r = self.images_delete(image_id, command)
+        return r.headers
index 1025ce2..96c6468 100644 (file)
@@ -99,7 +99,7 @@ class ImageClient(Client):
         return reply
 
     def register(self, name, location, params={}, properties={}):
-        """Register image put at location
+        """Register an image that is uploaded at location
 
         :param name: (str)
 
@@ -133,9 +133,12 @@ class ImageClient(Client):
         """Unregister an image
 
         :param image_id: (str)
+
+        :returns: (dict) response headers
         """
         path = path4url('images', image_id)
-        self.delete(path, success=204)
+        r = self.delete(path, success=204)
+        return r.headers
 
     def list_members(self, image_id):
         """
@@ -165,7 +168,9 @@ class ImageClient(Client):
         :param member: (str) user to allow access to current user's images
         """
         path = path4url('images', image_id, 'members', member)
-        self.put(path, success=204)
+        self.set_header('Content-Length', len(member))
+        r = self.put(path, success=204)
+        return r.headers
 
     def remove_member(self, image_id, member):
         """
@@ -174,7 +179,8 @@ class ImageClient(Client):
         :param member: (str) user to deprive from current user's images
         """
         path = path4url('images', image_id, 'members', member)
-        self.delete(path, success=204)
+        r = self.delete(path, success=204)
+        return r.headers
 
     def set_members(self, image_id, members):
         """
@@ -184,4 +190,5 @@ class ImageClient(Client):
         """
         path = path4url('images', image_id, 'members')
         req = {'memberships': [{'member_id': member} for member in members]}
-        self.put(path, json=req, success=204)
+        r = self.put(path, json=req, success=204)
+        return r.headers