Revision 9e826a59

/dev/null
1
Tools in this dir depend on being able to import pithos.
2
You need to adjust PYTHONPATH accordingly for this to work.
3
e.g.
4
    export PYTHONPATH=$HOME/pithos
b/snf-pithos-tools/pithos/tools/dispatcher.py
38 38

  
39 39
from optparse import OptionParser
40 40

  
41
from carrot.connection import BrokerConnection
42
from carrot.messaging import Consumer
43
from carrot.messaging import Publisher
41
try:
42
    from carrot.connection import BrokerConnection
43
    from carrot.messaging import Consumer
44
    from carrot.messaging import Publisher
45
except ImportError:
46
    sys.stderr.write("Dispatcher requires 'carrot' python library to " \
47
                     "be installed\n")
48
    sys.exit(1)
49

  
44 50

  
45 51

  
46 52
BROKER_HOST = 'localhost'
......
56 62
DEBUG = False
57 63

  
58 64

  
59
if __name__ == '__main__':
65
def main():
60 66
    parser = OptionParser()
61 67
    parser.add_option('-v', '--verbose', action='store_true', default=False,
62 68
                      dest='verbose', help='Enable verbose logging')
......
81 87
    parser.add_option('--test', action='store_true', default=False,
82 88
                      dest='test', help='Produce a dummy message for testing')
83 89
    opts, args = parser.parse_args()
84
    
90

  
85 91
    if opts.verbose:
86 92
        DEBUG = True
87 93
    logging.basicConfig(format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
88 94
                        datefmt='%Y-%m-%d %H:%M:%S',
89 95
                        level=logging.DEBUG if DEBUG else logging.INFO)
90 96
    logger = logging.getLogger('dispatcher')
91
    
97

  
92 98
    conn = BrokerConnection(hostname=opts.host, port=opts.port,
93 99
                            userid=opts.user, password=opts.password,
94 100
                            virtual_host=opts.vhost)
......
103 109
    consumer = Consumer(connection=conn, queue=opts.queue,
104 110
                        exchange=opts.exchange, routing_key=opts.key,
105 111
                        exchange_type="topic")
106
    
112

  
107 113
    callback = None
108 114
    if opts.callback:
109 115
        cb = opts.callback.rsplit('.', 1)
......
111 117
            __import__(cb[0])
112 118
            cb_module = sys.modules[cb[0]]
113 119
            callback = getattr(cb_module, cb[1])
114
    
120

  
115 121
    def process_message(message_data, message):
116 122
        logger.debug('%s', message_data)
117 123
        if callback:
118 124
            callback(message_data)
119 125
        message.ack()
120
    
126

  
121 127
    consumer.register_callback(process_message)
122 128
    try:
123 129
        consumer.wait()
124 130
    except KeyboardInterrupt:
125 131
        pass
126 132

  
133
if __name__ == '__main__':
134
    main()
135

  
b/snf-pithos-tools/pithos/tools/fs.py
1
#!/usr/bin/env python
2

  
3
# Copyright 2011 GRNET S.A. All rights reserved.
4
# 
5
# Redistribution and use in source and binary forms, with or
6
# without modification, are permitted provided that the following
7
# conditions are met:
8
# 
9
#   1. Redistributions of source code must retain the above
10
#      copyright notice, this list of conditions and the following
11
#      disclaimer.
12
# 
13
#   2. Redistributions in binary form must reproduce the above
14
#      copyright notice, this list of conditions and the following
15
#      disclaimer in the documentation and/or other materials
16
#      provided with the distribution.
17
# 
18
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
# POSSIBILITY OF SUCH DAMAGE.
30
# 
31
# The views and conclusions contained in the software and
32
# documentation are those of the authors and should not be
33
# interpreted as representing official policies, either expressed
34
# or implied, of GRNET S.A.
35

  
36
from cStringIO import StringIO
37
from errno import (EACCES, EBADF, EINVAL, EISDIR, EIO, ENOENT, ENOTDIR,
38
                    ENOTEMPTY)
39
from getpass import getuser
40
from stat import S_IFDIR, S_IFREG
41
from sys import argv
42
from time import time
43

  
44
from pithos.lib.compat import parse_http_date
45
from pithos.lib.client import OOS_Client, Fault
46
from pithos.lib.fuse import FUSE, FuseOSError, Operations
47
from pithos.lib.util import get_user, get_auth, get_server
48

  
49

  
50
epoch = int(time())
51

  
52

  
53
class StoreFS(Operations):
54
    def __init__(self, verbose=False):
55
        self.verbose = verbose
56
        self.client = OOS_Client(get_server(), get_auth(), get_user())
57
    
58
    def __call__(self, op, path, *args):
59
        container, sep, object = path[1:].partition('/')
60
        if self.verbose:
61
            data = repr(args)[:100]
62
            print '-> %s %r %r %r' % (op, container, object, data)
63
        ret = '[Unhandled Exception]'
64
        
65
        try:
66
            if object:
67
                func = getattr(self, 'object_' + op, None)
68
                funcargs = (container, object) + args
69
            elif container:
70
                func = getattr(self, 'container_' + op, None)
71
                funcargs = (container,) + args
72
            else:
73
                func = getattr(self, 'account_' + op, None)
74
                funcargs = args
75

  
76
            if not func:
77
                # Fallback to defaults
78
                func = getattr(self, op)
79
                funcargs = (path,) + args
80
            
81
            ret = func(*funcargs)
82
            return ret
83
        except FuseOSError, e:
84
            ret = str(e)
85
            raise
86
        finally:
87
            if self.verbose:
88
                print '<-', op, repr(ret)
89
    
90
    
91
    def _get_container_meta(self, container, **kwargs):
92
        try:
93
            return self.client.retrieve_container_metadata(container, **kwargs)
94
        except Fault:
95
            raise FuseOSError(ENOENT)
96
    
97
    def _get_object_meta(self, container, object, **kwargs):
98
        try:
99
            return self.client.retrieve_object_metadata(container, object,
100
                                                        **kwargs)
101
        except Fault:
102
            raise FuseOSError(ENOENT)
103
    
104
    
105
    # Global
106
    
107
    def statfs(self, path):
108
        return dict(f_bsize=1024, f_blocks=1024**2, f_bfree=1024**2,
109
                    f_bavail=1024**2)
110
    
111
    
112
    # Account Level
113
    
114
    def account_chmod(self, mode):
115
        self.client.update_account_metadata(mode=str(mode))
116
    
117
    def account_chown(self, uid, gid):
118
        self.client.update_account_metadata(uid=uid, gid=gid)
119
    
