Example #1
0
 def add_check(self, r, path):
     if not r:
         return # non-distributed object, i.e. LIT file
     r = ICheckResults(r)
     assert isinstance(path, (list, tuple))
     self.objects_checked += 1
     if r.is_healthy():
         self.objects_healthy += 1
     else:
         self.objects_unhealthy += 1
     if not r.is_recoverable():
         self.objects_unrecoverable += 1
     self.all_results[tuple(path)] = r
     self.all_results_by_storage_index[r.get_storage_index()] = r
     self.corrupt_shares.extend(r.get_data()["list-corrupt-shares"])
Example #2
0
 def __init__(self, node, check_results, storage_broker, history, monitor):
     self.node = node
     self.check_results = ICheckResults(check_results)
     assert check_results.get_storage_index() == node.get_storage_index()
     self._storage_broker = storage_broker
     self._history = history
     self._monitor = monitor
 def data_problems(self, ctx, data):
     all_objects = self.monitor.get_status().get_all_results()
     for path in sorted(all_objects.keys()):
         cr = all_objects[path]
         assert ICheckResults.providedBy(cr)
         if not cr.is_healthy():
             yield path, cr
Example #4
0
 def data_problems(self, ctx, data):
     all_objects = self.monitor.get_status().get_all_results()
     for path in sorted(all_objects.keys()):
         cr = all_objects[path]
         assert ICheckResults.providedBy(cr)
         if not cr.is_healthy():
             yield path, cr
Example #5
0
 def __init__(self, client, results):
     """
     :param allmydata.interfaces.IStatsProducer client: stats provider.
     :param allmydata.interfaces.ICheckResults results: results of check/vefify operation.
     """
     super(CheckResultsRenderer, self).__init__()
     self._client = client
     self._results = ICheckResults(results)
 def add_check(self, r, path):
     if not r:
         return  # non-distributed object, i.e. LIT file
     r = ICheckResults(r)
     assert isinstance(path, (list, tuple))
     self.objects_checked += 1
     if r.is_healthy():
         self.objects_healthy += 1
     else:
         self.objects_unhealthy += 1
     if not r.is_recoverable():
         self.objects_unrecoverable += 1
     self.all_results[tuple(path)] = r
     self.all_results_by_storage_index[r.get_storage_index()] = r
     self.corrupt_shares.extend(r.get_corrupt_shares())
Example #7
0
    def problems(self, req, tag):
        all_objects = self.monitor.get_status().get_all_results()
        problems = []

        for path in sorted(all_objects.keys()):
            cr = all_objects[path]
            assert ICheckResults.providedBy(cr)
            if not cr.is_healthy():
                summary_text = ""
                summary = cr.get_summary()
                if summary:
                    summary_text = ": " + summary
                summary_text += " [SI: %s]" % cr.get_storage_index_string()
                problems.append({
                    # Not sure self._join_pathstring(path) is the
                    # right thing to use here.
                    "problem":
                    self._join_pathstring(path) + self._html(summary_text),
                })

        return SlotsSequenceElement(tag, problems)
