#!/usr/bin/python import sys, datetime, os PORTDIR = "/usr/portage/" LOGFILE = "/var/log/emerge.log" green_start = "\033[32m" red_start = "\033[1;31m" color_stop = "\033[m" QUIET = False PACKAGES = [] def GREEN(string): return green_start + string + color_stop def RED(string): return red_start + string + color_stop class package: def __init__(self, name, version=0): self.name = name self.version = version self.versions = [] self.emerge_time = "infinity" self.plotData = [] def add_version(self, version, emerge_time, emerge_date): """Add version to the class version list""" self.versions.append((version, emerge_time, emerge_date)) def seek_versions(self, whatToSeek, Input): if whatToSeek == "date": for i in self.versions: if i[1] == Input: return i[2] if whatToSeek == "version": for i in self.versions: if i[1] == Input: return i[0] def average_time(self): """Return average time from class version list""" total_time = 0 for i in self.versions: total_time += i[1] average_time = total_time/len(self.versions) return average_time def total_time(self): """Return total time from class version list""" total_time = 0 for i in self.versions: total_time += i[1] return total_time def min_max_time(self, setting): """Return maximum or minimum time from class version list""" emerge_times = [] for i in self.versions: emerge_times.append(i[1]) emerge_times.sort() if setting == "max": return emerge_times[-1] elif setting == "min": return emerge_times[0] def print_current_emerge(self): """Function used to print all the current emerge stuff""" print("\t" + GREEN(self.name + '-' + self.version) + "\n\t current time: " + give_time(self.emerge_time)) if len(self.versions) == 1: print("\t last time: "), print(give_time(self.average_time()), end='') elif len(self.versions) > 1: print("\t average time: ", end='') print(give_time(self.average_time()), end='') else: print("\t average time: " + GREEN("unknown\n")), return print("\n\t " + '-'*45, end='') print("\n\t time to finish: ", end='') if type(self.emerge_time) != str: finish_time = self.average_time() - self.emerge_time if finish_time > 0: print(give_time(finish_time), "\n") else: print(GREEN("any time now")) else: print(GREEN("unknown")) print def print_versions(self): """This prints the emerge times for different versions in the 'package' operating mode of the script""" if QUIET == False: version_length = 0 time_length = 0 for p in self.versions: if len(p[0]) > version_length: version_length = len(p[0]) if len(give_time(p[1], True)) > time_length: time_length = len(give_time(p[1], True)) # Create the data for plotting in the format (emerge time, emerge date) self.plotData.append((p[1], p[2])) dots = (version_length + time_length + len(self.name) + len(give_date(self.versions[0][2])) + 14) for p in self.versions: pad = time_length - len(give_time(p[1], True)) name = self.name p_time = give_time(p[1]) p_date = give_date(p[2]) print('-' * dots + "\n" + GREEN(name + (p[0]).ljust(version_length)) + " >>> " + p_time + " "*pad + " >>> " + p_date) print("\n" + "Package " + GREEN(self.name) + " emerged"), if len(self.versions) > 1: print(str(len(self.versions)) + " times.\n") elif len(self.versions) == 1: print("once.\n") def print_pretended_times(self): """This is used the print all the pretended times""" if QUIET == False: print("\t" + GREEN(self.name + '-' + self.version)), if len(self.versions) > 1: aver_time = self.average_time() if QUIET == False: print("\n\taverage time: " + give_time(aver_time)) return aver_time else: if QUIET == False: print("\n\t no previous emerges") return 0 def print_min_max_ave(self): if len(self.versions) == 1: return maxi = self.min_max_time("max") mini = self.min_max_time("min") average = self.average_time() total = self.total_time() print("-"*20 + "\nMaximum emerge time:\n" + give_time(maxi) + "for version " + GREEN(self.seek_versions("version", maxi).lstrip("-")) + " on " + give_date(self.seek_versions("date", maxi))) print("-"*20 + "\nMinimum emerge time:\n" + give_time(mini) + "for version " + GREEN(self.seek_versions("version", mini).lstrip("-")) + " on " + give_date(self.seek_versions("date", mini))) print("-"*20 + "\nAverage emerge time:\n" + give_time(average)) print("-"*20 + "\nIn total spent:\n" + give_time(total)) def plotToScreen(self): dates = [] times = [] for i in self.plotData: dates.append(datetime.date.fromtimestamp(i[1])) times.append(i[0]) fig = plt.figure() plt.plot_date(dates, times, xdate=True, ydate=False) plt.ylabel("Emerge time [s]") plt.suptitle(self.name) plt.grid() fig.autofmt_xdate() plt.show() def give_time(time, nocolor=False): """Converts time in seconds to human readable form""" global green_start, color_stop if green_start == "": nocolor = False if nocolor == True: green_start = "" color_stop = "" if type(time) == str: return(GREEN("unknown")) days = time/(3600.0*24.0) hours = (days - int(days))*24.0 minutes = (hours - int(hours))*60.0 seconds = (minutes - int(minutes))*60.0 days = int(days) hours = int(hours) minutes = int(minutes) seconds = int(round(seconds)) pdays = str() phours = str() pminutes = str() pseconds = str() if days > 0: pdays = (GREEN(str(days)) + " day ") if days != 1: pdays = (GREEN(str(days)) + " days ") if hours > 0: phours = (GREEN(str(hours)) + " hour ") if hours != 1: phours = (GREEN(str(hours)) + " hours ") if minutes > 0: pminutes = (GREEN(str(minutes)) + " minute ") if minutes != 1: pminutes = (GREEN(str(minutes)) + " minutes ") if seconds > 0: pseconds = (GREEN(str(seconds)) + " second ") if seconds != 1: pseconds = (GREEN(str(seconds)) + " seconds ") if nocolor == True: green_start = "\033[32m" color_stop = "\033[m" return (pdays + phours + pminutes + pseconds) def give_date(emerge_date): """Returns a date string from a standard POSIX time""" date = datetime.datetime.fromtimestamp(emerge_date) date = "{:%d.%m.%Y %H:%M:%S}".format(date) return date def open_log(): """Attempt to open the LOGFILE.""" try: f = open(LOGFILE, 'r') except IOError as detail: print(detail) sys.exit(1) return f def search_log_for_package(package_class): """Searchs emerge log for given package and adds all found versions with their emerge times to the class""" log = open_log() for line in log: if ((">>>" in line) and ("emerge" in line)): if package_class.name in line: version = line.partition(package_class.name)[2].partition(' ')[0] digit = version.strip('-')[0].isdigit() if digit: time_string = line.partition(">>>") start_time = float(time_string[0].strip().strip(':')) elif ((":::" in line) and ("completed emerge" in line)): if package_class.name in line: if digit: time_string = line.partition(":::") stop_time = float(time_string[0].strip().strip(':')) emerge_time = stop_time - start_time package_class.add_version(version, emerge_time, start_time) def search_log_for_all_packages(): """Goes through the emerge.log and lists all packages in there""" log = open_log() all_packages = [] total_emerge_time = 0 emerge_number = 0 for line in log: if ((">>>" in line) and ("emerge" in line)): pack = line.partition(')')[2].strip().partition(' ')[0] start_time = float(line.partition(':')[0]) all_packages.append((pack, start_time)) elif ((":::" in line) and ("completed emerge" in line)): for p in all_packages: if p[0] in line: print("\t" + give_date(p[1]) + " >>> " + GREEN(p[0])) emerge_number += 1 all_packages.pop(all_packages.index(p)) print("\n" + GREEN(str(emerge_number)) + " emerges in total found.") def get_package(name): """Take the user-input package name and search for it in PORTDIR. """ dirlist = os.listdir(PORTDIR) possible_package = [] # If the given name is in the format xxx/zzz # assume that xxx is the package group if '/' in name: group = name.partition('/')[0] pkg = name.partition('/')[2] directory = PORTDIR + group if group in dirlist: dirs = os.listdir(directory) if pkg in dirs: possible_package.append(name) # Go through the directory listing searching for anything # that matches the given name for i in dirlist: directory = PORTDIR + i if os.path.isdir(directory): dirs = os.listdir(directory) if name in dirs: possible_package.append(i + '/' + name) if len(possible_package) > 1: print("Multiple packages found for '" + name + "'.") print("Possible packages: ") for value in possible_package: print("\t" + value) elif len(possible_package) == 1: package = possible_package[0] return package else: print("No package '" + name + "' found") sys.exit(1) def list_pretended(): """List all the pretended packages given by emerge -p output. Create a class out of each of those packages and add them to the list.""" log = open_log() for line in sys.stdin: if "[ebuild" in line: full_name = line.partition("] ")[2].partition(' ')[0] version = full_name.partition('/')[2].partition('-')[2] while not version[0].isdigit(): version = version.partition('-')[2] package_name = full_name[:-len(version)-1] PACKAGES.append(package(package_name, version)) def list_emerge_processes(): """Look for the ebuild process with ps. If the process is found parse the command for the package. With this package search the LOGFILE for the emerge startup time.""" f = open_log() now = datetime.datetime.today() for i in os.popen("ps ax"): if (("ebuild.sh" in i) and ("/bin/bash" not in i)): pack = i.partition('[')[2].partition(']')[0] version = pack.partition('/')[2].partition('-')[2] while not version[0].isdigit(): version = version.partition('-')[2] package_name = pack[:-len(version)-1] PACKAGES.append(package(package_name, version)) if len(PACKAGES) == 0: print("No current emerge process found.") return 1 for line in f: if ((">>>" in line) and ("emerge" in line)): for p in PACKAGES: difference = 0 if (p.name + '-' + p.version in line): time = float(line.partition(' ')[0].strip(":")) timestamp = datetime.datetime.fromtimestamp(time) difference = (now - timestamp).total_seconds() if ((p.emerge_time == "infinity") or (difference < p.emerge_time)): p.emerge_time = difference return 0 def search_syncs(): f = open_log() print("These emerge syncs found") print("\tDate Server") print("\t------------------------------") a = 0 for line in f: if "=== Sync completed with" in line: time = float(line.partition(' ')[0].strip(":")) server = line.rpartition(' ')[2] print("\t" + GREEN(give_date(time)) + " === " + server), a += 1 print("\n" + GREEN(str(a)) + " emerge syncs found. ") def main(status, user_package=None): try: _main(status, user_package) except IOError: sys.exit() def _main(status, user_package=None): """Main function. Handle all the different modes of operation.""" if status == "package": for p in user_package: pack = get_package(p) pack = package(pack) search_log_for_package(pack) if len(pack.versions) != 0: pack.print_versions() pack.print_min_max_ave() if matplotWorking: pack.plotToScreen() else: print("Package " + GREEN(pack.name) + " has never been emerged.") elif status == "sync": search_syncs() return elif status == "list": search_log_for_all_packages() return elif status == "current": if list_emerge_processes(): return print("Currently emerging:") for p in PACKAGES: search_log_for_package(p) p.print_current_emerge() elif status == "pretended": list_pretended() print("This is how long these packages would take to emerge") total_pretended_time = 0 for p in PACKAGES: search_log_for_package(p) total_pretended_time += p.print_pretended_times() print print("Total emerge time of " + GREEN(str(len(PACKAGES))) + " package(s): "+ give_time(total_pretended_time)) def usage(): usage = """Usage: emerge-timer.py [package] [options] Calculate emerge times from emerge log. Options: \t-c, --current \t Show time until currently compiling package finishes \t-p, --pretended Calculate compile time from piped 'emerge -p' output \t-l, --list \t List all emerged packages \t-s, --sync \t Show emerge sync history \t-h, --help \t Show this helpscreen \t-q, --quiet \t Be less verbose \t--no-color \t Use colorless output \t--plot \t\t Plot emerge times into a 2D scatter plot \t\t\t (needs matlibplot installed for this to work)""" print(usage) sys.exit(0) if __name__ == "__main__": # Set the default mode as "package" mode = "package" input_packages = None simulation = False matplotWorking = False for arg in sys.argv[1:]: if arg == "-p" or arg == "--pretended": mode = "pretended" if arg == "-c" or arg == "--current": mode = "current" if arg == "-h" or arg == "--help": usage() if arg == "-l" or arg == "--list": mode = "list" if arg == "-s" or arg == "--sync": mode = "sync" if arg == "-q" or arg == "--quiet": QUIET = True sys.argv.pop(sys.argv.index(arg)) if arg == "--plot": try: import matplotlib.pyplot as plt matplotWorking = True except ImportError: print(RED("Cannot initialize matplotlib!")) print(RED("Check that you have properly installed matplotlib.\n")) matplotWorking = False sys.argv.pop(sys.argv.index(arg)) if arg == "--no-color": green_start = "" color_stop = "" sys.argv.pop(sys.argv.index(arg)) if arg == "--simulate": simulation = True if len(sys.argv) > 1: input_packages = sys.argv[1:] else: usage() main(mode, input_packages)