From bf56e2bb51e120b1a4f34f967a75ea99b2b8b904 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@allmydata.com>
Date: Mon, 12 Jan 2009 18:56:19 -0700
Subject: [PATCH] deep-check-and-repair: improve results and their HTML
 representation

---
 src/allmydata/immutable/filenode.py           | 14 ++++++-
 src/allmydata/interfaces.py                   |  5 +++
 src/allmydata/web/check_results.py            | 40 ++++++++++++++-----
 .../web/deep-check-and-repair-results.xhtml   | 10 +++--
 4 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/src/allmydata/immutable/filenode.py b/src/allmydata/immutable/filenode.py
index 50fc996c..b2b63c5b 100644
--- a/src/allmydata/immutable/filenode.py
+++ b/src/allmydata/immutable/filenode.py
@@ -208,6 +208,8 @@ class FileNode(_ImmutableFileNodeBase, log.PrefixingLogMixin):
                 crr.post_repair_results = cr
                 return defer.succeed(crr)
             else:
+                crr.repair_attempted = True
+                crr.repair_successful = False # until proven successful
                 def _gather_repair_results(ur):
                     assert IUploadResults.providedBy(ur), ur
                     # clone the cr -- check results to form the basic of the prr -- post-repair results
@@ -222,14 +224,22 @@ class FileNode(_ImmutableFileNodeBase, log.PrefixingLogMixin):
                     prr.data['servers-responding'] = list(servers_responding)
                     prr.data['count-shares-good'] = len(sm)
                     prr.data['count-good-share-hosts'] = len(sm)
-                    prr.set_healthy(len(sm) >= self.u.total_shares)
+                    is_healthy = len(sm) >= self.u.total_shares
+                    prr.set_healthy(is_healthy)
+                    crr.repair_successful = is_healthy
                     prr.set_needs_rebalancing(len(sm) >= self.u.total_shares)
 
                     crr.post_repair_results = prr
                     return crr
+                def _repair_error(f):
+                    # as with mutable repair, I'm not sure if I want to pass
+                    # through a failure or not. TODO
+                    crr.repair_successful = False
+                    crr.repair_failure = f
+                    return f
                 r = Repairer(client=self._client, verifycap=verifycap, monitor=monitor)
                 d = r.start()
-                d.addCallback(_gather_repair_results)
+                d.addCallbacks(_gather_repair_results, _repair_error)
                 return d
 
         d.addCallback(_maybe_repair)
diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py
index f2be3e0c..563ba72c 100644
--- a/src/allmydata/interfaces.py
+++ b/src/allmydata/interfaces.py
@@ -1833,6 +1833,11 @@ class IDeepCheckAndRepairResults(Interface):
         be slash-joined) to an ICheckAndRepairResults instance, one for each
         object that was checked."""
 
+    def get_results_for_storage_index(storage_index):
+        """Retrive the ICheckAndRepairResults instance for the given (binary)
+        storage index. Raises KeyError if there are no results for that
+        storage index."""
+
 
 class IRepairable(Interface):
     def repair(check_results):
diff --git a/src/allmydata/web/check_results.py b/src/allmydata/web/check_results.py
index 7aea7d3b..399db889 100644
--- a/src/allmydata/web/check_results.py
+++ b/src/allmydata/web/check_results.py
@@ -10,6 +10,13 @@ from allmydata.interfaces import ICheckAndRepairResults, ICheckResults
 from allmydata.util import base32, idlib
 
 class ResultsBase:
+    def _join_pathstring(self, path):
+        if path:
+            pathstring = "/".join(self._html(path))
+        else:
+            pathstring = "<root>"
+        return pathstring
+
     def _render_results(self, ctx, cr):
         assert ICheckResults(cr)
         c = IClient(ctx)
@@ -266,7 +273,7 @@ class CheckAndRepairResults(CheckerBase, rend.Page, ResultsBase):
 
     def render_post_repair_results(self, ctx, data):
         cr = self._render_results(ctx, self.r.get_post_repair_results())
-        return ctx.tag[cr]
+        return ctx.tag[T.div["Post-Repair Checker Results:"], cr]
 
     def render_maybe_pre_repair_results(self, ctx, data):
         if self.r.get_repair_attempted():
@@ -358,7 +365,7 @@ class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
         if summary:
             summary_text = ": " + summary
         summary_text += " [SI: %s]" % cr.get_storage_index_string()
-        return ctx.tag["/".join(self._html(path)), self._html(summary_text)]
+        return ctx.tag[self._join_pathstring(path), self._html(summary_text)]
 
 
     def render_servers_with_corrupt_shares_p(self, ctx, data):
@@ -413,11 +420,7 @@ class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
 
     def render_object(self, ctx, data):
         path, r = data
-        if path:
-            pathstring = "/".join(self._html(path))
-        else:
-            pathstring = "<root>"
-        ctx.fillSlots("path", pathstring)
+        ctx.fillSlots("path", self._join_pathstring(path))
         ctx.fillSlots("healthy", str(r.is_healthy()))
         ctx.fillSlots("recoverable", str(r.is_recoverable()))
         storage_index = r.get_storage_index()
@@ -438,6 +441,19 @@ class DeepCheckAndRepairResults(rend.Page, ResultsBase, ReloadMixin):
         #self.r = results
         self.monitor = monitor
 
+    def childFactory(self, ctx, name):
+        if not name:
+            return self
+        # /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
+        # about a specific file or directory that was checked
+        si = base32.a2b(name)
+        r = self.monitor.get_status()
+        try:
+            return CheckAndRepairResults(r.get_results_for_storage_index(si))
+        except KeyError:
+            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)
@@ -530,7 +546,8 @@ class DeepCheckAndRepairResults(rend.Page, ResultsBase, ReloadMixin):
 
     def render_problem(self, ctx, data):
         path, cr = data
-        return ["/".join(self._html(path)), ": ", self._html(cr.get_summary())]
+        return ctx.tag[self._join_pathstring(path), ": ",
+                       self._html(cr.get_summary())]
 
     def render_post_repair_problems_p(self, ctx, data):
         c = self.monitor.get_status().get_counters()
@@ -583,11 +600,16 @@ class DeepCheckAndRepairResults(rend.Page, ResultsBase, ReloadMixin):
 
     def render_object(self, ctx, data):
         path, r = data
-        ctx.fillSlots("path", "/".join(self._html(path)))
+        ctx.fillSlots("path", self._join_pathstring(path))
         ctx.fillSlots("healthy_pre_repair",
                       str(r.get_pre_repair_results().is_healthy()))
+        ctx.fillSlots("recoverable_pre_repair",
+                      str(r.get_pre_repair_results().is_recoverable()))
         ctx.fillSlots("healthy_post_repair",
                       str(r.get_post_repair_results().is_healthy()))
+        storage_index = r.get_storage_index()
+        ctx.fillSlots("storage_index",
+                      self._render_si_link(ctx, storage_index))
         ctx.fillSlots("summary",
                       self._html(r.get_pre_repair_results().get_summary()))
         return ctx.tag
diff --git a/src/allmydata/web/deep-check-and-repair-results.xhtml b/src/allmydata/web/deep-check-and-repair-results.xhtml
index b171d7bf..fac98855 100644
--- a/src/allmydata/web/deep-check-and-repair-results.xhtml
+++ b/src/allmydata/web/deep-check-and-repair-results.xhtml
@@ -70,17 +70,21 @@
 <div n:render="return" />
 
 <div>
-<table n:render="sequence" n:data="all_objects">
+<table n:render="sequence" n:data="all_objects" border="1">
   <tr n:pattern="header">
     <td>Relative Path</td>
-    <td>Healthy</td>
-    <td>Post-Repair</td>
+    <td>Healthy Pre-Repair</td>
+    <td>Recoverable Pre-Repair</td>
+    <td>Healthy Post-Repair</td>
+    <td>Storage Index</td>
     <td>Summary</td>
   </tr>
   <tr n:pattern="item" n:render="object">
     <td><n:slot name="path"/></td>
     <td><n:slot name="healthy_pre_repair"/></td>
+    <td><n:slot name="recoverable_pre_repair"/></td>
     <td><n:slot name="healthy_post_repair"/></td>
+    <td><n:slot name="storage_index"/></td>
     <td><n:slot name="summary"/></td>
   </tr>
 </table>
-- 
2.45.2