[Debian-ha-commits] [crmsh] 01/02: New upstream version 2.3.2

Valentin Vidic vvidic-guest at moszumanska.debian.org
Fri Oct 28 13:17:36 UTC 2016


This is an automated email from the git hooks/post-receive script.

vvidic-guest pushed a commit to branch master
in repository crmsh.

commit 4b7ca3f2fd0ca4aa296fe0c5410d0570b29b9ef8
Author: Valentin Vidic <Valentin.Vidic at CARNet.hr>
Date:   Fri Oct 28 15:07:46 2016 +0200

    New upstream version 2.3.2
---
 ChangeLog                                         | 20 +++++
 configure.ac                                      |  2 +-
 crm                                               |  2 +-
 crmsh/cibconfig.py                                | 40 ++++++++--
 crmsh/cliformat.py                                |  2 +
 crmsh/cmd_status.py                               | 19 ++---
 crmsh/corosync.py                                 |  1 +
 crmsh/crm_pssh.py                                 |  2 +-
 crmsh/history.py                                  |  6 +-
 crmsh/logparser.py                                | 28 ++++---
 crmsh/parse.py                                    | 36 +++++++--
 crmsh/ui_configure.py                             |  8 +-
 crmsh/ui_script.py                                |  2 +-
 crmsh/utils.py                                    | 35 +++++++++
 doc/crm.8.adoc                                    | 27 ++++++-
 doc/development.md                                | 93 ++++++++++++++++++++++-
 doc/website-v1/news.adoc                          |  7 +-
 doc/website-v1/news/2016-09-01-release-2_1_7.adoc | 46 +++++++++++
 doc/website-v1/news/2016-09-02-release-2_3_1.adoc | 33 ++++++++
 doc/website-v1/news/2016-09-05-release-2_2_2.adoc | 36 +++++++++
 hb_report/hb_report.in                            |  7 ++
 scripts/health/collect.py                         |  5 +-
 scripts/init/corosync.conf.template               | 15 +++-
 setup.py                                          |  2 +-
 test/unittests/test_cliformat.py                  | 26 +++++++
 25 files changed, 443 insertions(+), 57 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 1d7e229..dd4778a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+* Tue Oct 25 2016 Kristoffer Grönlund <kgronlund at suse.com> and many others