120
    def account_getattr(self, fh=None):
121
        meta = self.client.retrieve_account_metadata()
122
        mode = int(meta.get('x-account-meta-mode', 0755))
123
        last_modified = meta.get('last-modified', None)
124
        modified = parse_http_date(last_modified) if last_modified else epoch
125
        count = int(meta['x-account-container-count'])
126
        uid = int(meta.get('x-account-meta-uid', 0))
127
        gid = int(meta.get('x-account-meta-gid', 0))
128
        
129
        return {
130
            'st_mode': S_IFDIR | mode,
131
            'st_nlink': 2 + count,
132
            'st_uid': uid,
133
            'st_gid': gid,
134
            'st_ctime': epoch,
135
            'st_mtime': modified,
136
            'st_atime': modified}
137
    
138
    def account_getxattr(self, name, position=0):
139
        meta = self.client.retrieve_account_metadata(restricted=True)
140
        return meta.get('xattr-' + name, '')
141
    
142
    def account_listxattr(self):
143
        meta = self.client.retrieve_account_metadata(restricted=True)
144
        prefix = 'xattr-'
145
        return [k[len(prefix):] for k in meta if k.startswith(prefix)]
146
    
147
    def account_readdir(self, fh):
148
        return ['.', '..'] + self.client.list_containers() or []
149
    
150
    def account_removexattr(self, name):
151
        attr = 'xattr-' + name
152
        self.client.delete_account_metadata([attr])
153
    
154
    def account_setxattr(self, name, value, options, position=0):
155
        attr = 'xattr-' + name
156
        meta = {attr: value}
157
        self.client.update_account_metadata(**meta)
158
    
159
    
160
    # Container Level
161
    
162
    def container_chmod(self, container, mode):
163
        self.client.update_container_metadata(container, mode=str(mode))
164
    
165
    def container_chown(self, container, uid, gid):
166
        self.client.update_container_metadata(container, uid=uid, gid=gid)
167
    
168
    def container_getattr(self, container, fh=None):
169
        meta = self._get_container_meta(container)
170
        mode = int(meta.get('x-container-meta-mode', 0755))
171
        modified = parse_http_date(meta['last-modified'])
172
        count = int(meta['x-container-object-count'])
173
        uid = int(meta.get('x-account-meta-uid', 0))
174
        gid = int(meta.get('x-account-meta-gid', 0))
175
        
176
        return {
177
            'st_mode': S_IFDIR | mode,
178
            'st_nlink': 2 + count,
179
            'st_uid': uid,
180
            'st_gid': gid,
181
            'st_ctime': epoch,
182
            'st_mtime': modified,
183
            'st_atime': modified}
184
    
185
    def container_getxattr(self, container, name, position=0):
186
        meta = self._get_container_meta(container)
187
        return meta.get('xattr-' + name, '')
188
    
189
    def container_listxattr(self, container):
190
        meta = self._get_container_meta(container, restricted=True)
191
        prefix = 'xattr-'
192
        return [k[len(prefix):] for k in meta if k.startswith(prefix)]
193
    
194
    def container_mkdir(self, container, mode):
195
        mode = str(mode & 0777)
196
        self.client.create_container(container, mode=mode)
197
    
198
    def container_readdir(self, container, fh):
199
        objects = self.client.list_objects(container, delimiter='/', prefix='')
200
        files = [o for o in objects if not o.endswith('/')]
201
        return ['.', '..'] + files
202
    
203
    def container_removexattr(self, container, name):
204
        attr = 'xattr-' + name
205
        self.client.delete_container_metadata(container, [attr])
206
    
207
    def container_rename(self, container, path):
208
        new_container, sep, new_object = path[1:].partition('/')
209
        if not new_container or new_object:
210
            raise FuseOSError(EINVAL)
211
        self.client.delete_container(container)
212
        self.client.create_container(new_container)
213
    
214
    def container_rmdir(self, container):
215
        try:
216
            self.client.delete_container(container)
217
        except Fault:
218
            raise FuseOSError(ENOENT)
219
    
220
    def container_setxattr(self, container, name, value, options, position=0):
221
        attr = 'xattr-' + name
222
        meta = {attr: value}
223
        self.client.update_container_metadata(container, **meta)
224
    
225
    
226
    # Object Level
227
    
228
    def object_chmod(self, container, object, mode):
229
        self.client.update_object_metadata(container, object, mode=str(mode))
230
    
231
    def object_chown(self, container, uid, gid):
232
        self.client.update_object_metadata(container, object,
233
                                            uid=str(uid), gid=str(gid))
234
    
235
    def object_create(self, container, object, mode, fi=None):
236
        mode &= 0777
237
        self.client.create_object(container, object,
238
                                    f=None,
239
                                    content_type='application/octet-stream',
240
                                    mode=str(mode))
241
        return 0
242
    
243
    def object_getattr(self, container, object, fh=None):
244
        meta = self._get_object_meta(container, object)
245
        modified = parse_http_date(meta['last-modified'])
246
        uid = int(meta.get('x-account-meta-uid', 0))
247
        gid = int(meta.get('x-account-meta-gid', 0))
248
        size = int(meta.get('content-length', 0))
249
        
250
        if meta['content-type'] == 'application/directory':
251
            mode = int(meta.get('x-object-meta-mode', 0755))
252
            flags = S_IFDIR
253
            nlink = 2
254
        else:
255
            mode = int(meta.get('x-object-meta-mode', 0644))
256
            flags = S_IFREG
257
            nlink = 1
258
        
259
        return {
260
            'st_mode': flags | mode,
261
            'st_nlink': nlink,
262
            'st_uid': uid,
263
            'st_gid': gid,
264
            'st_ctime': epoch,
265
            'st_mtime': modified,
266
            'st_atime': modified,
267
            'st_size': size}
268
    
269
    def object_getxattr(self, container, object, name, position=0):
270
        meta = self._get_object_meta(container, object, restricted=True)
271
        return meta.get('xattr-' + name, '')
272
    
273
    def object_listxattr(self, container, object):
274
        meta = self._get_object_meta(container, object, restricted=True)
275
        prefix = 'xattr-'
276
        return [k[len(prefix):] for k in meta if k.startswith(prefix)]
277
    
278
    def object_mkdir(self, container, object, mode):
279
        mode = str(mode & 0777)
280
        self.client.create_directory_marker(container, object)
281
        self.client.update_object_metadata(container, object, mode=mode)
282
    
283
    def object_read(self, container, object, nbyte, offset, fh):
284
        data = self.client.retrieve_object(container, object)
285
        return data[offset:offset + nbyte]
