Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2012-12-19 10:49:45
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh", Maintainer is "dmuhameda...@suse.com"

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2012-10-18 
15:54:35.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.crmsh.new/crmsh.changes 2012-12-19 
10:50:40.000000000 +0100
@@ -1,0 +2,16 @@
+Tue Dec 11 17:22:00 UTC 2012 - dmuhameda...@suse.com
+
+- cibconfig: improve id management on element update
+- cibconfig: don't bail out if filter fails
+- ui: improve quotes insertion
+- history: optimize source refreshing
+- history: fix setting up the timeframe alias for limit
+- history: fix unpacking reports specified without directory
+- history: add log subcommand to transition
+- ra: add support for nagios plugins
+- ra: don't print duplicate RAs in the list command (bnc#793585)
+- utils: make sure that there's at least one column (savannah#37658)
+- build: pcmk.pc renamed to pacemaker.pc in pacemaker v1.1.8
+- upstream cs: fe29639e39eb (v1.2.3)
+
+-------------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.PvgYRl/_old  2012-12-19 10:50:42.000000000 +0100
+++ /var/tmp/diff_new_pack.PvgYRl/_new  2012-12-19 10:50:42.000000000 +0100
@@ -45,7 +45,7 @@
 Summary:        Pacemaker command line interface
 License:        GPL-2.0
 Group:          %{pkg_group}
-Version:        1.2.1
+Version:        1.2.3
 Release:        0
 Url:            http://savannah.nongnu.org/projects/crmsh
 Source0:        crmsh.tar.bz2

++++++ crmsh.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/.hg_archival.txt new/crmsh/.hg_archival.txt
--- old/crmsh/.hg_archival.txt  2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/.hg_archival.txt  2012-12-11 18:29:34.000000000 +0100
@@ -1,5 +1,5 @@
 repo: 13c3bd69e935090cd25213c474cafc3f01b5910b
-node: b6bb311c7bd36c05206f3a1cd98193ccf46d8da0
+node: 355467d3974841ba2f0400ffe6cb251d88185786
 branch: default
-latesttag: crmsh-1.2.0
-latesttagdistance: 25
+latesttag: crmsh-1.2.3
+latesttagdistance: 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/.hgtags new/crmsh/.hgtags
--- old/crmsh/.hgtags   2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/.hgtags   2012-12-11 18:29:34.000000000 +0100
@@ -18,3 +18,6 @@
 f731b72e3c82226b22c133a27462a019055f39e7 SLE11-HAE-GMC
 fd6bd4648675c2be80c7bed2d8f8d3e5a5074c59 Pacemaker-1.0.5
 45886ac2c124ce49fc004dbc080974dc76dea605 crmsh-1.2.0
+b6bb311c7bd36c05206f3a1cd98193ccf46d8da0 crmsh-1.2.1
+4ca0d2abf345d207a5fbdbc444d70d8b18b0cc6a crmsh-1.2.2
+fe29639e39eb95b2d806f0567e6e0b6cf1070e96 crmsh-1.2.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/ChangeLog new/crmsh/ChangeLog
--- old/crmsh/ChangeLog 2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/ChangeLog 2012-12-11 18:29:34.000000000 +0100
@@ -1,3 +1,21 @@
+* Tue Dec 11 2012 Dejan Muhamedagic <de...@suse.de> and many others
+- stable release 1.2.3
+- ra: don't print duplicate RAs in the list command (bnc#793585)
+- history: optimize source refreshing
+
+* Thu Dec  6 2012 Dejan Muhamedagic <de...@suse.de> and many others
+- stable release 1.2.2
+- cibconfig: don't bail out if filter fails
+- cibconfig: improve id management on element update
+- ra: add support for nagios plugins
+- utils: make sure that there's at least one column (savannah#37658)
+- ui: improve quotes insertion (possible regression)
+- history: adjust log patterns for pacemaker v1.1.8
+- history: fix setting up the timeframe alias for limit
+- history: fix unpacking reports specified without directory
+- history: add log subcommand to transition
+- build: pcmk.pc renamed to pacemaker.pc in pacemaker v1.1.8
+
 * Mon Oct 15 2012 Dejan Muhamedagic <de...@suse.de> and many others
 - stable release 1.2.1
 - cibconfig: show error message on id in use
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/configure.ac new/crmsh/configure.ac
--- old/crmsh/configure.ac      2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/configure.ac      2012-12-11 18:29:34.000000000 +0100
@@ -21,7 +21,7 @@
 dnl     checks for library functions
 dnl     checks for system services
 