+- Release 2.3.2
+- high: history: Quote archive tarball name if it contains spaces (bsc#998959)
+- high: history: Prefer /var/log/messages over ha-log.txt (bsc#998891)
+- high: parse: Support target pattern in fencing topology
+- high: cibconfig: Ensure temp CIB is readable by crm_diff (bsc#999683)
+- medium: corosync: Fix missing variable in del-node
+- medium: scripts: Drop logrotate check from cluster health
+- medium: scripts: Better corosync defaults (bsc#1001164)
+- medium: cibconfig: Remove from tags when removing object
+- medium: ui_configure: option to obscure passwords
+- low: cmd_status: More detail in verify output
+- low: crm_pssh: Fix nodenum envvar name
+- low: cmd_status: Highlight plural forms (bsc#996806)
+- doc: Fix inverted boolean in resource set documentation
+
+* Fri Sep 2 2016 Kristoffer Grönlund <kgronlund at suse.com> and many others
+- Release 2.3.1
+- Require Python 2.6+, not 2.7 (bsc#995611) (#152)
+
 * Fri Aug 12 2016 Kristoffer Grönlund <kgronlund at suse.com> and many others
 - Release 2.3.0
 - medium: constants: Add missing alerts constants (#150) (bsc#992789)
diff --git a/configure.ac b/configure.ac
index 07323aa..307d3c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,7 +8,7 @@ dnl License: GNU General Public License (GPL)
 
 AC_PREREQ([2.53])
 
-AC_INIT([crmsh],[2.3.0],[users at clusterlabs.org])
+AC_INIT([crmsh],[2.3.2],[users at clusterlabs.org])
 
 AC_ARG_WITH(version,
     [  --with-version=version   Override package version (if you're a packager needing to pretend) ],
diff --git a/crm b/crm
index a098b2b..e0d4ce7 100755
--- a/crm
+++ b/crm
@@ -22,7 +22,7 @@
 import sys
 from distutils import version
 
-minimum_version = '2.7'
+minimum_version = '2.6'
 v_min = version.StrictVersion(minimum_version)
 v_this = version.StrictVersion(sys.version[:3])
 if v_min > v_this:
diff --git a/crmsh/cibconfig.py b/crmsh/cibconfig.py
index 9a772b4..8d47c5e 100644
--- a/crmsh/cibconfig.py
+++ b/crmsh/cibconfig.py
@@ -30,7 +30,7 @@ from .msg import missing_obj_err, common_warning, update_err, unsupported_err, e
 from .msg import invalid_id_err, cib_ver_unsupported_err
 from .utils import ext_cmd, safe_open_w, pipe_string, safe_close_w, crm_msec
 from .utils import ask, lines2cli, olist
-from .utils import page_string, cibadmin_can_patch, str2tmp
+from .utils import page_string, cibadmin_can_patch, str2tmp, ensure_sudo_readable
 from .utils import run_ptest, is_id_valid, edit_file, get_boolean, filter_string
 from .xmlutil import is_child_rsc, rsc_constraint, sanitize_cib, rename_id, get_interesting_nodes
 from .xmlutil import is_pref_location, get_topnode, new_cib, get_rscop_defaults_meta_node
@@ -585,6 +585,12 @@ class CibObjectSetRaw(CibObjectSet):
     def repr(self, format_mode="ignored"):
         "Return a string containing xml of all objects."
         cib_elem = cib_factory.obj_set2cib(self.obj_set)
+
+        from .utils import obscured
+        for nvp in cib_elem.xpath('//nvpair'):
+            if 'value' in nvp.attrib:
+                nvp.set('value', obscured(nvp.get('name'), nvp.get('value')))
+
         s = etree.tostring(cib_elem, pretty_print=True)
         return '<?xml version="1.0" ?>\n' + s
 
@@ -1923,7 +1929,9 @@ class CibFencingOrder(CibObject):
         s = clidisplay.keyword(self.obj_type)
         d = ordereddict.odict()
         for c in self.node.iterchildren("fencing-level"):
-            if "target-attribute" in c.attrib:
+            if "target-pattern" in c.attrib:
+                target = (None, c.get("target-pattern"))
+            elif "target-attribute" in c.attrib:
                 target = (c.get("target-attribute"), c.get("target-value"))
             else:
                 target = c.get("target")
@@ -1943,9 +1951,10 @@ class CibFencingOrder(CibObject):
 
         def fmt_target(tgt):
             if isinstance(tgt, tuple):
+                if tgt[0] is None:
+                    return "pattern:%s" % (tgt[1])
                 return "attr:%s=%s" % tgt
-            else:
-                return tgt + ":"
+            return tgt + ":"
         return cli_format([s] + ["%s %s" % (fmt_target(x), ' '.join(dd[x]))
                                  for x in dd.keys()],
                           break_lines=(format_mode > 0))
@@ -2558,13 +2567,14 @@ class CibFactory(object):
         self._set_cib_attributes(self.cib_elem)
         cib_s = etree.tostring(self.cib_orig, pretty_print=True)
         tmpf = str2tmp(cib_s, suffix=".xml")
-        if not tmpf:
+        if not tmpf or not ensure_sudo_readable(tmpf):
             return False
         tmpfiles.add(tmpf)
         cibadmin_opts = force and "-P --force" or "-P"
 
         # produce a diff:
         # dump_new_conf | crm_diff -o self.cib_orig -n -
+
         common_debug("Input: %s" % (etree.tostring(self.cib_elem)))
         rc, cib_diff = filter_string("%s -o %s -n -" %
                                      (self._crm_diff_cmd, tmpf),
@@ -3637,6 +3647,16 @@ class CibFactory(object):
         rmnode(obj.node)
         self._add_to_remove_queue(obj)
         self.cib_objects.remove(obj)
+        for tag in self.related_tags(obj):
+            # remove self from tag
+            # remove tag if self is last tagged object in tag
+            selfies = [x for x in tag.node.iterchildren() if x.get('id') == obj.obj_id]
+            for c in selfies:
+                rmnode(c)
+            if len(tag.node.xpath('./obj_ref')) == 0:
+                self._remove_obj(tag)
+                if not self._no_constraint_rm_msg:
+                    err_buf.info("hanging %s deleted" % str(tag))
         for c_obj in self.related_constraints(obj):
             if is_simpleconstraint(c_obj.node) and obj.children:
                 # the first child inherits constraints
@@ -3652,6 +3672,16 @@ class CibFactory(object):
             elif deleted:
                 err_buf.info("constraint %s updated" % str(c_obj))
 
+    def related_tags(self, obj):
+        def related_tag(tobj):
+            if tobj.obj_type != 'tag':
+                return False
+            for c in tobj.node.iterchildren():
+                if c.get('id') == obj.obj_id:
+                    return True
+            return False
+        return [x for x in self.cib_objects if related_tag(x)]
+
     def related_constraints(self, obj):
         def related_constraint(obj2):
             return is_constraint(obj2.node) and rsc_constraint(obj.obj_id, obj2.node)
diff --git a/crmsh/cliformat.py b/crmsh/cliformat.py
index 082422d..a385aeb 100644
--- a/crmsh/cliformat.py
+++ b/crmsh/cliformat.py
@@ -64,10 +64,12 @@ def cli_operations(node, break_lines=True):
 def cli_nvpair(nvp):
     'Converts an nvpair tag or a (name, value) pair to CLI syntax'
     from .cibconfig import cib_factory
+    from .utils import obscured
     nodeid = nvp.get('id')
     idref = nvp.get('id-ref')
     name = nvp.get('name')
     value = nvp.get('value')
+    value = obscured(name, value)
     if idref is not None:
         if name is not None:
             return '@%s:%s' % (idref, name)
diff --git a/crmsh/cmd_status.py b/crmsh/cmd_status.py
index a3bd8dc..cba9512 100644
--- a/crmsh/cmd_status.py
+++ b/crmsh/cmd_status.py
@@ -17,7 +17,7 @@ _WARNS = ['pending',
           r'UNKNOWN\!',
           'Stopped',
           'standby']
-_OKS = ['Online', 'online', 'ok', 'master', 'Started', 'Master', 'Slave']
+_OKS = ['Masters', 'Slaves', 'Started', 'Master', 'Slave', 'Online', 'online', 'ok', 'master']
 _ERRORS = ['not running',
            'unknown error',
            'invalid parameter',
@@ -40,14 +40,14 @@ _ERRORS = ['not running',
 
 
 class CrmMonFilter(object):
-    _OK = re.compile(r'(%s)' % '|'.join(_OKS))
+    _OK = re.compile(r'(%s)' % '|'.join(r"(?:\b%s\b)" % (w) for w in _OKS))
     _WARNS = re.compile(r'(%s)' % '|'.join(_WARNS))
     _ERROR = re.compile(r'(%s)' % ('|'.join(_ERRORS)))
     _NODES = re.compile(r'(\d+ Nodes configured)')
     _RESOURCES = re.compile(r'(\d+ Resources configured)')
 
     _RESOURCE = re.compile(r'(\S+)(\s+)\((\S+:\S+)\):')
-    _GROUP = re.compile(r'(Resource Group|Clone Set): (\S+)')
+    _GROUP = re.compile(r'((?:Resource Group)|(?:Clone Set)|(?:Master/Slave Set)): (\S+)')
 
     def _filter(self, line):
         line = self._RESOURCE.sub("%s%s(%s):" % (clidisplay.help_header(r'\1'),
@@ -55,10 +55,11 @@ class CrmMonFilter(object):
                                                  r'\3'), line)
         line = self._NODES.sub(clidisplay.help_header(r'\1'), line)
         line = self._RESOURCES.sub(clidisplay.help_header(r'\1'), line)
-        line = self._GROUP.sub(r'\1: ' + clidisplay.help_header(r'\2'), line)
-        line = self._WARNS.sub(clidisplay.warn(r'\1'), line)
-        line = self._OK.sub(clidisplay.ok(r'\1'), line)
-        line = self._ERROR.sub(clidisplay.error(r'\1'), line)
+        line, ngroups = self._GROUP.subn(r'\1: ' + clidisplay.help_header(r'\2'), line)
+        if ngroups == 0:
+            line = self._WARNS.sub(clidisplay.warn(r'\1'), line)
+            line = self._OK.sub(clidisplay.ok(r'\1'), line)
+            line = self._ERROR.sub(clidisplay.error(r'\1'), line)
         return line
 
     def __call__(self, text):
@@ -126,9 +127,9 @@ def cmd_verify(args):
     '''
     from . import config
     if "ptest" in config.core.ptest:
-        cmd1 = "crm_verify -LV; %s -L -VVVV" % (config.core.ptest)
+        cmd1 = "crm_verify -LVVV; %s -L -VVVV" % (config.core.ptest)
     else:
-        cmd1 = "crm_verify -LV; %s -LjV" % (config.core.ptest)
+        cmd1 = "crm_verify -LVVV; %s -LjV" % (config.core.ptest)
 
         if "scores" in args:
             cmd1 += " -s"
diff --git a/crmsh/corosync.py b/crmsh/corosync.py
index 64a0d0f..ce0824a 100644
--- a/crmsh/corosync.py
+++ b/crmsh/corosync.py
@@ -444,6 +444,7 @@ def del_node(addr):
     if nth == -1:
         return
 
+    num_nodes = p.count('nodelist.node')
     p.set('quorum.two_node', '1' if num_nodes == 2 else '0')
 
     f = open(conf(), 'w')
diff --git a/crmsh/crm_pssh.py b/crmsh/crm_pssh.py
index d3fd27c..165e0e5 100644
--- a/crmsh/crm_pssh.py
+++ b/crmsh/crm_pssh.py
@@ -83,7 +83,7 @@ def do_pssh(l, opts):
     for host, cmdline in l:
         cmd = ['ssh', host,
                '-o', 'PasswordAuthentication=no',
-               '-o', 'SendEnv=PSSH_NODENUM',
+               '-o', 'SendEnv=PARALLAX_NODENUM',
                '-o', 'StrictHostKeyChecking=no']
         if hasattr(opts, 'options'):
             for opt in opts.options:
diff --git a/crmsh/history.py b/crmsh/history.py
index 2aa7247..3c32e06 100644
--- a/crmsh/history.py
+++ b/crmsh/history.py
@@ -18,7 +18,7 @@ from . import utils
 from .msg import common_debug, common_warn, common_err, common_error, common_info, warn_once
 
 
-_LOG_FILES = ("ha-log.txt", "ha-log", "cluster-log.txt", "messages", "journal.log", "pacemaker.log")
+_LOG_FILES = ("messages", "ha-log.txt", "ha-log", "cluster-log.txt", "journal.log", "pacemaker.log")
 
 
 #
@@ -225,8 +225,8 @@ class Report(object):
         except Exception, msg:
             common_err("%s: %s" % (tarball, msg))
             return None
-        common_debug("tar -x%s < %s" % (tar_unpack_option, bfname))
-        rc = utils.pipe_cmd_nosudo("tar -x%s < %s" % (tar_unpack_option, bfname))
+        common_debug("tar -x%s < %s" % (tar_unpack_option, utils.quote(bfname)))
+        rc = utils.pipe_cmd_nosudo("tar -x%s < %s" % (tar_unpack_option, utils.quote(bfname)))
         if self.source == "live":
             os.remove(bfname)
         os.chdir(cwd)
diff --git a/crmsh/logparser.py b/crmsh/logparser.py
index 19c4398..69ed002 100644
--- a/crmsh/logparser.py
+++ b/crmsh/logparser.py
@@ -620,16 +620,20 @@ class LogParser(object):
         Load state from cache file
         """
         fn = self._metafile()
-        if os.path.isfile(fn) and time.time() - os.stat(fn).st_mtime < _METADATA_CACHE_AGE:
-            try:
-                with open(fn, 'rb') as f:
-                    try:
-                        if not self.from_dict(json.load(f)):
-                            return False
-                        crmlog.common_debug("Transition metadata loaded from %s" % (fn))
-                        return True
-                    except ValueError as e:
-                        crmlog.common_debug("Failed to load metadata: %s" % (e))
-            except IOError as e:
-                return False
+        if os.path.isfile(fn):
+            meta_mtime = os.stat(fn).st_mtime
+            logf_mtime = max([os.stat(f).st_mtime for f in self.filenames if os.path.isfile(f)])
+
+            if meta_mtime >= logf_mtime and time.time() - meta_mtime < _METADATA_CACHE_AGE:
+                try:
+                    with open(fn, 'rb') as f:
+                        try:
+                            if not self.from_dict(json.load(f)):
+                                return False
+                            crmlog.common_debug("Transition metadata loaded from %s" % (fn))
+                            return True
+                        except ValueError as e:
+                            crmlog.common_debug("Failed to load metadata: %s" % (e))
+                except IOError as e:
+                    return False
         return False
diff --git a/crmsh/parse.py b/crmsh/parse.py
index 0398b82..4fbf1bb 100644
--- a/crmsh/parse.py
+++ b/crmsh/parse.py
@@ -42,6 +42,7 @@ _TAG_RE = re.compile(r"([a-zA-Z_][^\s:]*):?$")
 _ROLE2_RE = re.compile(r"role=(.+)$", re.IGNORECASE)
 _TARGET_RE = re.compile(r'([^:]+):$')
 _TARGET_ATTR_RE = re.compile(r'attr:([\w-]+)=([\w-]+)$', re.IGNORECASE)
+_TARGET_PATTERN_RE = re.compile(r'pattern:(.+)$', re.IGNORECASE)
 TERMINATORS = ('params', 'meta', 'utilization', 'operations', 'op', 'rule', 'attributes')
 
 
@@ -1052,6 +1053,19 @@ class FencingOrderParser(BaseParser):
                    index=<+int> devices="\w,\w..."/>
     </fencing-topology>
 
+    from 1.1.14 on, target can be a regexp pattern:
+
+    pattern:<pattern> maps to XML:
+
+    <fencing-topology>
+    <fencing-level id=<id> target-pattern=<pattern>
+                   index=<+int> devices="\w,\w..."/>
+    </fencing-topology>
+
+    fencing-topology \
+      pcmk-1: poison-pill power \
+      pcmk-2: disk,network power
+
     """
     def parse(self, cmd):
         self.begin(cmd, min_args=1)
@@ -1063,6 +1077,8 @@ class FencingOrderParser(BaseParser):
         while self.has_tokens():
             if self.try_match(_TARGET_ATTR_RE):
                 target = (self.matched(1), self.matched(2))
+            elif self.try_match(_TARGET_PATTERN_RE):
+                target = (None, self.matched(1))
             elif self.try_match(_TARGET_RE):
                 target = self.matched(1)
             else:
@@ -1090,12 +1106,20 @@ class FencingOrderParser(BaseParser):
         targets = defaultdict(repeat(1).next)
         for target, devices in lvl_generator():
             if isinstance(target, tuple):
-                c = xmlutil.child(out, 'fencing-level',
-                                  index=str(targets[target[0]]),
-                                  devices=devices)
-                c.set('target-attribute', target[0])
-                c.set('target-value', target[1])
-                targets[target[0]] += 1
+                if target[0] is None:
+                    # pattern
+                    c = xmlutil.child(out, 'fencing-level',
+                                      index=str(targets[target[1]]),
+                                      devices=devices)
+                    c.set('target-pattern', target[1])
+                    targets[target[1]] += 1
+                else:
+                    c = xmlutil.child(out, 'fencing-level',
+                                      index=str(targets[target[0]]),
+                                      devices=devices)
+                    c.set('target-attribute', target[0])
+                    c.set('target-value', target[1])
+                    targets[target[0]] += 1
             else:
                 xmlutil.child(out, 'fencing-level',
                               target=target,
diff --git a/crmsh/ui_configure.py b/crmsh/ui_configure.py
index 2497b4b..ef1dcf9 100644
--- a/crmsh/ui_configure.py
+++ b/crmsh/ui_configure.py
@@ -302,8 +302,12 @@ class CibConfig(command.UI):
     @command.completers_repeating(_id_show_list)
     def do_show(self, context, *args):
         "usage: show [xml] [<id>...]"
-        set_obj = mkset_obj(*args)
-        return set_obj.show()
+        from .utils import obscure
+        osargs = [arg[8:] for arg in args if arg.startswith('obscure:')]
+        args = [arg for arg in args if not arg.startswith('obscure:')]
+        with obscure(osargs):
+            set_obj = mkset_obj(*args)
+            return set_obj.show()
 
     @command.name("get_property")
     @command.alias("get-property")
diff --git a/crmsh/ui_script.py b/crmsh/ui_script.py
index 61ce3f3..117344f 100644
--- a/crmsh/ui_script.py
+++ b/crmsh/ui_script.py
@@ -346,7 +346,7 @@ class Script(command.UI):
         """
         import yaml
         import os
-        from collections import OrderedDict
+        from .ordereddict import OrderedDict
 
         def flatten(script):
             if not isinstance(script, dict):
diff --git a/crmsh/utils.py b/crmsh/utils.py
index 239a235..404f547 100644
--- a/crmsh/utils.py
+++ b/crmsh/utils.py
@@ -245,6 +245,19 @@ def add_sudo(cmd):
     return cmd
 
 
+def ensure_sudo_readable(f):
+    # make sure the tempfile is readable to crm_diff (bsc#999683)
+    if config.core.user:
+        from pwd import getpwnam
+        uid = getpwnam(config.core.user).pw_uid
+        try:
+            os.chown(f, uid, -1)
+        except os.error as err:
+            common_err('Failed setting temporary file permissions: %s' % (err))
+            return False
+    return True
+
+
 def pipe_string(cmd, s):
     rc = -1  # command failed
     cmd = add_sudo(cmd)
@@ -1594,5 +1607,27 @@ def cluster_copy_file(local_path, nodes=None):
             err_buf.ok(host)
     return ok
 
+# a set of fnmatch patterns to match attributes whose values
+# should be obscured as a sequence of **** when printed
+_obscured_nvpairs = []
+
+def obscured(key, value):
+    if key is not None and value is not None:
+        for o in _obscured_nvpairs:
+            if fnmatch.fnmatch(key, o):
+                return '*' * 6
+    return value
+
+ at contextmanager
+def obscure(obscure_list):
+    global _obscured_nvpairs
+    prev = _obscured_nvpairs
+    _obscured_nvpairs = obscure_list
+    try:
+        yield
+    finally:
+        _obscured_nvpairs = prev
+
+
 
 # vim:ts=4:sw=4:et:
diff --git a/doc/crm.8.adoc b/doc/crm.8.adoc
index 248227c..5641baf 100644
--- a/doc/crm.8.adoc
+++ b/doc/crm.8.adoc
@@ -1,5 +1,5 @@
 :man source:   crm
-:man version:  2.3.0
+:man version:  2.3.2
 :man manual:   crmsh documentation
 
 crm(8)
@@ -663,7 +663,7 @@ each one implies different values for the two resource set attributes,
 +sequential+ and +require-all+.
 
 +sequential+::
-  If true, the resources in the set do not depend on each other
+  If false, the resources in the set do not depend on each other
   internally. Setting +sequential+ to +true+ implies a strict order of
   dependency within the set.
 
@@ -785,7 +785,7 @@ This primitive resource has two lists of parameters with descending
 priority. The parameter list with the highest priority is applied
 first, but only if the rule expressions for that parameter list all
 apply. In this case, the rule `#uname eq node1` limits the parameter
-list so that it is only applied on `node1.
+list so that it is only applied on `node1`.
 
 Note that rule expressions are not terminated and are immediately
 followed by the data to which the rule is applied. In this case, the
@@ -2909,6 +2909,10 @@ From Pacemaker version 1.1.14, it is possible to use a node attribute
 as the +target+ in a fencing topology. The syntax for this usage is
 described below.
 
+From Pacemaker version 1.1.14, it is also possible to use regular
+expression patterns as the +target+ in a fencing topology. The configured
+fencing sequence then applies to all devices matching the pattern.
+
 Usage:
 ...............
 fencing_topology <stonith_resources> [<stonith_resources> ...]
@@ -2917,7 +2921,7 @@ fencing_topology <fencing_order> [<fencing_order> ...]
 fencing_order :: <target> <stonith_resources> [<stonith_resources> ...]
 
 stonith_resources :: <rsc>[,<rsc>...]
-target :: <node>: | attr:<node-attribute>=<value>
+target :: <node>: | attr:<node-attribute>=<value> | pattern:<pattern>
 ...............
 Example:
 ...............
@@ -2932,6 +2936,13 @@ fencing_topology \
 # Fencing anything on rack 1 requires fencing via both APC 1 and 2,
 # to defeat the redundancy provided by two separate UPS units.
 fencing_topology attr:rack=1 apc01,apc02
+
+# Fencing for all machines named green.* is done using the pear
+# fencing device first, while all machines named red.* are fenced
+# using the apple fencing device first.
+fencing_topology \
+    pattern:green.* pear apple \
+    pattern:red.* apple pear
 ...............
 
 [[cmdhelp_configure_filter,filter CIB objects]]
@@ -3803,6 +3814,12 @@ To refine a selection of objects using multiple modifiers, the keywords
 +foo+, the following combination can be used:
 +show type:primitive and tag:foo+.
 
+To hide values when displaying the configuration, use the
++obscure:<glob>+ argument. This can be useful when sending the
+configuration over a public channel, to avoid exposing potentially
+sensitive information. The +<glob>+ argument is a bash-style pattern
+matching attribute keys.
+
 Usage:
 ...............
 show [xml] [<id>
@@ -3810,6 +3827,7 @@ show [xml] [<id>
            | type:<type>
            | tag:<id>
            | related:<obj>
+           | obscure:<glob>
            ...]
 
 type :: node | primitive | group | clone | ms | rsc_template
@@ -3827,6 +3845,7 @@ show webapp
 show type:primitive
 show xml tag:db tag:fs
 show related:webapp
+show type:primitive obscure:passwd
 ...............
 
 [[cmdhelp_configure_tag,Define resource tags]]
diff --git a/doc/development.md b/doc/development.md
index 53ad006..2406b4d 100644
--- a/doc/development.md
+++ b/doc/development.md
@@ -31,6 +31,43 @@ make
 If everything worked out as it should, the website should now be
 generated in `doc/website-v1/gen`.
 
+## Test suite
+
+There are two separate test suites for crmsh:
+
+* `test/unittests` - These are unit tests that test small pieces of
+  code or functionality. To run these tests, run the `test/run` script
+  from the project root.
+
+* `test/testcases` - These are larger integration tests which require
+  a Pacemaker installation on the machine where the tests are to
+  run. Usually, we run these tests using the OBS and the `osc` command
+  line tool:
+
+  1. Check out the crmsh python package to a directory (usually
+  `~/build-service/network:ha-clustering:Factory/crmsh`)
+
+  2. Replace the tarball for crmsh in the OBS project with an archive
+  built from the current source tree. Replace the version number with
+  whatever version is the current one on OBS:
+
+    git archive --format=tar --prefix=crmsh-2.3.0+git.1470991992.7deaa3a/ -o <tmpdir>/crmsh-2.3.0+git.1470991992.7deaa3a.tar HEAD
+    bzip2 <tmpdir>/crmsh-2.3.0+git.1470991992.7deaa3a.tar
+    cp <tmpdir>/crmsh-2.3.0+git.1470991992.7deaa3a.tar.bz2 ~/build-service/network:ha-clustering:Factory/crmsh/crmsh-2.3.0+git.1470991992.7deaa3a.tar.bz2
+
+  3. Build the rpm package for crmsh with the `with_regression_tests`
+  flag set to 1:
+
+    cd ~/build-service/network:ha-clustering:Factory/crmsh
+    osc build -d --no-verify --release=1 --define with_regression_tests 1 openSUSE_Tumbleweed x86_64 crmsh.spec
+
+To simplify this process, there is a utility called `obs` which can be
+downloaded here: https://github.com/krig/obs-scripts
+
+Using the `obs` script, the above is reduced to calling `obs test
+factory`, given an appropriate `obs.conf` file. See the README in the
+obs-scripts project for more details on using `obs`.
+
 ## Modules
 
 This is the list of all modules including short descriptions.
@@ -186,7 +223,7 @@ This is the list of all modules including short descriptions.
 
 	A bag of useful XML functions. Needs more order.
 
-## Code improvements
+## Code improvements / TODO
 
 These are some thoughts on how to improve maintainability and
 make crmsh nicer. Mostly for people looking at the code, the
@@ -195,6 +232,58 @@ users shouldn't notice much (or any) difference.
 Everybody's invited to comment and make further suggestions, in
 particular experienced pythonistas.
 
+### Syntax bug with automatic constraint handling
+
+See issue on github https://github.com/ClusterLabs/crmsh/issues/140 .
+
+The problem is the sequence of modifications: crmsh tries to be too
+smart and changes the constraint which refers to all members of the
+group so that it now refers to the group. But when the group is
+then deleted, the constraint is also deleted.
+
+### Rewrite the hb_report script completely in Python
+
+Right now, the `hb_report` script is written in bash. This means it
+has some duplicated code, for example finding pacemaker binaries,
+with crmsh. It also means that it can be difficult to debug and
+maintain. It would be better if it was completely implemented in
+Python.
+
+### Python 3 compatibility
+
+The code is currently only compatible with Python 2.6+. We will need
+to port crmsh to Python 3 eventually. The best solution for this is
+probably using the six python library which enables code which is
+both Python 2 and Python 3-compatible.
+
+### Validate more using pacemaker schema
+
+- We have the pacemaker CIB schema available (see schema.py),
+however using it is difficult and so it is not used in enough
+places.
+
+### Investigate switching to python-prompt-toolkit
+
+Either switch crmsh over to using the prompt toolkit for
+implementing the interactive mode, or at least look at it
+to see what ideas we can lift.
+
+https://github.com/jonathanslenders/python-prompt-toolkit
+
+### History transition should be able to save the graph to a file
+
+See https://github.com/ClusterLabs/crmsh/issues/98
+
+### Add support for ordering attribute on resource sets
+
+See https://github.com/ClusterLabs/crmsh/issues/84
+
+### Better version detection
+
+Be better at detecting and handling the Pacemaker version.
+Ensure backwards compatibility, for example with old vs.
+new ACL command syntax.
+
 ### Syntax highlighting
 
 - syntax highlighting is done before producing output, which
@@ -222,4 +311,4 @@ particular experienced pythonistas.
 ### Bad namespace separation
 
 - xmlutil and utils are just a loose collection of functions,
-  need to be organized better (get rid of 'from xyz import *')
+need to be organized better (get rid of 'from xyz import *')
diff --git a/doc/website-v1/news.adoc b/doc/website-v1/news.adoc
index c43ad43..b00b4fc 100644
--- a/doc/website-v1/news.adoc
+++ b/doc/website-v1/news.adoc
@@ -1,14 +1,17 @@
 = News
 
-link:/news/2016-08-12-release-2_3_0[2016-08-12 10:30]
+link:/news/2016-09-05-release-2_2_2[2016-09-05 19:00]
 
 :leveloffset: 1
 
-include::news/2016-08-12-release-2_3_0.adoc[]
+include::news/2016-09-05-release-2_2_2.adoc[]
 
 :leveloffset: 0
 
 ''''
+* link:/news/2016-09-02-release-2_3_1[2016-09-02 10:00 Releasing crmsh version 2.3.1]
+* link:/news/2016-09-01-release-2_1_7[2016-09-01 09:00 Announcing crmsh stable release 2.1.7]
+* link:/news/2016-08-12-release-2_3_0[2016-08-12 10:30 Releasing crmsh version 2.3.0]
 * link:/news/2016-04-28-release-2_2_1[2016-04-28 01:00 crmsh 2.2.1 and 2.1.6 are released]
 * link:/news/2016-01-15-release-2_2_0[2016-01-15 15:00 crmsh 2.2.0 is released]
 * link:/news/2016-01-12-release-2_1_5[2016-01-12 10:00 Announcing crmsh stable release 2.1.5]
diff --git a/doc/website-v1/news/2016-09-01-release-2_1_7.adoc b/doc/website-v1/news/2016-09-01-release-2_1_7.adoc
new file mode 100644
index 0000000..d1e477e
--- /dev/null
+++ b/doc/website-v1/news/2016-09-01-release-2_1_7.adoc
@@ -0,0 +1,46 @@
+Announcing crmsh stable release 2.1.7
+=====================================
+:Author: Kristoffer Gronlund
+:Email: kgronlund at suse.com
+:Date: 2016-09-01 09:00
+
+Today I are proud to announce the release of `crmsh` version 2.1.7!
+The major new thing in this release is a backports of the events-based
+alerts support from the 2.3 branch.
+
+Big thanks to Hideo Yamauchi for his patience and testing of the
+alerts backport.
+
+This time, the list of changes is small enough that I can add it right
+here:
+
+- high: parse: Backport of event-driven alerts parser (#150)
+- high: hb_report: Don't collect logs from journalctl if -M is set (bsc#990025)
+- high: hb_report: Skip lines without timestamps in log correctly (bsc#989810)
+- high: constants: Add maintenance to set of known attributes (bsc#981659)
+- high: utils: Avoid deadlock if DC changes during idle wait (bsc#978480)
+- medium: scripts: no-quorum-policy=ignore is deprecated (bsc#981056)
+- low: cibconfig: Don't mix up CLI name with XML tag
+
+You can also get the list of changes from the changelog:
+
+* https://github.com/ClusterLabs/crmsh/blob/2.1.7/ChangeLog
+
+Right now, I don't have a set of pre-built rpm packages for Linux
+distributions ready, but I am going to make this available soon. This
+is in particular for centOS 6.x which still relies on Python 2.6
+support which makes running the later releases there more
+difficult. These packages will most likely appear as a subrepository
+here (more details coming soon):
+
+* http://download.opensuse.org/repositories/network:/ha-clustering:/Stable/
+
+Archives of the tagged release:
+
+* https://github.com/ClusterLabs/crmsh/archive/2.1.7.tar.gz
+* https://github.com/ClusterLabs/crmsh/archive/2.1.7.zip
+
+
+Thank you,
+
+Kristoffer
diff --git a/doc/website-v1/news/2016-09-02-release-2_3_1.adoc b/doc/website-v1/news/2016-09-02-release-2_3_1.adoc
new file mode 100644
index 0000000..2a90bef
--- /dev/null
+++ b/doc/website-v1/news/2016-09-02-release-2_3_1.adoc
@@ -0,0 +1,33 @@
+Releasing crmsh version 2.3.1
+=============================
+:Author: Kristoffer Gronlund
+:Email: kgronlund at suse.com
+:Date: 2016-09-02 10:00
+
+Hello everyone!
+
+Today I am releasing crmsh version 2.3.1. The only change this time is
+to lower the Python version requirement from 2.7 to 2.6. This is so
+that crmsh remains compatible with centOS 6, where there is no
+standardized Python 2.7 version available. For users of other
+distributions where Python 2.7 is available, there are no other
+changes in this release and no need to upgrade.
+
+The source code can be downloaded from Github:
+
+* https://github.com/ClusterLabs/crmsh/releases/tag/2.3.1
+
+Packages for several popular Linux distributions can be downloaded
+from the Stable repository at the OBS:
+
+* http://download.opensuse.org/repositories/network:/ha-clustering:/Stable/
+
+Archives of the tagged release:
+
+* https://github.com/ClusterLabs/crmsh/archive/2.3.1.tar.gz
+* https://github.com/ClusterLabs/crmsh/archive/2.3.1.zip
+
+As usual, a huge thank you to all contributors and users of crmsh!
+
+Cheers,
+Kristoffer
diff --git a/doc/website-v1/news/2016-09-05-release-2_2_2.adoc b/doc/website-v1/news/2016-09-05-release-2_2_2.adoc
new file mode 100644
index 0000000..9816b3c
--- /dev/null
+++ b/doc/website-v1/news/2016-09-05-release-2_2_2.adoc
@@ -0,0 +1,36 @@
+Releasing crmsh version 2.2.2
+=============================
+:Author: Kristoffer Gronlund
+:Email: kgronlund at suse.com
+:Date: 2016-09-05 19:00
+
+Hello everyone!
+
+Today I am releasing crmsh version 2.2.2. The biggest change in this
+release is the backport of the support for event-based alerts from the
+2.3 branch. The full list of changes follows below:
+
+- high: parse: Backport of event-driven alerts parser (#150)
+- high: hb_report: Don't collect logs from journalctl if -M is set (bsc#990025)
+- high: hb_report: Skip lines without timestamps in log correctly (bsc#989810)
+- high: constants: Add maintenance to set of known attributes (bsc#981659)
+- high: utils: Avoid deadlock if DC changes during idle wait (bsc#978480)
+- medium: scripts: no-quorum-policy=ignore is deprecated (bsc#981056)
+- medium: tmpfiles: Create temporary directory if non-existing (bsc#981583)
+- medium: xmlutil: reduce unknown attribute to warning (bsc#981659)
+- medium: ui_resource: Add force argument to resource cleanup (bsc#979420)
+- parse: Use original _TARGET_RE
+
+The source code can be downloaded from Github:
+
+* https://github.com/ClusterLabs/crmsh/releases/tag/2.2.2
+
+Archives of the tagged release:
+
+* https://github.com/ClusterLabs/crmsh/archive/2.2.2.tar.gz
+* https://github.com/ClusterLabs/crmsh/archive/2.2.2.zip
+
+As usual, a huge thank you to all contributors and users of crmsh!
+
+Cheers,
+Kristoffer
diff --git a/hb_report/hb_report.in b/hb_report/hb_report.in
index 6110ca4..8f6fe0d 100755
--- a/hb_report/hb_report.in
+++ b/hb_report/hb_report.in
@@ -305,6 +305,13 @@ findlog() {
 	fi
 
 	if [ -n "$EXTRA_LOGS" ]; then
+		for l in $EXTRA_LOGS; do
+			if [ -f "$l" ] && [ "$l" != "$PCMK_LOG" ]; then
+				echo $l
+				return
+			fi
+		done
+
 		if [ -f "$WORKDIR/$JOURNAL_F" ]; then
 			echo $WORKDIR/$JOURNAL_F
 			return
diff --git a/scripts/health/collect.py b/scripts/health/collect.py
index d6fa94b..bb87368 100755
--- a/scripts/health/collect.py
+++ b/scripts/health/collect.py
@@ -14,10 +14,7 @@ def rpm_info():
     return crm_script.rpmcheck(PACKAGES)
 
 def logrotate_info():
-    rc, _, _ = crm_script.call(
-        'grep -r corosync.conf /etc/logrotate.d',
-        shell=True)
-    return {'corosync.conf': rc == 0}
+    return {}
 
 def sys_info():
     sysname, nodename, release, version, machine = os.uname()
diff --git a/scripts/init/corosync.conf.template b/scripts/init/corosync.conf.template
index 3fab031..3806c74 100644
--- a/scripts/init/corosync.conf.template
+++ b/scripts/init/corosync.conf.template
@@ -1,9 +1,17 @@
 # Please read the corosync.conf.5 manual page
 totem {
 	version: 2
+	secauth: on
+	crypto_hash: sha1
+	crypto_cipher: aes256
+	cluster_name: hacluster
+	clear_node_high_bit: yes
 
-	crypto_cipher: none
-	crypto_hash: none
+	token: 5000
+	token_retransmits_before_loss_const: 10
+	join: 60
+	consensus: 6000
+	max_messages: 20
 
 	interface {
 		ringnumber: 0
@@ -17,9 +25,10 @@ totem {
 
 logging {
 	fileline: off
+	to_stderr: no
 	to_logfile: no
-	to_syslog: yes
 	logfile: /var/log/cluster/corosync.log
+	to_syslog: yes
 	debug: off
 	timestamp: on
 	logger_subsys {
diff --git a/setup.py b/setup.py
index de4b13e..44bebb8 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@
 from setuptools import setup
 
 setup(name='crmsh',
-      version='2.3.0',
+      version='2.3.2',
       description='Command-line interface for High-Availability cluster management',
       author='Kristoffer Gronlund',
       author_email='kgronlund at suse.com',
diff --git a/test/unittests/test_cliformat.py b/test/unittests/test_cliformat.py
index 9b5b9f4..e196c7d 100644
--- a/test/unittests/test_cliformat.py
+++ b/test/unittests/test_cliformat.py
@@ -187,6 +187,28 @@ target="ha-one"></fencing-level>
 
 
 @with_setup(setup_func, teardown_func)
+def test_fencing2():
+    xml = """<fencing-topology>
+    <fencing-level devices="apple" id="fencing" index="1"
+target-pattern="green.*"></fencing-level>
+    <fencing-level devices="pear" id="fencing" index="2"
+target-pattern="green.*"></fencing-level>
+    <fencing-level devices="pear" id="fencing" index="1"
+target-pattern="red.*"></fencing-level>
+    <fencing-level devices="apple" id="fencing" index="2"
+target-pattern="red.*"></fencing-level>
+  </fencing-topology>"""
+    data = etree.fromstring(xml)
+    obj = factory.create_from_node(data)
+    assert_is_not_none(obj)
+    data = obj.repr_cli(format_mode=-1)
+    print data
+    exp = 'fencing_topology pattern:green.* apple pear pattern:red.* pear apple'
+    eq_(exp, data)
+    assert obj.cli_use_validate()
+
+
+ at with_setup(setup_func, teardown_func)
 def test_master():
     xml = """<master id="ms-1">
     <crmsh-ref id="dummy3" />
@@ -257,6 +279,10 @@ def test_new_role():
 def test_topology_1114():
     roundtrip('fencing_topology attr:rack=1 node1,node2')
 
+ at with_setup(setup_func, teardown_func)
+def test_topology_1114_pattern():
+    roundtrip('fencing_topology pattern:.* network disk')
+
 
 @with_setup(setup_func, teardown_func)
 def test_locrule():

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-ha/crmsh.git



More information about the Debian-HA-Commits mailing list