git-svn-id: https://s3tools.svn.sourceforge.net/svnroot/s3tools/s3py/trunk@64 830e0280-6d2a-0410-9c65-932aecc39d9d
Michal Ludvig authored on 2007/01/26 11:34:081 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,162 @@ |
0 |
+## Amazon S3 manager |
|
1 |
+## Author: Michal Ludvig <michal@logix.cz> |
|
2 |
+## http://www.logix.cz/michal |
|
3 |
+## License: GPL Version 2 |
|
4 |
+ |
|
5 |
+import os, os.path |
|
6 |
+import errno |
|
7 |
+import random |
|
8 |
+import pickle |
|
9 |
+ |
|
10 |
+class S3fs(object): |
|
11 |
+ _sync_attrs = [ "tree" ] |
|
12 |
+ fsname = None |
|
13 |
+ tree = None |
|
14 |
+ def __init__(self, fsname = None): |
|
15 |
+ self.n = S3fsObjectName() |
|
16 |
+ if fsname: |
|
17 |
+ self.openfs(fsname) |
|
18 |
+ |
|
19 |
+ def mkfs(self, fsname): |
|
20 |
+ self._object_name = self.n.fs(fsname) |
|
21 |
+ if self.object_exists(self._object_name): |
|
22 |
+ raise S3fsError("Filesystem already exists", errno.EEXIST) |
|
23 |
+ self.fsname = fsname |
|
24 |
+ root_inode = S3fsInode(self) |
|
25 |
+ S3fsSync.store(self, root_inode) |
|
26 |
+ self.tree = { "/" : root_inode.inode_id } |
|
27 |
+ S3fsSync.store(self, self) |
|
28 |
+ |
|
29 |
+ def openfs(self, fsname): |
|
30 |
+ raise S3fsError("Not implemented", errno.ENOSYS) |
|
31 |
+ |
|
32 |
+class S3fsInode(object): |
|
33 |
+ _fs = None |
|
34 |
+ |
|
35 |
+ ## Interface for S3fsSync |
|
36 |
+ _sync_attrs = [ "properties" ] |
|
37 |
+ _object_name = None |
|
38 |
+ |
|
39 |
+ ## Properties |
|
40 |
+ inode_id = None |
|
41 |
+ properties = { |
|
42 |
+ "ctime" : None, |
|
43 |
+ "mtime" : None, |
|
44 |
+ "uid" : None, |
|
45 |
+ "gid" : None, |
|
46 |
+ "mode" : None, |
|
47 |
+ } |
|
48 |
+ |
|
49 |
+ def __init__(self, fs, inode_id = None): |
|
50 |
+ if not inode_id: |
|
51 |
+ inode_id = fs.n.rndstr(10) |
|
52 |
+ self.inode_id = inode_id |
|
53 |
+ self._object_name = fs.n.inode(fs.fsname, inode_id) |
|
54 |
+ self._fs = fs |
|
55 |
+ |
|
56 |
+ def setprop(self, property, value): |
|
57 |
+ self.assert_property_name(property) |
|
58 |
+ self.properties[property] = value |
|
59 |
+ return value |
|
60 |
+ |
|
61 |
+ def getprop(self, property): |
|
62 |
+ self.assert_property_name(property) |
|
63 |
+ return self.properties[property] |
|
64 |
+ |
|
65 |
+ def assert_property_name(self, property): |
|
66 |
+ if not self.properties.has_key(property): |
|
67 |
+ raise ValueError("Property '%s' not known to S3fsInode") |
|
68 |
+ |
|
69 |
+class S3fsLocalDir(S3fs): |
|
70 |
+ def __init__(self, directory): |
|
71 |
+ S3fs.__init__(self) |
|
72 |
+ if not os.path.isdir(directory): |
|
73 |
+ raise S3fsError("Directory %s does not exist" % directory, errno.ENOENT) |
|
74 |
+ self._dir = directory |
|
75 |
+ |
|
76 |
+ def lock(self): |
|
77 |
+ pass |
|
78 |
+ |
|
79 |
+ def unlock(self): |
|
80 |
+ pass |
|
81 |
+ |
|
82 |
+ def object_exists(self, object_name): |
|
83 |
+ real_path = os.path.join(self._dir, object_name) |
|
84 |
+ if os.path.isfile(real_path): ## Is file, all good |
|
85 |
+ return True |
|
86 |
+ if os.path.exists(real_path): ## Exists but is not file! |
|
87 |
+ raise S3fsError("Object %s (%s) is not a regular file" % (object_name, real_path), errno.EINVAL) |
|
88 |
+ return False |
|
89 |
+ |
|
90 |
+ def object_write(self, object_name, contents): |
|
91 |
+ real_path = os.path.join(self._dir, object_name) |
|
92 |
+ f = open(real_path, "wb") |
|
93 |
+ f.write(contents) |
|
94 |
+ f.close() |
|
95 |
+ |
|
96 |
+ def object_read(self, object_name): |
|
97 |
+ real_path = os.path.join(self._dir, object_name) |
|
98 |
+ f = open(real_path, "rb") |
|
99 |
+ contents = f.read() |
|
100 |
+ f.close() |
|
101 |
+ return contents |
|
102 |
+ |
|
103 |
+class S3fsObjectName(object): |
|
104 |
+ _rnd_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" |
|
105 |
+ _rnd_chars_len = len(_rnd_chars) |
|
106 |
+ |
|
107 |
+ def __init__(self): |
|
108 |
+ random.seed() |
|
109 |
+ |
|
110 |
+ def rndstr(self, len): |
|
111 |
+ retval = "" |
|
112 |
+ while len > 0: |
|
113 |
+ retval += self._rnd_chars[random.randint(0, self._rnd_chars_len-1)] |
|
114 |
+ len -= 1 |
|
115 |
+ return retval |
|
116 |
+ |
|
117 |
+ def fs(self, fsname): |
|
118 |
+ return "fs-%s" % fsname |
|
119 |
+ |
|
120 |
+ def inode(self, fsname, inode_id): |
|
121 |
+ return "%s-i-%s" % (fsname, inode_id) |
|
122 |
+ |
|
123 |
+class S3fsSync(object): |
|
124 |
+ @staticmethod |
|
125 |
+ def store(fs, instance, object_name = None): |
|
126 |
+ if not object_name: |
|
127 |
+ object_name = instance._object_name |
|
128 |
+ to_sync = {} |
|
129 |
+ for attr in instance._sync_attrs: |
|
130 |
+ if hasattr(instance, attr): |
|
131 |
+ to_sync[attr] = getattr(instance, attr) |
|
132 |
+ fs.object_write(object_name, pickle.dumps(to_sync)) |
|
133 |
+ print "Stored object: %s" % (object_name) |
|
134 |
+ |
|
135 |
+ @staticmethod |
|
136 |
+ def load(fs, instance, object_name = None): |
|
137 |
+ if not object_name: |
|
138 |
+ object_name = instance._object_name |
|
139 |
+ from_sync = pickle.loads(fs.object_read(object_name)) |
|
140 |
+ for attr in instance._sync_attrs: |
|
141 |
+ if from_sync.has_key[attr]: |
|
142 |
+ setattr(instance, attr, from_sync[attr]) |
|
143 |
+ print "Loaded object: %s" % (object_name) |
|
144 |
+ |
|
145 |
+class S3fsError(Exception): |
|
146 |
+ def __init__(self, message, errno = -1): |
|
147 |
+ Exception.__init__(self, message) |
|
148 |
+ self.errno = errno |
|
149 |
+ |
|
150 |
+if __name__ == "__main__": |
|
151 |
+ local_dir = "/tmp/s3fs" |
|
152 |
+ try: |
|
153 |
+ fs = S3fsLocalDir(local_dir) |
|
154 |
+ except S3fsError, e: |
|
155 |
+ if e.errno == errno.ENOENT: |
|
156 |
+ os.mkdir(local_dir) |
|
157 |
+ else: |
|
158 |
+ raise |
|
159 |
+ print "RandomStrings = %s %s" % (fs.n.rndstr(5), fs.n.rndstr(10)) |
|
160 |
+ |
|
161 |
+ fs.mkfs("testFs") |