+ elapsed = datetime.now().replace(microsecond=0) - self.startTime
+ out = StringIO()
+ out.write('<h2>DHT Statistics</h2>\n')
+ out.write("<table border='0' cellspacing='20px'>\n<tr>\n")
+ out.write('<td>\n')
+
+ # General
+ out.write("<table border='1' cellpadding='4px'>\n")
+ out.write("<tr><th><h3>General</h3></th><th>Value</th></tr>\n")
+ out.write("<tr title='Elapsed time since the DHT was started'><td>Up time</td><td>" + str(elapsed) + '</td></tr>\n')
+ out.write("<tr title='Whether this node is reachable by other nodes'><td>Reachable</td><td>" + str(self.reachable) + '</td></tr>\n')
+ out.write("</table>\n")
+ out.write('</td><td>\n')
+
+ # Routing
+ out.write("<table border='1' cellpadding='4px'>\n")
+ out.write("<tr><th><h3>Routing Table</h3></th><th>Value</th></tr>\n")
+ out.write("<tr title='The number of connected nodes'><td>Number of nodes</td><td>" + str(self.nodes) + '</td></tr>\n')
+ out.write("<tr title='The estimated number of connected users in the entire DHT'><td>Total number of users</td><td>" + str(self.users) + '</td></tr>\n')
+ out.write("</table>\n")
+ out.write('</td><td>\n')
+
+ # Database
+ out.write("<table border='1' cellpadding='4px'>\n")
+ out.write("<tr><th><h3>Database</h3></th><th>Value</th></tr>\n")
+ out.write("<tr title='Number of distinct keys in the database'><td>Keys</td><td>" + str(self.keys) + '</td></tr>\n')
+ out.write("<tr title='Total number of values stored locally'><td>Values</td><td>" + str(self.values) + '</td></tr>\n')
+ out.write("</table>\n")
+ out.write("</td></tr><tr><td colspan='3'>\n")
+
+ # Transport
+ out.write("<table border='1' cellpadding='4px'>\n")
+ out.write("<tr><th><h3>Transport</h3></th><th>Packets</th><th>Bytes</th><th>Speed</th></tr>\n")
+ out.write("<tr title='Stats for packets received from the DHT'><td>Downloaded</td>")
+ out.write('<td>' + str(self.downPackets) + '</td>')
+ out.write('<td>' + byte_format(self.downBytes) + '</td>')
+ out.write('<td>' + byte_format(self.downBytes / (elapsed.days*86400.0 + elapsed.seconds)) + '/sec</td></tr>\n')
+ out.write("<tr title='Stats for packets sent to the DHT'><td>Uploaded</td>")
+ out.write('<td>' + str(self.upPackets) + '</td>')
+ out.write('<td>' + byte_format(self.upBytes) + '</td>')
+ out.write('<td>' + byte_format(self.upBytes / (elapsed.days*86400.0 + elapsed.seconds)) + '/sec</td></tr>\n')
+ out.write("</table>\n")
+ out.write("</td></tr><tr><td colspan='3'>\n")
+
+ # Actions
+ out.write("<table border='1' cellpadding='4px'>\n")
+ out.write("<tr><th><h3>Actions</h3></th><th>Started</th><th>Sent</th>")
+ out.write("<th>Successful</th><th>Failed</th><th>Completed</th><th>Received</th><th>Error</th>")
+ out.write("<th>Successful Delay</th><th>Failed Delay</th><th>Total Delay</th></tr>\n")
+ actions = self.actions.keys()
+ actions.sort()
+ for action in actions:
+ out.write("<tr><td>" + action + "</td>")
+ for i in xrange(7):
+ out.write("<td>" + str(self.actions[action][i]) + "</td>")
+ for i in xrange(3):
+ count = self.actions[action][i+2]
+ if count > 0:
+ total_delay = self.actions[action][i+7]
+ avg_delay = total_delay / count
+ avg_delay_sec = avg_delay.days*86400.0 + avg_delay.seconds + avg_delay.microseconds/1000000.0
+ else:
+ avg_delay_sec = 0.0
+ out.write("<td>%0.2f</td>" % avg_delay_sec)
+ out.write('</tr>\n')
+ out.write("</table>\n")
+ out.write("</td></tr>\n")
+ out.write("</table>\n")
+
+ return out.getvalue()
+
+ #{ Called by the action
+ def startedAction(self, action):
+ """Record that an action was started.
+
+ @param action: the name of the action
+ """
+ act = self.actions.setdefault(action, [0, 0, 0, 0, 0, 0, 0, timedelta(), timedelta(), timedelta()])
+ act[0] += 1