Example #8
0
class Repairer:
    def __init__(self, node, check_results):
        self.node = node
        self.check_results = ICheckResults(check_results)
        assert check_results.storage_index == self.node.get_storage_index()

    def start(self, force=False):
        # download, then re-publish. If a server had a bad share, try to
        # replace it with a good one of the same shnum.

        # The normal repair operation should not be used to replace
        # application-specific merging of alternate versions: i.e if there
        # are multiple highest seqnums with different roothashes. In this
        # case, the application must use node.upload() (referencing the
        # servermap that indicates the multiple-heads condition), or
        # node.overwrite(). The repair() operation will refuse to run in
        # these conditions unless a force=True argument is provided. If
        # force=True is used, then the highest root hash will be reinforced.

        # Likewise, the presence of an unrecoverable latest version is an
        # unusual event, and should ideally be handled by retrying a couple
        # times (spaced out over hours or days) and hoping that new shares
        # will become available. If repair(force=True) is called, data will
        # be lost: a new seqnum will be generated with the same contents as
        # the most recent recoverable version, skipping over the lost
        # version. repair(force=False) will refuse to run in a situation like
        # this.

        # Repair is designed to fix the following injuries:
        #  missing shares: add new ones to get at least N distinct ones
        #  old shares: replace old shares with the latest version
        #  bogus shares (bad sigs): replace the bad one with a good one

        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 "
                                           "versions, so force=True must be "
                                           "passed to the repair() operation")
            # continuing on means that node.upload() will pick a seqnum that
            # is higher than everything visible in the servermap, effectively
            # discarding the unrecoverable versions.
        if smap.needs_merge():
            if not force:
                raise MustForceRepairError("There were multiple recoverable "
                                           "versions with identical seqnums, "
                                           "so force=True must be passed to "
                                           "the repair() operation")
            # continuing on means that smap.best_recoverable_version() will
            # pick the one with the highest roothash, and then node.upload()
            # will replace all shares with its contents

        # missing shares are handled during upload, which tries to find a
        # home for every share

        # old shares are handled during upload, which will replace any share
        # that was present in the servermap

        # bogus shares need to be managed here. We might notice a bogus share
        # during mapupdate (whether done for a filecheck or just before a
        # download) by virtue of it having an invalid signature. We might
        # also notice a bad hash in the share during verify or download. In
        # either case, the problem will be noted in the servermap, and the
        # bad share (along with its checkstring) will be recorded in
        # servermap.bad_shares . Publish knows that it should try and replace
        # these.

        # I chose to use the retrieve phase to ensure that the privkey is
        # available, to avoid the extra roundtrip that would occur if we,
        # say, added an smap.get_privkey() method.

        if not self.node.get_writekey():
            raise RepairRequiresWritecapError("Sorry, repair currently requires a writecap, to set the write-enabler properly.")

        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):
        rr = RepairResults(smap)
        rr.set_successful(True)
        return rr
Example #9
0
    def _render_results(self, req, cr):
        assert ICheckResults(cr)
        c = self._client
        sb = c.get_storage_broker()
        r = []

        def add(name, value):
            r.append(tags.li(name + ": ", value))

        add("Report", tags.pre("\n".join(self._html(cr.get_report()))))

        add(
            "Share Counts", "need %d-of-%d, have %d" %
            (cr.get_encoding_needed(), cr.get_encoding_expected(),
             cr.get_share_counter_good()))
        add("Happiness Level", str(cr.get_happiness()))
        add("Hosts with good shares", str(cr.get_host_counter_good_shares()))

        if cr.get_corrupt_shares():
            badsharemap = []
            for (s, si, shnum) in cr.get_corrupt_shares():
                d = tags.tr(
                    tags.td("sh#%d" % shnum),
                    tags.td(tags.div(s.get_nickname(), class_="nickname"),
                            tags.div(tags.tt(s.get_name()), class_="nodeid")),
                )
                badsharemap.append(d)
            add(
                "Corrupt shares",
                tags.table(
                    tags.tr(
                        tags.th("Share ID"),
                        tags.th((tags.div("Nickname"),
                                 tags.div("Node ID", class_="nodeid")),
                                class_="nickname-and-peerid")), badsharemap))
        else:
            add("Corrupt shares", "none")

        add("Wrong Shares", str(cr.get_share_counter_wrong()))

        sharemap_data = []
        shares_on_server = dictutil.DictOfSets()

        # FIXME: The two tables below contain nickname-and-nodeid
        # table column markup which is duplicated with each other,
        # introducer.xhtml, and deep-check-results.xhtml. All of these
        # (and any other presentations of nickname-and-nodeid) should be combined.

        for shareid in sorted(cr.get_sharemap().keys()):
            servers = sorted(cr.get_sharemap()[shareid],
                             key=lambda s: s.get_longname())
            for i, s in enumerate(servers):
                shares_on_server.add(s, shareid)
                shareid_s = ""
                if i == 0:
                    if isinstance(shareid, bytes):
                        shareid_s = str(shareid, "utf-8")
                    else:
                        shareid_s = str(shareid)
                d = tags.tr(
                    tags.td(shareid_s),
                    tags.td(tags.div(s.get_nickname(), class_="nickname"),
                            tags.div(tags.tt(s.get_name()), class_="nodeid")))
                sharemap_data.append(d)

        add(
            "Good Shares (sorted in share order)",
            tags.table(
                tags.tr(
                    tags.th("Share ID"),
                    tags.th(tags.div("Nickname"),
                            tags.div("Node ID", class_="nodeid"),
                            class_="nickname-and-peerid")), sharemap_data))

        add("Recoverable Versions", str(cr.get_version_counter_recoverable()))
        add("Unrecoverable Versions",
            str(cr.get_version_counter_unrecoverable()))

        # this table is sorted by permuted order
        permuted_servers = [
            s for s in sb.get_servers_for_psi(cr.get_storage_index())
        ]

        num_shares_left = sum(
            [len(shareids) for shareids in shares_on_server.values()])
        servermap = []
        for s in permuted_servers:
            shareids = list(shares_on_server.get(s, []))
            shareids.reverse()
            shareids_s = [
                tags.tt(str(shareid), " ") for shareid in sorted(shareids)
            ]

            d = tags.tr(
                tags.td(tags.div(s.get_nickname(), class_="nickname"),
                        tags.div(tags.tt(s.get_name()), class_="nodeid")),
                tags.td(shareids_s),
            )
            servermap.append(d)
            num_shares_left -= len(shareids)
            if not num_shares_left:
                break

        add(
            "Share Balancing (servers in permuted order)",
            tags.table(
                tags.tr(
                    tags.th(tags.div("Nickname"),
                            tags.div("Node ID", class_="nodeid"),
                            class_="nickname-and-peerid"),
                    tags.th("Share IDs")), servermap))

        return tags.ul(r)
