Add a store list command
authorGiorgos Verigakis <verigak@gmail.com>
Tue, 28 Feb 2012 17:53:43 +0000 (19:53 +0200)
committerGiorgos Verigakis <verigak@gmail.com>
Wed, 9 May 2012 10:18:43 +0000 (13:18 +0300)
kamaki/cli.py
kamaki/clients/__init__.py
kamaki/clients/pithos.py
kamaki/clients/storage.py
setup.py

index cdcc299..c1bba2d 100755 (executable)
@@ -660,14 +660,12 @@ class glance_setmembers(object):
         self.client.set_members(image_id, member)
 
 
-class store_command(object):
-    """base class for all store_* commands"""
+class _store_account_command(object):
+    """Base class for account level storage commands"""
     
-    def update_parser(cls, parser):
+    def update_parser(self, parser):
         parser.add_option('--account', dest='account', metavar='NAME',
                           help="Specify an account to use")
-        parser.add_option('--container', dest='container', metavar='NAME',
-                          help="Specify a container to use")
     
     def progress(self, message):
         """Return a generator function to be used for progress tracking"""
@@ -688,18 +686,26 @@ class store_command(object):
     def main(self):
         if self.options.account is not None:
             self.client.account = self.options.account
+
+
+class _store_container_command(_store_account_command):
+    """Base class for container level storage commands"""
+    
+    def update_parser(self, parser):
+        super(_store_container_command, self).update_parser(parser)
+        parser.add_option('--container', dest='container', metavar='NAME',
+                          help="Specify a container to use")
+    
+    def main(self):
+        super(_store_container_command, self).main()
         if self.options.container is not None:
             self.client.container = self.options.container
 
 
 @command(api='storage')
-class store_create(object):
+class store_create(_store_account_command):
     """Create a container"""
     
-    def update_parser(cls, parser):
-        parser.add_option('--account', dest='account', metavar='NAME',
-                          help="Specify an account to use")
-    
     def main(self, container):
         if self.options.account:
             self.client.account = self.options.account
@@ -707,13 +713,9 @@ class store_create(object):
 
 
 @command(api='storage')
-class store_container(object):
+class store_container(_store_account_command):
     """Get container info"""
     
-    def update_parser(cls, parser):
-        parser.add_option('--account', dest='account', metavar='NAME',
-                          help="Specify an account to use")
-    
     def main(self, container):
         if self.options.account:
             self.client.account = self.options.account
@@ -722,7 +724,29 @@ class store_container(object):
 
 
 @command(api='storage')
-class store_upload(store_command):
+class store_list(_store_container_command):
+    """List objects"""
+    
+    def format_size(self, size):
+        units = ('B', 'K', 'M', 'G', 'T')
+        size = float(size)
+        for unit in units:
+            if size <= 1024:
+                break
+            size /= 1024
+        s = ('%.1f' % size).rstrip('.0')
+        return s + unit
+    
+    
+    def main(self, path=''):
+        super(store_list, self).main()
+        for object in self.client.list_objects():
+            size = self.format_size(object['bytes'])
+            print '%6s %s' % (size, object['name'])
+        
+
+@command(api='storage')
+class store_upload(_store_container_command):
     """Upload a file"""
     
     def main(self, path, remote_path=None):
@@ -738,7 +762,7 @@ class store_upload(store_command):
 
 
 @command(api='storage')
-class store_download(store_command):
+class store_download(_store_container_command):
     """Download a file"""
         
     def main(self, remote_path, local_path='-'):
@@ -764,7 +788,7 @@ class store_download(store_command):
 
 
 @command(api='storage')
-class store_delete(store_command):
+class store_delete(_store_container_command):
     """Delete a file"""
     
     def main(self, path):
index ce5ce24..6c26127 100644 (file)
@@ -46,7 +46,7 @@ recvlog = logging.getLogger('clients.recv')
 # Add a convenience json property to the responses
 def _json(self):
     try:
-        return json.loads(self.content)
+        return json.loads(self.content) if self.content else {}
     except ValueError:
         raise ClientError("Invalid JSON reply", self.status_code)
 requests.Response.json = property(_json)
index 99f4901..d56e110 100644 (file)
@@ -94,7 +94,7 @@ class PithosClient(StorageClient):
         assert size == file_size
                 
         path = '/%s/%s/%s' % (self.account, self.container, object)
-        params = {'hashmap': '', 'format': 'json'}
+        params = dict(format='json', hashmap='')
         hashmap = dict(bytes=size, hashes=hashes.keys())
         r = self.put(path, params=params, json=hashmap, success=(201, 409))
         
index f7b5a76..5b6a2d9 100644 (file)
@@ -84,7 +84,7 @@ class StorageClient(Client):
     def get_object(self, object):
         self.assert_container()
         path = '/%s/%s/%s' % (self.account, self.container, object)
-        r = self.get(path, raw=True)
+        r = self.get(path, raw=True, success=200)
         size = int(r.headers['content-length'])
         return r.raw, size
     
@@ -92,3 +92,10 @@ class StorageClient(Client):
         self.assert_container()
         path = '/%s/%s/%s' % (self.account, self.container, object)
         self.delete(path, success=204)
+    
+    def list_objects(self, path=''):
+        self.assert_container()
+        path = '/%s/%s' % (self.account, self.container)
+        params = dict(format='json')
+        r = self.get(path, params=params, success=(200, 204))
+        return r.json
index 3b02e0d..15e89c8 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -51,7 +51,7 @@ setup(
         'console_scripts': ['kamaki = kamaki.cli:main']
     },
     install_requires=[
-        'requests>=0.10.2',
+        'requests>=0.10.6',
         'clint>=0.3'
     ]
 )