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