From 55170dcd2b2191a31b9a5ff101a1e180128ffd1f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 2 Jun 2020 13:49:34 -0400 Subject: [PATCH 01/94] Rearrange imports --- src/allmydata/web/check_results.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 7a9badad4..8325a7e8c 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -1,12 +1,30 @@ import time import json -from nevow import rend, inevow, tags as T -from twisted.web import http, html -from allmydata.web.common import getxmlfile, get_arg, get_root, WebError +from nevow import ( + rend, + inevow, + tags as T, +) +from twisted.web import ( + http, + html, +) +from allmydata.web.common import ( + getxmlfile, + get_arg, + get_root, + WebError, +) from allmydata.web.operations import ReloadMixin -from allmydata.interfaces import ICheckAndRepairResults, ICheckResults -from allmydata.util import base32, dictutil +from allmydata.interfaces import ( + ICheckAndRepairResults, + ICheckResults, +) +from allmydata.util import ( + base32, + dictutil, +) def json_check_counts(r): From 950d111793bbc5a48a804d08eb2b145617bfa360 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 2 Jun 2020 13:57:38 -0400 Subject: [PATCH 02/94] Use POST target paths without pre-existing query arguments `action="."` on `/uri/URI:DIR2:$DIRCAP` will render as `/uri/`, which is not the result we want. --- src/allmydata/web/info.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/allmydata/web/info.py b/src/allmydata/web/info.py index fa62ea23d..7eccfea9e 100644 --- a/src/allmydata/web/info.py +++ b/src/allmydata/web/info.py @@ -261,7 +261,7 @@ class MoreInfoElement(Element): @renderer def deep_check_form(self, req, tag): ophandle = base32.b2a(os.urandom(16)) - deep_check = T.form(action=".", method="post", + deep_check = T.form(action=req.path, method="post", enctype="multipart/form-data")( T.fieldset( T.input(type="hidden", name="t", value="start-deep-check"), @@ -287,7 +287,7 @@ class MoreInfoElement(Element): @renderer def deep_size_form(self, req, tag): ophandle = base32.b2a(os.urandom(16)) - deep_size = T.form(action=".", method="post", + deep_size = T.form(action=req.path, method="post", enctype="multipart/form-data")( T.fieldset( T.input(type="hidden", name="t", value="start-deep-size"), @@ -300,7 +300,7 @@ class MoreInfoElement(Element): @renderer def deep_stats_form(self, req, tag): ophandle = base32.b2a(os.urandom(16)) - deep_stats = T.form(action=".", method="post", + deep_stats = T.form(action=req.path, method="post", enctype="multipart/form-data")( T.fieldset( T.input(type="hidden", name="t", value="start-deep-stats"), @@ -313,7 +313,7 @@ class MoreInfoElement(Element): @renderer def manifest_form(self, req, tag): ophandle = base32.b2a(os.urandom(16)) - manifest = T.form(action=".", method="post", + manifest = T.form(action=req.path, method="post", enctype="multipart/form-data")( T.fieldset( T.input(type="hidden", name="t", value="start-manifest"), From ebf23a4f0e76c0d61276686a32fe31b838857738 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 2 Jun 2020 16:44:29 -0400 Subject: [PATCH 03/94] Use twisted web tags in literal check results template --- src/allmydata/web/literal-check-results.xhtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/literal-check-results.xhtml b/src/allmydata/web/literal-check-results.xhtml index 95b4b0cf7..35723417a 100644 --- a/src/allmydata/web/literal-check-results.xhtml +++ b/src/allmydata/web/literal-check-results.xhtml @@ -1,4 +1,4 @@ - + Tahoe-LAFS - Check Results @@ -11,7 +11,7 @@
Literal files are always healthy: their data is contained in the URI
-
+
From 3901559e346ca42f98078355de27d48b05c09de5 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 2 Jun 2020 16:51:37 -0400 Subject: [PATCH 04/94] Make LiteralCheckResultsRenderer a MultiFormatResource --- src/allmydata/web/check_results.py | 40 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 8325a7e8c..2a250efe1 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -10,11 +10,20 @@ from twisted.web import ( http, html, ) +from twisted.python.filepath import FilePath +from twisted.web.template import ( + Element, + XMLFile, + renderer, + renderElement, + tags, +) from allmydata.web.common import ( getxmlfile, get_arg, get_root, WebError, + MultiFormatResource, ) from allmydata.web.operations import ReloadMixin from allmydata.interfaces import ( @@ -199,30 +208,37 @@ class ResultsBase(object): target = target + "?output=%s" % output return T.a(href=target)[si_s] -class LiteralCheckResultsRenderer(rend.Page, ResultsBase): - docFactory = getxmlfile("literal-check-results.xhtml") + +class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase): def __init__(self, client): + super(LiteralCheckResultsRenderer, self).__init__() self.client = client - rend.Page.__init__(self, client) - def renderHTTP(self, ctx): - if self.want_json(ctx): - return self.json(ctx) - return rend.Page.renderHTTP(self, ctx) + def render_HTML(self, req): + return renderElement(req, LiteralCheckResultsElement()) - def json(self, ctx): - inevow.IRequest(ctx).setHeader("content-type", "text/plain") + def render_JSON(self, req): + req.setHeader("content-type", "text/plain") data = json_check_results(None) return json.dumps(data, indent=1) + "\n" - def render_return(self, ctx, data): - req = inevow.IRequest(ctx) + +class LiteralCheckResultsElement(Element): + + loader = XMLFile(FilePath(__file__).sibling("literal-check-results.xhtml")) + + def __init__(self): + super(LiteralCheckResultsElement, self).__init__() + + @renderer + def return(self, req, tag): return_to = get_arg(req, "return_to", None) if return_to: - return T.div[T.a(href=return_to)["Return to file."]] + return tags.div(tags.a("Return to file.", href=return_to)) return "" + class CheckerBase(object): def renderHTTP(self, ctx): From c15001269ffad7a3e12e1893ebbc5ad06985fa1a Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 2 Jun 2020 16:52:09 -0400 Subject: [PATCH 05/94] Avoid using a Python keyword as a renderer name --- src/allmydata/web/check_results.py | 2 +- src/allmydata/web/literal-check-results.xhtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 2a250efe1..d3c868298 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -232,7 +232,7 @@ class LiteralCheckResultsElement(Element): super(LiteralCheckResultsElement, self).__init__() @renderer - def return(self, req, tag): + def return_to(self, req, tag): return_to = get_arg(req, "return_to", None) if return_to: return tags.div(tags.a("Return to file.", href=return_to)) diff --git a/src/allmydata/web/literal-check-results.xhtml b/src/allmydata/web/literal-check-results.xhtml index 35723417a..f82ddf8d3 100644 --- a/src/allmydata/web/literal-check-results.xhtml +++ b/src/allmydata/web/literal-check-results.xhtml @@ -11,7 +11,7 @@
Literal files are always healthy: their data is contained in the URI
-
+
From cbc414725c169e40eda4612ebf08d1fdb9d20472 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 3 Jun 2020 14:06:55 -0400 Subject: [PATCH 06/94] Use "output" as format argument Nearly everywhere else the query argument for output format is "t"; but here "t" is used for operations, and "output" is used for output format. The test suite will fail without this. --- src/allmydata/web/check_results.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index d3c868298..bfe785d41 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -211,6 +211,8 @@ class ResultsBase(object): class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase): + formatArgument = "output" + def __init__(self, client): super(LiteralCheckResultsRenderer, self).__init__() self.client = client From 9d104f5893343e208f1c0cb57969161f3842bf06 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 3 Jun 2020 17:57:27 -0400 Subject: [PATCH 07/94] Use twisted tags in check results template file --- src/allmydata/web/check-results.xhtml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/allmydata/web/check-results.xhtml b/src/allmydata/web/check-results.xhtml index 5367552fc..2c9f5c283 100644 --- a/src/allmydata/web/check-results.xhtml +++ b/src/allmydata/web/check-results.xhtml @@ -1,4 +1,4 @@ - + Tahoe-LAFS - Check Results @@ -7,17 +7,17 @@ -

File Check Results for SI=

+

File Check Results for SI=

- +
-
+
-
+
-
+
From fea4e516f3485465024e16b8a552b7d32e0e6e3b Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 3 Jun 2020 17:56:22 -0400 Subject: [PATCH 08/94] Make CheckResultsRenderer a MultiFormatResource --- src/allmydata/web/check_results.py | 77 +++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index bfe785d41..94b55d52d 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -243,49 +243,77 @@ class LiteralCheckResultsElement(Element): class CheckerBase(object): - def renderHTTP(self, ctx): - if self.want_json(ctx): - return self.json(ctx) - return rend.Page.renderHTTP(self, ctx) + # def renderHTTP(self, ctx): + # if self.want_json(ctx): + # return self.json(ctx) + # return rend.Page.renderHTTP(self, ctx) - def render_storage_index(self, ctx, data): + @renderer + def storage_index(self, req, tag): return self.r.get_storage_index_string() - def render_return(self, ctx, data): - req = inevow.IRequest(ctx) + @renderer + def return_to(self, req, tag): + # req = inevow.IRequest(ctx) return_to = get_arg(req, "return_to", None) if return_to: - return T.div[T.a(href=return_to)["Return to file/directory."]] + return tags.div(tags.a("Return to file/directory.", href=return_to)) return "" -class CheckResultsRenderer(CheckerBase, rend.Page, ResultsBase): - docFactory = getxmlfile("check-results.xhtml") + +class CheckResultsRenderer(MultiFormatResource, ResultsBase): + + formatArgument = "output" def __init__(self, client, results): + """ + TODO: update these with the correct values. + :param allmydata.test.no_network._NoNetworkClient client: + :param allmydata.check_results.CheckResults results: + """ + super(CheckResultsRenderer, self).__init__() self.client = client self.r = ICheckResults(results) - rend.Page.__init__(self, results) - def json(self, ctx): - inevow.IRequest(ctx).setHeader("content-type", "text/plain") + def render_HTML(self, req): + return renderElement(req, CheckResultsRendererElement(self.r)) + + def render_JSON(self, req): + req.setHeader("content-type", "text/plain") data = json_check_results(self.r) return json.dumps(data, indent=1) + "\n" - def render_summary(self, ctx, data): + +class CheckResultsRendererElement(Element, CheckerBase): + + loader = XMLFile(FilePath(__file__).sibling("check-results.xhtml")) + + def __init__(self, r): + super(CheckResultsRendererElement, self).__init__() + # TODO: use a better name + self.r = r + + @renderer + def summary(self, req, tag): results = [] - if data.is_healthy(): + # XXX: how did `data` get populated? + if self.r.is_healthy(): results.append("Healthy") - elif data.is_recoverable(): + elif self.r.is_recoverable(): results.append("Not Healthy!") else: results.append("Not Recoverable!") results.append(" : ") - results.append(self._html(data.get_summary())) - return ctx.tag[results] + # TODO: what is self._html? + # results.append(self._html(self.r.get_summary())) + results.append(self.r.get_summary()) + return tag(results) - def render_repair(self, ctx, data): - if data.is_healthy(): + @renderer + def repair(self, req, tag): + if self.r.is_healthy(): return "" + #repair = T.form(action=".", method="post", # enctype="multipart/form-data")[ # T.fieldset[ @@ -294,12 +322,15 @@ class CheckResultsRenderer(CheckerBase, rend.Page, ResultsBase): # T.input(type="submit", value="Repair"), # ]] #return ctx.tag[repair] + return "" # repair button disabled until we make it work correctly, # see #622 for details - def render_results(self, ctx, data): - cr = self._render_results(ctx, data) - return ctx.tag[cr] + @renderer + def results(self, req, tag): + cr = self._render_results(req, self.r) + return tag(cr) + class CheckAndRepairResultsRenderer(CheckerBase, rend.Page, ResultsBase): docFactory = getxmlfile("check-and-repair-results.xhtml") From 8e37bb1ebbd0048791f72f5d04275bc289a6b3d7 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 3 Jun 2020 21:42:20 -0400 Subject: [PATCH 09/94] Make ResultsBase a base class of CheckResultsRendererElement --- src/allmydata/web/check_results.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 94b55d52d..c6a3d25ea 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -261,7 +261,7 @@ class CheckerBase(object): return "" -class CheckResultsRenderer(MultiFormatResource, ResultsBase): +class CheckResultsRenderer(MultiFormatResource): formatArgument = "output" @@ -276,7 +276,7 @@ class CheckResultsRenderer(MultiFormatResource, ResultsBase): self.r = ICheckResults(results) def render_HTML(self, req): - return renderElement(req, CheckResultsRendererElement(self.r)) + return renderElement(req, CheckResultsRendererElement(self.client, self.r)) def render_JSON(self, req): req.setHeader("content-type", "text/plain") @@ -284,12 +284,13 @@ class CheckResultsRenderer(MultiFormatResource, ResultsBase): return json.dumps(data, indent=1) + "\n" -class CheckResultsRendererElement(Element, CheckerBase): +class CheckResultsRendererElement(Element, CheckerBase, ResultsBase): loader = XMLFile(FilePath(__file__).sibling("check-results.xhtml")) - def __init__(self, r): + def __init__(self, client, r): super(CheckResultsRendererElement, self).__init__() + self.client = client # TODO: use a better name self.r = r From d2540b89d370f1144e088734da593967fc3fb970 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 3 Jun 2020 21:43:15 -0400 Subject: [PATCH 10/94] Use twisted web tags in ResultsBase --- src/allmydata/web/check_results.py | 71 ++++++++++++++++-------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index c6a3d25ea..0a56a55e8 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -107,32 +107,35 @@ class ResultsBase(object): sb = c.get_storage_broker() r = [] def add(name, value): - r.append(T.li[name + ": ", value]) + r.append(tags.li(name + ": ", value)) + + add("Report", tags.pre("\n".join(self._html(cr.get_report())))) - 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("Happiness Level", cr.get_happiness()) - add("Hosts with good shares", cr.get_host_counter_good_shares()) + add("Happiness Level", str(cr.get_happiness())) + add("Hosts with good shares", str(cr.get_host_counter_good_shares())) + + rrr = cr.get_corrupt_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()]]], - ] + 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", T.table()[ - T.tr[T.th["Share ID"], - T.th(class_="nickname-and-peerid")[T.div["Nickname"], T.div(class_="nodeid")["Node ID"]]], - badsharemap]) + 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", cr.get_share_counter_wrong()) + add("Wrong Shares", str(cr.get_share_counter_wrong())) sharemap_data = [] shares_on_server = dictutil.DictOfSets() @@ -147,18 +150,19 @@ class ResultsBase(object): 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()]]] - ] + 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)", - 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]) + 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", cr.get_version_counter_recoverable()) - add("Unrecoverable Versions", cr.get_version_counter_unrecoverable()) + 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 @@ -171,20 +175,23 @@ class ResultsBase(object): 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], - ] + shareids_s = [tags.tt(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)", - 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] + 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) def _html(self, s): if isinstance(s, (str, unicode)): @@ -206,7 +213,7 @@ class ResultsBase(object): output = get_arg(ctx, "output") if output: target = target + "?output=%s" % output - return T.a(href=target)[si_s] + return tags.a(si_s, href=target) class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase): From 6f1601aeebcd247f643ff26e39de3ea696129b4f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 16:18:04 -0400 Subject: [PATCH 11/94] Stringify numbers before wrapping them up in tags --- src/allmydata/web/check_results.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 0a56a55e8..d798fac4f 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -149,7 +149,7 @@ class ResultsBase(object): shares_on_server.add(s, shareid) shareid_s = "" if i == 0: - shareid_s = shareid + 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"))) @@ -175,7 +175,7 @@ class ResultsBase(object): for s in permuted_servers: shareids = list(shares_on_server.get(s, [])) shareids.reverse() - shareids_s = [tags.tt(shareid, " ") for shareid in sorted(shareids)] + 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")), From 80e9f48551df94f7e69f81a7e175181c6fbf5f2d Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 16:18:27 -0400 Subject: [PATCH 12/94] Add line breaks in a comment --- src/allmydata/web/check_results.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index d798fac4f..a6f65d52b 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -140,7 +140,10 @@ class ResultsBase(object): 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. + # 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], From d9005fcccc4cb4ab290d241bb8ef3d1481258a13 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 16:39:13 -0400 Subject: [PATCH 13/94] Use twisted tags in check and repair results template --- src/allmydata/web/check-and-repair-results.xhtml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/allmydata/web/check-and-repair-results.xhtml b/src/allmydata/web/check-and-repair-results.xhtml index 34eb31787..d2c9c3a47 100644 --- a/src/allmydata/web/check-and-repair-results.xhtml +++ b/src/allmydata/web/check-and-repair-results.xhtml @@ -1,4 +1,4 @@ - + Tahoe-LAFS - Check Results @@ -7,17 +7,17 @@ -

File Check-And-Repair Results for SI=

+

File Check-And-Repair Results for SI=

-
+
-
+
-
+
-
+
-
+
From 0eea108123a33acc92ac95d92f2a25b93795cb01 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 16:41:30 -0400 Subject: [PATCH 14/94] Make CheckAndRepairResultsRenderer a MultiFormatResource --- src/allmydata/web/check_results.py | 62 ++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index a6f65d52b..3f272f991 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -343,23 +343,40 @@ class CheckResultsRendererElement(Element, CheckerBase, ResultsBase): return tag(cr) -class CheckAndRepairResultsRenderer(CheckerBase, rend.Page, ResultsBase): - docFactory = getxmlfile("check-and-repair-results.xhtml") +class CheckAndRepairResultsRenderer(MultiFormatResource): + + formatArgument = "output" def __init__(self, client, results): + # TODO: document params + super(CheckAndRepairResultsRenderer, self).__init__() self.client = client + # TODO: use a better name self.r = None if results: self.r = ICheckAndRepairResults(results) - rend.Page.__init__(self, results) - def json(self, ctx): - inevow.IRequest(ctx).setHeader("content-type", "text/plain") + def render_HTML(self, req): + elem = CheckAndRepairResultsRendererElement(self.r) + return renderElement(req, elem) + + def render_JSON(self, req): + req.setHeader("content-type", "text/plain") data = json_check_and_repair_results(self.r) return json.dumps(data, indent=1) + "\n" - def render_summary(self, ctx, data): - cr = data.get_post_repair_results() + +class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): + + loader = XMLFile(FilePath(__file__).sibling("check-and-repair-results.xhtml")) + + def __init__(self, r): + super(CheckAndRepairResultsRendererElement, self).__init__() + self.r = r + + @renderer + def summary(self, req, tag): + cr = self.r.get_post_repair_results() results = [] if cr.is_healthy(): results.append("Healthy") @@ -369,24 +386,27 @@ class CheckAndRepairResultsRenderer(CheckerBase, rend.Page, ResultsBase): results.append("Not Recoverable!") results.append(" : ") results.append(self._html(cr.get_summary())) - return ctx.tag[results] + return tag(results) - def render_repair_results(self, ctx, data): - if data.get_repair_attempted(): - if data.get_repair_successful(): - return ctx.tag["Repair successful"] + @renderer + def repair_results(self, req, tag): + if self.r.get_repair_attempted(): + if self.r.get_repair_successful(): + return tag("Repair successful") else: - return ctx.tag["Repair unsuccessful"] - return ctx.tag["No repair necessary"] + return tag("Repair unsuccessful") + return tag("No repair necessary") - def render_post_repair_results(self, ctx, data): - cr = self._render_results(ctx, data.get_post_repair_results()) - return ctx.tag[T.div["Post-Repair Checker Results:"], cr] + @renderer + def post_repair_results(self, req, tag): + cr = self._render_results(req, self.r.get_post_repair_results()) + return tag(tags.div("Post-Repair Checker Results:"), cr) - def render_maybe_pre_repair_results(self, ctx, data): - if data.get_repair_attempted(): - cr = self._render_results(ctx, data.get_pre_repair_results()) - return ctx.tag[T.div["Pre-Repair Checker Results:"], cr] + @renderer + def maybe_pre_repair_results(self, req, tag): + if self.r.get_repair_attempted(): + cr = self._render_results(req, self.r.get_pre_repair_results()) + return tag(tags.div("Pre-Repair Checker Results:"), cr) return "" From 9dcaa104c5ea121859585da17ef6ffe76e1478fd Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 17:03:51 -0400 Subject: [PATCH 15/94] Use twisted tags in deep check results template --- src/allmydata/web/deep-check-results.xhtml | 71 +++++++++++----------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index cb2330b7d..f9770eb17 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -1,87 +1,86 @@ - + Tahoe-LAFS - Deep Check Results - + -

Deep-Check Results for root SI=

+

Deep-Check Results for root SI=

-

+

Counters:

    -
  • Objects Checked:
  • -
  • Objects Healthy:
  • -
  • Objects Unhealthy:
  • -
  • Objects Unrecoverable:
  • -
  • Corrupt Shares:
  • - +
  • Objects Checked:
  • +
  • Objects Healthy:
  • +
  • Objects Unhealthy:
  • +
  • Objects Unrecoverable:
  • +
  • Corrupt Shares:
-
+

Files/Directories That Had Problems:

-
    -
  • -
  • None
  • +
      +
    • +
    • None
-
+

Servers on which corrupt shares were found

-
    -
  • -
  • None
  • +
      +
    • +
    • None
-
+

Corrupt Shares

If repair fails, these shares need to be manually inspected and removed.

- - +
+ - - - - - + + + + +
Server Server Nickname Storage Index Share Number
-
+

All Results

- - +
+ - - - - - - + + + + + +
Relative Path Healthy Recoverable Storage Index Summary
-
+
From c33c0a884de10b61ffbf78e0fec0034c0933b9c6 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 17:46:06 -0400 Subject: [PATCH 16/94] Make DeepCheckResultsRenderer a MultiFormatResource --- src/allmydata/web/check_results.py | 148 +++++++++++++++++++---------- 1 file changed, 99 insertions(+), 49 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 3f272f991..4199b2c47 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -410,14 +410,17 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): return "" -class DeepCheckResultsRenderer(rend.Page, ResultsBase, ReloadMixin): - docFactory = getxmlfile("deep-check-results.xhtml") +class DeepCheckResultsRenderer(MultiFormatResource): + + formatArgument = "output" def __init__(self, client, monitor): + # TODO: document params + super(DeepCheckResultsRenderer, self).__init__() self.client = client self.monitor = monitor - def childFactory(self, ctx, name): + def getChild(self, name, req): if not name: return self # /operation/$OPHANDLE/$STORAGEINDEX provides detailed information @@ -431,13 +434,18 @@ class DeepCheckResultsRenderer(rend.Page, ResultsBase, ReloadMixin): raise WebError("No detailed results for SI %s" % html.escape(name), http.NOT_FOUND) - def renderHTTP(self, ctx): - if self.want_json(ctx): - return self.json(ctx) - return rend.Page.renderHTTP(self, ctx) + def render_HTML(self, req): + elem = DeepCheckResultsRendererElement(self.monitor) + return renderElement(req, elem) - def json(self, ctx): - inevow.IRequest(ctx).setHeader("content-type", "text/plain") + # def renderHTTP(self, ctx): + # if self.want_json(ctx): + # return self.json(ctx) + # return rend.Page.renderHTTP(self, ctx) + + def render_JSON(self, req): + # inevow.IRequest(ctx).setHeader("content-type", "text/plain") + req.setHeader("content-type", "text/plain") data = {} data["finished"] = self.monitor.is_finished() res = self.monitor.get_status() @@ -459,28 +467,49 @@ class DeepCheckResultsRenderer(rend.Page, ResultsBase, ReloadMixin): data["stats"] = res.get_stats() return json.dumps(data, indent=1) + "\n" - def render_root_storage_index(self, ctx, data): + +class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): + + loader = XMLFile(FilePath(__file__).sibling("deep-check-results.xhtml")) + + def __init__(self, monitor): + super(DeepCheckResultsRendererElement, self).__init__() + self.monitor = monitor + + @renderer + def root_storage_index(self, req, tag): return self.monitor.get_status().get_root_storage_index_string() - def data_objects_checked(self, ctx, data): + @renderer + def objects_checked(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-checked"] - def data_objects_healthy(self, ctx, data): + + @renderer + def objects_healthy(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-healthy"] - def data_objects_unhealthy(self, ctx, data): + + @renderer + def objects_unhealthy(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-unhealthy"] - def data_objects_unrecoverable(self, ctx, data): + + @renderer + def objects_unrecoverable(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-unrecoverable"] - def data_count_corrupt_shares(self, ctx, data): + @renderer + def count_corrupt_shares(self, req, tag): return self.monitor.get_status().get_counters()["count-corrupt-shares"] - def render_problems_p(self, ctx, data): + @renderer + def problems_p(self, req, tag): c = self.monitor.get_status().get_counters() if c["count-objects-unhealthy"]: - return ctx.tag + return tag return "" - def data_problems(self, ctx, data): + # TODO: use SlotsSequenceElement to render this. + @renderer + def problems(self, req, tag): all_objects = self.monitor.get_status().get_all_results() for path in sorted(all_objects.keys()): cr = all_objects[path] @@ -488,78 +517,99 @@ class DeepCheckResultsRenderer(rend.Page, ResultsBase, ReloadMixin): if not cr.is_healthy(): yield path, cr - def render_problem(self, ctx, data): + @renderer + def render_problem(self, req, tag): + # TODO: deal with this. path, cr = data summary_text = "" summary = cr.get_summary() if summary: summary_text = ": " + summary summary_text += " [SI: %s]" % cr.get_storage_index_string() - return ctx.tag[self._join_pathstring(path), self._html(summary_text)] + return tag(self._join_pathstring(path), self._html(summary_text)) - - def render_servers_with_corrupt_shares_p(self, ctx, data): + @renderer + def servers_with_corrupt_shares_p(self, req, tag): if self.monitor.get_status().get_counters()["count-corrupt-shares"]: - return ctx.tag + return tag return "" - def data_servers_with_corrupt_shares(self, ctx, data): + # TODO: use SlotsSequenceElement to render this. + @renderer + def servers_with_corrupt_shares(self, ctx, data): servers = [s for (s, storage_index, sharenum) in self.monitor.get_status().get_corrupt_shares()] servers.sort(key=lambda s: s.get_longname()) return servers - def render_server_problem(self, ctx, server): + @renderer + def server_problem(self, req, tag): + # def server_problem(self, ctx, server): + # TODO: where do `server` come from now? data = [server.get_name()] nickname = server.get_nickname() if nickname: data.append(" (%s)" % self._html(nickname)) - return ctx.tag[data] + return tag(data) - - def render_corrupt_shares_p(self, ctx, data): + @renderer + def corrupt_shares_p(self, req, tag): if self.monitor.get_status().get_counters()["count-corrupt-shares"]: - return ctx.tag + return tag return "" - def data_corrupt_shares(self, ctx, data): + + # TODO: Probably should use SlotsSequenceElement to render this. + @renderer + def corrupt_shares(self, req, tag): return self.monitor.get_status().get_corrupt_shares() - def render_share_problem(self, ctx, data): + + @renderer + def share_problem(self, req, tag): + # def render_share_problem(self, req, tag): server, storage_index, sharenum = data nickname = server.get_nickname() - ctx.fillSlots("serverid", server.get_name()) + tag.fillSlots("serverid", server.get_name()) if nickname: - ctx.fillSlots("nickname", self._html(nickname)) - ctx.fillSlots("si", self._render_si_link(ctx, storage_index)) - ctx.fillSlots("shnum", str(sharenum)) - return ctx.tag + tag.fillSlots("nickname", self._html(nickname)) + tag.fillSlots("si", self._render_si_link(ctx, storage_index)) + tag.fillSlots("shnum", str(sharenum)) + return tag - def render_return(self, ctx, data): - req = inevow.IRequest(ctx) + @renderer + def return_to(self, req, tag): + # req = inevow.IRequest(ctx) return_to = get_arg(req, "return_to", None) if return_to: - return T.div[T.a(href=return_to)["Return to file/directory."]] + return tags.div(tags.a("Return to file/directory.", href=return_to)) return "" + # TODO: use SlotsSequenceElement to render this. + @renderer def data_all_objects(self, ctx, data): r = self.monitor.get_status().get_all_results() for path in sorted(r.keys()): yield (path, r[path]) - def render_object(self, ctx, data): + @renderer + def object(self, ctx, data): + # def render_object(self, ctx, data): + # TODO: figure `data` out path, r = data - ctx.fillSlots("path", self._join_pathstring(path)) - ctx.fillSlots("healthy", str(r.is_healthy())) - ctx.fillSlots("recoverable", str(r.is_recoverable())) + tag.fillSlots("path", self._join_pathstring(path)) + tag.fillSlots("healthy", str(r.is_healthy())) + tag.fillSlots("recoverable", str(r.is_recoverable())) storage_index = r.get_storage_index() - ctx.fillSlots("storage_index", self._render_si_link(ctx, storage_index)) - ctx.fillSlots("summary", self._html(r.get_summary())) - return ctx.tag + tag.fillSlots("storage_index", self._render_si_link(req, storage_index)) + tag.fillSlots("summary", self._html(r.get_summary())) + return tag - def render_runtime(self, ctx, data): - req = inevow.IRequest(ctx) + @renderer + def runtime(self, req, tag): + # req = inevow.IRequest(ctx) runtime = time.time() - req.processing_started_timestamp - return ctx.tag["runtime: %s seconds" % runtime] + return tag("runtime: %s seconds" % runtime) + class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin): docFactory = getxmlfile("deep-check-and-repair-results.xhtml") From 27e5ce090aa7bb29aa05a2da212eba299022bb0c Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 17:46:34 -0400 Subject: [PATCH 17/94] Update deep check results template --- src/allmydata/web/deep-check-results.xhtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index f9770eb17..11a2e7f4b 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -24,7 +24,7 @@

Files/Directories That Had Problems:

-
    +
    • None
    @@ -33,7 +33,7 @@

    Servers on which corrupt shares were found

    -
      +
      • None
      @@ -42,7 +42,7 @@

      Corrupt Shares

      If repair fails, these shares need to be manually inspected and removed.

      - +
      @@ -58,11 +58,11 @@
      Server Server Nickname
      -
      +

      All Results

      - +
      From 842b2b6be511afd5aff6a6b8204e787a68c7162e Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 18:03:33 -0400 Subject: [PATCH 18/94] Use twisted tags in "deep check and repair results" template --- .../web/deep-check-and-repair-results.xhtml | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index a7db34837..366dcf346 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -1,76 +1,76 @@ - + Tahoe-LAFS - Deep Check Results - +

      Deep-Check-And-Repair Results for root - SI=

      + SI= -

      +

      Counters:

        -
      • Objects Checked:
      • +
      • Objects Checked:
      • -
      • Objects Healthy (before repair):
      • -
      • Objects Unhealthy (before repair):
      • -
      • Corrupt Shares (before repair):
      • +
      • Objects Healthy (before repair):
      • +
      • Objects Unhealthy (before repair):
      • +
      • Corrupt Shares (before repair):
      • -
      • Repairs Attempted:
      • -
      • Repairs Successful:
      • -
      • Repairs Unsuccessful:
      • +
      • Repairs Attempted:
      • +
      • Repairs Successful:
      • +
      • Repairs Unsuccessful:
      • -
      • Objects Healthy (after repair):
      • -
      • Objects Unhealthy (after repair):
      • -
      • Corrupt Shares (after repair):
      • +
      • Objects Healthy (after repair):
      • +
      • Objects Unhealthy (after repair):
      • +
      • Corrupt Shares (after repair):
      -
      +

      Files/Directories That Had Problems:

      -
        -
      • -
      • None
      • +
          +
        • +
        • None
      -
      +

      Files/Directories That Still Have Problems:

      -
        -
      • -
      • None
      • +
          +
        • +
        • None
      -
      +

      Servers on which corrupt shares were found

      -
        -
      • -
      • None
      • +
          +
        • +
        • None
      -
      +

      Remaining Corrupt Shares

      These shares need to be manually inspected and removed.

      -
        -
      • -
      • None
      • +
          +
        • +
        • None
      -
      +
      -

      Relative Path Healthy
      - +
      + @@ -78,18 +78,18 @@ - - - - - - - + + + + + + +
      Relative Path Healthy Pre-Repair Recoverable Pre-RepairStorage Index Summary
      -
      +
      From e4efcdd925682486039bd23bfd2e83d7214f747e Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 18:17:33 -0400 Subject: [PATCH 19/94] Make DeepCheckAndRepairResultsRenderer a MultiFormatResource --- src/allmydata/web/check_results.py | 168 +++++++++++++++++++---------- 1 file changed, 111 insertions(+), 57 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 4199b2c47..19cfefe87 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -586,7 +586,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): # TODO: use SlotsSequenceElement to render this. @renderer - def data_all_objects(self, ctx, data): + def all_objects(self, ctx, data): r = self.monitor.get_status().get_all_results() for path in sorted(r.keys()): yield (path, r[path]) @@ -611,14 +611,15 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): return tag("runtime: %s seconds" % runtime) -class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin): - docFactory = getxmlfile("deep-check-and-repair-results.xhtml") +class DeepCheckAndRepairResultsRenderer(MultiFormatResource): + + formatArgument = "output" def __init__(self, client, monitor): self.client = client self.monitor = monitor - def childFactory(self, ctx, name): + def getChild(self, name, req): if not name: return self # /operation/$OPHANDLE/$STORAGEINDEX provides detailed information @@ -632,13 +633,18 @@ class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin): raise WebError("No detailed results for SI %s" % html.escape(name), http.NOT_FOUND) - def renderHTTP(self, ctx): - if self.want_json(ctx): - return self.json(ctx) - return rend.Page.renderHTTP(self, ctx) + def render_HTML(self, req): + elem = DeepCheckAndRepairResultsRendererElement(self.monitor) + return renderElement(req, elem) - def json(self, ctx): - inevow.IRequest(ctx).setHeader("content-type", "text/plain") + # def renderHTTP(self, ctx): + # if self.want_json(ctx): + # return self.json(ctx) + # return rend.Page.renderHTTP(self, ctx) + + def render_JSON(self, req): + # inevow.IRequest(ctx).setHeader("content-type", "text/plain") + req.setHeader("content-type", "text/plain") res = self.monitor.get_status() data = {} data["finished"] = self.monitor.is_finished() @@ -679,40 +685,70 @@ class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin): data["stats"] = res.get_stats() return json.dumps(data, indent=1) + "\n" - def render_root_storage_index(self, ctx, data): + +class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin): + + loader = XMLFile(FilePath(__file__).sibling("deep-check-and-repair-results.xhtml")) + + def __init__(self, monitor): + # TODO: document params + super(DeepCheckAndRepairResultsRendererElement, self).__init__() + self.monitor = monitor + + @renderer + def root_storage_index(self, req, tag): return self.monitor.get_status().get_root_storage_index_string() - def data_objects_checked(self, ctx, data): + @renderer + def objects_checked(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-checked"] - def data_objects_healthy(self, ctx, data): + @renderer + def objects_healthy(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-healthy-pre-repair"] - def data_objects_unhealthy(self, ctx, data): + + @renderer + def objects_unhealthy(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-unhealthy-pre-repair"] - def data_corrupt_shares(self, ctx, data): + + @renderer + def corrupt_shares(self, req, tag): return self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"] - def data_repairs_attempted(self, ctx, data): + @renderer + def repairs_attempted(self, req, tag): return self.monitor.get_status().get_counters()["count-repairs-attempted"] - def data_repairs_successful(self, ctx, data): + + @renderer + def repairs_successful(self, req, tag): return self.monitor.get_status().get_counters()["count-repairs-successful"] - def data_repairs_unsuccessful(self, ctx, data): + + @renderer + def repairs_unsuccessful(self, req, tag): return self.monitor.get_status().get_counters()["count-repairs-unsuccessful"] - def data_objects_healthy_post(self, ctx, data): + @renderer + def objects_healthy_post(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-healthy-post-repair"] - def data_objects_unhealthy_post(self, ctx, data): + + @renderer + def objects_unhealthy_post(self, req, tag): return self.monitor.get_status().get_counters()["count-objects-unhealthy-post-repair"] - def data_corrupt_shares_post(self, ctx, data): + + @renderer + def corrupt_shares_post(self, req, tag): return self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"] - def render_pre_repair_problems_p(self, ctx, data): + @renderer + def pre_repair_problems_p(self, req, tag): c = self.monitor.get_status().get_counters() if c["count-objects-unhealthy-pre-repair"]: - return ctx.tag + return tag return "" - def data_pre_repair_problems(self, ctx, data): + # TODO: use SlotsSequenceElement + @renderer + def pre_repair_problems(self, req, tag): all_objects = self.monitor.get_status().get_all_results() for path in sorted(all_objects.keys()): r = all_objects[path] @@ -721,19 +757,24 @@ class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin): if not cr.is_healthy(): yield path, cr - def render_problem(self, ctx, data): - path, cr = data - return ctx.tag[self._join_pathstring(path), ": ", - self._html(cr.get_summary())] + @renderer + def problem(self, req, tag): + # TODO: figure this out + # path, cr = data + return tag(self._join_pathstring(path), ": ", + self._html(cr.get_summary())) - def render_post_repair_problems_p(self, ctx, data): + @renderer + def post_repair_problems_p(self, req, tag): c = self.monitor.get_status().get_counters() if (c["count-objects-unhealthy-post-repair"] or c["count-corrupt-shares-post-repair"]): - return ctx.tag + return tag return "" - def data_post_repair_problems(self, ctx, data): + # TODO: use SlotsSequenceElement + @renderer + def post_repair_problems(self, req, tag): all_objects = self.monitor.get_status().get_all_results() for path in sorted(all_objects.keys()): r = all_objects[path] @@ -742,56 +783,69 @@ class DeepCheckAndRepairResultsRenderer(rend.Page, ResultsBase, ReloadMixin): if not cr.is_healthy(): yield path, cr - def render_servers_with_corrupt_shares_p(self, ctx, data): + @renderer + def servers_with_corrupt_shares_p(self, req, tag): if self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"]: - return ctx.tag + return tag return "" - def data_servers_with_corrupt_shares(self, ctx, data): + + @renderer + def servers_with_corrupt_shares(self, req, tag): return [] # TODO - def render_server_problem(self, ctx, data): + + @renderer + def server_problem(self, req, tag): pass - - def render_remaining_corrupt_shares_p(self, ctx, data): + @renderer + def remaining_corrupt_shares_p(self, req, tag): if self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"]: - return ctx.tag + return tag return "" - def data_post_repair_corrupt_shares(self, ctx, data): + + @renderer + def post_repair_corrupt_shares(self, req, tag): return [] # TODO - def render_share_problem(self, ctx, data): + @renderer + def render_share_problem(self, req, tag): pass - - def render_return(self, ctx, data): - req = inevow.IRequest(ctx) + @renderer + def return_to(self, req, tag): + # req = inevow.IRequest(ctx) return_to = get_arg(req, "return_to", None) if return_to: - return T.div[T.a(href=return_to)["Return to file/directory."]] + return tags.div(tags.a("Return to file/directory.", href=return_to)) return "" - def data_all_objects(self, ctx, data): + # TODO: use SlotsSequenceElement + @renderer + def all_objects(self, req, tag): r = self.monitor.get_status().get_all_results() for path in sorted(r.keys()): yield (path, r[path]) - def render_object(self, ctx, data): - path, r = data - ctx.fillSlots("path", self._join_pathstring(path)) - ctx.fillSlots("healthy_pre_repair", + @renderer + def object(self, req, tag): + # TODO: figure this out + # path, r = data + tag.fillSlots("path", self._join_pathstring(path)) + tag.fillSlots("healthy_pre_repair", str(r.get_pre_repair_results().is_healthy())) - ctx.fillSlots("recoverable_pre_repair", + tag.fillSlots("recoverable_pre_repair", str(r.get_pre_repair_results().is_recoverable())) - ctx.fillSlots("healthy_post_repair", + tag.fillSlots("healthy_post_repair", str(r.get_post_repair_results().is_healthy())) storage_index = r.get_storage_index() - ctx.fillSlots("storage_index", + tag.fillSlots("storage_index", self._render_si_link(ctx, storage_index)) - ctx.fillSlots("summary", + tag.fillSlots("summary", self._html(r.get_pre_repair_results().get_summary())) - return ctx.tag + return tag - def render_runtime(self, ctx, data): - req = inevow.IRequest(ctx) + @renderer + def runtime(self, req, tag): + # req = inevow.IRequest(ctx) runtime = time.time() - req.processing_started_timestamp - return ctx.tag["runtime: %s seconds" % runtime] + return tag("runtime: %s seconds" % runtime) From 19db5fb3128732ae7880dad351e42924fc1f02d0 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 18:17:44 -0400 Subject: [PATCH 20/94] Update "deep check and repair results" template --- src/allmydata/web/deep-check-and-repair-results.xhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index 366dcf346..60cb10fba 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -66,7 +66,7 @@
    -
    +
    From 7237fef6307675b8ec0cf5d900e475e2a34b7822 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 19:04:42 -0400 Subject: [PATCH 21/94] Add property client to CheckAndRepairResultsRendererElement --- src/allmydata/web/check_results.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 19cfefe87..4339e5e30 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -357,7 +357,7 @@ class CheckAndRepairResultsRenderer(MultiFormatResource): self.r = ICheckAndRepairResults(results) def render_HTML(self, req): - elem = CheckAndRepairResultsRendererElement(self.r) + elem = CheckAndRepairResultsRendererElement(self.client, self.r) return renderElement(req, elem) def render_JSON(self, req): @@ -370,8 +370,9 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): loader = XMLFile(FilePath(__file__).sibling("check-and-repair-results.xhtml")) - def __init__(self, r): + def __init__(self, client, r): super(CheckAndRepairResultsRendererElement, self).__init__() + self.client = client self.r = r @renderer From 0d1269264a6044afe2ece2d5ec80ce5b12ce28ac Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 4 Jun 2020 19:14:34 -0400 Subject: [PATCH 22/94] Stringify numbers in DeepCheckResultsRenderer --- src/allmydata/web/check_results.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 4339e5e30..757326bf7 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -477,29 +477,31 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): super(DeepCheckResultsRendererElement, self).__init__() self.monitor = monitor + # TODO: Add a helper to query monitor.get_status.get_counters() + @renderer def root_storage_index(self, req, tag): return self.monitor.get_status().get_root_storage_index_string() @renderer def objects_checked(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-checked"] + return str(self.monitor.get_status().get_counters()["count-objects-checked"]) @renderer def objects_healthy(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-healthy"] + return str(self.monitor.get_status().get_counters()["count-objects-healthy"]) @renderer def objects_unhealthy(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-unhealthy"] + return str(self.monitor.get_status().get_counters()["count-objects-unhealthy"]) @renderer def objects_unrecoverable(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-unrecoverable"] + return str(self.monitor.get_status().get_counters()["count-objects-unrecoverable"]) @renderer def count_corrupt_shares(self, req, tag): - return self.monitor.get_status().get_counters()["count-corrupt-shares"] + return str(self.monitor.get_status().get_counters()["count-corrupt-shares"]) @renderer def problems_p(self, req, tag): From 77120ba70c9c54a51a71ca41a76602f3f1cf9790 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 11 Jun 2020 12:44:48 -0400 Subject: [PATCH 23/94] Update tests for ResultsRenderer classes --- src/allmydata/test/test_checker.py | 98 +++++++++++++++++++----------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 5eed6f21f..bc4215ab3 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -3,6 +3,13 @@ import json import os.path, shutil from twisted.trial import unittest from twisted.internet import defer + +from nevow.inevow import IRequest +from zope.interface import implementer +from twisted.web.server import Request +from twisted.web.test.requesthelper import DummyChannel +from twisted.web.template import flattenString + from allmydata import check_results, uri from allmydata import uri as tahoe_uri from allmydata.util import base32 @@ -23,6 +30,17 @@ class FakeClient(object): def get_storage_broker(self): return self.storage_broker +# XXX: We have to have this class because `common.get_arg()` expects a +# `nevow.inevow.IRequest`, which `twisted.web.server.Request` isn't. +# Also, the request needs to have `args` and `fields` properties so +# that `allmydata.web.common.get_arg()` won't complain. +@implementer(IRequest) +class TestRequest(Request): + def __init__(self, args=None, fields=None): + Request.__init__(self, DummyChannel()) + self.args = args or {} + self.fields = fields or {} + class WebResultsRendering(unittest.TestCase, WebRenderingMixin): def create_fake_client(self): @@ -51,34 +69,39 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): c.storage_broker = sb return c - def render_json(self, page): - d = self.render1(page, args={"output": ["json"]}) - return d + def render_json(self, resource): + return resource.render(TestRequest(args={"output": ["json"]})) + + def render_element(self, element, args=None): + d = flattenString(TestRequest(args), element) + return unittest.TestCase().successResultOf(d) def test_literal(self): - c = self.create_fake_client() - lcr = web_check_results.LiteralCheckResultsRenderer(c) + lcr = web_check_results.LiteralCheckResultsElement() - d = self.render1(lcr) + d = self.render_element(lcr) def _check(html): s = self.remove_tags(html) self.failUnlessIn("Literal files are always healthy", s) - d.addCallback(_check) - d.addCallback(lambda ignored: - self.render1(lcr, args={"return_to": ["FOOURL"]})) + _check(d) + + d = self.render_element(lcr, args={"return_to": ["FOOURL"]}) def _check_return_to(html): s = self.remove_tags(html) self.failUnlessIn("Literal files are always healthy", s) self.failUnlessIn('Return to file.', html) - d.addCallback(_check_return_to) - d.addCallback(lambda ignored: self.render_json(lcr)) + _check_return_to(d) + + c = self.create_fake_client() + lcr = web_check_results.LiteralCheckResultsRenderer(c) + + d = self.render_json(lcr) def _check_json(js): j = json.loads(js) self.failUnlessEqual(j["storage-index"], "") self.failUnlessEqual(j["results"]["healthy"], True) - d.addCallback(_check_json) - return d + _check_json(d) def test_check(self): c = self.create_fake_client() @@ -108,8 +131,8 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): healthy=True, recoverable=True, summary="groovy", **data) - w = web_check_results.CheckResultsRenderer(c, cr) - html = self.render2(w) + w = web_check_results.CheckResultsRendererElement(c, cr) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated self.failUnlessIn("Healthy : groovy", s) @@ -126,8 +149,8 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): healthy=False, recoverable=True, summary="ungroovy", **data) - w = web_check_results.CheckResultsRenderer(c, cr) - html = self.render2(w) + w = web_check_results.CheckResultsRendererElement(c, cr) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated self.failUnlessIn("Not Healthy! : ungroovy", s) @@ -138,22 +161,23 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): healthy=False, recoverable=False, summary="rather dead", **data) - w = web_check_results.CheckResultsRenderer(c, cr) - html = self.render2(w) + w = web_check_results.CheckResultsRendererElement(c, cr) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated self.failUnlessIn("Not Recoverable! : rather dead", s) self.failUnlessIn("Corrupt shares: Share ID Nickname Node ID sh#2 peer-0 00000000", s) - html = self.render2(w) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated self.failUnlessIn("Not Recoverable! : rather dead", s) - html = self.render2(w, args={"return_to": ["FOOURL"]}) + html = self.render_element(w, args={"return_to": ["FOOURL"]}) self.failUnlessIn('Return to file/directory.', html) + w = web_check_results.CheckResultsRenderer(c, cr) d = self.render_json(w) def _check_json(jdata): j = json.loads(jdata) @@ -178,15 +202,15 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): 'recoverable': False, } self.failUnlessEqual(j["results"], expected) - d.addCallback(_check_json) - d.addCallback(lambda ignored: self.render1(w)) + _check_json(d) + + w = web_check_results.CheckResultsRendererElement(c, cr) + d = self.render_element(w) def _check(html): s = self.remove_tags(html) self.failUnlessIn("File Check Results for SI=2k6avp", s) self.failUnlessIn("Not Recoverable! : rather dead", s) - d.addCallback(_check) - return d - + _check(html) def test_check_and_repair(self): c = self.create_fake_client() @@ -244,8 +268,8 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): crr.post_repair_results = post_cr crr.repair_attempted = False - w = web_check_results.CheckAndRepairResultsRenderer(c, crr) - html = self.render2(w) + w = web_check_results.CheckAndRepairResultsRendererElement(c, crr) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s) @@ -256,7 +280,7 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): crr.repair_attempted = True crr.repair_successful = True - html = self.render2(w) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s) @@ -271,7 +295,7 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): summary="better", **data) crr.post_repair_results = post_cr - html = self.render2(w) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s) @@ -286,7 +310,7 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): summary="worse", **data) crr.post_repair_results = post_cr - html = self.render2(w) + html = self.render_element(w) s = self.remove_tags(html) self.failUnlessIn("File Check-And-Repair Results for SI=2k6avp", s) @@ -294,7 +318,8 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): self.failUnlessIn("Repair unsuccessful", s) self.failUnlessIn("Post-Repair Checker Results:", s) - d = self.render_json(w) + w2 = web_check_results.CheckAndRepairResultsRenderer(c, crr) + d = self.render_json(w2) def _got_json(data): j = json.loads(data) self.failUnlessEqual(j["repair-attempted"], True) @@ -302,16 +327,15 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): "2k6avpjga3dho3zsjo6nnkt7n4") self.failUnlessEqual(j["pre-repair-results"]["summary"], "illing") self.failUnlessEqual(j["post-repair-results"]["summary"], "worse") - d.addCallback(_got_json) + _got_json(d) - w2 = web_check_results.CheckAndRepairResultsRenderer(c, None) - d.addCallback(lambda ignored: self.render_json(w2)) + w3 = web_check_results.CheckAndRepairResultsRenderer(c, None) + d = self.render_json(w3) def _got_lit_results(data): j = json.loads(data) self.failUnlessEqual(j["repair-attempted"], False) self.failUnlessEqual(j["storage-index"], "") - d.addCallback(_got_lit_results) - return d + _got_lit_results(d) class BalancingAct(GridTestMixin, unittest.TestCase): # test for #1115 regarding the 'count-good-share-hosts' metric From 02cd42c130e9ff0c9d1dc7a799ffa078a29983a1 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 11 Jun 2020 15:48:44 -0400 Subject: [PATCH 24/94] Simplify WebResultsRendering.test_literal --- src/allmydata/test/test_checker.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index bc4215ab3..62725da90 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -79,29 +79,23 @@ class WebResultsRendering(unittest.TestCase, WebRenderingMixin): def test_literal(self): lcr = web_check_results.LiteralCheckResultsElement() - d = self.render_element(lcr) - def _check(html): - s = self.remove_tags(html) - self.failUnlessIn("Literal files are always healthy", s) - _check(d) + html = self.render_element(lcr) + s = self.remove_tags(html) + self.failUnlessIn("Literal files are always healthy", s) - d = self.render_element(lcr, args={"return_to": ["FOOURL"]}) - def _check_return_to(html): - s = self.remove_tags(html) - self.failUnlessIn("Literal files are always healthy", s) - self.failUnlessIn('Return to file.', - html) - _check_return_to(d) + html = self.render_element(lcr, args={"return_to": ["FOOURL"]}) + s = self.remove_tags(html) + self.failUnlessIn("Literal files are always healthy", s) + self.failUnlessIn('Return to file.', html) c = self.create_fake_client() lcr = web_check_results.LiteralCheckResultsRenderer(c) - d = self.render_json(lcr) - def _check_json(js): - j = json.loads(js) - self.failUnlessEqual(j["storage-index"], "") - self.failUnlessEqual(j["results"]["healthy"], True) - _check_json(d) + js = self.render_json(lcr) + j = json.loads(js) + self.failUnlessEqual(j["storage-index"], "") + self.failUnlessEqual(j["results"]["healthy"], True) + def test_check(self): c = self.create_fake_client() From c44a167a660763465721432e925261cef0835d2a Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 11 Jun 2020 15:51:39 -0400 Subject: [PATCH 25/94] Remove WebRenderingMixin from WebResultsRendering --- src/allmydata/test/test_checker.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 62725da90..2bea98280 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -1,4 +1,5 @@ +import re import json import os.path, shutil from twisted.trial import unittest @@ -41,7 +42,12 @@ class TestRequest(Request): self.args = args or {} self.fields = fields or {} -class WebResultsRendering(unittest.TestCase, WebRenderingMixin): +class WebResultsRendering(unittest.TestCase): + + def remove_tags(self, s): + s = re.sub(r'<[^>]*>', ' ', s) + s = re.sub(r'\s+', ' ', s) + return s def create_fake_client(self): sb = StorageFarmBroker(True, None, EMPTY_CLIENT_CONFIG) From 048840ac99251bff328d7b963de90e6ff401e68c Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 11 Jun 2020 16:00:21 -0400 Subject: [PATCH 26/94] Stringify numbers --- src/allmydata/web/check_results.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 757326bf7..c1edb799c 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -702,45 +702,48 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin def root_storage_index(self, req, tag): return self.monitor.get_status().get_root_storage_index_string() + def _get_counter(self, name): + return str(self.monitor.get_status().get_counters().get(name)) + @renderer def objects_checked(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-checked"] + return self._get_counter("count-objects-checked") @renderer def objects_healthy(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-healthy-pre-repair"] + return self._get_counter("count-objects-healthy-pre-repair") @renderer def objects_unhealthy(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-unhealthy-pre-repair"] + return self._get_counter("count-objects-unhealthy-pre-repair") @renderer def corrupt_shares(self, req, tag): - return self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"] + return self._get_counter("count-corrupt-shares-pre-repair") @renderer def repairs_attempted(self, req, tag): - return self.monitor.get_status().get_counters()["count-repairs-attempted"] + return self._get_counter("count-repairs-attempted") @renderer def repairs_successful(self, req, tag): - return self.monitor.get_status().get_counters()["count-repairs-successful"] + return self._get_counter("count-repairs-successful") @renderer def repairs_unsuccessful(self, req, tag): - return self.monitor.get_status().get_counters()["count-repairs-unsuccessful"] + return self._get_counter("count-repairs-unsuccessful") @renderer def objects_healthy_post(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-healthy-post-repair"] + return self._get_counter("count-objects-healthy-post-repair") @renderer def objects_unhealthy_post(self, req, tag): - return self.monitor.get_status().get_counters()["count-objects-unhealthy-post-repair"] + return self._get_counter("count-objects-unhealthy-post-repair") @renderer def corrupt_shares_post(self, req, tag): - return self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"] + return self._get_counter("count-corrupt-shares-post-repair") @renderer def pre_repair_problems_p(self, req, tag): From c4be4b566cab402c2c6af6158de67be783525988 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 11 Jun 2020 20:50:03 -0400 Subject: [PATCH 27/94] Make TestRequest an object Wouldn't pass PythonTwoRegressions.test_new_style_classes without this. --- src/allmydata/test/test_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 2bea98280..75619beaa 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -36,7 +36,7 @@ class FakeClient(object): # Also, the request needs to have `args` and `fields` properties so # that `allmydata.web.common.get_arg()` won't complain. @implementer(IRequest) -class TestRequest(Request): +class TestRequest(object, Request): def __init__(self, args=None, fields=None): Request.__init__(self, DummyChannel()) self.args = args or {} From 3783349ea7bf280b42205c6744bb9f5c2544af84 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 09:38:18 -0400 Subject: [PATCH 28/94] Add span tags to "deep check and repair results" template Tests expect tags around these numbers, let us add those. --- .../web/deep-check-and-repair-results.xhtml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index 60cb10fba..72ee726db 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -15,19 +15,19 @@

    Counters:

      -
    • Objects Checked:
    • +
    • Objects Checked:
    • -
    • Objects Healthy (before repair):
    • -
    • Objects Unhealthy (before repair):
    • -
    • Corrupt Shares (before repair):
    • +
    • Objects Healthy (before repair):
    • +
    • Objects Unhealthy (before repair):
    • +
    • Corrupt Shares (before repair):
    • -
    • Repairs Attempted:
    • -
    • Repairs Successful:
    • -
    • Repairs Unsuccessful:
    • +
    • Repairs Attempted:
    • +
    • Repairs Successful:
    • +
    • Repairs Unsuccessful:
    • -
    • Objects Healthy (after repair):
    • -
    • Objects Unhealthy (after repair):
    • -
    • Corrupt Shares (after repair):
    • +
    • Objects Healthy (after repair):
    • +
    • Objects Unhealthy (after repair):
    • +
    • Corrupt Shares (after repair):
    From cfb1560d154f460abda8b77d38b1e06ce863a23c Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 14:01:41 -0400 Subject: [PATCH 29/94] Render "all objects" table using SlotsSequenceElement --- src/allmydata/web/check_results.py | 38 +++++++++---------- .../web/deep-check-and-repair-results.xhtml | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index c1edb799c..14cf8b5c5 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -24,6 +24,7 @@ from allmydata.web.common import ( get_root, WebError, MultiFormatResource, + SlotsSequenceElement, ) from allmydata.web.operations import ReloadMixin from allmydata.interfaces import ( @@ -825,30 +826,25 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin return tags.div(tags.a("Return to file/directory.", href=return_to)) return "" - # TODO: use SlotsSequenceElement @renderer def all_objects(self, req, tag): - r = self.monitor.get_status().get_all_results() - for path in sorted(r.keys()): - yield (path, r[path]) + results = self.monitor.get_status().get_all_results() + objects = [] - @renderer - def object(self, req, tag): - # TODO: figure this out - # path, r = data - tag.fillSlots("path", self._join_pathstring(path)) - tag.fillSlots("healthy_pre_repair", - str(r.get_pre_repair_results().is_healthy())) - tag.fillSlots("recoverable_pre_repair", - str(r.get_pre_repair_results().is_recoverable())) - tag.fillSlots("healthy_post_repair", - str(r.get_post_repair_results().is_healthy())) - storage_index = r.get_storage_index() - tag.fillSlots("storage_index", - self._render_si_link(ctx, storage_index)) - tag.fillSlots("summary", - self._html(r.get_pre_repair_results().get_summary())) - return tag + for path in sorted(results.keys()): + result = results[path] + storage_index = result.get_storage_index() + obj = { + "path": self._join_pathstring(path), + "healthy_pre_repair": str(result.get_pre_repair_results().is_healthy()), + "recoverable_pre_repair": str(result.get_pre_repair_results().is_recoverable()), + "healthy_post_repair": str(result.get_post_repair_results().is_healthy()), + "storage_index": self._render_si_link(req, storage_index), + "summary": self._html(result.get_pre_repair_results().get_summary()), + } + objects.append(obj) + + return SlotsSequenceElement(tag, objects) @renderer def runtime(self, req, tag): diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index 72ee726db..9d16e27b9 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -78,7 +78,7 @@
    - + From c7aa789a22024fc053f8de0e578b81d516beef2a Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 17:28:54 -0400 Subject: [PATCH 30/94] Fix "class has no children attribute" error --- src/allmydata/web/check_results.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 14cf8b5c5..5cd2bdbdc 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -622,6 +622,7 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): def __init__(self, client, monitor): self.client = client self.monitor = monitor + self.children = {} def getChild(self, name, req): if not name: From f168dcd19c78639c1a3ff94bc67fb58b6bb74842 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 17:30:39 -0400 Subject: [PATCH 31/94] Use SlotsSequenceElement to render pre-repair problems --- src/allmydata/web/check_results.py | 13 +++++-------- .../web/deep-check-and-repair-results.xhtml | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 5cd2bdbdc..ad02385f1 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -754,23 +754,20 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin return tag return "" - # TODO: use SlotsSequenceElement @renderer def pre_repair_problems(self, req, tag): all_objects = self.monitor.get_status().get_all_results() + problems = [] + for path in sorted(all_objects.keys()): r = all_objects[path] assert ICheckAndRepairResults.providedBy(r) cr = r.get_pre_repair_results() if not cr.is_healthy(): - yield path, cr + problem = self._join_pathstring(path), ": ", self._html(cr.get_summary()) + problems.append(problem) - @renderer - def problem(self, req, tag): - # TODO: figure this out - # path, cr = data - return tag(self._join_pathstring(path), ": ", - self._html(cr.get_summary())) + return SlotsSequenceElement(tag, problems) @renderer def post_repair_problems_p(self, req, tag): diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index 9d16e27b9..50131ac60 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -35,8 +35,8 @@

    Files/Directories That Had Problems:

      -
    • -
    • None
    • +
    • +
    • None
    From e0c686bb923301cb725875f7fc8fbff3557dddfb Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 17:36:34 -0400 Subject: [PATCH 32/94] Use SlotsSequenceElement to render post-repair problems --- src/allmydata/web/check_results.py | 8 ++++++-- src/allmydata/web/deep-check-and-repair-results.xhtml | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index ad02385f1..f8ad4241a 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -777,16 +777,20 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin return tag return "" - # TODO: use SlotsSequenceElement @renderer def post_repair_problems(self, req, tag): all_objects = self.monitor.get_status().get_all_results() + problems = [] + for path in sorted(all_objects.keys()): r = all_objects[path] assert ICheckAndRepairResults.providedBy(r) cr = r.get_post_repair_results() if not cr.is_healthy(): - yield path, cr + problem = self._join_pathstring(path), ": ", self._html(cr.get_summary()) + problems.append(problem) + + return SlotsSequenceElement(tag, problems) @renderer def servers_with_corrupt_shares_p(self, req, tag): diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index 50131ac60..b0700a71f 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -44,8 +44,8 @@

    Files/Directories That Still Have Problems:

      -
    • -
    • None
    • +
    • +
    • None
    From 19f6ec71026fa3ed70e3ec978a634208c14f7b75 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 17:43:47 -0400 Subject: [PATCH 33/94] Use SlotsSequenceElement to render servers with corrupt shares This was previously not implemented; leaving it as such. --- src/allmydata/web/check_results.py | 9 ++++----- src/allmydata/web/deep-check-and-repair-results.xhtml | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index f8ad4241a..8b281045b 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -800,11 +800,10 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def servers_with_corrupt_shares(self, req, tag): - return [] # TODO - - @renderer - def server_problem(self, req, tag): - pass + # TODO: this was originally unimplemented before porting to + # twisted.web.template; leaving it as such. + corrupt = [] + return SlotsSequenceElement(tag, corrupt) @renderer def remaining_corrupt_shares_p(self, req, tag): diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index b0700a71f..71fa92878 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -52,8 +52,8 @@

    Servers on which corrupt shares were found

      -
    • -
    • None
    • +
    • +
    • None
    From bbd6bde478daa14d789f826caa2ab4934f9b4f9f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 17:47:51 -0400 Subject: [PATCH 34/94] Use SlotsSequenceElement to render remaining corrupt shares This was previously not implemented; leaving it as such. --- src/allmydata/web/check_results.py | 9 ++++----- src/allmydata/web/deep-check-and-repair-results.xhtml | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 8b281045b..370f1c308 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -813,11 +813,10 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def post_repair_corrupt_shares(self, req, tag): - return [] # TODO - - @renderer - def render_share_problem(self, req, tag): - pass + # TODO: this was not implemented before porting to + # twisted.web.template; leaving it as such. + corrupt = [] + return SlotsSequenceElement(tag, corrupt) @renderer def return_to(self, req, tag): diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index 71fa92878..c9bd998e5 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -61,8 +61,8 @@

    Remaining Corrupt Shares

    These shares need to be manually inspected and removed.

      -
    • -
    • None
    • +
    • +
    • None
    From c41bca24f5f8e5feebcf037fe3c1ce4535da5405 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Sun, 14 Jun 2020 22:15:01 -0400 Subject: [PATCH 35/94] Use SlotsSequenceElement to render deep check results problems --- src/allmydata/web/check_results.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 370f1c308..748fd0aa3 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -511,26 +511,24 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): return tag return "" - # TODO: use SlotsSequenceElement to render this. @renderer 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(): - yield path, cr + summary_text = "" + summary = cr.get_summary() + if summary: + summary_text = ": " + summary + summary_text += " [SI: %s]" % cr.get_storage_index_string() + problem = self._join_pathstring(path), ":", self._html(summary_text) + problems.append(problem) - @renderer - def render_problem(self, req, tag): - # TODO: deal with this. - path, cr = data - summary_text = "" - summary = cr.get_summary() - if summary: - summary_text = ": " + summary - summary_text += " [SI: %s]" % cr.get_storage_index_string() - return tag(self._join_pathstring(path), self._html(summary_text)) + return SlotsSequenceElement(tag, problems) @renderer def servers_with_corrupt_shares_p(self, req, tag): From 46cb3b6bc3affdefc98672b54ac23418e486da7f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 13:34:11 -0400 Subject: [PATCH 36/94] Use SlotsSequenceElement to render servers with corrupt shares --- src/allmydata/web/check_results.py | 21 ++++++++++----------- src/allmydata/web/deep-check-results.xhtml | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 748fd0aa3..94c42ded7 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -536,24 +536,23 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): return tag return "" - # TODO: use SlotsSequenceElement to render this. @renderer def servers_with_corrupt_shares(self, ctx, data): servers = [s for (s, storage_index, sharenum) in self.monitor.get_status().get_corrupt_shares()] servers.sort(key=lambda s: s.get_longname()) - return servers - @renderer - def server_problem(self, req, tag): - # def server_problem(self, ctx, server): - # TODO: where do `server` come from now? - data = [server.get_name()] - nickname = server.get_nickname() - if nickname: - data.append(" (%s)" % self._html(nickname)) - return tag(data) + problems = [] + + for server in servers: + name = [server.get_name()] + nick = server.get_nickname() + if nick: + name.append(" (%s)" % self._html(nickname)) + problems.append(name) + + return SlotsSequenceElement(tag, problems) @renderer def corrupt_shares_p(self, req, tag): diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index 11a2e7f4b..5a2cf3542 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -34,8 +34,8 @@

    Servers on which corrupt shares were found

      -
    • -
    • None
    • +
    • +
    • None
    From 4e594b09bf719b6f76d6cb52d9b44faecdc1f644 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 13:59:22 -0400 Subject: [PATCH 37/94] Use SlotsSequenceElement to render corrupt shares --- src/allmydata/web/check_results.py | 26 +++++++++++----------- src/allmydata/web/deep-check-results.xhtml | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 94c42ded7..fde245dbe 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -560,22 +560,22 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): return tag return "" - # TODO: Probably should use SlotsSequenceElement to render this. @renderer def corrupt_shares(self, req, tag): - return self.monitor.get_status().get_corrupt_shares() + shares = self.monitor.get_status().get_corrupt_shares() + problems = [] - @renderer - def share_problem(self, req, tag): - # def render_share_problem(self, req, tag): - server, storage_index, sharenum = data - nickname = server.get_nickname() - tag.fillSlots("serverid", server.get_name()) - if nickname: - tag.fillSlots("nickname", self._html(nickname)) - tag.fillSlots("si", self._render_si_link(ctx, storage_index)) - tag.fillSlots("shnum", str(sharenum)) - return tag + for share in shares: + server, storage_index, sharenum = share + problem = { + "serverid": server.get_name(), + "nickname": self._html(nickname), + "si": self._render_si_link(req, storage_index), + "shnum": str(sharenum), + } + problems.append(problem) + + return SlotsSequenceElement(tag, problems) @renderer def return_to(self, req, tag): diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index 5a2cf3542..2c5c18544 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -43,13 +43,13 @@

    Corrupt Shares

    If repair fails, these shares need to be manually inspected and removed.

    Storage Index Summary
    - + - + From 8094b3144d570da7df6ff8644a3056f523bc1ca6 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 14:23:23 -0400 Subject: [PATCH 38/94] Use SlotsSequenceElement to render all objects --- src/allmydata/web/check_results.py | 33 +++++++++++----------- src/allmydata/web/deep-check-results.xhtml | 4 +-- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index fde245dbe..9189b8da0 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -585,25 +585,24 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): return tags.div(tags.a("Return to file/directory.", href=return_to)) return "" - # TODO: use SlotsSequenceElement to render this. @renderer - def all_objects(self, ctx, data): - r = self.monitor.get_status().get_all_results() - for path in sorted(r.keys()): - yield (path, r[path]) + def all_objects(self, req, tag): + results = self.monitor.get_status().get_all_results() + objects = [] - @renderer - def object(self, ctx, data): - # def render_object(self, ctx, data): - # TODO: figure `data` out - path, r = data - tag.fillSlots("path", self._join_pathstring(path)) - tag.fillSlots("healthy", str(r.is_healthy())) - tag.fillSlots("recoverable", str(r.is_recoverable())) - storage_index = r.get_storage_index() - tag.fillSlots("storage_index", self._render_si_link(req, storage_index)) - tag.fillSlots("summary", self._html(r.get_summary())) - return tag + for path in sorted(results.keys()): + result = results.get(path) + storage_index = result.get_storage_index() + object = { + "path": self._join_pathstring(path), + "healthy": str(result.is_healthy()), + "recoverable": str(result.is_recoverable()), + "storage_index": self._render_si_link(req, storage_index), + "summary": self._html(result.get_summary()), + } + objects.append(object) + + return SlotsSequenceElement(tag, objects) @renderer def runtime(self, req, tag): diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index 2c5c18544..e868c6e4f 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -63,14 +63,14 @@

    All Results

    Server Server Nickname Storage Index Share Number
    - + - + From 33ddac879cfe65e506c735555cd72d2f8f812161 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 12 Jun 2020 17:48:56 -0400 Subject: [PATCH 39/94] Remove unused code --- src/allmydata/web/check_results.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 9189b8da0..248c771a8 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -254,18 +254,12 @@ class LiteralCheckResultsElement(Element): class CheckerBase(object): - # def renderHTTP(self, ctx): - # if self.want_json(ctx): - # return self.json(ctx) - # return rend.Page.renderHTTP(self, ctx) - @renderer def storage_index(self, req, tag): return self.r.get_storage_index_string() @renderer def return_to(self, req, tag): - # req = inevow.IRequest(ctx) return_to = get_arg(req, "return_to", None) if return_to: return tags.div(tags.a("Return to file/directory.", href=return_to)) @@ -440,13 +434,7 @@ class DeepCheckResultsRenderer(MultiFormatResource): elem = DeepCheckResultsRendererElement(self.monitor) return renderElement(req, elem) - # def renderHTTP(self, ctx): - # if self.want_json(ctx): - # return self.json(ctx) - # return rend.Page.renderHTTP(self, ctx) - def render_JSON(self, req): - # inevow.IRequest(ctx).setHeader("content-type", "text/plain") req.setHeader("content-type", "text/plain") data = {} data["finished"] = self.monitor.is_finished() @@ -579,7 +567,6 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): @renderer def return_to(self, req, tag): - # req = inevow.IRequest(ctx) return_to = get_arg(req, "return_to", None) if return_to: return tags.div(tags.a("Return to file/directory.", href=return_to)) @@ -606,7 +593,6 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): @renderer def runtime(self, req, tag): - # req = inevow.IRequest(ctx) runtime = time.time() - req.processing_started_timestamp return tag("runtime: %s seconds" % runtime) @@ -638,13 +624,7 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): elem = DeepCheckAndRepairResultsRendererElement(self.monitor) return renderElement(req, elem) - # def renderHTTP(self, ctx): - # if self.want_json(ctx): - # return self.json(ctx) - # return rend.Page.renderHTTP(self, ctx) - def render_JSON(self, req): - # inevow.IRequest(ctx).setHeader("content-type", "text/plain") req.setHeader("content-type", "text/plain") res = self.monitor.get_status() data = {} @@ -816,7 +796,6 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def return_to(self, req, tag): - # req = inevow.IRequest(ctx) return_to = get_arg(req, "return_to", None) if return_to: return tags.div(tags.a("Return to file/directory.", href=return_to)) @@ -844,6 +823,5 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def runtime(self, req, tag): - # req = inevow.IRequest(ctx) runtime = time.time() - req.processing_started_timestamp return tag("runtime: %s seconds" % runtime) From 2e60408bf62dc510cf6871929a5e102995423df2 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 14:29:33 -0400 Subject: [PATCH 40/94] Wrap counters in tags Unless the rendered page contains strings of the form "Objects Checked: n" etc, test_POST_DIRURL_deepcheck will not pass. --- src/allmydata/web/deep-check-results.xhtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index e868c6e4f..572e44ff1 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -14,11 +14,11 @@

    Counters:

      -
    • Objects Checked:
    • -
    • Objects Healthy:
    • -
    • Objects Unhealthy:
    • -
    • Objects Unrecoverable:
    • -
    • Corrupt Shares:
    • +
    • Objects Checked:
    • +
    • Objects Healthy:
    • +
    • Objects Unhealthy:
    • +
    • Objects Unrecoverable:
    • +
    • Corrupt Shares:
    From 1ee756c8df8e61a332fb70de2e4752b1842fa076 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 14:36:44 -0400 Subject: [PATCH 41/94] Use Beautiful Soup to check for favicon --- src/allmydata/test/web/test_grid.py | 15 ++++++++++++--- src/allmydata/test/web/test_web.py | 9 ++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/allmydata/test/web/test_grid.py b/src/allmydata/test/web/test_grid.py index 2b953b82c..73c354567 100644 --- a/src/allmydata/test/web/test_grid.py +++ b/src/allmydata/test/web/test_grid.py @@ -21,7 +21,12 @@ from allmydata.mutable import publish from .. import common_util as testutil from ..common import WebErrorMixin, ShouldFailMixin from ..no_network import GridTestMixin -from .common import unknown_rwcap, unknown_rocap, unknown_immcap, FAVICON_MARKUP +from .common import ( + assert_soup_has_favicon, + unknown_immcap, + unknown_rocap, + unknown_rwcap, +) DIR_HTML_TAG = '' @@ -92,7 +97,9 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi def _got_html_good(res): self.failUnlessIn("Healthy", res) self.failIfIn("Not Healthy", res) - self.failUnlessIn(FAVICON_MARKUP, res) + soup = BeautifulSoup(res, 'html5lib') + assert_soup_has_favicon(self, soup) + d.addCallback(_got_html_good) d.addCallback(self.CHECK, "good", "t=check&return_to=somewhere") def _got_html_good_return_to(res): @@ -235,7 +242,9 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi self.failUnlessIn("Healthy", res) self.failIfIn("Not Healthy", res) self.failUnlessIn("No repair necessary", res) - self.failUnlessIn(FAVICON_MARKUP, res) + soup = BeautifulSoup(res, 'html5lib') + assert_soup_has_favicon(self, soup) + d.addCallback(_got_html_good) d.addCallback(self.CHECK, "sick", "t=check&repair=true") diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 5be0b2f7b..94e628ef9 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -3080,13 +3080,15 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi res = yield self.get_operation_results(None, "123", "html") self.failUnlessIn("Objects Checked: 11", res) self.failUnlessIn("Objects Healthy: 11", res) - self.failUnlessIn(FAVICON_MARKUP, res) + soup = BeautifulSoup(res, 'html5lib') + assert_soup_has_favicon(self, soup) res = yield self.GET("/operations/123/") # should be the same as without the slash self.failUnlessIn("Objects Checked: 11", res) self.failUnlessIn("Objects Healthy: 11", res) - self.failUnlessIn(FAVICON_MARKUP, res) + soup = BeautifulSoup(res, 'html5lib') + assert_soup_has_favicon(self, soup) yield self.shouldFail2(error.Error, "one", "404 Not Found", "No detailed results for SI bogus", @@ -3136,7 +3138,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi self.failUnlessIn("Objects Unhealthy (after repair): 0", res) self.failUnlessIn("Corrupt Shares (after repair): 0", res) - self.failUnlessIn(FAVICON_MARKUP, res) + soup = BeautifulSoup(res, 'html5lib') + assert_soup_has_favicon(self, soup) d.addCallback(_check_html) return d From de999126413b4e782d521c3e09373d20f01ef13a Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 15:17:43 -0400 Subject: [PATCH 42/94] Replace `ctx` references with `req` references --- src/allmydata/web/check_results.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 248c771a8..af12fa370 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -203,18 +203,17 @@ class ResultsBase(object): assert isinstance(s, (list, tuple)) return [html.escape(w) for w in s] - def want_json(self, ctx): - output = get_arg(inevow.IRequest(ctx), "output", "").lower() + def want_json(self, req): + output = get_arg(req, "output", "").lower() if output.lower() == "json": return True return False - def _render_si_link(self, ctx, storage_index): + def _render_si_link(self, req, storage_index): si_s = base32.b2a(storage_index) - req = inevow.IRequest(ctx) ophandle = req.prepath[-1] - target = "%s/operations/%s/%s" % (get_root(ctx), ophandle, si_s) - output = get_arg(ctx, "output") + target = "%s/operations/%s/%s" % (get_root(req), ophandle, si_s) + output = get_arg(req, "output") if output: target = target + "?output=%s" % output return tags.a(si_s, href=target) From cdb3747470afd707349dd52723fb87dca5097728 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 15:18:11 -0400 Subject: [PATCH 43/94] Remove nevow imports --- src/allmydata/web/check_results.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index af12fa370..c109966e5 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -1,11 +1,7 @@ import time import json -from nevow import ( - rend, - inevow, - tags as T, -) + from twisted.web import ( http, html, From fae4f44404ff81f182ee4f848b963fdceacd837c Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 22:27:22 -0400 Subject: [PATCH 44/94] Prefer `req` to `ctx` --- src/allmydata/web/check_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index c109966e5..4051e1379 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -98,7 +98,7 @@ class ResultsBase(object): pathstring = "" return pathstring - def _render_results(self, ctx, cr): + def _render_results(self, req, cr): assert ICheckResults(cr) c = self.client sb = c.get_storage_broker() From 23a51117d2fea008beeaef0ca7d96922d904b834 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 15 Jun 2020 22:28:28 -0400 Subject: [PATCH 45/94] Rename `r` to `check_results` --- src/allmydata/web/check_results.py | 51 ++++++++++++++---------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 4051e1379..ffd366385 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -251,7 +251,7 @@ class CheckerBase(object): @renderer def storage_index(self, req, tag): - return self.r.get_storage_index_string() + return self.check_results.get_storage_index_string() @renderer def return_to(self, req, tag): @@ -273,14 +273,14 @@ class CheckResultsRenderer(MultiFormatResource): """ super(CheckResultsRenderer, self).__init__() self.client = client - self.r = ICheckResults(results) + self.check_results = ICheckResults(results) def render_HTML(self, req): - return renderElement(req, CheckResultsRendererElement(self.client, self.r)) + return renderElement(req, CheckResultsRendererElement(self.client, self.check_results)) def render_JSON(self, req): req.setHeader("content-type", "text/plain") - data = json_check_results(self.r) + data = json_check_results(self.check_results) return json.dumps(data, indent=1) + "\n" @@ -288,31 +288,27 @@ class CheckResultsRendererElement(Element, CheckerBase, ResultsBase): loader = XMLFile(FilePath(__file__).sibling("check-results.xhtml")) - def __init__(self, client, r): + def __init__(self, client, results): super(CheckResultsRendererElement, self).__init__() self.client = client - # TODO: use a better name - self.r = r + self.check_results = results @renderer def summary(self, req, tag): results = [] - # XXX: how did `data` get populated? - if self.r.is_healthy(): + if self.check_results.is_healthy(): results.append("Healthy") - elif self.r.is_recoverable(): + elif self.check_results.is_recoverable(): results.append("Not Healthy!") else: results.append("Not Recoverable!") results.append(" : ") - # TODO: what is self._html? - # results.append(self._html(self.r.get_summary())) - results.append(self.r.get_summary()) + results.append(self._html(self.check_results.get_summary())) return tag(results) @renderer def repair(self, req, tag): - if self.r.is_healthy(): + if self.check_results.is_healthy(): return "" #repair = T.form(action=".", method="post", @@ -329,10 +325,9 @@ class CheckResultsRendererElement(Element, CheckerBase, ResultsBase): @renderer def results(self, req, tag): - cr = self._render_results(req, self.r) + cr = self._render_results(req, self.check_results) return tag(cr) - class CheckAndRepairResultsRenderer(MultiFormatResource): formatArgument = "output" @@ -342,17 +337,17 @@ class CheckAndRepairResultsRenderer(MultiFormatResource): super(CheckAndRepairResultsRenderer, self).__init__() self.client = client # TODO: use a better name - self.r = None + self.check_results = None if results: - self.r = ICheckAndRepairResults(results) + self.check_results = ICheckAndRepairResults(results) def render_HTML(self, req): - elem = CheckAndRepairResultsRendererElement(self.client, self.r) + elem = CheckAndRepairResultsRendererElement(self.client, self.check_results) return renderElement(req, elem) def render_JSON(self, req): req.setHeader("content-type", "text/plain") - data = json_check_and_repair_results(self.r) + data = json_check_and_repair_results(self.check_results) return json.dumps(data, indent=1) + "\n" @@ -360,14 +355,14 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): loader = XMLFile(FilePath(__file__).sibling("check-and-repair-results.xhtml")) - def __init__(self, client, r): + def __init__(self, client, results): super(CheckAndRepairResultsRendererElement, self).__init__() self.client = client - self.r = r + self.check_results = results @renderer def summary(self, req, tag): - cr = self.r.get_post_repair_results() + cr = self.check_results.get_post_repair_results() results = [] if cr.is_healthy(): results.append("Healthy") @@ -381,8 +376,8 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): @renderer def repair_results(self, req, tag): - if self.r.get_repair_attempted(): - if self.r.get_repair_successful(): + if self.check_results.get_repair_attempted(): + if self.check_results.get_repair_successful(): return tag("Repair successful") else: return tag("Repair unsuccessful") @@ -390,13 +385,13 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): @renderer def post_repair_results(self, req, tag): - cr = self._render_results(req, self.r.get_post_repair_results()) + cr = self._render_results(req, self.check_results.get_post_repair_results()) return tag(tags.div("Post-Repair Checker Results:"), cr) @renderer def maybe_pre_repair_results(self, req, tag): - if self.r.get_repair_attempted(): - cr = self._render_results(req, self.r.get_pre_repair_results()) + if self.check_results.get_repair_attempted(): + cr = self._render_results(req, self.check_results.get_pre_repair_results()) return tag(tags.div("Pre-Repair Checker Results:"), cr) return "" From 79be7955660fe3e7283c1063d48571f17f78c745 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 16 Jun 2020 07:12:08 -0400 Subject: [PATCH 46/94] Remove unused want_json() --- src/allmydata/web/check_results.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index ffd366385..f675c53bb 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -199,12 +199,6 @@ class ResultsBase(object): assert isinstance(s, (list, tuple)) return [html.escape(w) for w in s] - def want_json(self, req): - output = get_arg(req, "output", "").lower() - if output.lower() == "json": - return True - return False - def _render_si_link(self, req, storage_index): si_s = base32.b2a(storage_index) ophandle = req.prepath[-1] From 22977352bb6669355d20f72ac7ed269231596075 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 16 Jun 2020 09:40:37 -0400 Subject: [PATCH 47/94] Document parameters of ResultsRenderer classes --- src/allmydata/web/check_results.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index f675c53bb..8a3ecc61b 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -214,6 +214,9 @@ class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase): formatArgument = "output" def __init__(self, client): + """ + :param allmydata.interfaces.IStatsProducer client: stats provider. + """ super(LiteralCheckResultsRenderer, self).__init__() self.client = client @@ -261,9 +264,8 @@ class CheckResultsRenderer(MultiFormatResource): def __init__(self, client, results): """ - TODO: update these with the correct values. - :param allmydata.test.no_network._NoNetworkClient client: - :param allmydata.check_results.CheckResults 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 @@ -327,10 +329,12 @@ class CheckAndRepairResultsRenderer(MultiFormatResource): formatArgument = "output" def __init__(self, client, results): - # TODO: document params + """ + :param allmydata.interfaces.IStatsProducer client: stats provider. + :param allmydata.interfaces.ICheckResults results: check/verify results. + """ super(CheckAndRepairResultsRenderer, self).__init__() self.client = client - # TODO: use a better name self.check_results = None if results: self.check_results = ICheckAndRepairResults(results) @@ -395,7 +399,10 @@ class DeepCheckResultsRenderer(MultiFormatResource): formatArgument = "output" def __init__(self, client, monitor): - # TODO: document params + """ + :param allmydata.interfaces.IStatsProducer client: stats provider. + :param allmydata.monitor.IMonitor monitor: status, progress, and cancellation provider. + """ super(DeepCheckResultsRenderer, self).__init__() self.client = client self.monitor = monitor @@ -586,6 +593,10 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): formatArgument = "output" def __init__(self, client, monitor): + """ + :param allmydata.interfaces.IStatsProducer client: stats provider. + :param allmydata.monitor.IMonitor monitor: status, progress, and cancellation provider. + """ self.client = client self.monitor = monitor self.children = {} @@ -656,7 +667,6 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin loader = XMLFile(FilePath(__file__).sibling("deep-check-and-repair-results.xhtml")) def __init__(self, monitor): - # TODO: document params super(DeepCheckAndRepairResultsRendererElement, self).__init__() self.monitor = monitor From fe1b0cb7ed3801b709e5491ccd77ef6627ce0f53 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 17:51:04 -0400 Subject: [PATCH 48/94] Rename class variables --- src/allmydata/web/check_results.py | 64 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 8a3ecc61b..db6b28796 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -88,7 +88,7 @@ def json_check_and_repair_results(r): return data class ResultsBase(object): - # self.client must point to the Client, so we can get nicknames and + # self._client must point to the Client, so we can get nicknames and # determine the permuted peer order def _join_pathstring(self, path): @@ -100,7 +100,7 @@ class ResultsBase(object): def _render_results(self, req, cr): assert ICheckResults(cr) - c = self.client + c = self._client sb = c.get_storage_broker() r = [] def add(name, value): @@ -218,7 +218,7 @@ class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase): :param allmydata.interfaces.IStatsProducer client: stats provider. """ super(LiteralCheckResultsRenderer, self).__init__() - self.client = client + self._client = client def render_HTML(self, req): return renderElement(req, LiteralCheckResultsElement()) @@ -248,7 +248,7 @@ class CheckerBase(object): @renderer def storage_index(self, req, tag): - return self.check_results.get_storage_index_string() + return self._results.get_storage_index_string() @renderer def return_to(self, req, tag): @@ -268,15 +268,15 @@ class CheckResultsRenderer(MultiFormatResource): :param allmydata.interfaces.ICheckResults results: results of check/vefify operation. """ super(CheckResultsRenderer, self).__init__() - self.client = client - self.check_results = ICheckResults(results) + self._client = client + self._results = ICheckResults(results) def render_HTML(self, req): - return renderElement(req, CheckResultsRendererElement(self.client, self.check_results)) + return renderElement(req, CheckResultsRendererElement(self._client, self._results)) def render_JSON(self, req): req.setHeader("content-type", "text/plain") - data = json_check_results(self.check_results) + data = json_check_results(self._results) return json.dumps(data, indent=1) + "\n" @@ -286,25 +286,25 @@ class CheckResultsRendererElement(Element, CheckerBase, ResultsBase): def __init__(self, client, results): super(CheckResultsRendererElement, self).__init__() - self.client = client - self.check_results = results + self._client = client + self._results = results @renderer def summary(self, req, tag): results = [] - if self.check_results.is_healthy(): + if self._results.is_healthy(): results.append("Healthy") - elif self.check_results.is_recoverable(): + elif self._results.is_recoverable(): results.append("Not Healthy!") else: results.append("Not Recoverable!") results.append(" : ") - results.append(self._html(self.check_results.get_summary())) + results.append(self._html(self._results.get_summary())) return tag(results) @renderer def repair(self, req, tag): - if self.check_results.is_healthy(): + if self._results.is_healthy(): return "" #repair = T.form(action=".", method="post", @@ -321,7 +321,7 @@ class CheckResultsRendererElement(Element, CheckerBase, ResultsBase): @renderer def results(self, req, tag): - cr = self._render_results(req, self.check_results) + cr = self._render_results(req, self._results) return tag(cr) class CheckAndRepairResultsRenderer(MultiFormatResource): @@ -334,18 +334,18 @@ class CheckAndRepairResultsRenderer(MultiFormatResource): :param allmydata.interfaces.ICheckResults results: check/verify results. """ super(CheckAndRepairResultsRenderer, self).__init__() - self.client = client - self.check_results = None + self._client = client + self._results = None if results: - self.check_results = ICheckAndRepairResults(results) + self._results = ICheckAndRepairResults(results) def render_HTML(self, req): - elem = CheckAndRepairResultsRendererElement(self.client, self.check_results) + elem = CheckAndRepairResultsRendererElement(self._client, self._results) return renderElement(req, elem) def render_JSON(self, req): req.setHeader("content-type", "text/plain") - data = json_check_and_repair_results(self.check_results) + data = json_check_and_repair_results(self._results) return json.dumps(data, indent=1) + "\n" @@ -355,12 +355,12 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): def __init__(self, client, results): super(CheckAndRepairResultsRendererElement, self).__init__() - self.client = client - self.check_results = results + self._client = client + self._results = results @renderer def summary(self, req, tag): - cr = self.check_results.get_post_repair_results() + cr = self._results.get_post_repair_results() results = [] if cr.is_healthy(): results.append("Healthy") @@ -374,8 +374,8 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): @renderer def repair_results(self, req, tag): - if self.check_results.get_repair_attempted(): - if self.check_results.get_repair_successful(): + if self._results.get_repair_attempted(): + if self._results.get_repair_successful(): return tag("Repair successful") else: return tag("Repair unsuccessful") @@ -383,13 +383,13 @@ class CheckAndRepairResultsRendererElement(Element, CheckerBase, ResultsBase): @renderer def post_repair_results(self, req, tag): - cr = self._render_results(req, self.check_results.get_post_repair_results()) + cr = self._render_results(req, self._results.get_post_repair_results()) return tag(tags.div("Post-Repair Checker Results:"), cr) @renderer def maybe_pre_repair_results(self, req, tag): - if self.check_results.get_repair_attempted(): - cr = self._render_results(req, self.check_results.get_pre_repair_results()) + if self._results.get_repair_attempted(): + cr = self._render_results(req, self._results.get_pre_repair_results()) return tag(tags.div("Pre-Repair Checker Results:"), cr) return "" @@ -404,7 +404,7 @@ class DeepCheckResultsRenderer(MultiFormatResource): :param allmydata.monitor.IMonitor monitor: status, progress, and cancellation provider. """ super(DeepCheckResultsRenderer, self).__init__() - self.client = client + self._client = client self.monitor = monitor def getChild(self, name, req): @@ -415,7 +415,7 @@ class DeepCheckResultsRenderer(MultiFormatResource): si = base32.a2b(name) r = self.monitor.get_status() try: - return CheckResultsRenderer(self.client, + return CheckResultsRenderer(self._client, r.get_results_for_storage_index(si)) except KeyError: raise WebError("No detailed results for SI %s" % html.escape(name), @@ -597,7 +597,7 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): :param allmydata.interfaces.IStatsProducer client: stats provider. :param allmydata.monitor.IMonitor monitor: status, progress, and cancellation provider. """ - self.client = client + self._client = client self.monitor = monitor self.children = {} @@ -610,7 +610,7 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): s = self.monitor.get_status() try: results = s.get_results_for_storage_index(si) - return CheckAndRepairResultsRenderer(self.client, results) + return CheckAndRepairResultsRenderer(self._client, results) except KeyError: raise WebError("No detailed results for SI %s" % html.escape(name), http.NOT_FOUND) From c8d218fb9f10831faceff6019c3143b7f047eb79 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 18:09:21 -0400 Subject: [PATCH 49/94] Add newsfragment --- newsfragments/3316.minor | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/3316.minor diff --git a/newsfragments/3316.minor b/newsfragments/3316.minor new file mode 100644 index 000000000..9457b486e --- /dev/null +++ b/newsfragments/3316.minor @@ -0,0 +1 @@ +Port checker result pages' rendering from nevow to twisted web templates. From 47f9147e165f860ed0b6b8897f5992faf6e4e01c Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 21:32:49 -0400 Subject: [PATCH 50/94] Rename a class for consistency's sake --- src/allmydata/test/test_checker.py | 2 +- src/allmydata/web/check_results.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 75619beaa..ba212c65e 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -83,7 +83,7 @@ class WebResultsRendering(unittest.TestCase): return unittest.TestCase().successResultOf(d) def test_literal(self): - lcr = web_check_results.LiteralCheckResultsElement() + lcr = web_check_results.LiteralCheckResultsRendererElement() html = self.render_element(lcr) s = self.remove_tags(html) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index db6b28796..89bc41b79 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -221,7 +221,7 @@ class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase): self._client = client def render_HTML(self, req): - return renderElement(req, LiteralCheckResultsElement()) + return renderElement(req, LiteralCheckResultsRendererElement()) def render_JSON(self, req): req.setHeader("content-type", "text/plain") @@ -229,12 +229,12 @@ class LiteralCheckResultsRenderer(MultiFormatResource, ResultsBase): return json.dumps(data, indent=1) + "\n" -class LiteralCheckResultsElement(Element): +class LiteralCheckResultsRendererElement(Element): loader = XMLFile(FilePath(__file__).sibling("literal-check-results.xhtml")) def __init__(self): - super(LiteralCheckResultsElement, self).__init__() + super(LiteralCheckResultsRendererElement, self).__init__() @renderer def return_to(self, req, tag): From c3440ce21312da568e51fd1d529c3dd5f185bcef Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 21:53:11 -0400 Subject: [PATCH 51/94] Use a helper to query monitor counters --- src/allmydata/web/check_results.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 89bc41b79..290d9a1c4 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -457,31 +457,32 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): super(DeepCheckResultsRendererElement, self).__init__() self.monitor = monitor - # TODO: Add a helper to query monitor.get_status.get_counters() - @renderer def root_storage_index(self, req, tag): return self.monitor.get_status().get_root_storage_index_string() + def _get_monitor_counter(self, name): + return self.monitor.get_status().get_counters().get(name) + @renderer def objects_checked(self, req, tag): - return str(self.monitor.get_status().get_counters()["count-objects-checked"]) + return str(self._get_monitor_counter("count-objects-checked")) @renderer def objects_healthy(self, req, tag): - return str(self.monitor.get_status().get_counters()["count-objects-healthy"]) + return str(self._get_monitor_counter("count-objects-healthy")) @renderer def objects_unhealthy(self, req, tag): - return str(self.monitor.get_status().get_counters()["count-objects-unhealthy"]) + return str(self._get_monitor_counter("count-objects-unhealthy")) @renderer def objects_unrecoverable(self, req, tag): - return str(self.monitor.get_status().get_counters()["count-objects-unrecoverable"]) + return str(self._get_monitor_counter("count-objects-unrecoverable")) @renderer def count_corrupt_shares(self, req, tag): - return str(self.monitor.get_status().get_counters()["count-corrupt-shares"]) + return str(self._get_monitor_counter("count-corrupt-shares")) @renderer def problems_p(self, req, tag): @@ -511,7 +512,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): @renderer def servers_with_corrupt_shares_p(self, req, tag): - if self.monitor.get_status().get_counters()["count-corrupt-shares"]: + if self._get_monitor_counter("count-corrupt-shares"): return tag return "" @@ -535,7 +536,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): @renderer def corrupt_shares_p(self, req, tag): - if self.monitor.get_status().get_counters()["count-corrupt-shares"]: + if self._get_monitor_counter("count-corrupt-shares"): return tag return "" From 0483fc91c1dd5045f6f6797a0bf27b3aa22d2b8e Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 21:56:42 -0400 Subject: [PATCH 52/94] Remove unused import --- src/allmydata/web/check_results.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 290d9a1c4..8fdfece7c 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -15,7 +15,6 @@ from twisted.web.template import ( tags, ) from allmydata.web.common import ( - getxmlfile, get_arg, get_root, WebError, From 4f213e93bed80a5cdfdc04232dd06f0886c90f2b Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 21:57:14 -0400 Subject: [PATCH 53/94] Remove an unused variable --- src/allmydata/web/check_results.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 8fdfece7c..fdb6a39d5 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -114,8 +114,6 @@ class ResultsBase(object): add("Happiness Level", str(cr.get_happiness())) add("Hosts with good shares", str(cr.get_host_counter_good_shares())) - rrr = cr.get_corrupt_shares() - if cr.get_corrupt_shares(): badsharemap = [] for (s, si, shnum) in cr.get_corrupt_shares(): From e121cc0a84d126d38416b3f6a7e8fb1f8c761bb1 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 22:01:21 -0400 Subject: [PATCH 54/94] Use the right names Missed this in testing because this code was not reached in testing. --- src/allmydata/web/check_results.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index fdb6a39d5..811885ec3 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -514,7 +514,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): return "" @renderer - def servers_with_corrupt_shares(self, ctx, data): + def servers_with_corrupt_shares(self, req, tag): servers = [s for (s, storage_index, sharenum) in self.monitor.get_status().get_corrupt_shares()] @@ -524,8 +524,8 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): for server in servers: name = [server.get_name()] - nick = server.get_nickname() - if nick: + nickname = server.get_nickname() + if nickname: name.append(" (%s)" % self._html(nickname)) problems.append(name) @@ -544,6 +544,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): for share in shares: server, storage_index, sharenum = share + nickname = server.get_nickname() problem = { "serverid": server.get_name(), "nickname": self._html(nickname), From dfb477a2b14f0683063d228d0e1859e445e5e4a5 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 17 Jun 2020 22:02:15 -0400 Subject: [PATCH 55/94] Remove unused import --- src/allmydata/test/test_checker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index ba212c65e..bd1bd87c6 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -20,7 +20,6 @@ from allmydata.storage.server import storage_index_to_dir from allmydata.monitor import Monitor from allmydata.test.no_network import GridTestMixin from allmydata.immutable.upload import Data -from allmydata.test.common_web import WebRenderingMixin from allmydata.mutable.publish import MutableData from .common import ( From 427f99621f2e77fb688f50e066b0e3a7496668e2 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 10 Jul 2020 06:56:29 -0400 Subject: [PATCH 56/94] Do more null checks --- src/allmydata/web/check_results.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 811885ec3..c9c429622 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -671,9 +671,13 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def root_storage_index(self, req, tag): + if not self.monitor.get_status(): + return "" return self.monitor.get_status().get_root_storage_index_string() def _get_counter(self, name): + if not self.monitor.get_status(): + return "" return str(self.monitor.get_status().get_counters().get(name)) @renderer @@ -718,8 +722,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def pre_repair_problems_p(self, req, tag): - c = self.monitor.get_status().get_counters() - if c["count-objects-unhealthy-pre-repair"]: + if self._get_counter("count-objects-unhealthy-pre-repair"): return tag return "" @@ -740,9 +743,8 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def post_repair_problems_p(self, req, tag): - c = self.monitor.get_status().get_counters() - if (c["count-objects-unhealthy-post-repair"] - or c["count-corrupt-shares-post-repair"]): + if (self._get_counter("count-objects-unhealthy-post-repair") + or self._get_counter("count-corrupt-shares-post-repair")): return tag return "" @@ -763,7 +765,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def servers_with_corrupt_shares_p(self, req, tag): - if self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"]: + if self._get_counter("count-corrupt-shares-pre-repair"): return tag return "" @@ -776,7 +778,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def remaining_corrupt_shares_p(self, req, tag): - if self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"]: + if self._get_counter("count-corrupt-shares-post-repair"): return tag return "" @@ -796,7 +798,9 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def all_objects(self, req, tag): - results = self.monitor.get_status().get_all_results() + results = {} + if self.monitor.get_status(): + results = self.monitor.get_status().get_all_results() objects = [] for path in sorted(results.keys()): @@ -816,5 +820,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin @renderer def runtime(self, req, tag): - runtime = time.time() - req.processing_started_timestamp + runtime = 'unknown' + if hasattr(req, 'processing_started_timestamp'): + runtime = time.time() - req.processing_started_timestamp return tag("runtime: %s seconds" % runtime) From f9dc2509deed10b0ec516b14595e7fa00f81a63d Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 10 Jul 2020 08:00:04 -0400 Subject: [PATCH 57/94] Test "deep check and repair" page --- src/allmydata/test/test_checker.py | 105 +++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index bd1bd87c6..b4d904180 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -2,6 +2,9 @@ import re import json import os.path, shutil + +from bs4 import BeautifulSoup + from twisted.trial import unittest from twisted.internet import defer @@ -26,6 +29,11 @@ from .common import ( EMPTY_CLIENT_CONFIG, ) +from .web.common import ( + assert_soup_has_favicon, + assert_soup_has_tag_with_content, +) + class FakeClient(object): def get_storage_broker(self): return self.storage_broker @@ -40,6 +48,7 @@ class TestRequest(object, Request): Request.__init__(self, DummyChannel()) self.args = args or {} self.fields = fields or {} + self.prepath = [b""] class WebResultsRendering(unittest.TestCase): @@ -336,6 +345,102 @@ class WebResultsRendering(unittest.TestCase): self.failUnlessEqual(j["storage-index"], "") _got_lit_results(d) + def test_deep_check_and_repair_renderer(self): + monitor = Monitor() + status = check_results.DeepCheckAndRepairResults("") + monitor.set_status(status) + elem = web_check_results.DeepCheckAndRepairResultsRendererElement(monitor) + doc = self.render_element(elem) + soup = BeautifulSoup(doc, 'html5lib') + + assert_soup_has_favicon(self, soup) + + assert_soup_has_tag_with_content( + self, soup, u"title", + u"Tahoe-LAFS - Deep Check Results" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h1", + u"Deep-Check-And-Repair Results for root SI=" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Checked: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Healthy (before repair): 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Unhealthy (before repair): 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Corrupt Shares (before repair): 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Repairs Attempted: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Repairs Attempted: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Repairs Successful: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + "Repairs Unsuccessful: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Healthy (after repair): 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Unhealthy (after repair): 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Corrupt Shares (after repair): 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"Files/Directories That Had Problems:" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"Files/Directories That Still Have Problems:" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"Servers on which corrupt shares were found" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"Remaining Corrupt Shares" + ) + + class BalancingAct(GridTestMixin, unittest.TestCase): # test for #1115 regarding the 'count-good-share-hosts' metric From 267b89ae5be0ee0e5659a397b72c19eccf5afe25 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 10 Jul 2020 16:34:16 -0400 Subject: [PATCH 58/94] Remove unused import --- src/allmydata/test/web/test_web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 822603ade..59a9d6ec6 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -65,7 +65,7 @@ from ..common_web import ( Error, ) from allmydata.client import _Client, SecretHolder -from .common import unknown_rwcap, unknown_rocap, unknown_immcap, FAVICON_MARKUP +from .common import unknown_rwcap, unknown_rocap, unknown_immcap # create a fake uploader/downloader, and a couple of fake dirnodes, then # create a webserver that works against them From b49a3eaf3a81f2812d88e2e8e22f81c1e1a11fcc Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 10 Jul 2020 21:46:23 -0400 Subject: [PATCH 59/94] Check for a non-standard property on request --- src/allmydata/web/check_results.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index c9c429622..a29ddb188 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -583,7 +583,9 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): @renderer def runtime(self, req, tag): - runtime = time.time() - req.processing_started_timestamp + runtime = 'unknown' + if hasattr(req, 'processing_started_timestamp'): + runtime = time.time() - req.processing_started_timestamp return tag("runtime: %s seconds" % runtime) From 6eba2fb2e6a122105a8f50f73e5addb64665f9a4 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Sat, 11 Jul 2020 07:59:28 -0400 Subject: [PATCH 60/94] Test "deep check results" page These are some basic tests -- let's see if this improves test coverage, and by how much. --- src/allmydata/test/test_checker.py | 47 ++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index b4d904180..4c1313793 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -345,6 +345,53 @@ class WebResultsRendering(unittest.TestCase): self.failUnlessEqual(j["storage-index"], "") _got_lit_results(d) + def test_deep_check_renderer(self): + monitor = Monitor() + status = check_results.DeepCheckResults("") + monitor.set_status(status) + + elem = web_check_results.DeepCheckResultsRendererElement(monitor) + + doc = self.render_element(elem) + soup = BeautifulSoup(doc, 'html5lib') + + assert_soup_has_favicon(self, soup) + + assert_soup_has_tag_with_content( + self, soup, u"title", + u"Tahoe-LAFS - Deep Check Results" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h1", + "Deep-Check Results for root SI=" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Checked: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Healthy: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Unhealthy: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Objects Unrecoverable: 0" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"Corrupt Shares: 0" + ) + def test_deep_check_and_repair_renderer(self): monitor = Monitor() status = check_results.DeepCheckAndRepairResults("") From b4b820ed7bba5af9beb835313120c8d2f69e1630 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 14 Jul 2020 20:47:23 -0400 Subject: [PATCH 61/94] Use a helper method to get monitor counter --- src/allmydata/web/check_results.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index a29ddb188..256274141 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -483,8 +483,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): @renderer def problems_p(self, req, tag): - c = self.monitor.get_status().get_counters() - if c["count-objects-unhealthy"]: + if self._get_monitor_counter("count-objects-unhealthy"): return tag return "" From ef26d66e79883a2fa848b1fac6b399c2aa61f240 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 14 Jul 2020 20:50:12 -0400 Subject: [PATCH 62/94] Use correct twisted web template directives --- src/allmydata/web/deep-check-results.xhtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index 572e44ff1..abe9bfd63 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -25,8 +25,8 @@

    Files/Directories That Had Problems:

      -
    • -
    • None
    • +
    • +
    • None
    From 99ce9ff198961b48486a7428850de1690ecafd8d Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 14 Jul 2020 21:42:28 -0400 Subject: [PATCH 63/94] Correct argument to SlotsSequenceElement --- src/allmydata/web/check_results.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 256274141..c8165a67b 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -501,8 +501,11 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): if summary: summary_text = ": " + summary summary_text += " [SI: %s]" % cr.get_storage_index_string() - problem = self._join_pathstring(path), ":", self._html(summary_text) - problems.append(problem) + problems.append({ + # TODO: result of _join_pathstring() seems to be + # weird. Might as well use "path" as the key here? + self._join_pathstring(path): self._html(summary_text) + }) return SlotsSequenceElement(tag, problems) From 6322404b1e7735423e0992355896756c64f901a6 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 14 Jul 2020 21:44:41 -0400 Subject: [PATCH 64/94] Update "deep check results renderer" test --- src/allmydata/test/test_checker.py | 74 +++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 4c1313793..e043a5675 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -16,6 +16,10 @@ from twisted.web.template import flattenString from allmydata import check_results, uri from allmydata import uri as tahoe_uri +from allmydata.interfaces import ( + ICheckResults, + IDeepCheckResults, +) from allmydata.util import base32 from allmydata.web import check_results as web_check_results from allmydata.storage_client import StorageFarmBroker, NativeStorageServer @@ -49,6 +53,61 @@ class TestRequest(object, Request): self.args = args or {} self.fields = fields or {} self.prepath = [b""] + self.postpath = [b""] + + +@implementer(ICheckResults) +class FakeResults(object): + + def __init__(self): + # TODO: figure out a good enough fake SI. + self._storage_index = "fake-si" + + def get_storage_index(self): + return self._storage_index + + def get_storage_index_string(self): + return base32.b2a(self._storage_index) + + def is_healthy(self): + return False + + def is_recoverable(self): + return False + + def get_summary(self): + return "A fake summary" + + +@implementer(IDeepCheckResults) +class FakeDeepCheckResults(object): + + def get_root_storage_index_string(self): + # TODO: figure out a good enough fake root SI. + return "fake-root-si" + + def get_counters(self): + return { + "count-objects-checked": 4, + "count-objects-healthy": 1, + "count-objects-unhealthy": 1, + "count-objects-unrecoverable": 1, + "count-corrupt-shares": 1, + } + + def get_all_results(self): + return { + # TODO: fill this, perhaps with one each of healthy, + # unhealthy, unrecoverable, corrupt. + "fake-result-path": FakeResults() + } + + def get_corrupt_shares(self): + return [ + # TODO: fill this with: + # (IServer, storage_index, sharenum) + ] + class WebResultsRendering(unittest.TestCase): @@ -345,10 +404,11 @@ class WebResultsRendering(unittest.TestCase): self.failUnlessEqual(j["storage-index"], "") _got_lit_results(d) + def test_deep_check_renderer(self): monitor = Monitor() - status = check_results.DeepCheckResults("") - monitor.set_status(status) + result = FakeDeepCheckResults() + monitor.set_status(result) elem = web_check_results.DeepCheckResultsRendererElement(monitor) @@ -369,27 +429,27 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Checked: 0" + u"Objects Checked: 4" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Healthy: 0" + u"Objects Healthy: 1" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unhealthy: 0" + u"Objects Unhealthy: 1" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unrecoverable: 0" + u"Objects Unrecoverable: 1" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Corrupt Shares: 0" + u"Corrupt Shares: 1" ) def test_deep_check_and_repair_renderer(self): From 1ed722494ca871bbb95e020b4d70b12cb19f46b6 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 10:35:56 -0400 Subject: [PATCH 65/94] Add a FakeServer class for testing --- src/allmydata/test/test_checker.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index e043a5675..f975a9a5a 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -17,6 +17,7 @@ from twisted.web.template import flattenString from allmydata import check_results, uri from allmydata import uri as tahoe_uri from allmydata.interfaces import ( + IServer, ICheckResults, IDeepCheckResults, ) @@ -56,6 +57,19 @@ class TestRequest(object, Request): self.postpath = [b""] +@implementer(IServer) +class FakeServer(object): + + def get_name(self): + return "fake name" + + def get_longname(self): + return "fake longname" + + def get_nickname(self): + return "fake nickname" + + @implementer(ICheckResults) class FakeResults(object): @@ -78,7 +92,11 @@ class FakeResults(object): def get_summary(self): return "A fake summary" + def get_corrupt_shares(self): + return (FakeServer(), None, None) + +# TODO: maybe use check_results.DeepCheckResults? @implementer(IDeepCheckResults) class FakeDeepCheckResults(object): @@ -106,6 +124,7 @@ class FakeDeepCheckResults(object): return [ # TODO: fill this with: # (IServer, storage_index, sharenum) + FakeResults().get_corrupt_shares(), ] From ddfe20c8ab90543202b65e9eab6a9ceae0873852 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 13:24:20 -0400 Subject: [PATCH 66/94] Use SlotsSequenceElement correctly --- src/allmydata/web/check_results.py | 8 ++++---- src/allmydata/web/deep-check-results.xhtml | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index c8165a67b..f459b1f50 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -502,9 +502,9 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): summary_text = ": " + summary summary_text += " [SI: %s]" % cr.get_storage_index_string() problems.append({ - # TODO: result of _join_pathstring() seems to be - # weird. Might as well use "path" as the key here? - self._join_pathstring(path): self._html(summary_text) + # Not sure self._join_pathstring(path) is the + # right thing to use here. + "problem": self._html(path) + self._html(summary_text), }) return SlotsSequenceElement(tag, problems) @@ -529,7 +529,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): nickname = server.get_nickname() if nickname: name.append(" (%s)" % self._html(nickname)) - problems.append(name) + problems.append({"problem": name}) return SlotsSequenceElement(tag, problems) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index abe9bfd63..f3b2e7d75 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -25,7 +25,9 @@

    Files/Directories That Had Problems:

      -
    • +
    • + +
    • None
    @@ -34,7 +36,9 @@

    Servers on which corrupt shares were found

      -
    • +
    • + +
    • None
    From f547509332854f36de67a8d07379c51d1a4aa8ba Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 13:24:32 -0400 Subject: [PATCH 67/94] Use table headers correctly --- src/allmydata/web/deep-check-results.xhtml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index f3b2e7d75..542bfdd75 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -47,11 +47,11 @@

    Corrupt Shares

    If repair fails, these shares need to be manually inspected and removed.

    Relative Path Healthy Recoverable Storage Index Summary
    - - - - - + + + + + @@ -67,12 +67,12 @@

    All Results

    ServerServer NicknameStorage IndexShare Number
    ServerServer NicknameStorage IndexShare Number
    - - - - - - + + + + + + From 3c079bef7365ef43351b193d2ea39528a8c14caf Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 13:35:22 -0400 Subject: [PATCH 68/94] Expand "deep check results" test --- src/allmydata/test/test_checker.py | 33 ++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index f975a9a5a..538ba0640 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -90,10 +90,11 @@ class FakeResults(object): return False def get_summary(self): - return "A fake summary" + return "fake summary results" def get_corrupt_shares(self): - return (FakeServer(), None, None) + # Returns (IServer, storage_index, sharenum) + return (FakeServer(), "fake-si", 0) # TODO: maybe use check_results.DeepCheckResults? @@ -121,11 +122,8 @@ class FakeDeepCheckResults(object): } def get_corrupt_shares(self): - return [ - # TODO: fill this with: - # (IServer, storage_index, sharenum) - FakeResults().get_corrupt_shares(), - ] + # Returns a set of (IServer, storage_index, sharenum) + return { FakeResults().get_corrupt_shares() } class WebResultsRendering(unittest.TestCase): @@ -430,7 +428,6 @@ class WebResultsRendering(unittest.TestCase): monitor.set_status(result) elem = web_check_results.DeepCheckResultsRendererElement(monitor) - doc = self.render_element(elem) soup = BeautifulSoup(doc, 'html5lib') @@ -471,6 +468,26 @@ class WebResultsRendering(unittest.TestCase): u"Corrupt Shares: 1" ) + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"Files/Directories That Had Problems:" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"Servers on which corrupt shares were found" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"Corrupt Shares" + ) + + assert_soup_has_tag_with_content( + self, soup, u"h2", + u"All Results" + ) + def test_deep_check_and_repair_renderer(self): monitor = Monitor() status = check_results.DeepCheckAndRepairResults("") From 69afd292c2e6c1c453d9da9e9de9c2d5b7bd25ba Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 13:43:50 -0400 Subject: [PATCH 69/94] Use a different format for path Seems that _join_pathstring() is the wrong thing to use here. --- src/allmydata/web/check_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index f459b1f50..d6c8fb508 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -573,7 +573,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): result = results.get(path) storage_index = result.get_storage_index() object = { - "path": self._join_pathstring(path), + "path": self._html(path), "healthy": str(result.is_healthy()), "recoverable": str(result.is_recoverable()), "storage_index": self._render_si_link(req, storage_index), From 07c24dbe5931abaa041118882f3b268b6e6d1df9 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 16:14:10 -0400 Subject: [PATCH 70/94] Add a missing super() call --- src/allmydata/web/check_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index d6c8fb508..401ec3486 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -600,9 +600,9 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): :param allmydata.interfaces.IStatsProducer client: stats provider. :param allmydata.monitor.IMonitor monitor: status, progress, and cancellation provider. """ + super(DeepCheckAndRepairResultsRenderer, self).__init__() self._client = client self.monitor = monitor - self.children = {} def getChild(self, name, req): if not name: From b5a465cc8d8dcab65253b458385f0001e16dd7de Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 16:20:36 -0400 Subject: [PATCH 71/94] Format "all objects" table correctly --- .../web/deep-check-and-repair-results.xhtml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index c9bd998e5..936613c70 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -71,12 +71,12 @@
    Relative PathHealthyRecoverableStorage IndexSummary
    Relative PathHealthyRecoverableStorage IndexSummary
    - - - - - - + + + + + + @@ -86,6 +86,9 @@ + + +
    Relative PathHealthy Pre-RepairRecoverable Pre-RepairHealthy Post-RepairStorage IndexSummaryRelative PathHealthy Pre-RepairRecoverable Pre-RepairHealthy Post-RepairStorage IndexSummary
    Nothing to report yet.
    From e6eb866cef4ffe7693ec97447f3eaf70a1e3e5da Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 16:21:46 -0400 Subject: [PATCH 72/94] Add missing "header" renderer It does not do much here, but it is good to be consistent --- src/allmydata/web/deep-check-results.xhtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index 542bfdd75..09532d10d 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -47,7 +47,7 @@

    Corrupt Shares

    If repair fails, these shares need to be manually inspected and removed.

    - + @@ -67,7 +67,7 @@

    All Results

    Server Server Nickname Storage Index
    - + From 589e143a4a2a9306e4f0388fc1b36e8afcdbf61e Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 16:24:11 -0400 Subject: [PATCH 73/94] Add an empty renderer --- src/allmydata/web/deep-check-results.xhtml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/allmydata/web/deep-check-results.xhtml b/src/allmydata/web/deep-check-results.xhtml index 09532d10d..7fb324050 100644 --- a/src/allmydata/web/deep-check-results.xhtml +++ b/src/allmydata/web/deep-check-results.xhtml @@ -81,6 +81,9 @@ + + +
    Relative Path Healthy Recoverable
    Nothing to report yet.
    From 69693663708247015f97271642971102c3426310 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 16:38:30 -0400 Subject: [PATCH 74/94] Document, use, and test "join pathstring" method --- src/allmydata/test/test_checker.py | 7 ++++++- src/allmydata/web/check_results.py | 11 +++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 538ba0640..49d38b88c 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -118,7 +118,7 @@ class FakeDeepCheckResults(object): return { # TODO: fill this, perhaps with one each of healthy, # unhealthy, unrecoverable, corrupt. - "fake-result-path": FakeResults() + (u"some", u"fake", u"path"): FakeResults() } def get_corrupt_shares(self): @@ -473,6 +473,11 @@ class WebResultsRendering(unittest.TestCase): u"Files/Directories That Had Problems:" ) + assert_soup_has_tag_with_content( + self, soup, u"li", + u"some/fake/path: fake summary results [SI: mzqwwzjnonuq]" + ) + assert_soup_has_tag_with_content( self, soup, u"h2", u"Servers on which corrupt shares were found" diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 401ec3486..d244d25c8 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -91,6 +91,13 @@ class ResultsBase(object): # determine the permuted peer order def _join_pathstring(self, path): + """ + :param tuple path: a path represented by a tuple, such as + ``(u'some', u'dir', u'file')``. + + :return: a string joined by path separaters, such as + ``u'some/dir/file'``. + """ if path: pathstring = "/".join(self._html(path)) else: @@ -504,7 +511,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): problems.append({ # Not sure self._join_pathstring(path) is the # right thing to use here. - "problem": self._html(path) + self._html(summary_text), + "problem": self._join_pathstring(path) + self._html(summary_text), }) return SlotsSequenceElement(tag, problems) @@ -573,7 +580,7 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): result = results.get(path) storage_index = result.get_storage_index() object = { - "path": self._html(path), + "path": self._join_pathstring(path), "healthy": str(result.is_healthy()), "recoverable": str(result.is_recoverable()), "storage_index": self._render_si_link(req, storage_index), From 8fedbd52c2fa8bb28dadc4a750722774be7951c4 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 16:58:39 -0400 Subject: [PATCH 75/94] Expand "deep check and repair" test --- src/allmydata/test/test_checker.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 49d38b88c..0cdee2aa8 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -20,6 +20,7 @@ from allmydata.interfaces import ( IServer, ICheckResults, IDeepCheckResults, + ICheckAndRepairResults, ) from allmydata.util import base32 from allmydata.web import check_results as web_check_results @@ -126,6 +127,25 @@ class FakeDeepCheckResults(object): return { FakeResults().get_corrupt_shares() } +@implementer(ICheckAndRepairResults) +class FakeDeepCheckAndRepairResults(object): + + def get_storage_index(self): + return "" + + def get_pre_repair_results(self): + return FakeResults() + + def get_post_repair_results(self): + return FakeResults() + + def get_repair_attempted(self): + return True + + def get_repair_successful(self): + return False + + class WebResultsRendering(unittest.TestCase): def remove_tags(self, s): @@ -495,8 +515,15 @@ class WebResultsRendering(unittest.TestCase): def test_deep_check_and_repair_renderer(self): monitor = Monitor() + status = check_results.DeepCheckAndRepairResults("") + status.add_check_and_repair( + FakeDeepCheckAndRepairResults(), + (u"some", u"fake", u"path") + ) + monitor.set_status(status) + elem = web_check_results.DeepCheckAndRepairResultsRendererElement(monitor) doc = self.render_element(elem) soup = BeautifulSoup(doc, 'html5lib') From a13ec344b99f41c813902194b22a7b84aee81bf2 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 17:07:54 -0400 Subject: [PATCH 76/94] Correct usage of SlotsSequenceElement --- src/allmydata/web/check_results.py | 8 ++++---- .../web/deep-check-and-repair-results.xhtml | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index d244d25c8..27ca2fed7 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -748,7 +748,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin cr = r.get_pre_repair_results() if not cr.is_healthy(): problem = self._join_pathstring(path), ": ", self._html(cr.get_summary()) - problems.append(problem) + problems.append({"problem": problem}) return SlotsSequenceElement(tag, problems) @@ -770,7 +770,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin cr = r.get_post_repair_results() if not cr.is_healthy(): problem = self._join_pathstring(path), ": ", self._html(cr.get_summary()) - problems.append(problem) + problems.append({"problem": problem}) return SlotsSequenceElement(tag, problems) @@ -784,7 +784,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin def servers_with_corrupt_shares(self, req, tag): # TODO: this was originally unimplemented before porting to # twisted.web.template; leaving it as such. - corrupt = [] + corrupt = [{"share":"unimplemented"}] return SlotsSequenceElement(tag, corrupt) @renderer @@ -797,7 +797,7 @@ class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin def post_repair_corrupt_shares(self, req, tag): # TODO: this was not implemented before porting to # twisted.web.template; leaving it as such. - corrupt = [] + corrupt = [{"share":"unimplemented"}] return SlotsSequenceElement(tag, corrupt) @renderer diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index 936613c70..c8cf2deea 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -35,7 +35,9 @@

    Files/Directories That Had Problems:

      -
    • +
    • + +
    • None
    @@ -44,7 +46,9 @@

    Files/Directories That Still Have Problems:

      -
    • +
    • + +
    • None
    @@ -52,7 +56,9 @@

    Servers on which corrupt shares were found

      -
    • +
    • + +
    • None
    @@ -61,7 +67,9 @@

    Remaining Corrupt Shares

    These shares need to be manually inspected and removed.

      -
    • +
    • + +
    • None
@@ -70,7 +78,7 @@
- + From 2d7095866a3fefd88fb38a386ea3bc59de3ef463 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 17:09:53 -0400 Subject: [PATCH 77/94] Expand "deep check and repair results" page test --- src/allmydata/test/test_checker.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 0cdee2aa8..0fa2be7fd 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -542,7 +542,7 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Checked: 0" + u"Objects Checked: 1" ) assert_soup_has_tag_with_content( @@ -552,22 +552,17 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unhealthy (before repair): 0" + u"Objects Unhealthy (before repair): 1" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Corrupt Shares (before repair): 0" + u"Corrupt Shares (before repair): 3" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Repairs Attempted: 0" - ) - - assert_soup_has_tag_with_content( - self, soup, u"li", - u"Repairs Attempted: 0" + u"Repairs Attempted: 1" ) assert_soup_has_tag_with_content( @@ -577,7 +572,7 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - "Repairs Unsuccessful: 0" + "Repairs Unsuccessful: 1" ) assert_soup_has_tag_with_content( @@ -587,12 +582,12 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unhealthy (after repair): 0" + u"Objects Unhealthy (after repair): 1" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Corrupt Shares (after repair): 0" + u"Corrupt Shares (after repair): 3" ) assert_soup_has_tag_with_content( From 7f93e28a7c9c4ff7ff4c00a7acce0ad3032f9dd5 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 18:43:52 -0400 Subject: [PATCH 78/94] Parameterize classes used in check/repair tests --- src/allmydata/test/test_checker.py | 134 ++++++++++++++++------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 0fa2be7fd..eb94f5c23 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -72,78 +72,60 @@ class FakeServer(object): @implementer(ICheckResults) -class FakeResults(object): +class FakeCheckResults(object): - def __init__(self): - # TODO: figure out a good enough fake SI. - self._storage_index = "fake-si" + def __init__(self, si=None, + healthy=False, recoverable=False, + summary="fake summary"): + self._storage_index = si + self._is_healthy = healthy + self._is_recoverable = recoverable + self._summary = summary def get_storage_index(self): return self._storage_index def get_storage_index_string(self): - return base32.b2a(self._storage_index) + return base32.b2a_or_none(self._storage_index) def is_healthy(self): - return False + return self._is_healthy def is_recoverable(self): - return False + return self._is_recoverable def get_summary(self): - return "fake summary results" + return self._summary def get_corrupt_shares(self): - # Returns (IServer, storage_index, sharenum) - return (FakeServer(), "fake-si", 0) - - -# TODO: maybe use check_results.DeepCheckResults? -@implementer(IDeepCheckResults) -class FakeDeepCheckResults(object): - - def get_root_storage_index_string(self): - # TODO: figure out a good enough fake root SI. - return "fake-root-si" - - def get_counters(self): - return { - "count-objects-checked": 4, - "count-objects-healthy": 1, - "count-objects-unhealthy": 1, - "count-objects-unrecoverable": 1, - "count-corrupt-shares": 1, - } - - def get_all_results(self): - return { - # TODO: fill this, perhaps with one each of healthy, - # unhealthy, unrecoverable, corrupt. - (u"some", u"fake", u"path"): FakeResults() - } - - def get_corrupt_shares(self): - # Returns a set of (IServer, storage_index, sharenum) - return { FakeResults().get_corrupt_shares() } + # returns a list of (IServer, storage_index, sharenum) + return [(FakeServer(), "", 0)] @implementer(ICheckAndRepairResults) -class FakeDeepCheckAndRepairResults(object): +class FakeCheckAndRepairResults(object): + + def __init__(self, si=None, + repair_attempted=False, + repair_success=False): + self._storage_index = si + self._repair_attempted = repair_attempted + self._repair_success = repair_success def get_storage_index(self): - return "" + return self._storage_index def get_pre_repair_results(self): - return FakeResults() + return FakeCheckResults() def get_post_repair_results(self): - return FakeResults() + return FakeCheckResults() def get_repair_attempted(self): - return True + return self._repair_attempted def get_repair_successful(self): - return False + return self._repair_success class WebResultsRendering(unittest.TestCase): @@ -443,9 +425,26 @@ class WebResultsRendering(unittest.TestCase): def test_deep_check_renderer(self): + status = check_results.DeepCheckResults("fake-root-si") + status.add_check( + FakeCheckResults("", False, False), + (u"fake", u"unhealthy", u"unrecoverable") + ) + status.add_check( + FakeCheckResults("", True, True), + (u"fake", u"healthy", u"recoverable") + ) + status.add_check( + FakeCheckResults("", True, False), + (u"fake", u"healthy", u"unrecoverable") + ) + status.add_check( + FakeCheckResults("", False, True), + (u"fake", u"unhealthy", u"recoverable") + ) + monitor = Monitor() - result = FakeDeepCheckResults() - monitor.set_status(result) + monitor.set_status(status) elem = web_check_results.DeepCheckResultsRendererElement(monitor) doc = self.render_element(elem) @@ -470,22 +469,22 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Healthy: 1" + u"Objects Healthy: 2" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unhealthy: 1" + u"Objects Unhealthy: 2" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unrecoverable: 1" + u"Objects Unrecoverable: 2" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Corrupt Shares: 1" + u"Corrupt Shares: 4" ) assert_soup_has_tag_with_content( @@ -495,7 +494,12 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"some/fake/path: fake summary results [SI: mzqwwzjnonuq]" + u"fake/unhealthy/recoverable: fake summary" + ) + + assert_soup_has_tag_with_content( + self, soup, u"li", + u"fake/unhealthy/unrecoverable: fake summary" ) assert_soup_has_tag_with_content( @@ -514,14 +518,22 @@ class WebResultsRendering(unittest.TestCase): ) def test_deep_check_and_repair_renderer(self): - monitor = Monitor() - status = check_results.DeepCheckAndRepairResults("") + status.add_check_and_repair( - FakeDeepCheckAndRepairResults(), - (u"some", u"fake", u"path") + FakeCheckAndRepairResults("attempted/success", True, True), + (u"attempted", u"success") + ) + status.add_check_and_repair( + FakeCheckAndRepairResults("attempted/failure", True, False), + (u"attempted", u"failure") + ) + status.add_check_and_repair( + FakeCheckAndRepairResults("unattempted/failure", False, False), + (u"unattempted", u"failure") ) + monitor = Monitor() monitor.set_status(status) elem = web_check_results.DeepCheckAndRepairResultsRendererElement(monitor) @@ -542,7 +554,7 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Checked: 1" + u"Objects Checked: 3" ) assert_soup_has_tag_with_content( @@ -552,7 +564,7 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unhealthy (before repair): 1" + u"Objects Unhealthy (before repair): 3" ) assert_soup_has_tag_with_content( @@ -562,12 +574,12 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Repairs Attempted: 1" + u"Repairs Attempted: 2" ) assert_soup_has_tag_with_content( self, soup, u"li", - u"Repairs Successful: 0" + u"Repairs Successful: 1" ) assert_soup_has_tag_with_content( @@ -582,7 +594,7 @@ class WebResultsRendering(unittest.TestCase): assert_soup_has_tag_with_content( self, soup, u"li", - u"Objects Unhealthy (after repair): 1" + u"Objects Unhealthy (after repair): 3" ) assert_soup_has_tag_with_content( From 80bf5f0a53c0e7779d9f35a1cbc8a89faecac59f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 15 Jul 2020 19:19:51 -0400 Subject: [PATCH 79/94] Remove an unused import --- src/allmydata/test/test_checker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index eb94f5c23..758755e9c 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -19,7 +19,6 @@ from allmydata import uri as tahoe_uri from allmydata.interfaces import ( IServer, ICheckResults, - IDeepCheckResults, ICheckAndRepairResults, ) from allmydata.util import base32 From 9f5c58c173705df87d7bb8847ca3adaa31d28268 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 06:27:40 -0400 Subject: [PATCH 80/94] Use super() --- src/allmydata/test/test_checker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 758755e9c..e02343a5f 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -48,9 +48,9 @@ class FakeClient(object): # Also, the request needs to have `args` and `fields` properties so # that `allmydata.web.common.get_arg()` won't complain. @implementer(IRequest) -class TestRequest(object, Request): +class TestRequest(Request, object): def __init__(self, args=None, fields=None): - Request.__init__(self, DummyChannel()) + super(TestRequest, self).__init__(DummyChannel()) self.args = args or {} self.fields = fields or {} self.prepath = [b""] From 80802a74cdf8b6ed1a971cf086d36508fc91d903 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 06:53:52 -0400 Subject: [PATCH 81/94] Use BeautifulSoup to remove tags --- src/allmydata/test/test_checker.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index e02343a5f..ee9be5abc 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -129,10 +129,9 @@ class FakeCheckAndRepairResults(object): class WebResultsRendering(unittest.TestCase): - def remove_tags(self, s): - s = re.sub(r'<[^>]*>', ' ', s) - s = re.sub(r'\s+', ' ', s) - return s + @staticmethod + def remove_tags(html): + return BeautifulSoup(html).get_text() def create_fake_client(self): sb = StorageFarmBroker(True, None, EMPTY_CLIENT_CONFIG) From adca146a1b2b66f9f69477150a29f238f11c1e6d Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 07:10:44 -0400 Subject: [PATCH 82/94] Use space as separator when removing tags --- src/allmydata/test/test_checker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index ee9be5abc..252938143 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -131,7 +131,7 @@ class WebResultsRendering(unittest.TestCase): @staticmethod def remove_tags(html): - return BeautifulSoup(html).get_text() + return BeautifulSoup(html).get_text(separator=" ") def create_fake_client(self): sb = StorageFarmBroker(True, None, EMPTY_CLIENT_CONFIG) @@ -227,7 +227,7 @@ class WebResultsRendering(unittest.TestCase): self.failUnlessIn("Wrong Shares: 0", s) self.failUnlessIn("Recoverable Versions: 1", s) self.failUnlessIn("Unrecoverable Versions: 0", s) - self.failUnlessIn("Good Shares (sorted in share order): Share ID Nickname Node ID shareid1 peer-0 00000000 peer-f ffffffff", s) + self.failUnlessIn("Good Shares (sorted in share order): Share ID Nickname Node ID shareid1 peer-0 00000000 peer-f ffffffff", s) cr = check_results.CheckResults(u, u.get_storage_index(), healthy=False, recoverable=True, @@ -250,7 +250,7 @@ class WebResultsRendering(unittest.TestCase): s = self.remove_tags(html) self.failUnlessIn("File Check Results for SI=2k6avp", s) # abbreviated self.failUnlessIn("Not Recoverable! : rather dead", s) - self.failUnlessIn("Corrupt shares: Share ID Nickname Node ID sh#2 peer-0 00000000", s) + self.failUnlessIn("Corrupt shares: Share ID Nickname Node ID sh#2 peer-0 00000000", s) html = self.render_element(w) s = self.remove_tags(html) From e6867a1355e101216ca40ed78c2829ad0ca8e648 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 07:15:39 -0400 Subject: [PATCH 83/94] Use docstring to describe a class --- src/allmydata/test/test_checker.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 252938143..bc0dc0482 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -43,12 +43,17 @@ class FakeClient(object): def get_storage_broker(self): return self.storage_broker -# XXX: We have to have this class because `common.get_arg()` expects a -# `nevow.inevow.IRequest`, which `twisted.web.server.Request` isn't. -# Also, the request needs to have `args` and `fields` properties so -# that `allmydata.web.common.get_arg()` won't complain. @implementer(IRequest) class TestRequest(Request, object): + """ + A minimal Request class to use in tests. + + XXX: We have to have this class because `common.get_arg()` expects + a `nevow.inevow.IRequest`, which `twisted.web.server.Request` + isn't. The request needs to have `args`, `fields`, `prepath`, and + `postpath` properties so that `allmydata.web.common.get_arg()` + won't complain. + """ def __init__(self, args=None, fields=None): super(TestRequest, self).__init__(DummyChannel()) self.args = args or {} From 1b7a35e7730eaab6ed0bf516b1c9635561067036 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 07:18:01 -0400 Subject: [PATCH 84/94] Do not strip tags where it is not necessary --- src/allmydata/test/test_checker.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index bc0dc0482..c22d42914 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -175,12 +175,10 @@ class WebResultsRendering(unittest.TestCase): lcr = web_check_results.LiteralCheckResultsRendererElement() html = self.render_element(lcr) - s = self.remove_tags(html) - self.failUnlessIn("Literal files are always healthy", s) + self.failUnlessIn("Literal files are always healthy", html) html = self.render_element(lcr, args={"return_to": ["FOOURL"]}) - s = self.remove_tags(html) - self.failUnlessIn("Literal files are always healthy", s) + self.failUnlessIn("Literal files are always healthy", html) self.failUnlessIn('Return to file.', html) c = self.create_fake_client() From ca6cf887f757b9497b5a8f8ebc49c71d931554a5 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 07:26:11 -0400 Subject: [PATCH 85/94] Test JSON rendering synchronously --- src/allmydata/test/test_checker.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index c22d42914..779c7fa66 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -405,24 +405,18 @@ class WebResultsRendering(unittest.TestCase): self.failUnlessIn("Repair unsuccessful", s) self.failUnlessIn("Post-Repair Checker Results:", s) - w2 = web_check_results.CheckAndRepairResultsRenderer(c, crr) - d = self.render_json(w2) - def _got_json(data): - j = json.loads(data) - self.failUnlessEqual(j["repair-attempted"], True) - self.failUnlessEqual(j["storage-index"], - "2k6avpjga3dho3zsjo6nnkt7n4") - self.failUnlessEqual(j["pre-repair-results"]["summary"], "illing") - self.failUnlessEqual(j["post-repair-results"]["summary"], "worse") - _got_json(d) + w = web_check_results.CheckAndRepairResultsRenderer(c, crr) + j = json.loads(self.render_json(w)) + self.failUnlessEqual(j["repair-attempted"], True) + self.failUnlessEqual(j["storage-index"], + "2k6avpjga3dho3zsjo6nnkt7n4") + self.failUnlessEqual(j["pre-repair-results"]["summary"], "illing") + self.failUnlessEqual(j["post-repair-results"]["summary"], "worse") - w3 = web_check_results.CheckAndRepairResultsRenderer(c, None) - d = self.render_json(w3) - def _got_lit_results(data): - j = json.loads(data) - self.failUnlessEqual(j["repair-attempted"], False) - self.failUnlessEqual(j["storage-index"], "") - _got_lit_results(d) + w = web_check_results.CheckAndRepairResultsRenderer(c, None) + j = json.loads(self.render_json(w)) + self.failUnlessEqual(j["repair-attempted"], False) + self.failUnlessEqual(j["storage-index"], "") def test_deep_check_renderer(self): From 0f28a9387982ad957a8f7b84ab1e2f9b0c4dbb1f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 08:08:48 -0400 Subject: [PATCH 86/94] Remove unused import --- src/allmydata/test/test_checker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/allmydata/test/test_checker.py b/src/allmydata/test/test_checker.py index 779c7fa66..2296194f0 100644 --- a/src/allmydata/test/test_checker.py +++ b/src/allmydata/test/test_checker.py @@ -1,5 +1,4 @@ -import re import json import os.path, shutil From 4007fb4de7e0ab40c8f2aadc985ddb2770a975cb Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 16:12:48 -0400 Subject: [PATCH 87/94] Rearrange imports --- src/allmydata/test/web/test_web.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/allmydata/test/web/test_web.py b/src/allmydata/test/web/test_web.py index 1ada50433..0b975f388 100644 --- a/src/allmydata/test/web/test_web.py +++ b/src/allmydata/test/web/test_web.py @@ -54,6 +54,9 @@ from .common import ( assert_soup_has_tag_with_attributes, assert_soup_has_tag_with_content, assert_soup_has_tag_with_attributes_and_content, + unknown_rwcap, + unknown_rocap, + unknown_immcap, ) from allmydata.interfaces import IMutableFileNode, SDMF_VERSION, MDMF_VERSION @@ -64,7 +67,6 @@ from ..common_web import ( Error, ) from allmydata.client import _Client, SecretHolder -from .common import unknown_rwcap, unknown_rocap, unknown_immcap # create a fake uploader/downloader, and a couple of fake dirnodes, then # create a webserver that works against them From 951250f9ff327bf00291b2cae301515d71024ead Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 28 Jul 2020 16:13:58 -0400 Subject: [PATCH 88/94] Remove unused FAVICON_MARKUP --- src/allmydata/test/web/common.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/allmydata/test/web/common.py b/src/allmydata/test/web/common.py index 871cdeb26..1f568ad8d 100644 --- a/src/allmydata/test/web/common.py +++ b/src/allmydata/test/web/common.py @@ -5,8 +5,6 @@ unknown_rwcap = u"lafs://from_the_future_rw_\u263A".encode('utf-8') unknown_rocap = u"ro.lafs://readonly_from_the_future_ro_\u263A".encode('utf-8') unknown_immcap = u"imm.lafs://immutable_from_the_future_imm_\u263A".encode('utf-8') -FAVICON_MARKUP = '' - def assert_soup_has_favicon(testcase, soup): """ From 3e11779976872bfbad20169b99f8c1a7d65d1f9e Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 6 Aug 2020 22:18:00 -0400 Subject: [PATCH 89/94] Do some error checking --- src/allmydata/web/check_results.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 27ca2fed7..d996058f7 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -463,30 +463,34 @@ class DeepCheckResultsRendererElement(Element, ResultsBase, ReloadMixin): @renderer def root_storage_index(self, req, tag): + if not self.monitor.get_status(): + return "" return self.monitor.get_status().get_root_storage_index_string() def _get_monitor_counter(self, name): - return self.monitor.get_status().get_counters().get(name) + if not self.monitor.get_status(): + return "" + return str(self.monitor.get_status().get_counters().get(name)) @renderer def objects_checked(self, req, tag): - return str(self._get_monitor_counter("count-objects-checked")) + return self._get_monitor_counter("count-objects-checked") @renderer def objects_healthy(self, req, tag): - return str(self._get_monitor_counter("count-objects-healthy")) + return self._get_monitor_counter("count-objects-healthy") @renderer def objects_unhealthy(self, req, tag): - return str(self._get_monitor_counter("count-objects-unhealthy")) + return self._get_monitor_counter("count-objects-unhealthy") @renderer def objects_unrecoverable(self, req, tag): - return str(self._get_monitor_counter("count-objects-unrecoverable")) + return self._get_monitor_counter("count-objects-unrecoverable") @renderer def count_corrupt_shares(self, req, tag): - return str(self._get_monitor_counter("count-corrupt-shares")) + return self._get_monitor_counter("count-corrupt-shares") @renderer def problems_p(self, req, tag): From 76777cd4e128bc444949213c67f10a7df2f5ded3 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 6 Aug 2020 22:22:26 -0400 Subject: [PATCH 90/94] Inherit "deep check and repair" page from "deep check" page --- src/allmydata/web/check_results.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index d996058f7..913c4d7e6 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -676,12 +676,12 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): return json.dumps(data, indent=1) + "\n" -class DeepCheckAndRepairResultsRendererElement(Element, ResultsBase, ReloadMixin): +class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): loader = XMLFile(FilePath(__file__).sibling("deep-check-and-repair-results.xhtml")) def __init__(self, monitor): - super(DeepCheckAndRepairResultsRendererElement, self).__init__() + super(DeepCheckAndRepairResultsRendererElement, self).__init__(monitor) self.monitor = monitor @renderer From c0109b70b645a95ad98d0a5fecd563ba2c6bb777 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 6 Aug 2020 22:25:34 -0400 Subject: [PATCH 91/94] Remove methods common to base class --- src/allmydata/web/check_results.py | 56 ++++++++---------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 913c4d7e6..85e80a9f4 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -684,60 +684,45 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): super(DeepCheckAndRepairResultsRendererElement, self).__init__(monitor) self.monitor = monitor - @renderer - def root_storage_index(self, req, tag): - if not self.monitor.get_status(): - return "" - return self.monitor.get_status().get_root_storage_index_string() - - def _get_counter(self, name): - if not self.monitor.get_status(): - return "" - return str(self.monitor.get_status().get_counters().get(name)) - - @renderer - def objects_checked(self, req, tag): - return self._get_counter("count-objects-checked") - @renderer def objects_healthy(self, req, tag): - return self._get_counter("count-objects-healthy-pre-repair") + return self._get_monitor_counter("count-objects-healthy-pre-repair") @renderer def objects_unhealthy(self, req, tag): - return self._get_counter("count-objects-unhealthy-pre-repair") + return self._get_monitor_counter("count-objects-unhealthy-pre-repair") @renderer def corrupt_shares(self, req, tag): - return self._get_counter("count-corrupt-shares-pre-repair") + return self._get_monitor_counter("count-corrupt-shares-pre-repair") @renderer def repairs_attempted(self, req, tag): - return self._get_counter("count-repairs-attempted") + return self._get_monitor_counter("count-repairs-attempted") @renderer def repairs_successful(self, req, tag): - return self._get_counter("count-repairs-successful") + return self._get_monitor_counter("count-repairs-successful") @renderer def repairs_unsuccessful(self, req, tag): - return self._get_counter("count-repairs-unsuccessful") + return self._get_monitor_counter("count-repairs-unsuccessful") @renderer def objects_healthy_post(self, req, tag): - return self._get_counter("count-objects-healthy-post-repair") + return self._get_monitor_counter("count-objects-healthy-post-repair") @renderer def objects_unhealthy_post(self, req, tag): - return self._get_counter("count-objects-unhealthy-post-repair") + return self._get_monitor_counter("count-objects-unhealthy-post-repair") @renderer def corrupt_shares_post(self, req, tag): - return self._get_counter("count-corrupt-shares-post-repair") + return self._get_monitor_counter("count-corrupt-shares-post-repair") @renderer def pre_repair_problems_p(self, req, tag): - if self._get_counter("count-objects-unhealthy-pre-repair"): + if self._get_monitor_counter("count-objects-unhealthy-pre-repair"): return tag return "" @@ -758,8 +743,8 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): @renderer def post_repair_problems_p(self, req, tag): - if (self._get_counter("count-objects-unhealthy-post-repair") - or self._get_counter("count-corrupt-shares-post-repair")): + if (self._get_monitor_counter("count-objects-unhealthy-post-repair") + or self._get_monitor_counter("count-corrupt-shares-post-repair")): return tag return "" @@ -780,7 +765,7 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): @renderer def servers_with_corrupt_shares_p(self, req, tag): - if self._get_counter("count-corrupt-shares-pre-repair"): + if self._get_monitor_counter("count-corrupt-shares-pre-repair"): return tag return "" @@ -793,7 +778,7 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): @renderer def remaining_corrupt_shares_p(self, req, tag): - if self._get_counter("count-corrupt-shares-post-repair"): + if self._get_monitor_counter("count-corrupt-shares-post-repair"): return tag return "" @@ -804,13 +789,6 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): corrupt = [{"share":"unimplemented"}] return SlotsSequenceElement(tag, corrupt) - @renderer - def return_to(self, req, tag): - return_to = get_arg(req, "return_to", None) - if return_to: - return tags.div(tags.a("Return to file/directory.", href=return_to)) - return "" - @renderer def all_objects(self, req, tag): results = {} @@ -833,9 +811,3 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): return SlotsSequenceElement(tag, objects) - @renderer - def runtime(self, req, tag): - runtime = 'unknown' - if hasattr(req, 'processing_started_timestamp'): - runtime = time.time() - req.processing_started_timestamp - return tag("runtime: %s seconds" % runtime) From 36352ee1827370ef4b81ec31831d11aec5f4a273 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 6 Aug 2020 22:28:32 -0400 Subject: [PATCH 92/94] Reuse "servers with corrupt shares" from base class --- src/allmydata/web/check_results.py | 13 ------------- .../web/deep-check-and-repair-results.xhtml | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index 85e80a9f4..d7a19705a 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -763,19 +763,6 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): return SlotsSequenceElement(tag, problems) - @renderer - def servers_with_corrupt_shares_p(self, req, tag): - if self._get_monitor_counter("count-corrupt-shares-pre-repair"): - return tag - return "" - - @renderer - def servers_with_corrupt_shares(self, req, tag): - # TODO: this was originally unimplemented before porting to - # twisted.web.template; leaving it as such. - corrupt = [{"share":"unimplemented"}] - return SlotsSequenceElement(tag, corrupt) - @renderer def remaining_corrupt_shares_p(self, req, tag): if self._get_monitor_counter("count-corrupt-shares-post-repair"): diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml index c8cf2deea..be7f35b1d 100644 --- a/src/allmydata/web/deep-check-and-repair-results.xhtml +++ b/src/allmydata/web/deep-check-and-repair-results.xhtml @@ -57,7 +57,7 @@

Servers on which corrupt shares were found

  • - +
  • None
From c52bc6a7818388453589b7c9e8bc5ae4b09dc801 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 6 Aug 2020 22:32:00 -0400 Subject: [PATCH 93/94] Document "deep check and repair" renderer element --- src/allmydata/web/check_results.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index d7a19705a..f43f93c5f 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -677,6 +677,10 @@ class DeepCheckAndRepairResultsRenderer(MultiFormatResource): class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): + """ + The page generated here has several elements common to "deep check + results" page; hence the code reuse. + """ loader = XMLFile(FilePath(__file__).sibling("deep-check-and-repair-results.xhtml")) From f4cfc0c613df51cd64da9e440c7f1d0d24423f0d Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 6 Aug 2020 22:40:44 -0400 Subject: [PATCH 94/94] Assign a ticket to unimplemented functionality --- src/allmydata/web/check_results.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py index f43f93c5f..500ac15a7 100644 --- a/src/allmydata/web/check_results.py +++ b/src/allmydata/web/check_results.py @@ -777,6 +777,8 @@ class DeepCheckAndRepairResultsRendererElement(DeepCheckResultsRendererElement): def post_repair_corrupt_shares(self, req, tag): # TODO: this was not implemented before porting to # twisted.web.template; leaving it as such. + # + # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3371 corrupt = [{"share":"unimplemented"}] return SlotsSequenceElement(tag, corrupt)
Relative Path Healthy Pre-Repair Recoverable Pre-Repair