def fail_recover(name, addy, active_pings): if len(active_pings) == 0: logger.info("We have no active pingers for %s", name) db.mark_failed(name, addy, timefunc.utcnow()) else: uptime = up_today(active_pings) # If a remailers uptime is < 20%, then we mark it as failed and # record the time of failure in the genealogy table. This has to # be a low percentage or bunker would be considered dead. if uptime < config.deadpoint: logger.debug("%s is under %d%%, flagging it failed", name, config.deadpoint * 10) db.mark_failed(name, addy, timefunc.utcnow()) # Stats are greater than 50%, so delete any entries for this # remailer from the failed table. if uptime > config.livepoint: logger.debug("%s is healthy, deleting any failed flags it might have", name) db.mark_recovered(name, addy)
def uptimes(): ago = timefunc.hours_ago(config.active_age) ahead = timefunc.hours_ahead(config.active_future) uptimes = avg_uptime() #logger.debug("Writing Uptime HTML file %s", config.uptime_report_name) filename = "%s/%s" % (config.reportdir, config.uptime_report_name) uptimefile = open(filename, 'w') uptimefile.write("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <meta http-equiv="Content-Style-Type" content="text/css2" /> <meta name="keywords" content="Mixmaster,Remailer,Banana,Bananasplit"> <title>Bananasplit Website - Failing Remailers</title> <link rel="StyleSheet" href="stats.css" type="text/css"> </head> <body> <h1>Remailer Uptimes</h1> <p>This report provides an overview of the average uptime for each remailer based on the results from all currently responding pingers. Consider that this report doesn't define a scope for acceptable ping results; all are considered good. This means a single pinger can skew the average.</p> <table border="0" bgcolor="#000000"> <tr bgcolor="#F08080"> <th>Remailer Name</th> <th>Average Uptime</th> <th>Average Latency</th> <th>Pingers Reporting</th></tr>\n""") rotate_color = 0 for uptime in uptimes: # Rotate background colours for rows if rotate_color: bgcolor = "#ADD8E6" else: bgcolor = "#E0FFFF" rotate_color = not rotate_color name = uptime[0] up = uptime[1] count = uptime[3] lathrs,latmin = timefunc.hours_mins(uptime[2]) uptimefile.write('<tr bgcolor="%s">' % bgcolor) uptimefile.write('<th class="tableleft">%s</th>' % name) uptimefile.write('<td>%3.2f</td>' % up) uptimefile.write('<td>%d:%02d</td>' % (lathrs, latmin)) uptimefile.write('<td>%d</td></tr>\n' % (count, )) uptimefile.write('</table>\n') uptimefile.write('<br>Last update: %s (UTC)<br>\n' % timefunc.utcnow()) uptimefile.write('<br><a href="index.html">Index</a>\n') uptimefile.write('</body></html>') uptimefile.close()
def write_remailer_stats(name, addy, vitals): # Create a filename for the remailer details, open it and write a title and timestamp. noat = addy.replace("@", ".") filename = "%s/%s.%s.txt" % (config.reportdir, name, noat) statfile = open("%s" % (filename,), "w") statfile.write("Pinger statistics for the %(rem_name)s remailer (%(rem_addy)s)\n" % vitals) statfile.write("Last update: %s (UTC)\n" % timefunc.utcnow()) statfile.write("\nPingers\n") statfile.write(" Known: %d\t" % db.count_total_pingers()) statfile.write("Alive: %d\t" % vitals["rem_count_all"]) statfile.write("In-Scope: %d\t" % vitals["rem_active_count"]) # Out of scope pings are total Alive pings minus In-Scope Pings oos = vitals["rem_count_all"] - vitals["rem_active_count"] statfile.write("Out-of-Scope: %d\n" % oos) statfile.write("\nUptime\n") statfile.write(" Lowest: %3.2f%%\t\t" % (vitals["rem_uptime_min"] / 10.00)) statfile.write("Average: %3.2f%%\n" % (float(vitals["rem_uptime_avg"]) / 10.00)) statfile.write(" Highest: %3.2f%%\t" % (vitals["rem_uptime_max"] / 10.00)) statfile.write("StdDev: %3.2f%%\n" % (float(vitals["rem_uptime_stddev"]) / 10.00)) statfile.write("\nLatency\n") statfile.write(" Lowest: %d:%02d\t\t" % timefunc.hours_mins(vitals["rem_latency_min"])) statfile.write("Average: %d:%02d\n" % timefunc.hours_mins(vitals["rem_latency_avg"])) statfile.write(" Highest: %d:%02d\t\t" % timefunc.hours_mins(vitals["rem_latency_max"])) statfile.write("StdDev: %d:%02d\n" % timefunc.hours_mins(vitals["rem_latency_stddev"])) statfile.write("\nIn-scope pings\n") for row in db.remailer_active_pings(vitals): entry = db_process(row) statfile.write(entry) statfile.write("\n\nOut of scope pings\n") for row in db.remailer_ignored_pings(vitals): entry = db_process(row) statfile.write(entry) statfile.write("\n\nDead pings\n") for row in db.remailer_inactive_pings(vitals): entry = db_process(row) statfile.write(entry) statfile.close()
def writekeystats(): global now, ago, ahead now = timefunc.utcnow() ago = timefunc.hours_ago(config.active_age) ahead = timefunc.hours_ahead(config.active_future) write_stats()
def index(): """Generate an HTML index table referencing remailer name against pinger name. The content of the table is remailer uptimes.""" # Generate an index filename and open it. filename = "%s/index.html" % config.reportdir index = open(filename, 'w') # Write the standard HTML headers and the initial BODY parts. index.write('''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <meta http-equiv="Content-Style-Type" content="text/css2" /> <meta name="keywords" content="Mixmaster,Echolot,Remailer,Banana,Bananasplit"> <title>Bananasplit Website - Meta Statistics</title> <link rel="StyleSheet" href="stats.css" type="text/css"> </head> <body> <table border="0" bgcolor="#000000">\n''') # For a pinger to be considered active, it must appear in tables mlist2 # and pingers. This basically means, don't create empty pinger columns # in the index file. active_pingers = active_pinger_names() index.write('<tr bgcolor="#F08080"><th></th><th>Chain From</th><th>Chain To</th>\n') # Write the pinger names as a header row. for pinger in active_pingers: index.write('<th><a href="%s">%s</a></th>\n' % (pinger[1], pinger[0])) # Add the final header row titles and close the row. index.write('<th>Average</th><th>StdDev</th><th>Count</th></tr>\n') # Set a flag for rotating backgroup colours color_flag = False # Define some time related variables now = utcnow() # Make a dictionary of how many chain from's for each remailer chfr_dict = chain_from_count2() chto_dict = chain_to_count2() for name, addy in distinct_rem_names(): # We should have a key for every remailer, this is just a safeguard fr_count = keycheck(chfr_dict, name) to_count = keycheck(chto_dict, name) # First lets establish the bacground color for the row being processed color = bg_color(color_flag) color_flag = not color_flag file, chfr, chto, url = gen_filename(name, addy) # Generate the HTML for Table Row headers index.write('<tr bgcolor="%s"><th class="tableleft">\n' % (color,)) # Generate the HTML for the Remailer Name column index.write('<a href="%s" title="%s">' % (url, addy)) index.write('%s</a></th>\n' % (name,)) # Generate the HTML for the Chain From column index.write('<td align="center">') index.write('<a href="chfr.%s" title="Broken Chains from %s">' % (url, addy)) index.write('%s</a></td>\n' % (fr_count,)) # Generate the HTML for the Chain To column index.write('<td align="center">') index.write('<a href="chto.%s" title="Broken Chains to %s">' % (url, addy)) index.write('%s</a></td>\n' % (to_count,)) uptimes = remailer_index_pings(name, addy) # Now we need a loop to parse each pinger column within the remailer # row for pinger in active_pingers: ping_name = pinger[0] title = "Remailer: %s Pinger: %s" % (name, ping_name) index.write('<td align="center" title="%s">' % title) if ping_name in uptimes: index.write('%3.1f</td>\n' % (uptimes[ping_name],)) else: index.write('</td>\n') avg,stddev,count = remailer_index_stats(name, addy) # Average and StdDev can return 'None' if remailers have no current # data. We have to catch this in order to present floats to the string # formatting line. if avg == None: avg = 0 if stddev == None: stddev = 0 # Write the average, stddev and count to the end of each index row. index.write('<td>%3.2f</td><td>%3.2f</td><td>%d</td>' % (avg, stddev, count)) index.write('</tr>\n') # Add a row to the bottom of the table showing the count of remailer known # to each pinger. We have to write a few empty cells to make things line # up because of the chain counts and totals columns. pinger_totals = remailer_index_count() index.write('<tr bgcolor="#F08080"><th class="tableleft">Count</th><td></td><td></td>\n') for pinger in active_pingers: ping_name = pinger[0] if pinger_totals.has_key(ping_name): index.write('<td title="%s">%s</td>\n' % (ping_name, pinger_totals[ping_name])) else: index.write('<td title="%s">0</td>\n' % (ping_name,)) index.write('<td></td><td></td><td></td></tr>\n</table>\n') index.write('<br>Last update: %s (UTC)<br>\n' % utcnow()) index.write('<br><a href="%s">Remailer Genealogy</a>' % config.gene_report_name) index.write('<br><a href="%s">Failing Remailers</a>' % config.failed_report_name) index.write('<br><a href="%s">Uptime Averages</a>' % config.uptime_report_name) index.write('<br><a href="%s">Keyring Stats</a>' % config.keyindex) # Write closing tags and close file index.write('</body></html>') index.close()
def genealogy(): #logger.debug("Writing Geneology HTML file %s", config.gene_report_name) filename = "%s/%s" % (config.reportdir, config.gene_report_name) genefile = open(filename, 'w') # Write standard html header section genefile.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n') genefile.write('<html>\n<head>\n') genefile.write('<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">\n') genefile.write('<meta http-equiv="Content-Style-Type" content="text/css2" />\n') genefile.write('<meta name="keywords" content="Mixmaster,Echolot,Remailer,Banana,Bananasplit">\n') genefile.write('<title>Bananasplit Website - Remailer Genealogy</title>\n') genefile.write('<link rel="StyleSheet" href="stats.css" type="text/css">\n') genefile.write('</head>\n\n<body>\n') genefile.write('<h1>Remailer Genealogy</h1>\n') genefile.write('When pingers report a remailer as below %s0%%, ' % config.deadpoint) genefile.write('it is timestamped with a failed date. ') genefile.write('If it recovers to above ') genefile.write('%s0%%, the timestamp is removed. ' % config.livepoint) down_days = config.dead_after_hours / 24 genefile.write('Should the remailer fail to recover after %s days, ' % down_days) genefile.write('it is considered dead. If it returns after this ') genefile.write('it will be considered a new remailer.<br><br>\n') genefile.write('<table border="0" bgcolor="#000000">\n') genefile.write('<tr bgcolor="#F08080">\n') genefile.write('<th>Remailer Name</th><th>Remailer Address</th>') genefile.write('<th>First Seen Date</th><th>Died On Date</th>') genefile.write('<th>Failed Date</th><th>Comments</th>\n</tr>\n') genealogies = gene_get_stats() rotate_color = 0 for genealogy in genealogies: #Set up some friendly names for fields if genealogy[0]: rem_name = genealogy[0] #else: logger.error("Genealogy entry with no remailer name") if genealogy[1]: rem_addy = genealogy[1] rem_addy_noat = genealogy[1].replace('@','.') #else: logger.error("Genealogy entry with no remailer address") if genealogy[2]: first_seen = genealogy[2].strftime("%Y-%m-%d") else: first_seen = False if genealogy[3]: last_seen = genealogy[3].strftime("%Y-%m-%d") else: last_seen = False if genealogy[4]: last_fail = genealogy[4].strftime("%Y-%m-%d") else: last_fail = False if genealogy[5]: comments = genealogy[5] else: comments = "" # Rotate background colours for rows if rotate_color: bgcolor = "#ADD8E6" else: bgcolor = "#E0FFFF" rotate_color = not rotate_color if last_seen: genefile.write('<tr bgcolor="%s">' % bgcolor) genefile.write('<th class="tableleft">%s</th>\n' % rem_name) else: geneurl = '%s.%s.txt' % (rem_name, rem_addy_noat) genefile.write('<tr bgcolor="%s"><th class="tableleft">' % bgcolor) genefile.write('<a href="%s" title="%s">' % (geneurl, rem_addy)) genefile.write('%s</a></th>\n' % rem_name) # If the remailer address exists, write a table cell for it if rem_addy: genefile.write('<td>%s</td>' % rem_addy) else: genefile.write('<td></td>') # If the remailer has a first_seen entry, write a table cell for it if first_seen: genefile.write('<td>%s</td>' % first_seen) else: genefile.write('<td></td>') # If thre remailer has a lest_seen entry, write a table cell for it if last_seen: genefile.write('<td>%s</td>' % last_seen) else: genefile.write('<td></td>') # If the remailer has a lest_fail entry, write a table cell for it if last_fail: genefile.write('<td>%s</td>' % last_fail) else: genefile.write('<td></td>') # If the remailer has a comment, write a table cell for it if comments: genefile.write('<td>%s</td>' % comments) else: genefile.write('<td></td>') genefile.write('<tr>\n') genefile.write('</table>\n') genefile.write('<br>Last update: %s (UTC)<br>\n' % utcnow()) genefile.write('<br><a href="index.html">Index</a>\n') genefile.write('<br><a href="%s">Failing Remailers</a>\n' % config.failed_report_name) genefile.write('</body></html>') genefile.close()
def main(): init_logging() # Before anything else, initialise logging. logger.info("Beginning process cycle at %s (UTC)", timefunc.utcnow()) socket.setdefaulttimeout(config.timeout) global stat_re, addy_re, chain_re stat_re = re.compile("(\w{1,12})\s+([0-9A-H?]{12}\s.*)") addy_re = re.compile('\$remailer\{"([0-9a-z]{1,12})"\}\s\=\s"\<(.*)\>\s') chain_re = re.compile("\((\S{1,12})\s(\S{1,12})\)") # Are we running in testmode? Testmode implies the script was executed # without a --live argument. testmode = live_or_test(sys.argv) # If not in testmode, fetch url's and process them if not testmode: pingers = db.pinger_names() for row in pingers: url = url_fetch(row[1]) if url: url_process(row[0], url) # Fetch pubring.mix files and write them to the DB getkeystats() else: logger.debug("Running in testmode, url's will not be retreived") # We need to do some periodic housekeeping. It's not very process # intensive so might as well do it every time we run. db.housekeeping(timefunc.hours_ago(config.dead_after_hours)) # For a pinger to be considered active, it must appear in tables mlist2 # and pingers. This basically means, don't create empty pinger columns # in the index file. active_pingers = db.active_pinger_names() # A boolean value to rotate row colours within the index rotate_color = 0 # The main loop. This creates individual remailer text files and # indexing data based on database values. for name, addy in db.distinct_rem_names(): logger.debug("Generating statsistics for remailer %s", name) # remailer_vitals is a dictionary of standard deviation and average # values for a specific remailer. remailer_vitals = gen_remailer_vitals(name, addy) # remailer_active_pings: Based on the vitals generated above, we now # extract stats lines for pingers considered active. The up_hist # part is used by the fail_recover routine. remailer_active_pings = db.remailer_active_pings(remailer_vitals) # If a remailers is perceived to be dead, timestamp it in the # genealogy table. Likewise, if it's not dead, unstamp it. fail_recover(name, addy, remailer_active_pings) # Write the remailer text file that contains pinger stats and averages logger.debug("Writing stats file for %s %s", name, addy) write_remailer_stats(name, addy, remailer_vitals) # Rotate the colour used in index generation. rotate_color = not rotate_color db.gene_find_new() index() genealogy() uptimes() chainstats() writekeystats() logger.info("Processing cycle completed at %s (UTC)", timefunc.utcnow())
# remailer_active_pings: Based on the vitals generated above, we now # extract stats lines for pingers considered active. The up_hist # part is used by the fail_recover routine. remailer_active_pings = db.remailer_active_pings(remailer_vitals) # If a remailers is perceived to be dead, timestamp it in the # genealogy table. Likewise, if it's not dead, unstamp it. fail_recover(name, addy, remailer_active_pings) # Write the remailer text file that contains pinger stats and averages logger.debug("Writing stats file for %s %s", name, addy) write_remailer_stats(name, addy, remailer_vitals) # Rotate the colour used in index generation. rotate_color = not rotate_color db.gene_find_new() index() genealogy() uptimes() chainstats() writekeystats() logger.info("Processing cycle completed at %s (UTC)", timefunc.utcnow()) # Call main function. now = timefunc.utcnow() ago = timefunc.hours_ago(config.active_age) ahead = timefunc.hours_ahead(config.active_future) if __name__ == "__main__": main()
def failed(report_name): filename = "%s/%s" % (config.reportdir, config.failed_report_name) htmlfile = open(filename, 'w') # Write standard html header section htmlfile.write("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <meta http-equiv="Content-Style-Type" content="text/css2" /> <meta name="keywords" content="Mixmaster,Remailer,Banana,Bananasplit"> <title>Bananasplit Website - Failing Remailers</title> <link rel="StyleSheet" href="stats.css" type="text/css"> </head> <body> <h1>Failing Remailers</h1> Remailer stats are normally biased away from the current day. This is because different latencies would skew results due to outstanding pings. However the current day does provide a rapid insight into remailers that are currently experiencing problems.<br> <p>The following table lists remailers that are <u>currently</u> averaging less than %d%% return on pings. Please consider that this could be due to very high latency rather than actual remailer failure.</p> <p><i>Note: Bunker is not actually failing. It's a Middleman remailer with no current stats which means it cannot random hop pings back to the pinger.</i></P> <table border="0" bgcolor="#000000"> <tr bgcolor="#F08080"> <th>Remailer Name</th><th>Ping Responses</th> </tr>""" % (config.failpoint * 10)) rotate_color = 0 for name, addy in db.distinct_rem_names(): remailer_vitals = gen_remailer_vitals(name, addy) logger.debug("Checking remailer %s %s", name, addy) addy_noat = addy.replace('@',".") full_name = "%s.%s" % (name, addy_noat) active_pings = db.remailer_active_pings(remailer_vitals) if len(active_pings) == 0: logger.info("We have no active pings for %s %s", name, addy) continue remailer_vitals['uptime'] = up_today(active_pings) if remailer_vitals['uptime'] < config.failpoint: logger.info("Remailer %s %s is failed.", name, addy) # Rotate background colours for rows if rotate_color: bgcolor = "#ADD8E6" else: bgcolor = "#E0FFFF" rotate_color = not rotate_color htmlfile.write('<tr bgcolor="%s"><th class="tableleft"><a href="%s.txt" title="%s">%s</a></th>' % (bgcolor, full_name, addy, name)) htmlfile.write('<td>%d%%</td></tr>\n' % (remailer_vitals['uptime'] * 10)) # if remailer_vitals('uptime'] > config.goodpoint: # # Stats are greater than 50%, so delete any entries for this # # remailer from the failed table. # logger.debug("%s is healthy, deleting any DB entries it might have", name) htmlfile.write('</table>\n') htmlfile.write('<br>Last update: %s (UTC)<br>\n' % utcnow()) htmlfile.write('<br><a href="index.html">Return to Index</a>') htmlfile.write('</body></html>')
elif uptime_now == '?': score = 0 else: score = int(uptime_now) total += score return int(total / len(entries)) def init_logging(): """Initialise logging. This should be the first thing done so that all further operations are logged.""" loglevels = {'debug': logging.DEBUG, 'info': logging.INFO, 'warn': logging.WARN, 'error': logging.ERROR} level = loglevels['info'] global logger logger = logging.getLogger('failed') hdlr = logging.FileHandler("/home/crooks/failed.log") formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') hdlr.setFormatter(formatter) logger.addHandler(hdlr) logger.setLevel(level) # ----- Main Routine ----- init_logging() logger.info("Processing started at %s", utcnow()) # Look for remailers in mlist2 that aren't in contacts. If there are any, # insert them into contacts. failed('failed') logger.info("Processing finished at %s", utcnow())