-AC_INIT(crmsh, 1.2.1, linux-ha@linux...@lists.linux-ha.org)
+AC_INIT(crmsh, 1.2.3, linux...@lists.linux-ha.org)
 
 AC_ARG_WITH(version,
     [  --with-version=version   Override package version (if you're a packager 
needing to pretend) ],
@@ -197,6 +197,9 @@
 
 CRM_DAEMON_DIR=`$PKGCONFIG pcmk --variable=daemondir`
 if test "X$CRM_DAEMON_DIR" = X; then
+       CRM_DAEMON_DIR=`$PKGCONFIG pacemaker --variable=daemondir`
+fi
+if test "X$CRM_DAEMON_DIR" = X; then
        CRM_DAEMON_DIR=`extract_header_define $GLUE_HEADER GLUE_DAEMON_DIR`
 fi
 AC_DEFINE_UNQUOTED(CRM_DAEMON_DIR,"$CRM_DAEMON_DIR", Location for Pacemaker 
daemons)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/crmsh.spec new/crmsh/crmsh.spec
--- old/crmsh/crmsh.spec        2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/crmsh.spec        2012-12-11 18:29:34.000000000 +0100
@@ -17,7 +17,7 @@
 
 Name:          crmsh
 Summary:       Pacemaker command line interface
-Version:       1.2.1
+Version:       1.2.3
 Release:       %{crmsh_release}%{?dist}
 License:       GPLv2+
 Url:           http://www.clusterlabs.org
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/doc/crm.8.txt new/crmsh/doc/crm.8.txt
--- old/crmsh/doc/crm.8.txt     2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/doc/crm.8.txt     2012-12-11 18:29:34.000000000 +0100
@@ -1406,6 +1406,31 @@
 
 See <<topics_Checks,Configuration semantic checks>> for more details.
 
+[[cmdhelp_options_add-quotes,add quotes around parameters containing spaces]]
+==== `add-quotes`
+
+The shell (as in `/bin/sh`) parser strips quotes from the command
+line. This may sometimes make it really difficult to type values
+which contain white space. One typical example is the configure
+filter command. The crm shell will supply extra quotes around
+arguments which contain white space. The default is `yes`.
+
+.Note on quotes use
+****************************
+Adding quotes around arguments automatically has been introduced
+with version 1.2.2 and it is technically a regression. Being a
+regression is the only reason the `add-quotes` option exists. If
+you have custom shell scripts which would break, just set the
+`add-quotes` option to `no`.
+
+For instance, with adding quotes enabled, it is possible to do
+the following:
+...............
+    # crm configure primitive d1 ocf:heartbeat:Dummy meta description="some 
description here"
+    # crm configure filter 'sed "s/hostlist=./&node-c /"' fencing
+...............
+****************************
+
 [[cmdhelp_options_show,show current user preference]]
 ==== `show`
 
@@ -2088,13 +2113,11 @@
 It is possible to filter the XML representation of objects, but
 probably not as useful as the configuration language. The
 presentation is somewhat different from what would be displayed
-by the `show` command---each element is shown on a single, i.e.
-there are no backslashes and no other embelishments.
+by the `show` command---each element is shown on a single line,
+i.e. there are no backslashes and no other embelishments.
 
 Don't forget to put quotes around the filter if it contains
-spaces. Further, if you use this in a single-shot mode (from the
-`sh` command line), protect all quotes with a backslash.
-Otherwise, they won't reach the `crm` shell parser.
+spaces.
 
 Usage:
 ...............
@@ -2104,7 +2127,7 @@
 Examples:
 ...............
         filter "sed '/^primitive/s/target-role=[^ ]*//'"
-        # crm configure filter \"sed \'/^primitive/s/target-role=[^ ]*//\'\"
+        # crm configure filter "sed '/^primitive/s/target-role=[^ ]*//'"
 ...............
 
 [[cmdhelp_configure_delete,delete CIB objects]]
@@ -3008,6 +3031,9 @@
 report was produced. In optimal case this output should not
 differ from the one produced by the locally installed `pengine`.
 
+The `log` subcommand shows the full log for the duration of the
+transition.
+
 If the PE input file number is not provided, it defaults to the
 last one, i.e. the last transition. If the number is negative,
 then the corresponding transition relative to the last one is
@@ -3025,6 +3051,7 @@
 ...............
         transition [<number>|<index>|<file>] [nograph] [v...] [scores] 
[actions] [utilization]
         transition showdot [<number>|<index>|<file>]
+        transition log [<number>|<index>|<file>]
 ...............
 Examples:
 ...............
@@ -3034,6 +3061,7 @@
         transition pe-error-3.bz2
         transition node-a/pengine/pe-input-2.bz2
         transition showdot 444
+        transition log
 ...............
 
 [[cmdhelp_history_session,manage history sessions]]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/Makefile.am 
new/crmsh/modules/Makefile.am
--- old/crmsh/modules/Makefile.am       2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/Makefile.am       2012-12-11 18:29:34.000000000 +0100
@@ -36,6 +36,7 @@
                        report.py               \
                        rsctest.py              \
                        log_patterns.py         \
+                       log_patterns_118.py             \
                        crm_pssh.py     \
                        singletonmixin.py       \
                        template.py     \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/cibconfig.py 
new/crmsh/modules/cibconfig.py
--- old/crmsh/modules/cibconfig.py      2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/cibconfig.py      2012-12-11 18:29:34.000000000 +0100
@@ -2318,8 +2318,8 @@
         the new properties should be merged with the old.
         Otherwise, users may be surprised.
         '''
-        id_store.remove_xml(obj.node)
         if len(cli_list) >= 2 and cli_list[1][0] == "raw":
+            id_store.remove_xml(obj.node)
             doc = xml.dom.minidom.parseString(cli_list[1][1])
             id_store.store_xml(doc.childNodes[0])
             return self.update_element(obj,doc.childNodes[0])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/log_patterns_118.py 
new/crmsh/modules/log_patterns_118.py
--- old/crmsh/modules/log_patterns_118.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/crmsh/modules/log_patterns_118.py       2012-12-11 18:29:34.000000000 
+0100
@@ -0,0 +1,74 @@
+# Copyright (C) 2012 Dejan Muhamedagic <dmuhameda...@suse.de>
+#
+# log pattern specification (for pacemaker v1.1.8)
+#
+# patterns are grouped one of several classes:
+#  - resource: pertaining to a resource
+#  - node: pertaining to a node
+#  - quorum: quorum changes
+#  - events: other interesting events (core dumps, etc)
+# 
+# paterns are grouped based on a detail level
+# detail level 0 is the lowest, i.e. should match the least
+# number of relevant messages
+
+# NB:
+# %% stands for whatever user input we get, for instance a
+# resource name or node name or just some regular expression
+# in optimal case, it should be surrounded by literals
+#
+# [Note that resources may contain clone numbers!]
+
+log_patterns = {
+       "resource": (
+               ( # detail 0
+                       
"crmd.*Initiating.*%%_(start|stop|promote|demote|migrate)_",
+                       "lrmd.*operation_finished: %%_",
+                       "crmd.*process_lrm_event: LRM operation 
%%_(start|stop|promote|demote|migrate)_.*confirmed=true",
+                       "crmd.*process_lrm_event: LRM operation %%_.*Timed Out",
+                       "[(]%%[)][[]",
+               ),
+               ( # detail 1
+                       "crmd.*Initiating%%_(monitor_0|notify)",
+               ),
+       ),
+       "node": (
+               ( # detail 0
+                       " %% .*Corosync.Cluster.Engine",
+                       " %% .*Executive.Service.RELEASE",
+                       " %% .*crm_shutdown:.Requesting.shutdown",
+                       " %% .*pcmk_shutdown:.Shutdown.complete",
+                       " %% .*Configuration.validated..Starting.heartbeat",
+                       "pengine.*Scheduling Node %% for STONITH",
+                       "pengine.*pe_fence_node: Node %% will be fenced",
+                       "crmd.* tengine_stonith_callback: .* for %% failed",
+                       "stonith-ng.*log_operation:.*host '%%'",
+                       "te_fence_node: Exec.*on %% ",
+                       "pe_fence_node: Node %% will be fenced",
+                       "stonith-ng.*remote_op_timeout.*on %% for.*timed out",
+                       "stonith-ng.*can_fence_host_with_device:.*can not fence 
%%:",
+                       "stonithd.*Succeeded.*node %%:",
+                       "pcmk_peer_update.*(lost|memb): %% ",
+                       "crmd.*ccm_event.*(NEW|LOST):.* %% ",
+               ),
+               ( # detail 1
+               ),
+       ),
+       "quorum": (
+               ( # detail 0
+                       "crmd.*crm_update_quorum:.Updating.quorum.status",
+                       "crmd.*ais.disp.*quorum.(lost|ac?quir)",
+               ),
+               ( # detail 1
+               ),
+       ),
+       "events": (
+               ( # detail 0
+                       "CRIT:",
+                       "ERROR:",
+               ),
+               ( # detail 1
+                       "WARN:",
+               ),
+       ),
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/main.py new/crmsh/modules/main.py
--- old/crmsh/modules/main.py   2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/main.py   2012-12-11 18:29:34.000000000 +0100
@@ -290,23 +290,15 @@
     vars.tmpl_conf_dir = xdg_file(vars.tmpl_conf_dir, \
         vars.xdg_map["crmconf"], "d", "config")
 
-def compatibility():
-    from distutils.version import LooseVersion
-    try:
-        vars.pcmk_version = get_stdout("crmd version").split()[2]
-        if LooseVersion(vars.pcmk_version) >= LooseVersion("1.1.8"):
-            vars.node_type_opt = True
-            vars.attr_defaults["node"] = {"type": "normal"}
-            vars.cib_no_section_rc = 6
-            vars.transition_patt[0] = "pengine.* process_pe_message: 
Calculated Transition ([0-9]+): (.*/pe-[^-]+-(%%)[.]bz2)" # transition start
-    except Exception,msg:
-        vars.pcmk_version = "1.1.1"
-        common_warn(msg)
-        common_warn("could not get the pacemaker version, bad installation?")
+def compatibility_setup():
+    if is_pcmk_118():
+        vars.node_type_opt = True
+        vars.attr_defaults["node"] = {"type": "normal"}
+        vars.cib_no_section_rc = 6
 
 def do_work():
     global user_args
-    compatibility()
+    compatibility_setup()
     # this special case is silly, but we have to keep it to
     # preserve the backward compatibility
     if len(user_args) == 1 and user_args[0].startswith("conf"):
@@ -325,9 +317,10 @@
         # '...\'...\''  do work
         l = []
         for s in user_args:
-            if ' ' in s and not ('"' in s or "'" in s):
-                q = '"'
-                s = "%s%s%s" % (q,s,q)
+            if user_prefs.get_add_quotes() and ' ' in s:
+                q = '"' in s and "'" or '"'
+                if not q in s:
+                    s = "%s%s%s" % (q,s,q)
             l.append(s)
         if parse_line(levels,shlex.split(' '.join(l))):
             # if the user entered a level, then just continue
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/ra.py new/crmsh/modules/ra.py
--- old/crmsh/modules/ra.py     2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/ra.py     2012-12-11 18:29:34.000000000 +0100
@@ -80,6 +80,9 @@
                 l = stdout2list("/usr/sbin/%s -o metadata" % ra_type)
             else:
                 l = stdout2list("stonith -m -t %s" % ra_type)
+        elif ra_class == "nagios":
+            l = stdout2list("%s/check_%s --metadata" % \
+                (vars.nagios_dir, ra_type))
         return l
     def providers(self, ra_type,ra_class = "ocf"):
         'List of providers for a class:type.'
@@ -92,7 +95,7 @@
         return l
     def classes(self):
         'List of classes.'
-        return "heartbeat lsb ocf stonith".split()
+        return "heartbeat lsb nagios ocf stonith".split()
     def types(self, ra_class = "ocf", ra_provider = ""):
         'List of types for a class.'
         l = []
@@ -104,6 +107,9 @@
         elif ra_class == "stonith":
             l = stdout2list("stonith -L")
             l.extend(os_types_list("/usr/sbin/fence_*"))
+        elif ra_class == "nagios":
+            l = os_types_list("%s/check_*" % vars.nagios_dir)
+            l = [x.replace("check_","") for x in l]
         l = list(set(l))
         l.sort()
         if ra_class == "stonith":
@@ -208,13 +214,12 @@
     id = "ra_types-%s-%s" % (ra_class,ra_provider)
     if wcache.is_cached(id):
         return wcache.retrieve(id)
-    if ra_provider:
-        list = []
-        for ra in ra_if().types(ra_class):
-            if ra_provider in ra_providers(ra,ra_class):
-                list.append(ra)
-    else:
-        list = ra_if().types(ra_class)
+    list = []
+    for ra in ra_if().types(ra_class):
+        if (not ra_provider or \
+                ra_provider in ra_providers(ra,ra_class)) \
+                and ra not in list:
+            list.append(ra)
     list.sort()
     return wcache.store(id,list)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/report.py new/crmsh/modules/report.py
--- old/crmsh/modules/report.py 2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/report.py 2012-12-11 18:29:34.000000000 +0100
@@ -31,7 +31,6 @@
 from xmlutil import *
 from utils import *
 from msg import *
-from log_patterns import log_patterns
 _NO_PSSH = False
 try:
     from crm_pssh import next_loglines, next_peinputs
@@ -370,6 +369,7 @@
     crmd: [13667]: info: run_graph: Transition 399 (Complete=5,
     Pending=1, Fired=1, Skipped=0, Incomplete=3,
     Source=...
+    Returns dict: d[Pending]=np, d[Fired]=nf, ...
     '''
     d = {}
     s = msg
@@ -379,30 +379,73 @@
             return d
         d[r.group(1)] = int(r.group(2))
         s = s[r.end():]
-def transition_actions(msg_l, te_invoke_msg, pe_file):
-    '''
-    Get the number of actions for the transition.
-    '''
-    # check if there were any actions in this transition
+
+def extract_pe_file(msg):
+    msg_a = msg.split()
+    if len(msg_a) < 8:
+        # this looks too short
+        common_warn("log message <%s> unexpected format, please report a bug" 
% msg)
+        return ""
+    return msg_a[-1]
+def extract_node(msg):
+    msg_a = msg.split()
+    if len(msg_a) < 8:
+        # this looks too short
+        common_warn("log message <%s> unexpected format, please report a bug" 
% msg)
+        return ""
+    return msg_a[3]
+
+def get_matching_run_msg(te_invoke_msg, trans_msg_l):
+    run_msg = ""
+    pe_file = extract_pe_file(te_invoke_msg)
     pe_num = get_pe_num(pe_file)
     if pe_num == "-1":
-        common_debug("no PE number for file: %s" % pe_file)
-        return -1
-    te_invoke_patt = vars.transition_patt[0].replace("%%", pe_num)
+        common_warn("%s: strange, transition number not found" % pe_file)
+        return ""
     run_patt = vars.transition_patt[1].replace("%%", pe_num)
-    r = re.search(te_invoke_patt, te_invoke_msg)
-    if not r:
-        return -1
-    trans_num = r.group(1)
-    unpack_patt = vars.transition_patt[2].replace("%%", trans_num)
-    for msg in msg_l:
-        try:
-            return int(re.search(unpack_patt, msg).group(2))
-        except:
-            if re.search(run_patt, msg):
-                act_d = run_graph_msg_actions(msg)
-                return sum(act_d.values())
-    return -1
+    for msg in trans_msg_l:
+        if re.search(run_patt, msg):
+            run_msg = msg
+            break
+    return run_msg
+
+class Transition(object):
+    '''
+    Capture transition related information.
+    '''
+    def __init__(self, te_invoke_msg, run_msg):
+        self.te_invoke_msg = te_invoke_msg
+        self.run_msg = run_msg
+        self.parse_msgs()
+    def __str__(self):
+        return "%s:%s" % (self.dc, \
+            
os.path.basename(self.pe_file).replace(".bz2","").replace("pe-","").replace("input-",""))
+    def parse_msgs(self):
+        self.pe_file = extract_pe_file(self.te_invoke_msg)
+        self.pe_num = get_pe_num(self.pe_file)
+        self.dc = extract_node(self.te_invoke_msg)
+        self.start_ts = syslog_ts(self.te_invoke_msg)
+        if self.run_msg:
+            self.end_ts = syslog_ts(self.run_msg)
+        else:
+            common_warn("end of transition %s not found in logs (transition 
not complete yet?)" % self)
+            self.end_ts = time.time()
+    def total_actions(self):
+        if self.run_msg:
+            act_d = run_graph_msg_actions(self.run_msg)
+            return sum(act_d.values())
+        else:
+            return -1
+    def transition_info(self):
+        print "Transition %s (%s -" % (self, shorttime(self.start_ts)),
+        if self.run_msg:
+            print "%s):" % shorttime(self.end_ts)
+            act_d = run_graph_msg_actions(self.run_msg)
+            total = sum(act_d.values())
+            s = ", ".join(["%d %s" % (act_d[x], x) for x in act_d if act_d[x]])
+            print "\ttotal %d actions: %s" % (total, s)
+        else:
+            print "[unfinished])"
 
 def mkarchive(dir):
     "Create an archive from a directory"
@@ -420,6 +463,7 @@
         print "Report saved in '%s'" % archive
     return True
 
+CH_SRC, CH_TIME, CH_UPD = range(1,4)
 class Report(Singleton):
     '''
     A hb_report class.
@@ -430,18 +474,22 @@
     "NORMAL", "GREEN", "CYAN", "MAGENTA", "YELLOW", "WHITE", "BLUE", "RED"
     )
     session_sub = "session"
+    outdir = os.path.join(vars.report_cache, "psshout")
+    errdir = os.path.join(vars.report_cache, "pssherr")
     def __init__(self):
+        # main source attributes
         self.source = None
-        self.loc = None
-        self.ready = False
         self.from_dt = None
         self.to_dt = None
-        self.detail = 0
+        self.log_l = []
+        self.central_log = None
+        self.setnodes = [] # optional
+        # derived
+        self.loc = None
+        self.ready = False
         self.nodecolor = {}
         self.logobj = None
         self.desc = None
-        self.log_l = []
-        self.central_log = None
         self.peinputs_l = []
         self.cibgrp_d = {}
         self.cibcln_d = {}
@@ -449,12 +497,13 @@
         self.cibnotcloned_l = []
         self.cibcloned_l = []
         self.cibnode_l = []
-        self.setnodes = []
-        self.outdir = os.path.join(vars.report_cache,"psshout")
-        self.errdir = os.path.join(vars.report_cache,"pssherr")
         self.last_live_update = 0
+        self.detail = 0
         self.log_filter_out = []
         self.log_filter_out_re = []
+        # change_origin may be CH_SRC, CH_TIME, CH_UPD
+        # depending on the change_origin, we update our attributes
+        self.change_origin = CH_SRC
     def error(self, s):
         common_err("%s: %s" % (self.source, s))
     def warn(self, s):
@@ -464,7 +513,7 @@
     def node_list(self):
         return self.cibnode_l
     def peinputs_list(self):
-        return [get_pe_num(x) for x in self.peinputs_l]
+        return [x.pe_num for x in self.peinputs_l]
     def session_subcmd_list(self):
         return ["save", "load", "pack", "delete", "list", "update"]
     def session_list(self):
@@ -488,14 +537,16 @@
         else:
             self.error("this doesn't look like a report tarball")
             return None
+        self.change_origin = CH_SRC
         if os.path.isdir(loc):
             return loc
         cwd = os.getcwd()
-        try:
-            os.chdir(parentdir)
-        except OSError,msg:
-            self.error(msg)
-            return None
+        if parentdir:
+            try:
+                os.chdir(parentdir)
+            except OSError,msg:
+                self.error(msg)
+                return None
         import tarfile
         try:
             tf = tarfile.open(bfname)
@@ -514,6 +565,11 @@
         if rc != 0:
             return None
         return loc
+    def pe_report_path(self, t_obj):
+        pe_base = os.path.basename(t_obj.pe_file)
+        return os.path.join(self.loc, t_obj.dc, "pengine", pe_base)
+    def short_pe_path(self, pe_file):
+        return pe_file.replace("%s/" % self.loc,"")
     def get_nodes(self):
         return [ os.path.basename(p)
             for p in os.listdir(self.loc)
@@ -548,7 +604,7 @@
         return True
     def _live_loc(self):
         return os.path.join(vars.report_cache,"live")
-    def is_last_live_recent(self):
+    def is_live_recent(self):
         '''
         Look at the last live hb_report. If it's recent enough,
         return True.
@@ -558,6 +614,12 @@
             return (time.time() - last_ts <= self.live_recent)
         except:
             return False
+    def is_live_very_recent(self):
+        '''
+        Look at the last live hb_report. If it's recent enough,
+        return True.
+        '''
+        return (time.time() - self.last_live_update) <= self.short_live_recent
     def find_node_log(self, node):
         p = os.path.join(self.loc, node)
         for lf in ("ha-log.txt", "messages"):
@@ -583,7 +645,7 @@
                 l.append(log)
             else:
                 self.warn("no log found for node %s" % node)
-        self.log_l = l
+        return l
     def append_newlogs(self, a):
         '''
         Append new logs fetched from nodes.
@@ -596,51 +658,41 @@
                 continue
             append_file(rptlog,fl[0])
             update_loginfo(rptlog, logfile, nextpos, fl[0])
-    def unpack_new_peinputs(self, a):
+    def unpack_new_peinputs(self, node, pe_l):
         '''
         Untar PE inputs fetched from nodes.
         '''
         if not os.path.isdir(self.outdir):
             return
-        for node,pe_l in a:
-            fl = glob.glob("%s/*%s*" % (self.outdir,node))
-            if not fl:
-                continue
-            u_dir = os.path.join(self.loc, node)
-            rc = pipe_cmd_nosudo("tar -C %s -x < %s" % (u_dir,fl[0]))
-    def find_new_peinputs(self, node_l):
+        fl = glob.glob("%s/*%s*" % (self.outdir,node))
+        if not fl:
+            return -1
+        u_dir = os.path.join(self.loc, node)
+        return pipe_cmd_nosudo("tar -C %s -x < %s" % (u_dir,fl[0]))
+    def find_new_peinputs(self, node):
         '''
         Get a list of pe inputs appearing in new logs.
         The log is put in self.outdir/node by pssh.
         '''
         if not os.path.isdir(self.outdir):
             return []
-        l = []
-        for node in node_l:
-            fl = glob.glob("%s/*%s*" % (self.outdir,node))
-            if not fl:
-                continue
-            try:
-                f = open(fl[0])
-            except IOError,msg:
-                common_err("open %s: %s"%(fl[0],msg))
-                continue
-            pe_l = self.list_transitions([x for x in f], future_pe = True)
-            if pe_l:
-                l.append([node,pe_l])
-        return l
-    def update_live(self):
+        fl = glob.glob("%s/*%s*" % (self.outdir,node))
+        if not fl:
+            return []
+        try:
+            f = open(fl[0])
+        except IOError,msg:
+            common_err("open %s: %s" % (fl[0], msg))
+            return []
+        common_debug("updating transitions from logs")
+        return self.list_transitions([x for x in f], future_pe = True)
+    def update_live_report(self):
         '''
         Update the existing live report, if it's older than
         self.short_live_recent:
         - append newer logs
         - get new PE inputs
         '''
-        if (time.time() - self.last_live_update) <= self.short_live_recent:
-            return True
-        if _NO_PSSH:
-            warn_once("pssh not installed, slow live updates ahead")
-            return False
         a = []
         common_info("fetching new logs, please wait ...")
         for rptlog in self.log_l:
@@ -649,28 +701,38 @@
             if logf:
                 a.append([node, rptlog, logf, pos])
         if not a:
-            common_info("no elligible logs found :(")
+            common_info("no elligible logs found")
             return False
         rmdir_r(self.outdir)
         rmdir_r(self.errdir)
+        self.logobj = None
+        self.last_live_update = time.time()
         rc1 = next_loglines(a, self.outdir, self.errdir)
         self.append_newlogs(a)
-        pe_l = self.find_new_peinputs([x[0] for x in a])
+        new_peinputs_l = []
+        node_pe_l = []
+        for node in [x[0] for x in a]:
+            pe_l = self.find_new_peinputs(node)
+            if pe_l:
+                node_pe_l.append([node, [x.pe_file for x in pe_l]])
+                new_peinputs_l += pe_l
         rmdir_r(self.outdir)
         rmdir_r(self.errdir)
-        rc2 = True
-        if pe_l:
-            rc2 = next_peinputs(pe_l, self.outdir, self.errdir)
-        self.unpack_new_peinputs(pe_l)
-        self.logobj = None
+        if not node_pe_l:
+            return rc1
+        rc2 = next_peinputs(node_pe_l, self.outdir, self.errdir)
+        unpack_rc = 0
+        for node,pe_l in node_pe_l:
+            unpack_rc |= self.unpack_new_peinputs(node, pe_l)
+        rc2 |= (unpack_rc == 0)
         rmdir_r(self.outdir)
         rmdir_r(self.errdir)
-        self.last_live_update = time.time()
+        self.peinputs_l += new_peinputs_l
         return (rc1 and rc2)
     def get_live_report(self):
         if not acquire_lock(vars.report_cache):
             return None
-        loc = self.new_live_hb_report()
+        loc = self.new_live_report()
         release_lock(vars.report_cache)
         return loc
     def manage_live_report(self, force = False):
@@ -684,15 +746,22 @@
             # the live report is there, but we were just invoked
             self.loc = d
             self.report_setup()
-        if not force and self.is_last_live_recent():
-            if not acquire_lock(vars.report_cache):
-                return None
-            rc = self.update_live()
-            release_lock(vars.report_cache)
-            if rc:
+        if not force and self.is_live_recent():
+            # try just to refresh the live report
+            if self.to_dt or self.is_live_very_recent():
                 return self._live_loc()
+            if not _NO_PSSH:
+                if not acquire_lock(vars.report_cache):
+                    return None
+                rc = self.update_live_report()
+                release_lock(vars.report_cache)
+                if rc:
+                    self.change_origin = CH_UPD
+                    return self._live_loc()
+            else:
+                warn_once("pssh not installed, slow live updates ahead")
         return self.get_live_report()
-    def new_live_hb_report(self):
+    def new_live_report(self):
         '''
         Run hb_report to get logs now.
         '''
@@ -720,7 +789,10 @@
         return self.unpack_report(tarball)
     def set_source(self,src):
         'Set our source.'
-        self.source = src
+        if self.source != src:
+            self.change_origin = CH_SRC
+            self.source = src
+            self.ready = False
     def set_period(self,from_dt,to_dt):
         '''
         Set from/to_dt.
@@ -732,6 +804,7 @@
         self.from_dt = from_dt
         self.to_dt = to_dt
         if need_ref:
+            self.change_origin = CH_UPD
             self.refresh_source(force = True)
         if self.logobj:
             self.logobj.set_log_timeframe(self.from_dt, self.to_dt)
@@ -747,19 +820,21 @@
         not part of the cluster).
         '''
         self.setnodes = args
+    def get_cib_loc(self):
+        nl = self.get_nodes()
+        if not nl:
+            return ""
+        return os.path.join(self.loc, nl[0], "cib.xml")
     def read_cib(self):
         '''
         Get some information from the report's CIB (node list,
         resource list, groups). If "live" and not central log,
         then use cibadmin.
         '''
-        nl = self.get_nodes()
-        if not nl:
-            return
-        if self.source == "live" and not self.central_log:
-            doc = cibdump2doc()
-        else:
-            doc = file2doc(os.path.join(self.loc,nl[0],"cib.xml"))
+        doc = None
+        cib_f = self.get_cib_loc()
+        if cib_f:
+            doc = file2doc(cib_f)
         if not doc:
             return  # no cib?
         try: conf = doc.getElementsByTagName("configuration")[0]
@@ -783,6 +858,16 @@
         for n in self.cibnode_l:
             self.nodecolor[n] = self.nodecolors[i]
             i = (i+1) % len(self.nodecolors)
+    def get_invoke_trans_msgs(self, msg_l):
+        te_invoke_patt = vars.transition_patt[0].replace("%%", "[0-9]+")
+        return [x for x in msg_l if re.search(te_invoke_patt, x)]
+    def get_all_trans_msgs(self, msg_l=None):
+        trans_re_l = [x.replace("%%", "[0-9]+") for x in vars.transition_patt]
+        if not msg_l:
+            return self.logobj.get_matches(trans_re_l)
+        else:
+            re_s = '|'.join(trans_re_l)
+            return [x for x in msg_l if re.search(re_s, x)]
     def list_transitions(self, msg_l = None, future_pe = False):
         '''
         List transitions by reading logs.
