]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
adc744d39c116510969d7e1bd7c3c8938d24ad76
[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             for p in self.versions:
132
133                 pad = time_length - len(give_time(p[1], True))
134
135                 print('-'*90 + "\n" +
136                       green_start + self.name + (p[0]).ljust(version_length) +
137                       color_stop + "  >>>  " + (give_time(p[1])) + " "*pad +
138                       "  >>>  " + give_date(p[2]))
139
140         print('-'*90 + "\n" + "Package " + green_start +
141               self.name + color_stop + " emerged " +
142               str(len(self.versions)) + " times.")
143
144         print
145
146
147     def print_pretended_times(self):
148         """This is used the print all the pretended times"""
149
150         if QUIET == False:
151             print("\t" + green_start + self.name + '-' + self.version + color_stop),
152
153         if len(self.versions) > 1:
154             aver_time = self.average_time()
155
156             if QUIET == False:
157                 print("\n\taverage time: " + give_time(aver_time))
158
159             return aver_time
160
161         else:
162             if QUIET == False:
163                 print("\n\t no previous emerges")
164
165             return 0
166
167
168     def print_min_max_ave(self):
169         maxi = self.max_time()
170         mini = self.min_time()
171         average = self.average_time()
172         total = self.total_time()
173
174         print("Max time:\t" + give_time(maxi) +
175               "\nMin time:\t" + give_time(mini) +
176               "\nAverage time:\t" + give_time(average) +
177               "\nIn total spent:\t" + give_time(total) +
178               "emerging " + green_start + self.name + color_stop)
179
180
181
182 def give_time(time, nocolor=False):
183     """Converts time in seconds to human readable form"""
184     global green_start, color_stop
185
186     if green_start == "":
187         nocolor = False
188
189     if nocolor == True:
190         green_start = ""
191         color_stop = ""
192
193     if type(time) == str:
194         return(green_start + "unknown" + color_stop)
195
196
197     days = time/(3600.0*24.0)
198     hours = (days - int(days))*24.0
199     minutes = (hours - int(hours))*60.0
200     seconds = (minutes - int(minutes))*60.0
201
202     days = int(days)
203     hours = int(hours)
204     minutes = int(minutes)
205     seconds = int(round(seconds))
206
207     pdays = str()
208     phours = str()
209     pminutes = str()
210     pseconds = str()
211
212     if days > 0:
213         pdays = (green_start + str(days) +
214                  color_stop + " day ")
215         if days != 1:
216             pdays = (green_start + str(days) +
217                          color_stop + " days ")
218
219     if hours > 0:
220         phours = (green_start + str(hours) +
221                   color_stop + " hour ")
222         if hours != 1:
223             phours = (green_start + str(hours) +
224                           color_stop + " hours ")
225
226     if minutes > 0:
227         pminutes = (green_start + str(minutes) +
228                     color_stop + " minute ")
229         if minutes != 1:
230             pminutes = (green_start + str(minutes) +
231                         color_stop + " minutes ")
232
233     if seconds > 0:
234         pseconds = (green_start + str(seconds) +
235                     color_stop + " second ")
236         if seconds != 1:
237             pseconds = (green_start + str(seconds) +
238                         color_stop + " seconds ")
239
240     if nocolor == True:
241         green_start = "\033[32m"
242         color_stop = "\033[m"
243
244     return (pdays + phours + pminutes + pseconds)
245
246
247
248 def give_date(emerge_date):
249     """Returns a date string from a standard POSIX time"""
250     date = datetime.datetime.fromtimestamp(emerge_date)
251
252     date = "{:%d.%m.%Y %H:%M:%S}".format(date)
253
254     return date
255
256
257
258 def open_log():
259     """Attempt to open the LOGFILE."""
260
261     try:
262         f = open(LOGFILE, 'r')
263     except IOError as detail:
264         print detail
265         sys.exit(1)
266
267     return f
268
269
270
271 def search_log_for_package(package_class):
272     """Searchs emerge log for given package and adds all found
273     versions with their emerge times to the class"""
274
275     log = open_log()
276
277     for line in log:
278         if ((">>>" in line) and ("emerge" in line)):
279             if package_class.name in line:
280
281                 version = line.partition(package_class.name)[2].partition(' ')[0]
282                 digit = version.strip('-')[0].isdigit()
283
284                 if digit:
285                     time_string = line.partition(">>>")
286                     start_time = float(time_string[0].strip().strip(':'))
287
288         elif ((":::" in line) and ("completed emerge" in line)):
289
290             if package_class.name in line:
291                 if digit:
292                     time_string = line.partition(":::")
293                     stop_time = float(time_string[0].strip().strip(':'))
294
295                     emerge_time = stop_time - start_time
296
297                     package_class.add_version(version, emerge_time, start_time)
298
299
300
301 def get_package(name):
302     """Take the user-input package name and search for it
303     in PORTDIR. """
304
305     dirlist = os.listdir(PORTDIR)
306     possible_package = []
307
308
309     # If the given name is in the format xxx/zzz
310     # assume that xxx is the package group
311     if '/' in name:
312         group = name.partition('/')[0]
313         pkg = name.partition('/')[2]
314         directory = PORTDIR + group
315
316         if group in dirlist:
317             dirs = os.listdir(directory)
318             if pkg in dirs:
319                 possible_package.append(name)
320
321
322     # Go through the directory listing searching for anything
323     # that matches the given name
324     for i in dirlist:
325         directory = PORTDIR + i
326         if os.path.isdir(directory):
327             dirs = os.listdir(directory)
328             if name in dirs:
329                 possible_package.append(i + '/' + name)
330
331
332     if len(possible_package) > 1:
333         print("Multiple packages found for '" + name + "'.")
334         print("Possible packages: ")
335         for value in possible_package:
336             print("\t" + value)
337
338
339     elif len(possible_package) == 1:
340         package = possible_package[0]
341         return package
342
343
344     else:
345         print("No package '" + name + "' found")
346
347
348     sys.exit(1)
349
350
351
352 def list_pretended():
353     """List all the pretended packages given by emerge -p
354     output. Create a class out of each of those packages and add them
355     to the list."""
356
357     log = open_log()
358
359     for line in sys.stdin:
360         if "[ebuild" in line:
361             full_name = line.partition("] ")[2].partition(' ')[0]
362
363             version = full_name.partition('/')[2].partition('-')[2]
364             while not version[0].isdigit():
365                 version = version.partition('-')[2]
366             package_name = full_name[:-len(version)-1]
367
368             PACKAGES.append(package(package_name, version))
369
370
371
372 def list_emerge_processes():
373     """Look for the ebuild process with ps. If the process is found parse
374     the command for the package. With this package search the LOGFILE for
375     the emerge startup time."""
376
377     f = open_log()
378
379     now = datetime.datetime.today()
380
381     for i in os.popen("ps ax"):
382         if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
383             pack = i.partition('[')[2].partition(']')[0]
384
385             version = pack.partition('/')[2].partition('-')[2]
386
387             while not version[0].isdigit():
388                 version = version.partition('-')[2]
389
390             package_name = pack[:-len(version)-1]
391
392             PACKAGES.append(package(package_name, version))
393
394
395     if len(PACKAGES) == 0:
396         print "No current emerge process found."
397
398         return 1
399
400
401     for line in f:
402         if ((">>>" in line) and ("emerge" in line)):
403             for p in PACKAGES:
404                 difference = 0
405
406                 if (p.name + '-' + p.version in line):
407
408                     time = float(line.partition(' ')[0].strip(":"))
409
410                     timestamp = datetime.datetime.fromtimestamp(time)
411                     difference = (now - timestamp).total_seconds()
412
413                     if ((difference < p.emerge_time) or
414                         (p.emerge_time == "infinity")):
415
416                         p.emerge_time = difference
417
418     return 0
419
420
421
422 def main(status, user_package=None):
423     """Main function. Hanlde all the different modes of operation."""
424
425     if status == "package":
426         user_package = get_package(user_package)
427
428         pack = package(user_package)
429
430         search_log_for_package(pack)
431
432         if len(pack.versions) != 0:
433             pack.print_versions()
434             pack.print_min_max_ave()
435
436         else:
437             print("Package " + green_start + pack.name +
438                   color_stop + " has never been emerged.")
439
440
441     elif status == "current":
442         if list_emerge_processes():
443             return
444
445         print "Currently emerging:"
446
447         for p in PACKAGES:
448             search_log_for_package(p)
449             p.print_current_emerge()
450
451
452     elif status == "pretended":
453         list_pretended()
454
455         print "This is how long these packages would take to emerge"
456
457         total_pretended_time = 0
458
459         for p in PACKAGES:
460             search_log_for_package(p)
461
462             total_pretended_time += p.print_pretended_times()
463
464             print
465
466         print("Total emerge time of " + green_start + str(len(PACKAGES)) +
467               color_stop + " package(s): "+ give_time(total_pretended_time))
468
469
470 def usage():
471     usage = """Usage: emerge-timer.py [package] [options]
472
473 Calculate emerge times from emerge log.
474
475 Options:
476 \t-c, --current \t Show time until currently compiling package finishes
477 \t-p, --pretended  Calculate compile time from piped 'emerge -p' output
478 \t-h, --help \t Show this helpscreen
479 \t-q, --quiet \t Be less verbose
480 \t--no-color \t Use colorless output
481 \t--simulate \t Do a simulation run"""
482
483     print usage
484
485     sys.exit(0)
486
487
488 if __name__ == "__main__":
489
490     # Set the default mode as "package"
491     mode = "package"
492     input_package = None
493     simulation = False
494
495     for arg in sys.argv[1:]:
496
497         if arg == "-p" or arg == "--pretended":
498             mode = "pretended"
499
500         if arg == "-c" or arg == "--current":
501             mode = "current"
502
503         if arg == "-h" or arg == "--help":
504             usage()
505
506         if arg == "-q" or arg == "--quiet":
507             QUIET = True
508
509         if arg == "--no-color":
510             green_start = ""
511             color_stop = ""
512
513         if arg == "--simulate":
514             simulation = True
515
516
517     if len(sys.argv) > 1:
518         input_package = sys.argv[1]
519     else:
520         usage()
521
522     if simulation == True:
523
524         print(red_start + "\n" + '*'*25 + "\n" + "THIS IS A SIMULATION RUN\n"
525               + '*'*25 + "\n")
526
527         print("Beginning 'package' mode check")
528
529         print("Checking for one emerge\n" + color_stop)
530
531         LOGFILE = "simulate/fake_emerge.log"
532         PORTDIR = "simulate/"
533
534         main("package", "first_fake_package")
535
536         print(red_start + "\nChecking for three emerges\n" + color_stop)
537
538         main("package", "second_fake_package")
539
540         print(red_start + "\n'package' mode check complete\n")
541
542         print(30*'*')
543
544         print("\nBeginning 'current' mode check")
545         print("Current emerge with no emerge process\n" + color_stop)
546
547         main("current", None)
548
549         print(red_start + "\nCurrent emerge with emerge processes\n" + color_stop)
550
551         PACKAGES.append(package("test-group/second_fake_package", "2.9-r2"))
552         PACKAGES[0].emerge_time = 60
553         PACKAGES.append(package("test-group/first_fake_package", "1.10.2-r1"))
554         PACKAGES[1].emerge_time = 120
555
556         main("current", None)
557
558         print(red_start + "\nCurrent emerge with incomplete emerge log" +
559               "(causes error in some cases)\n" + color_stop)
560
561         PACKAGES = []
562         PACKAGES.append(package("test-group/third_fake_package", "2.9-r2"))
563
564         main("current", None)
565
566         print(red_start + "\n" + '*'*20 + "\n" + "SIMULATION FINISHED\n" +
567               '*'*20 + color_stop)
568
569
570     else:
571         main(mode, input_package)