Example #10
0
 def repair(self, check_results, force=False, monitor=None):
     assert ICheckResults(check_results)
     r = Repairer(self, check_results, self._storage_broker,
                  self._history, monitor)
     d = r.start(force)
     return d
Example #11
0
 def __init__(self, node, check_results):
     self.node = node
     self.check_results = ICheckResults(check_results)
     assert check_results.storage_index == self.node.get_storage_index()
Example #12
0
    def _render_results(self, ctx, cr):
        assert ICheckResults(cr)
        c = self.client
        sb = c.get_storage_broker()
        data = cr.get_data()
        r = []

        def add(name, value):
            r.append(T.li[name + ": ", value])

        add("Report", T.pre["\n".join(self._html(cr.get_report()))])
        add(
            "Share Counts", "need %d-of-%d, have %d" %
            (data["count-shares-needed"], data["count-shares-expected"],
             data["count-shares-good"]))
        add("Hosts with good shares", data["count-good-share-hosts"])

        if data["list-corrupt-shares"]:
            badsharemap = []
            for (serverid, si, shnum) in data["list-corrupt-shares"]:
                nickname = sb.get_nickname_for_serverid(serverid)
                badsharemap.append(T.tr[T.td["sh#%d" % shnum], T.td[
                    T.div(class_="nickname")[nickname],
                    T.div(class_="nodeid")[T.tt[base32.b2a(serverid)]]], ])
            add(
                "Corrupt shares",
                T.table()[T.tr[T.th["Share ID"],
                               T.th(class_="nickname-and-peerid")[
                                   T.div["Nickname"],
                                   T.div(class_="nodeid")["Node ID"]]],
                          badsharemap])
        else:
            add("Corrupt shares", "none")

        add("Wrong Shares", data["count-wrong-shares"])

        sharemap = []
        servers = {}

        # FIXME: The two tables below contain nickname-and-nodeid table column markup which is duplicated with each other, introducer.xhtml, and deep-check-results.xhtml. All of these (and any other presentations of nickname-and-nodeid) should be combined.

        for shareid in sorted(data["sharemap"].keys()):
            serverids = data["sharemap"][shareid]
            for i, serverid in enumerate(serverids):
                if serverid not in servers:
                    servers[serverid] = []
                servers[serverid].append(shareid)
                shareid_s = ""
                if i == 0:
                    shareid_s = shareid
                nickname = sb.get_nickname_for_serverid(serverid)
                sharemap.append(T.tr[T.td[shareid_s], T.td[
                    T.div(class_="nickname")[nickname],
                    T.div(class_="nodeid")[T.tt[base32.b2a(serverid)]]]])
        add(
            "Good Shares (sorted in share order)",
            T.table()[T.tr[T.th["Share ID"],
                           T.th(class_="nickname-and-peerid")[
                               T.div["Nickname"],
                               T.div(class_="nodeid")["Node ID"]]], sharemap])

        add("Recoverable Versions", data["count-recoverable-versions"])
        add("Unrecoverable Versions", data["count-unrecoverable-versions"])

        # this table is sorted by permuted order
        sb = c.get_storage_broker()
        permuted_servers = [
            s for s in sb.get_servers_for_psi(cr.get_storage_index())
        ]

        num_shares_left = sum([len(shares) for shares in servers.values()])
        servermap = []
        for s in permuted_servers:
            nickname = s.get_nickname()
            shareids = servers.get(s.get_serverid(), [])
            shareids.reverse()
            shareids_s = [T.tt[shareid, " "] for shareid in sorted(shareids)]
            servermap.append(
                T.tr[T.td[T.div(class_="nickname")[nickname],
                          T.div(class_="nodeid")[T.tt[s.get_name()]]],
                     T.td[shareids_s], ])
            num_shares_left -= len(shareids)
            if not num_shares_left:
                break
        add(
            "Share Balancing (servers in permuted order)",
            T.table()[T.tr[T.th(
                class_="nickname-and-peerid")[T.div["Nickname"],
                                              T.div(
                                                  class_="nodeid")["Node ID"]],
                           T.th["Share IDs"]], servermap])

        return T.ul[r]
