StatsPlugin.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. import time
  2. import html
  3. import os
  4. import json
  5. import sys
  6. import itertools
  7. from Plugin import PluginManager
  8. from Config import config
  9. from util import helper
  10. from Debug import Debug
  11. from Db import Db
  12. @PluginManager.registerTo("UiRequest")
  13. class UiRequestPlugin(object):
  14. def formatTableRow(self, row, class_name=""):
  15. back = []
  16. for format, val in row:
  17. if val is None:
  18. formatted = "n/a"
  19. elif format == "since":
  20. if val:
  21. formatted = "%.0f" % (time.time() - val)
  22. else:
  23. formatted = "n/a"
  24. else:
  25. formatted = format % val
  26. back.append("<td>%s</td>" % formatted)
  27. return "<tr class='%s'>%s</tr>" % (class_name, "".join(back))
  28. def getObjSize(self, obj, hpy=None):
  29. if hpy:
  30. return float(hpy.iso(obj).domisize) / 1024
  31. else:
  32. return 0
  33. def renderHead(self):
  34. import main
  35. from Crypt import CryptConnection
  36. # Memory
  37. yield "rev%s | " % config.rev
  38. yield "%s | " % main.file_server.ip_external_list
  39. yield "Port: %s | " % main.file_server.port
  40. yield "Network: %s | " % main.file_server.supported_ip_types
  41. yield "Opened: %s | " % main.file_server.port_opened
  42. yield "Crypt: %s, TLSv1.3: %s | " % (CryptConnection.manager.crypt_supported, CryptConnection.ssl.HAS_TLSv1_3)
  43. yield "In: %.2fMB, Out: %.2fMB | " % (
  44. float(main.file_server.bytes_recv) / 1024 / 1024,
  45. float(main.file_server.bytes_sent) / 1024 / 1024
  46. )
  47. yield "Peerid: %s | " % main.file_server.peer_id
  48. yield "Time: %.2fs | " % main.file_server.getTimecorrection()
  49. yield "Blocks: %s" % Debug.num_block
  50. try:
  51. import psutil
  52. process = psutil.Process(os.getpid())
  53. mem = process.get_memory_info()[0] / float(2 ** 20)
  54. yield "Mem: %.2fMB | " % mem
  55. yield "Threads: %s | " % len(process.threads())
  56. yield "CPU: usr %.2fs sys %.2fs | " % process.cpu_times()
  57. yield "Files: %s | " % len(process.open_files())
  58. yield "Sockets: %s | " % len(process.connections())
  59. yield "Calc size <a href='?size=1'>on</a> <a href='?size=0'>off</a>"
  60. except Exception:
  61. pass
  62. yield "<br>"
  63. def renderConnectionsTable(self):
  64. import main
  65. # Connections
  66. yield "<b>Connections</b> (%s, total made: %s, in: %s, out: %s):<br>" % (
  67. len(main.file_server.connections), main.file_server.last_connection_id,
  68. main.file_server.num_incoming, main.file_server.num_outgoing
  69. )
  70. yield "<table class='connections'><tr> <th>id</th> <th>type</th> <th>ip</th> <th>open</th> <th>crypt</th> <th>ping</th>"
  71. yield "<th>buff</th> <th>bad</th> <th>idle</th> <th>open</th> <th>delay</th> <th>cpu</th> <th>out</th> <th>in</th> <th>last sent</th>"
  72. yield "<th>wait</th> <th>version</th> <th>time</th> <th>sites</th> </tr>"
  73. for connection in main.file_server.connections:
  74. if "cipher" in dir(connection.sock):
  75. cipher = connection.sock.cipher()[0]
  76. tls_version = connection.sock.version()
  77. else:
  78. cipher = connection.crypt
  79. tls_version = ""
  80. if "time" in connection.handshake and connection.last_ping_delay:
  81. time_correction = connection.handshake["time"] - connection.handshake_time - connection.last_ping_delay
  82. else:
  83. time_correction = 0.0
  84. yield self.formatTableRow([
  85. ("%3d", connection.id),
  86. ("%s", connection.type),
  87. ("%s:%s", (connection.ip, connection.port)),
  88. ("%s", connection.handshake.get("port_opened")),
  89. ("<span title='%s %s'>%s</span>", (cipher, tls_version, connection.crypt)),
  90. ("%6.3f", connection.last_ping_delay),
  91. ("%s", connection.incomplete_buff_recv),
  92. ("%s", connection.bad_actions),
  93. ("since", max(connection.last_send_time, connection.last_recv_time)),
  94. ("since", connection.start_time),
  95. ("%.3f", max(-1, connection.last_sent_time - connection.last_send_time)),
  96. ("%.3f", connection.cpu_time),
  97. ("%.0fk", connection.bytes_sent / 1024),
  98. ("%.0fk", connection.bytes_recv / 1024),
  99. ("<span title='Recv: %s'>%s</span>", (connection.last_cmd_recv, connection.last_cmd_sent)),
  100. ("%s", list(connection.waiting_requests.keys())),
  101. ("%s r%s", (connection.handshake.get("version"), connection.handshake.get("rev", "?"))),
  102. ("%.2fs", time_correction),
  103. ("%s", connection.sites)
  104. ])
  105. yield "</table>"
  106. def renderTrackers(self):
  107. # Trackers
  108. yield "<br><br><b>Trackers:</b><br>"
  109. yield "<table class='trackers'><tr> <th>address</th> <th>request</th> <th>successive errors</th> <th>last_request</th></tr>"
  110. from Site import SiteAnnouncer # importing at the top of the file breaks plugins
  111. for tracker_address, tracker_stat in sorted(SiteAnnouncer.global_stats.items()):
  112. yield self.formatTableRow([
  113. ("%s", tracker_address),
  114. ("%s", tracker_stat["num_request"]),
  115. ("%s", tracker_stat["num_error"]),
  116. ("%.0f min ago", min(999, (time.time() - tracker_stat["time_request"]) / 60))
  117. ])
  118. yield "</table>"
  119. if "AnnounceShare" in PluginManager.plugin_manager.plugin_names:
  120. yield "<br><br><b>Shared trackers:</b><br>"
  121. yield "<table class='trackers'><tr> <th>address</th> <th>added</th> <th>found</th> <th>latency</th> <th>successive errors</th> <th>last_success</th></tr>"
  122. from AnnounceShare import AnnounceSharePlugin
  123. for tracker_address, tracker_stat in sorted(AnnounceSharePlugin.tracker_storage.getTrackers().items()):
  124. yield self.formatTableRow([
  125. ("%s", tracker_address),
  126. ("%.0f min ago", min(999, (time.time() - tracker_stat["time_added"]) / 60)),
  127. ("%.0f min ago", min(999, (time.time() - tracker_stat.get("time_found", 0)) / 60)),
  128. ("%.3fs", tracker_stat["latency"]),
  129. ("%s", tracker_stat["num_error"]),
  130. ("%.0f min ago", min(999, (time.time() - tracker_stat["time_success"]) / 60)),
  131. ])
  132. yield "</table>"
  133. def renderTor(self):
  134. import main
  135. yield "<br><br><b>Tor hidden services (status: %s):</b><br>" % main.file_server.tor_manager.status
  136. for site_address, onion in list(main.file_server.tor_manager.site_onions.items()):
  137. yield "- %-34s: %s<br>" % (site_address, onion)
  138. def renderDbStats(self):
  139. yield "<br><br><b>Db</b>:<br>"
  140. for db in Db.opened_dbs:
  141. tables = [row["name"] for row in db.execute("SELECT name FROM sqlite_master WHERE type = 'table'").fetchall()]
  142. table_rows = {}
  143. for table in tables:
  144. table_rows[table] = db.execute("SELECT COUNT(*) AS c FROM %s" % table).fetchone()["c"]
  145. db_size = os.path.getsize(db.db_path) / 1024.0 / 1024.0
  146. yield "- %.3fs: %s %.3fMB, table rows: %s<br>" % (
  147. time.time() - db.last_query_time, db.db_path, db_size, json.dumps(table_rows, sort_keys=True)
  148. )
  149. def renderSites(self):
  150. yield "<br><br><b>Sites</b>:"
  151. yield "<table>"
  152. yield "<tr><th>address</th> <th>connected</th> <th title='connected/good/total'>peers</th> <th>content.json</th> <th>out</th> <th>in</th> </tr>"
  153. for site in list(self.server.sites.values()):
  154. yield self.formatTableRow([
  155. (
  156. """<a href='#' onclick='document.getElementById("peers_%s").style.display="initial"; return false'>%s</a>""",
  157. (site.address, site.address)
  158. ),
  159. ("%s", [peer.connection.id for peer in list(site.peers.values()) if peer.connection and peer.connection.connected]),
  160. ("%s/%s/%s", (
  161. len([peer for peer in list(site.peers.values()) if peer.connection and peer.connection.connected]),
  162. len(site.getConnectablePeers(100)),
  163. len(site.peers)
  164. )),
  165. ("%s (loaded: %s)", (
  166. len(site.content_manager.contents),
  167. len([key for key, val in dict(site.content_manager.contents).items() if val])
  168. )),
  169. ("%.0fk", site.settings.get("bytes_sent", 0) / 1024),
  170. ("%.0fk", site.settings.get("bytes_recv", 0) / 1024),
  171. ], "serving-%s" % site.settings["serving"])
  172. yield "<tr><td id='peers_%s' style='display: none; white-space: pre' colspan=6>" % site.address
  173. for key, peer in list(site.peers.items()):
  174. if peer.time_found:
  175. time_found = int(time.time() - peer.time_found) / 60
  176. else:
  177. time_found = "--"
  178. if peer.connection:
  179. connection_id = peer.connection.id
  180. else:
  181. connection_id = None
  182. if site.content_manager.has_optional_files:
  183. yield "Optional files: %4s " % len(peer.hashfield)
  184. time_added = (time.time() - peer.time_added) / (60 * 60 * 24)
  185. yield "(#%4s, rep: %2s, err: %s, found: %.1fs min, add: %.1f day) %30s -<br>" % (connection_id, peer.reputation, peer.connection_error, time_found, time_added, key)
  186. yield "<br></td></tr>"
  187. yield "</table>"
  188. def renderBigfiles(self):
  189. yield "<br><br><b>Big files</b>:<br>"
  190. for site in list(self.server.sites.values()):
  191. if not site.settings.get("has_bigfile"):
  192. continue
  193. bigfiles = {}
  194. yield """<a href="#" onclick='document.getElementById("bigfiles_%s").style.display="initial"; return false'>%s</a><br>""" % (site.address, site.address)
  195. for peer in list(site.peers.values()):
  196. if not peer.time_piecefields_updated:
  197. continue
  198. for sha512, piecefield in peer.piecefields.items():
  199. if sha512 not in bigfiles:
  200. bigfiles[sha512] = []
  201. bigfiles[sha512].append(peer)
  202. yield "<div id='bigfiles_%s' style='display: none'>" % site.address
  203. for sha512, peers in bigfiles.items():
  204. yield "<br> - " + sha512 + " (hash id: %s)<br>" % site.content_manager.hashfield.getHashId(sha512)
  205. yield "<table>"
  206. for peer in peers:
  207. yield "<tr><td>" + peer.key + "</td><td>" + peer.piecefields[sha512].tostring() + "</td></tr>"
  208. yield "</table>"
  209. yield "</div>"
  210. def renderRequests(self):
  211. import main
  212. yield "<div style='float: left'>"
  213. yield "<br><br><b>Sent commands</b>:<br>"
  214. yield "<table>"
  215. for stat_key, stat in sorted(main.file_server.stat_sent.items(), key=lambda i: i[1]["bytes"], reverse=True):
  216. yield "<tr><td>%s</td><td style='white-space: nowrap'>x %s =</td><td>%.0fkB</td></tr>" % (stat_key, stat["num"], stat["bytes"] / 1024)
  217. yield "</table>"
  218. yield "</div>"
  219. yield "<div style='float: left; margin-left: 20%; max-width: 50%'>"
  220. yield "<br><br><b>Received commands</b>:<br>"
  221. yield "<table>"
  222. for stat_key, stat in sorted(main.file_server.stat_recv.items(), key=lambda i: i[1]["bytes"], reverse=True):
  223. yield "<tr><td>%s</td><td style='white-space: nowrap'>x %s =</td><td>%.0fkB</td></tr>" % (stat_key, stat["num"], stat["bytes"] / 1024)
  224. yield "</table>"
  225. yield "</div>"
  226. yield "<div style='clear: both'></div>"
  227. def renderMemory(self):
  228. import gc
  229. from Ui import UiRequest
  230. hpy = None
  231. if self.get.get("size") == "1": # Calc obj size
  232. try:
  233. import guppy
  234. hpy = guppy.hpy()
  235. except Exception:
  236. pass
  237. self.sendHeader()
  238. # Object types
  239. obj_count = {}
  240. for obj in gc.get_objects():
  241. obj_type = str(type(obj))
  242. if obj_type not in obj_count:
  243. obj_count[obj_type] = [0, 0]
  244. obj_count[obj_type][0] += 1 # Count
  245. obj_count[obj_type][1] += float(sys.getsizeof(obj)) / 1024 # Size
  246. yield "<br><br><b>Objects in memory (types: %s, total: %s, %.2fkb):</b><br>" % (
  247. len(obj_count),
  248. sum([stat[0] for stat in list(obj_count.values())]),
  249. sum([stat[1] for stat in list(obj_count.values())])
  250. )
  251. for obj, stat in sorted(list(obj_count.items()), key=lambda x: x[1][0], reverse=True): # Sorted by count
  252. yield " - %.1fkb = %s x <a href=\"/Listobj?type=%s\">%s</a><br>" % (stat[1], stat[0], obj, html.escape(obj))
  253. # Classes
  254. class_count = {}
  255. for obj in gc.get_objects():
  256. obj_type = str(type(obj))
  257. if obj_type != "<type 'instance'>":
  258. continue
  259. class_name = obj.__class__.__name__
  260. if class_name not in class_count:
  261. class_count[class_name] = [0, 0]
  262. class_count[class_name][0] += 1 # Count
  263. class_count[class_name][1] += float(sys.getsizeof(obj)) / 1024 # Size
  264. yield "<br><br><b>Classes in memory (types: %s, total: %s, %.2fkb):</b><br>" % (
  265. len(class_count),
  266. sum([stat[0] for stat in list(class_count.values())]),
  267. sum([stat[1] for stat in list(class_count.values())])
  268. )
  269. for obj, stat in sorted(list(class_count.items()), key=lambda x: x[1][0], reverse=True): # Sorted by count
  270. yield " - %.1fkb = %s x <a href=\"/Dumpobj?class=%s\">%s</a><br>" % (stat[1], stat[0], obj, html.escape(obj))
  271. from greenlet import greenlet
  272. objs = [obj for obj in gc.get_objects() if isinstance(obj, greenlet)]
  273. yield "<br>Greenlets (%s):<br>" % len(objs)
  274. for obj in objs:
  275. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  276. from Worker import Worker
  277. objs = [obj for obj in gc.get_objects() if isinstance(obj, Worker)]
  278. yield "<br>Workers (%s):<br>" % len(objs)
  279. for obj in objs:
  280. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  281. from Connection import Connection
  282. objs = [obj for obj in gc.get_objects() if isinstance(obj, Connection)]
  283. yield "<br>Connections (%s):<br>" % len(objs)
  284. for obj in objs:
  285. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  286. from socket import socket
  287. objs = [obj for obj in gc.get_objects() if isinstance(obj, socket)]
  288. yield "<br>Sockets (%s):<br>" % len(objs)
  289. for obj in objs:
  290. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  291. from msgpack import Unpacker
  292. objs = [obj for obj in gc.get_objects() if isinstance(obj, Unpacker)]
  293. yield "<br>Msgpack unpacker (%s):<br>" % len(objs)
  294. for obj in objs:
  295. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  296. from Site.Site import Site
  297. objs = [obj for obj in gc.get_objects() if isinstance(obj, Site)]
  298. yield "<br>Sites (%s):<br>" % len(objs)
  299. for obj in objs:
  300. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  301. objs = [obj for obj in gc.get_objects() if isinstance(obj, self.server.log.__class__)]
  302. yield "<br>Loggers (%s):<br>" % len(objs)
  303. for obj in objs:
  304. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj.name)))
  305. objs = [obj for obj in gc.get_objects() if isinstance(obj, UiRequest)]
  306. yield "<br>UiRequests (%s):<br>" % len(objs)
  307. for obj in objs:
  308. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  309. from Peer import Peer
  310. objs = [obj for obj in gc.get_objects() if isinstance(obj, Peer)]
  311. yield "<br>Peers (%s):<br>" % len(objs)
  312. for obj in objs:
  313. yield " - %.1fkb: %s<br>" % (self.getObjSize(obj, hpy), html.escape(repr(obj)))
  314. objs = [(key, val) for key, val in sys.modules.items() if val is not None]
  315. objs.sort()
  316. yield "<br>Modules (%s):<br>" % len(objs)
  317. for module_name, module in objs:
  318. yield " - %.3fkb: %s %s<br>" % (self.getObjSize(module, hpy), module_name, html.escape(repr(module)))
  319. # /Stats entry point
  320. @helper.encodeResponse
  321. def actionStats(self):
  322. import gc
  323. self.sendHeader()
  324. if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
  325. yield "This function is disabled on this proxy"
  326. return
  327. s = time.time()
  328. # Style
  329. yield """
  330. <style>
  331. * { font-family: monospace }
  332. table td, table th { text-align: right; padding: 0px 10px }
  333. .connections td { white-space: nowrap }
  334. .serving-False { opacity: 0.3 }
  335. </style>
  336. """
  337. renderers = [
  338. self.renderHead(),
  339. self.renderConnectionsTable(),
  340. self.renderTrackers(),
  341. self.renderTor(),
  342. self.renderDbStats(),
  343. self.renderSites(),
  344. self.renderBigfiles(),
  345. self.renderRequests()
  346. ]
  347. for part in itertools.chain(*renderers):
  348. yield part
  349. if config.debug:
  350. for part in self.renderMemory():
  351. yield part
  352. gc.collect() # Implicit grabage collection
  353. yield "Done in %.1f" % (time.time() - s)
  354. @helper.encodeResponse
  355. def actionDumpobj(self):
  356. import gc
  357. import sys
  358. self.sendHeader()
  359. if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
  360. yield "This function is disabled on this proxy"
  361. return
  362. # No more if not in debug mode
  363. if not config.debug:
  364. yield "Not in debug mode"
  365. return
  366. class_filter = self.get.get("class")
  367. yield """
  368. <style>
  369. * { font-family: monospace; white-space: pre }
  370. table * { text-align: right; padding: 0px 10px }
  371. </style>
  372. """
  373. objs = gc.get_objects()
  374. for obj in objs:
  375. obj_type = str(type(obj))
  376. if obj_type != "<type 'instance'>" or obj.__class__.__name__ != class_filter:
  377. continue
  378. yield "%.1fkb %s... " % (float(sys.getsizeof(obj)) / 1024, html.escape(str(obj)))
  379. for attr in dir(obj):
  380. yield "- %s: %s<br>" % (attr, html.escape(str(getattr(obj, attr))))
  381. yield "<br>"
  382. gc.collect() # Implicit grabage collection
  383. @helper.encodeResponse
  384. def actionListobj(self):
  385. import gc
  386. import sys
  387. self.sendHeader()
  388. if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
  389. yield "This function is disabled on this proxy"
  390. return
  391. # No more if not in debug mode
  392. if not config.debug:
  393. yield "Not in debug mode"
  394. return
  395. type_filter = self.get.get("type")
  396. yield """
  397. <style>
  398. * { font-family: monospace; white-space: pre }
  399. table * { text-align: right; padding: 0px 10px }
  400. </style>
  401. """
  402. yield "Listing all %s objects in memory...<br>" % html.escape(type_filter)
  403. ref_count = {}
  404. objs = gc.get_objects()
  405. for obj in objs:
  406. obj_type = str(type(obj))
  407. if obj_type != type_filter:
  408. continue
  409. refs = [
  410. ref for ref in gc.get_referrers(obj)
  411. if hasattr(ref, "__class__") and
  412. ref.__class__.__name__ not in ["list", "dict", "function", "type", "frame", "WeakSet", "tuple"]
  413. ]
  414. if not refs:
  415. continue
  416. try:
  417. yield "%.1fkb <span title=\"%s\">%s</span>... " % (
  418. float(sys.getsizeof(obj)) / 1024, html.escape(str(obj)), html.escape(str(obj)[0:100].ljust(100))
  419. )
  420. except Exception:
  421. continue
  422. for ref in refs:
  423. yield " ["
  424. if "object at" in str(ref) or len(str(ref)) > 100:
  425. yield str(ref.__class__.__name__)
  426. else:
  427. yield str(ref.__class__.__name__) + ":" + html.escape(str(ref))
  428. yield "] "
  429. ref_type = ref.__class__.__name__
  430. if ref_type not in ref_count:
  431. ref_count[ref_type] = [0, 0]
  432. ref_count[ref_type][0] += 1 # Count
  433. ref_count[ref_type][1] += float(sys.getsizeof(obj)) / 1024 # Size
  434. yield "<br>"
  435. yield "<br>Object referrer (total: %s, %.2fkb):<br>" % (len(ref_count), sum([stat[1] for stat in list(ref_count.values())]))
  436. for obj, stat in sorted(list(ref_count.items()), key=lambda x: x[1][0], reverse=True)[0:30]: # Sorted by count
  437. yield " - %.1fkb = %s x %s<br>" % (stat[1], stat[0], html.escape(str(obj)))
  438. gc.collect() # Implicit grabage collection
  439. @helper.encodeResponse
  440. def actionGcCollect(self):
  441. import gc
  442. self.sendHeader()
  443. yield str(gc.collect())
  444. # /About entry point
  445. @helper.encodeResponse
  446. def actionEnv(self):
  447. import main
  448. self.sendHeader()
  449. yield """
  450. <style>
  451. * { font-family: monospace; white-space: pre; }
  452. h2 { font-size: 100%; margin-bottom: 0px; }
  453. small { opacity: 0.5; }
  454. table { border-collapse: collapse; }
  455. td { padding-right: 10px; }
  456. </style>
  457. """
  458. if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local:
  459. yield "This function is disabled on this proxy"
  460. return
  461. yield from main.actions.testEnv(format="html")
  462. @PluginManager.registerTo("Actions")
  463. class ActionsPlugin:
  464. def formatTable(self, *rows, format="text"):
  465. if format == "html":
  466. return self.formatTableHtml(*rows)
  467. else:
  468. return self.formatTableText(*rows)
  469. def formatHead(self, title, format="text"):
  470. if format == "html":
  471. return "<h2>%s</h2>" % title
  472. else:
  473. return "\n* %s\n" % title
  474. def formatTableHtml(self, *rows):
  475. yield "<table>"
  476. for row in rows:
  477. yield "<tr>"
  478. for col in row:
  479. yield "<td>%s</td>" % html.escape(str(col))
  480. yield "</tr>"
  481. yield "</table>"
  482. def formatTableText(self, *rows):
  483. for row in rows:
  484. yield " "
  485. for col in row:
  486. yield " " + str(col)
  487. yield "\n"
  488. def testEnv(self, format="text"):
  489. import gevent
  490. import msgpack
  491. import pkg_resources
  492. import importlib
  493. import coincurve
  494. import sqlite3
  495. from Crypt import CryptBitcoin
  496. yield "\n"
  497. yield from self.formatTable(
  498. ["ZeroNet version:", "%s rev%s" % (config.version, config.rev)],
  499. ["Python:", "%s" % sys.version],
  500. ["Platform:", "%s" % sys.platform],
  501. ["Crypt verify lib:", "%s" % CryptBitcoin.lib_verify_best],
  502. ["OpenSSL:", "%s" % CryptBitcoin.sslcrypto.ecc.get_backend()],
  503. ["Libsecp256k1:", "%s" % type(coincurve._libsecp256k1.lib).__name__],
  504. ["SQLite:", "%s, API: %s" % (sqlite3.sqlite_version, sqlite3.version)],
  505. format=format
  506. )
  507. yield self.formatHead("Libraries:")
  508. rows = []
  509. for lib_name in ["gevent", "greenlet", "msgpack", "base58", "merkletools", "rsa", "socks", "pyasn1", "gevent_ws", "websocket", "maxminddb"]:
  510. try:
  511. module = importlib.import_module(lib_name)
  512. if "__version__" in dir(module):
  513. version = module.__version__
  514. elif "version" in dir(module):
  515. version = module.version
  516. else:
  517. version = "unknown version"
  518. if type(version) is tuple:
  519. version = ".".join(map(str, version))
  520. rows.append(["- %s:" % lib_name, version, "at " + module.__file__])
  521. except Exception as err:
  522. rows.append(["! Error importing %s:", repr(err)])
  523. """
  524. try:
  525. yield " - %s<br>" % html.escape(repr(pkg_resources.get_distribution(lib_name)))
  526. except Exception as err:
  527. yield " ! %s<br>" % html.escape(repr(err))
  528. """
  529. yield from self.formatTable(*rows, format=format)
  530. yield self.formatHead("Library config:", format=format)
  531. yield from self.formatTable(
  532. ["- gevent:", gevent.config.loop.__module__],
  533. ["- msgpack unpacker:", msgpack.Unpacker.__module__],
  534. format=format
  535. )