source file: /home/buildslave/tahoe/edgy/build/src/allmydata/web/status.py
file stats: 772 lines, 721 executed: 93.4% covered
coverage versus previous test: 0 lines added, 1 lines removed
    1. 
    2. import time, pprint, itertools
    3. import simplejson
    4. from twisted.internet import defer
    5. from nevow import rend, inevow, tags as T
    6. from allmydata.util import base32, idlib
    7. from allmydata.web.common import getxmlfile, get_arg, \
    8.      abbreviate_time, abbreviate_rate, abbreviate_size, plural
    9. from allmydata.interfaces import IUploadStatus, IDownloadStatus, \
   10.      IPublishStatus, IRetrieveStatus, IServermapUpdaterStatus
   11. 
   12. class RateAndTimeMixin:
   13. 
   14.     def render_time(self, ctx, data):
   15.         return abbreviate_time(data)
   16. 
   17.     def render_rate(self, ctx, data):
   18.         return abbreviate_rate(data)
   19. 
   20. class UploadResultsRendererMixin(RateAndTimeMixin):
   21.     # this requires a method named 'upload_results'
   22. 
   23.     def render_pushed_shares(self, ctx, data):
   24.         d = self.upload_results()
   25.         d.addCallback(lambda res: res.pushed_shares)
   26.         return d
   27. 
   28.     def render_preexisting_shares(self, ctx, data):
   29.         d = self.upload_results()
   30.         d.addCallback(lambda res: res.preexisting_shares)
   31.         return d
   32. 
   33.     def render_sharemap(self, ctx, data):
   34.         d = self.upload_results()
   35.         d.addCallback(lambda res: res.sharemap)
   36.         def _render(sharemap):
   37.             if sharemap is None:
   38.                 return "None"
   39.             l = T.ul()
   40.             for shnum, peerids in sorted(sharemap.items()):
   41.                 peerids = ', '.join([idlib.shortnodeid_b2a(i) for i in peerids])
   42.                 l[T.li["%d -> placed on [%s]" % (shnum, peerids)]]
   43.             return l
   44.         d.addCallback(_render)
   45.         return d
   46. 
   47.     def render_servermap(self, ctx, data):
   48.         d = self.upload_results()
   49.         d.addCallback(lambda res: res.servermap)
   50.         def _render(servermap):
   51.             if servermap is None:
   52.                 return "None"
   53.             l = T.ul()
   54.             for peerid in sorted(servermap.keys()):
   55.                 peerid_s = idlib.shortnodeid_b2a(peerid)
   56.                 shares_s = ",".join(["#%d" % shnum
   57.                                      for shnum in servermap[peerid]])
   58.                 l[T.li["[%s] got share%s: %s" % (peerid_s,
   59.                                                  plural(servermap[peerid]),
   60.                                                  shares_s)]]
   61.             return l
   62.         d.addCallback(_render)
   63.         return d
   64. 
   65.     def data_file_size(self, ctx, data):
   66.         d = self.upload_results()
   67.         d.addCallback(lambda res: res.file_size)
   68.         return d
   69. 
   70.     def _get_time(self, name):
   71.         d = self.upload_results()
   72.         d.addCallback(lambda res: res.timings.get(name))
   73.         return d
   74. 
   75.     def data_time_total(self, ctx, data):
   76.         return self._get_time("total")
   77. 
   78.     def data_time_storage_index(self, ctx, data):
   79.         return self._get_time("storage_index")
   80. 
   81.     def data_time_contacting_helper(self, ctx, data):
   82.         return self._get_time("contacting_helper")
   83. 
   84.     def data_time_existence_check(self, ctx, data):
   85.         return self._get_time("existence_check")
   86. 
   87.     def data_time_cumulative_fetch(self, ctx, data):
   88.         return self._get_time("cumulative_fetch")
   89. 
   90.     def data_time_helper_total(self, ctx, data):
   91.         return self._get_time("helper_total")
   92. 
   93.     def data_time_peer_selection(self, ctx, data):
   94.         return self._get_time("peer_selection")
   95. 
   96.     def data_time_total_encode_and_push(self, ctx, data):
   97.         return self._get_time("total_encode_and_push")
   98. 
   99.     def data_time_cumulative_encoding(self, ctx, data):
  100.         return self._get_time("cumulative_encoding")
  101. 
  102.     def data_time_cumulative_sending(self, ctx, data):
  103.         return self._get_time("cumulative_sending")
  104. 
  105.     def data_time_hashes_and_close(self, ctx, data):
  106.         return self._get_time("hashes_and_close")
  107. 
  108.     def _get_rate(self, name):
  109.         d = self.upload_results()
  110.         def _convert(r):
  111.             file_size = r.file_size
  112.             time = r.timings.get(name)
  113.             if time is None:
  114.                 return None
  115.             try:
  116.                 return 1.0 * file_size / time
  117.             except ZeroDivisionError:
  118.                 return None
  119.         d.addCallback(_convert)
  120.         return d
  121. 
  122.     def data_rate_total(self, ctx, data):
  123.         return self._get_rate("total")
  124. 
  125.     def data_rate_storage_index(self, ctx, data):
  126.         return self._get_rate("storage_index")
  127. 
  128.     def data_rate_encode(self, ctx, data):
  129.         return self._get_rate("cumulative_encoding")
  130. 
  131.     def data_rate_push(self, ctx, data):
  132.         return self._get_rate("cumulative_sending")
  133. 
  134.     def data_rate_encode_and_push(self, ctx, data):
  135.         d = self.upload_results()
  136.         def _convert(r):
  137.             file_size = r.file_size
  138.             time1 = r.timings.get("cumulative_encoding")
  139.             time2 = r.timings.get("cumulative_sending")
  140.             if (file_size is None or time1 is None or time2 is None):
  141.                 return None
  142.             try:
  143.                 return 1.0 * file_size / (time1+time2)
  144.             except ZeroDivisionError:
  145.                 return None
  146.         d.addCallback(_convert)
  147.         return d
  148. 
  149.     def data_rate_ciphertext_fetch(self, ctx, data):
  150.         d = self.upload_results()
  151.         def _convert(r):
  152.             fetch_size = r.ciphertext_fetched
  153.             time = r.timings.get("cumulative_fetch")
  154.             if (fetch_size is None or time is None):
  155.                 return None
  156.             try:
  157.                 return 1.0 * fetch_size / time
  158.             except ZeroDivisionError:
  159.                 return None
  160.         d.addCallback(_convert)
  161.         return d
  162. 
  163. class UploadStatusPage(UploadResultsRendererMixin, rend.Page):
  164.     docFactory = getxmlfile("upload-status.xhtml")
  165. 
  166.     def __init__(self, data):
  167.         rend.Page.__init__(self, data)
  168.         self.upload_status = data
  169. 
  170.     def upload_results(self):
  171.         return defer.maybeDeferred(self.upload_status.get_results)
  172. 
  173.     def render_results(self, ctx, data):
  174.         d = self.upload_results()
  175.         def _got_results(results):
  176.             if results:
  177.                 return ctx.tag
  178.             return ""
  179.         d.addCallback(_got_results)
  180.         return d
  181. 
  182.     def render_started(self, ctx, data):
  183.         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
  184.         started_s = time.strftime(TIME_FORMAT,
  185.                                   time.localtime(data.get_started()))
  186.         return started_s
  187. 
  188.     def render_si(self, ctx, data):
  189.         si_s = base32.b2a_or_none(data.get_storage_index())
  190.         if si_s is None:
  191.             si_s = "(None)"
  192.         return si_s
  193. 
  194.     def render_helper(self, ctx, data):
  195.         return {True: "Yes",
  196.                 False: "No"}[data.using_helper()]
  197. 
  198.     def render_total_size(self, ctx, data):
  199.         size = data.get_size()
  200.         if size is None:
  201.             return "(unknown)"
  202.         return size
  203. 
  204.     def render_progress_hash(self, ctx, data):
  205.         progress = data.get_progress()[0]
  206.         # TODO: make an ascii-art bar
  207.         return "%.1f%%" % (100.0 * progress)
  208. 
  209.     def render_progress_ciphertext(self, ctx, data):
  210.         progress = data.get_progress()[1]
  211.         # TODO: make an ascii-art bar
  212.         return "%.1f%%" % (100.0 * progress)
  213. 
  214.     def render_progress_encode_push(self, ctx, data):
  215.         progress = data.get_progress()[2]
  216.         # TODO: make an ascii-art bar
  217.         return "%.1f%%" % (100.0 * progress)
  218. 
  219.     def render_status(self, ctx, data):
  220.         return data.get_status()
  221. 
  222. class DownloadResultsRendererMixin(RateAndTimeMixin):
  223.     # this requires a method named 'download_results'
  224. 
  225.     def render_servermap(self, ctx, data):
  226.         d = self.download_results()
  227.         d.addCallback(lambda res: res.servermap)
  228.         def _render(servermap):
  229.             if servermap is None:
  230.                 return "None"
  231.             l = T.ul()
  232.             for peerid in sorted(servermap.keys()):
  233.                 peerid_s = idlib.shortnodeid_b2a(peerid)
  234.                 shares_s = ",".join(["#%d" % shnum
  235.                                      for shnum in servermap[peerid]])
  236.                 l[T.li["[%s] has share%s: %s" % (peerid_s,
  237.                                                  plural(servermap[peerid]),
  238.                                                  shares_s)]]
  239.             return l
  240.         d.addCallback(_render)
  241.         return d
  242. 
  243.     def render_servers_used(self, ctx, data):
  244.         d = self.download_results()
  245.         d.addCallback(lambda res: res.servers_used)
  246.         def _got(servers_used):
  247.             if not servers_used:
  248.                 return ""
  249.             peerids_s = ", ".join(["[%s]" % idlib.shortnodeid_b2a(peerid)
  250.                                    for peerid in servers_used])
  251.             return T.li["Servers Used: ", peerids_s]
  252.         d.addCallback(_got)
  253.         return d
  254. 
  255.     def render_problems(self, ctx, data):
  256.         d = self.download_results()
  257.         d.addCallback(lambda res: res.server_problems)
  258.         def _got(server_problems):
  259.             if not server_problems:
  260.                 return ""
  261.             l = T.ul()
  262.             for peerid in sorted(server_problems.keys()):
  263.                 peerid_s = idlib.shortnodeid_b2a(peerid)
  264.                 l[T.li["[%s]: %s" % (peerid_s, server_problems[peerid])]]
  265.             return T.li["Server Problems:", l]
  266.         d.addCallback(_got)
  267.         return d
  268. 
  269.     def data_file_size(self, ctx, data):
  270.         d = self.download_results()
  271.         d.addCallback(lambda res: res.file_size)
  272.         return d
  273. 
  274.     def _get_time(self, name):
  275.         d = self.download_results()
  276.         d.addCallback(lambda res: res.timings.get(name))
  277.         return d
  278. 
  279.     def data_time_total(self, ctx, data):
  280.         return self._get_time("total")
  281. 
  282.     def data_time_peer_selection(self, ctx, data):
  283.         return self._get_time("peer_selection")
  284. 
  285.     def data_time_uri_extension(self, ctx, data):
  286.         return self._get_time("uri_extension")
  287. 
  288.     def data_time_hashtrees(self, ctx, data):
  289.         return self._get_time("hashtrees")
  290. 
  291.     def data_time_segments(self, ctx, data):
  292.         return self._get_time("segments")
  293. 
  294.     def data_time_cumulative_fetch(self, ctx, data):
  295.         return self._get_time("cumulative_fetch")
  296. 
  297.     def data_time_cumulative_decode(self, ctx, data):
  298.         return self._get_time("cumulative_decode")
  299. 
  300.     def data_time_cumulative_decrypt(self, ctx, data):
  301.         return self._get_time("cumulative_decrypt")
  302. 
  303.     def data_time_paused(self, ctx, data):
  304.         return self._get_time("paused")
  305. 
  306.     def _get_rate(self, name):
  307.         d = self.download_results()
  308.         def _convert(r):
  309.             file_size = r.file_size
  310.             time = r.timings.get(name)
  311.             if time is None:
  312.                 return None
  313.             try:
  314.                 return 1.0 * file_size / time
  315.             except ZeroDivisionError:
  316.                 return None
  317.         d.addCallback(_convert)
  318.         return d
  319. 
  320.     def data_rate_total(self, ctx, data):
  321.         return self._get_rate("total")
  322. 
  323.     def data_rate_segments(self, ctx, data):
  324.         return self._get_rate("segments")
  325. 
  326.     def data_rate_fetch(self, ctx, data):
  327.         return self._get_rate("cumulative_fetch")
  328. 
  329.     def data_rate_decode(self, ctx, data):
  330.         return self._get_rate("cumulative_decode")
  331. 
  332.     def data_rate_decrypt(self, ctx, data):
  333.         return self._get_rate("cumulative_decrypt")
  334. 
  335.     def render_server_timings(self, ctx, data):
  336.         d = self.download_results()
  337.         d.addCallback(lambda res: res.timings.get("fetch_per_server"))
  338.         def _render(per_server):
  339.             if per_server is None:
  340.                 return ""
  341.             l = T.ul()
  342.             for peerid in sorted(per_server.keys()):
  343.                 peerid_s = idlib.shortnodeid_b2a(peerid)
  344.                 times_s = ", ".join([self.render_time(None, t)
  345.                                      for t in per_server[peerid]])
  346.                 l[T.li["[%s]: %s" % (peerid_s, times_s)]]
  347.             return T.li["Per-Server Segment Fetch Response Times: ", l]
  348.         d.addCallback(_render)
  349.         return d
  350. 
  351. class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page):
  352.     docFactory = getxmlfile("download-status.xhtml")
  353. 
  354.     def __init__(self, data):
  355.         rend.Page.__init__(self, data)
  356.         self.download_status = data
  357. 
  358.     def download_results(self):
  359.         return defer.maybeDeferred(self.download_status.get_results)
  360. 
  361.     def render_results(self, ctx, data):
  362.         d = self.download_results()
  363.         def _got_results(results):
  364.             if results:
  365.                 return ctx.tag
  366.             return ""
  367.         d.addCallback(_got_results)
  368.         return d
  369. 
  370.     def render_started(self, ctx, data):
  371.         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
  372.         started_s = time.strftime(TIME_FORMAT,
  373.                                   time.localtime(data.get_started()))
  374.         return started_s
  375. 
  376.     def render_si(self, ctx, data):
  377.         si_s = base32.b2a_or_none(data.get_storage_index())
  378.         if si_s is None:
  379.             si_s = "(None)"
  380.         return si_s
  381. 
  382.     def render_helper(self, ctx, data):
  383.         return {True: "Yes",
  384.                 False: "No"}[data.using_helper()]
  385. 
  386.     def render_total_size(self, ctx, data):
  387.         size = data.get_size()
  388.         if size is None:
  389.             return "(unknown)"
  390.         return size
  391. 
  392.     def render_progress(self, ctx, data):
  393.         progress = data.get_progress()
  394.         # TODO: make an ascii-art bar
  395.         return "%.1f%%" % (100.0 * progress)
  396. 
  397.     def render_status(self, ctx, data):
  398.         return data.get_status()
  399. 
  400. class RetrieveStatusPage(rend.Page, RateAndTimeMixin):
  401.     docFactory = getxmlfile("retrieve-status.xhtml")
  402. 
  403.     def __init__(self, data):
  404.         rend.Page.__init__(self, data)
  405.         self.retrieve_status = data
  406. 
  407.     def render_started(self, ctx, data):
  408.         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
  409.         started_s = time.strftime(TIME_FORMAT,
  410.                                   time.localtime(data.get_started()))
  411.         return started_s
  412. 
  413.     def render_si(self, ctx, data):
  414.         si_s = base32.b2a_or_none(data.get_storage_index())
  415.         if si_s is None:
  416.             si_s = "(None)"
  417.         return si_s
  418. 
  419.     def render_helper(self, ctx, data):
  420.         return {True: "Yes",
  421.                 False: "No"}[data.using_helper()]
  422. 
  423.     def render_current_size(self, ctx, data):
  424.         size = data.get_size()
  425.         if size is None:
  426.             size = "(unknown)"
  427.         return size
  428. 
  429.     def render_progress(self, ctx, data):
  430.         progress = data.get_progress()
  431.         # TODO: make an ascii-art bar
  432.         return "%.1f%%" % (100.0 * progress)
  433. 
  434.     def render_status(self, ctx, data):
  435.         return data.get_status()
  436. 
  437.     def render_encoding(self, ctx, data):
  438.         k, n = data.get_encoding()
  439.         return ctx.tag["Encoding: %s of %s" % (k, n)]
  440. 
  441.     def render_problems(self, ctx, data):
  442.         problems = data.problems
  443.         if not problems:
  444.             return ""
  445.         l = T.ul()
  446.         for peerid in sorted(problems.keys()):
  447.             peerid_s = idlib.shortnodeid_b2a(peerid)
  448.             l[T.li["[%s]: %s" % (peerid_s, problems[peerid])]]
  449.         return ctx.tag["Server Problems:", l]
  450. 
  451.     def _get_rate(self, data, name):
  452.         file_size = self.retrieve_status.get_size()
  453.         time = self.retrieve_status.timings.get(name)
  454.         if time is None or file_size is None:
  455.             return None
  456.         try:
  457.             return 1.0 * file_size / time
  458.         except ZeroDivisionError:
  459.             return None
  460. 
  461.     def data_time_total(self, ctx, data):
  462.         return self.retrieve_status.timings.get("total")
  463.     def data_rate_total(self, ctx, data):
  464.         return self._get_rate(data, "total")
  465. 
  466.     def data_time_fetch(self, ctx, data):
  467.         return self.retrieve_status.timings.get("fetch")
  468.     def data_rate_fetch(self, ctx, data):
  469.         return self._get_rate(data, "fetch")
  470. 
  471.     def data_time_decode(self, ctx, data):
  472.         return self.retrieve_status.timings.get("decode")
  473.     def data_rate_decode(self, ctx, data):
  474.         return self._get_rate(data, "decode")
  475. 
  476.     def data_time_decrypt(self, ctx, data):
  477.         return self.retrieve_status.timings.get("decrypt")
  478.     def data_rate_decrypt(self, ctx, data):
  479.         return self._get_rate(data, "decrypt")
  480. 
  481.     def render_server_timings(self, ctx, data):
  482.         per_server = self.retrieve_status.timings.get("fetch_per_server")
  483.         if not per_server:
  484.             return ""
  485.         l = T.ul()
  486.         for peerid in sorted(per_server.keys()):
  487.             peerid_s = idlib.shortnodeid_b2a(peerid)
  488.             times_s = ", ".join([self.render_time(None, t)
  489.                                  for t in per_server[peerid]])
  490.             l[T.li["[%s]: %s" % (peerid_s, times_s)]]
  491.         return T.li["Per-Server Fetch Response Times: ", l]
  492. 
  493. 
  494. class PublishStatusPage(rend.Page, RateAndTimeMixin):
  495.     docFactory = getxmlfile("publish-status.xhtml")
  496. 
  497.     def __init__(self, data):
  498.         rend.Page.__init__(self, data)
  499.         self.publish_status = data
  500. 
  501.     def render_started(self, ctx, data):
  502.         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
  503.         started_s = time.strftime(TIME_FORMAT,
  504.                                   time.localtime(data.get_started()))
  505.         return started_s
  506. 
  507.     def render_si(self, ctx, data):
  508.         si_s = base32.b2a_or_none(data.get_storage_index())
  509.         if si_s is None:
  510.             si_s = "(None)"
  511.         return si_s
  512. 
  513.     def render_helper(self, ctx, data):
  514.         return {True: "Yes",
  515.                 False: "No"}[data.using_helper()]
  516. 
  517.     def render_current_size(self, ctx, data):
  518.         size = data.get_size()
  519.         if size is None:
  520.             size = "(unknown)"
  521.         return size
  522. 
  523.     def render_progress(self, ctx, data):
  524.         progress = data.get_progress()
  525.         # TODO: make an ascii-art bar
  526.         return "%.1f%%" % (100.0 * progress)
  527. 
  528.     def render_status(self, ctx, data):
  529.         return data.get_status()
  530. 
  531.     def render_encoding(self, ctx, data):
  532.         k, n = data.get_encoding()
  533.         return ctx.tag["Encoding: %s of %s" % (k, n)]
  534. 
  535.     def render_sharemap(self, ctx, data):
  536.         servermap = data.get_servermap()
  537.         if servermap is None:
  538.             return ctx.tag["None"]
  539.         l = T.ul()
  540.         sharemap = servermap.make_sharemap()
  541.         for shnum in sorted(sharemap.keys()):
  542.             l[T.li["%d -> Placed on " % shnum,
  543.                    ", ".join(["[%s]" % idlib.shortnodeid_b2a(peerid)
  544.                               for peerid in sharemap[shnum]])]]
  545.         return ctx.tag["Sharemap:", l]
  546. 
  547.     def render_problems(self, ctx, data):
  548.         problems = data.problems
  549.         if not problems:
  550.             return ""
  551.         l = T.ul()
  552.         for peerid in sorted(problems.keys()):
  553.             peerid_s = idlib.shortnodeid_b2a(peerid)
  554.             l[T.li["[%s]: %s" % (peerid_s, problems[peerid])]]
  555.         return ctx.tag["Server Problems:", l]
  556. 
  557.     def _get_rate(self, data, name):
  558.         file_size = self.publish_status.get_size()
  559.         time = self.publish_status.timings.get(name)
  560.         if time is None:
  561.             return None
  562.         try:
  563.             return 1.0 * file_size / time
  564.         except ZeroDivisionError:
  565.             return None
  566. 
  567.     def data_time_total(self, ctx, data):
  568.         return self.publish_status.timings.get("total")
  569.     def data_rate_total(self, ctx, data):
  570.         return self._get_rate(data, "total")
  571. 
  572.     def data_time_setup(self, ctx, data):
  573.         return self.publish_status.timings.get("setup")
  574. 
  575.     def data_time_encrypt(self, ctx, data):
  576.         return self.publish_status.timings.get("encrypt")
  577.     def data_rate_encrypt(self, ctx, data):
  578.         return self._get_rate(data, "encrypt")
  579. 
  580.     def data_time_encode(self, ctx, data):
  581.         return self.publish_status.timings.get("encode")
  582.     def data_rate_encode(self, ctx, data):
  583.         return self._get_rate(data, "encode")
  584. 
  585.     def data_time_pack(self, ctx, data):
  586.         return self.publish_status.timings.get("pack")
  587.     def data_rate_pack(self, ctx, data):
  588.         return self._get_rate(data, "pack")
  589.     def data_time_sign(self, ctx, data):
  590.         return self.publish_status.timings.get("sign")
  591. 
  592.     def data_time_push(self, ctx, data):
  593.         return self.publish_status.timings.get("push")
  594.     def data_rate_push(self, ctx, data):
  595.         return self._get_rate(data, "push")
  596. 
  597.     def render_server_timings(self, ctx, data):
  598.         per_server = self.publish_status.timings.get("send_per_server")
  599.         if not per_server:
  600.             return ""
  601.         l = T.ul()
  602.         for peerid in sorted(per_server.keys()):
  603.             peerid_s = idlib.shortnodeid_b2a(peerid)
  604.             times_s = ", ".join([self.render_time(None, t)
  605.                                  for t in per_server[peerid]])
  606.             l[T.li["[%s]: %s" % (peerid_s, times_s)]]
  607.         return T.li["Per-Server Response Times: ", l]
  608. 
  609. class MapupdateStatusPage(rend.Page, RateAndTimeMixin):
  610.     docFactory = getxmlfile("map-update-status.xhtml")
  611. 
  612.     def __init__(self, data):
  613.         rend.Page.__init__(self, data)
  614.         self.update_status = data
  615. 
  616.     def render_started(self, ctx, data):
  617.         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
  618.         started_s = time.strftime(TIME_FORMAT,
  619.                                   time.localtime(data.get_started()))
  620.         return started_s
  621. 
  622.     def render_finished(self, ctx, data):
  623.         when = data.get_finished()
  624.         if not when:
  625.             return "not yet"
  626.         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
  627.         started_s = time.strftime(TIME_FORMAT,
  628.                                   time.localtime(data.get_finished()))
  629.         return started_s
  630. 
  631.     def render_si(self, ctx, data):
  632.         si_s = base32.b2a_or_none(data.get_storage_index())
  633.         if si_s is None:
  634.             si_s = "(None)"
  635.         return si_s
  636. 
  637.     def render_helper(self, ctx, data):
  638.         return {True: "Yes",
  639.                 False: "No"}[data.using_helper()]
  640. 
  641.     def render_progress(self, ctx, data):
  642.         progress = data.get_progress()
  643.         # TODO: make an ascii-art bar
  644.         return "%.1f%%" % (100.0 * progress)
  645. 
  646.     def render_status(self, ctx, data):
  647.         return data.get_status()
  648. 
  649.     def render_problems(self, ctx, data):
  650.         problems = data.problems
  651.         if not problems:
  652.             return ""
  653.         l = T.ul()
  654.         for peerid in sorted(problems.keys()):
  655.             peerid_s = idlib.shortnodeid_b2a(peerid)
  656.             l[T.li["[%s]: %s" % (peerid_s, problems[peerid])]]
  657.         return ctx.tag["Server Problems:", l]
  658. 
  659.     def render_privkey_from(self, ctx, data):
  660.         peerid = data.get_privkey_from()
  661.         if peerid:
- 662.             return ctx.tag["Got privkey from: [%s]"
  663.                            % idlib.shortnodeid_b2a(peerid)]
  664.         else:
  665.             return ""
  666. 
  667.     def data_time_total(self, ctx, data):
  668.         return self.update_status.timings.get("total")
  669. 
  670.     def data_time_initial_queries(self, ctx, data):
  671.         return self.update_status.timings.get("initial_queries")
  672. 
  673.     def data_time_cumulative_verify(self, ctx, data):
  674.         return self.update_status.timings.get("cumulative_verify")
  675. 
  676.     def render_server_timings(self, ctx, data):
  677.         per_server = self.update_status.timings.get("per_server")
  678.         if not per_server:
  679.             return ""
  680.         l = T.ul()
  681.         for peerid in sorted(per_server.keys()):
  682.             peerid_s = idlib.shortnodeid_b2a(peerid)
  683.             times = []
  684.             for op,started,t in per_server[peerid]:
  685.                 #times.append("%s/%.4fs/%s/%s" % (op,
  686.                 #                              started,
  687.                 #                              self.render_time(None, started - self.update_status.get_started()),
  688.                 #                              self.render_time(None,t)))
  689.                 if op == "query":
  690.                     times.append( self.render_time(None, t) )
  691.                 elif op == "late":
  692.                     times.append( "late(" + self.render_time(None, t) + ")" )
  693.                 else:
  694.                     times.append( "privkey(" + self.render_time(None, t) + ")" )
  695.             times_s = ", ".join(times)
  696.             l[T.li["[%s]: %s" % (peerid_s, times_s)]]
  697.         return T.li["Per-Server Response Times: ", l]
  698. 
  699.     def render_timing_chart(self, ctx, data):
  700.         imageurl = self._timing_chart()
  701.         return ctx.tag[imageurl]
  702. 
  703.     def _timing_chart(self):
  704.         started = self.update_status.get_started()
  705.         total = self.update_status.timings.get("total")
  706.         per_server = self.update_status.timings.get("per_server")
  707.         base = "http://chart.apis.google.com/chart?"
  708.         pieces = ["cht=bhs", "chs=400x300"]
  709.         pieces.append("chco=ffffff,4d89f9,c6d9fd") # colors
  710.         data0 = []
  711.         data1 = []
  712.         data2 = []
  713.         peerids_s = []
  714.         top_abs = started
  715.         # we sort the queries by the time at which we sent the first request
  716.         sorttable = [ (times[0][1], peerid)
  717.                       for peerid, times in per_server.items() ]
  718.         sorttable.sort()
  719.         peerids = [t[1] for t in sorttable]
  720. 
  721.         for peerid in peerids:
  722.             times = per_server[peerid]
  723.             peerid_s = idlib.shortnodeid_b2a(peerid)
  724.             peerids_s.append(peerid_s)
  725.             # for servermap updates, there are either one or two queries per
  726.             # peer. The second (if present) is to get the privkey.
  727.             op,q_started,q_elapsed = times[0]
  728.             data0.append("%.3f" % (q_started-started))
  729.             data1.append("%.3f" % q_elapsed)
  730.             top_abs = max(top_abs, q_started+q_elapsed)
  731.             if len(times) > 1:
  732.                 op,p_started,p_elapsed = times[0]
  733.                 data2.append("%.3f" % p_elapsed)
  734.                 top_abs = max(top_abs, p_started+p_elapsed)
  735.             else:
  736.                 data2.append("0.0")
  737.         finished = self.update_status.get_finished()
  738.         if finished:
  739.             top_abs = max(top_abs, finished)
  740.         top_rel = top_abs - started
  741.         chd = "chd=t:" + "|".join([",".join(data0),
  742.                                    ",".join(data1),
  743.                                    ",".join(data2)])
  744.         pieces.append(chd)
  745.         chds = "chds=0,%0.3f" % top_rel
  746.         pieces.append(chds)
  747.         pieces.append("chxt=x,y")
  748.         pieces.append("chxr=0,0.0,%0.3f" % top_rel)
  749.         pieces.append("chxl=1:|" + "|".join(reversed(peerids_s)))
  750.         # use up to 10 grid lines, at decimal multiples.
  751.         # mathutil.next_power_of_k doesn't handle numbers smaller than one,
  752.         # unfortunately.
  753.         #pieces.append("chg="
  754. 
  755.         if total is not None:
  756.             finished_f = 1.0 * total / top_rel
  757.             pieces.append("chm=r,FF0000,0,%0.3f,%0.3f" % (finished_f,
  758.                                                           finished_f+0.01))
  759.         url = base + "&".join(pieces)
  760.         return T.img(src=url, align="right", float="right")
  761. 
  762. 
  763. class Status(rend.Page):
  764.     docFactory = getxmlfile("status.xhtml")
  765.     addSlash = True
  766. 
  767.     def __init__(self, history):
  768.         rend.Page.__init__(self, history)
  769.         self.history = history
  770. 
  771.     def renderHTTP(self, ctx):
  772.         req = inevow.IRequest(ctx)
  773.         t = get_arg(req, "t")
  774.         if t == "json":
  775.             return self.json(req)
  776.         return rend.Page.renderHTTP(self, ctx)
  777. 
  778.     def json(self, req):
  779.         req.setHeader("content-type", "text/plain")
  780.         data = {}
  781.         data["active"] = active = []
  782.         for s in self._get_active_operations():
  783.             si_s = base32.b2a_or_none(s.get_storage_index())
  784.             size = s.get_size()
  785.             status = s.get_status()
  786.             if IUploadStatus.providedBy(s):
  787.                 h,c,e = s.get_progress()
  788.                 active.append({"type": "upload",
  789.                                "storage-index-string": si_s,
  790.                                "total-size": size,
  791.                                "status": status,
  792.                                "progress-hash": h,
  793.                                "progress-ciphertext": c,
  794.                                "progress-encode-push": e,
  795.                                })
  796.             elif IDownloadStatus.providedBy(s):
  797.                 active.append({"type": "download",
  798.                                "storage-index-string": si_s,
  799.                                "total-size": size,
  800.                                "status": status,
  801.                                "progress": s.get_progress(),
  802.                                })
  803. 
  804.         return simplejson.dumps(data, indent=1) + "\n"
  805. 
  806.     def _get_all_statuses(self):
  807.         h = self.history
  808.         return itertools.chain(h.list_all_upload_statuses(),
  809.                                h.list_all_download_statuses(),
  810.                                h.list_all_mapupdate_statuses(),
  811.                                h.list_all_publish_statuses(),
  812.                                h.list_all_retrieve_statuses(),
  813.                                h.list_all_helper_statuses(),
  814.                                )
  815. 
  816.     def data_active_operations(self, ctx, data):
  817.         return self._get_active_operations()
  818. 
  819.     def _get_active_operations(self):
  820.         active = [s
  821.                   for s in self._get_all_statuses()
  822.                   if s.get_active()]
  823.         return active
  824. 
  825.     def data_recent_operations(self, ctx, data):
  826.         return self._get_recent_operations()
  827. 
  828.     def _get_recent_operations(self):
  829.         recent = [s
  830.                   for s in self._get_all_statuses()
  831.                   if not s.get_active()]
  832.         recent.sort(lambda a,b: cmp(a.get_started(), b.get_started()))
  833.         recent.reverse()
  834.         return recent
  835. 
  836.     def render_row(self, ctx, data):
  837.         s = data
  838. 
  839.         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
  840.         started_s = time.strftime(TIME_FORMAT,
  841.                                   time.localtime(s.get_started()))
  842.         ctx.fillSlots("started", started_s)
  843. 
  844.         si_s = base32.b2a_or_none(s.get_storage_index())
  845.         if si_s is None:
  846.             si_s = "(None)"
  847.         ctx.fillSlots("si", si_s)
  848.         ctx.fillSlots("helper", {True: "Yes",
  849.                                  False: "No"}[s.using_helper()])
  850. 
  851.         size = s.get_size()
  852.         if size is None:
  853.             size = "(unknown)"
  854.         elif isinstance(size, (int, long, float)):
  855.             size = abbreviate_size(size)
  856.         ctx.fillSlots("total_size", size)
  857. 
  858.         progress = data.get_progress()
  859.         if IUploadStatus.providedBy(data):
  860.             link = "up-%d" % data.get_counter()
  861.             ctx.fillSlots("type", "upload")
  862.             # TODO: make an ascii-art bar
  863.             (chk, ciphertext, encandpush) = progress
  864.             progress_s = ("hash: %.1f%%, ciphertext: %.1f%%, encode: %.1f%%" %
  865.                           ( (100.0 * chk),
  866.                             (100.0 * ciphertext),
  867.                             (100.0 * encandpush) ))
  868.             ctx.fillSlots("progress", progress_s)
  869.         elif IDownloadStatus.providedBy(data):
  870.             link = "down-%d" % data.get_counter()
  871.             ctx.fillSlots("type", "download")
  872.             ctx.fillSlots("progress", "%.1f%%" % (100.0 * progress))
  873.         elif IPublishStatus.providedBy(data):
  874.             link = "publish-%d" % data.get_counter()
  875.             ctx.fillSlots("type", "publish")
  876.             ctx.fillSlots("progress", "%.1f%%" % (100.0 * progress))
  877.         elif IRetrieveStatus.providedBy(data):
  878.             ctx.fillSlots("type", "retrieve")
  879.             link = "retrieve-%d" % data.get_counter()
  880.             ctx.fillSlots("progress", "%.1f%%" % (100.0 * progress))
  881.         else:
  882.             assert IServermapUpdaterStatus.providedBy(data)
  883.             ctx.fillSlots("type", "mapupdate %s" % data.get_mode())
  884.             link = "mapupdate-%d" % data.get_counter()
  885.             ctx.fillSlots("progress", "%.1f%%" % (100.0 * progress))
  886.         ctx.fillSlots("status", T.a(href=link)[s.get_status()])
  887.         return ctx.tag
  888. 
  889.     def childFactory(self, ctx, name):
  890.         h = self.history
  891.         stype,count_s = name.split("-")
  892.         count = int(count_s)
  893.         if stype == "up":
  894.             for s in itertools.chain(h.list_all_upload_statuses(),
  895.                                      h.list_all_helper_statuses()):
  896.                 # immutable-upload helpers use the same status object as a
  897.                 # regular immutable-upload
  898.                 if s.get_counter() == count:
  899.                     return UploadStatusPage(s)
  900.         if stype == "down":
  901.             for s in h.list_all_download_statuses():
  902.                 if s.get_counter() == count:
  903.                     return DownloadStatusPage(s)
  904.         if stype == "mapupdate":
  905.             for s in h.list_all_mapupdate_statuses():
  906.                 if s.get_counter() == count:
  907.                     return MapupdateStatusPage(s)
  908.         if stype == "publish":
  909.             for s in h.list_all_publish_statuses():
  910.                 if s.get_counter() == count:
  911.                     return PublishStatusPage(s)
  912.         if stype == "retrieve":
  913.             for s in h.list_all_retrieve_statuses():
  914.                 if s.get_counter() == count:
  915.                     return RetrieveStatusPage(s)
  916. 
  917. 
  918. class HelperStatus(rend.Page):
  919.     docFactory = getxmlfile("helper.xhtml")
  920. 
  921.     def __init__(self, helper):
  922.         rend.Page.__init__(self, helper)
  923.         self.helper = helper
  924. 
  925.     def renderHTTP(self, ctx):
  926.         req = inevow.IRequest(ctx)
  927.         t = get_arg(req, "t")
  928.         if t == "json":
  929.             return self.render_JSON(req)
  930.         return rend.Page.renderHTTP(self, ctx)
  931. 
  932.     def data_helper_stats(self, ctx, data):
  933.         return self.helper.get_stats()
  934. 
  935.     def render_JSON(self, req):
  936.         req.setHeader("content-type", "text/plain")
  937.         if self.helper:
  938.             stats = self.helper.get_stats()
  939.             return simplejson.dumps(stats, indent=1) + "\n"
  940.         return simplejson.dumps({}) + "\n"
  941. 
  942.     def render_active_uploads(self, ctx, data):
  943.         return data["chk_upload_helper.active_uploads"]
  944. 
  945.     def render_incoming(self, ctx, data):
  946.         return "%d bytes in %d files" % (data["chk_upload_helper.incoming_size"],
  947.                                          data["chk_upload_helper.incoming_count"])
  948. 
  949.     def render_encoding(self, ctx, data):
  950.         return "%d bytes in %d files" % (data["chk_upload_helper.encoding_size"],
  951.                                          data["chk_upload_helper.encoding_count"])
  952. 
  953.     def render_upload_requests(self, ctx, data):
  954.         return str(data["chk_upload_helper.upload_requests"])
  955. 
  956.     def render_upload_already_present(self, ctx, data):
  957.         return str(data["chk_upload_helper.upload_already_present"])
  958. 
  959.     def render_upload_need_upload(self, ctx, data):
  960.         return str(data["chk_upload_helper.upload_need_upload"])
  961. 
  962.     def render_upload_bytes_fetched(self, ctx, data):
  963.         return str(data["chk_upload_helper.fetched_bytes"])
  964. 
  965.     def render_upload_bytes_encoded(self, ctx, data):
  966.         return str(data["chk_upload_helper.encoded_bytes"])
  967. 
  968. 
  969. class Statistics(rend.Page):
  970.     docFactory = getxmlfile("statistics.xhtml")
  971. 
  972.     def __init__(self, provider):
  973.         rend.Page.__init__(self, provider)
  974.         self.provider = provider
  975. 
  976.     def renderHTTP(self, ctx):
  977.         req = inevow.IRequest(ctx)
  978.         t = get_arg(req, "t")
  979.         if t == "json":
  980.             stats = self.provider.get_stats()
  981.             req.setHeader("content-type", "text/plain")
  982.             return simplejson.dumps(stats, indent=1) + "\n"
  983.         return rend.Page.renderHTTP(self, ctx)
  984. 
  985.     def data_get_stats(self, ctx, data):
  986.         return self.provider.get_stats()
  987. 
  988.     def render_load_average(self, ctx, data):
  989.         return str(data["stats"].get("load_monitor.avg_load"))
  990. 
  991.     def render_peak_load(self, ctx, data):
  992.         return str(data["stats"].get("load_monitor.max_load"))
  993. 
  994.     def render_uploads(self, ctx, data):
  995.         files = data["counters"].get("uploader.files_uploaded", 0)
  996.         bytes = data["counters"].get("uploader.bytes_uploaded", 0)
  997.         return ("%s files / %s bytes (%s)" %
  998.                 (files, bytes, abbreviate_size(bytes)))
  999. 
 1000.     def render_downloads(self, ctx, data):
 1001.         files = data["counters"].get("downloader.files_downloaded", 0)
 1002.         bytes = data["counters"].get("downloader.bytes_downloaded", 0)
 1003.         return ("%s files / %s bytes (%s)" %
 1004.                 (files, bytes, abbreviate_size(bytes)))
 1005. 
 1006.     def render_publishes(self, ctx, data):
 1007.         files = data["counters"].get("mutable.files_published", 0)
 1008.         bytes = data["counters"].get("mutable.bytes_published", 0)
 1009.         return "%s files / %s bytes (%s)" % (files, bytes,
 1010.                                              abbreviate_size(bytes))
 1011. 
 1012.     def render_retrieves(self, ctx, data):
 1013.         files = data["counters"].get("mutable.files_retrieved", 0)
 1014.         bytes = data["counters"].get("mutable.bytes_retrieved", 0)
 1015.         return "%s files / %s bytes (%s)" % (files, bytes,
 1016.                                              abbreviate_size(bytes))
 1017. 
 1018.     def render_raw(self, ctx, data):
 1019.         raw = pprint.pformat(data)
 1020.         return ctx.tag[raw]