286
    
287
    def object_readdir(self, container, object, fh):
288
        objects = self.client.list_objects(container, delimiter='/',
289
                                            prefix=object)
290
        files = [o.rpartition('/')[2] for o in objects if not o.endswith('/')]
291
        return ['.', '..'] + files
292
    
293
    def object_removexattr(self, container, object, name):
294
        attr = 'xattr-' + name
295
        self.client.delete_object_metadata(container, object, [attr])
296
    
297
    def object_rename(self, container, object, path):
298
        new_container, sep, new_object = path[1:].partition('/')
299
        if not new_container or not new_object:
300
            raise FuseOSError(EINVAL)
301
        self.client.move_object(container, object, new_container, new_object)
302
    
303
    def object_rmdir(self, container, object):
304
        self.client.delete_object(container, object)
305
    
306
    def object_setxattr(self, container, object, name, value, options,
307
                        position=0):
308
        attr = 'xattr-' + name
309
        meta = {attr: value}
310
        self.client.update_object_metadata(container, object, **meta)
311
    
312
    def object_truncate(self, container, object, length, fh=None):
313
        data = self.client.retrieve_object(container, object)
314
        f = StringIO(data[:length])
315
        self.client.update_object(container, object, f)
316
    
317
    def object_unlink(self, container, object):
318
        self.client.delete_object(container, object)
319
    
320
    def object_write(self, container, object, data, offset, fh):
321
        f = StringIO(data)
322
        self.client.update_object(container, object, f, offset=offset)
323
        return len(data)
324

  
325

  
326
def main():
327
    if len(argv) != 2:
328
        print 'usage: %s <mountpoint>' % argv[0]
329
        exit(1)
330
    
331
    user = getuser()
332
    fs = StoreFS(verbose=True)
333
    fuse = FUSE(fs, argv[1], foreground=True)
334

  
335

  
336
if __name__ == '__main__':
337
    main()
338

  
/dev/null
1
#!/usr/bin/env python
2

  
3
# Copyright 2011 GRNET S.A. All rights reserved.
4
# 
5
# Redistribution and use in source and binary forms, with or
6
# without modification, are permitted provided that the following
7
# conditions are met:
8
# 
9
#   1. Redistributions of source code must retain the above
10
#      copyright notice, this list of conditions and the following
11
#      disclaimer.
12
# 
13
#   2. Redistributions in binary form must reproduce the above
14
#      copyright notice, this list of conditions and the following
15
#      disclaimer in the documentation and/or other materials
16
#      provided with the distribution.
17
# 
18
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
# POSSIBILITY OF SUCH DAMAGE.
30
# 
31
# The views and conclusions contained in the software and
32
# documentation are those of the authors and should not be
33
# interpreted as representing official policies, either expressed
34
# or implied, of GRNET S.A.
35

  
36
from cStringIO import StringIO
37
from errno import (EACCES, EBADF, EINVAL, EISDIR, EIO, ENOENT, ENOTDIR,
38
                    ENOTEMPTY)
39
from getpass import getuser
40
from stat import S_IFDIR, S_IFREG
41
from sys import argv
42
from time import time
43

  
44
from pithos.lib.compat import parse_http_date
45
from pithos.lib.client import OOS_Client, Fault
46
from pithos.lib.fuse import FUSE, FuseOSError, Operations
47
from pithos.lib.util import get_user, get_auth, get_server
48

  
49

  
50
epoch = int(time())
51

  
52

  
53
class StoreFS(Operations):
54
    def __init__(self, verbose=False):
55
        self.verbose = verbose
56
        self.client = OOS_Client(get_server(), get_auth(), get_user())
57
    
58
    def __call__(self, op, path, *args):
59
        container, sep, object = path[1:].partition('/')
60
        if self.verbose:
61
            data = repr(args)[:100]
62
            print '-> %s %r %r %r' % (op, container, object, data)
63
        ret = '[Unhandled Exception]'
64
        
65
        try:
66
            if object:
67
                func = getattr(self, 'object_' + op, None)
68
                funcargs = (container, object) + args
69
            elif container:
70
                func = getattr(self, 'container_' + op, None)
71
                funcargs = (container,) + args
72
            else:
73
                func = getattr(self, 'account_' + op, None)
74
                funcargs = args
75

  
76
            if not func:
77
                # Fallback to defaults
78
                func = getattr(self, op)
79
                funcargs = (path,) + args
80
            
81
            ret = func(*funcargs)
82
            return ret
83
        except FuseOSError, e:
84
            ret = str(e)
85
            raise
86
        finally:
87
            if self.verbose:
88
                print '<-', op, repr(ret)
89
    
90
    
91
    def _get_container_meta(self, container, **kwargs):
92
        try:
93
            return self.client.retrieve_container_metadata(container, **kwargs)
94
        except Fault:
95
            raise FuseOSError(ENOENT)
96
    
97
    def _get_object_meta(self, container, object, **kwargs):
98
        try:
99
            return self.client.retrieve_object_metadata(container, object,
100
                                                        **kwargs)
101
        except Fault:
102
            raise FuseOSError(ENOENT)
103
    
104
    
105
    # Global
106
    
107
    def statfs(self, path):
108
        return dict(f_bsize=1024, f_blocks=1024**2, f_bfree=1024**2,
109
                    f_bavail=1024**2)
110
    
111
    
112
    # Account Level
113
    
114
    def account_chmod(self, mode):
115
        self.client.update_account_metadata(mode=str(mode))
116
    
117
    def account_chown(self, uid, gid):
118
        self.client.update_account_metadata(uid=uid, gid=gid)
119
    
120
    def account_getattr(self, fh=None):
121
        meta = self.client.retrieve_account_metadata()
122
        mode = int(meta.get('x-account-meta-mode', 0755))
123
        last_modified = meta.get('last-modified', None)
124
        modified = parse_http_date(last_modified) if last_modified else epoch
125
        count = int(meta['x-account-container-count'])
126
        uid = int(meta.get('x-account-meta-uid', 0))
127
        gid = int(meta.get('x-account-meta-gid', 0))
128
        
129
        return {
130
            'st_mode': S_IFDIR | mode,
131
            'st_nlink': 2 + count,
132
            'st_uid': uid,
133
            'st_gid': gid,
134
            'st_ctime': epoch,
135
            'st_mtime': modified,
136
            'st_atime': modified}
137
    
138
    def account_getxattr(self, name, position=0):
139
        meta = self.client.retrieve_account_metadata(restricted=True)
140
        return meta.get('xattr-' + name, '')
141
    
142
    def account_listxattr(self):
