instead of weird errors. Closes #874 and #786.
Previously, if the file had 0 shares, this would raise TypeError as it tried
to call download_version(None). If the file had some shares but fewer than
'k', it would incorrectly raise MustForceRepairError.
Added get_successful() to the IRepairResults API, to give repair() a place to
report non-code-bug problems like this.
class IRepairResults(Interface):
"""I contain the results of a repair operation."""
+ def get_successful(self):
+ """Returns a boolean: True if the repair made the file healthy, False
+ if not. Repair failure generally indicates a file that has been
+ damaged beyond repair."""
class IClient(Interface):
self.cr_results.repair_attempted = True
d = self._node.repair(self.results)
def _repair_finished(repair_results):
- self.cr_results.repair_successful = True
+ self.cr_results.repair_successful = repair_results.get_successful()
r = CheckResults(from_string(self._node.get_uri()), self._storage_index)
self.cr_results.post_repair_results = r
self._fill_checker_results(repair_results.servermap, r)
from zope.interface import implements
+from twisted.internet import defer
from allmydata.interfaces import IRepairResults, ICheckResults
class RepairResults:
def __init__(self, smap):
self.servermap = smap
-
+ def set_successful(self, successful):
+ self.successful = successful
+ def get_successful(self):
+ return self.successful
def to_string(self):
return ""
smap = self.check_results.get_servermap()
+ best_version = smap.best_recoverable_version()
+ if not best_version:
+ # the file is damaged beyond repair
+ rr = RepairResults(smap)
+ rr.set_successful(False)
+ return defer.succeed(rr)
+
if smap.unrecoverable_newer_versions():
if not force:
raise MustForceRepairError("There were unrecoverable newer "
if not self.node.get_writekey():
raise RepairRequiresWritecapError("Sorry, repair currently requires a writecap, to set the write-enabler properly.")
- best_version = smap.best_recoverable_version()
d = self.node.download_version(smap, best_version, fetch_privkey=True)
d.addCallback(self.node.upload, smap)
d.addCallback(self.get_results, smap)
return d
def get_results(self, res, smap):
- return RepairResults(smap)
+ rr = RepairResults(smap)
+ rr.set_successful(True)
+ return rr
d.addCallback(lambda check_results: self._fn.repair(check_results))
def _check_results(rres):
self.failUnless(IRepairResults.providedBy(rres))
+ self.failUnless(rres.get_successful())
# TODO: examine results
self.copy_shares()
current_shares = self.old_shares[-1]
self.failUnlessEqual(old_shares, current_shares)
+ def test_unrepairable_0shares(self):
+ d = self.publish_one()
+ def _delete_all_shares(ign):
+ shares = self._storage._peers
+ for peerid in shares:
+ shares[peerid] = {}
+ d.addCallback(_delete_all_shares)
+ d.addCallback(lambda ign: self._fn.check(Monitor()))
+ d.addCallback(lambda check_results: self._fn.repair(check_results))
+ def _check(crr):
+ self.failUnlessEqual(crr.get_successful(), False)
+ d.addCallback(_check)
+ return d
+
+ def test_unrepairable_1share(self):
+ d = self.publish_one()
+ def _delete_all_shares(ign):
+ shares = self._storage._peers
+ for peerid in shares:
+ for shnum in list(shares[peerid]):
+ if shnum > 0:
+ del shares[peerid][shnum]
+ d.addCallback(_delete_all_shares)
+ d.addCallback(lambda ign: self._fn.check(Monitor()))
+ d.addCallback(lambda check_results: self._fn.repair(check_results))
+ def _check(crr):
+ self.failUnlessEqual(crr.get_successful(), False)
+ d.addCallback(_check)
+ return d
+
def test_merge(self):
self.old_shares = []
d = self.publish_multiple()
self._fn.repair(check_results, force=True))
# this should give us 10 shares of the highest roothash
def _check_repair_results(rres):
+ self.failUnless(rres.get_successful())
pass # TODO
d.addCallback(_check_repair_results)
d.addCallback(lambda res: self._fn.get_servermap(MODE_CHECK))
d.addCallback(lambda check_results: self._fn.repair(check_results))
# this should give us 10 shares of v3
def _check_repair_results(rres):
+ self.failUnless(rres.get_successful())
pass # TODO
d.addCallback(_check_repair_results)
d.addCallback(lambda res: self._fn.get_servermap(MODE_CHECK))