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 - [email protected] + +- 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 - [email protected] + +- 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 - [email protected] + +- 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 - [email protected] + +- 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 - [email protected] + +- 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 <[email protected]> # 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")) + + [email protected]('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}'") + + [email protected]('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") + ])