@@ -790,57 +875,57 @@
         Some callers need original PE file path (future_pe),
         otherwise we produce the path within the report and check
         if the transition files exist.
+        NB: future_pe means that the peinput has not been fetched yet.
         If the caller doesn't provide the message list, then we
         build it from the collected log files (self.logobj).
         Otherwise, we get matches for transition patterns.
+
+        WARN: We rely here on the message format (syslog,
+        pacemaker).
         '''
-        trans_re_l = [x.replace("%%", "[0-9]+") for x in vars.transition_patt]
-        if not msg_l:
-            msg_l = self.logobj.get_matches(trans_re_l)
-        else:
-            re_s = '|'.join(trans_re_l)
-            msg_l = [x for x in msg_l if re.search(re_s, x)]
+        trans_msg_l = self.get_all_trans_msgs(msg_l)
+        trans_start_msg_l = self.get_invoke_trans_msgs(trans_msg_l)
         pe_l = []
-        for msg in msg_l:
-            msg_a = msg.split()
-            if len(msg_a) < 8:
-                # this looks too short
-                common_warn("log message <%s> unexpected format, please report 
a bug" % msg)
-                continue
-            if "run_graph:" in msg:
-                continue # we want another message
-            common_debug("trans msg use: %s" % msg_a)
-            node = msg_a[3]
-            pe_file = msg_a[-1]
-            pe_base = os.path.basename(pe_file)
-            num_actions = transition_actions(msg_l, msg, pe_file)
-            if num_actions <= 0: # empty transition
-                common_debug("skipping empty transition (%s)" % pe_base)
+        for msg in trans_start_msg_l:
+            run_msg = get_matching_run_msg(msg, trans_msg_l)
+            t_obj = Transition(msg, run_msg)
+            num_actions = t_obj.total_actions()
+            if num_actions == 0: # empty transition
+                common_debug("skipping empty transition (%s)" % t_obj)
                 continue
             if not future_pe:
-                pe_l_file = os.path.join(self.loc, node, "pengine", pe_base)
+                pe_l_file = self.pe_report_path(t_obj)
                 if not os.path.isfile(pe_l_file):
-                    warn_once("%s in the logs, but not in the report" % 
pe_l_file)
+                    warn_once("%s in the logs, but not in the report" % t_obj)
                     continue
-            else:
-                pe_l_file = pe_file
-            if pe_l_file in pe_l:
-                common_debug("duplicate %s, replacing older PE file" % 
pe_l_file)
-                pe_l.remove(pe_l_file)
-            common_debug("found PE input: %s" % pe_l_file)
-            pe_l.append(pe_l_file)
+            common_debug("found PE input: %s" % t_obj)
+            pe_l.append(t_obj)
         return pe_l
     def report_setup(self):
+        if not self.change_origin:
+            return
         if not self.loc:
             return
-        self.desc = os.path.join(self.loc,"description.txt")
-        self.find_logs()
-        self.find_central_log()
-        self.read_cib()
-        self.set_node_colors()
+        if self.change_origin == CH_SRC:
+            vars.pcmk_version = None
+            self.desc = os.path.join(self.loc,"description.txt")
+            self.log_l = self.find_logs()
+            self.find_central_log()
+            self.read_cib()
+            self.set_node_colors()
+        elif self.change_origin == CH_UPD:
+            l = self.find_logs()
+            if self.log_l != l:
+                self.log_l = l
+                self.read_cib()
+                self.set_node_colors()
         self.logobj = LogSyslog(self.central_log, self.log_l, \
                 self.from_dt, self.to_dt)
-        self.peinputs_l = self.list_transitions()
+        if self.change_origin != CH_UPD:
+            common_debug("getting transitions from logs")
+            self.peinputs_l = self.list_transitions()
+        self.ready = self.check_report()
+        self.change_origin = 0
     def prepare_source(self):
         '''
         Unpack a hb_report tarball.
