]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
6255468a8e7051d6ed2705a5a3134cd5d5e93c65
[emerge-timer] / emerge-timer.py
1 #!/usr/bin/python
2
3
4 import sys, datetime, os
5
6
7 PORTDIR = "/usr/portage/"
8 LOGFILE = "/var/log/emerge.log"
9
10
11 green_start = "\033[32m"
12 red_start = "\033[1;31m"
13 color_stop = "\033[m"
14
15 QUIET = False
16
17 PACKAGES = []
18
19
20
21 class package:
22     def __init__(self, name, version=0):
23         self.name = name
24         self.version = version
25         self.versions = []
26         self.emerge_time = "infinity"
27
28
29     def add_version(self, version, emerge_time, emerge_date):
30         """Add version to the class version list"""
31         self.versions.append((version, emerge_time, emerge_date))
32
33
34     def average_time(self):
35         """Return average time from class version list"""
36         total_time = 0
37         for i in self.versions:
38             total_time += i[1]
39
40         average_time = total_time/len(self.versions)
41
42         return average_time
43
44
45     def total_time(self):
46         """Return total time from class version list"""
47         total_time = 0
48         for i in self.versions:
49             total_time += i[1]
50
51         return total_time
52
53
54     def max_time(self):
55         """Return maximum time from class version list"""
56
57         emerge_times = []
58         for i in self.versions:
59             emerge_times.append(i[1])
60
61         emerge_times.sort()
62
63         return emerge_times[-1]
64
65
66     def min_time(self):
67         """Return minimum time from class version list"""
68
69         emerge_times = []
70         for i in self.versions:
71             emerge_times.append(i[1])
72
73         emerge_times.sort()
74
75         return emerge_times[0]
76
77
78
79     def print_current_emerge(self):
80         """Function used to print all the current emerge stuff"""
81
82         print("\t" + green_start + self.name + '-' + self.version +
83               color_stop + "\n\t current time: " + give_time(self.emerge_time))
84
85
86         if len(self.versions) == 1:
87             print("\t last time:   "),
88             print(give_time(self.average_time())),
89
90         elif len(self.versions) > 1:
91             print("\t average time:"),
92             print(give_time(self.average_time())),
93
94         else:
95             print("\t average time: " + green_start + "unknown\n" + color_stop),
96             return
97
98         print("\n\t " + '-'*45),
99
100         print("\n\t time to finish:"),
101
102         if type(self.emerge_time) != str:
103
104             finish_time = self.average_time() - self.emerge_time
105
106             if finish_time > 0:
107                 print(give_time(finish_time))
108             else:
109                 print(green_start + "any time now" + color_stop)
110         else:
111             print(green_start + "unknown" + color_stop)
112         print
113
114
115     def print_versions(self):
116         """This prints the emerge times for different versions in the
117         'package' operating mode of the script"""
118
119         if QUIET == False:
120
121             version_length = 0
122             time_length = 0
123
124             for p in self.versions:
125                 if len(p[0]) > version_length:
126                     version_length = len(p[0])
127
128                 if len(give_time(p[1], True)) > time_length:
129                     time_length = len(give_time(p[1], True))
130
131             dots =  (version_length + time_length + len(self.name)
132                      + len(give_date(self.versions[0][2])) + 14)
133
134             for p in self.versions:
135
136                 pad = time_length - len(give_time(p[1], True))
137
138                 name = self.name
139                 p_time = give_time(p[1])
140                 p_date = give_date(p[2])
141
142                 print('-' * dots + "\n" +
143                       green_start + name + (p[0]).ljust(version_length) +
144                       color_stop + "  >>>  " + p_time + " "*pad +
145                       "  >>>  " + p_date)
146
147         print("\n" + "Package " + green_start +
148               self.name + color_stop + " emerged " +
149               str(len(self.versions)) + " times.\n")
150
151
152
153     def print_pretended_times(self):
154         """This is used the print all the pretended times"""
155
156         if QUIET == False:
157             print("\t" + green_start + self.name + '-' + self.version + color_stop),
158
159         if len(self.versions) > 1:
160             aver_time = self.average_time()
161
162             if QUIET == False:
163                 print("\n\taverage time: " + give_time(aver_time))
164
165             return aver_time
166
167         else:
168             if QUIET == False:
169                 print("\n\t no previous emerges")
170
171             return 0
172
173
174     def print_min_max_ave(self):
175         maxi = self.max_time()
176         mini = self.min_time()
177         average = self.average_time()
178         total = self.total_time()
179
180         print("Max time:\t" + give_time(maxi) +
181               "\nMin time:\t" + give_time(mini) +
182               "\nAverage time:\t" + give_time(average) +
183               "\nIn total spent:\t" + give_time(total) +
184               "emerging " + green_start + self.name + color_stop)
185
186
187
188 def give_time(time, nocolor=False):
189     """Converts time in seconds to human readable form"""
190     global green_start, color_stop
191
192     if green_start == "":
193         nocolor = False
194
195     if nocolor == True:
196         green_start = ""
197         color_stop = ""
198
199     if type(time) == str:
200         return(green_start + "unknown" + color_stop)
201
202
203     days = time/(3600.0*24.0)
204     hours = (days - int(days))*24.0
205     minutes = (hours - int(hours))*60.0
206     seconds = (minutes - int(minutes))*60.0
207
208     days = int(days)
209     hours = int(hours)
210     minutes = int(minutes)
211     seconds = int(round(seconds))
212
213     pdays = str()
214     phours = str()
215     pminutes = str()
216     pseconds = str()
217
218     if days > 0:
219         pdays = (green_start + str(days) +
220                  color_stop + " day ")
221         if days != 1:
222             pdays = (green_start + str(days) +
223                          color_stop + " days ")
224
225     if hours > 0:
226         phours = (green_start + str(hours) +
227                   color_stop + " hour ")
228         if hours != 1:
229             phours = (green_start + str(hours) +
230                           color_stop + " hours ")
231
232     if minutes > 0:
233         pminutes = (green_start + str(minutes) +
234                     color_stop + " minute ")
235         if minutes != 1:
236             pminutes = (green_start + str(minutes) +
237                         color_stop + " minutes ")
238
239     if seconds > 0:
240         pseconds = (green_start + str(seconds) +
241                     color_stop + " second ")
242         if seconds != 1:
243             pseconds = (green_start + str(seconds) +
244                         color_stop + " seconds ")
245
246     if nocolor == True:
247         green_start = "\033[32m"
248         color_stop = "\033[m"
249
250     return (pdays + phours + pminutes + pseconds)
251
252
253
254 def give_date(emerge_date):
255     """Returns a date string from a standard POSIX time"""
256     date = datetime.datetime.fromtimestamp(emerge_date)
257
258     date = "{:%d.%m.%Y %H:%M:%S}".format(date)
259
260     return date
261
262
263
264 def open_log():
265     """Attempt to open the LOGFILE."""
266
267     try:
268         f = open(LOGFILE, 'r')
269     except IOError as detail:
270         print detail
271         sys.exit(1)
272
273     return f
274
275
276
277 def search_log_for_package(package_class):
278     """Searchs emerge log for given package and adds all found
279     versions with their emerge times to the class"""
280
281     log = open_log()
282
283     for line in log:
284         if ((">>>" in line) and ("emerge" in line)):
285             if package_class.name in line:
286
287                 version = line.partition(package_class.name)[2].partition(' ')[0]
288                 digit = version.strip('-')[0].isdigit()
289
290                 if digit:
291                     time_string = line.partition(">>>")
292                     start_time = float(time_string[0].strip().strip(':'))
293
294         elif ((":::" in line) and ("completed emerge" in line)):
295
296             if package_class.name in line:
297                 if digit:
298                     time_string = line.partition(":::")
299                     stop_time = float(time_string[0].strip().strip(':'))
300
301                     emerge_time = stop_time - start_time
302
303                     package_class.add_version(version, emerge_time, start_time)
304
305 def search_log_for_all_packages():
306     """Goes through the emerge.log and lists all packages in there"""
307
308     log = open_log()
309
310     all_packages = []
311
312     for line in log:
313         if ((">>>" in line) and ("emerge" in line)):
314             pack = line.partition(')')[2].strip().partition(' ')[0]
315             start_time = float(line.partition(':')[0])
316
317             all_packages.append((pack, start_time))
318
319         elif ((":::" in line) and ("completed emerge" in line)):
320             for p in all_packages:
321                 if p[0] in line:
322                     stop_time = float(line.partition(':')[0])
323
324                     print("\t" + give_date(p[1]) + " >>> " + green_start + p[0] + color_stop)
325
326                     all_packages.pop(all_packages.index(p))
327
328
329 def get_package(name):
330     """Take the user-input package name and search for it
331     in PORTDIR. """
332
333     dirlist = os.listdir(PORTDIR)
334     possible_package = []
335
336
337     # If the given name is in the format xxx/zzz
338     # assume that xxx is the package group
339     if '/' in name:
340         group = name.partition('/')[0]
341         pkg = name.partition('/')[2]
342         directory = PORTDIR + group
343
344         if group in dirlist:
345             dirs = os.listdir(directory)
346             if pkg in dirs:
347                 possible_package.append(name)
348
349
350     # Go through the directory listing searching for anything
351     # that matches the given name
352     for i in dirlist:
353         directory = PORTDIR + i
354         if os.path.isdir(directory):
355             dirs = os.listdir(directory)
356             if name in dirs:
357                 possible_package.append(i + '/' + name)
358
359
360     if len(possible_package) > 1:
361         print("Multiple packages found for '" + name + "'.")
362         print("Possible packages: ")
363         for value in possible_package:
364             print("\t" + value)
365
366
367     elif len(possible_package) == 1:
368         package = possible_package[0]
369         return package
370
371
372     else:
373         print("No package '" + name + "' found")
374
375
376     sys.exit(1)
377
378
379
380 def list_pretended():
381     """List all the pretended packages given by emerge -p
382     output. Create a class out of each of those packages and add them
383     to the list."""
384
385     log = open_log()
386
387     for line in sys.stdin:
388         if "[ebuild" in line:
389             full_name = line.partition("] ")[2].partition(' ')[0]
390
391             version = full_name.partition('/')[2].partition('-')[2]
392             while not version[0].isdigit():
393                 version = version.partition('-')[2]
394             package_name = full_name[:-len(version)-1]
395
396             PACKAGES.append(package(package_name, version))
397
398
399
400 def list_emerge_processes():
401     """Look for the ebuild process with ps. If the process is found parse
402     the command for the package. With this package search the LOGFILE for
403     the emerge startup time."""
404
405     f = open_log()
406
407     now = datetime.datetime.today()
408
409     for i in os.popen("ps ax"):
410         if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
411             pack = i.partition('[')[2].partition(']')[0]
412
413             version = pack.partition('/')[2].partition('-')[2]
414
415             while not version[0].isdigit():
416                 version = version.partition('-')[2]
417
418             package_name = pack[:-len(version)-1]
419
420             PACKAGES.append(package(package_name, version))
421
422
423     if len(PACKAGES) == 0:
424         print "No current emerge process found."
425
426         return 1
427
428
429     for line in f:
430         if ((">>>" in line) and ("emerge" in line)):
431             for p in PACKAGES:
432                 difference = 0
433
434                 if (p.name + '-' + p.version in line):
435
436                     time = float(line.partition(' ')[0].strip(":"))
437
438                     timestamp = datetime.datetime.fromtimestamp(time)
439                     difference = (now - timestamp).total_seconds()
440
441                     if ((difference < p.emerge_time) or
442                         (p.emerge_time == "infinity")):
443
444                         p.emerge_time = difference
445
446     return 0
447
448
449
450 def main(status, user_package=None):
451     """Main function. Hanlde all the different modes of operation."""
452
453     if status == "package":
454         for p in user_package:
455             pack = get_package(p)
456
457             pack = package(pack)
458
459             search_log_for_package(pack)
460
461             if len(pack.versions) != 0:
462                 pack.print_versions()
463                 pack.print_min_max_ave()
464
465             else:
466                 print("Package " + green_start + pack.name +
467                       color_stop + " has never been emerged.")
468
469             print
470
471
472     elif status == "current":
473         if list_emerge_processes():
474             return
475
476         print "Currently emerging:"
477
478         for p in PACKAGES:
479             search_log_for_package(p)
480             p.print_current_emerge()
481
482
483     elif status == "pretended":
484         list_pretended()
485
486         print "This is how long these packages would take to emerge"
487
488         total_pretended_time = 0
489
490         for p in PACKAGES:
491             search_log_for_package(p)
492
493             total_pretended_time += p.print_pretended_times()
494
495             print
496
497         print("Total emerge time of " + green_start + str(len(PACKAGES)) +
498               color_stop + " package(s): "+ give_time(total_pretended_time))
499
500
501 def usage():
502     usage = """Usage: emerge-timer.py [package] [options]
503
504 Calculate emerge times from emerge log.
505
506 Options:
507 \t-c, --current \t Show time until currently compiling package finishes
508 \t-p, --pretended  Calculate compile time from piped 'emerge -p' output
509 \t-l, --list \t List all emerged packages
510 \t-h, --help \t Show this helpscreen
511 \t-q, --quiet \t Be less verbose
512 \t--no-color \t Use colorless output
513 \t--simulate \t Do a simulation run"""
514
515     print usage
516
517     sys.exit(0)
518
519
520 if __name__ == "__main__":
521
522     # Set the default mode as "package"
523     mode = "package"
524     input_packages = None
525     simulation = False
526
527     for arg in sys.argv[1:]:
528
529         if arg == "-p" or arg == "--pretended":
530             mode = "pretended"
531
532         if arg == "-c" or arg == "--current":
533             mode = "current"
534
535         if arg == "-h" or arg == "--help":
536             usage()
537
538         if arg == "-l" or arg == "--list":
539             search_log_for_all_packages()
540             sys.exit(1)
541
542         if arg == "-q" or arg == "--quiet":
543             QUIET = True
544
545             sys.argv.pop(sys.argv.index(arg))
546
547         if arg == "--no-color":
548             green_start = ""
549             color_stop = ""
550
551             sys.argv.pop(sys.argv.index(arg))
552
553         if arg == "--simulate":
554             simulation = True
555
556
557     if len(sys.argv) > 1:
558         input_packages = sys.argv[1:]
559     else:
560         usage()
561
562     if simulation == True:
563
564         print(red_start + "\n" + '*'*25 + "\n" + "THIS IS A SIMULATION RUN\n"
565               + '*'*25 + "\n")
566
567         print("Beginning 'package' mode check")
568
569         print("Checking for one emerge\n" + color_stop)
570
571         LOGFILE = "simulate/fake_emerge.log"
572         PORTDIR = "simulate/"
573
574         main("package", "first_fake_package")
575
576         print(red_start + "\nChecking for three emerges\n" + color_stop)
577
578         main("package", "second_fake_package")
579
580         print(red_start + "\n'package' mode check complete\n")
581
582         print(30*'*')
583
584         print("\nBeginning 'current' mode check")
585         print("Current emerge with no emerge process\n" + color_stop)
586
587         main("current", None)
588
589         print(red_start + "\nCurrent emerge with emerge processes\n" + color_stop)
590
591         PACKAGES.append(package("test-group/second_fake_package", "2.9-r2"))
592         PACKAGES[0].emerge_time = 60
593         PACKAGES.append(package("test-group/first_fake_package", "1.10.2-r1"))
594         PACKAGES[1].emerge_time = 120
595
596         main("current", None)
597
598         print(red_start + "\nCurrent emerge with incomplete emerge log" +
599               "(causes error in some cases)\n" + color_stop)
600
601         PACKAGES = []
602         PACKAGES.append(package("test-group/third_fake_package", "2.9-r2"))
603
604         main("current", None)
605
606         print(red_start + "\n" + '*'*20 + "\n" + "SIMULATION FINISHED\n" +
607               '*'*20 + color_stop)
608
609
610     else:
611         main(mode, input_packages)