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)