143
        meta = self.client.retrieve_account_metadata(restricted=True)
144
        prefix = 'xattr-'
145
        return [k[len(prefix):] for k in meta if k.startswith(prefix)]
146
    
147
    def account_readdir(self, fh):
148
        return ['.', '..'] + self.client.list_containers() or []
149
    
150
    def account_removexattr(self, name):
151
        attr = 'xattr-' + name
152
        self.client.delete_account_metadata([attr])
153
    
154
    def account_setxattr(self, name, value, options, position=0):
155
        attr = 'xattr-' + name
156
        meta = {attr: value}
157
        self.client.update_account_metadata(**meta)
158
    
159
    
160
    # Container Level
161
    
162
    def container_chmod(self, container, mode):
163
        self.client.update_container_metadata(container, mode=str(mode))
164
    
165
    def container_chown(self, container, uid, gid):
166
        self.client.update_container_metadata(container, uid=uid, gid=gid)
167
    
168
    def container_getattr(self, container, fh=None):
169
        meta = self._get_container_meta(container)
170
        mode = int(meta.get('x-container-meta-mode', 0755))
171
        modified = parse_http_date(meta['last-modified'])
172
        count = int(meta['x-container-object-count'])
173
        uid = int(meta.get('x-account-meta-uid', 0))
174
        gid = int(meta.get('x-account-meta-gid', 0))
175
        
176
        return {
177
            'st_mode': S_IFDIR | mode,
178
            'st_nlink': 2 + count,
179
            'st_uid': uid,
180
            'st_gid': gid,
181
            'st_ctime': epoch,
182
            'st_mtime': modified,
183
            'st_atime': modified}
184
    
185
    def container_getxattr(self, container, name, position=0):
186
        meta = self._get_container_meta(container)
187
        return meta.get('xattr-' + name, '')
188
    
189
    def container_listxattr(self, container):
190
        meta = self._get_container_meta(container, restricted=True)
191
        prefix = 'xattr-'
192
        return [k[len(prefix):] for k in meta if k.startswith(prefix)]
193
    
194
    def container_mkdir(self, container, mode):
195
        mode = str(mode & 0777)
196
        self.client.create_container(container, mode=mode)
197
    
198
    def container_readdir(self, container, fh):
199
        objects = self.client.list_objects(container, delimiter='/', prefix='')
200
        files = [o for o in objects if not o.endswith('/')]
201
        return ['.', '..'] + files
202
    
203
    def container_removexattr(self, container, name):
204
        attr = 'xattr-' + name
205
        self.client.delete_container_metadata(container, [attr])
206
    
207
    def container_rename(self, container, path):
208
        new_container, sep, new_object = path[1:].partition('/')
209
        if not new_container or new_object:
210
            raise FuseOSError(EINVAL)
211
        self.client.delete_container(container)
212
        self.client.create_container(new_container)
213
    
214
    def container_rmdir(self, container):
215
        try:
216
            self.client.delete_container(container)
217
        except Fault:
218
            raise FuseOSError(ENOENT)
219
    
220
    def container_setxattr(self, container, name, value, options, position=0):
221
        attr = 'xattr-' + name
222
        meta = {attr: value}
223
        self.client.update_container_metadata(container, **meta)
224
    
225
    
226
    # Object Level
227
    
228
    def object_chmod(self, container, object, mode):
229
        self.client.update_object_metadata(container, object, mode=str(mode))
230
    
231
    def object_chown(self, container, uid, gid):
232
        self.client.update_object_metadata(container, object,
233
                                            uid=str(uid), gid=str(gid))
234
    
235
    def object_create(self, container, object, mode, fi=None):
236
        mode &= 0777
237
        self.client.create_object(container, object,
238
                                    f=None,
239
                                    content_type='application/octet-stream',
240
                                    mode=str(mode))
241
        return 0
242
    
243
    def object_getattr(self, container, object, fh=None):
244
        meta = self._get_object_meta(container, object)
245
        modified = parse_http_date(meta['last-modified'])
246
        uid = int(meta.get('x-account-meta-uid', 0))
247
        gid = int(meta.get('x-account-meta-gid', 0))
248
        size = int(meta.get('content-length', 0))
249
        
250
        if meta['content-type'] == 'application/directory':
251
            mode = int(meta.get('x-object-meta-mode', 0755))
252
            flags = S_IFDIR
253
            nlink = 2
254
        else:
255
            mode = int(meta.get('x-object-meta-mode', 0644))
256
            flags = S_IFREG
257
            nlink = 1
258
        
259
        return {
260
            'st_mode': flags | mode,
261
            'st_nlink': nlink,
262
            'st_uid': uid,
263
            'st_gid': gid,
264
            'st_ctime': epoch,
265
            'st_mtime': modified,
266
            'st_atime': modified,
267
            'st_size': size}
268
    
269
    def object_getxattr(self, container, object, name, position=0):
270
        meta = self._get_object_meta(container, object, restricted=True)
271
        return meta.get('xattr-' + name, '')
272
    
273
    def object_listxattr(self, container, object):
274
        meta = self._get_object_meta(container, object, restricted=True)
275
        prefix = 'xattr-'
276
        return [k[len(prefix):] for k in meta if k.startswith(prefix)]
277
    
278
    def object_mkdir(self, container, object, mode):
279
        mode = str(mode & 0777)
280
        self.client.create_directory_marker(container, object)
281
        self.client.update_object_metadata(container, object, mode=mode)
282
    
283
    def object_read(self, container, object, nbyte, offset, fh):
284
        data = self.client.retrieve_object(container, object)
285
        return data[offset:offset + nbyte]
286
    
287
    def object_readdir(self, container, object, fh):
288
        objects = self.client.list_objects(container, delimiter='/',
289
                                            prefix=object)
290
        files = [o.rpartition('/')[2] for o in objects if not o.endswith('/')]
291
        return ['.', '..'] + files
292
    
293
    def object_removexattr(self, container, object, name):
294
        attr = 'xattr-' + name
295
        self.client.delete_object_metadata(container, object, [attr])
296
    
297
    def object_rename(self, container, object, path):
298
        new_container, sep, new_object = path[1:].partition('/')
299
        if not new_container or not new_object:
300
            raise FuseOSError(EINVAL)
301
        self.client.move_object(container, object, new_container, new_object)
302
    
303
    def object_rmdir(self, container, object):
304
        self.client.delete_object(container, object)
305
    
306
    def object_setxattr(self, container, object, name, value, options,
307
                        position=0):
308
        attr = 'xattr-' + name
309
        meta = {attr: value}
