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]