Merge branch 'next'
[pithos] / snf-pithos-tools / pithos / tools / sh.py
index 767b26f..e29c83b 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-# Copyright 2011 GRNET S.A. All rights reserved.
+# Copyright 2011-2012 GRNET S.A. All rights reserved.
 # 
 # Redistribution and use in source and binary forms, with or
 # without modification, are permitted provided that the following
@@ -39,9 +39,9 @@ from os import environ
 from sys import argv, exit, stdin, stdout
 from datetime import datetime
 
-from pithos.lib.client import Pithos_Client, Fault
-from pithos.lib.util import get_user, get_auth, get_server, get_api
-from pithos.lib.transfer import upload, download
+from pithos.tools.lib.client import Pithos_Client, Fault
+from pithos.tools.lib.util import get_user, get_auth, get_url
+from pithos.tools.lib.transfer import upload, download
 
 import json
 import logging
@@ -68,20 +68,18 @@ class Command(object):
     
     def __init__(self, name, argv):
         parser = OptionParser('%%prog %s [options] %s' % (name, self.syntax))
-        parser.add_option('--host', dest='host', metavar='HOST',
-                          default=get_server(), help='use server HOST')
-        parser.add_option('--user', dest='user', metavar='USERNAME',
+        parser.add_option('--url', dest='url', metavar='URL',
+                          default=get_url(), help='server URL (currently: %s)' % get_url())
+        parser.add_option('--user', dest='user', metavar='USER',
                           default=get_user(),
-                          help='use account USERNAME')
-        parser.add_option('--token', dest='token', metavar='AUTH',
+                          help='account USER (currently: %s)' % get_user())
+        parser.add_option('--token', dest='token', metavar='TOKEN',
                           default=get_auth(),
-                          help='use account AUTH')
-        parser.add_option('--api', dest='api', metavar='API',
-                          default=get_api(), help='use api API')
+                          help='account TOKEN (currently: %s)' % get_auth())
         parser.add_option('-v', action='store_true', dest='verbose',
-                          default=False, help='use verbose output')
+                          default=False, help='verbose output')
         parser.add_option('-d', action='store_true', dest='debug',
-                          default=False, help='use debug output')
+                          default=False, help='debug output')
         self.add_options(parser)
         options, args = parser.parse_args(argv)
         
@@ -92,7 +90,7 @@ class Command(object):
                 val = getattr(options, key)
                 setattr(self, key, val)
         
-        self.client = Pithos_Client(self.host, self.token, self.user, self.api, self.verbose,
+        self.client = Pithos_Client(self.url, self.token, self.user, self.verbose,
                              self.debug)
         
         self.parser = parser
@@ -145,6 +143,11 @@ class List(Command):
                           default=None, help='show metadata until that date')
         parser.add_option('--format', action='store', dest='format',
                           default='%d/%m/%Y', help='format to parse until date')
+        parser.add_option('--shared', action='store_true', dest='shared',
+                          default=False, help='show only shared')
+        parser.add_option('--public', action='store_true', dest='public',
+                          default=False, help='show only public')
+        
     
     def execute(self, container=None):
         if container:
@@ -154,7 +157,7 @@ class List(Command):
     
     def list_containers(self):
         attrs = ['limit', 'marker', 'if_modified_since',
-                 'if_unmodified_since']
+                 'if_unmodified_since', 'shared', 'public']
         args = self._build_args(attrs)
         args['format'] = 'json' if self.detail else 'text'
         
@@ -169,7 +172,8 @@ class List(Command):
         #prepate params
         params = {}
         attrs = ['limit', 'marker', 'prefix', 'delimiter', 'path',
-                 'meta', 'if_modified_since', 'if_unmodified_since']
+                 'meta', 'if_modified_since', 'if_unmodified_since',
+                 'shared', 'public']
         args = self._build_args(attrs)
         args['format'] = 'json' if self.detail else 'text'
         
@@ -228,12 +232,23 @@ class CreateContainer(Command):
     syntax = '<container> [key=val] [...]'
     description = 'create a container'
     
+    def add_options(self, parser):
+        parser.add_option('--versioning', action='store', dest='versioning',
+                          default=None, help='set container versioning (auto/none)')
+        parser.add_option('--quota', action='store', dest='quota',
+                          default=None, help='set default container quota')
+    
     def execute(self, container, *args):
         meta = {}
         for arg in args:
             key, sep, val = arg.partition('=')
             meta[key] = val
-        ret = self.client.create_container(container, **meta)
+        policy = {}
+        if getattr(self, 'versioning'):
+            policy['versioning'] = self.versioning
+        if getattr(self, 'quota'):
+            policy['quota'] = self.quota
+        ret = self.client.create_container(container, meta=meta, policies=policy)
         if not ret:
             print 'Container already exists'
 
@@ -247,6 +262,12 @@ class Delete(Command):
                           default=None, help='remove history until that date')
         parser.add_option('--format', action='store', dest='format',
                           default='%d/%m/%Y', help='format to parse until date')
+        parser.add_option('--delimiter', action='store', type='str',
+                          dest='delimiter', default=None,
+                          help='mass delete objects with path staring with <src object> + delimiter')
+        parser.add_option('-r', action='store_true',
+                          dest='recursive', default=False,
+                          help='mass delimiter objects with delimiter /')
     
     def execute(self, path):
         container, sep, object = path.partition('/')
@@ -256,7 +277,12 @@ class Delete(Command):
             until = int(_time.mktime(t))
         
         if object:
-            self.client.delete_object(container, object, until)
+            kwargs = {}
+            if self.delimiter:
+                kwargs['delimiter'] = self.delimiter
+            elif self.recursive:
+                kwargs['delimiter'] = '/'
+            self.client.delete_object(container, object, until, **kwargs)
         else:
             self.client.delete_container(container, until)
 
@@ -293,10 +319,13 @@ class GetObject(Command):
         parser.add_option('--versionlist', action='store_true',
                           dest='versionlist', default=False,
                           help='get the full object version list')
+        parser.add_option('--hashmap', action='store_true',
+                          dest='hashmap', default=False,
+                          help='get the object hashmap instead')
     
     def execute(self, path):
         attrs = ['if_match', 'if_none_match', 'if_modified_since',
-                 'if_unmodified_since']
+                 'if_unmodified_since', 'hashmap']
         args = self._build_args(attrs)
         args['format'] = 'json' if self.detail else 'text'
         if self.range:
@@ -315,11 +344,17 @@ class GetObject(Command):
         elif self.version:
             data = self.client.retrieve_object_version(container, object,
                                                        self.version, **args)
+        elif self.hashmap:
+            if 'detail' in args.keys():
+                args.pop('detail')
+            args.pop('format')
+            self.detail = True
+            data = self.client.retrieve_object_hashmap(container, object, **args)
         else:
             data = self.client.retrieve_object(container, object, **args)    
         
         f = open(self.file, 'w') if self.file else stdout
-        if self.detail:
+        if self.detail or type(data) == types.DictionaryType:
             if self.versionlist:
                 print_versions(data, f=f)
             else:
@@ -360,7 +395,7 @@ class PutObject(Command):
         #                  help='use for large file support')
         parser.add_option('--manifest', action='store',
                           dest='x_object_manifest', default=None,
-                          help='upload a manifestation file')
+                          help='provide object parts prefix in <container>/<object> form')
         parser.add_option('--content-type', action='store',
                           dest='content_type', default=None,
                           help='create object with specific content type')
@@ -402,12 +437,9 @@ class PutObject(Command):
                                                     meta=meta, **args)
         elif self.use_hashes:
             data = f.read()
-            if data is object:
-                hashmap = json.loads()
-                self.client.create_object_by_hashmap(container, object, hashmap,
-                                                 meta=meta, **args)
-            else:
-                print "Expected object"
+            hashmap = json.loads(data)
+            self.client.create_object_by_hashmap(container, object, hashmap,
+                                             meta=meta, **args)
         elif self.x_object_manifest:
             self.client.create_manifestation(container, object, self.x_object_manifest)
         elif not f:
@@ -432,6 +464,12 @@ class CopyObject(Command):
         parser.add_option('--content-type', action='store',
                           dest='content_type', default=None,
                           help='change object\'s content type')
+        parser.add_option('--delimiter', action='store', type='str',
+                          dest='delimiter', default=None,
+                          help='mass copy objects with path staring with <src object> + delimiter')
+        parser.add_option('-r', action='store_true',
+                          dest='recursive', default=False,
+                          help='mass copy with delimiter /')
     
     def execute(self, src, dst, *args):
         src_container, sep, src_object = src.partition('/')
@@ -448,6 +486,10 @@ class CopyObject(Command):
             dst_object = dst
         
         args = {'content_type':self.content_type} if self.content_type else {}
+        if self.delimiter:
+               args['delimiter'] = self.delimiter
+        elif self.recursive:
+               args['delimiter'] = '/'
         self.client.copy_object(src_container, src_object, dst_container,
                                 dst_object, meta, self.public, self.version,
                                 **args)
@@ -487,7 +529,7 @@ class UpdateObject(Command):
         parser.add_option('--offset', action='store',
                           dest='offset',
                           default=None, help='starting offest to be updated')
-        parser.add_option('--range', action='store', dest='content-range',
+        parser.add_option('--range', action='store', dest='content_range',
                           default=None, help='range of data to be updated')
         parser.add_option('--chunked', action='store_true', dest='chunked',
                           default=False, help='set chunked transfer mode')
@@ -528,7 +570,8 @@ class UpdateObject(Command):
         
         
         attrs = ['content_encoding', 'content_disposition', 'x_object_sharing',
-                 'x_object_public', 'replace']
+                 'x_object_public', 'x_object_manifest', 'replace', 'offset',
+                 'content_range']
         args = self._build_args(attrs)
         
         if self.no_sharing:
@@ -560,6 +603,12 @@ class MoveObject(Command):
         parser.add_option('--content-type', action='store',
                           dest='content_type', default=None,
                           help='change object\'s content type')
+        parser.add_option('--delimiter', action='store', type='str',
+                          dest='delimiter', default=None,
+                          help='mass move objects with path staring with <src object> + delimiter')
+        parser.add_option('-r', action='store_true',
+                          dest='recursive', default=False,
+                          help='mass move objects with delimiter /')
     
     def execute(self, src, dst, *args):
         src_container, sep, src_object = src.partition('/')
@@ -575,6 +624,10 @@ class MoveObject(Command):
             meta[key] = val
         
         args = {'content_type':self.content_type} if self.content_type else {}
+        if self.delimiter:
+               args['delimiter'] = self.delimiter
+        elif self.recursive:
+               args['delimiter'] = '/'
         self.client.move_object(src_container, src_object, dst_container,
                                 dst_object, meta, self.public, **args)