]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/web/check_results.py
more refactoring: move get_all_serverids() and get_nickname_for_serverid() from Clien...
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / web / check_results.py
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(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]