9 logger = logging.getLogger(__name__)
\r
11 class BackEnd(basebackend.BaseBackEnd):
\r
13 def __init__(self, basepath):
\r
14 self.basepath = basepath
\r
16 if not os.path.exists(basepath):
\r
17 os.makedirs(basepath)
\r
18 db = os.path.join(basepath, 'db')
\r
19 self.con = sqlite3.connect(db)
\r
21 sql = '''create table if not exists objects(name text)'''
\r
22 self.con.execute(sql)
\r
23 sql = '''create table if not exists metadata(object_id int, name text, value text)'''
\r
24 self.con.execute(sql)
\r
27 # TODO: Create/delete account?
\r
28 # TODO: Catch OSError exceptions.
\r
30 def get_account_meta(self, account):
\r
32 returns a dictionary with the account metadata
\r
34 logger.debug("get_account_meta: %s", account)
\r
35 fullname = os.path.join(self.basepath, account)
\r
36 if not os.path.exists(fullname):
\r
37 raise NameError('Account does not exist')
\r
38 contents = os.listdir(fullname)
\r
39 count = len(contents)
\r
40 size = os.stat(fullname).st_size
\r
41 meta = self.__get_metadata(account)
\r
42 meta.update({'name': account, 'count': count, 'bytes': size})
\r
45 def update_account_meta(self, account, meta):
\r
47 updates the metadata associated with the account
\r
49 logger.debug("update_account_meta: %s %s", account, meta)
\r
50 fullname = os.path.join(self.basepath, account)
\r
51 if not os.path.exists(fullname):
\r
52 os.makedirs(fullname)
\r
53 self.__put_metadata(account, meta)
\r
56 def create_container(self, account, name):
\r
58 creates a new container with the given name
\r
59 if it doesn't exist under the basepath
\r
61 logger.debug("create_container: %s %s", account, name)
\r
62 fullname = os.path.join(self.basepath, account, name)
\r
63 if not os.path.exists(fullname):
\r
64 os.makedirs(fullname)
\r
66 raise NameError('Container already exists')
\r
69 def delete_container(self, account, name):
\r
71 deletes the container with the given name
\r
72 if it exists under the basepath and is empty
\r
74 logger.debug("delete_container: %s %s", account, name)
\r
75 fullname = os.path.join(self.basepath, account, name)
\r
76 if not os.path.exists(fullname):
\r
77 raise NameError('Container does not exist')
\r
78 if os.listdir(fullname):
\r
79 raise IndexError('Container is not empty')
\r
82 self.__del_dbpath(os.path.join(account, name))
\r
85 def get_container_meta(self, account, name):
\r
87 returns a dictionary with the container metadata
\r
89 logger.debug("get_container_meta: %s %s", account, name)
\r
90 fullname = os.path.join(self.basepath, account, name)
\r
91 if not os.path.exists(fullname):
\r
92 raise NameError('Container does not exist')
\r
93 contents = os.listdir(fullname)
\r
94 count = len(contents)
\r
95 size = os.stat(fullname).st_size
\r
96 meta = self.__get_metadata(os.path.join(account, name))
\r
97 meta.update({'name': name, 'count': count, 'bytes': size})
\r
100 def update_container_meta(self, account, name, meta):
\r
102 updates the metadata associated with the container
\r
104 logger.debug("update_container_meta: %s %s %s", account, name, meta)
\r
105 fullname = os.path.join(self.basepath, account, name)
\r
106 if not os.path.exists(fullname):
\r
107 raise NameError('Container does not exist')
\r
108 self.__put_metadata(os.path.join(account, name), meta)
\r
111 def list_containers(self, account, marker = None, limit = 10000):
\r
113 returns a list of at most limit (default = 10000) containers
\r
114 starting from the next item after the optional marker
\r
116 logger.debug("list_containers: %s %s %s", account, marker, limit)
\r
117 fullname = os.path.join(self.basepath, account)
\r
118 if not os.path.exists(fullname):
\r
119 raise NameError('Account does not exist')
\r
120 containers = os.listdir(fullname)
\r
124 start = containers.index(marker) + 1
\r
127 if not limit or limit > 10000:
\r
130 return containers[start:start + limit]
\r
132 def list_objects(self, account, container, prefix = '', delimiter = None, marker = None, limit = 10000):
\r
134 returns a list of objects existing under a container
\r
136 logger.info("list_objects: %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit)
\r
137 fullname = os.path.join(self.basepath, account, container)
\r
138 if not os.path.exists(fullname):
\r
139 raise NameError('Container does not exist')
\r
141 prefix = os.path.join(account, container, prefix.lstrip('/'))
\r
142 c = self.con.execute('select * from objects where name like ''?'' order by name', (os.path.join(prefix, '%'),))
\r
143 objects = [x[0][len(prefix):] for x in c.fetchall()]
\r
145 pseudo_objects = []
\r
148 i = pseudo_name.find(delimiter)
\r
150 pseudo_name = pseudo_name[:i]
\r
151 if pseudo_name not in pseudo_objects:
\r
152 pseudo_objects.append(pseudo_name)
\r
153 # TODO: Virtual directories.
\r
154 objects = pseudo_objects
\r
159 start = objects.index(marker) + 1
\r
162 if not limit or limit > 10000:
\r
165 return objects[start:start + limit]
\r
167 def get_object_meta(self, account, container, name, keys = None):
\r
169 returns a dictionary with the object metadata
\r
171 logger.info("get_object_meta: %s %s %s %s", account, container, name, keys)
\r
172 fullname = os.path.join(self.basepath, account, container)
\r
173 if not os.path.exists(fullname):
\r
174 raise NameError('Container does not exist')
\r
176 link = self.__get_linkinfo(os.path.join(account, container, name))
\r
177 location = os.path.join(self.basepath, account, container, link)
\r
178 size = os.path.getsize(location)
\r
179 mtime = os.path.getmtime(location)
\r
180 meta = self.__get_metadata(os.path.join(account, container, name))
\r
181 meta.update({'name': name, 'bytes': size, 'last_modified': mtime})
\r
182 if 'hash' not in meta:
\r
183 meta['hash'] = self.__object_hash(location)
\r
184 if 'content_type' not in meta:
\r
185 meta['content_type'] = 'application/octet-stream'
\r
188 def update_object_meta(self, account, container, name, meta):
\r
190 updates the metadata associated with the object
\r
192 logger.info("update_object_meta: %s %s %s %s", account, container, name, meta)
\r
193 fullname = os.path.join(self.basepath, account, container)
\r
194 if not os.path.exists(fullname):
\r
195 raise NameError('Container does not exist')
\r
197 link = self.__get_linkinfo(os.path.join(account, container, name))
\r
199 raise NameError('Object does not exist')
\r
200 self.__put_metadata(os.path.join(account, container, name), meta)
\r
203 def get_object(self, account, container, name, offset = 0, length = -1):
\r
205 returns the object data
\r
207 logger.info("get_object: %s %s %s %s %s", account, container, name, offset, length)
\r
208 fullname = os.path.join(self.basepath, account, container)
\r
209 if not os.path.exists(fullname):
\r
210 raise NameError('Container does not exist')
\r
212 link = self.__get_linkinfo(os.path.join(account, container, name))
\r
213 location = os.path.join(self.basepath, account, container, link)
\r
214 f = open(location, 'r')
\r
217 data = f.read(length)
\r
221 def update_object(self, account, container, name, data, offset = 0):
\r
223 creates/updates an object with the specified data
\r
225 logger.info("put_object: %s %s %s %s %s", account, container, name, data, offset)
\r
226 fullname = os.path.join(self.basepath, account, container)
\r
227 if not os.path.exists(fullname):
\r
228 raise NameError('Container does not exist')
\r
231 link = self.__get_linkinfo(os.path.join(account, container, name))
\r
234 link = self.__put_linkinfo(os.path.join(account, container, name))
\r
235 location = os.path.join(self.basepath, account, container, link)
\r
236 f = open(location, 'w')
\r
241 self.__put_metadata(os.path.join(account, container, name), {'hash': self.__object_hash(location)})
\r
244 def copy_object(self, account, src_container, src_name, dest_container, dest_name):
\r
248 logger.info("copy_object: %s %s %s %s %s", account, src_container, src_name, dest_container, dest_name)
\r
249 link = self.__get_linkinfo(os.path.join(account, src_container, src_name))
\r
250 src_location = os.path.join(self.basepath, account, src_container, link)
\r
252 dest_fullname = os.path.join(self.basepath, account, dest_container)
\r
253 if not os.path.exists(dest_fullname):
\r
254 raise NameError('Destination container does not exist')
\r
256 link = self.__get_linkinfo(os.path.join(account, dest_container, dest_name))
\r
259 link = self.__put_linkinfo(os.path.join(account, dest_container, dest_name))
\r
260 dest_location = os.path.join(self.basepath, account, dest_container, link)
\r
262 shutil.copyfile(src_location, dest_location)
\r
263 # TODO: accept metadata changes
\r
264 self.__put_metadata(os.path.join(account, dest_container, dest_name), self.__get_metadata(os.path.join(account, src_container, src_name)))
\r
267 def delete_object(self, account, container, name):
\r
271 logger.info("delete_object: %s %s %s", account, container, name)
\r
272 fullname = os.path.join(self.basepath, account, container)
\r
273 if not os.path.exists(fullname):
\r
274 raise NameError('Container does not exist')
\r
276 # delete object data
\r
277 link = self.__get_linkinfo(os.path.join(account, container, name))
\r
278 location = os.path.join(self.basepath, account, container, link)
\r
280 os.remove(location)
\r
283 # delete object metadata
\r
284 self.__del_dbpath(os.path.join(account, container, name))
\r
287 def __get_linkinfo(self, path):
\r
288 c = self.con.execute('select rowid from objects where name=''?''', (path,))
\r
293 raise NameError('Requested path not found')
\r
295 def __put_linkinfo(self, path):
\r
296 id = self.con.execute('insert into objects (name) values (?)', (path,)).lastrowid
\r
300 def __get_metadata(self, path):
\r
301 c = self.con.execute('select m.name, m.value from metadata m, objects o where o.rowid = m.object_id and o.name = ''?''', (path,))
\r
302 return dict(c.fetchall())
\r
304 def __put_metadata(self, path, meta):
\r
305 c = self.con.execute('select rowid from objects where name=''?''', (path,))
\r
310 link = self.con.execute('insert into objects (name) values (?)', (path,)).lastrowid
\r
311 for k, v in meta.iteritems():
\r
312 self.con.execute('insert or replace into metadata (object_id, name, value) values (?, ?, ?)', (link, k, v))
\r
316 def __del_dbpath(self, path):
\r
317 self.con.execute('delete from metadata where object_id in (select rowid from objects where name = ''?'')', (path,))
\r
318 self.con.execute('delete from objects where name = ''?''', (path,))
\r
322 def __object_hash(self, location, block_size = 8192):
\r
323 md5 = hashlib.md5()
\r
324 f = open(location, 'r')
\r
326 data = f.read(block_size)
\r
331 return md5.hexdigest()
\r