ppf: Update DatabaseAccess classes to initialize database in constructor.
[cs-p2p-next.git] / ppf / new / storage.py
1 """
2 Storage class for P2P logging information.
3
4 2011, Razvan Deaconescu, razvan.deaconescu@cs.pub.ro
5 """
6
7 import os
8 import os.path
9 import re
10 import logging
11 import sqlite3
12
13 #
14 # Logging code heavily inspired by Logging HOWTO documentation:
15 #     http://docs.python.org/dev/howto/logging.html#configuring-logging
16 #
17
18 # Create logger; default logging level is DEBUG.
19 logger = logging.getLogger(__name__)
20 logger.setLevel(logging.DEBUG)
21
22 # Create console handler and set level to ERROR.
23 ch = logging.StreamHandler()
24 ch.setLevel(logging.DEBUG)
25
26 # Create formatter.
27 formatter = logging.Formatter('%(filename)s:%(lineno)s - %(levelname)s: %(message)s')
28
29 # Add formatter to console handler.
30 ch.setFormatter(formatter)
31
32 # Add console handler to logger.
33 logger.addHandler(ch)
34
35
36 message_types = {
37         'CHOKE': {'id': 1, 'parameters': None},
38         'UNCHOKE': {'id': 2, 'parameters': None},
39         'INTERESTED': {'id': 3, 'parameters': None},
40         'NOT_INTERESTED': {'id': 4, 'parameters': None},
41         'HAVE': {'id': 5, 'parameters': None},
42         'BITFIELD': {'id': 6, 'parameters': None},
43         'REQUEST': {'id': 7, 'parameters': None},
44         'PIECE': {'id': 8, 'parameters': None},
45         'CANCEL': {'id': 9, 'parameters': None},
46         'DHT_PORT': {'id': 10, 'parameters': None}
47 }
48
49 bittorrent_clients = {
50         'Tribler': {
51             'id': 1,
52             'language': 'Python',
53             'url': 'http://www.tribler.org/trac',
54             'dht_support': True,
55             'streaming_support': True,
56             'pxe_support': None,
57             'features': None
58         },
59         'NextShare': {
60             'id': 2,
61             'language': 'Python',
62             'url': 'https://trac.p2p-next.org/',
63             'dht_support': True,
64             'streaming_support': True,
65             'pxe_support': None,
66             'features': None
67         },
68         'libtorrent-rasterbar': {
69             'id': 3,
70             'language': 'C++',
71             'url': 'http://www.rasterbar.com/products/libtorrent/',
72             'dht_support': True,
73             'streaming_support': True,
74             'pxe_support': None,
75             'features': None
76         },
77         'Vuze': {
78             'id': 4,
79             'language': 'Java',
80             'url': 'http://www.vuze.com/',
81             'dht_support': True,
82             'streaming_support': True,
83             'pxe_support': None,
84             'features': None
85         },
86         'Transmission': {
87             'id': 5,
88             'language': 'C',
89             'url': 'http://www.transmissionbt.com/',
90             'dht_support': True,
91             'streaming_support': False,
92             'pxe_support': None,
93             'features': None
94         },
95         'Aria': {
96             'id': 6,
97             'language': 'C',
98             'url': 'http://aria2.sourceforge.net/',
99             'dht_support': True,
100             'streaming_support': False,
101             'pxe_support': None,
102             'features': None
103         },
104         'Mainline': {
105             'id': 7,
106             'language': 'Python',
107             'url': 'http://www.bittorrent.com/',
108             'dht_support': True,
109             'streaming_support': False,
110             'pxe_support': None,
111             'features': None
112         }
113 }
114
115 transfer_directions = {
116         'receive': 1,
117         'send': 2
118 }
119
120 class Swarm(object):
121     """ Class mimics a C structure. """
122     def __init__(self, torrent_filename=None, data_size=None,
123             description=None):
124         self.torrent_filename = torrent_filename
125         self.data_size = data_size
126         self.description = description
127
128 class ClientSession(object):
129     """ Class mimics a C structure. """
130     # TODO: Add timezone.
131     def __init__(self, swarm_id=None, btclient=None, system_os=None,
132             system_os_version=None, system_ram=None, system_cpu=None,
133             public_ip=None, public_port=None, ds_limit=None, us_limit=None,
134             start_time=None, dht_enabled=None, pxe_enabled=None,
135             streaming_enabled=None, features=None, description=None):
136         self.swarm_id = swarm_id
137         self.btclient = btclient
138         self.system_os = system_os
139         self.system_os_version = system_os_version
140         self.system_ram = system_ram
141         self.system_cpu = system_cpu
142         self.public_ip = public_ip
143         self.public_port = public_port
144         self.ds_limit = ds_limit
145         self.us_limit = us_limit
146         self.start_time = start_time
147         self.dht_enabled = dht_enabled
148         self.pxe_enabled = pxe_enabled
149         self.streaming_enabled = streaming_enabled
150         self.features = features
151         self.description = description
152
153 class PeerStatusMessage(object):
154     """ Class mimics a C structure. """
155     def __init__(self, swarm_id=None, client_session_id=None, timestamp=None,
156             peer_ip=None, peer_port=None, download_speed=None,
157             upload_speed=None):
158         self.swarm_id = swarm_id
159         self.client_session_id = client_session_id
160         self.timestamp = timestamp
161         self.peer_ip = peer_ip
162         self.peer_port = peer_port
163         self.download_speed = download_speed
164         self.upload_speed = upload_speed
165
166 class StatusMessage(object):
167     """ Class mimics a C structure. """
168     def __init__(self, swarm_id=None, client_session_id=None, timestamp=None,
169             time=None, num_peers=None, num_dht_peers=None,
170             download_speed=None, upload_speed=None, download_size=None,
171             upload_size=None, eta=None):
172         self.swarm_id = swarm_id
173         self.client_session_id = client_session_id
174         self.timestamp = timestamp
175         self.num_peers = num_peers
176         self.num_dht_peers = num_dht_peers
177         self.download_speed = download_speed
178         self.upload_speed = upload_speed
179         self.download_size = download_size
180         self.upload_size = upload_size
181         self.eta = eta
182
183 class VerboseMessage(object):
184     """ Class mimics a C structure. """
185     def __init__(self, swarm_id=None, client_session_id=None, timestamp=None,
186             transfer_direction=None, peer_ip=None, peer_port=None,
187             message_type=None, index=None, begin=None, length=None,
188             listen_port=None):
189         self.swarm_id = swarm_id
190         self.client_session_id = client_session_id
191         self.timestamp = timestamp
192         self.transfer_direction = transfer_direction
193         self.peer_ip = peer_ip
194         self.peer_port = peer_port
195         self.message_type = message_type
196         self.index = index
197         self.begin = begin
198         self.length = length
199         self.listen_port = listen_port
200
201 class SwarmDataAccess(object):
202     def __init__(self):
203         pass
204
205     def add_swarm(self, swarm):
206         pass
207
208     def remove_swarm(self):
209         pass
210
211     def get_swarm(self):
212         pass
213
214     def update_swarm(self):
215         pass
216
217     def add_client_session(self, session):
218         pass
219
220     def remove_client_session(self):
221         pass
222
223     def get_client_session(self):
224         pass
225
226     def update_client_session(self):
227         pass
228
229     def add_peer_status_message(self, msg):
230         pass
231
232     def remove_peer_status_message(self):
233         pass
234
235     def get_peer_status_message(self):
236         pass
237
238     def update_peer_status_message(self):
239         pass
240
241     def add_status_message(self, msg):
242         pass
243
244     def remove_status_message(self):
245         pass
246
247     def get_status_message(self):
248         pass
249
250     def update_status_message(self):
251         pass
252
253     def add_verbose_message(self, msg):
254         pass
255
256     def remove_verbose_message(self):
257         pass
258
259     def get_verbose_message(self):
260         pass
261
262     def update_verbose_message(self):
263         pass
264
265 class FileAccess(SwarmDataAccess):
266     def __init__(self, path):
267         self.base_path = path
268
269 def find_last_numeric_subfolder(path):
270     """
271     Find last numeric folder in base_path folder.
272     The last numeric folder is the last swarm_id.
273     """
274     dir_list = []
275     pattern = re.compile("[0-9]+")
276
277     # Browse entries in base_path folder.
278     listing = os.listdir(path)
279     for entry in listing:
280         # If directory name is a number (id) add it to the list.
281         if os.path.isdir(os.path.join(path, entry)):
282             if pattern.match(entry):
283                 dir_list.append(int(entry))
284
285     if not dir_list:
286         return None
287     else:
288         dir_list.sort()
289         return dir_list[len(dir_list)-1]
290
291 class TreeTextFileAccess(FileAccess):
292     def __init__(self, path):
293         super(TreeTextFileAccess, self).__init__(path)
294
295     def add_swarm(self, swarm):
296         """
297         Create a subfolder with an unique id. Add 1 to the last numeric
298         subfolder id. In case none exists, use 1 as id.
299         """
300         id = find_last_numeric_subfolder(self.base_path)
301         if id == None:
302             id = 1
303         else:
304             id = id+1
305
306         swarm_path = os.path.join(self.base_path, str(id))
307         os.mkdir(swarm_path)
308
309         swarm_config = os.path.join(swarm_path, "swarm.conf")
310         f = open(swarm_config, 'w')
311         f.write("""id = %s
312         torrent_filename = %s
313         data_size = %s
314         description = %s
315         """ %(id, swarm.torrent_filename, swarm.data_size, swarm.description))
316         f.close()
317
318     def add_client_session(self, session):
319         """
320         Create session subfolder in swarm subfolder and add config file.
321         TODO: Throw exception in case swarm subfolder doesn't exist.
322         """
323         swarm_path = os.path.join(self.base_path, str(session.swarm_id))
324
325         # Search first available folder in swarm_path.
326         id = find_last_numeric_subfolder(swarm_path)
327         if id == None:
328             id = 1
329         else:
330             id = id+1
331
332         # Create session subfolder.
333         session_path = os.path.join(swarm_path, str(id))
334         os.mkdir(session_path)
335
336         # Create and populate configuration file.
337         session_config = os.path.join(session_path, "client_session.conf")
338         f = open(session_config, 'w')
339         f.write("""id = %s
340         swarm_id = %s
341         btclient = %s
342         system_os = %s
343         system_os_version = %s
344         system_ram = %s
345         system_cpu = %s
346         public_ip = %s
347         public_port = %s
348         ds_limit = %s
349         us_limit = %s
350         start_time = %s
351         dht_enabled = %s
352         pxe_enabled = %s
353         streaming_enabled = %s
354         features = %s
355         description = %s
356         """ %(id, session.swarm_id, session.btclient, session.system_os,
357             session.system_os_version, session.system_ram, session.system_cpu,
358             session.public_ip, session.public_port, session.ds_limit,
359             session.us_limit, session.start_time, session.dht_enabled,
360             session.pxe_enabled, session.streaming_enabled,
361             session.features, session.description))
362         f.close()
363
364     def add_peer_status_message(self, msg):
365         # TODO: id is number of lines in file.
366         swarm_path = os.path.join(self.base_path, str(msg.swarm_id))
367         session_path = os.path.join(swarm_path, str(msg.client_session_id))
368         message_file = os.path.join(session_path, "peer_status.txt")
369
370         f = open(message_file, 'a')
371         f.write("""%s,%s,%s,%s,%s,%s,%s,%s\n"""
372                 %(1, msg.swarm_id, msg.client_session_id, msg.timestamp,
373                     msg.peer_ip, msg.peer_port, msg.download_speed,
374                     msg.upload_speed))
375         f.close()
376
377     def add_status_message(self, msg):
378         # TODO: id is number of lines in file.
379         swarm_path = os.path.join(self.base_path, str(msg.swarm_id))
380         session_path = os.path.join(swarm_path, str(msg.client_session_id))
381         message_file = os.path.join(session_path, "status.txt")
382
383         f = open(message_file, 'a')
384         f.write("""%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n"""
385                 %(1, msg.swarm_id, msg.client_session_id, msg.timestamp,
386                     msg.num_peers, msg.num_dht_peers, msg.download_speed,
387                     msg.upload_speed, msg.download_size, msg.upload_size,
388                     msg.eta))
389         f.close()
390
391     def add_verbose_message(self, msg):
392         # TODO: id is number of lines in file.
393         swarm_path = os.path.join(self.base_path, str(msg.swarm_id))
394         session_path = os.path.join(swarm_path, str(msg.client_session_id))
395         message_file = os.path.join(session_path, "verbose.txt")
396
397         f = open(message_file, 'a')
398         f.write("""%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n"""
399                 %(1, msg.swarm_id, msg.client_session_id, msg.timestamp,
400                     msg.transfer_direction, msg.peer_ip, msg.peer_port,
401                     msg.message_type, msg.index, msg.begin, msg.length,
402                     msg.listen_port))
403         f.close()
404
405 class DatabaseAccess(SwarmDataAccess):
406     def __init__(self, database):
407         self.database = database
408
409     def connect(self):
410         self.conn = None
411         self.cursor = None
412
413     def disconnect(self):
414         self.cursor.close()
415         self.conn.close()
416
417 class SQLiteDatabaseAccess(DatabaseAccess):
418     def __init___(self, database):
419         super(SQLiteDatabaseAccess, self).__init__(database)
420
421     def connect(self):
422         self.conn = sqlite3.connect(self.database)
423         self.cursor = self.conn.cursor()
424
425     def add_swarm(self, swarm):
426         pass
427
428     def add_client_session(self, session):
429         pass
430
431     def add_peer_status_message(self, msg):
432         pass
433
434     def add_status_message(self, msg):
435         pass
436
437     def add_verbose_message(self, msg):
438         pass
439
440 class MySQLDatabaseAccess(DatabaseAccess):
441     def __init___(self, database):
442         super(SQLiteDatabaseAccess, self).__init__(database)
443
444     def connect(self):
445         pass
446
447     def add_swarm(self, swarm):
448         pass
449
450     def add_client_session(self, session):
451         pass
452
453     def add_peer_status_message(self, msg):
454         pass
455
456     def add_status_message(self, msg):
457         pass
458
459     def add_verbose_message(self, msg):
460         pass