2 """Store statistics for the Khashmir DHT."""
4 from datetime import datetime, timedelta
5 from copy import deepcopy
8 """Store the statistics for the Khashmir DHT.
10 @ivar _StatsTemplate: a template for returning all the statistics
11 @type config: C{dictionary}
12 @ivar config: the configuration parameters for the DHT
13 @ivar startTime: the time the program was started
14 @ivar reachable: whether we can be contacted by other nodes
15 @type table: L{ktable.KTable}
16 @ivar table: the routing table for the DHT
17 @ivar lastTableUpdate: the last time an update of the table stats was done
18 @ivar nodes: the number of nodes connected
19 @ivar users: the estimated number of total users in the DHT
21 @ivar store: the database for the DHT
22 @ivar lastDBUpdate: the last time an update of the database stats was done
23 @ivar keys: the number of distinct keys in the database
24 @ivar values: the number of values in the database
25 @ivar downPackets: the number of packets received
26 @ivar upPackets: the number of packets sent
27 @ivar downBytes: the number of bytes received
28 @ivar upBytes: the number of bytes sent
29 @ivar actions: a dictionary of the actions and their statistics, keys are
30 the action name, values are a list of 5 elements for the number of
31 times the action was sent, responded to, failed, received, and
35 _StatsTemplate = [{'name': 'uptime',
38 'tip': 'The elapsed time since the program started',
44 'tip': 'Whether other nodes can contact us (not NATted or firewalled)',
48 'group': 'Routing Table',
49 'desc': 'Number of nodes',
50 'tip': 'The number of nodes we are connected to',
54 'group': 'Routing Table',
55 'desc': 'Total number of users',
56 'tip': 'The estimated total number of users in the DHT',
62 'tip': 'The number of distinct keys in the database',
68 'tip': 'The total number of values in the database',
71 {'name': 'downPackets',
73 'desc': 'Downloaded packets',
74 'tip': 'The number of received packets',
79 'desc': 'Uploaded packets',
80 'tip': 'The number of sent packets',
85 'desc': 'Downloaded bytes',
86 'tip': 'The number of bytes received by the DHT',
91 'desc': 'Uploaded bytes',
92 'tip': 'The number of bytes sent by the DHT',
98 'tip': 'The number of requests for each action',
103 def __init__(self, table, store, config):
104 """Initialize the statistics.
106 @type table: L{ktable.KTable}
107 @param table: the routing table for the DHT
108 @type store: L{db.DB}
109 @param store: the database for the DHT
110 @type config: C{dictionary}
111 @param config: the configuration parameters for the DHT
115 self.startTime = datetime.now()
116 self.reachable = False
120 self.lastTableUpdate = datetime.now()
126 self.lastDBUpdate = datetime.now()
137 def tableStats(self):
138 """Collect some statistics about the routing table.
140 @rtype: (C{int}, C{int})
141 @return: the number of contacts in the routing table, and the estimated
142 number of nodes in the entire DHT
144 if datetime.now() - self.lastTableUpdate > timedelta(seconds = 15):
145 self.lastTableUpdate = datetime.now()
146 self.nodes = reduce(lambda a, b: a + len(b.l), self.table.buckets, 0)
147 self.users = self.config['K'] * (2**(len(self.table.buckets) - 1))
148 return (self.nodes, self.users)
151 """Collect some statistics about the database.
153 @rtype: (C{int}, C{int})
154 @return: the number of keys and values in the database
156 if datetime.now() - self.lastDBUpdate > timedelta(minutes = 1):
157 self.lastDBUpdate = datetime.now()
158 self.keys, self.values = self.store.keyStats()
159 return (self.keys, self.values)
162 """Gather all the statistics for the DHT.
164 @rtype: C{list} of C{dictionary}
165 @return: each dictionary has keys describing the statistic:
166 name, group, desc, tip, and value
170 stats = self._StatsTemplate[:]
172 val = getattr(self, stat['name'], None)
173 if stat['name'] == 'uptime':
174 stat['value'] = dattime.now() - self.startTime
175 elif stat['name'] == 'actions':
176 stat['value'] = deepcopy(actions)
177 elif val is not None:
182 #{ Called by the transport
183 def sentAction(self, action):
184 """Record that an action was attempted.
186 @param action: the name of the action
188 act = self.actions.setdefault(action, [0, 0, 0, 0, 0])
191 def responseAction(self, response, action):
192 """Record that a response to an action was received.
194 @param response: the response
195 @param action: the name of the action
196 @return: the response (for use in deferreds)
198 act = self.actions.setdefault(action, [0, 0, 0, 0, 0])
202 def failedAction(self, response, action):
203 """Record that a failed response to an action was received.
205 @param response: the response
206 @param action: the name of the action
207 @return: the response (for use in deferreds)
209 act = self.actions.setdefault(action, [0, 0, 0, 0, 0])
213 def receivedAction(self, action):
214 """Record that an action was received.
216 @param action: the name of the action
218 self.reachable = True
219 act = self.actions.setdefault(action, [0, 0, 0, 0, 0])
222 def errorAction(self, action):
223 """Record that a received action resulted in an error.
225 @param action: the name of the action
227 act = self.actions.setdefault(action, [0, 0, 0, 0, 0])
230 def sentBytes(self, bytes):
231 """Record that a single packet of some bytes was sent.
233 @param bytes: the number of bytes in the packet
236 self.upBytes += bytes
238 def receivedBytes(self, bytes):
239 """Record that a single packet of some bytes was received.
241 @param bytes: the number of bytes in the packet
243 self.downPackets += 1
244 self.downBytes += bytes