Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / fs.py @ db117cac

History | View | Annotate | Download (12.2 kB)

1
#!/usr/bin/env python
2

    
3
# Copyright 2011-2012 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 synnefo.lib.parsedate import parse_http_date
45

    
46
from pithos.tools.lib.client import OOS_Client, Fault
47
from pithos.tools.lib.fuse import FUSE, FuseOSError, Operations
48
from pithos.tools.lib.util import get_user, get_auth, get_url
49

    
50

    
51
epoch = int(time())
52

    
53

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

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

    
326

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

    
336

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