2006-12-01 03:14:23 +00:00
|
|
|
import os
|
|
|
|
|
|
|
|
from foolscap import Referenceable
|
|
|
|
from twisted.application import service
|
|
|
|
from twisted.python.failure import Failure
|
|
|
|
from allmydata.util import idlib
|
2006-12-02 02:17:50 +00:00
|
|
|
from zope.interface import implements
|
2006-12-03 00:25:57 +00:00
|
|
|
from allmydata.interfaces import RIBucketWriter, RIBucketReader
|
2006-12-01 03:14:23 +00:00
|
|
|
|
2006-12-04 09:03:25 +00:00
|
|
|
from allmydata.util.assertutil import precondition, _assert
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
class NoSuchBucketError(Failure):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class BucketStore(service.MultiService, Referenceable):
|
|
|
|
def __init__(self, store_dir):
|
|
|
|
precondition(os.path.isdir(store_dir))
|
|
|
|
service.MultiService.__init__(self)
|
|
|
|
self._store_dir = store_dir
|
|
|
|
|
|
|
|
self._leases = set() # should do weakref dances.
|
|
|
|
|
|
|
|
def _get_bucket_dir(self, verifierid):
|
|
|
|
avid = idlib.b2a(verifierid)
|
|
|
|
return os.path.join(self._store_dir, avid)
|
|
|
|
|
|
|
|
def has_bucket(self, verifierid):
|
|
|
|
return os.path.exists(self._get_bucket_dir(verifierid))
|
|
|
|
|
2006-12-04 02:07:41 +00:00
|
|
|
def allocate_bucket(self, verifierid, bucket_num, size,
|
|
|
|
leaser_credentials, canary):
|
2006-12-01 03:14:23 +00:00
|
|
|
bucket_dir = self._get_bucket_dir(verifierid)
|
|
|
|
precondition(not os.path.exists(bucket_dir))
|
|
|
|
precondition(isinstance(bucket_num, int))
|
2006-12-01 10:04:54 +00:00
|
|
|
bucket = WriteBucket(bucket_dir, verifierid, bucket_num, size)
|
2006-12-01 03:14:23 +00:00
|
|
|
bucket.set_leaser(leaser_credentials)
|
2006-12-04 02:07:41 +00:00
|
|
|
lease = Lease(verifierid, leaser_credentials, bucket, canary)
|
2006-12-01 03:14:23 +00:00
|
|
|
self._leases.add(lease)
|
|
|
|
return lease
|
|
|
|
|
2006-12-03 10:01:43 +00:00
|
|
|
def get_buckets(self, verifierid):
|
2006-12-01 03:14:23 +00:00
|
|
|
# for now, only returns those created by this process, in this run
|
2006-12-01 10:04:54 +00:00
|
|
|
bucket_dir = self._get_bucket_dir(verifierid)
|
|
|
|
if os.path.exists(bucket_dir):
|
2006-12-03 10:01:43 +00:00
|
|
|
b = ReadBucket(bucket_dir, verifierid)
|
2006-12-04 02:07:41 +00:00
|
|
|
return [(b.get_bucket_num(), b)]
|
2006-12-01 03:14:23 +00:00
|
|
|
else:
|
2006-12-03 10:01:43 +00:00
|
|
|
return []
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
class Lease(Referenceable):
|
2006-12-02 02:17:50 +00:00
|
|
|
implements(RIBucketWriter)
|
|
|
|
|
2006-12-04 02:07:41 +00:00
|
|
|
def __init__(self, verifierid, leaser, bucket, canary):
|
2006-12-01 03:14:23 +00:00
|
|
|
self._leaser = leaser
|
|
|
|
self._verifierid = verifierid
|
|
|
|
self._bucket = bucket
|
2006-12-04 02:07:41 +00:00
|
|
|
canary.notifyOnDisconnect(self._lost_canary)
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
def get_bucket(self):
|
|
|
|
return self._bucket
|
|
|
|
|
|
|
|
def remote_write(self, data):
|
|
|
|
self._bucket.write(data)
|
|
|
|
|
2006-12-03 00:25:57 +00:00
|
|
|
def remote_close(self):
|
|
|
|
self._bucket.close()
|
2006-12-01 03:14:23 +00:00
|
|
|
|
2006-12-04 02:07:41 +00:00
|
|
|
def _lost_canary(self):
|
|
|
|
pass
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
class Bucket:
|
2006-12-01 10:04:54 +00:00
|
|
|
def __init__(self, bucket_dir, verifierid):
|
2006-12-01 03:14:23 +00:00
|
|
|
self._bucket_dir = bucket_dir
|
|
|
|
self._verifierid = verifierid
|
|
|
|
|
|
|
|
def _write_attr(self, name, val):
|
2006-12-01 03:36:57 +00:00
|
|
|
f = file(os.path.join(self._bucket_dir, name), 'wb')
|
2006-12-01 03:14:23 +00:00
|
|
|
f.write(val)
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
def _read_attr(self, name):
|
2006-12-01 03:49:07 +00:00
|
|
|
f = file(os.path.join(self._bucket_dir, name), 'rb')
|
2006-12-01 03:14:23 +00:00
|
|
|
data = f.read()
|
|
|
|
f.close()
|
|
|
|
return data
|
|
|
|
|
2006-12-01 10:04:54 +00:00
|
|
|
def is_complete(self):
|
|
|
|
return os.path.exists(os.path.join(self._bucket_dir, 'closed'))
|
|
|
|
|
|
|
|
class WriteBucket(Bucket):
|
|
|
|
def __init__(self, bucket_dir, verifierid, bucket_num, size):
|
|
|
|
Bucket.__init__(self, bucket_dir, verifierid)
|
|
|
|
precondition(not os.path.exists(bucket_dir))
|
|
|
|
os.mkdir(bucket_dir)
|
|
|
|
|
|
|
|
self._size = size
|
|
|
|
self._data = file(os.path.join(self._bucket_dir, 'data'), 'wb')
|
|
|
|
self._bytes_written = 0
|
|
|
|
|
|
|
|
self._write_attr('bucket_num', str(bucket_num))
|
|
|
|
|
2006-12-01 03:14:23 +00:00
|
|
|
def set_leaser(self, leaser):
|
2006-12-01 10:04:54 +00:00
|
|
|
self._write_attr('leases', leaser)
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
def write(self, data):
|
|
|
|
precondition(len(data) + self._bytes_written <= self._size)
|
|
|
|
self._data.write(data)
|
|
|
|
self._data.flush()
|
2006-12-01 03:37:36 +00:00
|
|
|
self._bytes_written += len(data)
|
2006-12-01 03:14:23 +00:00
|
|
|
|
2006-12-03 00:25:57 +00:00
|
|
|
def close(self):
|
2006-12-01 03:14:23 +00:00
|
|
|
precondition(self._bytes_written == self._size)
|
|
|
|
self._data.close()
|
2006-12-01 09:45:55 +00:00
|
|
|
self._write_attr('closed', '')
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
def is_complete(self):
|
2006-12-01 10:04:54 +00:00
|
|
|
complete = Bucket.is_complete(self)
|
|
|
|
if complete:
|
|
|
|
_assert(os.path.getsize(os.path.join(self._bucket_dir, 'data')) == self._size)
|
|
|
|
return complete
|
|
|
|
|
2006-12-04 02:07:41 +00:00
|
|
|
class ReadBucket(Bucket, Referenceable):
|
|
|
|
implements(RIBucketReader)
|
|
|
|
|
2006-12-01 10:04:54 +00:00
|
|
|
def __init__(self, bucket_dir, verifierid):
|
|
|
|
Bucket.__init__(self, bucket_dir, verifierid)
|
|
|
|
precondition(self.is_complete()) # implicitly asserts bucket_dir exists
|
2006-12-01 03:14:23 +00:00
|
|
|
|
|
|
|
def get_bucket_num(self):
|
|
|
|
return int(self._read_attr('bucket_num'))
|
|
|
|
|
|
|
|
def read(self):
|
2006-12-01 10:04:54 +00:00
|
|
|
return self._read_attr('data')
|
2006-12-04 02:07:41 +00:00
|
|
|
remote_read = read
|