[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [depictor/master] Ingest historical bwauth statistics data



commit e7a7dce049904664f0c1d7ba99ff76779c23e9ea
Author: Tom Ritter <tom@xxxxxxxxx>
Date:   Fri Apr 14 23:42:30 2017 -0500

    Ingest historical bwauth statistics data
    
    Update the parseOldConsensuses.py script to ingest historical
    bwauth data. Note that this script is generally run one time
    and is not intended to be a well-maintained script for future use.
    It needs care and feeding for each major run.
    
    Additionally create a mergeDatabase script. This script...
    probably does not do what we want. It's included mostly as a
    placeholder for future development if we want to correct and use
    it in the future.
---
 mergeDatabases.py      |  66 ++++++++++++++++
 parseOldConsensuses.py | 208 ++++++++++++++++++++++++++++++++++++++++++++-----
 utility.py             |   8 ++
 3 files changed, 263 insertions(+), 19 deletions(-)

diff --git a/mergeDatabases.py b/mergeDatabases.py
new file mode 100755
index 0000000..ac025f5
--- /dev/null
+++ b/mergeDatabases.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import time
+import sqlite3
+import datetime
+import operator
+import traceback
+import subprocess
+
+if __name__ == '__main__':
+	if len(sys.argv) != 3:
+		print "Usage: ", sys.argv[0], "src.db dest.db"
+		print "\tMerge all the data from src into dest"
+		sys.exit(1)
+
+	if not os.path.isfile(sys.argv[1]):
+		print "Source is not a file"
+		sys.exit(1)
+	if not os.path.isfile(sys.argv[2]):
+		print "Dest is not a file"
+		sys.exit(1)
+
+	src = sqlite3.connect(sys.argv[1])
+	dst = sqlite3.connect(sys.argv[2])
+
+	s_tbls = src.execute("SELECT name FROM sqlite_master WHERE type = 'table'")
+	for t in s_tbls:
+		t = t[0]
+		skip_table = False
+
+		d_tbl = dst.execute("SELECT name FROM sqlite_master WHERE type = 'table' and name = ?", (t,))
+		if not d_tbl.fetchone():
+			print "Skipping table", t, "which is in src but not in dst"
+			continue
+
+		s_cols = src.execute("PRAGMA table_info(" + t + ")")
+		d_cols = dst.execute("PRAGMA table_info(" + t + ")")
+		s_cols = s_cols.fetchall()
+		d_cols = d_cols.fetchall()
+		if len(s_cols) != len(d_cols):
+			print "Skipping table", t, "which has", len(s_cols), "columns in src and", len(d_cols)
+			continue
+		for i in range(len(s_cols)):
+			if s_cols[i] != d_cols[i]:
+				print "Skipping table", t, "because column", 1, "is", s_cols[i], "in src and", d_cols[i], "in dst"
+				skip_table = True
+		
+		if skip_table:
+			continue
+
+		print "Merging table", t
+		merged = 0
+		s = src.execute("SELECT * FROM " + t)
+		for r in s.fetchall():
+			date = r[0]
+			has_value = False
+			for v in r[1:]:
+				if v:
+					has_value = True
+			if has_value:
+				merged += 1
+				dst.execute("INSERT OR REPLACE INTO " + t + " VALUES (" + ",".join("?" * len(r)) + ")", r)
+				dst.commit()
+		print "Inserted or updated", merged, "rows"
\ No newline at end of file
diff --git a/parseOldConsensuses.py b/parseOldConsensuses.py
index 834c386..defa2f5 100755
--- a/parseOldConsensuses.py
+++ b/parseOldConsensuses.py
@@ -18,6 +18,7 @@ import stem.util.conf
 import stem.util.enum
 
 from stem import Flag
+from stem.descriptor.reader import DescriptorReader
 from stem.util.lru_cache import lru_cache
 
 def get_dirauths_in_tables():
@@ -48,17 +49,23 @@ def get_dirauth_from_filename(filename):
 		return "tor26"
 	elif key == "0232AF901C31A04EE9848595AF9BB7620D4C5B2E" or key == "585769C78764D58426B8B52B6651A5A71137189A":
 		return "dannenberg"
-        elif key == "27B6B5996C426270A5C95488AA5BCEB6BCC86956":
-                return "turtles"
+	elif key == "27B6B5996C426270A5C95488AA5BCEB6BCC86956":
+		return "turtles"
 	else:
 		raise Exception("Unexpcected dirauth key: " + key + " " + filename)
 
 def unix_time(dt):
     return (dt - datetime.datetime.utcfromtimestamp(0)).total_seconds() * 1000.0
 
+def ut_to_datetime(ut):
+	return datetime.datetime.utcfromtimestamp(ut / 1000)
+
+def ut_to_datetime_format(ut):
+	return ut_to_datetime(ut).strftime("%Y-%m-%d-%H-%M-%S")
+
 def get_time_from_filename(filename):
 	voteTime = filename.split('-')
-	if len(voteTime) < 9:
+	if len(voteTime) < 7:
 		raise Exception("Strange filename: " + filename)
 
 	v = [int(x) for x in filename.split('-')[0:6]]
@@ -66,26 +73,30 @@ def get_time_from_filename(filename):
 	voteTime = unix_time(voteTime)
 	return voteTime
 
-def main(dir):
-	dirAuths = get_dirauths_in_tables()
-	dbc = sqlite3.connect(os.path.join('data', 'historical.db'))
-
+def dirauth_relay_votes(directory, dirAuths, dbc):
 	dirauth_columns = ""
 	dirauth_columns_questions = ""
 	for d in dirAuths:
 		dirauth_columns += d + "_known integer, " + d + "_running integer, " + d + "_bwauth integer, "
 		dirauth_columns_questions += ",?,?,?"
 
+	dbc.execute("CREATE TABLE IF NOT EXISTS vote_data(date integer, " + dirauth_columns + "PRIMARY KEY(date ASC))")
+	dbc.commit()
+
 	votes = {}
-	for root, dirs, files in os.walk(dir):
+	for root, dirs, files in os.walk(directory):
 		for f in files:
-                        filepath = os.path.join(root, f)
-                        print filepath
+			filepath = os.path.join(root, f)
+			print filepath
 
 			if '"' in f:
 				raise Exception("Potentially malicious filename")
-                        elif "votes-" in f and ".tar" in f:
-                                continue
+			elif "votes-" in f and ".tar" in f:
+				continue
+			elif "consensuses-" in f and ".tar" in f:
+				continue
+			elif "-vote-" not in f:
+				continue
 
 			voteTime = get_time_from_filename(f)
 			if voteTime not in votes:
@@ -104,11 +115,8 @@ def main(dir):
 			votes[voteTime][dirauth]['running'] = int(subprocess.check_output('egrep "^s " "' + filepath + '" | grep " Running" | wc -l', shell=True))
 			votes[voteTime][dirauth]['bwlines'] = int(subprocess.check_output('grep Measured= "' + filepath + '" | wc -l', shell=True))
 
-	dbc.execute("CREATE TABLE IF NOT EXISTS vote_data(date integer, " + dirauth_columns + "PRIMARY KEY(date ASC))")
-	dbc.commit()
-
 	for t in votes:
-		print t
+		print ut_to_datetime(t)
 		print "\t", len(votes[t])
 		for d in votes[t]:
 			print "\t", d, votes[t][d]['bwlines'], votes[t][d]['running']
@@ -127,13 +135,175 @@ def main(dir):
 		dbc.execute("INSERT OR REPLACE INTO vote_data VALUES (?" + dirauth_columns_questions + ")", insertValues)
 		dbc.commit()
 
+def bwauth_measurements(directory, dirAuths, dbc):
+	#Find all the consensuses and votesrm
+	votes = {}
+	consensuses = {}
+	for root, dirs, files in os.walk(directory):
+		for f in files:
+			filepath = os.path.join(root, f)
+
+			if '"' in f:
+				raise Exception("Potentially malicious filename")
+			elif "votes-" in f and ".tar" in f:
+				continue
+			elif "consensuses-" in f and ".tar" in f:
+				continue
+
+			if "-consensus" in f:
+				consensusTime = get_time_from_filename(f)
+				if consensusTime not in consensuses:
+					consensuses[consensusTime] = filepath
+				else:
+					print "Found two consensuses with the same time:", ut_to_datetime(consensusTime)
+
+				#print "Consensus:", filepath
+			elif "-vote-" in f:
+				voteTime = get_time_from_filename(f)
+
+				# Test to see if we already processed this one
+				cur = dbc.cursor()
+				cur.execute("SELECT * FROM bwauth_data WHERE date = ?", (voteTime,))
+				if cur.fetchone():
+					print "Skipping", f, "because we already processed it"
+					continue
+				elif voteTime not in votes:
+					votes[voteTime] = {}
+
+				dirauth = get_dirauth_from_filename(f)
+
+				if dirauth not in dirAuths:
+					raise Exception("Found a dirauth I don't know about (probably spelling): " + dirauth)
+				elif dirauth not in votes[voteTime]:
+					votes[voteTime][dirauth] = filepath
+				else:
+					print "Found two votes for dirauth " + dirauth + ":", filepath, "and", votes[voteTime][dirauth]
+
+				#print "Vote:", dirauth, filepath
+
+	#Make sure we have a consensus for each vote
+	to_del = []
+	for v in votes:
+		if v not in consensuses:
+			print "Have votes for time", ut_to_datetime(v), "but no consensus!"
+			to_del.append(v)
+			#sys.exit(1)
+	for i in to_del:
+		del votes[i]
+
+	#Make the table
+	bwauth_columns = ""
+	bwauth_columns_questions = ""
+	for d in dirAuths:
+		bwauth_columns += d + "_above integer, " + d + "_shared integer, " + d + "_exclusive integer, " + d + "_below integer, " + d + "_unmeasured integer, "
+		bwauth_columns_questions += ",?,?,?,?,?"
+
+	dbc.execute("CREATE TABLE IF NOT EXISTS bwauth_data(date integer, " + bwauth_columns + "PRIMARY KEY(date ASC))")
+	dbc.commit()
+
+	reviewed = 0
+	for v in votes:
+		reviewed += 1
+		print "Reviewing", consensuses[v], "(" + str(reviewed) + "/" + str(len(votes)) + ")"
+
+		#Get the consensus data
+		consensusRouters = {}
+		with DescriptorReader(consensuses[v]) as reader:
+			reader.register_skip_listener(my_listener)
+			for relay in reader:
+				consensusRouters[relay.fingerprint] = "Unmeasured" if relay.is_unmeasured else relay.bandwidth
+		
+		#The vote data
+		bwauthVotes = {}
+		for d in votes[v]:
+			if d not in bwauthVotes:
+				bwauthVotes[d] = {}
+
+			measured_something = False
+			with DescriptorReader(votes[v][d]) as reader:
+				reader.register_skip_listener(my_listener)
+				for relay in reader:
+					if relay.measured:
+						bwauthVotes[d][relay.fingerprint] = relay.measured
+						measured_something = True
+			if not measured_something:
+				del bwauthVotes[d]
+
+		#Now match them up and store the data
+		thisConsensusResults = {}
+		for r in consensusRouters:
+			for d in bwauthVotes:
+				had_any_value = False
+				if d not in thisConsensusResults:
+					thisConsensusResults[d] = {'unmeasured' : 0, 'above' : 0, 'below' : 0, 'exclusive' : 0 , 'shared' : 0}
+
+				if consensusRouters[r] == "Unmeasured":
+					continue
+				elif r not in bwauthVotes[d]:
+					had_any_value = True
+					thisConsensusResults[d]['unmeasured'] += 1
+				elif consensusRouters[r] < bwauthVotes[d][r]:
+					had_any_value = True
+					thisConsensusResults[d]['above'] += 1
+				elif consensusRouters[r] > bwauthVotes[d][r]:
+					had_any_value = True
+					thisConsensusResults[d]['below'] += 1
+				elif consensusRouters[r] == bwauthVotes[d][r] and \
+					 1 == len([1 for d_i in bwauthVotes if d_i in bwauthVotes and r in bwauthVotes[d_i] and bwauthVotes[d_i][r] == consensusRouters[r]]):
+					had_any_value = True
+				 	thisConsensusResults[d]['exclusive'] += 1
+			 	elif consensusRouters[r] == bwauthVotes[d][r] and \
+					 1 != len([1 for d_i in bwauthVotes if d_i in bwauthVotes and r in bwauthVotes[d_i] and bwauthVotes[d_i][r] == consensusRouters[r] ]):
+				 	had_any_value = True
+				 	thisConsensusResults[d]['shared'] += 1
+			 	else:
+			 		print "What case am I in???"
+			 		sys.exit(1)
+
+			 	if not had_any_value:
+		 			del thisConsensusResults[d]
+
+ 		insertValues = [v]
+ 		for d in dirAuths: 
+ 			if d in thisConsensusResults:
+				insertValues.append(thisConsensusResults[d]['above'])
+				insertValues.append(thisConsensusResults[d]['shared'])
+				insertValues.append(thisConsensusResults[d]['exclusive'])
+				insertValues.append(thisConsensusResults[d]['below'])
+				insertValues.append(thisConsensusResults[d]['unmeasured'])
+			else:
+				insertValues.append(None)
+				insertValues.append(None)
+				insertValues.append(None)
+				insertValues.append(None)
+				insertValues.append(None)
+ 		
+		dbc.execute("INSERT OR REPLACE INTO bwauth_data VALUES (?" + bwauth_columns_questions + ")", insertValues)
+		dbc.commit()
+		
+def my_listener(path, exception):
+	print "Skipped!"
+	print path
+	print exception
+
+
+def main(itype, directory):
+	dirAuths = get_dirauths_in_tables()
+	dbc = sqlite3.connect(os.path.join('data', 'historical.db'))
+
+	if itype == "dirauth_relay_votes":
+		dirauth_relay_votes(directory, dirAuths, dbc)
+	elif itype == "bwauth_measurements":
+		bwauth_measurements(directory, dirAuths, dbc)
+	else:
+		print "Unknown ingestion type"
 
 if __name__ == '__main__':
 	try:
-		if len(sys.argv) != 2:
-			print "Usage: ", sys.argv[0], "vote-directory"
+		if len(sys.argv) != 3:
+			print "Usage: ", sys.argv[0], "ingestion-type vote-directory"
 		else:
-			main(sys.argv[1])
+			main(sys.argv[1], sys.argv[2])
 	except:
 		msg = "%s failed with:\n\n%s" % (sys.argv[0], traceback.format_exc())
 		print "Error: %s" % msg
diff --git a/utility.py b/utility.py
index 6cebe6b..aeb7b8d 100644
--- a/utility.py
+++ b/utility.py
@@ -84,3 +84,11 @@ def _get_documents(label, resource):
 def unix_time(dt):
     return (dt - datetime.datetime.utcfromtimestamp(0)).total_seconds() * 1000.0
 
+def ut_to_datetime(ut):
+	return datetime.datetime.utcfromtimestamp(ut / 1000)
+
+def ut_to_datetime_format(ut):
+	return consensus_datetime_format(ut_to_datetime(ut))
+
+def consensus_datetime_format(dt):
+	return dt.strftime("%Y-%m-%d-%H-%M-%S")



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits