source: trunk/src/allmydata/mutable/repairer.py

Last change on this file was 1cfe843d, checked in by Alexandre Detiste <alexandre.detiste@…>, at 2024-02-22T23:40:25Z

more python2 removal

  • Property mode set to 100644
File size: 5.9 KB
Line 
1"""
2Ported to Python 3.
3"""
4
5from zope.interface import implementer
6from twisted.internet import defer
7from allmydata.interfaces import IRepairResults, ICheckResults
8from allmydata.mutable.publish import MutableData
9from allmydata.mutable.common import MODE_REPAIR
10from allmydata.mutable.servermap import ServerMap, ServermapUpdater
11
12@implementer(IRepairResults)
13class RepairResults(object):
14
15    def __init__(self, smap):
16        self.servermap = smap
17    def set_successful(self, successful):
18        self.successful = successful
19    def get_successful(self):
20        return self.successful
21    def to_string(self):
22        return ""
23
24class RepairRequiresWritecapError(Exception):
25    """Repair currently requires a writecap."""
26
27class MustForceRepairError(Exception):
28    pass
29
30class Repairer(object):
31    def __init__(self, node, check_results, storage_broker, history, monitor):
32        self.node = node
33        self.check_results = ICheckResults(check_results)
34        assert check_results.get_storage_index() == node.get_storage_index()
35        self._storage_broker = storage_broker
36        self._history = history
37        self._monitor = monitor
38
39    def start(self, force=False):
40        # download, then re-publish. If a server had a bad share, try to
41        # replace it with a good one of the same shnum.
42
43        # The normal repair operation should not be used to replace
44        # application-specific merging of alternate versions: i.e if there
45        # are multiple highest seqnums with different roothashes. In this
46        # case, the application must use node.upload() (referencing the
47        # servermap that indicates the multiple-heads condition), or
48        # node.overwrite(). The repair() operation will refuse to run in
49        # these conditions unless a force=True argument is provided. If
50        # force=True is used, then the highest root hash will be reinforced.
51
52        # Likewise, the presence of an unrecoverable latest version is an
53        # unusual event, and should ideally be handled by retrying a couple
54        # times (spaced out over hours or days) and hoping that new shares
55        # will become available. If repair(force=True) is called, data will
56        # be lost: a new seqnum will be generated with the same contents as
57        # the most recent recoverable version, skipping over the lost
58        # version. repair(force=False) will refuse to run in a situation like
59        # this.
60
61        # Repair is designed to fix the following injuries:
62        #  missing shares: add new ones to get at least N distinct ones
63        #  old shares: replace old shares with the latest version
64        #  bogus shares (bad sigs): replace the bad one with a good one
65
66        # first, update the servermap in MODE_REPAIR, which files all shares
67        # and makes sure we get the privkey.
68        u = ServermapUpdater(self.node, self._storage_broker, self._monitor,
69                             ServerMap(), MODE_REPAIR)
70        if self._history:
71            self._history.notify_mapupdate(u.get_status())
72        d = u.update()
73        d.addCallback(self._got_full_servermap, force)
74        return d
75
76    def _got_full_servermap(self, smap, force):
77        best_version = smap.best_recoverable_version()
78        if not best_version:
79            # the file is damaged beyond repair
80            rr = RepairResults(smap)
81            rr.set_successful(False)
82            return defer.succeed(rr)
83
84        if smap.unrecoverable_newer_versions():
85            if not force:
86                raise MustForceRepairError("There were unrecoverable newer "
87                                           "versions, so force=True must be "
88                                           "passed to the repair() operation")
89            # continuing on means that node.upload() will pick a seqnum that
90            # is higher than everything visible in the servermap, effectively
91            # discarding the unrecoverable versions.
92        if smap.needs_merge():
93            if not force:
94                raise MustForceRepairError("There were multiple recoverable "
95                                           "versions with identical seqnums, "
96                                           "so force=True must be passed to "
97                                           "the repair() operation")
98            # continuing on means that smap.best_recoverable_version() will
99            # pick the one with the highest roothash, and then node.upload()
100            # will replace all shares with its contents
101
102        # missing shares are handled during upload, which tries to find a
103        # home for every share
104
105        # old shares are handled during upload, which will replace any share
106        # that was present in the servermap
107
108        # bogus shares need to be managed here. We might notice a bogus share
109        # during mapupdate (whether done for a filecheck or just before a
110        # download) by virtue of it having an invalid signature. We might
111        # also notice a bad hash in the share during verify or download. In
112        # either case, the problem will be noted in the servermap, and the
113        # bad share (along with its checkstring) will be recorded in
114        # servermap.bad_shares . Publish knows that it should try and replace
115        # these.
116
117        # I chose to use the retrieve phase to ensure that the privkey is
118        # available, to avoid the extra roundtrip that would occur if we,
119        # say, added an smap.get_privkey() method.
120
121        if not self.node.get_writekey():
122            raise RepairRequiresWritecapError("Sorry, repair currently requires a writecap, to set the write-enabler properly.")
123
124        d = self.node.download_version(smap, best_version, fetch_privkey=True)
125        d.addCallback(lambda data:
126            MutableData(data))
127        d.addCallback(self.node.upload, smap)
128        d.addCallback(self.get_results, smap)
129        return d
130
131    def get_results(self, res, smap):
132        rr = RepairResults(smap)
133        rr.set_successful(True)
134        return rr
Note: See TracBrowser for help on using the repository browser.