]> git.mxchange.org Git - quix0rs-apt-p2p.git/blob - apt_p2p_Khashmir/stats.py
aee9bbbe5f72ab0b18c01ffc36e23eba758c3326
[quix0rs-apt-p2p.git] / apt_p2p_Khashmir / stats.py
1
2 """Store statistics for the Khashmir DHT."""
3
4 from datetime import datetime, timedelta
5 from StringIO import StringIO
6
7 from util import byte_format
8
9 class StatsLogger:
10     """Store the statistics for the Khashmir DHT.
11     
12     @type config: C{dictionary}
13     @ivar config: the configuration parameters for the DHT
14     @ivar startTime: the time the program was started
15     @ivar reachable: whether we can be contacted by other nodes
16     @type table: L{ktable.KTable}
17     @ivar table: the routing table for the DHT
18     @ivar lastTableUpdate: the last time an update of the table stats was done
19     @ivar nodes: the number of nodes connected
20     @ivar users: the estimated number of total users in the DHT
21     @type store: L{db.DB}
22     @ivar store: the database for the DHT
23     @ivar lastDBUpdate: the last time an update of the database stats was done
24     @ivar keys: the number of distinct keys in the database
25     @ivar values: the number of values in the database
26     @ivar downPackets: the number of packets received
27     @ivar upPackets: the number of packets sent
28     @ivar downBytes: the number of bytes received
29     @ivar upBytes: the number of bytes sent
30     @ivar actions: a dictionary of the actions and their statistics, keys are
31         the action name, values are a list of 5 elements for the number of
32         times the action was sent, responded to, failed, received, and
33         generated an error
34     """
35     
36     def __init__(self, table, store, config):
37         """Initialize the statistics.
38         
39         @type table: L{ktable.KTable}
40         @param table: the routing table for the DHT
41         @type store: L{db.DB}
42         @param store: the database for the DHT
43         @type config: C{dictionary}
44         @param config: the configuration parameters for the DHT
45         """
46         # General
47         self.config = config
48         self.startTime = datetime.now().replace(microsecond=0)
49         self.reachable = False
50         
51         # Routing Table
52         self.table = table
53         self.lastTableUpdate = datetime.now()
54         self.nodes = 0
55         self.users = 0
56         
57         # Database
58         self.store = store
59         self.lastDBUpdate = datetime.now()
60         self.keys = 0
61         self.values = 0
62         
63         # Transport
64         self.downPackets = 0
65         self.upPackets = 0
66         self.downBytes = 0L
67         self.upBytes = 0L
68         self.actions = {}
69     
70     def tableStats(self):
71         """Collect some statistics about the routing table.
72         
73         @rtype: (C{int}, C{int})
74         @return: the number of contacts in the routing table, and the estimated
75             number of nodes in the entire DHT
76         """
77         if datetime.now() - self.lastTableUpdate > timedelta(seconds = 15):
78             self.lastTableUpdate = datetime.now()
79             self.nodes = reduce(lambda a, b: a + len(b.l), self.table.buckets, 0)
80             self.users = self.config['K'] * (2**(len(self.table.buckets) - 1))
81         return (self.nodes, self.users)
82     
83     def dbStats(self):
84         """Collect some statistics about the database.
85         
86         @rtype: (C{int}, C{int})
87         @return: the number of keys and values in the database
88         """
89         if datetime.now() - self.lastDBUpdate > timedelta(minutes = 1):
90             self.lastDBUpdate = datetime.now()
91             self.keys, self.values = self.store.keyStats()
92         return (self.keys, self.values)
93     
94     def formatHTML(self):
95         """Gather statistics for the DHT and format them for display in a browser.
96         
97         @rtype: C{string}
98         @return: the stats, formatted for display in the body of an HTML page
99         """
100         self.tableStats()
101         self.dbStats()
102         elapsed = datetime.now().replace(microsecond=0) - self.startTime
103         out = StringIO()
104         out.write('<h2>DHT Statistics</h2>\n')
105         out.write("<table border='0' cellspacing='20px'>\n<tr>\n")
106         out.write('<td>\n')
107
108         # General
109         out.write("<table border='1' cellpadding='4px'>\n")
110         out.write("<tr><th><h3>General</h3></th><th>Value</th></tr>\n")
111         out.write("<tr title='Elapsed time since the DHT was started'><td>Up time</td><td>" + str(elapsed) + '</td></tr>\n')
112         out.write("<tr title='Whether this node is reachable by other nodes'><td>Reachable</td><td>" + str(self.reachable) + '</td></tr>\n')
113         out.write("</table>\n")
114         out.write('</td><td>\n')
115         
116         # Routing
117         out.write("<table border='1' cellpadding='4px'>\n")
118         out.write("<tr><th><h3>Routing Table</h3></th><th>Value</th></tr>\n")
119         out.write("<tr title='The number of connected nodes'><td>Number of nodes</td><td>" + str(self.nodes) + '</td></tr>\n')
120         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')
121         out.write("</table>\n")
122         out.write('</td><td>\n')
123         
124         # Database
125         out.write("<table border='1' cellpadding='4px'>\n")
126         out.write("<tr><th><h3>Database</h3></th><th>Value</th></tr>\n")
127         out.write("<tr title='Number of distinct keys in the database'><td>Keys</td><td>" + str(self.keys) + '</td></tr>\n')
128         out.write("<tr title='Total number of values stored locally'><td>Values</td><td>" + str(self.values) + '</td></tr>\n')
129         out.write("</table>\n")
130         out.write("</td></tr><tr><td colspan='3'>\n")
131         
132         # Transport
133         out.write("<table border='1' cellpadding='4px'>\n")
134         out.write("<tr><th><h3>Transport</h3></th><th>Packets</th><th>Bytes</th><th>Speed</th></tr>\n")
135         out.write("<tr title='Stats for packets received from the DHT'><td>Downloaded</td>")
136         out.write('<td>' + str(self.downPackets) + '</td>')
137         out.write('<td>' + byte_format(self.downBytes) + '</td>')
138         out.write('<td>' + byte_format(self.downBytes / (elapsed.days*86400.0 + elapsed.seconds)) + '/sec</td></tr>\n')
139         out.write("<tr title='Stats for packets sent to the DHT'><td>Uploaded</td>")
140         out.write('<td>' + str(self.upPackets) + '</td>')
141         out.write('<td>' + byte_format(self.upBytes) + '</td>')
142         out.write('<td>' + byte_format(self.upBytes / (elapsed.days*86400.0 + elapsed.seconds)) + '/sec</td></tr>\n')
143         out.write("</table>\n")
144         out.write("</td></tr><tr><td colspan='3'>\n")
145         
146         # Actions
147         out.write("<table border='1' cellpadding='4px'>\n")
148         out.write("<tr><th><h3>Actions</h3></th><th>Started</th><th>Sent</th><th>OK</th><th>Failed</th><th>Received</th><th>Error</th></tr>\n")
149         actions = self.actions.keys()
150         actions.sort()
151         for action in actions:
152             out.write("<tr><td>" + action + "</td>")
153             for i in xrange(6):
154                 out.write("<td>" + str(self.actions[action][i]) + "</td>")
155             out.write('</tr>\n')
156         out.write("</table>\n")
157         out.write("</td></tr>\n")
158         out.write("</table>\n")
159         
160         return out.getvalue()
161
162     #{ Called by the action
163     def startedAction(self, action):
164         """Record that an action was started.
165         
166         @param action: the name of the action
167         """
168         act = self.actions.setdefault(action, [0, 0, 0, 0, 0, 0])
169         act[0] += 1
170     
171     #{ Called by the transport
172     def sentAction(self, action):
173         """Record that an action was attempted.
174         
175         @param action: the name of the action
176         """
177         act = self.actions.setdefault(action, [0, 0, 0, 0, 0, 0])
178         act[1] += 1
179         
180     def responseAction(self, response, action):
181         """Record that a response to an action was received.
182         
183         @param response: the response
184         @param action: the name of the action
185         @return: the response (for use in deferreds)
186         """
187         act = self.actions.setdefault(action, [0, 0, 0, 0, 0, 0])
188         act[2] += 1
189         return response
190         
191     def failedAction(self, response, action):
192         """Record that a failed response to an action was received.
193         
194         @param response: the response
195         @param action: the name of the action
196         @return: the response (for use in deferreds)
197         """
198         act = self.actions.setdefault(action, [0, 0, 0, 0, 0, 0])
199         act[3] += 1
200         return response
201         
202     def receivedAction(self, action):
203         """Record that an action was received.
204         
205         @param action: the name of the action
206         """
207         self.reachable = True
208         act = self.actions.setdefault(action, [0, 0, 0, 0, 0, 0])
209         act[4] += 1
210     
211     def errorAction(self, action):
212         """Record that a received action resulted in an error.
213         
214         @param action: the name of the action
215         """
216         act = self.actions.setdefault(action, [0, 0, 0, 0, 0, 0])
217         act[5] += 1
218     
219     def sentBytes(self, bytes):
220         """Record that a single packet of some bytes was sent.
221         
222         @param bytes: the number of bytes in the packet
223         """
224         self.upPackets += 1
225         self.upBytes += bytes
226         
227     def receivedBytes(self, bytes):
228         """Record that a single packet of some bytes was received.
229         
230         @param bytes: the number of bytes in the packet
231         """
232         self.downPackets += 1
233         self.downBytes += bytes