Example #13
0
class Repairer:
    def __init__(self, node, check_results):
        self.node = node
        self.check_results = ICheckResults(check_results)
        assert check_results.storage_index == self.node.get_storage_index()

    def start(self, force=False):
        # download, then re-publish. If a server had a bad share, try to
        # replace it with a good one of the same shnum.

        # The normal repair operation should not be used to replace
        # application-specific merging of alternate versions: i.e if there
        # are multiple highest seqnums with different roothashes. In this
        # case, the application must use node.upload() (referencing the
        # servermap that indicates the multiple-heads condition), or
        # node.overwrite(). The repair() operation will refuse to run in
        # these conditions unless a force=True argument is provided. If
        # force=True is used, then the highest root hash will be reinforced.

        # Likewise, the presence of an unrecoverable latest version is an
        # unusual event, and should ideally be handled by retrying a couple
        # times (spaced out over hours or days) and hoping that new shares
        # will become available. If repair(force=True) is called, data will
        # be lost: a new seqnum will be generated with the same contents as
        # the most recent recoverable version, skipping over the lost
        # version. repair(force=False) will refuse to run in a situation like
        # this.

        # Repair is designed to fix the following injuries:
        #  missing shares: add new ones to get at least N distinct ones
        #  old shares: replace old shares with the latest version
        #  bogus shares (bad sigs): replace the bad one with a good one

        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 "
                                           "versions, so force=True must be "
                                           "passed to the repair() operation")
            # continuing on means that node.upload() will pick a seqnum that
            # is higher than everything visible in the servermap, effectively
            # discarding the unrecoverable versions.
        if smap.needs_merge():
            if not force:
                raise MustForceRepairError("There were multiple recoverable "
                                           "versions with identical seqnums, "
                                           "so force=True must be passed to "
                                           "the repair() operation")
            # continuing on means that smap.best_recoverable_version() will
            # pick the one with the highest roothash, and then node.upload()
            # will replace all shares with its contents

        # missing shares are handled during upload, which tries to find a
        # home for every share

        # old shares are handled during upload, which will replace any share
        # that was present in the servermap

        # bogus shares need to be managed here. We might notice a bogus share
        # during mapupdate (whether done for a filecheck or just before a
        # download) by virtue of it having an invalid signature. We might
        # also notice a bad hash in the share during verify or download. In
        # either case, the problem will be noted in the servermap, and the
        # bad share (along with its checkstring) will be recorded in
        # servermap.bad_shares . Publish knows that it should try and replace
        # these.

        # I chose to use the retrieve phase to ensure that the privkey is
        # available, to avoid the extra roundtrip that would occur if we,
        # say, added an smap.get_privkey() method.

        if not self.node.get_writekey():
            raise RepairRequiresWritecapError(
                "Sorry, repair currently requires a writecap, to set the write-enabler properly."
            )

        d = self.node.download_version(smap, best_version, fetch_privkey=True)
        d.addCallback(lambda data: MutableData(data))
        d.addCallback(self.node.upload, smap)
        d.addCallback(self.get_results, smap)
        return d

    def get_results(self, res, smap):
        rr = RepairResults(smap)
        rr.set_successful(True)
        return rr
