0440703030
- PE: Demote from Master does not clear previous errors - crmd: Prevent secondary DC fencing resulting from CIB updates that are lost due to elections - crmd: Log duplicate DC detection as a WARNING not ERROR - crmd: Bug lf#2632 - Correctly handle nodes that return faster than stonith - Core: Treat GNUTLS_E_UNEXPECTED_PACKET_LENGTH as normal termination of a TLS session - cib: Call gnutls_bye() and shutdown() when disconnecting from remote TLS connections - cib: Remove disconnected remote connections from mainloop - cib: Attempt a graceful sign-off for remote TLS connections - Core: Ensure there is sufficient space for EOS when building short-form option strings (prevents segfault) - Core: Fix variable expansion in pkg-config files - PE: Resolve memory leak reported by valgrind - PE: Fix memory leak for re-allocated resources reported by valgrind - PE: Improve the merging with template's operations - crmd: Allow nodes to fence themselves if they're the last one standing (lf#2584) - stonith: Add an API call for listing installed agents - stonith: Allow the fencing history to be queried - stonith: Ensure completed operations are recorded as such in the history - stonith: Support --quiet to display just the seconds since epoch at which a node was last shot - stonith: Serialize actions for a given device - stonith: Add missing entries to stonith_error2string() (missing OBS-URL: https://build.opensuse.org/package/show/network:ha-clustering:Factory/pacemaker?expand=0&rev=18
246 lines
8.6 KiB
Diff
246 lines
8.6 KiB
Diff
# HG changeset patch
|
|
# User Dejan Muhamedagic <dejan@hello-penguin.com>
|
|
# 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 <rsc> [<rsc> ...]"
|
|
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 [<number>|<file>] [nograph] [v...] [scores] [actions] [utilization]
|
|
transition showdot [<number>|<file>]"""
|
|
@@ -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, \
|