# HG changeset patch # User Dejan Muhamedagic # Date 1313081065 -7200 # Node ID 441f4448eba6eda1a2cf44d3d63a0db9f8d56a20 # Parent c3068d22de72d1ba616d43c808091bef830eb9f6 Medium: Shell: reimplement the history latest command (bnc#710958) This command is going to show logs for the latest transition. Basically, it's the same as "history transition", but it will wait for the current (if any) transition to finish. (Also, the horrible transition command arg parsing has been improved.) diff --git a/shell/modules/report.py b/shell/modules/report.py --- a/shell/modules/report.py +++ b/shell/modules/report.py @@ -467,8 +467,7 @@ class Report(Singleton): def is_last_live_recent(self): ''' Look at the last live hb_report. If it's recent enough, - return True. Return True also if self.to_dt is not empty - (not an open end report). + return True. ''' try: last_ts = os.stat(self.desc).st_mtime @@ -800,6 +799,7 @@ class Report(Singleton): if self.source != "live": self.error("refresh not supported") return False + self.last_live_update = 0 self.loc = self.manage_live_report(force) self.report_setup() self.ready = self.check_report() @@ -884,18 +884,10 @@ class Report(Singleton): print "Nodes:",' '.join(self.cibnode_l) print "Groups:",' '.join(self.cibgrp_d.keys()) print "Resources:",' '.join(self.cibrsc_l) - def latest(self): - ''' - Get the very latest cluster events, basically from the - latest transition. - Some transitions may be empty though. - ''' def events(self): ''' Show all events. ''' - if not self.prepare_source(): - return False all_re_l = self.build_re("resource",self.cibrsc_l) + \ self.build_re("node",self.cibnode_l) if not all_re_l: @@ -906,6 +898,8 @@ class Report(Singleton): ''' Search for events within the given transition. ''' + if not self.prepare_source(): + return False pe_base = os.path.basename(pe_file) r = re.search("pe-[^-]+-([0-9]+)[.]", pe_base) pe_num = r.group(1) @@ -926,6 +920,9 @@ class Report(Singleton): self.warn("strange, no timestamps found") return False # limit the log scope temporarily + common_info("logs for transition %s (%s-%s)" % + (pe_file.replace(self.loc+"/",""), \ + shorttime(start_ts), shorttime(end_ts))) self.logobj.set_log_timeframe(start_ts, end_ts) self.events() self.logobj.set_log_timeframe(self.from_dt, self.to_dt) @@ -994,6 +991,11 @@ class Report(Singleton): 'Find a PE or dot file matching part of the path.' pe_l = path.endswith(".dot") and self.dotlist() or self.pelist() return [x for x in pe_l if x.endswith(path)] + def pe2dot(self, f): + f = f.replace("bz2","dot") + if os.path.isfile(f): + return f + return None def find_file(self, f): return file_find_by_name(self.loc, f) diff --git a/shell/modules/ui.py.in b/shell/modules/ui.py.in --- a/shell/modules/ui.py.in +++ b/shell/modules/ui.py.in @@ -1796,22 +1796,15 @@ Examine Pacemaker's history: node and re return crm_report.info() def latest(self,cmd): "usage: latest" - try: - prev_level = levels.previous().myname() - except: - prev_level = '' - if prev_level != "cibconfig": - common_err("%s is available only when invoked from configure" % cmd) - return False - ts = cib_factory.last_commit_at() - if not ts: - common_err("no last commit time found") - return False if not wait4dc("transition", not options.batch): return False - self._set_source("live", ts) + self._set_source("live") crm_report.refresh_source() - return crm_report.events() + f = self._get_pe_byidx(-1) + if not f: + common_err("no transitions found") + return False + crm_report.show_transition_log(f) def resource(self,cmd,*args): "usage: resource [ ...]" return crm_report.resource(*args) @@ -1853,6 +1846,30 @@ Examine Pacemaker's history: node and re else: s = '\n'.join(l) page_string(s) + def _get_pe_byname(self, s): + l = crm_report.find_pe_files(s) + if len(l) == 0: + common_err("%s: path not found" % s) + return None + elif len(l) > 1: + common_err("%s: path ambiguous" % s) + return None + return l[0] + def _get_pe_byidx(self, idx): + l = crm_report.pelist() + if len(l) < abs(idx): + common_err("pe input file not found") + return None + return l[idx] + def _get_pe_bynum(self, n): + l = crm_report.pelist([n]) + if len(l) == 0: + common_err("%s: PE file %d not found" % n) + return None + elif len(l) > 1: + common_err("%s: PE file %d ambiguous" % n) + return None + return l[0] def transition(self,cmd,*args): """usage: transition [|] [nograph] [v...] [scores] [actions] [utilization] transition showdot [|]""" @@ -1864,48 +1881,35 @@ Examine Pacemaker's history: node and re return False subcmd = "showdot" argl.remove(subcmd) - f = None - startarg = 1 - if argl and re.search('pe-', argl[0]): - l = crm_report.find_pe_files(argl[0]) - if len(l) == 0: - common_err("%s: path not found" % argl[0]) - return False - elif len(l) > 1: - common_err("%s: path ambiguous" % argl[0]) - return False - f = l[0] + if argl: + if re.search('pe-', argl[0]): + f = self._get_pe_byname(argl[0]) + argl.pop(0) + elif is_int(argl[0]): + n = int(argl[0]) + if n <= 0: + f = self._get_pe_byidx(n-1) + else: + f = self._get_pe_bynum(n) + argl.pop(0) + else: + f = self._get_pe_byidx(-1) else: - try: n = convert2ints(argl[0]) - except: n = None - if n is None: - idx = -1 - startarg = 0 # peinput number missing - elif n <= 0: - idx = n - 1 - n = [] # to get all peinputs - else: - idx = 0 - if subcmd == "showdot": - l = crm_report.dotlist(n) - else: - l = crm_report.pelist(n) - if len(l) < abs(idx): - if subcmd == "show": - common_err("pe input file not found") - else: - common_err("dot file not found") - return False - f = l[idx] + f = self._get_pe_byidx(-1) if not f: return False rc = True if subcmd == "show": self.pe_file = f # self.pe_file needed by self.ptest + common_info("running ptest with %s" % f) rc = ptestlike(self.ptest,'vv',"%s %s" % \ - (cmd, subcmd), *argl[startarg:]) + (cmd, subcmd), *argl) else: - show_dot_graph(f.replace("bz2","dot")) + f = crm_report.pe2dot(f) + if not f: + common_err("dot file not found in the report") + return False + show_dot_graph(f) if rc: crm_report.show_transition_log(f) return rc diff --git a/shell/modules/utils.py b/shell/modules/utils.py --- a/shell/modules/utils.py +++ b/shell/modules/utils.py @@ -449,6 +449,9 @@ def check_range(a): return False return (int(a[0]) <= int(a[1])) +def shorttime(ts): + return time.strftime("%X",time.localtime(ts)) + def sort_by_mtime(l): 'Sort a (small) list of files by time mod.' l2 = [(os.stat(x).st_mtime, x) for x in l] @@ -489,6 +492,13 @@ def convert2ints(l): else: # it's a string then return int(l) except: return None +def is_int(s): + 'Check if the string can be converted to an integer.' + try: + i = int(s) + return True + except: + return False def is_process(s): proc = subprocess.Popen("ps -e -o pid,command | grep -qs '%s'" % s, \