#!/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 = [] class package: def __init__(self, name, version=0): self.name = name self.version = version self.versions = [] self.emerge_time = "infinity" 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 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 max_time(self): """Return maximum time from class version list""" self.versions.sort() return self.versions[len(self.versions)-1][1] def min_time(self): """Return minimum time from class version list""" self.versions.sort() return self.versions[0][1] def print_current_emerge(self): """Function used to print all the current emerge stuff""" print("\t" + green_start + self.name + '-' + self.version + color_stop + "\n\t current time: " + give_time(self.emerge_time)) if len(self.versions) == 1: print("\t last time: "), print(give_time(self.average_time())), elif len(self.versions) > 1: print("\t average time:"), print(give_time(self.average_time())), else: print("\t average time: " + green_start + "unknown\n" + color_stop), return print("\n\t " + '-'*45), print("\n\t time to finish:"), if type(self.emerge_time) != str: finish_time = self.average_time() - self.emerge_time if finish_time > 0: print(give_time(finish_time)) else: print(green_start + "any time now" + color_stop) else: print(green_start + "unknown" + color_stop) 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)) for p in self.versions: pad = time_length - len(give_time(p[1], True)) print('-'*90 + "\n" + green_start + self.name + (p[0]).ljust(version_length) + color_stop + " >>> " + (give_time(p[1])) + " "*pad + " >>> " + give_date(p[2])) print('-'*90 + "\n" + "Package " + green_start + self.name + color_stop + " emerged " + str(len(self.versions)) + " times.") print def print_pretended_times(self): """This is used the print all the pretended times""" if QUIET == False: print("\t" + green_start + self.name + '-' + self.version + color_stop), 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): maxi = self.max_time() mini = self.min_time() average = self.average_time() total = self.total_time() print("Max time: \t" + give_time(maxi) + "\nMin time: \t" + give_time(mini) + "\nAverage time: \t" + give_time(average) + "\nIn total spent " + give_time(total) + " emerging " + green_start + self.name + color_stop) 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_start + "unknown" + color_stop) days = time/(3600*24) hours = (days - int(days))*24 minutes = (hours - int(hours))*60 seconds = (minutes - int(minutes))*60 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_start + str(days) + color_stop + " day ") if days != 1: pdays = (green_start + str(days) + color_stop + " days ") if hours > 0: phours = (green_start + str(hours) + color_stop + " hour ") if hours != 1: phours = (green_start + str(hours) + color_stop + " hours ") if minutes > 0: pminutes = (green_start + str(minutes) + color_stop + " minute ") if minutes != 1: pminutes = (green_start + str(minutes) + color_stop + " minutes ") pseconds = (green_start + str(seconds) + color_stop + " second ") if seconds != 1: pseconds = (green_start + str(seconds) + color_stop + " 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 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 ((difference < p.emerge_time) or (p.emerge_time == "infinity")): p.emerge_time = difference return 0 def main(status, user_package=None): """Main function. Hanlde all the different modes of operation.""" if status == "package": user_package = get_package(user_package) pack = package(user_package) search_log_for_package(pack) if len(pack.versions) != 0: pack.print_versions() pack.print_min_max_ave() else: print("Package " + green_start + pack.name + color_stop + " has never been emerged.") 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_start + str(len(PACKAGES)) + color_stop + " 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-h, --help \t Show this helpscreen \t-q, --quiet \t Be less verbose \t--no-color \t Use colorless output \t--simulate \t Do a simulation run""" print usage sys.exit(0) if __name__ == "__main__": # Set the default mode as "package" mode = "package" input_package = None simulation = 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 == "-q" or arg == "--quiet": QUIET = True if arg == "--no-color": green_start = "" color_stop = "" if arg == "--simulate": simulation = True if len(sys.argv) > 1: input_package = sys.argv[1] else: usage() if simulation == True: print(red_start + "\n" + '*'*25 + "\n" + "THIS IS A SIMULATION RUN\n" + '*'*25 + "\n") print("Beginning 'package' mode check") print("Checking for one emerge\n" + color_stop) LOGFILE = "simulate/fake_emerge.log" PORTDIR = "simulate/" main("package", "first_fake_package") print(red_start + "\nChecking for three emerges\n" + color_stop) main("package", "second_fake_package") print(red_start + "\n'package' mode check complete\n") print(30*'*') print("\nBeginning 'current' mode check") print("Current emerge with no emerge process\n" + color_stop) main("current", None) print(red_start + "\nCurrent emerge with emerge processes\n" + color_stop) PACKAGES.append(package("test-group/second_fake_package", "2.9-r2")) PACKAGES.append(package("test-group/first_fake_package", "1.10.2-r1")) main("current", None) print(red_start + "\nCurrent emerge with incomplete emerge log" + "(causes error in some cases)\n" + color_stop) PACKAGES = [] PACKAGES.append(package("test-group/third_fake_package", "2.9-r2")) main("current", None) print(red_start + "\n" + '*'*20 + "\n" + "SIMULATION FINISHED\n" + '*'*20 + color_stop) else: main(mode, input_package)