clean test account in test setup
[pithos] / tools / store
index 582cc50..9e3ab27 100755 (executable)
@@ -37,8 +37,10 @@ from getpass import getuser
 from optparse import OptionParser
 from os import environ
 from sys import argv, exit, stdin, stdout
-from pithos.lib.client import Pithos_Client, Fault
 from datetime import datetime
+from lib.client import Pithos_Client, Fault
+from lib.util import get_user, get_auth, get_server, get_api
+from lib.transfer import upload, download
 
 import json
 import logging
@@ -47,10 +49,6 @@ import re
 import time as _time
 import os
 
-#DEFAULT_HOST = 'pithos.dev.grnet.gr'
-DEFAULT_HOST = '127.0.0.1:8000'
-DEFAULT_API = 'v1'
-
 _cli_commands = {}
 
 def cli_command(*args):
@@ -70,15 +68,15 @@ 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')
+                          default=get_server(), help='use server HOST')
         parser.add_option('--user', dest='user', metavar='USERNAME',
-                          default=_get_user(),
+                          default=get_user(),
                           help='use account USERNAME')
         parser.add_option('--token', dest='token', metavar='AUTH',
-                          default=_get_auth(),
+                          default=get_auth(),
                           help='use account AUTH')
         parser.add_option('--api', dest='api', metavar='API',
-                          default=DEFAULT_API, help='use api API')
+                          default=get_api(), help='use api API')
         parser.add_option('-v', action='store_true', dest='verbose',
                           default=False, help='use verbose output')
         parser.add_option('-d', action='store_true', dest='debug',
@@ -206,11 +204,11 @@ class Meta(Command):
     
     def execute(self, path=''):
         container, sep, object = path.partition('/')
-        args = {'restricted':self.restricted}
+        args = {'restricted': self.restricted}
         if getattr(self, 'until'):
             t = _time.strptime(self.until, self.format)
             args['until'] = int(_time.mktime(t))
-            
+        
         if object:
             meta = self.client.retrieve_object_metadata(container, object,
                                                         self.restricted,
@@ -301,7 +299,7 @@ class GetObject(Command):
         args = self._build_args(attrs)
         args['format'] = 'json' if self.detail else 'text'
         if self.range:
-            args['range'] = 'bytes=%s' %self.range
+            args['range'] = 'bytes=%s' % self.range
         if getattr(self, 'if_range'):
             args['if-range'] = 'If-Range:%s' % getattr(self, 'if_range')
         
@@ -310,6 +308,8 @@ class GetObject(Command):
         if self.versionlist:
             if 'detail' in args.keys():
                 args.pop('detail')
+            args.pop('format')
+            self.detail = True
             data = self.client.retrieve_object_versionlist(container, object, **args)
         elif self.version:
             data = self.client.retrieve_object_version(container, object,
@@ -319,7 +319,6 @@ class GetObject(Command):
         
         f = self.file and open(self.file, 'w') or stdout
         if self.detail:
-            data = json.loads(data)
             if self.versionlist:
                 print_versions(data, f=f)
             else:
@@ -358,7 +357,7 @@ class PutObject(Command):
         #parser.add_option('-S', action='store',
         #                  dest='segment_size', default=False,
         #                  help='use for large file support')
-        parser.add_option('--manifest', action='store_true',
+        parser.add_option('--manifest', action='store',
                           dest='x_object_manifest', default=None,
                           help='upload a manifestation file')
         parser.add_option('--content-type', action='store',
@@ -372,7 +371,7 @@ class PutObject(Command):
                           help='file descriptor to read from (pass - for standard input)')
         parser.add_option('--public', action='store_true',
                           dest='x_object_public', default=False,
-                          help='make object publicly accessible (\'True\'/\'False\')')
+                          help='make object publicly accessible')
     
     def execute(self, path, *args):
         if path.find('=') != -1:
@@ -401,50 +400,56 @@ class PutObject(Command):
             self.client.create_object_using_chunks(container, object, f,
                                                     meta=meta, **args)
         elif self.use_hashes:
-            format = 'json' if detail else 'text'
-            self.client.create_object_by_hashmap(container, object, f, format,
-                                 meta=meta, **args)
+            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"
         elif self.x_object_manifest:
             self.client.create_manifestation(container, object, self.x_object_manifest)
         elif not f:
             self.client.create_zero_length_object(container, object, meta=meta, **args)
         else:
-            data = f.read()
-            self.client.create_object(container, object, data, meta=meta, **args)
+            self.client.create_object(container, object, f, meta=meta, **args)
         if f:
             f.close()
 
 @cli_command('copy', 'cp')
 class CopyObject(Command):
-    syntax = '<src container>/<src object> [<dst container>/]<dst object>'
+    syntax = '<src container>/<src object> [<dst container>/]<dst object> [key=val] [...]'
     description = 'copy an object to a different location'
     
     def add_options(self, parser):
         parser.add_option('--version', action='store',
                           dest='version', default=False,
                           help='copy specific version')
-        parser.add_option('--public', action='store',
-                          dest='public', default=None,
-                          help='publish/unpublish object (\'True\'/\'False\')')
+        parser.add_option('--public', action='store_true',
+                          dest='public', default=False,
+                          help='make object publicly accessible')
+        parser.add_option('--content-type', action='store',
+                          dest='content_type', default=None,
+                          help='change object\'s content type')
     
-    def execute(self, src, dst):
+    def execute(self, src, dst, *args):
         src_container, sep, src_object = src.partition('/')
         dst_container, sep, dst_object = dst.partition('/')
+        
+        #prepare user defined meta
+        meta = {}
+        for arg in args:
+            key, sep, val = arg.partition('=')
+            meta[key] = val
+        
         if not sep:
             dst_container = src_container
             dst_object = dst
-        version = getattr(self, 'version')
-        headers = None
-        if version:
-            headers = {}
-            headers['X_SOURCE_VERSION'] = version
-        if self.public and self.nopublic:
-            raise Fault('Conflicting options')
-        if self.public not in ['True', 'False', None]:
-            raise Fault('Not acceptable value for public')
-        public = eval(self.public) if self.public else None
+        
+        args = {'content_type':self.content_type} if self.content_type else {}
         self.client.copy_object(src_container, src_object, dst_container,
-                                dst_object, public, headers)
+                                dst_object, meta, self.public, self.version,
+                                **args)
 
 @cli_command('set')
 class SetMeta(Command):
@@ -503,9 +508,12 @@ class UpdateObject(Command):
         parser.add_option('-f', action='store',
                           dest='srcpath', default=None,
                           help='file descriptor to read from: pass - for standard input')
-        parser.add_option('--public', action='store',
+        parser.add_option('--public', action='store_true',
                           dest='x_object_public', default=False,
-                          help='publish/unpublish object (\'True\'/\'False\')')
+                          help='make object publicly accessible')
+        parser.add_option('--replace', action='store_true',
+                          dest='replace', default=False,
+                          help='override metadata')
     
     def execute(self, path, *args):
         if path.find('=') != -1:
@@ -517,13 +525,14 @@ class UpdateObject(Command):
             key, sep, val = arg.partition('=')
             meta[key] = val
         
-        if self.no_sharing:
-            self.x_object_sharing = ''
         
         attrs = ['content_encoding', 'content_disposition', 'x_object_sharing',
-                 'x_object_public']
+                 'x_object_public', 'replace']
         args = self._build_args(attrs)
         
+        if self.no_sharing:
+            args['x_object_sharing'] = ''
+        
         container, sep, object = path.partition('/')
         
         f = None
@@ -534,8 +543,7 @@ class UpdateObject(Command):
             self.client.update_object_using_chunks(container, object, f,
                                                     meta=meta, **args)
         else:
-            data = f.read() if f else None
-            self.client.update_object(container, object, data, meta=meta, **args)
+            self.client.update_object(container, object, f, meta=meta, **args)
         if f:
             f.close()
 
@@ -545,41 +553,29 @@ class MoveObject(Command):
     description = 'move an object to a different location'
     
     def add_options(self, parser):
-        parser.add_option('--public', action='store',
-                          dest='public', default=None,
-                          help='publish/unpublish object (\'True\'/\'False\')')
+        parser.add_option('--public', action='store_true',
+                          dest='public', default=False,
+                          help='make object publicly accessible')
+        parser.add_option('--content-type', action='store',
+                          dest='content_type', default=None,
+                          help='change object\'s content type')
     
-    def execute(self, src, dst):
+    def execute(self, src, dst, *args):
         src_container, sep, src_object = src.partition('/')
         dst_container, sep, dst_object = dst.partition('/')
         if not sep:
             dst_container = src_container
             dst_object = dst
-        if self.public not in ['True', 'False', None]:
-            raise Fault('Not acceptable value for public')
-        public = eval(self.public) if self.public else None
-        self.client.move_object(src_container, src_object, dst_container,
-                                dst_object, public, headers)
-
-@cli_command('remove')
-class TrashObject(Command):
-    syntax = '<container>/<object>'
-    description = 'trash an object'
-    
-    def execute(self, src):
-        src_container, sep, src_object = src.partition('/')
         
-        self.client.trash_object(src_container, src_object)
-
-@cli_command('restore')
-class RestoreObject(Command):
-    syntax = '<container>/<object>'
-    description = 'restore a trashed object'
-    
-    def execute(self, src):
-        src_container, sep, src_object = src.partition('/')
+        #prepare user defined meta
+        meta = {}
+        for arg in args:
+            key, sep, val = arg.partition('=')
+            meta[key] = val
         
-        self.client.restore_object(src_container, src_object)
+        args = {'content_type':self.content_type} if self.content_type else {}
+        self.client.move_object(src_container, src_object, dst_container,
+                                dst_object, meta, self.public, **args)
 
 @cli_command('unset')
 class UnsetObject(Command):
@@ -668,6 +664,46 @@ class UnpublishObject(Command):
         
         self.client.unpublish_object(src_container, src_object)
 
+@cli_command('sharing')
+class SharingObject(Command):
+    syntax = 'list users sharing objects with the user'
+    description = 'list user accounts sharing objects with the user'
+    
+    def add_options(self, parser):
+        parser.add_option('-l', action='store_true', dest='detail',
+                          default=False, help='show detailed output')
+        parser.add_option('-n', action='store', type='int', dest='limit',
+                          default=10000, help='show limited output')
+        parser.add_option('--marker', action='store', type='str',
+                          dest='marker', default=None,
+                          help='show output greater then marker')
+        
+    
+    def execute(self):
+        attrs = ['limit', 'marker']
+        args = self._build_args(attrs)
+        args['format'] = 'json' if self.detail else 'text'
+        
+        print_list(self.client.list_shared_by_others(**args))
+
+@cli_command('send')
+class Send(Command):
+    syntax = '<file> <container>[/<prefix>]'
+    description = 'upload file to container (using prefix)'
+    
+    def execute(self, file, path):
+        container, sep, prefix = path.partition('/')
+        upload(self.client, file, container, prefix)
+
+@cli_command('receive')
+class Receive(Command):
+    syntax = '<container>/<object> <file>'
+    description = 'download object to file'
+    
+    def execute(self, path, file):
+        container, sep, object = path.partition('/')
+        download(self.client, container, object, file)
+
 def print_usage():
     cmd = Command('', [])
     parser = cmd.parser
@@ -682,9 +718,9 @@ def print_usage():
     print '\nCommands:\n' + '\n'.join(sorted(commands))
 
 def print_dict(d, header='name', f=stdout, detail=True):
-    header = header in d and header or 'subdir'
+    header = header if header in d else 'subdir'
     if header and header in d:
-        f.write('%s\n' %d.pop(header))
+        f.write('%s\n' %d.pop(header).encode('utf8'))
     if detail:
         patterns = ['^x_(account|container|object)_meta_(\w+)$']
         patterns.append(patterns[0].replace('_', '-'))
@@ -713,24 +749,6 @@ def print_versions(data, f=stdout):
     for id, t in data['versions']:
         f.write('%s @ %s\n' % (str(id).rjust(30), datetime.fromtimestamp(t)))
 
-def _get_user():
-        try:
-            return os.environ['PITHOS_USER']
-        except KeyError:
-            return getuser()
-
-def _get_auth():
-        try:
-            return os.environ['PITHOS_AUTH']
-        except KeyError:
-            return '0000'
-
-def _get_server():
-    try:
-        return os.environ['PITHOS_SERVER']
-    except KeyError:
-        return DEFAULT_HOST
-
 def main():
     try:
         name = argv[1]
@@ -741,7 +759,6 @@ def main():
     
     cmd = cls(name, argv[2:])
     
-    #cmd.execute(*cmd.args)
     try:
         cmd.execute(*cmd.args)
     except TypeError, e: