]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
4f69d849ee616691f9f32f47a881c78b39f77b0f
[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
306
307 def get_package(name):
308     """Take the user-input package name and search for it
309     in PORTDIR. """
310
311     dirlist = os.listdir(PORTDIR)
312     possible_package = []
313
314
315     # If the given name is in the format xxx/zzz
316     # assume that xxx is the package group
317     if '/' in name:
318         group = name.partition('/')[0]
319         pkg = name.partition('/')[2]
320         directory = PORTDIR + group
321
322         if group in dirlist:
323             dirs = os.listdir(directory)
324             if pkg in dirs:
325                 possible_package.append(name)
326
327
328     # Go through the directory listing searching for anything
329     # that matches the given name
330     for i in dirlist:
331         directory = PORTDIR + i
332         if os.path.isdir(directory):
333             dirs = os.listdir(directory)
334             if name in dirs:
335                 possible_package.append(i + '/' + name)
336
337
338     if len(possible_package) > 1:
339         print("Multiple packages found for '" + name + "'.")
340         print("Possible packages: ")
341         for value in possible_package:
342             print("\t" + value)
343
344
345     elif len(possible_package) == 1:
346         package = possible_package[0]
347         return package
348
349
350     else:
351         print("No package '" + name + "' found")
352
353
354     sys.exit(1)
355
356
357
358 def list_pretended():
359     """List all the pretended packages given by emerge -p
360     output. Create a class out of each of those packages and add them
361     to the list."""
362
363     log = open_log()
364
365     for line in sys.stdin:
366         if "[ebuild" in line:
367             full_name = line.partition("] ")[2].partition(' ')[0]
368
369             version = full_name.partition('/')[2].partition('-')[2]
370             while not version[0].isdigit():
371                 version = version.partition('-')[2]
372             package_name = full_name[:-len(version)-1]
373
374             PACKAGES.append(package(package_name, version))
375
376
377
378 def list_emerge_processes():
379     """Look for the ebuild process with ps. If the process is found parse
380     the command for the package. With this package search the LOGFILE for
381     the emerge startup time."""
382
383     f = open_log()
384
385     now = datetime.datetime.today()
386
387     for i in os.popen("ps ax"):
388         if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
389             pack = i.partition('[')[2].partition(']')[0]
390
391             version = pack.partition('/')[2].partition('-')[2]
392
393             while not version[0].isdigit():
394                 version = version.partition('-')[2]
395
396             package_name = pack[:-len(version)-1]
397
398             PACKAGES.append(package(package_name, version))
399
400
401     if len(PACKAGES) == 0:
402         print "No current emerge process found."
403
404         return 1
405
406
407     for line in f:
408         if ((">>>" in line) and ("emerge" in line)):
409             for p in PACKAGES:
410                 difference = 0
411
412                 if (p.name + '-' + p.version in line):
413
414                     time = float(line.partition(' ')[0].strip(":"))
415
416                     timestamp = datetime.datetime.fromtimestamp(time)
417                     difference = (now - timestamp).total_seconds()
418
419                     if ((difference < p.emerge_time) or
420                         (p.emerge_time == "infinity")):
421
422                         p.emerge_time = difference
423
424     return 0
425
426
427
428 def main(status, user_package=None):
429     """Main function. Hanlde all the different modes of operation."""
430
431     if status == "package":
432         user_package = get_package(user_package)
433
434         pack = package(user_package)
435
436         search_log_for_package(pack)
437
438         if len(pack.versions) != 0:
439             pack.print_versions()
440             pack.print_min_max_ave()
441
442         else:
443             print("Package " + green_start + pack.name +
444                   color_stop + " has never been emerged.")
445
446
447     elif status == "current":
448         if list_emerge_processes():
449             return
450
451         print "Currently emerging:"
452
453         for p in PACKAGES:
454             search_log_for_package(p)
455             p.print_current_emerge()
456
457
458     elif status == "pretended":
459         list_pretended()
460
461         print "This is how long these packages would take to emerge"
462
463         total_pretended_time = 0
464
465         for p in PACKAGES:
466             search_log_for_package(p)
467
468             total_pretended_time += p.print_pretended_times()
469
470             print
471
472         print("Total emerge time of " + green_start + str(len(PACKAGES)) +
473               color_stop + " package(s): "+ give_time(total_pretended_time))
474
475
476 def usage():
477     usage = """Usage: emerge-timer.py [package] [options]
478
479 Calculate emerge times from emerge log.
480
481 Options:
482 \t-c, --current \t Show time until currently compiling package finishes
483 \t-p, --pretended  Calculate compile time from piped 'emerge -p' output
484 \t-h, --help \t Show this helpscreen
485 \t-q, --quiet \t Be less verbose
486 \t--no-color \t Use colorless output
487 \t--simulate \t Do a simulation run"""
488
489     print usage
490
491     sys.exit(0)
492
493
494 if __name__ == "__main__":
495
496     # Set the default mode as "package"
497     mode = "package"
498     input_package = None
499     simulation = False
500
501     for arg in sys.argv[1:]:
502
503         if arg == "-p" or arg == "--pretended":
504             mode = "pretended"
505
506         if arg == "-c" or arg == "--current":
507             mode = "current"
508
509         if arg == "-h" or arg == "--help":
510             usage()
511
512         if arg == "-q" or arg == "--quiet":
513             QUIET = True
514
515         if arg == "--no-color":
516             green_start = ""
517             color_stop = ""
518
519         if arg == "--simulate":
520             simulation = True
521
522
523     if len(sys.argv) > 1:
524         input_package = sys.argv[1]
525     else:
526         usage()
527
528     if simulation == True:
529
530         print(red_start + "\n" + '*'*25 + "\n" + "THIS IS A SIMULATION RUN\n"
531               + '*'*25 + "\n")
532
533         print("Beginning 'package' mode check")
534
535         print("Checking for one emerge\n" + color_stop)
536
537         LOGFILE = "simulate/fake_emerge.log"
538         PORTDIR = "simulate/"
539
540         main("package", "first_fake_package")
541
542         print(red_start + "\nChecking for three emerges\n" + color_stop)
543
544         main("package", "second_fake_package")
545
546         print(red_start + "\n'package' mode check complete\n")
547
548         print(30*'*')
549
550         print("\nBeginning 'current' mode check")
551         print("Current emerge with no emerge process\n" + color_stop)
552
553         main("current", None)
554
555         print(red_start + "\nCurrent emerge with emerge processes\n" + color_stop)
556
557         PACKAGES.append(package("test-group/second_fake_package", "2.9-r2"))
558         PACKAGES[0].emerge_time = 60
559         PACKAGES.append(package("test-group/first_fake_package", "1.10.2-r1"))
560         PACKAGES[1].emerge_time = 120
561
562         main("current", None)
563
564         print(red_start + "\nCurrent emerge with incomplete emerge log" +
565               "(causes error in some cases)\n" + color_stop)
566
567         PACKAGES = []
568         PACKAGES.append(package("test-group/third_fake_package", "2.9-r2"))
569
570         main("current", None)
571
572         print(red_start + "\n" + '*'*20 + "\n" + "SIMULATION FINISHED\n" +
573               '*'*20 + color_stop)
574
575
576     else:
577         main(mode, input_package)