Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2020-01-17 16:06:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Fri Jan 17 16:06:36 2020 rev:172 rq:765101 version:4.2.0+git.1578911004.c1a33535

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2020-01-07 
23:53:51.752047166 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.26092/crmsh.changes   2020-01-17 
16:07:24.520497424 +0100
@@ -1,0 +2,35 @@
+Mon Jan 13 10:32:18 UTC 2020 - xli...@suse.com
+
+- Update to version 4.2.0+git.1578911004.c1a33535:
+  * Dev: behave: Not allowed space value for option
+  * Fix: ui_cluster: Not allowed space value for option (bsc#1141976)
+
+-------------------------------------------------------------------
+Fri Jan 10 08:51:14 UTC 2020 - xli...@suse.com
+
+- Update to version 4.2.0+git.1578645670.4df2f015:
+  * Dev: unittest: add unit test for dump_D_process function
+  * Fix: hb_report: disable dump all tasks stack into dmesg(bsc#1158060)
+
+-------------------------------------------------------------------
+Fri Jan 10 07:41:56 UTC 2020 - xli...@suse.com
+
+- Update to version 4.2.0+git.1578641542.ddda681c:
+  * Dev: behave: varify help output for commands which replace as argparse
+  * Dev: replace optparse with argparse
+
+-------------------------------------------------------------------
+Tue Jan 07 12:39:32 UTC 2020 - xli...@suse.com
+
+- Update to version 4.2.0+git.1578400179.830baba1:
+  * Dev: unittest: remove "placement-strategy=balanced" in ut codes
+  * Dev: behave: test placement-strategy value is "default"
+  * Fix: bootstrap: set placement-strategy value as "default"(bsc#1129462)
+
+-------------------------------------------------------------------
+Tue Jan 07 09:34:24 UTC 2020 - xli...@suse.com
+
+- Update to version 4.2.0+git.1578389070.fb171448:
+  * Fix: crmsh.spec: using mktemp to create tmp file(bsc#1154163)
+
+-------------------------------------------------------------------

Old:
----
  crmsh-4.2.0+git.1578387778.867a085b.tar.bz2

New:
----
  crmsh-4.2.0+git.1578911004.c1a33535.tar.bz2

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

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.7mdbdW/_old  2020-01-17 16:07:25.348497816 +0100
+++ /var/tmp/diff_new_pack.7mdbdW/_new  2020-01-17 16:07:25.352497818 +0100
@@ -36,7 +36,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        4.2.0+git.1578387778.867a085b
+Version:        4.2.0+git.1578911004.c1a33535
 Release:        0
 Url:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.7mdbdW/_old  2020-01-17 16:07:25.380497831 +0100
+++ /var/tmp/diff_new_pack.7mdbdW/_new  2020-01-17 16:07:25.380497831 +0100
@@ -5,4 +5,4 @@
                 <param 
name="url">https://github.com/liangxin1300/crmsh.git</param>
               <param 
name="changesrevision">d8dc51b4cb34964aa72e918999ebc7f03b48f3c9</param></service><service
 name="tar_scm">
                 <param 
name="url">https://github.com/ClusterLabs/crmsh.git</param>
-              <param 
name="changesrevision">867a085b5d7e710060d7381cd34c4634760c2462</param></service></servicedata>
\ No newline at end of file
+              <param 
name="changesrevision">c1a33535c73acf9ebd41e4e2b30731b7b90f9db0</param></service></servicedata>
\ No newline at end of file

++++++ crmsh-4.2.0+git.1578387778.867a085b.tar.bz2 -> 
crmsh-4.2.0+git.1578911004.c1a33535.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.2.0+git.1578387778.867a085b/.travis.yml 
new/crmsh-4.2.0+git.1578911004.c1a33535/.travis.yml
--- old/crmsh-4.2.0+git.1578387778.867a085b/.travis.yml 2020-01-07 
10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/.travis.yml 2020-01-13 
11:23:24.000000000 +0100
@@ -34,7 +34,13 @@
 
       after_script:
         - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
-  
+
+    - name: "regression test for bootstrap bugs"
+      before_install:
+        - $FUNCTIONAL_TEST bootstrap before_install
+      script:
+        - $FUNCTIONAL_TEST bootstrap run bugs
+
     - name: "functional test for bootstrap - init, join and remove"
       before_install:
         - $FUNCTIONAL_TEST bootstrap before_install
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/bootstrap.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/bootstrap.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/bootstrap.py  2020-01-07 
10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/bootstrap.py  2020-01-13 
11:23:24.000000000 +0100
@@ -1441,7 +1441,7 @@
     status("Loading initial cluster configuration")
 
     crm_configure_load("update", """
-property cib-bootstrap-options: stonith-enabled=false 
placement-strategy=balanced
+property cib-bootstrap-options: stonith-enabled=false
 op_defaults op-options: timeout=600 record-pending=true
 rsc_defaults rsc-options: resource-stickiness=1 migration-threshold=3
 """)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/main.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/main.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/main.py       2020-01-07 
10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/main.py       2020-01-13 
11:23:24.000000000 +0100
@@ -84,58 +84,58 @@
 
 
 def make_option_parser():
-    from optparse import OptionParser
-    parser = OptionParser(usage="""%prog [-h|--help] [OPTIONS] [SUBCOMMAND 
ARGS...]
-or %prog help SUBCOMMAND
+    from argparse import ArgumentParser, REMAINDER
+    parser = ArgumentParser(prog='crm', usage="""%(prog)s [-h|--help] 
[OPTIONS] [SUBCOMMAND ARGS...]
+or %(prog)s help SUBCOMMAND
 
-For a list of available subcommands, use %prog help.
+For a list of available subcommands, use %(prog)s help.
 
-Use %prog without arguments for an interactive session.
+Use %(prog)s without arguments for an interactive session.
 Call a subcommand directly for a "single-shot" use.
-Call %prog with a level name as argument to start an interactive
+Call %(prog)s with a level name as argument to start an interactive
 session from that level.
 
-See the crm(8) man page or call %prog help for more details.""",
-                          version="%prog " + config.CRM_VERSION)
-    parser.disable_interspersed_args()
-    parser.add_option("-f", "--file", dest="filename", metavar="FILE",
-                      help="Load commands from the given file. If a dash (-) " 
+
-                      "is used in place of a file name, crm will read commands 
" +
-                      "from the shell standard input (stdin).")
-    parser.add_option("-c", "--cib", dest="cib", metavar="CIB",
-                      help="Start the session using the given shadow CIB file. 
" +
-                      "Equivalent to `cib use <CIB>`.")
-    parser.add_option("-D", "--display", dest="display", metavar="OUTPUT_TYPE",
-                      help="Choose one of the output options: plain, 
color-always, color, or uppercase. " +
-                      "The default is color if the terminal emulation supports 
colors, " +
-                      "else plain.")
-    parser.add_option("-F", "--force", action="store_true", default=False, 
dest="force",
-                      help="Make crm proceed with applying changes where it 
would normally " +
-                      "ask the user to confirm before proceeding. This option 
is mainly useful " +
-                      "in scripts, and should be used with care.")
-    parser.add_option("-n", "--no", action="store_true", default=False, 
dest="ask_no",
-                      help="Automatically answer no when prompted")
-    parser.add_option("-w", "--wait", action="store_true", default=False, 
dest="wait",
-                      help="Make crm wait for the cluster transition to finish 
" +
-                      "(for the changes to take effect) after each processed 
line.")
-    parser.add_option("-H", "--history", dest="history", 
metavar="DIR|FILE|SESSION",
-                      help="A directory or file containing a cluster report to 
load " +
-                      "into history, or the name of a previously saved history 
session.")
-    parser.add_option("-d", "--debug", action="store_true", default=False, 
dest="debug",
-                      help="Print verbose debugging information.")
-    parser.add_option("-R", "--regression-tests", action="store_true", 
default=False,
-                      dest="regression_tests",
-                      help="Enables extra verbose trace logging used by the 
regression " +
-                      "tests. Logs all external calls made by crmsh.")
-    parser.add_option("--scriptdir", dest="scriptdir", metavar="DIR",
-                      help="Extra directory where crm looks for cluster 
scripts, or a list " +
-                      "of directories separated by semi-colons (e.g. 
/dir1;/dir2;etc.).")
-    parser.add_option("-X", dest="profile", metavar="PROFILE",
-                      help="Collect profiling data and save in PROFILE.")
-    parser.add_option("-o", "--opt", action="append", type="string", 
metavar="OPTION=VALUE",
-                      help="Set crmsh option temporarily. If the options are 
saved using" +
-                      "+options save+ then the value passed here will also be 
saved." +
-                      "Multiple options can be set by using +-o+ multiple 
times.")
+See the crm(8) man page or call %(prog)s help for more details.""")
+    parser.add_argument('--version', action='version', version="%(prog)s " + 
config.CRM_VERSION)
+    parser.add_argument("-f", "--file", dest="filename", metavar="FILE",
+                        help="Load commands from the given file. If a dash (-) 
" +
+                        "is used in place of a file name, crm will read 
commands " +
+                        "from the shell standard input (stdin).")
+    parser.add_argument("-c", "--cib", dest="cib", metavar="CIB",
+                        help="Start the session using the given shadow CIB 
file. " +
+                        "Equivalent to `cib use <CIB>`.")
+    parser.add_argument("-D", "--display", dest="display", 
metavar="OUTPUT_TYPE",
+                        help="Choose one of the output options: plain, 
color-always, color, or uppercase. " +
+                        "The default is color if the terminal emulation 
supports colors, " +
+                        "else plain.")
+    parser.add_argument("-F", "--force", action="store_true", default=False, 
dest="force",
+                        help="Make crm proceed with applying changes where it 
would normally " +
+                        "ask the user to confirm before proceeding. This 
option is mainly useful " +
+                        "in scripts, and should be used with care.")
+    parser.add_argument("-n", "--no", action="store_true", default=False, 
dest="ask_no",
+                        help="Automatically answer no when prompted")
+    parser.add_argument("-w", "--wait", action="store_true", default=False, 
dest="wait",
+                        help="Make crm wait for the cluster transition to 
finish " +
+                        "(for the changes to take effect) after each processed 
line.")
+    parser.add_argument("-H", "--history", dest="history", 
metavar="DIR|FILE|SESSION",
+                        help="A directory or file containing a cluster report 
to load " +
+                        "into history, or the name of a previously saved 
history session.")
+    parser.add_argument("-d", "--debug", action="store_true", default=False, 
dest="debug",
+                        help="Print verbose debugging information.")
+    parser.add_argument("-R", "--regression-tests", action="store_true", 
default=False,
+                        dest="regression_tests",
+                        help="Enables extra verbose trace logging used by the 
regression " +
+                        "tests. Logs all external calls made by crmsh.")
+    parser.add_argument("--scriptdir", dest="scriptdir", metavar="DIR",
+                        help="Extra directory where crm looks for cluster 
scripts, or a list " +
+                        "of directories separated by semi-colons (e.g. 
/dir1;/dir2;etc.).")
+    parser.add_argument("-X", dest="profile", metavar="PROFILE",
+                        help="Collect profiling data and save in PROFILE.")
+    parser.add_argument("-o", "--opt", action="append", type=str, 
metavar="OPTION=VALUE",
+                        help="Set crmsh option temporarily. If the options are 
saved using" +
+                        "+options save+ then the value passed here will also 
be saved." +
+                        "Multiple options can be set by using +-o+ multiple 
times.")
+    parser.add_argument("SUBCOMMAND", nargs=REMAINDER)
     return parser
 
 
@@ -302,7 +302,8 @@
 
 
 def parse_options():
-    opts, args = option_parser.parse_args()
+    opts, args = option_parser.parse_known_args()
+    utils.check_space_option_value(opts)
     config.core.debug = "yes" if opts.debug else config.core.debug
     options.profile = opts.profile or options.profile
     options.regression_tests = opts.regression_tests or 
options.regression_tests
@@ -323,7 +324,7 @@
             config.set_option(s, n, v)
         except ValueError as e:
             raise ValueError("Expected -o <section>.<name>=<value>: %s" % (e))
-    return args
+    return opts.SUBCOMMAND
 
 
 def profile_run(context, user_args):
@@ -376,5 +377,6 @@
             traceback.print_exc()
             sys.stdout.flush()
         common_err(str(e))
+        sys.exit(1)
 
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/ui_cluster.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/ui_cluster.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/ui_cluster.py 2020-01-07 
10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/ui_cluster.py 2020-01-13 
11:23:24.000000000 +0100
@@ -2,8 +2,8 @@
 # Copyright (C) 2013 Kristoffer Gronlund <kgronl...@suse.com>
 # See COPYING for license information.
 
-import optparse
 import re
+from argparse import ArgumentParser, RawDescriptionHelpFormatter
 from . import command
 from . import utils
 from .msg import err_buf
@@ -14,11 +14,23 @@
 from .cibconfig import cib_factory
 
 
-class OptParser(optparse.OptionParser):
+class ArgParser(ArgumentParser):
     def format_epilog(self, formatter):
         return self.epilog or ""
 
 
+def parse_options(parser, args):
+    try:
+        options, args = parser.parse_known_args(list(args))
+    except:
+        return None, None
+    if options.help:
+        parser.print_help()
+        return None, None
+    utils.check_space_option_value(options)
+    return options, args
+
+
 def _remove_completer(args):
     try:
         n = utils.list_cluster_nodes()
@@ -163,7 +175,7 @@
         if len(args) > 0:
             if '--dry-run' in args or looks_like_hostnames(args):
                 args = ['--yes', '--nodes'] + [arg for arg in args if arg != 
'--dry-run']
-        parser = OptParser(usage="usage: init [options] [STAGE]", epilog="""
+        parser = ArgParser(usage="init [options] [STAGE]", epilog="""
 
 Stage can be one of:
     ssh         Create SSH keys for passwordless SSH between cluster nodes
@@ -186,74 +198,68 @@
     To use storage you have already configured, pass -s and -o to specify
     the block devices for SBD and OCFS2, and the automatic partitioning
     will be skipped.
-""", add_help_option=False)
+""", add_help=False, formatter_class=RawDescriptionHelpFormatter)
 
-        parser.add_option("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
-        parser.add_option("-q", "--quiet", action="store_true", dest="quiet",
-                          help="Be quiet (don't describe what's happening, 
just do it)")
-        parser.add_option("-y", "--yes", action="store_true", 
dest="yes_to_all",
-                          help='Answer "yes" to all prompts (use with caution, 
this is destructive, especially during the "storage" stage. The 
/root/.ssh/id_rsa key will be overwritten unless the option 
"--no-overwrite-sshkey" is used)')
-        parser.add_option("-t", "--template", dest="template",
-                          help='Optionally configure cluster with template 
"name" (currently only "ocfs2" is valid here)')
-        parser.add_option("-n", "--name", metavar="NAME", dest="name", 
default="hacluster",
-                          help='Set the name of the configured cluster.')
-        parser.add_option("-N", "--nodes", metavar="NODES", dest="nodes",
-                          help='Additional nodes to add to the created 
cluster. ' +
-                          'May include the current node, which will always be 
the initial cluster node.')
-        # parser.add_option("--quick-start", dest="quickstart", 
action="store_true", help="Perform basic system configuration (NTP, watchdog, 
/etc/hosts)")
-        parser.add_option("-S", "--enable-sbd", dest="diskless_sbd", 
action="store_true",
-                          help="Enable SBD even if no SBD device is configured 
(diskless mode)")
-        parser.add_option("-w", "--watchdog", dest="watchdog", 
metavar="WATCHDOG",
-                          help="Use the given watchdog device")
-        parser.add_option("--no-overwrite-sshkey", action="store_true", 
dest="no_overwrite_sshkey",
-                          help='Avoid "/root/.ssh/id_rsa" overwrite if "-y" 
option is used (False by default)')
-
-        network_group = optparse.OptionGroup(parser, "Network configuration", 
"Options for configuring the network and messaging layer.")
-        network_group.add_option("-i", "--interface", dest="nic", metavar="IF",
-                                 help="Bind to IP address on interface IF")
-        network_group.add_option("-u", "--unicast", action="store_true", 
dest="unicast",
-                                 help="Configure corosync to communicate over 
unicast (UDP), and not multicast. " +
-                                 "Default is multicast unless an environment 
where multicast cannot be used is detected.")
-        network_group.add_option("-A", "--admin-ip", dest="admin_ip", 
metavar="IP",
-                                 help="Configure IP address as an 
administration virtual IP")
-        network_group.add_option("-M", "--multi-heartbeats", 
action="store_true", dest="second_hb",
-                                 help="Configure corosync with second 
heartbeat line")
-        network_group.add_option("-I", "--ipv6", action="store_true", 
dest="ipv6",
-                                 help="Configure corosync use IPv6")
-        network_group.add_option("--qnetd-hostname",
-                                 dest="qdevice", metavar="HOST",
-                                 help="HOST or IP of the QNetd server to be 
used")
-        network_group.add_option("--qdevice-port",
-                                 dest="qdevice_port", metavar="PORT", 
type="int", default=5403,
-                                 help="TCP PORT of QNetd server(default:5403)")
-        network_group.add_option("--qdevice-algo",
-                                 dest="qdevice_algo", metavar="ALGORITHM", 
default="ffsplit",
-                                 help="QNetd decision ALGORITHM(ffsplit/lms, 
default:ffsplit)")
-        network_group.add_option("--qdevice-tie-breaker",
-                                 dest="qdevice_tie_breaker", 
metavar="TIE_BREAKER", default="lowest",
-                                 help="QNetd 
TIE_BREAKER(lowest/highest/valid_node_id, default:lowest)")
-        network_group.add_option("--qdevice-tls",
-                                 dest="qdevice_tls", metavar="TLS", 
default="on",
-                                 help="Whether using TLS on 
QDevice/QNetd(on/off/required, default:on)")
-        network_group.add_option("--qdevice-heuristics",
-                                 dest="qdevice_heuristics", metavar="COMMAND",
-                                 help="COMMAND to run with absolute path. For 
multiple commands, use \";\" to separate(details about heuristics can see man 8 
corosync-qdevice)")
-        parser.add_option_group(network_group)
-
-        storage_group = optparse.OptionGroup(parser, "Storage configuration", 
"Options for configuring shared storage.")
-        storage_group.add_option("-p", "--partition-device", 
dest="shared_device", metavar="DEVICE",
-                                 help='Partition this shared storage device 
(only used in "storage" stage)')
-        storage_group.add_option("-s", "--sbd-device", dest="sbd_device", 
metavar="DEVICE", action="append",
-                                 help="Block device to use for SBD fencing, 
use \";\" as separator or -s multiple times for multi path (up to 3 devices)")
-        storage_group.add_option("-o", "--ocfs2-device", dest="ocfs2_device", 
metavar="DEVICE",
-                                 help='Block device to use for OCFS2 (only 
used in "vgfs" stage)')
-        parser.add_option_group(storage_group)
-        try:
-            options, args = parser.parse_args(list(args))
-        except:
-            return
-        if options.help:
-            parser.print_help()
+        parser.add_argument("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
+        parser.add_argument("-q", "--quiet", action="store_true", dest="quiet",
+                            help="Be quiet (don't describe what's happening, 
just do it)")
+        parser.add_argument("-y", "--yes", action="store_true", 
dest="yes_to_all",
+                            help='Answer "yes" to all prompts (use with 
caution, this is destructive, especially during the "storage" stage. The 
/root/.ssh/id_rsa key will be overwritten unless the option 
"--no-overwrite-sshkey" is used)')
+        parser.add_argument("-t", "--template", dest="template",
+                            help='Optionally configure cluster with template 
"name" (currently only "ocfs2" is valid here)')
+        parser.add_argument("-n", "--name", metavar="NAME", dest="name", 
default="hacluster",
+                            help='Set the name of the configured cluster.')
+        parser.add_argument("-N", "--nodes", metavar="NODES", dest="nodes",
+                            help='Additional nodes to add to the created 
cluster. May include the current node, which will always be the initial cluster 
node.')
+        # parser.add_argument("--quick-start", dest="quickstart", 
action="store_true", help="Perform basic system configuration (NTP, watchdog, 
/etc/hosts)")
+        parser.add_argument("-S", "--enable-sbd", dest="diskless_sbd", 
action="store_true",
+                            help="Enable SBD even if no SBD device is 
configured (diskless mode)")
+        parser.add_argument("-w", "--watchdog", dest="watchdog", 
metavar="WATCHDOG",
+                            help="Use the given watchdog device")
+        parser.add_argument("--no-overwrite-sshkey", action="store_true", 
dest="no_overwrite_sshkey",
+                            help='Avoid "/root/.ssh/id_rsa" overwrite if "-y" 
option is used (False by default)')
+
+        network_group = parser.add_argument_group("Network configuration", 
"Options for configuring the network and messaging layer.")
+        network_group.add_argument("-i", "--interface", dest="nic", 
metavar="IF",
+                                   help="Bind to IP address on interface IF")
+        network_group.add_argument("-u", "--unicast", action="store_true", 
dest="unicast",
+                                   help="Configure corosync to communicate 
over unicast (UDP), and not multicast. " +
+                                   "Default is multicast unless an environment 
where multicast cannot be used is detected.")
+        network_group.add_argument("-A", "--admin-ip", dest="admin_ip", 
metavar="IP",
+                                   help="Configure IP address as an 
administration virtual IP")
+        network_group.add_argument("-M", "--multi-heartbeats", 
action="store_true", dest="second_hb",
+                                   help="Configure corosync with second 
heartbeat line")
+        network_group.add_argument("-I", "--ipv6", action="store_true", 
dest="ipv6",
+                                   help="Configure corosync use IPv6")
+        network_group.add_argument("--qnetd-hostname",
+                                   dest="qdevice", metavar="HOST",
+                                   help="HOST or IP of the QNetd server to be 
used")
+        network_group.add_argument("--qdevice-port",
+                                   dest="qdevice_port", metavar="PORT", 
type=int, default=5403,
+                                   help="TCP PORT of QNetd 
server(default:5403)")
+        network_group.add_argument("--qdevice-algo",
+                                   dest="qdevice_algo", metavar="ALGORITHM", 
default="ffsplit",
+                                   help="QNetd decision ALGORITHM(ffsplit/lms, 
default:ffsplit)")
+        network_group.add_argument("--qdevice-tie-breaker",
+                                   dest="qdevice_tie_breaker", 
metavar="TIE_BREAKER", default="lowest",
+                                   help="QNetd 
TIE_BREAKER(lowest/highest/valid_node_id, default:lowest)")
+        network_group.add_argument("--qdevice-tls",
+                                   dest="qdevice_tls", metavar="TLS", 
default="on",
+                                   help="Whether using TLS on 
QDevice/QNetd(on/off/required, default:on)")
+        network_group.add_argument("--qdevice-heuristics",
+                                   dest="qdevice_heuristics", 
metavar="COMMAND",
+                                   help="COMMAND to run with absolute path. 
For multiple commands, use \";\" to separate(details about heuristics can see 
man 8 corosync-qdevice)")
+
+        storage_group = parser.add_argument_group("Storage configuration", 
"Options for configuring shared storage.")
+        storage_group.add_argument("-p", "--partition-device", 
dest="shared_device", metavar="DEVICE",
+                                   help='Partition this shared storage device 
(only used in "storage" stage)')
+        storage_group.add_argument("-s", "--sbd-device", dest="sbd_device", 
metavar="DEVICE", action="append",
+                                   help="Block device to use for SBD fencing, 
use \";\" as separator or -s multiple times for multi path (up to 3 devices)")
+        storage_group.add_argument("-o", "--ocfs2-device", 
dest="ocfs2_device", metavar="DEVICE",
+                                   help='Block device to use for OCFS2 (only 
used in "vgfs" stage)')
+
+        options, args = parse_options(parser, args)
+        if options is None or args is None:
             return
 
         stage = ""
@@ -261,15 +267,12 @@
             stage = args[0]
         if stage not in bootstrap.INIT_STAGES and stage != "":
             parser.error("Invalid stage (%s)" % (stage))
-            return False
 
         if options.template and options.template != "ocfs2":
             parser.error("Invalid template (%s)" % (options.template))
-            return False
 
         # if options.geo and options.name == "hacluster":
         #    parser.error("For a geo cluster, each cluster must have a unique 
name (use --name to set)")
-        #    return False
 
         qdevice = None
         if options.qdevice:
@@ -321,7 +324,7 @@
         '''
         Join this node to an existing cluster
         '''
-        parser = OptParser(usage="usage: join [options] [STAGE]", epilog="""
+        parser = ArgParser(usage="join [options] [STAGE]", epilog="""
 
 Stage can be one of:
     ssh         Obtain SSH keys from existing cluster node (requires -c <host>)
@@ -331,22 +334,17 @@
     cluster     Start the cluster on this node
 
 If stage is not specified, each stage will be invoked in sequence.
-""", add_help_option=False)
-        parser.add_option("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
-        parser.add_option("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
-        parser.add_option("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
-        parser.add_option("-w", "--watchdog", dest="watchdog", 
metavar="WATCHDOG", help="Use the given watchdog device")
-
-        network_group = optparse.OptionGroup(parser, "Network configuration", 
"Options for configuring the network and messaging layer.")
-        network_group.add_option("-c", "--cluster-node", dest="cluster_node", 
help="IP address or hostname of existing cluster node", metavar="HOST")
-        network_group.add_option("-i", "--interface", dest="nic", help="Bind 
to IP address on interface IF", metavar="IF")
-        parser.add_option_group(network_group)
-        try:
-            options, args = parser.parse_args(list(args))
-        except:
-            return
-        if options.help:
-            parser.print_help()
+""", add_help=False, formatter_class=RawDescriptionHelpFormatter)
+        parser.add_argument("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
+        parser.add_argument("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
+        parser.add_argument("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
+        parser.add_argument("-w", "--watchdog", dest="watchdog", 
metavar="WATCHDOG", help="Use the given watchdog device")
+
+        network_group = parser.add_argument_group("Network configuration", 
"Options for configuring the network and messaging layer.")
+        network_group.add_argument("-c", "--cluster-node", 
dest="cluster_node", help="IP address or hostname of existing cluster node", 
metavar="HOST")
+        network_group.add_argument("-i", "--interface", dest="nic", help="Bind 
to IP address on interface IF", metavar="IF")
+        options, args = parse_options(parser, args)
+        if options is None or args is None:
             return
 
         stage = ""
@@ -354,7 +352,6 @@
             stage = args[0]
         if stage not in ("ssh", "csync2", "ssh_merge", "cluster", ""):
             parser.error("Invalid stage (%s)" % (stage))
-            return False
 
         bootstrap.bootstrap_join(
             cluster_node=options.cluster_node,
@@ -384,16 +381,13 @@
         Installs packages, sets up corosync and pacemaker, etc.
         Must be executed from a node in the existing cluster.
         '''
-        parser = OptParser(usage="usage: add [options] [node ...]", 
add_help_option=False)
-        parser.add_option("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
-        parser.add_option("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
-        try:
-            options, args = parser.parse_args(list(args))
-        except:
-            return
-        if options.help:
-            parser.print_help()
+        parser = ArgParser(usage="add [options] [node ...]", add_help=False, 
formatter_class=RawDescriptionHelpFormatter)
+        parser.add_argument("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
+        parser.add_argument("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
+        options, args = parse_options(parser, args)
+        if options is None or args is None:
             return
+
         for node in args:
             if not self._add_node(node, yes_to_all=options.yes_to_all):
                 return False
@@ -405,20 +399,17 @@
         '''
         Remove the given node(s) from the cluster.
         '''
-        parser = OptParser(usage="usage: remove [options] [<node> ...]", 
add_help_option=False)
-        parser.add_option("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
-        parser.add_option("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
-        parser.add_option("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
-        parser.add_option("-c", "--cluster-node", dest="cluster_node", 
help="IP address or hostname of cluster node which will be deleted", 
metavar="HOST")
-        parser.add_option("-F", "--force", dest="force", help="Remove current 
node", action="store_true")
-        parser.add_option("--qdevice", dest="qdevice", help="Remove QDevice 
configuration and service from cluster", action="store_true")
-        try:
-            options, args = parser.parse_args(list(args))
-        except:
-            return
-        if options.help:
-            parser.print_help()
+        parser = ArgParser(usage="remove [options] [<node> ...]", 
add_help=False, formatter_class=RawDescriptionHelpFormatter)
+        parser.add_argument("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
+        parser.add_argument("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
+        parser.add_argument("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
+        parser.add_argument("-c", "--cluster-node", dest="cluster_node", 
help="IP address or hostname of cluster node which will be deleted", 
metavar="HOST")
+        parser.add_argument("-F", "--force", dest="force", help="Remove 
current node", action="store_true")
+        parser.add_argument("--qdevice", dest="qdevice", help="Remove QDevice 
configuration and service from cluster", action="store_true")
+        options, args = parse_options(parser, args)
+        if options is None or args is None:
             return
+
         if options.cluster_node is not None and options.cluster_node not in 
args:
             args = list(args) + [options.cluster_node]
         if len(args) == 0:
@@ -492,7 +483,7 @@
         * arbitrator IP / hostname (optional)
         * list of tickets (can be empty)
         '''
-        parser = OptParser(usage="usage: geo-init [options]", epilog="""
+        parser = ArgParser(usage="geo-init [options]", epilog="""
 
 Cluster Description
 
@@ -507,19 +498,15 @@
 
   Name clusters using the --name parameter to
   crm bootstrap init.
-""", add_help_option=False)
-        parser.add_option("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
-        parser.add_option("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
-        parser.add_option("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
-        parser.add_option("-a", "--arbitrator", help="IP address of geo 
cluster arbitrator", dest="arbitrator", metavar="IP")
-        parser.add_option("-s", "--clusters", help="Geo cluster description 
(see details below)", dest="clusters", metavar="DESC")
-        parser.add_option("-t", "--tickets", help="Tickets to create 
(space-separated)", dest="tickets", metavar="LIST")
-        try:
-            options, args = parser.parse_args(list(args))
-        except:
-            return
-        if options.help:
-            parser.print_help()
+""", add_help=False, formatter_class=RawDescriptionHelpFormatter)
+        parser.add_argument("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
+        parser.add_argument("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
+        parser.add_argument("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
+        parser.add_argument("-a", "--arbitrator", help="IP address of geo 
cluster arbitrator", dest="arbitrator", metavar="IP")
+        parser.add_argument("-s", "--clusters", help="Geo cluster description 
(see details below)", dest="clusters", metavar="DESC")
+        parser.add_argument("-t", "--tickets", help="Tickets to create 
(space-separated)", dest="tickets", metavar="LIST")
+        options, args = parse_options(parser, args)
+        if options is None or args is None:
             return
 
         if options.clusters is None:
@@ -547,18 +534,14 @@
         '''
         Join this cluster to a geo configuration.
         '''
-        parser = OptParser(usage="usage: geo-join [options]", 
add_help_option=False)
-        parser.add_option("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
-        parser.add_option("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
-        parser.add_option("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
-        parser.add_option("-c", "--cluster-node", help="IP address of an 
already-configured geo cluster or arbitrator", dest="node", metavar="IP")
-        parser.add_option("-s", "--clusters", help="Geo cluster description 
(see geo-init for details)", dest="clusters", metavar="DESC")
-        try:
-            options, args = parser.parse_args(list(args))
-        except:
-            return
-        if options.help:
-            parser.print_help()
+        parser = ArgParser(usage="geo-join [options]", add_help=False, 
formatter_class=RawDescriptionHelpFormatter)
+        parser.add_argument("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
+        parser.add_argument("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
+        parser.add_argument("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
+        parser.add_argument("-c", "--cluster-node", help="IP address of an 
already-configured geo cluster or arbitrator", dest="node", metavar="IP")
+        parser.add_argument("-s", "--clusters", help="Geo cluster description 
(see geo-init for details)", dest="clusters", metavar="DESC")
+        options, args = parse_options(parser, args)
+        if options is None or args is None:
             return
         errs = []
         if options.node is None:
@@ -580,17 +563,13 @@
         '''
         Make this node a geo arbitrator.
         '''
-        parser = OptParser(usage="usage: geo-init-arbitrator [options]", 
add_help_option=False)
-        parser.add_option("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
-        parser.add_option("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
-        parser.add_option("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
-        parser.add_option("-c", "--cluster-node", help="IP address of an 
already-configured geo cluster", dest="other", metavar="IP")
-        try:
-            options, args = parser.parse_args(list(args))
-        except:
-            return
-        if options.help:
-            parser.print_help()
+        parser = ArgParser(usage="geo-init-arbitrator [options]", 
add_help=False, formatter_class=RawDescriptionHelpFormatter)
+        parser.add_argument("-h", "--help", action="store_true", dest="help", 
help="Show this help message")
+        parser.add_argument("-q", "--quiet", help="Be quiet (don't describe 
what's happening, just do it)", action="store_true", dest="quiet")
+        parser.add_argument("-y", "--yes", help='Answer "yes" to all prompts 
(use with caution)', action="store_true", dest="yes_to_all")
+        parser.add_argument("-c", "--cluster-node", help="IP address of an 
already-configured geo cluster", dest="other", metavar="IP")
+        options, args = parse_options(parser, args)
+        if options is None or args is None:
             return
         bootstrap.bootstrap_arbitrator(options.quiet, options.yes_to_all, 
options.other, ui_context=context)
         return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/utils.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/utils.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/crmsh/utils.py      2020-01-07 
10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh/utils.py      2020-01-13 
11:23:24.000000000 +0100
@@ -15,6 +15,7 @@
 import fnmatch
 import gc
 import ipaddress
+import argparse
 from contextlib import contextmanager, closing
 from . import config
 from . import userdir
@@ -2264,4 +2265,14 @@
         return res.group(1)
     else:
         return None
+
+
+def check_space_option_value(options):
+    if not isinstance(options, argparse.Namespace):
+        raise ValueError("Expected type of \"options\" is 
\"argparse.Namespace\", not \"{}\"".format(type(options)))
+
+    for opt in vars(options):
+        value = getattr(options, opt)
+        if isinstance(value, str) and len(value.strip()) == 0:
+            raise ValueError("Space value not allowed for dest 
\"{}\"".format(opt))
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.2.0+git.1578387778.867a085b/crmsh.spec.in 
new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh.spec.in
--- old/crmsh-4.2.0+git.1578387778.867a085b/crmsh.spec.in       2020-01-07 
10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/crmsh.spec.in       2020-01-13 
11:23:24.000000000 +0100
@@ -201,7 +201,7 @@
 # Run regression tests after installing the package
 # NB: this is called twice by OBS, that's why we touch the file
 %post test
-testfile=/tmp/.crmsh_regression_tests_ran
+testfile=`mktemp -t .crmsh_regression_tests_ran_XXXXXX`
 # check if time in file is less than 2 minutes ago
 if [ -e $testfile ] && [ "$(( $(date +%s) - $(cat $testfile) ))" -lt 120 ]; 
then
        echo "Skipping regression tests..."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.2.0+git.1578387778.867a085b/data-manifest 
new/crmsh-4.2.0+git.1578911004.c1a33535/data-manifest
--- old/crmsh-4.2.0+git.1578387778.867a085b/data-manifest       2020-01-07 
10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/data-manifest       2020-01-13 
11:23:24.000000000 +0100
@@ -65,6 +65,7 @@
 test/descriptions
 test/docker_scripts.sh
 test/evaltest.sh
+test/features/bootstrap_bugs.feature
 test/features/bootstrap_init_join_remove.feature
 test/features/bootstrap_options.feature
 test/features/environment.py
@@ -72,6 +73,7 @@
 test/features/qdevice_setup_remove.feature
 test/features/qdevice_validate.feature
 test/features/resource_failcount.feature
+test/features/steps/const.py
 test/features/steps/__init__.py
 test/features/steps/step_implenment.py
 test/features/steps/utils.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/hb_report/utillib.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/hb_report/utillib.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/hb_report/utillib.py        
2020-01-07 10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/hb_report/utillib.py        
2020-01-13 11:23:24.000000000 +0100
@@ -1649,11 +1649,29 @@
     time_f = os.path.join(constants.WORKDIR, constants.TIME_F)
     crmutils.str2file(out_string, time_f)
 
+
+def dump_D_process():
+    '''
+    dump D-state process stack
+    '''
+    out_string = ""
+    _, out, _ = crmutils.get_stdout_stderr("ps aux|awk '$8 ~ /^D/{print $2}'")
+    len_D_process = len(out.split('\n')) if out else 0
+    out_string += "Dump D-state process stack: {}\n".format(len_D_process)
+    if len_D_process == 0:
+        return out_string
+    for pid in out.split('\n'):
+        _, cmd_out, _ = crmutils.get_stdout_stderr("cat 
/proc/{}/comm".format(pid))
+        out_string += "pid: {}     comm: {}\n".format(pid, cmd_out)
+        _, stack_out, _ = crmutils.get_stdout_stderr("cat 
/proc/{}/stack".format(pid))
+        out_string += stack_out + "\n\n"
+    return out_string
+
+
 def dump_ocfs2():
     ocfs2_f = os.path.join(constants.WORKDIR, constants.OCFS2_F)
     with open(ocfs2_f, "w") as f:
-        #dump all tasks stack into dmesg
-        os.system("echo t > /proc/sysrq-trigger")
+        f.write(dump_D_process())
 
         cmds = [ "dmesg",  "ps -efL", "lsof",
                 "lsblk -o 
'NAME,KNAME,MAJ:MIN,FSTYPE,LABEL,RO,RM,MODEL,SIZE,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,ROTA,SCHED,MOUNTPOINT'",
@@ -1670,6 +1688,7 @@
             f.write("# %s\n"%(cmd))
             f.write(out)
 
+
 def touch_dc():
     if constants.SKIP_LVL:
         return
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/test/features/bootstrap_bugs.feature 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/bootstrap_bugs.feature
--- 
old/crmsh-4.2.0+git.1578387778.867a085b/test/features/bootstrap_bugs.feature    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/bootstrap_bugs.feature    
    2020-01-13 11:23:24.000000000 +0100
@@ -0,0 +1,35 @@
+@bootstrap
+Feature: Regression test for bootstrap bugs
+
+  Tag @clean means need to stop cluster service if the service is available
+
+  @clean
+  Scenario: Set placement-strategy value as "default"(bsc#1129462)
+    Given   Cluster service is "stopped" on "hanode1"
+    And     Cluster service is "stopped" on "hanode2"
+    When    Run "crm cluster init -y --no-overwrite-sshkey" on "hanode1"
+    Then    Cluster service is "started" on "hanode1"
+    And     Show cluster status on "hanode1"
+    When    Run "crm cluster join -c hanode1 -y" on "hanode2"
+    Then    Cluster service is "started" on "hanode2"
+    And     Online nodes are "hanode1 hanode2"
+    And     Show cluster status on "hanode1"
+    When    Run "crm configure get_property placement-strategy" on "hanode1"
+    Then    Got output "default"
+
+  @clean
+  Scenario: Space value not allowed for option(bsc#1141976)
+    When    Try "crm -c ' '"
+    Then    Except "ERROR: Space value not allowed for dest "cib""
+    When    Try "crm cluster init --name ' '"
+    Then    Except "ERROR: cluster.init: Space value not allowed for dest 
"name""
+    When    Try "crm cluster join -c ' '"
+    Then    Except "ERROR: cluster.join: Space value not allowed for dest 
"cluster_node""
+    When    Try "crm cluster remove -c ' '"
+    Then    Except "ERROR: cluster.remove: Space value not allowed for dest 
"cluster_node""
+    When    Try "crm cluster geo-init -a ' '"
+    Then    Except "ERROR: cluster.geo_init: Space value not allowed for dest 
"arbitrator""
+    When    Try "crm cluster geo-join -c ' '"
+    Then    Except "ERROR: cluster.geo_join: Space value not allowed for dest 
"node""
+    When    Try "crm cluster geo-init-arbitrator -c ' '"
+    Then    Except "ERROR: cluster.geo_init_arbitrator: Space value not 
allowed for dest "other""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/test/features/bootstrap_options.feature 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/bootstrap_options.feature
--- 
old/crmsh-4.2.0+git.1578387778.867a085b/test/features/bootstrap_options.feature 
    2020-01-07 10:02:58.000000000 +0100
+++ 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/bootstrap_options.feature 
    2020-01-13 11:23:24.000000000 +0100
@@ -11,6 +11,25 @@
   Tag @clean means need to stop cluster service if the service is available
 
   @clean
+  Scenario: Check help output
+    When    Run "crm -h" on "hanode1"
+    Then    Output is the same with expected "crm" help output
+    When    Run "crm cluster init -h" on "hanode1"
+    Then    Output is the same with expected "crm cluster init" help output
+    When    Run "crm cluster join -h" on "hanode1"
+    Then    Output is the same with expected "crm cluster join" help output
+    When    Run "crm cluster add -h" on "hanode1"
+    Then    Output is the same with expected "crm cluster add" help output
+    When    Run "crm cluster remove -h" on "hanode1"
+    Then    Output is the same with expected "crm cluster remove" help output
+    When    Run "crm cluster geo-init -h" on "hanode1"
+    Then    Output is the same with expected "crm cluster geo-init" help output
+    When    Run "crm cluster geo-join -h" on "hanode1"
+    Then    Output is the same with expected "crm cluster geo-join" help output
+    When    Run "crm cluster geo-init-arbitrator -h" on "hanode1"
+    Then    Output is the same with expected "crm cluster geo-init-arbitrator" 
help output
+
+  @clean
   Scenario: Init whole cluster service on node "hanode1" using "--nodes" option
     Given   Cluster service is "stopped" on "hanode1"
     And     Cluster service is "stopped" on "hanode2"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/test/features/steps/const.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/steps/const.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/test/features/steps/const.py        
1970-01-01 01:00:00.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/steps/const.py        
2020-01-13 11:23:24.000000000 +0100
@@ -0,0 +1,241 @@
+CRM_H_OUTPUT = '''usage: crm [-h|--help] [OPTIONS] [SUBCOMMAND ARGS...]
+or crm help SUBCOMMAND
+
+For a list of available subcommands, use crm help.
+
+Use crm without arguments for an interactive session.
+Call a subcommand directly for a "single-shot" use.
+Call crm with a level name as argument to start an interactive
+session from that level.
+
+See the crm(8) man page or call crm help for more details.
+
+positional arguments:
+  SUBCOMMAND
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version             show program's version number and exit
+  -f FILE, --file FILE  Load commands from the given file. If a dash (-) is
+                        used in place of a file name, crm will read commands
+                        from the shell standard input (stdin).
+  -c CIB, --cib CIB     Start the session using the given shadow CIB file.
+                        Equivalent to `cib use <CIB>`.
+  -D OUTPUT_TYPE, --display OUTPUT_TYPE
+                        Choose one of the output options: plain, color-always,
+                        color, or uppercase. The default is color if the
+                        terminal emulation supports colors, else plain.
+  -F, --force           Make crm proceed with applying changes where it would
+                        normally ask the user to confirm before proceeding.
+                        This option is mainly useful in scripts, and should be
+                        used with care.
+  -n, --no              Automatically answer no when prompted
+  -w, --wait            Make crm wait for the cluster transition to finish
+                        (for the changes to take effect) after each processed
+                        line.
+  -H DIR|FILE|SESSION, --history DIR|FILE|SESSION
+                        A directory or file containing a cluster report to
+                        load into history, or the name of a previously saved
+                        history session.
+  -d, --debug           Print verbose debugging information.
+  -R, --regression-tests
+                        Enables extra verbose trace logging used by the
+                        regression tests. Logs all external calls made by
+                        crmsh.
+  --scriptdir DIR       Extra directory where crm looks for cluster scripts,
+                        or a list of directories separated by semi-colons
+                        (e.g. /dir1;/dir2;etc.).
+  -X PROFILE            Collect profiling data and save in PROFILE.
+  -o OPTION=VALUE, --opt OPTION=VALUE
+                        Set crmsh option temporarily. If the options are saved
+                        using+options save+ then the value passed here will
+                        also be saved.Multiple options can be set by using
+                        +-o+ multiple times.'''
+
+
+CRM_CLUSTER_INIT_H_OUTPUT = '''usage: init [options] [STAGE]
+
+optional arguments:
+  -h, --help            Show this help message
+  -q, --quiet           Be quiet (don't describe what's happening, just do it)
+  -y, --yes             Answer "yes" to all prompts (use with caution, this is
+                        destructive, especially during the "storage" stage.
+                        The /root/.ssh/id_rsa key will be overwritten unless
+                        the option "--no-overwrite-sshkey" is used)
+  -t TEMPLATE, --template TEMPLATE
+                        Optionally configure cluster with template "name"
+                        (currently only "ocfs2" is valid here)
+  -n NAME, --name NAME  Set the name of the configured cluster.
+  -N NODES, --nodes NODES
+                        Additional nodes to add to the created cluster. May
+                        include the current node, which will always be the
+                        initial cluster node.
+  -S, --enable-sbd      Enable SBD even if no SBD device is configured
+                        (diskless mode)
+  -w WATCHDOG, --watchdog WATCHDOG
+                        Use the given watchdog device
+  --no-overwrite-sshkey
+                        Avoid "/root/.ssh/id_rsa" overwrite if "-y" option is
+                        used (False by default)
+
+Network configuration:
+  Options for configuring the network and messaging layer.
+
+  -i IF, --interface IF
+                        Bind to IP address on interface IF
+  -u, --unicast         Configure corosync to communicate over unicast (UDP),
+                        and not multicast. Default is multicast unless an
+                        environment where multicast cannot be used is
+                        detected.
+  -A IP, --admin-ip IP  Configure IP address as an administration virtual IP
+  -M, --multi-heartbeats
+                        Configure corosync with second heartbeat line
+  -I, --ipv6            Configure corosync use IPv6
+  --qnetd-hostname HOST
+                        HOST or IP of the QNetd server to be used
+  --qdevice-port PORT   TCP PORT of QNetd server(default:5403)
+  --qdevice-algo ALGORITHM
+                        QNetd decision ALGORITHM(ffsplit/lms, default:ffsplit)
+  --qdevice-tie-breaker TIE_BREAKER
+                        QNetd TIE_BREAKER(lowest/highest/valid_node_id,
+                        default:lowest)
+  --qdevice-tls TLS     Whether using TLS on QDevice/QNetd(on/off/required,
+                        default:on)
+  --qdevice-heuristics COMMAND
+                        COMMAND to run with absolute path. For multiple
+                        commands, use ";" to separate(details about heuristics
+                        can see man 8 corosync-qdevice)
+
+Storage configuration:
+  Options for configuring shared storage.
+
+  -p DEVICE, --partition-device DEVICE
+                        Partition this shared storage device (only used in
+                        "storage" stage)
+  -s DEVICE, --sbd-device DEVICE
+                        Block device to use for SBD fencing, use ";" as
+                        separator or -s multiple times for multi path (up to 3
+                        devices)
+  -o DEVICE, --ocfs2-device DEVICE
+                        Block device to use for OCFS2 (only used in "vgfs"
+                        stage)
+
+Stage can be one of:
+    ssh         Create SSH keys for passwordless SSH between cluster nodes
+    csync2      Configure csync2
+    corosync    Configure corosync
+    storage     Partition shared storage (ocfs2 template only)
+    sbd         Configure SBD (requires -s <dev>)
+    cluster     Bring the cluster online
+    vgfs        Create volume group and filesystem (ocfs2 template only,
+                requires -o <dev>)
+    admin       Create administration virtual IP (optional)
+    qdevice     Configure qdevice and qnetd
+
+Note:
+  - If stage is not specified, the script will run through each stage
+    in sequence, with prompts for required information.
+  - If using the ocfs2 template, the storage stage will partition a block
+    device into two pieces, one for SBD, the remainder for OCFS2.  This is
+    good for testing and demonstration, but not ideal for production.
+    To use storage you have already configured, pass -s and -o to specify
+    the block devices for SBD and OCFS2, and the automatic partitioning
+    will be skipped.'''
+
+
+CRM_CLUSTER_JOIN_H_OUTPUT = '''usage: join [options] [STAGE]
+
+optional arguments:
+  -h, --help            Show this help message
+  -q, --quiet           Be quiet (don't describe what's happening, just do it)
+  -y, --yes             Answer "yes" to all prompts (use with caution)
+  -w WATCHDOG, --watchdog WATCHDOG
+                        Use the given watchdog device
+
+Network configuration:
+  Options for configuring the network and messaging layer.
+
+  -c HOST, --cluster-node HOST
+                        IP address or hostname of existing cluster node
+  -i IF, --interface IF
+                        Bind to IP address on interface IF
+
+Stage can be one of:
+    ssh         Obtain SSH keys from existing cluster node (requires -c <host>)
+    csync2      Configure csync2 (requires -c <host>)
+    ssh_merge   Merge root's SSH known_hosts across all nodes (csync2 must
+                already be configured).
+    cluster     Start the cluster on this node
+
+If stage is not specified, each stage will be invoked in sequence.'''
+
+
+CRM_CLUSTER_ADD_H_OUTPUT = '''usage: add [options] [node ...]
+
+optional arguments:
+  -h, --help  Show this help message
+  -y, --yes   Answer "yes" to all prompts (use with caution)'''
+
+
+CRM_CLUSTER_REMOVE_H_OUTPUT = '''usage: remove [options] [<node> ...]
+
+optional arguments:
+  -h, --help            Show this help message
+  -q, --quiet           Be quiet (don't describe what's happening, just do it)
+  -y, --yes             Answer "yes" to all prompts (use with caution)
+  -c HOST, --cluster-node HOST
+                        IP address or hostname of cluster node which will be
+                        deleted
+  -F, --force           Remove current node
+  --qdevice             Remove QDevice configuration and service from 
cluster'''
+
+
+CRM_CLUSTER_GEO_INIT_H_OUTPUT = '''usage: geo-init [options]
+
+optional arguments:
+  -h, --help            Show this help message
+  -q, --quiet           Be quiet (don't describe what's happening, just do it)
+  -y, --yes             Answer "yes" to all prompts (use with caution)
+  -a IP, --arbitrator IP
+                        IP address of geo cluster arbitrator
+  -s DESC, --clusters DESC
+                        Geo cluster description (see details below)
+  -t LIST, --tickets LIST
+                        Tickets to create (space-separated)
+
+Cluster Description
+
+  This is a map of cluster names to IP addresses.
+  Each IP address will be configured as a virtual IP
+  representing that cluster in the geo cluster
+  configuration.
+
+  Example with two clusters named paris and amsterdam:
+
+  --clusters "paris=192.168.10.10 amsterdam=192.168.10.11"
+
+  Name clusters using the --name parameter to
+  crm bootstrap init.'''
+
+
+CRM_CLUSTER_GEO_JOIN_H_OUTPUT = '''usage: geo-join [options]
+
+optional arguments:
+  -h, --help            Show this help message
+  -q, --quiet           Be quiet (don't describe what's happening, just do it)
+  -y, --yes             Answer "yes" to all prompts (use with caution)
+  -c IP, --cluster-node IP
+                        IP address of an already-configured geo cluster or
+                        arbitrator
+  -s DESC, --clusters DESC
+                        Geo cluster description (see geo-init for details)'''
+
+
+CRM_CLUSTER_GEO_INIT_ARBIT_H_OUTPUT = '''usage: geo-init-arbitrator [options]
+
+optional arguments:
+  -h, --help            Show this help message
+  -q, --quiet           Be quiet (don't describe what's happening, just do it)
+  -y, --yes             Answer "yes" to all prompts (use with caution)
+  -c IP, --cluster-node IP
+                        IP address of an already-configured geo cluster'''
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/test/features/steps/step_implenment.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/steps/step_implenment.py
--- 
old/crmsh-4.2.0+git.1578387778.867a085b/test/features/steps/step_implenment.py  
    2020-01-07 10:02:58.000000000 +0100
+++ 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/features/steps/step_implenment.py  
    2020-01-13 11:23:24.000000000 +0100
@@ -4,6 +4,7 @@
 from crmsh import corosync, parallax
 from utils import check_cluster_state, check_service_state, online, 
run_command, me, \
                   run_command_local_or_remote
+import const
 
 
 @given('Cluster service is "{state}" on "{addr}"')
@@ -30,7 +31,8 @@
 
 @when('Run "{cmd}" on "{addr}"')
 def step_impl(context, cmd, addr):
-    run_command_local_or_remote(context, cmd, addr)
+    out = run_command_local_or_remote(context, cmd, addr)
+    context.stdout = out
 
 
 @when('Try "{cmd}"')
@@ -43,6 +45,12 @@
     time.sleep(int(second))
 
 
+@then('Got output "{msg}"')
+def step_impl(context, msg):
+    assert context.stdout == msg
+    context.stdout = None
+
+
 @then('Except "{msg}"')
 def step_impl(context, msg):
     assert context.command_error_output == msg
@@ -140,3 +148,18 @@
     if out:
         result = re.search(r'name=fail-count-{} value={}'.format(res, number), 
out)
         assert result is not None
+
+
+@then('Output is the same with expected "{cmd}" help output')
+def step_impl(context, cmd):
+    cmd_help = {}
+    cmd_help["crm"] = const.CRM_H_OUTPUT
+    cmd_help["crm_cluster_init"] = const.CRM_CLUSTER_INIT_H_OUTPUT
+    cmd_help["crm_cluster_join"] = const.CRM_CLUSTER_JOIN_H_OUTPUT
+    cmd_help["crm_cluster_add"] = const.CRM_CLUSTER_ADD_H_OUTPUT
+    cmd_help["crm_cluster_remove"] = const.CRM_CLUSTER_REMOVE_H_OUTPUT
+    cmd_help["crm_cluster_geo-init"] = const.CRM_CLUSTER_GEO_INIT_H_OUTPUT
+    cmd_help["crm_cluster_geo-join"] = const.CRM_CLUSTER_GEO_JOIN_H_OUTPUT
+    cmd_help["crm_cluster_geo-init-arbitrator"] = 
const.CRM_CLUSTER_GEO_INIT_ARBIT_H_OUTPUT
+    key = '_'.join(cmd.split())
+    assert context.stdout == cmd_help[key]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/test/unittests/__init__.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/unittests/__init__.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/test/unittests/__init__.py  
2020-01-07 10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/test/unittests/__init__.py  
2020-01-13 11:23:24.000000000 +0100
@@ -30,7 +30,6 @@
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair name="stonith-enabled" value="false" 
id="cib-bootstrap-options-stonith-enabled"/>
         <nvpair name="no-quorum-policy" value="ignore" 
id="cib-bootstrap-options-no-quorum-policy"/>
-        <nvpair name="placement-strategy" value="balanced" 
id="cib-bootstrap-options-placement-strategy"/>
         <nvpair name="dc-version" 
value="1.1.11+git20140221.0b7d85a-115.1-1.1.11+git20140221.0b7d85a" 
id="cib-bootstrap-options-dc-version"/>
         <nvpair name="cluster-infrastructure" value="corosync" 
id="cib-bootstrap-options-cluster-infrastructure"/>
         <nvpair name="symmetric-cluster" value="true" 
id="cib-bootstrap-options-symmetric-cluster"/>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/test/unittests/test_bugs.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/unittests/test_bugs.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/test/unittests/test_bugs.py 
2020-01-07 10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/test/unittests/test_bugs.py 
2020-01-13 11:23:24.000000000 +0100
@@ -790,8 +790,7 @@
        cluster-infrastructure=corosync \
        cluster-name=hacluster \
        stonith-enabled=true \
-       no-quorum-policy=ignore \
-       placement-strategy=balanced
+       no-quorum-policy=ignore
 rsc_defaults rsc-options: \
        resource-stickiness=1 \
        migration-threshold=3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1578387778.867a085b/test/unittests/test_report.py 
new/crmsh-4.2.0+git.1578911004.c1a33535/test/unittests/test_report.py
--- old/crmsh-4.2.0+git.1578387778.867a085b/test/unittests/test_report.py       
2020-01-07 10:02:58.000000000 +0100
+++ new/crmsh-4.2.0+git.1578911004.c1a33535/test/unittests/test_report.py       
2020-01-13 11:23:24.000000000 +0100
@@ -3,6 +3,7 @@
 import shutil
 from datetime import datetime
 from nose.tools import eq_, ok_
+from unittest import mock
 
 from hb_report.utillib import which, ts_to_dt, sub_string, random_string,\
                               head, create_tempfile, tail, grep,\
@@ -14,6 +15,7 @@
                               findln_by_time, get_conf_var, is_conf_set,\
                               line_time, get_command_info, Tempfile
 from hb_report import constants
+import hb_report
 import crmsh.utils
 
 
@@ -364,3 +366,30 @@
 def test_which():
     ok_(which("ls"))
     ok_(not which("llll"))
+
+
+@mock.patch('crmsh.utils.get_stdout_stderr')
+def test_dump_D_process_None(mock_get_stdout_stderr):
+    mock_get_stdout_stderr.return_value = (0, None, None)
+    eq_(hb_report.utillib.dump_D_process(), "Dump D-state process stack: 0\n")
+    mock_get_stdout_stderr.assert_called_once_with("ps aux|awk '$8 ~ 
/^D/{print $2}'")
+
+
+@mock.patch('crmsh.utils.get_stdout_stderr')
+def test_dump_D_process_None(mock_get_stdout_stderr):
+    mock_get_stdout_stderr.side_effect = [
+            (0, "10001\n10002", None),
+            (0, "comm_out for 10001", None),
+            (0, "stack_out for 10001", None),
+            (0, "comm_out for 10002", None),
+            (0, "stack_out for 10002", None)
+            ]
+    out_string = "Dump D-state process stack: 2\npid: 10001     comm: comm_out 
for 10001\nstack_out for 10001\n\npid: 10002     comm: comm_out for 
10002\nstack_out for 10002\n\n"
+    eq_(hb_report.utillib.dump_D_process(), out_string)
+    mock_get_stdout_stderr.assert_has_calls([
+        mock.call("ps aux|awk '$8 ~ /^D/{print $2}'"),
+        mock.call("cat /proc/10001/comm"),
+        mock.call("cat /proc/10001/stack"),
+        mock.call("cat /proc/10002/comm"),
+        mock.call("cat /proc/10002/stack")
+        ])


Reply via email to