source file: /home/buildslave/tahoe/edgy/build/src/allmydata/web/check_results.py
file stats: 488 lines, 417 executed: 85.5% covered
coverage versus previous test: 0 lines added, 0 lines removed
    1. 
    2. import time
    3. import simplejson
    4. from nevow import rend, inevow, tags as T
    5. from twisted.web import http, html
    6. from allmydata.web.common import getxmlfile, get_arg, get_root, WebError
    7. from allmydata.web.operations import ReloadMixin
    8. from allmydata.interfaces import ICheckAndRepairResults, ICheckResults
    9. from allmydata.util import base32, idlib
   10. 
   11. def json_check_counts(d):
   12.     r = {}
   13.     r["count-shares-good"] = d["count-shares-good"]
   14.     r["count-shares-needed"] = d["count-shares-needed"]
   15.     r["count-shares-expected"] = d["count-shares-expected"]
   16.     r["count-good-share-hosts"] = d["count-good-share-hosts"]
   17.     r["count-corrupt-shares"] = d["count-corrupt-shares"]
   18.     r["list-corrupt-shares"] = [ (idlib.nodeid_b2a(serverid),
   19.                                   base32.b2a(si), shnum)
   20.                                  for (serverid, si, shnum)
   21.                                  in d["list-corrupt-shares"] ]
   22.     r["servers-responding"] = [idlib.nodeid_b2a(serverid)
   23.                                for serverid in d["servers-responding"]]
   24.     sharemap = {}
   25.     for (shareid, serverids) in d["sharemap"].items():
   26.         sharemap[shareid] = [idlib.nodeid_b2a(serverid)
   27.                              for serverid in serverids]
   28.     r["sharemap"] = sharemap
   29. 
   30.     r["count-wrong-shares"] = d["count-wrong-shares"]
   31.     r["count-recoverable-versions"] = d["count-recoverable-versions"]
   32.     r["count-unrecoverable-versions"] = d["count-unrecoverable-versions"]
   33. 
   34.     return r
   35. 
   36. def json_check_results(r):
   37.     if r is None:
   38.         # LIT file
   39.         data = {"storage-index": "",
   40.                 "results": {"healthy": True},
   41.                 }
   42.         return data
   43.     data = {}
   44.     data["storage-index"] = r.get_storage_index_string()
   45.     data["summary"] = r.get_summary()
   46.     data["results"] = json_check_counts(r.get_data())
   47.     data["results"]["needs-rebalancing"] = r.needs_rebalancing()
   48.     data["results"]["healthy"] = r.is_healthy()
   49.     data["results"]["recoverable"] = r.is_recoverable()
   50.     return data
   51. 
   52. def json_check_and_repair_results(r):
   53.     if r is None:
   54.         # LIT file
   55.         data = {"storage-index": "",
   56.                 "repair-attempted": False,
   57.                 }
   58.         return data
   59.     data = {}
   60.     data["storage-index"] = r.get_storage_index_string()
   61.     data["repair-attempted"] = r.get_repair_attempted()
   62.     data["repair-successful"] = r.get_repair_successful()
   63.     pre = r.get_pre_repair_results()
   64.     data["pre-repair-results"] = json_check_results(pre)
   65.     post = r.get_post_repair_results()
   66.     data["post-repair-results"] = json_check_results(post)
   67.     return data
   68. 
   69. class ResultsBase:
   70.     # self.client must point to the Client, so we can get nicknames and
   71.     # determine the permuted peer order
   72. 
   73.     def _join_pathstring(self, path):
   74.         if path:
   75.             pathstring = "/".join(self._html(path))
   76.         else:
   77.             pathstring = "<root>"
   78.         return pathstring
   79. 
   80.     def _render_results(self, ctx, cr):
   81.         assert ICheckResults(cr)
   82.         c = self.client
   83.         sb = c.get_storage_broker()
   84.         data = cr.get_data()
   85.         r = []
   86.         def add(name, value):
   87.             r.append(T.li[name + ": ", value])
   88. 
   89.         add("Report", T.pre["\n".join(self._html(cr.get_report()))])
   90.         add("Share Counts",
   91.             "need %d-of-%d, have %d" % (data["count-shares-needed"],
   92.                                         data["count-shares-expected"],
   93.                                         data["count-shares-good"]))
   94.         add("Hosts with good shares", data["count-good-share-hosts"])
   95. 
   96.         if data["list-corrupt-shares"]:
   97.             badsharemap = []
   98.             for (serverid, si, shnum) in data["list-corrupt-shares"]:
   99.                 nickname = sb.get_nickname_for_serverid(serverid)
  100.                 badsharemap.append(T.tr[T.td["sh#%d" % shnum],
  101.                                         T.td[T.div(class_="nickname")[nickname],
  102.                                               T.div(class_="nodeid")[T.tt[base32.b2a(serverid)]]],
  103.                                         ])
  104.             add("Corrupt shares", T.table()[
  105.                 T.tr[T.th["Share ID"],
  106.                      T.th(class_="nickname-and-peerid")[T.div["Nickname"], T.div(class_="nodeid")["Node ID"]]],
  107.                 badsharemap])
  108.         else:
  109.             add("Corrupt shares", "none")
  110. 
  111.         add("Wrong Shares", data["count-wrong-shares"])
  112. 
  113.         sharemap = []
  114.         servers = {}
  115. 
  116.         # 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.
  117. 
  118.         for shareid in sorted(data["sharemap"].keys()):
  119.             serverids = data["sharemap"][shareid]
  120.             for i,serverid in enumerate(serverids):
  121.                 if serverid not in servers:
  122.                     servers[serverid] = []
  123.                 servers[serverid].append(shareid)
  124.                 shareid_s = ""
  125.                 if i == 0:
  126.                     shareid_s = shareid
  127.                 nickname = sb.get_nickname_for_serverid(serverid)
  128.                 sharemap.append(T.tr[T.td[shareid_s],
  129.                                      T.td[T.div(class_="nickname")[nickname],
  130.                                           T.div(class_="nodeid")[T.tt[base32.b2a(serverid)]]]
  131.                                      ])
  132.         add("Good Shares (sorted in share order)",
  133.             T.table()[T.tr[T.th["Share ID"], T.th(class_="nickname-and-peerid")[T.div["Nickname"], T.div(class_="nodeid")["Node ID"]]],
  134.                       sharemap])
  135. 
  136. 
  137.         add("Recoverable Versions", data["count-recoverable-versions"])
  138.         add("Unrecoverable Versions", data["count-unrecoverable-versions"])
  139. 
  140.         # this table is sorted by permuted order
  141.         sb = c.get_storage_broker()
  142.         permuted_peer_ids = [peerid
  143.                              for (peerid, rref)
  144.                              in sb.get_servers_for_index(cr.get_storage_index())]
  145. 
  146.         num_shares_left = sum([len(shares) for shares in servers.values()])
  147.         servermap = []
  148.         for serverid in permuted_peer_ids:
  149.             nickname = sb.get_nickname_for_serverid(serverid)
  150.             shareids = servers.get(serverid, [])
  151.             shareids.reverse()
  152.             shareids_s = [ T.tt[shareid, " "] for shareid in sorted(shareids) ]
  153.             servermap.append(T.tr[T.td[T.div(class_="nickname")[nickname],
  154.                                        T.div(class_="nodeid")[T.tt[base32.b2a(serverid)]]],
  155.                                   T.td[shareids_s],
  156.                                   ])
  157.             num_shares_left -= len(shareids)
  158.             if not num_shares_left:
  159.                 break
  160.         add("Share Balancing (servers in permuted order)",
  161.             T.table()[T.tr[T.th(class_="nickname-and-peerid")[T.div["Nickname"], T.div(class_="nodeid")["Node ID"]], T.th["Share IDs"]],
  162.                       servermap])
  163. 
  164.         return T.ul[r]
  165. 
  166.     def _html(self, s):
  167.         if isinstance(s, (str, unicode)):
  168.             return html.escape(s)
  169.         assert isinstance(s, (list, tuple))
  170.         return [html.escape(w) for w in s]
  171. 
  172.     def want_json(self, ctx):
  173.         output = get_arg(inevow.IRequest(ctx), "output", "").lower()
  174.         if output.lower() == "json":
  175.             return True
  176.         return False
  177. 
  178.     def _render_si_link(self, ctx, storage_index):
  179.         si_s = base32.b2a(storage_index)
  180.         root = get_root(ctx)
  181.         req = inevow.IRequest(ctx)
  182.         ophandle = req.prepath[-1]
  183.         target = "%s/operations/%s/%s" % (get_root(ctx), ophandle, si_s)
  184.         output = get_arg(ctx, "output")
  185.         if output:
  186.             target = target + "?output=%s" % output
  187.         return T.a(href=target)[si_s]
  188. 
  189. class LiteralCheckResults(rend.Page, ResultsBase):
  190.     docFactory = getxmlfile("literal-check-results.xhtml")
  191. 
  192.     def __init__(self, client):
  193.         self.client = client
  194.         rend.Page.__init__(self, client)
  195. 
  196.     def renderHTTP(self, ctx):
  197.         if self.want_json(ctx):
  198.             return self.json(ctx)
  199.         return rend.Page.renderHTTP(self, ctx)
  200. 
  201.     def json(self, ctx):
  202.         inevow.IRequest(ctx).setHeader("content-type", "text/plain")
  203.         data = json_check_results(None)
  204.         return simplejson.dumps(data, indent=1) + "\n"
  205. 
  206.     def render_return(self, ctx, data):
  207.         req = inevow.IRequest(ctx)
  208.         return_to = get_arg(req, "return_to", None)
  209.         if return_to:
  210.             return T.div[T.a(href=return_to)["Return to file."]]
  211.         return ""
  212. 
  213. class CheckerBase:
  214. 
  215.     def renderHTTP(self, ctx):
  216.         if self.want_json(ctx):
  217.             return self.json(ctx)
  218.         return rend.Page.renderHTTP(self, ctx)
  219. 
  220.     def render_storage_index(self, ctx, data):
  221.         return self.r.get_storage_index_string()
  222. 
  223.     def render_return(self, ctx, data):
  224.         req = inevow.IRequest(ctx)
  225.         return_to = get_arg(req, "return_to", None)
  226.         if return_to:
  227.             return T.div[T.a(href=return_to)["Return to file/directory."]]
  228.         return ""
  229. 
  230. class CheckResults(CheckerBase, rend.Page, ResultsBase):
  231.     docFactory = getxmlfile("check-results.xhtml")
  232. 
  233.     def __init__(self, client, results):
  234.         self.client = client
  235.         self.r = ICheckResults(results)
  236.         rend.Page.__init__(self, results)
  237. 
  238.     def json(self, ctx):
  239.         inevow.IRequest(ctx).setHeader("content-type", "text/plain")
  240.         data = json_check_results(self.r)
  241.         return simplejson.dumps(data, indent=1) + "\n"
  242. 
  243.     def render_summary(self, ctx, data):
  244.         results = []
  245.         if data.is_healthy():
  246.             results.append("Healthy")
  247.         elif data.is_recoverable():
  248.             results.append("Not Healthy!")
  249.         else:
  250.             results.append("Not Recoverable!")
  251.         results.append(" : ")
  252.         results.append(self._html(data.get_summary()))
  253.         return ctx.tag[results]
  254. 
  255.     def render_repair(self, ctx, data):
  256.         if data.is_healthy():
  257.             return ""
  258.         repair = T.form(action=".", method="post",
  259.                         enctype="multipart/form-data")[
  260.             T.fieldset[
  261.             T.input(type="hidden", name="t", value="check"),
  262.             T.input(type="hidden", name="repair", value="true"),
  263.             T.input(type="submit", value="Repair"),
  264.             ]]
  265.         return "" # repair button disabled until we make it work correctly,
  266.                   # see #622 for details
  267.         return ctx.tag[repair]
  268. 
  269.     def render_results(self, ctx, data):
  270.         cr = self._render_results(ctx, data)
  271.         return ctx.tag[cr]
  272. 
  273. class CheckAndRepairResults(CheckerBase, rend.Page, ResultsBase):
  274.     docFactory = getxmlfile("check-and-repair-results.xhtml")
  275. 
  276.     def __init__(self, client, results):
  277.         self.client = client
  278.         self.r = None
  279.         if results:
  280.             self.r = ICheckAndRepairResults(results)
  281.         rend.Page.__init__(self, results)
  282. 
  283.     def json(self, ctx):
  284.         inevow.IRequest(ctx).setHeader("content-type", "text/plain")
  285.         data = json_check_and_repair_results(self.r)
  286.         return simplejson.dumps(data, indent=1) + "\n"
  287. 
  288.     def render_summary(self, ctx, data):
  289.         cr = data.get_post_repair_results()
  290.         results = []
  291.         if cr.is_healthy():
  292.             results.append("Healthy")
  293.         elif cr.is_recoverable():
  294.             results.append("Not Healthy!")
  295.         else:
  296.             results.append("Not Recoverable!")
  297.         results.append(" : ")
  298.         results.append(self._html(cr.get_summary()))
  299.         return ctx.tag[results]
  300. 
  301.     def render_repair_results(self, ctx, data):
  302.         if data.get_repair_attempted():
  303.             if data.get_repair_successful():
  304.                 return ctx.tag["Repair successful"]
  305.             else:
  306.                 return ctx.tag["Repair unsuccessful"]
  307.         return ctx.tag["No repair necessary"]
  308. 
  309.     def render_post_repair_results(self, ctx, data):
  310.         cr = self._render_results(ctx, data.get_post_repair_results())
  311.         return ctx.tag[T.div["Post-Repair Checker Results:"], cr]
  312. 
  313.     def render_maybe_pre_repair_results(self, ctx, data):
  314.         if data.get_repair_attempted():
  315.             cr = self._render_results(ctx, data.get_pre_repair_results())
  316.             return ctx.tag[T.div["Pre-Repair Checker Results:"], cr]
  317.         return ""
  318. 
  319. 
  320. class DeepCheckResults(rend.Page, ResultsBase, ReloadMixin):
  321.     docFactory = getxmlfile("deep-check-results.xhtml")
  322. 
  323.     def __init__(self, client, monitor):
  324.         self.client = client
  325.         self.monitor = monitor
  326. 
  327.     def childFactory(self, ctx, name):
  328.         if not name:
  329.             return self
  330.         # /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
  331.         # about a specific file or directory that was checked
  332.         si = base32.a2b(name)
  333.         r = self.monitor.get_status()
  334.         try:
  335.             return CheckResults(self.client,
  336.                                 r.get_results_for_storage_index(si))
  337.         except KeyError:
  338.             raise WebError("No detailed results for SI %s" % html.escape(name),
  339.                            http.NOT_FOUND)
  340. 
  341.     def renderHTTP(self, ctx):
  342.         if self.want_json(ctx):
  343.             return self.json(ctx)
  344.         return rend.Page.renderHTTP(self, ctx)
  345. 
  346.     def json(self, ctx):
  347.         inevow.IRequest(ctx).setHeader("content-type", "text/plain")
  348.         data = {}
  349.         data["finished"] = self.monitor.is_finished()
  350.         res = self.monitor.get_status()
  351.         data["root-storage-index"] = res.get_root_storage_index_string()
  352.         c = res.get_counters()
  353.         data["count-objects-checked"] = c["count-objects-checked"]
  354.         data["count-objects-healthy"] = c["count-objects-healthy"]
  355.         data["count-objects-unhealthy"] = c["count-objects-unhealthy"]
  356.         data["count-corrupt-shares"] = c["count-corrupt-shares"]
  357.         data["list-corrupt-shares"] = [ (idlib.nodeid_b2a(serverid),
  358.                                          base32.b2a(storage_index),
  359.                                          shnum)
  360.                                         for (serverid, storage_index, shnum)
  361.                                         in res.get_corrupt_shares() ]
  362.         data["list-unhealthy-files"] = [ (path_t, json_check_results(r))
  363.                                          for (path_t, r)
  364.                                          in res.get_all_results().items()
  365.                                          if not r.is_healthy() ]
  366.         data["stats"] = res.get_stats()
  367.         return simplejson.dumps(data, indent=1) + "\n"
  368. 
  369.     def render_root_storage_index(self, ctx, data):
  370.         return self.monitor.get_status().get_root_storage_index_string()
  371. 
  372.     def data_objects_checked(self, ctx, data):
  373.         return self.monitor.get_status().get_counters()["count-objects-checked"]
  374.     def data_objects_healthy(self, ctx, data):
  375.         return self.monitor.get_status().get_counters()["count-objects-healthy"]
  376.     def data_objects_unhealthy(self, ctx, data):
  377.         return self.monitor.get_status().get_counters()["count-objects-unhealthy"]
  378.     def data_objects_unrecoverable(self, ctx, data):
  379.         return self.monitor.get_status().get_counters()["count-objects-unrecoverable"]
  380. 
  381.     def data_count_corrupt_shares(self, ctx, data):
  382.         return self.monitor.get_status().get_counters()["count-corrupt-shares"]
  383. 
  384.     def render_problems_p(self, ctx, data):
  385.         c = self.monitor.get_status().get_counters()
  386.         if c["count-objects-unhealthy"]:
  387.             return ctx.tag
  388.         return ""
  389. 
  390.     def data_problems(self, ctx, data):
  391.         all_objects = self.monitor.get_status().get_all_results()
  392.         for path in sorted(all_objects.keys()):
  393.             cr = all_objects[path]
  394.             assert ICheckResults.providedBy(cr)
  395.             if not cr.is_healthy():
  396.                 yield path, cr
  397. 
  398.     def render_problem(self, ctx, data):
  399.         path, cr = data
  400.         summary_text = ""
  401.         summary = cr.get_summary()
  402.         if summary:
  403.             summary_text = ": " + summary
  404.         summary_text += " [SI: %s]" % cr.get_storage_index_string()
  405.         return ctx.tag[self._join_pathstring(path), self._html(summary_text)]
  406. 
  407. 
  408.     def render_servers_with_corrupt_shares_p(self, ctx, data):
  409.         if self.monitor.get_status().get_counters()["count-corrupt-shares"]:
  410.             return ctx.tag
  411.         return ""
  412. 
  413.     def data_servers_with_corrupt_shares(self, ctx, data):
  414.         servers = [serverid
  415.                    for (serverid, storage_index, sharenum)
  416.                    in self.monitor.get_status().get_corrupt_shares()]
  417.         servers.sort()
  418.         return servers
  419. 
  420.     def render_server_problem(self, ctx, data):
  421.         serverid = data
  422.         data = [idlib.shortnodeid_b2a(serverid)]
  423.         sb = self.client.get_storage_broker()
  424.         nickname = sb.get_nickname_for_serverid(serverid)
  425.         if nickname:
  426.             data.append(" (%s)" % self._html(nickname))
  427.         return ctx.tag[data]
  428. 
  429. 
  430.     def render_corrupt_shares_p(self, ctx, data):
  431.         if self.monitor.get_status().get_counters()["count-corrupt-shares"]:
  432.             return ctx.tag
  433.         return ""
  434.     def data_corrupt_shares(self, ctx, data):
  435.         return self.monitor.get_status().get_corrupt_shares()
  436.     def render_share_problem(self, ctx, data):
  437.         serverid, storage_index, sharenum = data
  438.         sb = self.client.get_storage_broker()
  439.         nickname = sb.get_nickname_for_serverid(serverid)
  440.         ctx.fillSlots("serverid", idlib.shortnodeid_b2a(serverid))
  441.         if nickname:
  442.             ctx.fillSlots("nickname", self._html(nickname))
  443.         ctx.fillSlots("si", self._render_si_link(ctx, storage_index))
  444.         ctx.fillSlots("shnum", str(sharenum))
  445.         return ctx.tag
  446. 
  447.     def render_return(self, ctx, data):
  448.         req = inevow.IRequest(ctx)
  449.         return_to = get_arg(req, "return_to", None)
  450.         if return_to:
  451.             return T.div[T.a(href=return_to)["Return to file/directory."]]
  452.         return ""
  453. 
  454.     def data_all_objects(self, ctx, data):
  455.         r = self.monitor.get_status().get_all_results()
  456.         for path in sorted(r.keys()):
  457.             yield (path, r[path])
  458. 
  459.     def render_object(self, ctx, data):
  460.         path, r = data
  461.         ctx.fillSlots("path", self._join_pathstring(path))
  462.         ctx.fillSlots("healthy", str(r.is_healthy()))
  463.         ctx.fillSlots("recoverable", str(r.is_recoverable()))
  464.         storage_index = r.get_storage_index()
  465.         ctx.fillSlots("storage_index", self._render_si_link(ctx, storage_index))
  466.         ctx.fillSlots("summary", self._html(r.get_summary()))
  467.         return ctx.tag
  468. 
  469.     def render_runtime(self, ctx, data):
  470.         req = inevow.IRequest(ctx)
  471.         runtime = time.time() - req.processing_started_timestamp
  472.         return ctx.tag["runtime: %s seconds" % runtime]
  473. 
  474. class DeepCheckAndRepairResults(rend.Page, ResultsBase, ReloadMixin):
  475.     docFactory = getxmlfile("deep-check-and-repair-results.xhtml")
  476. 
  477.     def __init__(self, client, monitor):
  478.         self.client = client
  479.         self.monitor = monitor
  480. 
  481.     def childFactory(self, ctx, name):
  482.         if not name:
  483.             return self
  484.         # /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
  485.         # about a specific file or directory that was checked
  486.         si = base32.a2b(name)
  487.         r = self.monitor.get_status()
  488.         try:
  489.             return CheckAndRepairResults(self.client,
  490.                                          r.get_results_for_storage_index(si))
  491.         except KeyError:
  492.             raise WebError("No detailed results for SI %s" % html.escape(name),
  493.                            http.NOT_FOUND)
  494. 
  495.     def renderHTTP(self, ctx):
  496.         if self.want_json(ctx):
  497.             return self.json(ctx)
  498.         return rend.Page.renderHTTP(self, ctx)
  499. 
  500.     def json(self, ctx):
  501.         inevow.IRequest(ctx).setHeader("content-type", "text/plain")
  502.         res = self.monitor.get_status()
  503.         data = {}
  504.         data["finished"] = self.monitor.is_finished()
  505.         data["root-storage-index"] = res.get_root_storage_index_string()
  506.         c = res.get_counters()
  507.         data["count-objects-checked"] = c["count-objects-checked"]
  508. 
  509.         data["count-objects-healthy-pre-repair"] = c["count-objects-healthy-pre-repair"]
  510.         data["count-objects-unhealthy-pre-repair"] = c["count-objects-unhealthy-pre-repair"]
  511.         data["count-objects-healthy-post-repair"] = c["count-objects-healthy-post-repair"]
  512.         data["count-objects-unhealthy-post-repair"] = c["count-objects-unhealthy-post-repair"]
  513. 
  514.         data["count-repairs-attempted"] = c["count-repairs-attempted"]
  515.         data["count-repairs-successful"] = c["count-repairs-successful"]
  516.         data["count-repairs-unsuccessful"] = c["count-repairs-unsuccessful"]
  517. 
  518.         data["count-corrupt-shares-pre-repair"] = c["count-corrupt-shares-pre-repair"]
  519.         data["count-corrupt-shares-post-repair"] = c["count-corrupt-shares-pre-repair"]
  520. 
  521.         data["list-corrupt-shares"] = [ (idlib.nodeid_b2a(serverid),
  522.                                          base32.b2a(storage_index),
  523.                                          shnum)
  524.                                         for (serverid, storage_index, shnum)
  525.                                         in res.get_corrupt_shares() ]
  526. 
  527.         remaining_corrupt = [ (idlib.nodeid_b2a(serverid),
  528.                                base32.b2a(storage_index),
  529.                                shnum)
  530.                               for (serverid, storage_index, shnum)
  531.                               in res.get_remaining_corrupt_shares() ]
  532.         data["list-remaining-corrupt-shares"] = remaining_corrupt
  533. 
  534.         unhealthy = [ (path_t,
  535.                        json_check_results(crr.get_pre_repair_results()))
  536.                       for (path_t, crr)
  537.                       in res.get_all_results().items()
  538.                       if not crr.get_pre_repair_results().is_healthy() ]
  539.         data["list-unhealthy-files"] = unhealthy
  540.         data["stats"] = res.get_stats()
  541.         return simplejson.dumps(data, indent=1) + "\n"
  542. 
  543.     def render_root_storage_index(self, ctx, data):
  544.         return self.monitor.get_status().get_root_storage_index_string()
  545. 
  546.     def data_objects_checked(self, ctx, data):
  547.         return self.monitor.get_status().get_counters()["count-objects-checked"]
  548. 
  549.     def data_objects_healthy(self, ctx, data):
  550.         return self.monitor.get_status().get_counters()["count-objects-healthy-pre-repair"]
  551.     def data_objects_unhealthy(self, ctx, data):
  552.         return self.monitor.get_status().get_counters()["count-objects-unhealthy-pre-repair"]
  553.     def data_corrupt_shares(self, ctx, data):
  554.         return self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"]
  555. 
  556.     def data_repairs_attempted(self, ctx, data):
  557.         return self.monitor.get_status().get_counters()["count-repairs-attempted"]
  558.     def data_repairs_successful(self, ctx, data):
  559.         return self.monitor.get_status().get_counters()["count-repairs-successful"]
  560.     def data_repairs_unsuccessful(self, ctx, data):
  561.         return self.monitor.get_status().get_counters()["count-repairs-unsuccessful"]
  562. 
  563.     def data_objects_healthy_post(self, ctx, data):
  564.         return self.monitor.get_status().get_counters()["count-objects-healthy-post-repair"]
  565.     def data_objects_unhealthy_post(self, ctx, data):
  566.         return self.monitor.get_status().get_counters()["count-objects-unhealthy-post-repair"]
  567.     def data_corrupt_shares_post(self, ctx, data):
  568.         return self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"]
  569. 
  570.     def render_pre_repair_problems_p(self, ctx, data):
  571.         c = self.monitor.get_status().get_counters()
  572.         if c["count-objects-unhealthy-pre-repair"]:
  573.             return ctx.tag
  574.         return ""
  575. 
  576.     def data_pre_repair_problems(self, ctx, data):
  577.         all_objects = self.monitor.get_status().get_all_results()
  578.         for path in sorted(all_objects.keys()):
  579.             r = all_objects[path]
  580.             assert ICheckAndRepairResults.providedBy(r)
  581.             cr = r.get_pre_repair_results()
  582.             if not cr.is_healthy():
  583.                 yield path, cr
  584. 
  585.     def render_problem(self, ctx, data):
  586.         path, cr = data
  587.         return ctx.tag[self._join_pathstring(path), ": ",
  588.                        self._html(cr.get_summary())]
  589. 
  590.     def render_post_repair_problems_p(self, ctx, data):
  591.         c = self.monitor.get_status().get_counters()
  592.         if (c["count-objects-unhealthy-post-repair"]
  593.             or c["count-corrupt-shares-post-repair"]):
  594.             return ctx.tag
  595.         return ""
  596. 
  597.     def data_post_repair_problems(self, ctx, data):
  598.         all_objects = self.monitor.get_status().get_all_results()
  599.         for path in sorted(all_objects.keys()):
  600.             r = all_objects[path]
  601.             assert ICheckAndRepairResults.providedBy(r)
  602.             cr = r.get_post_repair_results()
  603.             if not cr.is_healthy():
  604.                 yield path, cr
  605. 
  606.     def render_servers_with_corrupt_shares_p(self, ctx, data):
  607.         if self.monitor.get_status().get_counters()["count-corrupt-shares-pre-repair"]:
  608.             return ctx.tag
  609.         return ""
  610.     def data_servers_with_corrupt_shares(self, ctx, data):
  611.         return [] # TODO
  612.     def render_server_problem(self, ctx, data):
  613.         pass
  614. 
  615. 
  616.     def render_remaining_corrupt_shares_p(self, ctx, data):
  617.         if self.monitor.get_status().get_counters()["count-corrupt-shares-post-repair"]:
  618.             return ctx.tag
  619.         return ""
  620.     def data_post_repair_corrupt_shares(self, ctx, data):
  621.         return [] # TODO
  622. 
  623.     def render_share_problem(self, ctx, data):
  624.         pass
  625. 
  626. 
  627.     def render_return(self, ctx, data):
  628.         req = inevow.IRequest(ctx)
  629.         return_to = get_arg(req, "return_to", None)
  630.         if return_to:
  631.             return T.div[T.a(href=return_to)["Return to file/directory."]]
  632.         return ""
  633. 
  634.     def data_all_objects(self, ctx, data):
  635.         r = self.monitor.get_status().get_all_results()
  636.         for path in sorted(r.keys()):
  637.             yield (path, r[path])
  638. 
  639.     def render_object(self, ctx, data):
  640.         path, r = data
  641.         ctx.fillSlots("path", self._join_pathstring(path))
  642.         ctx.fillSlots("healthy_pre_repair",
  643.                       str(r.get_pre_repair_results().is_healthy()))
  644.         ctx.fillSlots("recoverable_pre_repair",
  645.                       str(r.get_pre_repair_results().is_recoverable()))
  646.         ctx.fillSlots("healthy_post_repair",
  647.                       str(r.get_post_repair_results().is_healthy()))
  648.         storage_index = r.get_storage_index()
  649.         ctx.fillSlots("storage_index",
  650.                       self._render_si_link(ctx, storage_index))
  651.         ctx.fillSlots("summary",
  652.                       self._html(r.get_pre_repair_results().get_summary()))
  653.         return ctx.tag
  654. 
  655.     def render_runtime(self, ctx, data):
  656.         req = inevow.IRequest(ctx)
  657.         runtime = time.time() - req.processing_started_timestamp
  658.         return ctx.tag["runtime: %s seconds" % runtime]