mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-23 14:52:26 +00:00
storage: rewrite slot API, now use testv_and_readv_and_writev or readv
This commit is contained in:
parent
95a3da92fe
commit
e08b091d9f
@ -84,49 +84,19 @@ class RIBucketReader(RemoteInterface):
|
||||
|
||||
TestVector = ListOf(TupleOf(int, int, str, str))
|
||||
# elements are (offset, length, operator, specimen)
|
||||
# operator is one of "lt, le, eq, ne, ge, gt, nop"
|
||||
# operator is one of "lt, le, eq, ne, ge, gt"
|
||||
# nop always passes and is used to fetch data while writing.
|
||||
# you should use length==len(specimen) for everything except nop
|
||||
DataVector = ListOf(TupleOf(int, ShareData))
|
||||
# (offset, data). This limits us to 30 writes of 1MiB each per call
|
||||
TestAndWriteVectorsForShares = DictOf(int,
|
||||
TupleOf(TestVector,
|
||||
DataVector,
|
||||
ChoiceOf(None, int))) # new_length
|
||||
ReadVector = ListOf(TupleOf(int, int))
|
||||
TestResults = ListOf(str)
|
||||
ReadData = ListOf(ShareData)
|
||||
# returns data[offset:offset+length] for each element of TestVector
|
||||
|
||||
class RIMutableSlot(RemoteInterface):
|
||||
def testv_and_writev(write_enabler=Hash,
|
||||
testv=TestVector,
|
||||
datav=DataVector,
|
||||
new_length=ChoiceOf(None, int)):
|
||||
"""General-purpose test-and-set operation for mutable slots. Perform
|
||||
the given comparisons. If they all pass, then apply the write vector.
|
||||
|
||||
If new_length is not None, use it to set the size of the container.
|
||||
This can be used to pre-allocate space for a series of upcoming
|
||||
writes, or truncate existing data. If the container is growing,
|
||||
new_length will be applied before datav. If the container is
|
||||
shrinking, it will be applied afterwards.
|
||||
|
||||
Return the old data that was used for the comparisons.
|
||||
|
||||
The boolean return value is True if the write vector was applied,
|
||||
false if not.
|
||||
|
||||
If the write_enabler is wrong, this will raise BadWriteEnablerError.
|
||||
To enable share migration, the exception will have the nodeid used
|
||||
for the old write enabler embedded in it, in the following string::
|
||||
|
||||
The write enabler was recorded by nodeid '%s'.
|
||||
|
||||
"""
|
||||
return TupleOf(bool, TestResults)
|
||||
|
||||
def read(offset=int, length=int):
|
||||
return ShareData
|
||||
|
||||
def get_length():
|
||||
return int
|
||||
|
||||
class RIStorageServer(RemoteInterface):
|
||||
def allocate_buckets(storage_index=StorageIndex,
|
||||
renew_secret=LeaseRenewSecret,
|
||||
@ -170,13 +140,27 @@ class RIStorageServer(RemoteInterface):
|
||||
return DictOf(int, RIBucketReader, maxKeys=MAX_BUCKETS)
|
||||
|
||||
|
||||
def allocate_mutable_slot(storage_index=StorageIndex,
|
||||
write_enabler=Hash,
|
||||
renew_secret=LeaseRenewSecret,
|
||||
cancel_secret=LeaseCancelSecret,
|
||||
sharenums=SetOf(int, maxLength=MAX_BUCKETS),
|
||||
allocated_size=int):
|
||||
"""
|
||||
|
||||
def slot_readv(storage_index=StorageIndex,
|
||||
shares=ListOf(int), readv=ReadVector):
|
||||
"""Read a vector from the numbered shares associated with the given
|
||||
storage index. An empty shares list means to return data from all
|
||||
known shares. Returns a dictionary with one key per share."""
|
||||
return DictOf(int, DataVector) # shnum -> results
|
||||
|
||||
def slot_testv_and_readv_and_writev(storage_index=StorageIndex,
|
||||
secrets=TupleOf(Hash, Hash, Hash),
|
||||
tw_vectors=TestAndWriteVectorsForShares,
|
||||
r_vector=ReadVector,
|
||||
):
|
||||
"""General-purpose test-and-set operation for mutable slots. Perform
|
||||
a bunch of comparisons against the existing shares. If they all pass,
|
||||
then apply a bunch of write vectors to those shares. Then use the
|
||||
read vectors to extract data from all the shares and return the data.
|
||||
|
||||
This method is, um, large. The goal is to allow clients to update all
|
||||
the shares associated with a mutable file in a single round trip.
|
||||
|
||||
@param storage_index: the index of the bucket to be created or
|
||||
increfed.
|
||||
@param write_enabler: a secret that is stored along with the slot.
|
||||
@ -188,32 +172,51 @@ class RIStorageServer(RemoteInterface):
|
||||
stored for later comparison by the server. Each
|
||||
server is given a different secret.
|
||||
@param cancel_secret: Like renew_secret, but protects bucket decref.
|
||||
@param sharenums: these are the share numbers (probably between 0 and
|
||||
99) that the sender is proposing to store on this
|
||||
server.
|
||||
@param allocated_size: all shares will pre-allocate this many bytes.
|
||||
Use this to a) confirm that you can claim as
|
||||
much space as you want before you actually
|
||||
send the data, and b) reduce the disk-IO cost
|
||||
of doing incremental writes.
|
||||
|
||||
@return: dict mapping sharenum to slot. The return value may include
|
||||
more sharenums than asked, if some shares already existed.
|
||||
New leases are added for all
|
||||
shares.
|
||||
The 'secrets' argument is a tuple of (write_enabler, renew_secret,
|
||||
cancel_secret). The first is required to perform any write. The
|
||||
latter two are used when allocating new shares. To simply acquire a
|
||||
new lease on existing shares, use an empty testv and an empty writev.
|
||||
|
||||
Each share can have a separate test vector (i.e. a list of
|
||||
comparisons to perform). If all vectors for all shares pass, then all
|
||||
writes for all shares are recorded. Each comparison is a 4-tuple of
|
||||
(offset, length, operator, specimen), which effectively does a
|
||||
read(offset, length) and then compares the result against the
|
||||
specimen using the given equality/inequality operator. Reads from the
|
||||
end of the container are truncated, and missing shares behave like
|
||||
empty ones, so to assert that a share doesn't exist (for use when
|
||||
creating a new share), use (0, 1, 'eq', '').
|
||||
|
||||
The write vector will be applied to the given share, expanding it if
|
||||
necessary. A write vector applied to a share number that did not
|
||||
exist previously will cause that share to be created.
|
||||
|
||||
Each write vector is accompanied by a 'new_length' argument. If
|
||||
new_length is not None, use it to set the size of the container. This
|
||||
can be used to pre-allocate space for a series of upcoming writes, or
|
||||
truncate existing data. If the container is growing, new_length will
|
||||
be applied before datav. If the container is shrinking, it will be
|
||||
applied afterwards.
|
||||
|
||||
The read vector is used to extract data from all known shares,
|
||||
*before* any writes have been applied. The same vector is used for
|
||||
all shares. This captures the state that was tested by the test
|
||||
vector.
|
||||
|
||||
This method returns two values: a boolean and a dict. The boolean is
|
||||
True if the write vectors were applied, False if not. The dict is
|
||||
keyed by share number, and each value contains a list of strings, one
|
||||
for each element of the read vector.
|
||||
|
||||
If the write_enabler is wrong, this will raise BadWriteEnablerError.
|
||||
To enable share migration, the exception will have the nodeid used
|
||||
for the old write enabler embedded in it, in the following string::
|
||||
|
||||
The write enabler was recorded by nodeid '%s'.
|
||||
|
||||
"""
|
||||
return DictOf(int, RIMutableSlot, maxKeys=MAX_BUCKETS)
|
||||
|
||||
def get_mutable_slot(storage_index=StorageIndex):
|
||||
"""This returns an empty dictionary if the server has no shares
|
||||
of the slot mentioned."""
|
||||
return DictOf(int, RIMutableSlot, maxKeys=MAX_BUCKETS)
|
||||
|
||||
def readv_slots(storage_index=StorageIndex, readv=ReadVector):
|
||||
"""Read a vector from all shares associated with the given storage
|
||||
index. Returns a dictionary with one key per share."""
|
||||
return DictOf(int, DataVector) # shnum -> results
|
||||
return TupleOf(bool, DictOf(int, ReadData))
|
||||
|
||||
class IStorageBucketWriter(Interface):
|
||||
def put_block(segmentnum=int, data=ShareData):
|
||||
|
@ -8,7 +8,7 @@ from twisted.internet import defer
|
||||
from zope.interface import implements
|
||||
from allmydata.interfaces import RIStorageServer, RIBucketWriter, \
|
||||
RIBucketReader, IStorageBucketWriter, IStorageBucketReader, HASH_SIZE, \
|
||||
BadWriteEnablerError, RIMutableSlot
|
||||
BadWriteEnablerError
|
||||
from allmydata.util import fileutil, idlib, mathutil
|
||||
from allmydata.util.assertutil import precondition, _assert
|
||||
|
||||
@ -240,13 +240,7 @@ class BucketReader(Referenceable):
|
||||
assert struct.calcsize("L"), 4
|
||||
assert struct.calcsize("Q"), 8
|
||||
|
||||
class MutableShareFile(Referenceable):
|
||||
# note: at any given time, there should only be a single instance of this
|
||||
# class per filename. More than one is likely to corrupt the container,
|
||||
# because of state we cache in instance variables. This requires the
|
||||
# StorageServer to use a WeakValueDictionary, indexed by filename. This
|
||||
# could be improved by cacheing less and doing more IO.
|
||||
implements(RIMutableSlot)
|
||||
class MutableShareFile:
|
||||
|
||||
DATA_LENGTH_OFFSET = struct.calcsize(">32s32s32s")
|
||||
EXTRA_LEASE_OFFSET = DATA_LENGTH_OFFSET + 8
|
||||
@ -548,12 +542,6 @@ class MutableShareFile(Referenceable):
|
||||
assert magic == self.MAGIC
|
||||
return (write_enabler, write_enabler_nodeid)
|
||||
|
||||
def remote_read(self, offset, length):
|
||||
f = open(self.home, 'rb')
|
||||
data = self._read_share_data(f, offset, length)
|
||||
f.close()
|
||||
return data
|
||||
|
||||
def readv(self, readv):
|
||||
datav = []
|
||||
f = open(self.home, 'rb')
|
||||
@ -562,36 +550,37 @@ class MutableShareFile(Referenceable):
|
||||
f.close()
|
||||
return datav
|
||||
|
||||
def remote_get_length(self):
|
||||
f = open(self.home, 'rb')
|
||||
data_length = self._read_data_length(f)
|
||||
f.close()
|
||||
return data_length
|
||||
# def remote_get_length(self):
|
||||
# f = open(self.home, 'rb')
|
||||
# data_length = self._read_data_length(f)
|
||||
# f.close()
|
||||
# return data_length
|
||||
|
||||
def remote_testv_and_writev(self, write_enabler, testv, datav, new_length):
|
||||
def check_write_enabler(self, write_enabler):
|
||||
f = open(self.home, 'rb+')
|
||||
(real_write_enabler, write_enabler_nodeid) = \
|
||||
self._read_write_enabler_and_nodeid(f)
|
||||
f.close()
|
||||
if write_enabler != real_write_enabler:
|
||||
# accomodate share migration by reporting the nodeid used for the
|
||||
# old write enabler.
|
||||
f.close()
|
||||
msg = "The write enabler was recorded by nodeid '%s'." % \
|
||||
(idlib.b2a(write_enabler_nodeid),)
|
||||
raise BadWriteEnablerError(msg)
|
||||
|
||||
# check testv
|
||||
test_results_v = []
|
||||
test_failed = False
|
||||
def check_testv(self, testv):
|
||||
test_good = True
|
||||
f = open(self.home, 'rb+')
|
||||
for (offset, length, operator, specimen) in testv:
|
||||
data = self._read_share_data(f, offset, length)
|
||||
test_results_v.append(data)
|
||||
if not self.compare(data, operator, specimen):
|
||||
test_failed = True
|
||||
if test_failed:
|
||||
f.close()
|
||||
return (False, test_results_v)
|
||||
# now apply the write vector
|
||||
test_good = False
|
||||
break
|
||||
f.close()
|
||||
return test_good
|
||||
|
||||
def writev(self, datav, new_length):
|
||||
f = open(self.home, 'rb+')
|
||||
for (offset, data) in datav:
|
||||
self._write_share_data(f, offset, data)
|
||||
if new_length is not None:
|
||||
@ -599,12 +588,36 @@ class MutableShareFile(Referenceable):
|
||||
f.seek(self.DATA_LENGTH_OFFSET)
|
||||
f.write(struct.pack(">Q", new_length))
|
||||
f.close()
|
||||
return (True, test_results_v)
|
||||
|
||||
def compare(self, a, op, b):
|
||||
assert op in ("nop", "lt", "le", "eq", "ne", "ge", "gt")
|
||||
if op == "nop":
|
||||
return True
|
||||
assert op in ("lt", "le", "eq", "ne", "ge", "gt")
|
||||
if op == "lt":
|
||||
return a < b
|
||||
if op == "le":
|
||||
return a <= b
|
||||
if op == "eq":
|
||||
return a == b
|
||||
if op == "ne":
|
||||
return a != b
|
||||
if op == "ge":
|
||||
return a >= b
|
||||
if op == "gt":
|
||||
return a > b
|
||||
# never reached
|
||||
|
||||
class EmptyShare:
|
||||
|
||||
def check_testv(self, testv):
|
||||
test_good = True
|
||||
for (offset, length, operator, specimen) in testv:
|
||||
data = ""
|
||||
if not self.compare(data, operator, specimen):
|
||||
test_good = False
|
||||
break
|
||||
return test_good
|
||||
|
||||
def compare(self, a, op, b):
|
||||
assert op in ("lt", "le", "eq", "ne", "ge", "gt")
|
||||
if op == "lt":
|
||||
return a < b
|
||||
if op == "le":
|
||||
@ -842,51 +855,83 @@ class StorageServer(service.MultiService, Referenceable):
|
||||
except StopIteration:
|
||||
return iter([])
|
||||
|
||||
def remote_allocate_mutable_slot(self, storage_index,
|
||||
write_enabler,
|
||||
renew_secret, cancel_secret,
|
||||
sharenums,
|
||||
allocated_size):
|
||||
my_nodeid = self.my_nodeid
|
||||
sharenums = set(sharenums)
|
||||
shares = self.remote_get_mutable_slot(storage_index)
|
||||
existing_shnums = set(shares.keys())
|
||||
si_s = idlib.b2a(storage_index)
|
||||
bucketdir = os.path.join(self.sharedir, si_s)
|
||||
fileutil.make_dirs(bucketdir)
|
||||
for shnum in (sharenums - existing_shnums):
|
||||
filename = os.path.join(bucketdir, "%d" % shnum)
|
||||
shares[shnum] = create_mutable_sharefile(filename, my_nodeid,
|
||||
write_enabler)
|
||||
|
||||
# update the lease on everything
|
||||
ownerid = 1 # TODO
|
||||
expire_time = time.time() + 31*24*60*60 # one month
|
||||
anid = my_nodeid
|
||||
lease_info = (ownerid, expire_time, renew_secret, cancel_secret, anid)
|
||||
for share in shares.values():
|
||||
share.add_or_renew_lease(lease_info)
|
||||
return shares
|
||||
|
||||
def remote_get_mutable_slot(self, storage_index):
|
||||
"""This returns an empty dictionary if the server has no shares
|
||||
of the slot mentioned."""
|
||||
def remote_slot_testv_and_readv_and_writev(self, storage_index,
|
||||
secrets,
|
||||
test_and_write_vectors,
|
||||
read_vector):
|
||||
si_s = idlib.b2a(storage_index)
|
||||
(write_enabler, renew_secret, cancel_secret) = secrets
|
||||
# shares exist if there is a file for them
|
||||
bucketdir = os.path.join(self.sharedir, si_s)
|
||||
if not os.path.isdir(bucketdir):
|
||||
return {}
|
||||
slots = {}
|
||||
for sharenum_s in os.listdir(bucketdir):
|
||||
try:
|
||||
sharenum = int(sharenum_s)
|
||||
except ValueError:
|
||||
continue
|
||||
filename = os.path.join(bucketdir, sharenum_s)
|
||||
slots[sharenum] = MutableShareFile(filename)
|
||||
return slots
|
||||
shares = {}
|
||||
if os.path.isdir(bucketdir):
|
||||
for sharenum_s in os.listdir(bucketdir):
|
||||
try:
|
||||
sharenum = int(sharenum_s)
|
||||
except ValueError:
|
||||
continue
|
||||
filename = os.path.join(bucketdir, sharenum_s)
|
||||
msf = MutableShareFile(filename)
|
||||
msf.check_write_enabler(write_enabler)
|
||||
shares[sharenum] = msf
|
||||
# write_enabler is good for all existing shares.
|
||||
|
||||
def remote_readv_slots(self, storage_index, readv):
|
||||
# Now evaluate test vectors.
|
||||
testv_is_good = True
|
||||
for sharenum in test_and_write_vectors:
|
||||
(testv, datav, new_length) = test_and_write_vectors[sharenum]
|
||||
if sharenum in shares:
|
||||
if not shares[sharenum].check_testv(testv):
|
||||
testv_is_good = False
|
||||
break
|
||||
else:
|
||||
# compare the vectors against an empty share, in which all
|
||||
# reads return empty strings.
|
||||
if not EmptyShare().check_testv(testv):
|
||||
testv_is_good = False
|
||||
break
|
||||
|
||||
# now gather the read vectors, before we do any writes
|
||||
read_data = {}
|
||||
for sharenum, share in shares.items():
|
||||
read_data[sharenum] = share.readv(read_vector)
|
||||
|
||||
if testv_is_good:
|
||||
# now apply the write vectors
|
||||
for sharenum in test_and_write_vectors:
|
||||
(testv, datav, new_length) = test_and_write_vectors[sharenum]
|
||||
if sharenum not in shares:
|
||||
# allocate a new share
|
||||
allocated_size = 2000 # arbitrary, really
|
||||
share = self._allocate_slot_share(bucketdir, secrets,
|
||||
sharenum,
|
||||
allocated_size,
|
||||
owner_num=0)
|
||||
shares[sharenum] = share
|
||||
shares[sharenum].writev(datav, new_length)
|
||||
# and update the leases on all shares
|
||||
ownerid = 1 # TODO
|
||||
expire_time = time.time() + 31*24*60*60 # one month
|
||||
my_nodeid = self.my_nodeid
|
||||
anid = my_nodeid
|
||||
lease_info = (ownerid, expire_time, renew_secret, cancel_secret,
|
||||
anid)
|
||||
for share in shares.values():
|
||||
share.add_or_renew_lease(lease_info)
|
||||
|
||||
# all done
|
||||
return (testv_is_good, read_data)
|
||||
|
||||
def _allocate_slot_share(self, bucketdir, secrets, sharenum,
|
||||
allocated_size, owner_num=0):
|
||||
(write_enabler, renew_secret, cancel_secret) = secrets
|
||||
my_nodeid = self.my_nodeid
|
||||
fileutil.make_dirs(bucketdir)
|
||||
filename = os.path.join(bucketdir, "%d" % sharenum)
|
||||
share = create_mutable_sharefile(filename, my_nodeid, write_enabler)
|
||||
return share
|
||||
|
||||
def remote_slot_readv(self, storage_index, shares, readv):
|
||||
si_s = idlib.b2a(storage_index)
|
||||
# shares exist if there is a file for them
|
||||
bucketdir = os.path.join(self.sharedir, si_s)
|
||||
@ -898,9 +943,10 @@ class StorageServer(service.MultiService, Referenceable):
|
||||
sharenum = int(sharenum_s)
|
||||
except ValueError:
|
||||
continue
|
||||
filename = os.path.join(bucketdir, sharenum_s)
|
||||
msf = MutableShareFile(filename)
|
||||
datavs[sharenum] = msf.readv(readv)
|
||||
if sharenum in shares or not shares:
|
||||
filename = os.path.join(bucketdir, sharenum_s)
|
||||
msf = MutableShareFile(filename)
|
||||
datavs[sharenum] = msf.readv(readv)
|
||||
return datavs
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ import itertools
|
||||
from allmydata import interfaces
|
||||
from allmydata.util import fileutil, hashutil, idlib
|
||||
from allmydata.storage import BucketWriter, BucketReader, \
|
||||
WriteBucketProxy, ReadBucketProxy, StorageServer
|
||||
WriteBucketProxy, ReadBucketProxy, StorageServer, MutableShareFile
|
||||
from allmydata.interfaces import BadWriteEnablerError
|
||||
|
||||
class Bucket(unittest.TestCase):
|
||||
@ -476,228 +476,275 @@ class MutableServer(unittest.TestCase):
|
||||
write_enabler = self.write_enabler(we_tag)
|
||||
renew_secret = self.renew_secret(lease_tag)
|
||||
cancel_secret = self.cancel_secret(lease_tag)
|
||||
return ss.remote_allocate_mutable_slot(storage_index,
|
||||
write_enabler,
|
||||
renew_secret, cancel_secret,
|
||||
sharenums, size)
|
||||
rstaraw = ss.remote_slot_testv_and_readv_and_writev
|
||||
testandwritev = dict( [ (shnum, ([], [], None) )
|
||||
for shnum in sharenums ] )
|
||||
readv = []
|
||||
rc = rstaraw(storage_index,
|
||||
(write_enabler, renew_secret, cancel_secret),
|
||||
testandwritev,
|
||||
readv)
|
||||
(did_write, readv_data) = rc
|
||||
self.failUnless(did_write)
|
||||
self.failUnless(isinstance(readv_data, dict))
|
||||
self.failUnlessEqual(len(readv_data), 0)
|
||||
|
||||
def test_allocate(self):
|
||||
ss = self.create("test_allocate")
|
||||
shares = self.allocate(ss, "si1", "we1", self._secret.next(),
|
||||
self.allocate(ss, "si1", "we1", self._secret.next(),
|
||||
set([0,1,2]), 100)
|
||||
self.failUnlessEqual(len(shares), 3)
|
||||
self.failUnlessEqual(set(shares.keys()), set([0,1,2]))
|
||||
shares2 = ss.remote_get_mutable_slot("si1")
|
||||
self.failUnlessEqual(len(shares2), 3)
|
||||
self.failUnlessEqual(set(shares2.keys()), set([0,1,2]))
|
||||
|
||||
s0 = shares[0]
|
||||
self.failUnlessEqual(s0.remote_read(0, 10), "")
|
||||
self.failUnlessEqual(s0.remote_read(100, 10), "")
|
||||
read = ss.remote_slot_readv
|
||||
self.failUnlessEqual(read("si1", [0], [(0, 10)]),
|
||||
{0: [""]})
|
||||
self.failUnlessEqual(read("si1", [], [(0, 10)]),
|
||||
{0: [""], 1: [""], 2: [""]})
|
||||
self.failUnlessEqual(read("si1", [0], [(100, 10)]),
|
||||
{0: [""]})
|
||||
|
||||
# try writing to one
|
||||
WE = self.write_enabler("we1")
|
||||
secrets = ( self.write_enabler("we1"),
|
||||
self.renew_secret("we1"),
|
||||
self.cancel_secret("we1") )
|
||||
data = "".join([ ("%d" % i) * 10 for i in range(10) ])
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[],
|
||||
[(0, data),],
|
||||
new_length=None)
|
||||
self.failUnlessEqual(answer, (True, []))
|
||||
write = ss.remote_slot_testv_and_readv_and_writev
|
||||
answer = write("si1", secrets,
|
||||
{0: ([], [(0,data)], None)},
|
||||
[])
|
||||
self.failUnlessEqual(answer, (True, {0:[],1:[],2:[]}) )
|
||||
|
||||
self.failUnlessEqual(s0.remote_read(0, 20), "00000000001111111111")
|
||||
self.failUnlessEqual(s0.remote_read(95, 10), "99999")
|
||||
self.failUnlessEqual(s0.remote_get_length(), 100)
|
||||
self.failUnlessEqual(read("si1", [0], [(0,20)]),
|
||||
{0: ["00000000001111111111"]})
|
||||
self.failUnlessEqual(read("si1", [0], [(95,10)]),
|
||||
{0: ["99999"]})
|
||||
#self.failUnlessEqual(s0.remote_get_length(), 100)
|
||||
|
||||
bad_secrets = ("bad write enabler", secrets[1], secrets[2])
|
||||
self.failUnlessRaises(BadWriteEnablerError,
|
||||
s0.remote_testv_and_writev,
|
||||
"bad write enabler",
|
||||
[], [], None)
|
||||
write, "si1", bad_secrets,
|
||||
{}, [])
|
||||
|
||||
# this testv should fail
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(0, 12, "eq", "444444444444"),
|
||||
(20, 5, "eq", "22222"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["000000000011",
|
||||
"22222"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
answer = write("si1", secrets,
|
||||
{0: ([(0, 12, "eq", "444444444444"),
|
||||
(20, 5, "eq", "22222"),
|
||||
],
|
||||
[(0, "x"*100)],
|
||||
None),
|
||||
},
|
||||
[(0,12), (20,5)],
|
||||
)
|
||||
self.failUnlessEqual(answer, (False,
|
||||
{0: ["000000000011", "22222"],
|
||||
1: ["", ""],
|
||||
2: ["", ""],
|
||||
}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
|
||||
# as should this one
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "lt", "11111"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
answer = write("si1", secrets,
|
||||
{0: ([(10, 5, "lt", "11111"),
|
||||
],
|
||||
[(0, "x"*100)],
|
||||
None),
|
||||
},
|
||||
[(10,5)],
|
||||
)
|
||||
self.failUnlessEqual(answer, (False,
|
||||
{0: ["11111"],
|
||||
1: [""],
|
||||
2: [""]},
|
||||
))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
|
||||
|
||||
def test_operators(self):
|
||||
# test operators, the data we're comparing is '11111' in all cases.
|
||||
# test both fail+pass, reset data after each one.
|
||||
ss = self.create("test_operators")
|
||||
shares = self.allocate(ss, "si1", "we1", self._secret.next(),
|
||||
set([0,1,2]), 100)
|
||||
s0 = shares[0]
|
||||
WE = self.write_enabler("we1")
|
||||
data = "".join([ ("%d" % i) * 10 for i in range(10) ])
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[],
|
||||
[(0, data),],
|
||||
new_length=None)
|
||||
|
||||
# nop
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "nop", "11111"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "x"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
secrets = ( self.write_enabler("we1"),
|
||||
self.renew_secret("we1"),
|
||||
self.cancel_secret("we1") )
|
||||
data = "".join([ ("%d" % i) * 10 for i in range(10) ])
|
||||
write = ss.remote_slot_testv_and_readv_and_writev
|
||||
read = ss.remote_slot_readv
|
||||
|
||||
def reset():
|
||||
write("si1", secrets,
|
||||
{0: ([], [(0,data)], None)},
|
||||
[])
|
||||
|
||||
reset()
|
||||
|
||||
# lt
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "lt", "11110"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "lt", "11110"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "x"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
self.failUnlessEqual(read("si1", [], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "lt", "11111"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "lt", "11111"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "x"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "lt", "11112"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "lt", "11112"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
# le
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "le", "11110"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "le", "11110"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "x"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "le", "11111"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "le", "11111"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "le", "11112"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "le", "11112"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
# eq
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "eq", "11112"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "eq", "11112"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "x"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "eq", "11111"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "eq", "11111"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
# ne
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "ne", "11111"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "ne", "11111"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "x"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "ne", "11112"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "ne", "11112"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
# ge
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "ge", "11110"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "ge", "11110"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "ge", "11111"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "ge", "11111"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "ge", "11112"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "ge", "11112"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
# gt
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "gt", "11110"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "gt", "11110"),
|
||||
],
|
||||
[(0, "y"*100)], None)
|
||||
self.failUnlessEqual(answer, (True, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), "y"*100)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "y"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (True, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: ["y"*100]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "gt", "11111"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "gt", "11111"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "x"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[(10, 5, "gt", "11112"),
|
||||
answer = write("si1", secrets, {0: ([(10, 5, "gt", "11112"),
|
||||
],
|
||||
[(0, "x"*100)], None)
|
||||
self.failUnlessEqual(answer, (False, ["11111"]))
|
||||
self.failUnlessEqual(s0.remote_read(0, 100), data)
|
||||
s0.remote_testv_and_writev(WE, [], [(0,data)], None)
|
||||
[(0, "x"*100)],
|
||||
None,
|
||||
)}, [(10,5)])
|
||||
self.failUnlessEqual(answer, (False, {0: ["11111"]}))
|
||||
self.failUnlessEqual(read("si1", [0], [(0,100)]), {0: [data]})
|
||||
reset()
|
||||
|
||||
def test_readv(self):
|
||||
ss = self.create("test_allocate")
|
||||
shares = self.allocate(ss, "si1", "we1", self._secret.next(),
|
||||
set([0,1,2]), 100)
|
||||
WE = self.write_enabler("we1")
|
||||
ss = self.create("test_readv")
|
||||
secrets = ( self.write_enabler("we1"),
|
||||
self.renew_secret("we1"),
|
||||
self.cancel_secret("we1") )
|
||||
data = "".join([ ("%d" % i) * 10 for i in range(10) ])
|
||||
write = ss.remote_slot_testv_and_readv_and_writev
|
||||
read = ss.remote_slot_readv
|
||||
data = [("%d" % i) * 100 for i in range(3)]
|
||||
for i in range(3):
|
||||
rc = shares[i].remote_testv_and_writev(WE, [], [(0, data[i])],
|
||||
new_length=None)
|
||||
self.failUnlessEqual(rc, (True, []))
|
||||
answer = ss.remote_readv_slots("si1", [(0, 10)])
|
||||
rc = write("si1", secrets,
|
||||
{0: ([], [(0,data[0])], None),
|
||||
1: ([], [(0,data[1])], None),
|
||||
2: ([], [(0,data[2])], None),
|
||||
}, [])
|
||||
self.failUnlessEqual(rc, (True, {}))
|
||||
|
||||
answer = read("si1", [], [(0, 10)])
|
||||
self.failUnlessEqual(answer, {0: ["0"*10],
|
||||
1: ["1"*10],
|
||||
2: ["2"*10]})
|
||||
@ -716,15 +763,15 @@ class MutableServer(unittest.TestCase):
|
||||
|
||||
def test_leases(self):
|
||||
ss = self.create("test_leases")
|
||||
secret = 14
|
||||
shares = self.allocate(ss, "si1", "we1", secret, set([0,1,2]), 100)
|
||||
s0 = shares[0]
|
||||
WE = self.write_enabler("we1")
|
||||
def secrets(n):
|
||||
return ( self.write_enabler("we1"),
|
||||
self.renew_secret("we1-%d" % n),
|
||||
self.cancel_secret("we1-%d" % n) )
|
||||
data = "".join([ ("%d" % i) * 10 for i in range(10) ])
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[],
|
||||
[(0, data),],
|
||||
new_length=None)
|
||||
write = ss.remote_slot_testv_and_readv_and_writev
|
||||
read = ss.remote_slot_readv
|
||||
rc = write("si1", secrets(0), {0: ([], [(0,data)], None)}, [])
|
||||
self.failUnlessEqual(rc, (True, {}))
|
||||
|
||||
# create a random non-numeric file in the bucket directory, to
|
||||
# exercise the code that's supposed to ignore those.
|
||||
@ -736,40 +783,41 @@ class MutableServer(unittest.TestCase):
|
||||
|
||||
# re-allocate the slots and use the same secrets, that should update
|
||||
# the lease
|
||||
shares2 = self.allocate(ss, "si1", "we1", secret, set([0,1,2]), 100)
|
||||
write("si1", secrets(0), {0: ([], [(0,data)], None)}, [])
|
||||
|
||||
# renew it directly
|
||||
ss.remote_renew_lease("si1", self.renew_secret(secret))
|
||||
ss.remote_renew_lease("si1", secrets(0)[1])
|
||||
|
||||
# now allocate them with a bunch of different secrets, to trigger the
|
||||
# extended lease code
|
||||
shares2 = self.allocate(ss, "si1", "we1", secret+1, set([0,1,2]), 100)
|
||||
shares2 = self.allocate(ss, "si1", "we1", secret+2, set([0,1,2]), 100)
|
||||
shares2 = self.allocate(ss, "si1", "we1", secret+3, set([0,1,2]), 100)
|
||||
shares2 = self.allocate(ss, "si1", "we1", secret+4, set([0,1,2]), 100)
|
||||
shares2 = self.allocate(ss, "si1", "we1", secret+5, set([0,1,2]), 100)
|
||||
# cancel one of them
|
||||
ss.remote_cancel_lease("si1", self.cancel_secret(secret+5))
|
||||
write("si1", secrets(1), {0: ([], [(0,data)], None)}, [])
|
||||
write("si1", secrets(2), {0: ([], [(0,data)], None)}, [])
|
||||
write("si1", secrets(3), {0: ([], [(0,data)], None)}, [])
|
||||
write("si1", secrets(4), {0: ([], [(0,data)], None)}, [])
|
||||
write("si1", secrets(5), {0: ([], [(0,data)], None)}, [])
|
||||
|
||||
# cancel one of them
|
||||
ss.remote_cancel_lease("si1", secrets(5)[2])
|
||||
|
||||
s0 = MutableShareFile(os.path.join(bucket_dir, "0"))
|
||||
all_leases = s0.debug_get_leases()
|
||||
self.failUnlessEqual(len(all_leases), 5)
|
||||
|
||||
# and write enough data to expand the container, forcing the server
|
||||
# to move the leases
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[],
|
||||
[(0, data),],
|
||||
new_length=200)
|
||||
write("si1", secrets(0),
|
||||
{0: ([], [(0,data)], 200), },
|
||||
[])
|
||||
|
||||
# read back the leases, make sure they're still intact.
|
||||
self.compare_leases_without_timestamps(all_leases,
|
||||
s0.debug_get_leases())
|
||||
|
||||
ss.remote_renew_lease("si1", self.renew_secret(secret))
|
||||
ss.remote_renew_lease("si1", self.renew_secret(secret+1))
|
||||
ss.remote_renew_lease("si1", self.renew_secret(secret+2))
|
||||
ss.remote_renew_lease("si1", self.renew_secret(secret+3))
|
||||
ss.remote_renew_lease("si1", self.renew_secret(secret+4))
|
||||
ss.remote_renew_lease("si1", secrets(0)[1])
|
||||
ss.remote_renew_lease("si1", secrets(1)[1])
|
||||
ss.remote_renew_lease("si1", secrets(2)[1])
|
||||
ss.remote_renew_lease("si1", secrets(3)[1])
|
||||
ss.remote_renew_lease("si1", secrets(4)[1])
|
||||
self.compare_leases_without_timestamps(all_leases,
|
||||
s0.debug_get_leases())
|
||||
# get a new copy of the leases, with the current timestamps. Reading
|
||||
@ -782,40 +830,40 @@ class MutableServer(unittest.TestCase):
|
||||
# is present, to provide for share migration
|
||||
self.failUnlessRaises(IndexError,
|
||||
ss.remote_renew_lease, "si1",
|
||||
self.renew_secret(secret+20))
|
||||
secrets(20)[1])
|
||||
# same for cancelling
|
||||
self.failUnlessRaises(IndexError,
|
||||
ss.remote_cancel_lease, "si1",
|
||||
self.cancel_secret(secret+20))
|
||||
self.failUnlessEqual(all_leases, s0.debug_get_leases())
|
||||
s0.remote_read(0, 200)
|
||||
secrets(20)[2])
|
||||
self.failUnlessEqual(all_leases, s0.debug_get_leases())
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[],
|
||||
[(200, "make me bigger"),],
|
||||
new_length=None)
|
||||
# reading shares should not modify the timestamp
|
||||
read("si1", [], [(0,200)])
|
||||
self.failUnlessEqual(all_leases, s0.debug_get_leases())
|
||||
|
||||
write("si1", secrets(0),
|
||||
{0: ([], [(200, "make me bigger")], None)}, [])
|
||||
self.compare_leases_without_timestamps(all_leases,
|
||||
s0.debug_get_leases())
|
||||
|
||||
answer = s0.remote_testv_and_writev(WE,
|
||||
[],
|
||||
[(500, "make me really bigger"),],
|
||||
new_length=None)
|
||||
write("si1", secrets(0),
|
||||
{0: ([], [(500, "make me really bigger")], None)}, [])
|
||||
self.compare_leases_without_timestamps(all_leases,
|
||||
s0.debug_get_leases())
|
||||
|
||||
# now cancel them all
|
||||
ss.remote_cancel_lease("si1", self.cancel_secret(secret))
|
||||
ss.remote_cancel_lease("si1", self.cancel_secret(secret+1))
|
||||
ss.remote_cancel_lease("si1", self.cancel_secret(secret+2))
|
||||
ss.remote_cancel_lease("si1", self.cancel_secret(secret+3))
|
||||
ss.remote_cancel_lease("si1", secrets(0)[2])
|
||||
ss.remote_cancel_lease("si1", secrets(1)[2])
|
||||
ss.remote_cancel_lease("si1", secrets(2)[2])
|
||||
ss.remote_cancel_lease("si1", secrets(3)[2])
|
||||
|
||||
# the slot should still be there
|
||||
shares3 = ss.remote_get_mutable_slot("si1")
|
||||
self.failUnlessEqual(len(shares3), 3)
|
||||
remaining_shares = read("si1", [], [(0,10)])
|
||||
self.failUnlessEqual(len(remaining_shares), 1)
|
||||
self.failUnlessEqual(len(s0.debug_get_leases()), 1)
|
||||
|
||||
ss.remote_cancel_lease("si1", self.cancel_secret(secret+4))
|
||||
ss.remote_cancel_lease("si1", secrets(4)[2])
|
||||
# now the slot should be gone
|
||||
self.failUnlessEqual(ss.remote_get_mutable_slot("si1"), {})
|
||||
no_shares = read("si1", [], [(0,10)])
|
||||
self.failUnlessEqual(no_shares, {})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user