123 |
123 |
"""Return a dictionary with the account metadata."""
|
124 |
124 |
|
125 |
125 |
logger.debug("get_account_meta: %s %s", account, until)
|
126 |
|
node = self._lookup_account(account, user == account)
|
|
126 |
path, node = self._lookup_account(account, user == account)
|
127 |
127 |
if user != account:
|
128 |
128 |
if until or node is None or account not in self._allowed_accounts(user):
|
129 |
129 |
raise NotAllowedError
|
... | ... | |
135 |
135 |
# Account does not exist before until.
|
136 |
136 |
version_id = None
|
137 |
137 |
mtime = until
|
138 |
|
object_count, bytes, tstamp = self._get_statistics(node, account, until)
|
139 |
|
if mtime > tstamp:
|
140 |
|
tstamp = mtime
|
|
138 |
object_count, bytes, tstamp = self._get_statistics(node, path, until)
|
|
139 |
tstamp = max(mtime, tstamp)
|
141 |
140 |
if until is None:
|
142 |
141 |
modified = tstamp
|
143 |
142 |
else:
|
144 |
|
modified = self._get_statistics(node, account)[2] # Overall last modification.
|
145 |
|
if mtime > modified:
|
146 |
|
modified = mtime
|
|
143 |
modified = self._get_statistics(node, path)[2] # Overall last modification.
|
|
144 |
modified = max(mtime, modified)
|
147 |
145 |
|
148 |
146 |
# Proper count.
|
149 |
|
count = self.node.node_children(node)
|
|
147 |
# TODO: Fix this to count until.
|
|
148 |
count = self.node.node_count_children(node)
|
150 |
149 |
object_count -= count
|
151 |
150 |
|
152 |
151 |
if user != account:
|
... | ... | |
168 |
167 |
logger.debug("update_account_meta: %s %s %s", account, meta, replace)
|
169 |
168 |
if user != account:
|
170 |
169 |
raise NotAllowedError
|
171 |
|
node = self._lookup_account(account, True)
|
|
170 |
path, node = self._lookup_account(account, True)
|
172 |
171 |
self._put_metadata(user, node, meta, replace, False)
|
173 |
172 |
|
174 |
173 |
@backend_method
|
... | ... | |
210 |
209 |
node = self.node.node_lookup(account)
|
211 |
210 |
if node is not None:
|
212 |
211 |
raise NameError('Account already exists')
|
213 |
|
node = self.node.node_create(ROOTNODE, account)
|
214 |
|
self.node.version_create(node, 0, None, account, CLUSTER_NORMAL)
|
215 |
|
|
216 |
|
|
217 |
|
|
218 |
|
|
219 |
|
|
220 |
|
|
221 |
|
|
222 |
|
|
223 |
|
|
224 |
|
|
225 |
|
|
226 |
|
|
|
212 |
self._put_path(ROOTNODE, account)
|
227 |
213 |
|
228 |
214 |
@backend_method
|
229 |
215 |
def delete_account(self, user, account):
|
... | ... | |
232 |
218 |
logger.debug("delete_account: %s", account)
|
233 |
219 |
if user != account:
|
234 |
220 |
raise NotAllowedError
|
235 |
|
count = self._get_pathstats(account)[0]
|
236 |
|
if count > 0:
|
|
221 |
node = self.node.node_lookup(account)
|
|
222 |
if node is None:
|
|
223 |
return
|
|
224 |
if not self.node.node_remove(node):
|
237 |
225 |
raise IndexError('Account is not empty')
|
238 |
|
sql = 'delete from versions where name = ?'
|
239 |
|
self.con.execute(sql, (account,))
|
240 |
226 |
self.permissions.group_destroy(account)
|
241 |
227 |
|
242 |
|
@backend_method
|
243 |
|
def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None):
|
244 |
|
"""Return a list of containers existing under an account."""
|
245 |
|
|
246 |
|
logger.debug("list_containers: %s %s %s %s", account, marker, limit, until)
|
247 |
|
if user != account:
|
248 |
|
if until or account not in self._allowed_accounts(user):
|
249 |
|
raise NotAllowedError
|
250 |
|
allowed = self._allowed_containers(user, account)
|
251 |
|
start, limit = self._list_limits(allowed, marker, limit)
|
252 |
|
return allowed[start:start + limit]
|
253 |
|
else:
|
254 |
|
if shared:
|
255 |
|
allowed = [x.split('/', 2)[1] for x in self.permissions.access_list_shared(account)]
|
256 |
|
start, limit = self._list_limits(allowed, marker, limit)
|
257 |
|
return allowed[start:start + limit]
|
258 |
|
return [x[0] for x in self._list_objects(account, '', '/', marker, limit, False, [], until)]
|
|
228 |
# @backend_method
|
|
229 |
# def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None):
|
|
230 |
# """Return a list of containers existing under an account."""
|
|
231 |
#
|
|
232 |
# logger.debug("list_containers: %s %s %s %s", account, marker, limit, until)
|
|
233 |
# if user != account:
|
|
234 |
# if until or account not in self._allowed_accounts(user):
|
|
235 |
# raise NotAllowedError
|
|
236 |
# allowed = self._allowed_containers(user, account)
|
|
237 |
# start, limit = self._list_limits(allowed, marker, limit)
|
|
238 |
# return allowed[start:start + limit]
|
|
239 |
# else:
|
|
240 |
# if shared:
|
|
241 |
# allowed = [x.split('/', 2)[1] for x in self.permissions.access_list_shared(account)]
|
|
242 |
# start, limit = self._list_limits(allowed, marker, limit)
|
|
243 |
# return allowed[start:start + limit]
|
|
244 |
# return [x[0] for x in self._list_objects(account, '', '/', marker, limit, False, [], until)]
|
259 |
245 |
|
260 |
246 |
@backend_method
|
261 |
247 |
def get_container_meta(self, user, account, container, until=None):
|
... | ... | |
265 |
251 |
if user != account:
|
266 |
252 |
if until or container not in self._allowed_containers(user, account):
|
267 |
253 |
raise NotAllowedError
|
268 |
|
path, version_id, mtime = self._get_containerinfo(account, container, until)
|
269 |
|
count, bytes, tstamp = self._get_pathstats(path, until)
|
270 |
|
if mtime > tstamp:
|
271 |
|
tstamp = mtime
|
|
254 |
path, node = self._lookup_container(account, container)
|
|
255 |
props = self._get_properties(node, until)
|
|
256 |
count, bytes, tstamp = self._get_statistics(node, path, until)
|
|
257 |
tstamp = max(mtime, tstamp)
|
272 |
258 |
if until is None:
|
273 |
259 |
modified = tstamp
|
274 |
260 |
else:
|
275 |
|
modified = self._get_pathstats(path)[2] # Overall last modification
|
276 |
|
if mtime > modified:
|
277 |
|
modified = mtime
|
|
261 |
modified = self._get_statistics(node, path)[2] # Overall last modification.
|
|
262 |
modified = max(mtime, modified)
|
278 |
263 |
|
279 |
264 |
if user != account:
|
280 |
|
meta = {'name': container, 'modified': modified}
|
|
265 |
meta = {'name': container}
|
281 |
266 |
else:
|
282 |
|
meta = self._get_metadata(path, version_id)
|
283 |
|
meta.update({'name': container, 'count': count, 'bytes': bytes, 'modified': modified})
|
|
267 |
meta = dict(self.node.attribute_get(props[SERIAL]))
|
284 |
268 |
if until is not None:
|
285 |
269 |
meta.update({'until_timestamp': tstamp})
|
|
270 |
meta.update({'name': container, 'count': count, 'bytes': bytes})
|
|
271 |
meta.update({'modified': modified})
|
286 |
272 |
return meta
|
287 |
273 |
|
288 |
274 |
@backend_method
|
... | ... | |
292 |
278 |
logger.debug("update_container_meta: %s %s %s %s", account, container, meta, replace)
|
293 |
279 |
if user != account:
|
294 |
280 |
raise NotAllowedError
|
295 |
|
path, version_id, mtime = self._get_containerinfo(account, container)
|
296 |
|
self._put_metadata(user, path, meta, replace, False)
|
|
281 |
path, node = self._lookup_container(account, container)
|
|
282 |
self._put_metadata(user, node, meta, replace, False)
|
297 |
283 |
|
298 |
284 |
@backend_method
|
299 |
285 |
def get_container_policy(self, user, account, container):
|
... | ... | |
304 |
290 |
if container not in self._allowed_containers(user, account):
|
305 |
291 |
raise NotAllowedError
|
306 |
292 |
return {}
|
307 |
|
path = self._get_containerinfo(account, container)[0]
|
|
293 |
path = self._lookup_container(account, container)[0]
|
308 |
294 |
return self.policy.policy_get(path)
|
309 |
295 |
|
310 |
296 |
@backend_method
|
... | ... | |
314 |
300 |
logger.debug("update_container_policy: %s %s %s %s", account, container, policy, replace)
|
315 |
301 |
if user != account:
|
316 |
302 |
raise NotAllowedError
|
317 |
|
path = self._get_containerinfo(account, container)[0]
|
|
303 |
path = self._lookup_container(account, container)[0]
|
318 |
304 |
self._check_policy(policy)
|
319 |
305 |
if replace:
|
320 |
306 |
for k, v in self.default_policy.iteritems():
|
... | ... | |
330 |
316 |
if user != account:
|
331 |
317 |
raise NotAllowedError
|
332 |
318 |
try:
|
333 |
|
path, version_id, mtime = self._get_containerinfo(account, container)
|
|
319 |
path, node = self._lookup_container(account, container)
|
334 |
320 |
except NameError:
|
335 |
321 |
pass
|
336 |
322 |
else:
|
... | ... | |
338 |
324 |
if policy:
|
339 |
325 |
self._check_policy(policy)
|
340 |
326 |
path = '/'.join((account, container))
|
341 |
|
version_id = self._put_version(path, user)[0]
|
|
327 |
self._put_path(self._lookup_account(account, True)[1], path)
|
342 |
328 |
for k, v in self.default_policy.iteritems():
|
343 |
329 |
if k not in policy:
|
344 |
330 |
policy[k] = v
|
... | ... | |
351 |
337 |
logger.debug("delete_container: %s %s %s", account, container, until)
|
352 |
338 |
if user != account:
|
353 |
339 |
raise NotAllowedError
|
354 |
|
path, version_id, mtime = self._get_containerinfo(account, container)
|
|
340 |
path, node = self._lookup_container(account, container)
|
355 |
341 |
|
356 |
342 |
if until is not None:
|
357 |
|
sql = '''select version_id from versions where name like ? and tstamp <= ?
|
358 |
|
and version_id not in (select version_id from (%s))'''
|
359 |
|
sql = sql % self._sql_until() # Do not delete current versions.
|
360 |
|
c = self.con.execute(sql, (path + '/%', until))
|
361 |
|
for v in [x[0] for x in c.fetchall()]:
|
362 |
|
self._del_version(v)
|
|
343 |
versions = self.node.node_purge_children(node, until, CLUSTER_HISTORY)
|
|
344 |
for v in versions:
|
|
345 |
self.mapper.map_remv(v)
|
|
346 |
self.node.node_purge_children(node, until, CLUSTER_DELETED)
|
363 |
347 |
return
|
364 |
348 |
|
365 |
|
count = self._get_pathstats(path)[0]
|
366 |
|
if count > 0:
|
|
349 |
if self._get_statistics(node, path)[0] > 0:
|
367 |
350 |
raise IndexError('Container is not empty')
|
368 |
|
sql = 'delete from versions where name = ? or name like ?' # May contain hidden items.
|
369 |
|
self.con.execute(sql, (path, path + '/%',))
|
|
351 |
versions = self.node.node_purge_children(node, until, CLUSTER_HISTORY)
|
|
352 |
for v in versions:
|
|
353 |
self.mapper.map_remv(v)
|
|
354 |
self.node.node_purge_children(node, until, CLUSTER_DELETED)
|
|
355 |
self.node.node_remove(node)
|
370 |
356 |
self.policy.policy_unset(path)
|
371 |
|
self._copy_version(user, account, account, True, False) # New account version (for timestamp update).
|
372 |
357 |
|
373 |
|
@backend_method
|
374 |
|
def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], shared=False, until=None):
|
375 |
|
"""Return a list of objects existing under a container."""
|
376 |
|
|
377 |
|
logger.debug("list_objects: %s %s %s %s %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit, virtual, keys, shared, until)
|
378 |
|
allowed = []
|
379 |
|
if user != account:
|
380 |
|
if until:
|
381 |
|
raise NotAllowedError
|
382 |
|
allowed = self.permissions.access_list_paths(user, '/'.join((account, container)))
|
383 |
|
if not allowed:
|
384 |
|
raise NotAllowedError
|
385 |
|
else:
|
386 |
|
if shared:
|
387 |
|
allowed = self.permissions.access_list_shared('/'.join((account, container)))
|
388 |
|
path, version_id, mtime = self._get_containerinfo(account, container, until)
|
389 |
|
return self._list_objects(path, prefix, delimiter, marker, limit, virtual, keys, until, allowed)
|
390 |
|
|
391 |
|
@backend_method
|
392 |
|
def list_object_meta(self, user, account, container, until=None):
|
393 |
|
"""Return a list with all the container's object meta keys."""
|
394 |
|
|
395 |
|
logger.debug("list_object_meta: %s %s %s", account, container, until)
|
396 |
|
allowed = []
|
397 |
|
if user != account:
|
398 |
|
if until:
|
399 |
|
raise NotAllowedError
|
400 |
|
allowed = self.permissions.access_list_paths(user, '/'.join((account, container)))
|
401 |
|
if not allowed:
|
402 |
|
raise NotAllowedError
|
403 |
|
path, version_id, mtime = self._get_containerinfo(account, container, until)
|
404 |
|
sql = '''select distinct m.key from (%s) o, metadata m
|
405 |
|
where m.version_id = o.version_id and o.name like ?'''
|
406 |
|
sql = sql % self._sql_until(until)
|
407 |
|
param = (path + '/%',)
|
408 |
|
if allowed:
|
409 |
|
for x in allowed:
|
410 |
|
sql += ' and o.name like ?'
|
411 |
|
param += (x,)
|
412 |
|
c = self.con.execute(sql, param)
|
413 |
|
return [x[0] for x in c.fetchall()]
|
|
358 |
# @backend_method
|
|
359 |
# def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], shared=False, until=None):
|
|
360 |
# """Return a list of objects existing under a container."""
|
|
361 |
#
|
|
362 |
# logger.debug("list_objects: %s %s %s %s %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit, virtual, keys, shared, until)
|
|
363 |
# allowed = []
|
|
364 |
# if user != account:
|
|
365 |
# if until:
|
|
366 |
# raise NotAllowedError
|
|
367 |
# allowed = self.permissions.access_list_paths(user, '/'.join((account, container)))
|
|
368 |
# if not allowed:
|
|
369 |
# raise NotAllowedError
|
|
370 |
# else:
|
|
371 |
# if shared:
|
|
372 |
# allowed = self.permissions.access_list_shared('/'.join((account, container)))
|
|
373 |
# path, version_id, mtime = self._get_containerinfo(account, container, until)
|
|
374 |
# return self._list_objects(path, prefix, delimiter, marker, limit, virtual, keys, until, allowed)
|
|
375 |
|
|
376 |
# @backend_method
|
|
377 |
# def list_object_meta(self, user, account, container, until=None):
|
|
378 |
# """Return a list with all the container's object meta keys."""
|
|
379 |
#
|
|
380 |
# logger.debug("list_object_meta: %s %s %s", account, container, until)
|
|
381 |
# allowed = []
|
|
382 |
# if user != account:
|
|
383 |
# if until:
|
|
384 |
# raise NotAllowedError
|
|
385 |
# allowed = self.permissions.access_list_paths(user, '/'.join((account, container)))
|
|
386 |
# if not allowed:
|
|
387 |
# raise NotAllowedError
|
|
388 |
# path, version_id, mtime = self._get_containerinfo(account, container, until)
|
|
389 |
# sql = '''select distinct m.key from (%s) o, metadata m
|
|
390 |
# where m.version_id = o.version_id and o.name like ?'''
|
|
391 |
# sql = sql % self._sql_until(until)
|
|
392 |
# param = (path + '/%',)
|
|
393 |
# if allowed:
|
|
394 |
# for x in allowed:
|
|
395 |
# sql += ' and o.name like ?'
|
|
396 |
# param += (x,)
|
|
397 |
# c = self.con.execute(sql, param)
|
|
398 |
# return [x[0] for x in c.fetchall()]
|
414 |
399 |
|
415 |
400 |
@backend_method
|
416 |
401 |
def get_object_meta(self, user, account, container, name, version=None):
|
... | ... | |
418 |
403 |
|
419 |
404 |
logger.debug("get_object_meta: %s %s %s %s", account, container, name, version)
|
420 |
405 |
self._can_read(user, account, container, name)
|
421 |
|
path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name, version)
|
|
406 |
path, node = self._lookup_object(account, container, name)
|
|
407 |
props = self._get_version(node, version)
|
422 |
408 |
if version is None:
|
423 |
|
modified = mtime
|
|
409 |
modified = props[MTIME]
|
424 |
410 |
else:
|
425 |
|
modified = self._get_version(path, version)[2] # Overall last modification
|
|
411 |
# TODO: Use latest version if stop keeping statistics for leaves.
|
|
412 |
modified = self._get_statistics(node, path)[2] # Overall last modification.
|
426 |
413 |
|
427 |
|
meta = self._get_metadata(path, version_id)
|
428 |
|
meta.update({'name': name, 'bytes': size})
|
429 |
|
meta.update({'version': version_id, 'version_timestamp': mtime})
|
430 |
|
meta.update({'modified': modified, 'modified_by': muser})
|
|
414 |
meta = dict(self.node.attribute_get(props[SERIAL]))
|
|
415 |
meta.update({'name': name, 'bytes': props[SIZE]})
|
|
416 |
meta.update({'version': props[SERIAL], 'version_timestamp': props[MTIME]})
|
|
417 |
meta.update({'modified': modified, 'modified_by': props[MUSER]})
|
431 |
418 |
return meta
|
432 |
419 |
|
433 |
420 |
@backend_method
|
... | ... | |
436 |
423 |
|
437 |
424 |
logger.debug("update_object_meta: %s %s %s %s %s", account, container, name, meta, replace)
|
438 |
425 |
self._can_write(user, account, container, name)
|
439 |
|
path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name)
|
440 |
|
self._put_metadata(user, path, meta, replace)
|
|
426 |
path, node = self._lookup_object(account, container, name)
|
|
427 |
self._put_metadata(user, node, meta, replace)
|
441 |
428 |
|
442 |
429 |
@backend_method
|
443 |
430 |
def get_object_permissions(self, user, account, container, name):
|
... | ... | |
446 |
433 |
|
447 |
434 |
logger.debug("get_object_permissions: %s %s %s", account, container, name)
|
448 |
435 |
self._can_read(user, account, container, name)
|
449 |
|
path = self._get_objectinfo(account, container, name)[0]
|
|
436 |
path = self._lookup_object(account, container, name)[0]
|
450 |
437 |
return self.permissions.access_inherit(path)
|
451 |
438 |
|
452 |
439 |
@backend_method
|
... | ... | |
456 |
443 |
logger.debug("update_object_permissions: %s %s %s %s", account, container, name, permissions)
|
457 |
444 |
if user != account:
|
458 |
445 |
raise NotAllowedError
|
459 |
|
path = self._get_objectinfo(account, container, name)[0]
|
|
446 |
path = self._lookup_object(account, container, name)[0]
|
460 |
447 |
self._check_permissions(path, permissions)
|
461 |
448 |
self.permissions.access_set(path, permissions)
|
462 |
449 |
|
... | ... | |
466 |
453 |
|
467 |
454 |
logger.debug("get_object_public: %s %s %s", account, container, name)
|
468 |
455 |
self._can_read(user, account, container, name)
|
469 |
|
path = self._get_objectinfo(account, container, name)[0]
|
|
456 |
path = self._lookup_object(account, container, name)[0]
|
470 |
457 |
if self.permissions.public_check(path):
|
471 |
458 |
return '/public/' + path
|
472 |
459 |
return None
|
... | ... | |
477 |
464 |
|
478 |
465 |
logger.debug("update_object_public: %s %s %s %s", account, container, name, public)
|
479 |
466 |
self._can_write(user, account, container, name)
|
480 |
|
path = self._get_objectinfo(account, container, name)[0]
|
|
467 |
path = self._lookup_object(account, container, name)[0]
|
481 |
468 |
if not public:
|
482 |
469 |
self.permissions.public_unset(path)
|
483 |
470 |
else:
|
... | ... | |
489 |
476 |
|
490 |
477 |
logger.debug("get_object_hashmap: %s %s %s %s", account, container, name, version)
|
491 |
478 |
self._can_read(user, account, container, name)
|
492 |
|
path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name, version)
|
493 |
|
hashmap = self.mapper.map_retr(version_id)
|
494 |
|
return size, [binascii.hexlify(x) for x in hashmap]
|
|
479 |
path, node = self._lookup_object(account, container, name)
|
|
480 |
props = self._get_version(node, version)
|
|
481 |
hashmap = self.mapper.map_retr(props[SERIAL])
|
|
482 |
return props[SIZE], [binascii.hexlify(x) for x in hashmap]
|
495 |
483 |
|
496 |
484 |
@backend_method
|
497 |
485 |
def update_object_hashmap(self, user, account, container, name, size, hashmap, meta={}, replace_meta=False, permissions=None):
|
... | ... | |
506 |
494 |
ie = IndexError()
|
507 |
495 |
ie.data = missing
|
508 |
496 |
raise ie
|
509 |
|
path = self._get_containerinfo(account, container)[0]
|
510 |
|
path = '/'.join((path, name))
|
511 |
497 |
if permissions is not None:
|
512 |
498 |
self._check_permissions(path, permissions)
|
513 |
|
src_version_id, dest_version_id = self._copy_version(user, path, path, not replace_meta, False)
|
514 |
|
sql = 'update versions set size = ? where version_id = ?'
|
515 |
|
self.con.execute(sql, (size, dest_version_id))
|
|
499 |
path, node = self._put_object_path(account, container, name)
|
|
500 |
src_version_id, dest_version_id = self._copy_version(user, node, node, not replace_meta, False)
|
|
501 |
# TODO: Set size.
|
|
502 |
# sql = 'update versions set size = ? where version_id = ?'
|
|
503 |
# self.con.execute(sql, (size, dest_version_id))
|
516 |
504 |
self.mapper.map_stor(dest_version_id, [binascii.unhexlify(x) for x in hashmap])
|
517 |
|
for k, v in meta.iteritems():
|
518 |
|
sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
|
519 |
|
self.con.execute(sql, (dest_version_id, k, v))
|
|
505 |
# TODO: Check if can update meta with empty values.
|
|
506 |
self.node.attribute_set(dest_version_id, ((k, v) for k, v in meta.iteritems()))
|
520 |
507 |
if permissions is not None:
|
521 |
508 |
self.permissions.access_set(path, permissions)
|
522 |
509 |
|
... | ... | |
529 |
516 |
raise NotAllowedError
|
530 |
517 |
self._can_read(user, account, src_container, src_name)
|
531 |
518 |
self._can_write(user, account, dest_container, dest_name)
|
532 |
|
self._get_containerinfo(account, src_container)
|
533 |
|
if src_version is None:
|
534 |
|
src_path = self._get_objectinfo(account, src_container, src_name)[0]
|
535 |
|
else:
|
536 |
|
src_path = '/'.join((account, src_container, src_name))
|
537 |
|
dest_path = self._get_containerinfo(account, dest_container)[0]
|
538 |
|
dest_path = '/'.join((dest_path, dest_name))
|
|
519 |
src_path, src_node = self._lookup_object(account, src_container, src_name)
|
|
520 |
src_props = self._get_version(src_node)
|
539 |
521 |
if permissions is not None:
|
540 |
522 |
self._check_permissions(dest_path, permissions)
|
541 |
|
src_version_id, dest_version_id = self._copy_version(user, src_path, dest_path, not replace_meta, True, src_version)
|
542 |
|
for k, v in dest_meta.iteritems():
|
543 |
|
sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
|
544 |
|
self.con.execute(sql, (dest_version_id, k, v))
|
|
523 |
dest_path, dest_node = self._put_object_path(account, dest_container, dest_name)
|
|
524 |
src_version_id, dest_version_id = self._copy_version(user, src_node, dest_node, not replace_meta, True, src_version)
|
|
525 |
# TODO: Check if can update meta with empty values.
|
|
526 |
self.node.attribute_set(dest_version_id, ((k, v) for k, v in meta.iteritems()))
|
545 |
527 |
if permissions is not None:
|
546 |
528 |
self.permissions.access_set(dest_path, permissions)
|
547 |
529 |
|
... | ... | |
563 |
545 |
|
564 |
546 |
if until is not None:
|
565 |
547 |
path = '/'.join((account, container, name))
|
566 |
|
sql = '''select version_id from versions where name = ? and tstamp <= ?'''
|
567 |
|
c = self.con.execute(sql, (path, until))
|
568 |
|
for v in [x[0] in c.fetchall()]:
|
569 |
|
self._del_version(v)
|
|
548 |
node = self.node.node_lookup(path)
|
|
549 |
if node is None:
|
|
550 |
return
|
|
551 |
versions = self.node.node_purge(node, until, CLUSTER_NORMAL)
|
|
552 |
versions += self.node.node_purge(node, until, CLUSTER_HISTORY)
|
|
553 |
for v in versions:
|
|
554 |
self.mapper.map_remv(v)
|
|
555 |
self.node.node_purge_children(node, until, CLUSTER_DELETED)
|
570 |
556 |
try:
|
571 |
|
version_id = self._get_version(path)[0]
|
|
557 |
props = self._get_version(node)
|
572 |
558 |
except NameError:
|
573 |
559 |
pass
|
574 |
560 |
else:
|
575 |
561 |
self.permissions.access_clear(path)
|
576 |
562 |
return
|
577 |
563 |
|
578 |
|
path = self._get_objectinfo(account, container, name)[0]
|
579 |
|
self._put_version(path, user, 0, 1)
|
|
564 |
path, node = self._lookup_object(account, container, name)
|
|
565 |
self._copy_version(user, node, node, False, False, None, CLUSTER_DELETED)
|
580 |
566 |
self.permissions.access_clear(path)
|
581 |
567 |
|
582 |
|
@backend_method
|
583 |
|
def list_versions(self, user, account, container, name):
|
584 |
|
"""Return a list of all (version, version_timestamp) tuples for an object."""
|
585 |
|
|
586 |
|
logger.debug("list_versions: %s %s %s", account, container, name)
|
587 |
|
self._can_read(user, account, container, name)
|
588 |
|
# This will even show deleted versions.
|
589 |
|
path = '/'.join((account, container, name))
|
590 |
|
sql = '''select distinct version_id, tstamp from versions where name = ? and hide = 0'''
|
591 |
|
c = self.con.execute(sql, (path,))
|
592 |
|
return [(int(x[0]), int(x[1])) for x in c.fetchall()]
|
|
568 |
# @backend_method
|
|
569 |
# def list_versions(self, user, account, container, name):
|
|
570 |
# """Return a list of all (version, version_timestamp) tuples for an object."""
|
|
571 |
#
|
|
572 |
# logger.debug("list_versions: %s %s %s", account, container, name)
|
|
573 |
# self._can_read(user, account, container, name)
|
|
574 |
# # This will even show deleted versions.
|
|
575 |
# path = '/'.join((account, container, name))
|
|
576 |
# sql = '''select distinct version_id, tstamp from versions where name = ? and hide = 0'''
|
|
577 |
# c = self.con.execute(sql, (path,))
|
|
578 |
# return [(int(x[0]), int(x[1])) for x in c.fetchall()]
|
593 |
579 |
|
594 |
580 |
@backend_method(autocommit=0)
|
595 |
581 |
def get_block(self, hash):
|
... | ... | |
619 |
605 |
h, e = self.blocker.block_delta(binascii.unhexlify(hash), ((offset, data),))
|
620 |
606 |
return binascii.hexlify(h)
|
621 |
607 |
|
622 |
|
|
623 |
|
|
624 |
|
|
625 |
|
|
626 |
|
|
627 |
|
|
628 |
|
|
629 |
|
|
630 |
|
|
631 |
|
|
632 |
|
|
633 |
|
|
634 |
|
|
635 |
|
|
636 |
|
|
637 |
|
|
638 |
|
def _sql_until(self, until=None):
|
639 |
|
"""Return the sql to get the latest versions until the timestamp given."""
|
640 |
|
if until is None:
|
641 |
|
until = int(time.time())
|
642 |
|
sql = '''select version_id, name, tstamp, size from versions v
|
643 |
|
where version_id = (select max(version_id) from versions
|
644 |
|
where v.name = name and tstamp <= %s)
|
645 |
|
and hide = 0'''
|
646 |
|
return sql % (until,)
|
647 |
|
|
648 |
|
def _put_version(self, path, user, size=0, hide=0):
|
649 |
|
tstamp = int(time.time())
|
650 |
|
sql = 'insert into versions (name, user, tstamp, size, hide) values (?, ?, ?, ?, ?)'
|
651 |
|
id = self.con.execute(sql, (path, user, tstamp, size, hide)).lastrowid
|
652 |
|
return str(id), tstamp
|
653 |
|
|
654 |
|
def _get_versioninfo(self, account, container, name, until=None):
|
655 |
|
"""Return path, latest version, associated timestamp and size until the timestamp given."""
|
656 |
|
|
657 |
|
p = (account, container, name)
|
658 |
|
try:
|
659 |
|
p = p[:p.index(None)]
|
660 |
|
except ValueError:
|
661 |
|
pass
|
662 |
|
path = '/'.join(p)
|
663 |
|
node = self.node.node_lookup(path)
|
664 |
|
if node is not None:
|
665 |
|
props = self.node.version_lookup(node, until, CLUSTER_NORMAL)
|
666 |
|
# TODO: Do one lookup.
|
667 |
|
if props is None and until is not None:
|
668 |
|
props = self.node.version_lookup(node, until, CLUSTER_HISTORY)
|
669 |
|
if props is not None:
|
670 |
|
return path, props[SERIAL], props[MTIME], props[SIZE]
|
671 |
|
raise NameError('Path does not exist')
|
672 |
|
|
673 |
|
def _get_accountinfo(self, account, until=None):
|
674 |
|
try:
|
675 |
|
path, version_id, mtime, size = self._get_versioninfo(account, None, None, until)
|
676 |
|
return version_id, mtime
|
677 |
|
except:
|
678 |
|
raise NameError('Account does not exist')
|
679 |
|
|
680 |
|
def _get_containerinfo(self, account, container, until=None):
|
681 |
|
try:
|
682 |
|
path, version_id, mtime, size = self._get_versioninfo(account, container, None, until)
|
683 |
|
return path, version_id, mtime
|
684 |
|
except:
|
685 |
|
raise NameError('Container does not exist')
|
686 |
|
|
687 |
|
def _get_objectinfo(self, account, container, name, version=None):
|
688 |
|
path = '/'.join((account, container, name))
|
689 |
|
version_id, muser, mtime, size = self._get_version(path, version)
|
690 |
|
return path, version_id, muser, mtime, size
|
691 |
|
|
692 |
|
def _create_account(self, user, account):
|
693 |
|
try:
|
694 |
|
self._get_accountinfo(account)
|
695 |
|
except NameError:
|
696 |
|
self._put_version(account, user)
|
697 |
|
|
698 |
608 |
def _check_policy(self, policy):
|
699 |
609 |
for k in policy.keys():
|
700 |
610 |
if policy[k] == '':
|
... | ... | |
721 |
631 |
limit = 10000
|
722 |
632 |
return start, limit
|
723 |
633 |
|
724 |
|
def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None, allowed=[]):
|
725 |
|
cont_prefix = path + '/'
|
726 |
|
if keys and len(keys) > 0:
|
727 |
|
sql = '''select distinct o.name, o.version_id from (%s) o, metadata m where o.name like ? and
|
728 |
|
m.version_id = o.version_id and m.key in (%s)'''
|
729 |
|
sql = sql % (self._sql_until(until), ', '.join('?' * len(keys)))
|
730 |
|
param = (cont_prefix + prefix + '%',) + tuple(keys)
|
731 |
|
if allowed:
|
732 |
|
sql += ' and (' + ' or '.join(('o.name like ?',) * len(allowed)) + ')'
|
733 |
|
param += tuple([x + '%' for x in allowed])
|
734 |
|
sql += ' order by o.name'
|
735 |
|
else:
|
736 |
|
sql = 'select name, version_id from (%s) where name like ?'
|
737 |
|
sql = sql % self._sql_until(until)
|
738 |
|
param = (cont_prefix + prefix + '%',)
|
739 |
|
if allowed:
|
740 |
|
sql += ' and (' + ' or '.join(('name like ?',) * len(allowed)) + ')'
|
741 |
|
param += tuple([x + '%' for x in allowed])
|
742 |
|
sql += ' order by name'
|
743 |
|
c = self.con.execute(sql, param)
|
744 |
|
objects = [(x[0][len(cont_prefix):], x[1]) for x in c.fetchall()]
|
745 |
|
if delimiter:
|
746 |
|
pseudo_objects = []
|
747 |
|
for x in objects:
|
748 |
|
pseudo_name = x[0]
|
749 |
|
i = pseudo_name.find(delimiter, len(prefix))
|
750 |
|
if not virtual:
|
751 |
|
# If the delimiter is not found, or the name ends
|
752 |
|
# with the delimiter's first occurence.
|
753 |
|
if i == -1 or len(pseudo_name) == i + len(delimiter):
|
754 |
|
pseudo_objects.append(x)
|
755 |
|
else:
|
756 |
|
# If the delimiter is found, keep up to (and including) the delimiter.
|
757 |
|
if i != -1:
|
758 |
|
pseudo_name = pseudo_name[:i + len(delimiter)]
|
759 |
|
if pseudo_name not in [y[0] for y in pseudo_objects]:
|
760 |
|
if pseudo_name == x[0]:
|
761 |
|
pseudo_objects.append(x)
|
762 |
|
else:
|
763 |
|
pseudo_objects.append((pseudo_name, None))
|
764 |
|
objects = pseudo_objects
|
765 |
|
|
766 |
|
start, limit = self._list_limits([x[0] for x in objects], marker, limit)
|
767 |
|
return objects[start:start + limit]
|
768 |
|
|
769 |
|
def _del_version(self, version):
|
770 |
|
self.mapper.map_remv(version)
|
771 |
|
sql = 'delete from versions where version_id = ?'
|
772 |
|
self.con.execute(sql, (version,))
|
|
634 |
# def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None, allowed=[]):
|
|
635 |
# cont_prefix = path + '/'
|
|
636 |
# if keys and len(keys) > 0:
|
|
637 |
# sql = '''select distinct o.name, o.version_id from (%s) o, metadata m where o.name like ? and
|
|
638 |
# m.version_id = o.version_id and m.key in (%s)'''
|
|
639 |
# sql = sql % (self._sql_until(until), ', '.join('?' * len(keys)))
|
|
640 |
# param = (cont_prefix + prefix + '%',) + tuple(keys)
|
|
641 |
# if allowed:
|
|
642 |
# sql += ' and (' + ' or '.join(('o.name like ?',) * len(allowed)) + ')'
|
|
643 |
# param += tuple([x + '%' for x in allowed])
|
|
644 |
# sql += ' order by o.name'
|
|
645 |
# else:
|
|
646 |
# sql = 'select name, version_id from (%s) where name like ?'
|
|
647 |
# sql = sql % self._sql_until(until)
|
|
648 |
# param = (cont_prefix + prefix + '%',)
|
|
649 |
# if allowed:
|
|
650 |
# sql += ' and (' + ' or '.join(('name like ?',) * len(allowed)) + ')'
|
|
651 |
# param += tuple([x + '%' for x in allowed])
|
|
652 |
# sql += ' order by name'
|
|
653 |
# c = self.con.execute(sql, param)
|
|
654 |
# objects = [(x[0][len(cont_prefix):], x[1]) for x in c.fetchall()]
|
|
655 |
# if delimiter:
|
|
656 |
# pseudo_objects = []
|
|
657 |
# for x in objects:
|
|
658 |
# pseudo_name = x[0]
|
|
659 |
# i = pseudo_name.find(delimiter, len(prefix))
|
|
660 |
# if not virtual:
|
|
661 |
# # If the delimiter is not found, or the name ends
|
|
662 |
# # with the delimiter's first occurence.
|
|
663 |
# if i == -1 or len(pseudo_name) == i + len(delimiter):
|
|
664 |
# pseudo_objects.append(x)
|
|
665 |
# else:
|
|
666 |
# # If the delimiter is found, keep up to (and including) the delimiter.
|
|
667 |
# if i != -1:
|
|
668 |
# pseudo_name = pseudo_name[:i + len(delimiter)]
|
|
669 |
# if pseudo_name not in [y[0] for y in pseudo_objects]:
|
|
670 |
# if pseudo_name == x[0]:
|
|
671 |
# pseudo_objects.append(x)
|
|
672 |
# else:
|
|
673 |
# pseudo_objects.append((pseudo_name, None))
|
|
674 |
# objects = pseudo_objects
|
|
675 |
#
|
|
676 |
# start, limit = self._list_limits([x[0] for x in objects], marker, limit)
|
|
677 |
# return objects[start:start + limit]
|
773 |
678 |
|
774 |
679 |
# Path functions.
|
775 |
680 |
|
|
681 |
def _put_path(self, parent, path):
|
|
682 |
node = self.node.node_create(parent, path)
|
|
683 |
self.node.version_create(node, 0, None, path, CLUSTER_NORMAL)
|
|
684 |
|
|
685 |
def _put_object_path(self, account, container, name):
|
|
686 |
path, parent = self._lookup_container(account, container)
|
|
687 |
path = '/'.join((path, name))
|
|
688 |
node = self.node.node_lookup(path)
|
|
689 |
if node is None:
|
|
690 |
node = self.node.node_create(parent, path)
|
|
691 |
return path, node
|
|
692 |
|
776 |
693 |
def _lookup_account(self, account, create=True):
|
777 |
694 |
node = self.node.node_lookup(account)
|
778 |
695 |
if node is None and create:
|
779 |
|
node = self.node.node_create(ROOTNODE, account)
|
780 |
|
self.node.version_create(node, 0, None, account, CLUSTER_NORMAL)
|
781 |
|
return node
|
|
696 |
self._put_path(ROOTNODE, account)
|
|
697 |
return account, node
|
782 |
698 |
|
783 |
699 |
def _lookup_container(self, account, container):
|
784 |
|
node = self.node.node_lookup('/'.join((account, container)))
|
|
700 |
path = '/'.join((account, container))
|
|
701 |
node = self.node.node_lookup(path)
|
785 |
702 |
if node is None:
|
786 |
703 |
raise NameError('Container does not exist')
|
787 |
|
return node
|
|
704 |
return path, node
|
788 |
705 |
|
789 |
706 |
def _lookup_object(self, account, container, name):
|
790 |
|
node = self.node.node_lookup('/'.join((account, container, name)))
|
|
707 |
path = '/'.join((account, container, name))
|
|
708 |
node = self.node.node_lookup(path)
|
791 |
709 |
if node is None:
|
792 |
710 |
raise NameError('Object does not exist')
|
793 |
|
return node
|
|
711 |
return path, node
|
794 |
712 |
|
795 |
713 |
def _get_properties(self, node, until=None):
|
796 |
714 |
"""Return properties until the timestamp given."""
|
... | ... | |
807 |
725 |
def _get_statistics(self, node, path, until=None):
|
808 |
726 |
"""Return count, sum of size and latest timestamp of everything under node/path."""
|
809 |
727 |
|
|
728 |
# TODO: Remove this function.
|
|
729 |
|
810 |
730 |
if until is None:
|
811 |
731 |
return self.node.node_statistics(node, CLUSTER_NORMAL)
|
812 |
732 |
else:
|
... | ... | |
823 |
743 |
raise IndexError('Version does not exist')
|
824 |
744 |
return props
|
825 |
745 |
|
826 |
|
def _copy_version(self, user, src_node, dest_node, copy_meta=True, copy_data=True, src_version=None):
|
|
746 |
def _copy_version(self, user, src_node, dest_node, copy_meta=True, copy_data=True, src_version=None, dest_cluster=CLUSTER_NORMAL):
|
|
747 |
|
|
748 |
# TODO: Break this into two functions - one that creates and one that copies.
|
|
749 |
|
827 |
750 |
# Get source serial and size.
|
828 |
751 |
if src_version is not None:
|
829 |
752 |
src_props = self._get_version(src_node, src_version)
|
... | ... | |
848 |
771 |
dest_props = self.node.version_lookup(dest_node, inf, CLUSTER_NORMAL)
|
849 |
772 |
if dest_props is not None:
|
850 |
773 |
self.node.version_recluster(dest_props[SERIAL], CLUSTER_HISTORY)
|
851 |
|
dest_version_id, mtime = self.node.version_create(dest_node, size, src_version_id, user, CLUSTER_NORMAL)
|
|
774 |
dest_version_id, mtime = self.node.version_create(dest_node, size, src_version_id, user, dest_cluster)
|
852 |
775 |
|
853 |
776 |
# Copy meta and data.
|
854 |
777 |
if copy_meta and src_version_id is not None:
|