@@ -862,7 +947,6 @@
         if not self.loc:
             return False
         self.report_setup()
-        self.ready = self.check_report()
         return self.ready
     def refresh_source(self, force = False):
         '''
@@ -874,13 +958,19 @@
         self.last_live_update = 0
         self.loc = self.manage_live_report(force)
         self.report_setup()
-        self.ready = self.check_report()
         return self.ready
     def get_patt_l(self,type):
         '''
         get the list of patterns for this type, up to and
         including current detail level
         '''
+        cib_f = None
+        if self.source != "live" or self.central_log:
+            cib_f = self.get_cib_loc()
+        if is_pcmk_118(cib_f=cib_f):
+            from log_patterns_118 import log_patterns
+        else:
+            from log_patterns import log_patterns
         if not type in log_patterns:
             common_error("%s not featured in log patterns" % type)
             return None
@@ -974,72 +1064,28 @@
             self.error("no resources or nodes found")
             return False
         self.show_logs(re_l = all_re_l)
-    def get_transition_msgs(self, pe_file, msg_l = None):
-        if not msg_l:
-            trans_re_l = [x.replace("%%", "[0-9]+") for x in 
vars.transition_patt]
-            msg_l = self.logobj.get_matches(trans_re_l)
-        te_invoke_msg = ""
-        run_msg = ""
-        unpack_msg = ""
-        pe_num = get_pe_num(pe_file)
-        te_invoke_patt = vars.transition_patt[0].replace("%%", pe_num)
-        run_patt = vars.transition_patt[1].replace("%%", pe_num)
-        r = None
-        msg_l.reverse()
-        for msg in msg_l:
-            r = re.search(te_invoke_patt, msg)
-            if r:
-                te_invoke_msg = msg
-                break
-        if not r:
-            return ["", "", ""]
-        trans_num = r.group(1)
-        unpack_patt = vars.transition_patt[2].replace("%%", trans_num)
-        for msg in msg_l:
-            if re.search(run_patt, msg):
-                run_msg = msg
-            elif re.search(unpack_patt, msg):
-                unpack_msg = msg
-            if run_msg and unpack_msg:
-                break
-        return [unpack_msg, te_invoke_msg, run_msg]
-    def show_transition_log(self, pe_file):
+    def find_peinput(self, pe_file):
+        for t_obj in self.peinputs_l:
+            if self.pe_report_path(t_obj) == pe_file:
+                return t_obj
+        return None
+    def show_transition_log(self, pe_file, full_log=False):
         '''
         Search for events within the given transition.
         '''
         if not self.prepare_source():
             return False
-        pe_base = os.path.basename(pe_file)
-        pe_num = get_pe_num(pe_base)
-        unpack_msg, te_invoke_msg, run_msg = self.get_transition_msgs(pe_file)
-        if not te_invoke_msg:
-            common_warn("start of transition %s not found in logs" % pe_base)
+        t_obj = self.find_peinput(pe_file)
+        if not t_obj:
+            common_err("%s: transition not found" % pe_file)
             return False
-        if not run_msg:
-            common_warn("end of transition %s not found in logs (transition 
not complete yet?)" % pe_base)
-            return False
-        common_debug("transition start: %s" % te_invoke_msg)
-        common_debug("transition end: %s" % run_msg)
-        start_ts = syslog_ts(te_invoke_msg)
-        end_ts = syslog_ts(run_msg)
-        if not start_ts or not end_ts:
-            self.warn("strange, no timestamps found")
-            return False
-        act_d = run_graph_msg_actions(run_msg)
-        total = sum(act_d.values())
-        s = ""
-        for a in act_d:
-            if not act_d[a]:
-                continue
-            s = "%s %s=%d" % (s, a, act_d[a])
-        common_info("transition %s %d actions: %s" %
-            (pe_file.replace(self.loc+"/",""), total, s))
-        common_info("logs for transition %s (%s-%s)" %
-            (pe_file.replace(self.loc+"/",""), \
-            shorttime(start_ts), shorttime(end_ts)))
         # limit the log scope temporarily
-        self.logobj.set_log_timeframe(start_ts, end_ts)
-        self.events()
+        self.logobj.set_log_timeframe(t_obj.start_ts, t_obj.end_ts)
+        if full_log:
+            self.show_logs()
+        else:
+            t_obj.transition_info()
+            self.events()
         self.logobj.set_log_timeframe(self.from_dt, self.to_dt)
         return True
     def resource(self,*args):
@@ -1103,8 +1149,8 @@
                 a.append(a[0])
         elif a is not None:
             a = [a,a]
-        return [x for x in self.peinputs_l \
-            if pe_file_in_range(x, a)]
+        return [self.pe_report_path(x) for x in self.peinputs_l \
+            if pe_file_in_range(x.pe_file, a)]
     def dotlist(self, a = None):
         l = [x.replace("bz2","dot") for x in self.pelist(a)]
         return [x for x in l if os.path.isfile(x)]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/ui.py.in new/crmsh/modules/ui.py.in
--- old/crmsh/modules/ui.py.in  2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/ui.py.in  2012-12-11 18:29:34.000000000 +0100
@@ -122,6 +122,7 @@
         self.cmd_table["check-mode"] = (self.set_check_mode,(1,1),0,0)
         self.cmd_table["sort-elements"] = (self.set_sort_elements,(1,1),0,0)
         self.cmd_table["wait"] = (self.set_wait,(1,1),0,0)
+        self.cmd_table["add-quotes"] = (self.set_add_quotes,(1,1),0,0)
         self.cmd_table["save"] = (self.save_options,(0,0),0,0)
         self.cmd_table["show"] = (self.show_options,(0,0),0,0)
         setup_aliases(self)
@@ -162,6 +163,12 @@
             common_err("%s: bad boolean option"%opt)
             return True
         return user_prefs.set_wait(opt)
+    def set_add_quotes(self,cmd,opt):
+        "usage: add-quotes {yes|no}"
+        if not verify_boolean(opt):
+            common_err("%s: bad boolean option"%opt)
+            return True
+        return user_prefs.set_add_quotes(opt)
     def show_options(self,cmd):
         "usage: show"
         return user_prefs.write_rc(sys.stdout)
@@ -1761,6 +1768,7 @@
         self.cmd_aliases.update({
             "limit": ("timeframe",),
         })
+        setup_aliases(self)
         self._set_source(options.history)
         self.current_session = None
     def _no_source(self):
@@ -1922,14 +1930,15 @@
         return l[0]
     def transition(self,cmd,*args):
         """usage: transition [<number>|<index>|<file>] [nograph] [v...] 
[scores] [actions] [utilization]
-        transition showdot [<number>|<index>|<file>]"""
+        transition showdot [<number>|<index>|<file>]
+        transition log [<number>|<index>|<file>]"""
         argl = list(args)
         subcmd = "show"
-        if argl and argl[0] == "showdot":
-            if not user_prefs.dotty:
+        if argl and argl[0] in ("showdot", "log"):
+            if argl[0] == "showdot" and not user_prefs.dotty:
                 common_err("install graphviz to draw transition graphs")
                 return False
-            subcmd = "showdot"
+            subcmd = argl[0]
             argl.remove(subcmd)
         if argl:
             if re.search('pe-', argl[0]):
@@ -1949,18 +1958,21 @@
         if not f:
             return False
         rc = True
+        full_log = False
         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', cmd, *argl)
-        else:
+        elif subcmd == "showdot":
             f = crm_report.pe2dot(f)
             if not f:
                 common_err("dot file not found in the report")
                 return False
             show_dot_graph(f)
+        else:
+            full_log = True
         if rc:
-            crm_report.show_transition_log(f)
+            rc = crm_report.show_transition_log(f, full_log)
         return rc
     def session(self,cmd,subcmd = None,name = None):
         "usage: session [{save|load|delete} <name> | pack [<name>] | update | 
list]"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/userprefs.py 
new/crmsh/modules/userprefs.py
--- old/crmsh/modules/userprefs.py      2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/userprefs.py      2012-12-11 18:29:34.000000000 +0100
@@ -79,6 +79,7 @@
         self.force = False
         self.sort_elems = "yes"
         self.wait = False
+        self.add_quotes = "yes"
     def missing(self,n):
         common_err("could not find any %s on the system" % n)
     def check_skill_level(self,n):
@@ -168,6 +169,10 @@
         self.wait = is_boolean_true(opt) and "yes" or "no"
     def get_wait(self):
         return self.wait == "yes"
+    def set_add_quotes(self,opt):
+        self.add_quotes = is_boolean_true(opt) and "yes" or "no"
+    def get_add_quotes(self):
+        return self.add_quotes == "yes"
     def write_rc(self,f):
         print >>f, '%s "%s"' % ("editor",self.editor)
         print >>f, '%s "%s"' % ("pager",self.pager)
@@ -179,6 +184,7 @@
         print >>f, '%s "%s"' % ("check-frequency",self.check_frequency)
         print >>f, '%s "%s"' % ("check-mode",self.check_mode)
         print >>f, '%s "%s"' % ("wait",self.wait)
+        print >>f, '%s "%s"' % ("add-quotes",self.add_quotes)
     def save_options(self,rc_file):
         try: f = open(rc_file,"w")
         except IOError,msg:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/utils.py new/crmsh/modules/utils.py
--- old/crmsh/modules/utils.py  2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/utils.py  2012-12-11 18:29:34.000000000 +0100
@@ -155,6 +155,7 @@
 
 def filter_string(cmd,s,stderr_on = True):
     rc = -1 # command failed
+    outp = ''
     if stderr_on:
         stderr = None
     else:
@@ -167,8 +168,13 @@
         outp = p.communicate(s)[0]
         p.wait()
         rc = p.returncode
-    except IOError, msg:
+    except OSError, (errno, strerror):
+        if errno != os.errno.EPIPE:
+            common_err(strerror)
+        common_info("from: %s" % cmd)
+    except Exception, msg:
         common_err(msg)
+        common_info("from: %s" % cmd)
     return rc,outp
 
 def str2tmp(s):
@@ -381,6 +387,9 @@
     cmd = "crmadmin -S %s" % dc
     cnt = 0
     output_started = 0
+    init_sleep = 0.25
+    max_sleep = 1.00
+    sleep_time = init_sleep
     while True:
         s = get_stdout(add_sudo(cmd))
         if not s.startswith("Status"):
@@ -394,7 +403,9 @@
             if output_started:
                 sys.stderr.write(" done\n")
             return True
-        time.sleep(0.1)
+        time.sleep(sleep_time)
+        if sleep_time < max_sleep:
+            sleep_time *= 2
         if show_progress:
             if not output_started:
                 output_started = 1
@@ -620,6 +631,8 @@
         if len(s) > max_len:
             max_len = len(s)
     cols = w/(max_len + min_gap)  # approx.
+    if not cols:
+        cols = 1
     col_len = w/cols
     for i in range(len(l)/cols + 1):
         s = ''
@@ -691,6 +704,55 @@
             return None
     return dt
 
+def get_pcmk_version(dflt):
+    try:
+        v = get_stdout("crmd version").split()[2]
+        common_debug("found pacemaker version: %s" % v)
+    except Exception,msg:
+        v = dflt
+        common_warn("could not get the pacemaker version, bad installation?")
+    return v
+
+def get_cib_property(cib_f, attr, dflt):
+    """A poor man's get attribute procedure.
+    We don't want heavy parsing, this needs to be relatively
+    fast.
+    """
+    open_t = "<cluster_property_set"
+    close_t = "</cluster_property_set"
+    attr_s = 'name="%s"' % attr
+    ver_patt = re.compile('value="([^"]+)"')
+    ver = dflt # return some version in any case
+    try: f = open(cib_f, "r")
+    except IOError, msg:
+        common_err(msg)
+        return ver
+    state = 0
+    for s in f:
+        if state == 0:
+            if open_t in s:
+                state += 1
+        elif state == 1:
+            if close_t in s:
+                break
+            if attr_s in s:
+                r = ver_patt.search(s)
+                if r:
+                    ver = r.group(1)
+                break
+    f.close()
+    return ver
+
+def is_pcmk_118(cib_f=None):
+    if not vars.pcmk_version:
+        if cib_f:
+            vars.pcmk_version = get_cib_property(cib_f, "dc-version", "1.1.1")
+            common_debug("found pacemaker version: %s in cib: %s" % (ver, 
cib_f))
+        else:
+            vars.pcmk_version = get_pcmk_version("1.1.1")
+    from distutils.version import LooseVersion
+    return LooseVersion(vars.pcmk_version) >= LooseVersion("1.1.8")
+
 user_prefs = UserPrefs.getInstance()
 options = Options.getInstance()
 vars = Vars.getInstance()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/vars.py.in new/crmsh/modules/vars.py.in
--- old/crmsh/modules/vars.py.in        2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/modules/vars.py.in        2012-12-11 18:29:34.000000000 +0100
@@ -205,6 +205,7 @@
     crm_daemon_dir = "@CRM_DAEMON_DIR@"
     crm_daemon_user = "@CRM_DAEMON_USER@"
     crm_version = "@VERSION@ (Build @BUILD_VERSION@)"
+    nagios_dir = "/usr/lib/nagios/plugins"
 
     ra_if = None # class interface to RA
     stonithd_metadata = None # stonithd meta data
@@ -226,16 +227,17 @@
         if not ocf_root:
             ocf_root = "/usr/lib/ocf"
         os.environ["OCF_ROOT"] = ocf_root
+    pcmk_version = "" # set later
 
     # r.group(1) transition number (a different thing from file number)
     # r.group(2) contains full path
     # r.group(3) file number
     transition_patt = [
         "crmd.* do_te_invoke: Processing graph ([0-9]+) .*derived from 
(.*/pe-[^-]+-(%%)[.]bz2)", # transition start
+    # r.group(1) transition number (a different thing from file number)
+    # r.group(2) contains full path
+    # r.group(3) transition status
         "crmd.* run_graph: .*Transition 
([0-9]+).*Source=(.*/pe-[^-]+-(%%)[.]bz2).: (Stopped|Complete|Terminated)", # 
and stop
-    # r.group(1) transition number
-    # r.group(2) number of actions
-        "crmd.* run_graph: .*Transition (%%) .*Complete=([0-9]+),", # number 
of actions
     ]
 
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/test/testcases/Makefile.am 
new/crmsh/test/testcases/Makefile.am
--- old/crmsh/test/testcases/Makefile.am        2012-10-15 20:13:49.000000000 
+0200
+++ new/crmsh/test/testcases/Makefile.am        2012-12-11 18:29:34.000000000 
+0100
@@ -22,10 +22,10 @@
 testcases_SCRIPTS      =       ra.filter xmlonly.sh
 testcases_DATA         =       BSC basicset common.excl \
        confbasic confbasic-xml delete file node ra resource shadow acl \
-       rset rset-xml \
+       edit rset rset-xml \
        confbasic-xml.exp confbasic.exp delete.exp file.exp \
        node.exp ra.exp resource.exp shadow.exp acl.exp \
-       rset.exp rset-xml.exp
+       edit.exp rset.exp rset-xml.exp
 
 confbasic-xml.filter:
        ln xmlonly.sh $@
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/test/testcases/basicset 
new/crmsh/test/testcases/basicset
--- old/crmsh/test/testcases/basicset   2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/test/testcases/basicset   2012-12-11 18:29:34.000000000 +0100
@@ -1,5 +1,6 @@
 confbasic
 confbasic-xml
+edit
 rset
 rset-xml
 delete
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/test/testcases/edit 
new/crmsh/test/testcases/edit
--- old/crmsh/test/testcases/edit       2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/test/testcases/edit       2012-12-11 18:29:34.000000000 +0100
@@ -4,7 +4,7 @@
        attributes mem=16G
 primitive st stonith:null \
        params hostlist='node1' \
-       meta yoyo-meta="yoyo 2" \
+       meta description="some description here" \
        op start requires=nothing \
        op monitor interval=60m
 primitive p1 ocf:heartbeat:Dummy \
@@ -29,6 +29,11 @@
 primitive d3 ocf:heartbeat:Dummy
 group g2 d1 d2
 filter "sed '/g2/s/d1/p1/;/g1/s/p1/d1/'"
+filter "sed '$alocation loc-d1 d1 rule $id=r1 -inf: not_defined webserver rule 
$id=r2 webserver: defined webserver'"
+filter "sed 's/not_defined webserver/& or mem number:lte 0/'" loc-d1
+filter "sed 's/ or mem number:lte 0//'" loc-d1
+filter "sed 's/not_defined webserver/& rule -inf: not_defined a2/'" loc-d1
+filter "sed 's/not_defined webserver/& or mem number:lte 0/'" loc-d1
 _test
 verify
 .
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/test/testcases/edit.exp 
new/crmsh/test/testcases/edit.exp
--- old/crmsh/test/testcases/edit.exp   2012-10-15 20:13:49.000000000 +0200
+++ new/crmsh/test/testcases/edit.exp   2012-12-11 18:29:34.000000000 +0100
@@ -5,7 +5,7 @@
 .INP: erase nodes
 .INP: property default-action-timeout=2m
 .INP: node node1       attributes mem=16G
-.INP: primitive st stonith:null        params hostlist='node1'         meta 
yoyo-meta="yoyo 2"         op start requires=nothing       op monitor 
interval=60m
+.INP: primitive st stonith:null        params hostlist='node1'         meta 
description="some description here"        op start requires=nothing       op 
monitor interval=60m
 .INP: primitive p1 ocf:heartbeat:Dummy         op monitor interval=60m         
op monitor interval=120m OCF_CHECK_LEVEL=10
 .INP: filter "sed '$aprimitive p2 ocf:heartbeat:Dummy'"
 .INP: filter "sed '$agroup g1 p1 p2'"
@@ -29,6 +29,11 @@
 .INP: filter "sed '/g2/s/d1/p1/;/g1/s/p1/d1/'"
 ERROR: 27: d1 already in use at g2
 ERROR: 27: p1 already in use at g1
+.INP: filter "sed '$alocation loc-d1 d1 rule $id=r1 -inf: not_defined 
webserver rule $id=r2 webserver: defined webserver'"
+.INP: filter "sed 's/not_defined webserver/& or mem number:lte 0/'" loc-d1
+.INP: filter "sed 's/ or mem number:lte 0//'" loc-d1
+.INP: filter "sed 's/not_defined webserver/& rule -inf: not_defined a2/'" 
loc-d1
+.INP: filter "sed 's/not_defined webserver/& or mem number:lte 0/'" loc-d1
 .INP: _test
 .INP: verify
 .INP: show
@@ -44,13 +49,17 @@
 primitive p3 ocf:heartbeat:Dummy
 primitive st stonith:null \
        params hostlist="node1" \
-       meta yoyo-meta="yoyo 2" \
+       meta description="some description here" \
        op start interval="0" requires="nothing" \
        op monitor interval="60m"
 group g1 p1 p2
 group g2 d1 d2
 clone c1 g1
 location l1 p3 100: node1
+location loc-d1 d1 \
+       rule $id="r1" -inf: not_defined webserver or mem number:lte 0 \
+       rule $id="loc-d1-rule" -inf: not_defined a2 \
+       rule $id="r2" webserver: defined webserver
 order o1 inf: p3 c1
 property $id="cib-bootstrap-options" \
        default-action-timeout="2m"

-- 
To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org
For additional commands, e-mail: opensuse-commit+h...@opensuse.org

Reply via email to