310
        self.client.update_object_metadata(container, object, **meta)
311
    
312
    def object_truncate(self, container, object, length, fh=None):
313
        data = self.client.retrieve_object(container, object)
314
        f = StringIO(data[:length])
315
        self.client.update_object(container, object, f)
316
    
317
    def object_unlink(self, container, object):
318
        self.client.delete_object(container, object)
319
    
320
    def object_write(self, container, object, data, offset, fh):
321
        f = StringIO(data)
322
        self.client.update_object(container, object, f, offset=offset)
323
        return len(data)
324

  
325

  
326
if __name__ == '__main__':
327
    if len(argv) != 2:
328
        print 'usage: %s <mountpoint>' % argv[0]
329
        exit(1)
330
    
331
    user = getuser()
332
    fs = StoreFS(verbose=True)
333
    fuse = FUSE(fs, argv[1], foreground=True)
/dev/null
1
#!/usr/bin/env python
2

  
3
# Copyright 2011 GRNET S.A. All rights reserved.
4
# 
5
# Redistribution and use in source and binary forms, with or
6
# without modification, are permitted provided that the following
7
# conditions are met:
8
# 
9
#   1. Redistributions of source code must retain the above
10
#      copyright notice, this list of conditions and the following
11
#      disclaimer.
12
# 
13
#   2. Redistributions in binary form must reproduce the above
14
#      copyright notice, this list of conditions and the following
15
#      disclaimer in the documentation and/or other materials
16
#      provided with the distribution.
17
# 
18
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
# POSSIBILITY OF SUCH DAMAGE.
30
# 
31
# The views and conclusions contained in the software and
32
# documentation are those of the authors and should not be
33
# interpreted as representing official policies, either expressed
34
# or implied, of GRNET S.A.
35

  
36
from getpass import getuser
37
from optparse import OptionParser
38
from os import environ
39
from sys import argv, exit, stdin, stdout
40
from datetime import datetime
41

  
42
from pithos.lib.client import Pithos_Client, Fault
43
from pithos.lib.util import get_user, get_auth, get_server, get_api
44
from pithos.lib.transfer import upload, download
45

  
46
import json
47
import logging
48
import types
49
import re
50
import time as _time
51
import os
52

  
53
_cli_commands = {}
54

  
55
def cli_command(*args):
56
    def decorator(cls):
57
        cls.commands = args
58
        for name in args:
59
            _cli_commands[name] = cls
60
        return cls
61
    return decorator
62

  
63
def class_for_cli_command(name):
64
    return _cli_commands[name]
65

  
66
class Command(object):
67
    syntax = ''
68
    
69
    def __init__(self, name, argv):
70
        parser = OptionParser('%%prog %s [options] %s' % (name, self.syntax))
71
        parser.add_option('--host', dest='host', metavar='HOST',
72
                          default=get_server(), help='use server HOST')
73
        parser.add_option('--user', dest='user', metavar='USERNAME',
74
                          default=get_user(),
75
                          help='use account USERNAME')
76
        parser.add_option('--token', dest='token', metavar='AUTH',
77
                          default=get_auth(),
78
                          help='use account AUTH')
79
        parser.add_option('--api', dest='api', metavar='API',
80
                          default=get_api(), help='use api API')
81
        parser.add_option('-v', action='store_true', dest='verbose',
82
                          default=False, help='use verbose output')
83
        parser.add_option('-d', action='store_true', dest='debug',
84
                          default=False, help='use debug output')
85
        self.add_options(parser)
86
        options, args = parser.parse_args(argv)
87
        
88
        # Add options to self
89
        for opt in parser.option_list:
90
            key = opt.dest
91
            if key:
92
                val = getattr(options, key)
93
                setattr(self, key, val)
94
        
95
        self.client = Pithos_Client(self.host, self.token, self.user, self.api, self.verbose,
96
                             self.debug)
97
        
98
        self.parser = parser
99
        self.args = args
100
    
101
    def _build_args(self, attrs):
102
        args = {}
103
        for a in [a for a in attrs if getattr(self, a)]:
104
            args[a] = getattr(self, a)
105
        return args
106

  
107
    def add_options(self, parser):
108
        pass
109
    
110
    def execute(self, *args):
111
        pass
112

  
113
@cli_command('list', 'ls')
114
class List(Command):
115
    syntax = '[<container>[/<object>]]'
116
    description = 'list containers or objects'
117
    
118
    def add_options(self, parser):
119
        parser.add_option('-l', action='store_true', dest='detail',
120
                          default=False, help='show detailed output')
121
        parser.add_option('-n', action='store', type='int', dest='limit',
122
                          default=10000, help='show limited output')
123
        parser.add_option('--marker', action='store', type='str',
124
                          dest='marker', default=None,
125
                          help='show output greater then marker')
126
        parser.add_option('--prefix', action='store', type='str',
127
                          dest='prefix', default=None,
128
                          help='show output starting with prefix')
129
        parser.add_option('--delimiter', action='store', type='str',
130
                          dest='delimiter', default=None,
131
                          help='show output up to the delimiter')
132
        parser.add_option('--path', action='store', type='str',
133
                          dest='path', default=None,
134
                          help='show output starting with prefix up to /')
135
        parser.add_option('--meta', action='store', type='str',
136
                          dest='meta', default=None,
137
                          help='show output having the specified meta keys')
138
        parser.add_option('--if-modified-since', action='store', type='str',
139
                          dest='if_modified_since', default=None,
140
                          help='show output if modified since then')
141
        parser.add_option('--if-unmodified-since', action='store', type='str',
142
                          dest='if_unmodified_since', default=None,
143
                          help='show output if not modified since then')
144
        parser.add_option('--until', action='store', dest='until',
145
                          default=None, help='show metadata until that date')
146
        parser.add_option('--format', action='store', dest='format',
147
                          default='%d/%m/%Y', help='format to parse until date')
148
    
149
    def execute(self, container=None):
150
        if container:
151
            self.list_objects(container)
152
        else:
153
            self.list_containers()
154
    
155
    def list_containers(self):
156
        attrs = ['limit', 'marker', 'if_modified_since',
157
                 'if_unmodified_since']
158
        args = self._build_args(attrs)
159
        args['format'] = 'json' if self.detail else 'text'
160
        
161
        if getattr(self, 'until'):
162
            t = _time.strptime(self.until, self.format)
163
            args['until'] = int(_time.mktime(t))
164
        
165
        l = self.client.list_containers(**args)
166
        print_list(l)
167
    
168
    def list_objects(self, container):
169
        #prepate params
170
        params = {}