Example #14
0
    def _render_results(self, ctx, cr):
        assert ICheckResults(cr)
        c = self.client
        sb = c.get_storage_broker()
        r = []

        def add(name, value):
            r.append(T.li[name + ": ", value])

        add("Report", T.pre["\n".join(self._html(cr.get_report()))])
        add(
            "Share Counts", "need %d-of-%d, have %d" %
            (cr.get_encoding_needed(), cr.get_encoding_expected(),
             cr.get_share_counter_good()))
        add("Hosts with good shares", cr.get_host_counter_good_shares())

        if cr.get_corrupt_shares():
            badsharemap = []
            for (s, si, shnum) in cr.get_corrupt_shares():
                d = T.tr[T.td["sh#%d" % shnum],
                         T.td[T.div(class_="nickname")[s.get_nickname()],
                              T.div(class_="nodeid")[T.tt[s.get_name()]]], ]
                badsharemap.append(d)
            add(
                "Corrupt shares",
                T.table()[T.tr[T.th["Share ID"],
                               T.th(class_="nickname-and-peerid")[
                                   T.div["Nickname"],
                                   T.div(class_="nodeid")["Node ID"]]],
                          badsharemap])
        else:
            add("Corrupt shares", "none")

        add("Wrong Shares", cr.get_share_counter_wrong())

        sharemap_data = []
        shares_on_server = dictutil.DictOfSets()

        # FIXME: The two tables below contain nickname-and-nodeid table column markup which is duplicated with each other, introducer.xhtml, and deep-check-results.xhtml. All of these (and any other presentations of nickname-and-nodeid) should be combined.

        for shareid in sorted(cr.get_sharemap().keys()):
            servers = sorted(cr.get_sharemap()[shareid],
                             key=lambda s: s.get_longname())
            for i, s in enumerate(servers):
                shares_on_server.add(s, shareid)
                shareid_s = ""
                if i == 0:
                    shareid_s = shareid
                d = T.tr[T.td[shareid_s],
                         T.td[T.div(class_="nickname")[s.get_nickname()],
                              T.div(class_="nodeid")[T.tt[s.get_name()]]]]
                sharemap_data.append(d)
        add(
            "Good Shares (sorted in share order)",
            T.table()[T.tr[T.th["Share ID"],
                           T.th(class_="nickname-and-peerid")[
                               T.div["Nickname"],
                               T.div(class_="nodeid")["Node ID"]]],
                      sharemap_data])

        add("Recoverable Versions", cr.get_version_counter_recoverable())
        add("Unrecoverable Versions", cr.get_version_counter_unrecoverable())

        # this table is sorted by permuted order
        permuted_servers = [
            s for s in sb.get_servers_for_psi(cr.get_storage_index())
        ]

        num_shares_left = sum(
            [len(shareids) for shareids in shares_on_server.values()])
        servermap = []
        for s in permuted_servers:
            shareids = list(shares_on_server.get(s, []))
            shareids.reverse()
            shareids_s = [T.tt[shareid, " "] for shareid in sorted(shareids)]
            d = T.tr[T.td[T.div(class_="nickname")[s.get_nickname()],
                          T.div(class_="nodeid")[T.tt[s.get_name()]]],
                     T.td[shareids_s], ]
            servermap.append(d)
            num_shares_left -= len(shareids)
            if not num_shares_left:
                break
        add(
            "Share Balancing (servers in permuted order)",
            T.table()[T.tr[T.th(
                class_="nickname-and-peerid")[T.div["Nickname"],
                                              T.div(
                                                  class_="nodeid")["Node ID"]],
                           T.th["Share IDs"]], servermap])

        return T.ul[r]
Example #15
0
 def __init__(self, node, check_results):
     self.node = node
     self.check_results = ICheckResults(check_results)
     assert check_results.storage_index == self.node.get_storage_index()
Example #16
0
 def __init__(self, client, results):
     self.client = client
     self.r = ICheckResults(results)
     rend.Page.__init__(self, results)
Example #17
0
 def repair(self, check_results, force=False):
     assert ICheckResults(check_results)
     r = Repairer(self, check_results)
     d = r.start(force)
     return d