source file: /home/buildslave/tahoe/edgy/build/src/allmydata/mutable/repair.py
file stats: 31 lines, 28 executed: 90.3% covered
   1. 
   2. from zope.interface import implements
   3. from allmydata.interfaces import IRepairResults, ICheckerResults
   4. 
   5. class RepairResults:
   6.     implements(IRepairResults)
   7. 
   8.     def __init__(self, smap):
   9.         self.servermap = smap
  10. 
  11.     def to_string(self):
  12.         return ""
  13. 
  14. class MustForceRepairError(Exception):
  15.     pass
  16. 
  17. class Repairer:
  18.     def __init__(self, node, checker_results):
  19.         self.node = node
  20.         self.checker_results = ICheckerResults(checker_results)
  21.         assert checker_results.storage_index == self.node.get_storage_index()
  22. 
  23.     def start(self, force=False):
  24.         # download, then re-publish. If a server had a bad share, try to
  25.         # replace it with a good one of the same shnum.
  26. 
  27.         # The normal repair operation should not be used to replace
  28.         # application-specific merging of alternate versions: i.e if there
  29.         # are multiple highest seqnums with different roothashes. In this
  30.         # case, the application must use node.upload() (referencing the
  31.         # servermap that indicates the multiple-heads condition), or
  32.         # node.overwrite(). The repair() operation will refuse to run in
  33.         # these conditions unless a force=True argument is provided. If
  34.         # force=True is used, then the highest root hash will be reinforced.
  35. 
  36.         # Likewise, the presence of an unrecoverable latest version is an
  37.         # unusual event, and should ideally be handled by retrying a couple
  38.         # times (spaced out over hours or days) and hoping that new shares
  39.         # will become available. If repair(force=True) is called, data will
  40.         # be lost: a new seqnum will be generated with the same contents as
  41.         # the most recent recoverable version, skipping over the lost
  42.         # version. repair(force=False) will refuse to run in a situation like
  43.         # this.
  44. 
  45.         # Repair is designed to fix the following injuries:
  46.         #  missing shares: add new ones to get at least N distinct ones
  47.         #  old shares: replace old shares with the latest version
  48.         #  bogus shares (bad sigs): replace the bad one with a good one
  49. 
  50.         smap = self.checker_results.get_servermap()
  51. 
  52.         if smap.unrecoverable_newer_versions():
  53.             if not force:
  54.                 raise MustForceRepairError("There were unrecoverable newer "
  55.                                            "versions, so force=True must be "
  56.                                            "passed to the repair() operation")
  57.             # continuing on means that node.upload() will pick a seqnum that
  58.             # is higher than everything visible in the servermap, effectively
  59.             # discarding the unrecoverable versions.
  60.         if smap.needs_merge():
  61.             if not force:
  62.                 raise MustForceRepairError("There were multiple recoverable "
  63.                                            "versions with identical seqnums, "
  64.                                            "so force=True must be passed to "
  65.                                            "the repair() operation")
  66.             # continuing on means that smap.best_recoverable_version() will
  67.             # pick the one with the highest roothash, and then node.upload()
  68.             # will replace all shares with its contents
  69. 
  70.         # missing shares are handled during upload, which tries to find a
  71.         # home for every share
  72. 
  73.         # old shares are handled during upload, which will replace any share
  74.         # that was present in the servermap
  75. 
  76.         # bogus shares need to be managed here. We might notice a bogus share
  77.         # during mapupdate (whether done for a filecheck or just before a
  78.         # download) by virtue of it having an invalid signature. We might
  79.         # also notice a bad hash in the share during verify or download. In
  80.         # either case, the problem will be noted in the servermap, and the
  81.         # bad share (along with its checkstring) will be recorded in
  82.         # servermap.bad_shares . Publish knows that it should try and replace
  83.         # these.
  84. 
  85.         # I chose to use the retrieve phase to ensure that the privkey is
  86.         # available, to avoid the extra roundtrip that would occur if we,
  87.         # say, added an smap.get_privkey() method.
  88. 
  89.         assert self.node.get_writekey() # repair currently requires a writecap
  90. 
  91.         best_version = smap.best_recoverable_version()
  92.         d = self.node.download_version(smap, best_version, fetch_privkey=True)
  93.         d.addCallback(self.node.upload, smap)
  94.         d.addCallback(self.get_results, smap)
  95.         return d
  96. 
  97.     def get_results(self, res, smap):
  98.         return RepairResults(smap)