171
        attrs = ['limit', 'marker', 'prefix', 'delimiter', 'path',
172
                 'meta', 'if_modified_since', 'if_unmodified_since']
173
        args = self._build_args(attrs)
174
        args['format'] = 'json' if self.detail else 'text'
175
        
176
        if self.until:
177
            t = _time.strptime(self.until, self.format)
178
            args['until'] = int(_time.mktime(t))
179
        
180
        container, sep, object = container.partition('/')
181
        if object:
182
            return
183
        
184
        detail = 'json'
185
        #if request with meta quering disable trash filtering
186
        show_trashed = True if self.meta else False
187
        l = self.client.list_objects(container, **args)
188
        print_list(l, detail=self.detail)
189

  
190
@cli_command('meta')
191
class Meta(Command):
192
    syntax = '[<container>[/<object>]]'
193
    description = 'get account/container/object metadata'
194
    
195
    def add_options(self, parser):
196
        parser.add_option('-r', action='store_true', dest='restricted',
197
                          default=False, help='show only user defined metadata')
198
        parser.add_option('--until', action='store', dest='until',
199
                          default=None, help='show metadata until that date')
200
        parser.add_option('--format', action='store', dest='format',
201
                          default='%d/%m/%Y', help='format to parse until date')
202
        parser.add_option('--version', action='store', dest='version',
203
                          default=None, help='show specific version \
204
                                  (applies only for objects)')
205
    
206
    def execute(self, path=''):
207
        container, sep, object = path.partition('/')
208
        args = {'restricted': self.restricted}
209
        if getattr(self, 'until'):
210
            t = _time.strptime(self.until, self.format)
211
            args['until'] = int(_time.mktime(t))
212
        
213
        if object:
214
            meta = self.client.retrieve_object_metadata(container, object,
215
                                                        self.restricted,
216
                                                        self.version)
217
        elif container:
218
            meta = self.client.retrieve_container_metadata(container, **args)
219
        else:
220
            meta = self.client.retrieve_account_metadata(**args)
221
        if meta == None:
222
            print 'Entity does not exist'
223
        else:
224
            print_dict(meta, header=None)
225

  
226
@cli_command('create')
227
class CreateContainer(Command):
228
    syntax = '<container> [key=val] [...]'
229
    description = 'create a container'
230
    
231
    def execute(self, container, *args):
232
        meta = {}
233
        for arg in args:
234
            key, sep, val = arg.partition('=')
235
            meta[key] = val
236
        ret = self.client.create_container(container, **meta)
237
        if not ret:
238
            print 'Container already exists'
239

  
240
@cli_command('delete', 'rm')
241
class Delete(Command):
242
    syntax = '<container>[/<object>]'
243
    description = 'delete a container or an object'
244
    
245
    def add_options(self, parser):
246
        parser.add_option('--until', action='store', dest='until',
247
                          default=None, help='remove history until that date')
248
        parser.add_option('--format', action='store', dest='format',
249
                          default='%d/%m/%Y', help='format to parse until date')
250
    
251
    def execute(self, path):
252
        container, sep, object = path.partition('/')
253
        until = None
254
        if getattr(self, 'until'):
255
            t = _time.strptime(self.until, self.format)
256
            until = int(_time.mktime(t))
257
        
258
        if object:
259
            self.client.delete_object(container, object, until)
260
        else:
261
            self.client.delete_container(container, until)
262

  
263
@cli_command('get')
264
class GetObject(Command):
265
    syntax = '<container>/<object>'
266
    description = 'get the data of an object'
267
    
268
    def add_options(self, parser):
269
        parser.add_option('-l', action='store_true', dest='detail',
270
                          default=False, help='show detailed output')
271
        parser.add_option('--range', action='store', dest='range',
272
                          default=None, help='show range of data')
273
        parser.add_option('--if-range', action='store', dest='if_range',
274
                          default=None, help='show range of data')
275
        parser.add_option('--if-match', action='store', dest='if_match',
276
                          default=None, help='show output if ETags match')
277
        parser.add_option('--if-none-match', action='store',
278
                          dest='if_none_match', default=None,
279
                          help='show output if ETags don\'t match')
280
        parser.add_option('--if-modified-since', action='store', type='str',
281
                          dest='if_modified_since', default=None,
282
                          help='show output if modified since then')
283
        parser.add_option('--if-unmodified-since', action='store', type='str',
284
                          dest='if_unmodified_since', default=None,
285
                          help='show output if not modified since then')
286
        parser.add_option('-o', action='store', type='str',
287
                          dest='file', default=None,
288
                          help='save output in file')
289
        parser.add_option('--version', action='store', type='str',
290
                          dest='version', default=None,
291
                          help='get the specific \
292
                               version')
293
        parser.add_option('--versionlist', action='store_true',
294
                          dest='versionlist', default=False,
295
                          help='get the full object version list')
296
    
297
    def execute(self, path):
298
        attrs = ['if_match', 'if_none_match', 'if_modified_since',
299
                 'if_unmodified_since']
300
        args = self._build_args(attrs)
301
        args['format'] = 'json' if self.detail else 'text'
302
        if self.range:
303
            args['range'] = 'bytes=%s' % self.range
304
        if getattr(self, 'if_range'):
305
            args['if-range'] = 'If-Range:%s' % getattr(self, 'if_range')
306
        
307
        container, sep, object = path.partition('/')
308
        data = None
309
        if self.versionlist:
310
            if 'detail' in args.keys():
311
                args.pop('detail')
312
            args.pop('format')
313
            self.detail = True
314
            data = self.client.retrieve_object_versionlist(container, object, **args)
315
        elif self.version:
316
            data = self.client.retrieve_object_version(container, object,
317
                                                       self.version, **args)
318
        else:
319
            data = self.client.retrieve_object(container, object, **args)    
320
        
321
        f = open(self.file, 'w') if self.file else stdout
322
        if self.detail:
323
            if self.versionlist:
324
                print_versions(data, f=f)
325
            else:
326
                print_dict(data, f=f)
327
        else:
328
            f.write(data)
329
        f.close()
330

  
331
@cli_command('mkdir')
332
class PutMarker(Command):
333
    syntax = '<container>/<directory marker>'
334
    description = 'create a directory marker'
335
    
336
    def execute(self, path):
337
        container, sep, object = path.partition('/')
338
        self.client.create_directory_marker(container, object)
339

  
340
@cli_command('put')
341
class PutObject(Command):
342
    syntax = '<container>/<object> [key=val] [...]'
343
    description = 'create/override object'
344
    
345
    def add_options(self, parser):
346
        parser.add_option('--use_hashes', action='store_true', dest='use_hashes',
347
                          default=False, help='provide hashmap instead of data')
348
        parser.add_option('--chunked', action='store_true', dest='chunked',
349
                          default=False, help='set chunked transfer mode')
350
        parser.add_option('--etag', action='store', dest='etag',
351
                          default=None, help='check written data')
352
        parser.add_option('--content-encoding', action='store',
353
                          dest='content_encoding', default=None,
354
                          help='provide the object MIME content type')
355
        parser.add_option('--content-disposition', action='store', type='str',
356
                          dest='content_disposition', default=None,
357
                          help='provide the presentation style of the object')
358
        #parser.add_option('-S', action='store',
359
        #                  dest='segment_size', default=False,
360
        #                  help='use for large file support')
361
        parser.add_option('--manifest', action='store',
362
                          dest='x_object_manifest', default=None,
363
                          help='upload a manifestation file')
364
        parser.add_option('--content-type', action='store',
365
                          dest='content_type', default=None,
366
                          help='create object with specific content type')
367
        parser.add_option('--sharing', action='store',
368
                          dest='x_object_sharing', default=None,
369
                          help='define sharing object policy')
370
        parser.add_option('-f', action='store',
371
                          dest='srcpath', default=None,
372
                          help='file descriptor to read from (pass - for standard input)')
373
        parser.add_option('--public', action='store_true',
374
                          dest='x_object_public', default=False,
375
                          help='make object publicly accessible')
376
    
377
    def execute(self, path, *args):
378
        if path.find('=') != -1:
379
            raise Fault('Missing path argument')
380
        
381
        #prepare user defined meta
382
        meta = {}
383
        for arg in args:
384
            key, sep, val = arg.partition('=')
385
            meta[key] = val
386
        
387
        attrs = ['etag', 'content_encoding', 'content_disposition',
388
                 'content_type', 'x_object_sharing', 'x_object_public']
389
        args = self._build_args(attrs)
390
        
391
        container, sep, object = path.partition('/')
392
        
393
        f = None
394
        if self.srcpath:
395
            f = open(self.srcpath) if self.srcpath != '-' else stdin
396
        
397
        if self.use_hashes and not f:
398
            raise Fault('Illegal option combination')
399
        
400
        if self.chunked:
401
            self.client.create_object_using_chunks(container, object, f,
402
                                                    meta=meta, **args)
403
        elif self.use_hashes:
404
            data = f.read()
405
            if data is object:
406
                hashmap = json.loads()
407
                self.client.create_object_by_hashmap(container, object, hashmap,
408
                                                 meta=meta, **args)
409
            else:
410
                print "Expected object"
411
        elif self.x_object_manifest:
412
            self.client.create_manifestation(container, object, self.x_object_manifest)
413
        elif not f:
414
            self.client.create_zero_length_object(container, object, meta=meta, **args)
415
        else:
416
            self.client.create_object(container, object, f, meta=meta, **args)
417
        if f:
418
            f.close()
419

  
420
@cli_command('copy', 'cp')
421
class CopyObject(Command):
422
    syntax = '<src container>/<src object> [<dst container>/]<dst object> [key=val] [...]'
423
    description = 'copy an object to a different location'
424
    
425
    def add_options(self, parser):
426
        parser.add_option('--version', action='store',
427
                          dest='version', default=False,
428
                          help='copy specific version')
429
        parser.add_option('--public', action='store_true',
430
                          dest='public', default=False,
431
                          help='make object publicly accessible')
432
        parser.add_option('--content-type', action='store',
433
                          dest='content_type', default=None,
434
                          help='change object\'s content type')
435
    
436
    def execute(self, src, dst, *args):
437
        src_container, sep, src_object = src.partition('/')
438
        dst_container, sep, dst_object = dst.partition('/')
439
        
440
        #prepare user defined meta
441
        meta = {}
442
        for arg in args:
443
            key, sep, val = arg.partition('=')
444
            meta[key] = val
445
        
446
        if not sep:
447
            dst_container = src_container
448
            dst_object = dst
449
        
450
        args = {'content_type':self.content_type} if self.content_type else {}
451
        self.client.copy_object(src_container, src_object, dst_container,
452
                                dst_object, meta, self.public, self.version,
453
                                **args)
454

  
455
@cli_command('set')
456
class SetMeta(Command):
457
    syntax = '[<container>[/<object>]] key=val [key=val] [...]'
458
    description = 'set account/container/object metadata'
459
    
460
    def execute(self, path, *args):
461
        #in case of account fix the args
462
        if path.find('=') != -1:
463
            args = list(args)
464
            args.append(path)
465
            args = tuple(args)
466
            path = ''
467
        meta = {}
468
        for arg in args:
469
            key, sep, val = arg.partition('=')
470
            meta[key.strip()] = val.strip()
471
        container, sep, object = path.partition('/')
472
        if object:
473
            self.client.update_object_metadata(container, object, **meta)
474
        elif container:
475
            self.client.update_container_metadata(container, **meta)
476
        else:
477
            self.client.update_account_metadata(**meta)
478

  
479
@cli_command('update')
480
class UpdateObject(Command):
481
    syntax = '<container>/<object> path [key=val] [...]'
482
    description = 'update object metadata/data (default mode: append)'
483
    
484
    def add_options(self, parser):
485
        parser.add_option('-a', action='store_true', dest='append',
486
                          default=True, help='append data')
487
        parser.add_option('--offset', action='store',
488
                          dest='offset',
489
                          default=None, help='starting offest to be updated')
490
        parser.add_option('--range', action='store', dest='content-range',
491
                          default=None, help='range of data to be updated')
492
        parser.add_option('--chunked', action='store_true', dest='chunked',
493
                          default=False, help='set chunked transfer mode')
494
        parser.add_option('--content-encoding', action='store',
495
                          dest='content_encoding', default=None,
496
                          help='provide the object MIME content type')
497
        parser.add_option('--content-disposition', action='store', type='str',
498
                          dest='content_disposition', default=None,
499
                          help='provide the presentation style of the object')
500
        parser.add_option('--manifest', action='store', type='str',
501
                          dest='x_object_manifest', default=None,
502
                          help='use for large file support')        
503
        parser.add_option('--sharing', action='store',
504
                          dest='x_object_sharing', default=None,
505
                          help='define sharing object policy')
506
        parser.add_option('--nosharing', action='store_true',
507
                          dest='no_sharing', default=None,
508
                          help='clear object sharing policy')
509
        parser.add_option('-f', action='store',
510
                          dest='srcpath', default=None,
511
                          help='file descriptor to read from: pass - for standard input')
512
        parser.add_option('--public', action='store_true',
513
                          dest='x_object_public', default=False,
514
                          help='make object publicly accessible')
515
        parser.add_option('--replace', action='store_true',
516
                          dest='replace', default=False,
517
                          help='override metadata')
518
    
519
    def execute(self, path, *args):
520
        if path.find('=') != -1:
521
            raise Fault('Missing path argument')
522
        
523
        #prepare user defined meta
524
        meta = {}
525
        for arg in args:
526
            key, sep, val = arg.partition('=')
527
            meta[key] = val
528
        
529
        
530
        attrs = ['content_encoding', 'content_disposition', 'x_object_sharing',
531
                 'x_object_public', 'replace']
532
        args = self._build_args(attrs)
533
        
534
        if self.no_sharing:
535
            args['x_object_sharing'] = ''
536
        
537
        container, sep, object = path.partition('/')
538
        
539
        f = None
540
        if self.srcpath:
541
            f = open(self.srcpath) if self.srcpath != '-' else stdin
542
        
543
        if self.chunked:
544
            self.client.update_object_using_chunks(container, object, f,
545
                                                    meta=meta, **args)
546
        else:
547
            self.client.update_object(container, object, f, meta=meta, **args)
548
        if f:
549
            f.close()
550

  
551
@cli_command('move', 'mv')
552
class MoveObject(Command):
553
    syntax = '<src container>/<src object> [<dst container>/]<dst object>'
554
    description = 'move an object to a different location'
555
    
556
    def add_options(self, parser):
557
        parser.add_option('--public', action='store_true',
558
                          dest='public', default=False,
559
                          help='make object publicly accessible')
560
        parser.add_option('--content-type', action='store',
561
                          dest='content_type', default=None,
562
                          help='change object\'s content type')
563
    
564
    def execute(self, src, dst, *args):
565
        src_container, sep, src_object = src.partition('/')
566
        dst_container, sep, dst_object = dst.partition('/')
567
        if not sep:
568
            dst_container = src_container
569
            dst_object = dst
570
        
571
        #prepare user defined meta
572
        meta = {}
573
        for arg in args:
574
            key, sep, val = arg.partition('=')
575
            meta[key] = val
576
        
577
        args = {'content_type':self.content_type} if self.content_type else {}
578
        self.client.move_object(src_container, src_object, dst_container,
579
                                dst_object, meta, self.public, **args)
580

  
581
@cli_command('unset')
582
class UnsetObject(Command):
583
    syntax = '<container>/[<object>] key [key] [...]'
584
    description = 'delete metadata info'
585
    
586
    def execute(self, path, *args):
587
        #in case of account fix the args
588
        if len(args) == 0:
589
            args = list(args)
590
            args.append(path)
591
            args = tuple(args)
592
            path = ''
593
        meta = []
594
        for key in args:
595
            meta.append(key)
596
        container, sep, object = path.partition('/')
597
        if object:
598
            self.client.delete_object_metadata(container, object, meta)
599
        elif container:
600
            self.client.delete_container_metadata(container, meta)
601
        else:
602
            self.client.delete_account_metadata(meta)
603

  
604
@cli_command('group')
605
class CreateGroup(Command):
606
    syntax = 'key=val [key=val] [...]'
607
    description = 'create account groups'
608
    
609
    def execute(self, *args):
610
        groups = {}
611
        for arg in args:
612
            key, sep, val = arg.partition('=')
613
            groups[key] = val
614
        self.client.set_account_groups(**groups)
615

  
616
@cli_command('ungroup')
617
class DeleteGroup(Command):
618
    syntax = 'key [key] [...]'
619
    description = 'delete account groups'
620
    
621
    def execute(self, *args):
622
        groups = []
623
        for arg in args:
624
            groups.append(arg)
625
        self.client.unset_account_groups(groups)
626

  
627
@cli_command('policy')
628
class SetPolicy(Command):
629
    syntax = 'container key=val [key=val] [...]'
630
    description = 'set container policies'
631
    
632
    def execute(self, path, *args):
633
        if path.find('=') != -1:
634
            raise Fault('Missing container argument')
635
        
636
        container, sep, object = path.partition('/')
637
        
638
        if object:
639
            raise Fault('Only containers have policies')
640
        
641
        policies = {}
642
        for arg in args:
643
            key, sep, val = arg.partition('=')
644
            policies[key] = val
645
        
646
        self.client.set_container_policies(container, **policies)
647

  
648
@cli_command('publish')
649
class PublishObject(Command):
650
    syntax = '<container>/<object>'
651
    description = 'publish an object'
652
    
653
    def execute(self, src):
654
        src_container, sep, src_object = src.partition('/')
655
        
656
        self.client.publish_object(src_container, src_object)
657

  
658
@cli_command('unpublish')
659
class UnpublishObject(Command):
660
    syntax = '<container>/<object>'
661
    description = 'unpublish an object'
662
    
663
    def execute(self, src):
664
        src_container, sep, src_object = src.partition('/')
665
        
666
        self.client.unpublish_object(src_container, src_object)
667

  
668
@cli_command('sharing')
669
class SharingObject(Command):
670
    syntax = 'list users sharing objects with the user'
671
    description = 'list user accounts sharing objects with the user'
672
    
673
    def add_options(self, parser):
674
        parser.add_option('-l', action='store_true', dest='detail',
675
                          default=False, help='show detailed output')
676
        parser.add_option('-n', action='store', type='int', dest='limit',
677
                          default=10000, help='show limited output')
678
        parser.add_option('--marker', action='store', type='str',
679
                          dest='marker', default=None,
680
                          help='show output greater then marker')
681
        
682
    
683
    def execute(self):
684
        attrs = ['limit', 'marker']
685
        args = self._build_args(attrs)
686
        args['format'] = 'json' if self.detail else 'text'
687
        
688
        print_list(self.client.list_shared_by_others(**args))
689

  
690
@cli_command('send')
691
class Send(Command):
692
    syntax = '<file> <container>[/<prefix>]'
693
    description = 'upload file to container (using prefix)'
694
    
695
    def execute(self, file, path):
696
        container, sep, prefix = path.partition('/')
697
        upload(self.client, file, container, prefix)
698

  
699
@cli_command('receive')
700
class Receive(Command):
701
    syntax = '<container>/<object> <file>'
702
    description = 'download object to file'
703
    
704
    def execute(self, path, file):
705
        container, sep, object = path.partition('/')
706
        download(self.client, container, object, file)
707

  
708
def print_usage():
709
    cmd = Command('', [])
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff