Hello community, here is the log from the commit of package crmsh for openSUSE:Leap:15.2 checked in at 2020-03-15 07:11:49 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/crmsh (Old) and /work/SRC/openSUSE:Leap:15.2/.crmsh.new.3160 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Sun Mar 15 07:11:49 2020 rev:70 rq:784743 version:4.2.0+git.1584013187.b45cfcb6 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/crmsh/crmsh.changes 2020-02-11 23:27:28.125333734 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.crmsh.new.3160/crmsh.changes 2020-03-15 07:11:54.304982884 +0100 @@ -1,0 +2,10 @@ +Fri Mar 13 12:27:32 UTC 2020 - [email protected] + +- Update to version 4.2.0+git.1584013187.b45cfcb6: + * Fix: crmsh.spec.in: enable completion of crm command(bsc#1166329) + * Low: crmsh.spec.in: sync contents from NHF's crmsh.spec file + * Low: corosync: check whether local ip has already configured + * Low: bootstrap: check whether init node is online while joining + * Low: bootstrap: for udpu, don't check join node's ip was in the same network + +------------------------------------------------------------------- Old: ---- crmsh-4.2.0+git.1580544897.c42c9530.tar.bz2 New: ---- crmsh-4.2.0+git.1584013187.b45cfcb6.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.Q8VDgZ/_old 2020-03-15 07:11:54.704983123 +0100 +++ /var/tmp/diff_new_pack.Q8VDgZ/_new 2020-03-15 07:11:54.708983126 +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.1580544897.c42c9530 +Version: 4.2.0+git.1584013187.b45cfcb6 Release: 0 Url: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 @@ -106,7 +106,7 @@ Requires(post): mailx Requires(post): procps Requires(post): python3-python-dateutil -Requires(post): python3-nose +Requires(post): python3-tox Requires(post): python3-parallax Requires(post): pacemaker %if 0%{?suse_version} > 1110 @@ -160,7 +160,7 @@ make %{_smp_mflags} VERSION="%{version}" sysconfdir=%{_sysconfdir} localstatedir=%{_var} %if %{with regression_tests} -./test/run --quiet +tox if [ ! $? ]; then echo "Unit tests failed." exit 1 @@ -169,7 +169,7 @@ %install make DESTDIR=%{buildroot} docdir=%{crmsh_docdir} install -install -Dm0644 contrib/bash_completion.sh %{buildroot}%{_datadir}/bash-completion/completions/crm.sh +install -Dm0644 contrib/bash_completion.sh %{buildroot}%{_datadir}/bash-completion/completions/crm if [ -f %{buildroot}%{_bindir}/crm ]; then install -Dm0755 %{buildroot}%{_bindir}/crm %{buildroot}%{_sbindir}/crm rm %{buildroot}%{_bindir}/crm @@ -182,7 +182,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..." @@ -226,7 +226,7 @@ %dir %{crmsh_docdir} %dir %{crmsh_docdir}/contrib %dir %attr (770, %{uname}, %{gname}) %{_var}/cache/crm -%{_datadir}/bash-completion/completions/crm.sh +%{_datadir}/bash-completion/completions/crm %files scripts %defattr(-,root,root) ++++++ _service ++++++ --- /var/tmp/diff_new_pack.Q8VDgZ/_old 2020-03-15 07:11:54.740983145 +0100 +++ /var/tmp/diff_new_pack.Q8VDgZ/_new 2020-03-15 07:11:54.740983145 +0100 @@ -4,7 +4,7 @@ <param name="scm">git</param> <param name="filename">crmsh</param> <param name="versionformat">4.2.0+git.%ct.%h</param> - <param name="revision">master</param> + <param name="revision">b45cfcb6</param> <param name="changesgenerate">enable</param> </service> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.Q8VDgZ/_old 2020-03-15 07:11:54.756983154 +0100 +++ /var/tmp/diff_new_pack.Q8VDgZ/_new 2020-03-15 07:11:54.756983154 +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">a8fc76646a14dfcf7b34e62303d2195ef96c3daa</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">b256772e34cde162c8245581097d13070ae51254</param></service></servicedata> \ No newline at end of file ++++++ crmsh-4.2.0+git.1580544897.c42c9530.tar.bz2 -> crmsh-4.2.0+git.1584013187.b45cfcb6.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/configure.ac new/crmsh-4.2.0+git.1584013187.b45cfcb6/configure.ac --- old/crmsh-4.2.0+git.1580544897.c42c9530/configure.ac 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/configure.ac 2020-03-12 12:39:47.000000000 +0100 @@ -8,7 +8,7 @@ AC_PREREQ([2.53]) -AC_INIT([crmsh],[4.0.0],[[email protected]]) +AC_INIT([crmsh],[4.2.0],[[email protected]]) AC_ARG_WITH(version, [ --with-version=version Override package version (if you're a packager needing to pretend) ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh/bootstrap.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh/bootstrap.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh/bootstrap.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh/bootstrap.py 2020-03-12 12:39:47.000000000 +0100 @@ -18,6 +18,7 @@ import re import time import readline +import shutil from string import Template from lxml import etree from . import config @@ -38,6 +39,7 @@ SYSCONFIG_FW = "/etc/sysconfig/SuSEfirewall2" SYSCONFIG_FW_CLUSTER = "/etc/sysconfig/SuSEfirewall2.d/services/cluster" PCMK_REMOTE_AUTH = "/etc/pacemaker/authkey" +COROSYNC_CONF_ORIG = tmpfiles.create()[1] INIT_STAGES = ("ssh", "ssh_remote", "csync2", "csync2_remote", "corosync", "storage", "sbd", "cluster", "vgfs", "admin", "qdevice") @@ -228,13 +230,53 @@ status_long("Waiting for cluster") while True: _rc, out, _err = utils.get_stdout_stderr("crm_mon -1") - if "online" in out.lower(): + if is_online(out): break status_progress() - sleep(5) + sleep(2) status_done() +def get_cluster_node_hostname(): + """ + Get the hostname of the cluster node used during the join process if an IP address is used. + """ + peer_node = None + if _context.cluster_node: + if utils.valid_ip_addr(_context.cluster_node): + rc, out, err = utils.get_stdout_stderr("ssh {} crm_node --name".format(_context.cluster_node)) + if rc != 0: + error(err) + peer_node = out + else: + peer_node = _context.cluster_node + return peer_node + + +def is_online(crm_mon_txt): + """ + Check whether local node is online + Besides that, in join process, check whether init node is online + """ + if not re.search("Online: .* {} ".format(utils.this_node()), crm_mon_txt): + return False + + # if peer_node is None, this is in the init process + peer_node = get_cluster_node_hostname() + if peer_node is None: + return True + # In join process + # If the joining node is already online but can't find the init node + # The communication IP maybe mis-configured + if not re.search("Online: .* {} ".format(peer_node), crm_mon_txt): + shutil.copy(COROSYNC_CONF_ORIG, corosync.conf()) + csync2_update(corosync.conf()) + stop_service("corosync") + print() + error("Cannot see peer node \"{}\", please check the communication IP".format(peer_node)) + return True + + def pick_default_value(default_list, prev_list): """ Provide default value for function 'prompt_for_string'. @@ -1846,6 +1888,8 @@ else: corosync.set_value("totem.nodeid", nodeid) + shutil.copy(corosync.conf(), COROSYNC_CONF_ORIG) + # check if use IPv6 ipv6_flag = False ipv6 = corosync.get_value("totem.ip_version") @@ -1909,21 +1953,6 @@ ringXaddr_res) if not ringXaddr: error("No value for ring{}".format(i)) - - # this check does not work on GCP (bsc#1106946) - if utils.detect_cloud() != "google-cloud-platform": - _, outp = utils.get_stdout("ip addr show") - tmp = re.findall(r' {}/[0-9]+ '.format(ringXaddr), outp, re.M)[0].strip() - peer_ip = corosync.get_value("nodelist.node.ring{}_addr".format(i)) - # peer ring0_addr and local ring0_addr must be configured in the same network - if not utils.ip_in_network(peer_ip, tmp): - errmsg = " Peer IP {} is not in the same network: {}".format(peer_ip, tmp) - if _context.yes_to_all: - error(errmsg) - else: - print(term.render(clidisplay.error(errmsg))) - continue - ringXaddr_res.append(ringXaddr) break if not rrp_flag: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh/corosync.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh/corosync.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh/corosync.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh/corosync.py 2020-03-12 12:39:47.000000000 +0100 @@ -811,24 +811,47 @@ pass -def add_node_ucast(IParray, node_id=None): +def find_configured_ip(ip_list): + """ + find if the same IP already configured + If so, raise IPAlreadyConfiguredError + """ + with open(conf()) as f: + p = Parser(f.read()) + + # get exist ip list from corosync.conf + corosync_iplist = [] + for path in set(p.all_paths()): + if re.search('nodelist.node.ring[0-9]*_addr', path): + corosync_iplist.extend(p.get_all(path)) + + # all_possible_ip is a ip set to check whether one of them already configured + all_possible_ip = set(ip_list) + # get local ip list + local_ip_list = utils.ip_in_local(utils.is_ipv6(ip_list[0])) + # extend all_possible_ip if ip_list contain local ip + # to avoid this scenarios in join node: + # eth0's ip already configured in corosync.conf + # eth1's ip also want to add in nodelist + # if this scenarios happened, raise IPAlreadyConfiguredError + if bool(set(ip_list) & set(local_ip_list)): + all_possible_ip |= set(local_ip_list) + configured_ip = list(all_possible_ip & set(corosync_iplist)) + if configured_ip: + raise IPAlreadyConfiguredError("IP {} was already configured".format(','.join(configured_ip))) + + +def add_node_ucast(ip_list, node_id=None): + + find_configured_ip(ip_list) f = open(conf()).read() p = Parser(f) - # to check if the same IP already configured - exist_iplist = [] - for path in p.all_paths(): - if re.search('nodelist.node.ring[0-9]*_addr', path): - exist_iplist.extend(p.get_all(path)) - for ip in IParray: - if ip in exist_iplist: - raise IPAlreadyConfiguredError("IP {} was already configured".format(ip)) - if node_id is None: node_id = get_free_nodeid(p) node_value = [] - for i, addr in enumerate(IParray): + for i, addr in enumerate(ip_list): node_value += make_value('nodelist.node.ring{}_addr'.format(i), addr) node_value += make_value('nodelist.node.nodeid', str(node_id)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh/utils.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh/utils.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh/utils.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh/utils.py 2020-03-12 12:39:47.000000000 +0100 @@ -2083,6 +2083,11 @@ def version(self): return self.addr.version + +def is_ipv6(addr): + return IP(addr).version() == 6 + + # Set by detect_cloud() or iplist_for_cloud() # to avoid multiple requests _ip_for_cloud = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh.spec.in new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh.spec.in --- old/crmsh-4.2.0+git.1580544897.c42c9530/crmsh.spec.in 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/crmsh.spec.in 2020-03-12 12:39:47.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package crmsh # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,7 +12,7 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # @@ -34,7 +34,7 @@ Name: crmsh Summary: High Availability cluster command-line interface -License: GPL-2.0+ +License: GPL-2.0-or-later Group: %{pkg_group} Version: @VERSION@ Release: 0 @@ -49,14 +49,10 @@ %endif Requires: %{name}-scripts >= %{version}-%{release} Requires: /usr/bin/which -Requires: python3 >= 3.6 +Requires: python3 >= 3.4 Requires: python3-lxml -Requires: python3-python-dateutil -%if 0%{?suse_version} > 1320 Requires: python3-parallax -%else -Requires: python-parallax -%endif +Requires: python3-python-dateutil BuildRequires: python3-lxml BuildRequires: python3-setuptools @@ -111,11 +107,7 @@ Requires(post): procps Requires(post): python3-python-dateutil Requires(post): python3-tox -%if 0%{?suse_version} > 1320 Requires(post): python3-parallax -%else -Requires(post): python-parallax -%endif Requires(post): pacemaker %if 0%{?suse_version} > 1110 BuildArch: noarch @@ -150,23 +142,12 @@ %prep %setup -q -touch -r doc/crm.8.adoc{,.timestamp} # replace the shebang in all the scripts # with ${_bindir}/python3 find . -type f -exec perl -pi -e 'BEGIN{undef $/};s[^#\!/usr/bin/python[3]?][#\!%{_bindir}/python3]' {} \; find . -type f -exec perl -pi -e 'BEGIN{undef $/};s[^#\!/usr/bin/env python[3]?][#\!%{_bindir}/python3]' {} \; -# Force the local time -# -# 'hg archive' sets the file date to the date of the last commit. -# This can result in files having been created in the future -# when building on machines in timezones 'behind' the one the -# commit occurred in - which seriously confuses 'make' -find . -mtime -0 -exec touch \{\} \; -# keep the .adoc mtime because it gets embedded in crm.8.html -touch -r doc/crm.8.adoc{.timestamp,} - %build ./autogen.sh @@ -179,7 +160,7 @@ make %{_smp_mflags} VERSION="%{version}" sysconfdir=%{_sysconfdir} localstatedir=%{_var} %if %{with regression_tests} -tox -q +tox if [ ! $? ]; then echo "Unit tests failed." exit 1 @@ -188,7 +169,7 @@ %install make DESTDIR=%{buildroot} docdir=%{crmsh_docdir} install -install -Dm0644 contrib/bash_completion.sh %{buildroot}%{_sysconfdir}/bash_completion.d/crm.sh +install -Dm0644 contrib/bash_completion.sh %{buildroot}%{_datadir}/bash-completion/completions/crm if [ -f %{buildroot}%{_bindir}/crm ]; then install -Dm0755 %{buildroot}%{_bindir}/crm %{buildroot}%{_sbindir}/crm rm %{buildroot}%{_bindir}/crm @@ -245,7 +226,7 @@ %dir %{crmsh_docdir} %dir %{crmsh_docdir}/contrib %dir %attr (770, %{uname}, %{gname}) %{_var}/cache/crm -%config %{_sysconfdir}/bash_completion.d/crm.sh +%{_datadir}/bash-completion/completions/crm %files scripts %defattr(-,root,root) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/setup.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/setup.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/setup.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/setup.py 2020-03-12 12:39:47.000000000 +0100 @@ -4,7 +4,7 @@ from setuptools import setup setup(name='crmsh', - version='4.0.0', + version='4.2.0', description='Command-line interface for High-Availability cluster management', author='Kristoffer Gronlund', author_email='[email protected]', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/features/bootstrap_bugs.feature new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/features/bootstrap_bugs.feature --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/features/bootstrap_bugs.feature 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/features/bootstrap_bugs.feature 2020-03-12 12:39:47.000000000 +0100 @@ -33,3 +33,15 @@ 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"" + + @clean + Scenario: Setup cluster with crossed network(udpu only) + Given Cluster service is "stopped" on "hanode1" + Given Cluster service is "stopped" on "hanode2" + When Run "crm cluster init -u -i eth0 -y --no-overwrite-sshkey" on "hanode1" + Then Cluster service is "started" on "hanode1" + When Try "crm cluster join -c hanode1 -i eth1 -y" on "hanode2" + Then Cluster service is "stopped" on "hanode2" + And Except "Cannot see peer node "hanode1", please check the communication IP" in stderr + When Run "crm cluster join -c hanode1 -i eth0 -y" on "hanode2" + Then Cluster service is "started" on "hanode2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/features/steps/step_implenment.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/features/steps/step_implenment.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/features/steps/step_implenment.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/features/steps/step_implenment.py 2020-03-12 12:39:47.000000000 +0100 @@ -35,6 +35,11 @@ context.stdout = out +@when('Try "{cmd}" on "{addr}"') +def step_impl(context, cmd, addr): + run_command_local_or_remote(context, cmd, addr, err_record=True) + + @when('Try "{cmd}"') def step_impl(context, cmd): run_command(context, cmd, err_record=True) @@ -69,6 +74,12 @@ context.command_error_output = None +@then('Except "{msg}" in stderr') +def step_impl(context, msg): + assert msg in context.command_error_output + context.command_error_output = None + + @then('Cluster service is "{state}" on "{addr}"') def step_impl(context, state, addr): assert check_cluster_state(context, state, addr) is True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/features/steps/utils.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/features/steps/utils.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/features/steps/utils.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/features/steps/utils.py 2020-03-12 12:39:47.000000000 +0100 @@ -19,14 +19,17 @@ return out -def run_command_local_or_remote(context, cmd, addr): +def run_command_local_or_remote(context, cmd, addr, err_record=False): if addr == me(): - out = run_command(context, cmd) + out = run_command(context, cmd, err_record) return out else: try: results = parallax.parallax_call([addr], cmd) except ValueError as err: + if err_record: + context.command_error_output = str(err) + return context.logger.error("\n{}\n".format(err)) context.failed = True else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/testcases/ra.exp new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/testcases/ra.exp --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/testcases/ra.exp 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/testcases/ra.exp 2020-03-12 12:39:47.000000000 +0100 @@ -9,16 +9,10 @@ .EXT crm_resource --show-metadata ocf:pacemaker:Dummy Example stateless resource agent (ocf:pacemaker:Dummy) -This is a Dummy Resource Agent. It does absolutely nothing except -keep track of whether its running or not. -Its purpose in life is for testing and to serve as a template for RA writers. - -NB: Please pay attention to the timeouts specified in the actions -section below. They should be meaningful for the kind of resource -the agent manages. They should be the minimum advised timeouts, -but they shouldn't/cannot cover _all_ possible resource -instances. So, try to be neither overly generous nor too stingy, -but moderate. The minimum timeouts should never be below 10 seconds. +This is a dummy OCF resource agent. It does absolutely nothing except keep track +of whether it is running or not, and can be configured so that actions fail or +take a long time. Its purpose is primarily for testing, and to serve as a +template for resource agent writers. Parameters (*: required, []: default): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/testcases/resource.exp new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/testcases/resource.exp --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/testcases/resource.exp 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/testcases/resource.exp 2020-03-12 12:39:47.000000000 +0100 @@ -914,7 +914,7 @@ .TRY configure ms msg g .TRY resource scores .EXT crm_simulate -sUL -3 of 6 resources DISABLED and 0 BLOCKED from being started due to failures +2 of 6 resource instances DISABLED and 0 BLOCKED from further action due to failure Current cluster status: Node node1: UNCLEAN (offline) @@ -930,24 +930,24 @@ Allocation scores and utilization information: Original: node1 capacity: -native_color: st allocation score on node1: 0 -clone_color: c1 allocation score on node1: 0 -clone_color: p1:0 allocation score on node1: 0 -native_color: p1:0 allocation score on node1: -INFINITY -clone_color: m1 allocation score on node1: 0 -clone_color: p2:0 allocation score on node1: 0 -native_color: p2:0 allocation score on node1: -INFINITY +pcmk__native_allocate: st allocation score on node1: 0 +pcmk__clone_allocate: c1 allocation score on node1: 0 +pcmk__clone_allocate: p1:0 allocation score on node1: 0 +pcmk__native_allocate: p1:0 allocation score on node1: -INFINITY +pcmk__clone_allocate: m1 allocation score on node1: 0 +pcmk__clone_allocate: p2:0 allocation score on node1: 0 +pcmk__native_allocate: p2:0 allocation score on node1: -INFINITY p2:0 promotion score on none: 0 -native_color: p3 allocation score on node1: -INFINITY -clone_color: msg allocation score on node1: 0 -clone_color: g:0 allocation score on node1: 0 -clone_color: p0:0 allocation score on node1: 0 -clone_color: p4:0 allocation score on node1: 0 -group_color: g:0 allocation score on node1: -INFINITY -group_color: p0:0 allocation score on node1: -INFINITY -group_color: p4:0 allocation score on node1: -INFINITY -native_color: p0:0 allocation score on node1: -INFINITY -native_color: p4:0 allocation score on node1: -INFINITY +pcmk__native_allocate: p3 allocation score on node1: -INFINITY +pcmk__clone_allocate: msg allocation score on node1: 0 +pcmk__clone_allocate: g:0 allocation score on node1: 0 +pcmk__clone_allocate: p0:0 allocation score on node1: 0 +pcmk__clone_allocate: p4:0 allocation score on node1: 0 +pcmk__group_allocate: g:0 allocation score on node1: -INFINITY +pcmk__group_allocate: p0:0 allocation score on node1: -INFINITY +pcmk__group_allocate: p4:0 allocation score on node1: -INFINITY +pcmk__native_allocate: p0:0 allocation score on node1: -INFINITY +pcmk__native_allocate: p4:0 allocation score on node1: -INFINITY g:0 promotion score on none: 0 Remaining: node1 capacity: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/testcases/scripts.exp new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/testcases/scripts.exp --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/testcases/scripts.exp 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/testcases/scripts.exp 2020-03-12 12:39:47.000000000 +0100 @@ -287,7 +287,7 @@ ** localhost - crm --wait --no configure load update <<temporary file>> OK: 10: Configure cluster resources .INP: json '["show", "mailto"]' -{"category": "basic", "longdesc": "Notifies recipient by e-mail in the event of a resource takeover.", "name": "mailto", "shortdesc": "E-Mail", "steps": [{"longdesc": " This is a resource agent for MailTo. It sends email to a sysadmin\nwhenever a takeover occurs.", "parameters": [{"advanced": false, "longdesc": "", "name": "id", "required": true, "shortdesc": "Identifier for the cluster resource", "type": "resource", "unique": true}, {"advanced": false, "example": "", "longdesc": " The email address of sysadmin.", "name": "email", "required": true, "shortdesc": "Email address", "type": "email", "unique": false}, {"advanced": false, "example": "", "longdesc": " The subject of the email.", "name": "subject", "required": false, "shortdesc": "Subject", "type": "string", "unique": false}], "required": true, "shortdesc": "Notifies recipients by email in the event of resource takeover"}]} +{"category": "basic", "longdesc": "Notifies recipient by e-mail in the event of a resource takeover.", "name": "mailto", "shortdesc": "E-Mail", "steps": [{"longdesc": " This is a resource agent for MailTo. It sends email to a sysadmin\nwhenever a takeover occurs.", "parameters": [{"advanced": false, "longdesc": "", "name": "id", "required": true, "shortdesc": "Identifier for the cluster resource", "type": "resource", "unique": true}, {"advanced": false, "example": "0", "longdesc": " The email address of sysadmin.", "name": "email", "required": true, "shortdesc": "Email address", "type": "email", "unique": false}, {"advanced": false, "example": "Resource Group", "longdesc": " The subject of the email.", "name": "subject", "required": false, "shortdesc": "Subject", "type": "string", "unique": false}], "required": true, "shortdesc": "Notifies recipients by email in the event of resource takeover"}]} .INP: json '["verify", "mailto", {"id":"foo", "email":"[email protected]", "subject":"hello"}]' {"longdesc": "", "name": "install", "nodes": "", "shortdesc": "Ensure mail package is installed", "text": "mailx"} {"longdesc": "", "name": "cib", "nodes": "", "shortdesc": "Configure cluster resources", "text": "primitive foo ocf:heartbeat:MailTo\n\temail=\"[email protected]\"\n\tsubject=\"hello\"\n\top start timeout=\"10\"\n\top stop timeout=\"10\"\n\top monitor interval=\"10\" timeout=\"10\"\n\nclone c-foo foo"} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/unittests/test_bootstrap.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/unittests/test_bootstrap.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/unittests/test_bootstrap.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/unittests/test_bootstrap.py 2020-03-12 12:39:47.000000000 +0100 @@ -32,7 +32,6 @@ Global setUp. """ - #@mock.patch('crmsh.bootstrap.Context') def setUp(self): """ Test setUp. @@ -158,3 +157,128 @@ mock.call(['10.10.10.1', '20.20.20.1'], '1'), mock.call(['10.10.10.2', '20.20.20.2'], '2') ]) + + @mock.patch('crmsh.utils.valid_ip_addr') + @mock.patch('crmsh.utils.get_stdout_stderr') + def test_get_cluster_node_hostname_None(self, mock_stdout_stderr, mock_valid_ip): + bootstrap._context = mock.Mock(cluster_node=None) + + peer_node = bootstrap.get_cluster_node_hostname() + assert peer_node is None + + mock_valid_ip.assert_not_called() + mock_stdout_stderr.assert_not_called() + + @mock.patch('crmsh.utils.valid_ip_addr') + @mock.patch('crmsh.utils.get_stdout_stderr') + def test_get_cluster_node_hostname_IP(self, mock_stdout_stderr, mock_valid_ip): + bootstrap._context = mock.Mock(cluster_node="1.1.1.1") + mock_valid_ip.return_value = True + mock_stdout_stderr.return_value = (0, "node1", None) + + peer_node = bootstrap.get_cluster_node_hostname() + assert peer_node == "node1" + + mock_valid_ip.assert_called_once_with("1.1.1.1") + mock_stdout_stderr.assert_called_once_with("ssh 1.1.1.1 crm_node --name") + + @mock.patch('crmsh.utils.valid_ip_addr') + @mock.patch('crmsh.utils.get_stdout_stderr') + def test_get_cluster_node_hostname_HOST(self, mock_stdout_stderr, mock_valid_ip): + bootstrap._context = mock.Mock(cluster_node="node2") + mock_valid_ip.return_value = False + + peer_node = bootstrap.get_cluster_node_hostname() + assert peer_node == "node2" + + mock_valid_ip.assert_called_once_with("node2") + mock_stdout_stderr.assert_not_called() + + @mock.patch('crmsh.utils.this_node') + @mock.patch('re.search') + @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') + def test_is_online_local_offline(self, mock_get_peer, mock_search, mock_this_node): + mock_this_node.return_value = "node1" + mock_search.return_value = None + + assert bootstrap.is_online("text") is False + + mock_this_node.assert_called_once_with() + mock_get_peer.assert_not_called() + mock_search.assert_called_once_with("Online: .* node1 ", "text") + + @mock.patch('crmsh.utils.this_node') + @mock.patch('re.search') + @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') + def test_is_online_on_init_node(self, mock_get_peer, mock_search, mock_this_node): + mock_search.return_value = mock.Mock() + mock_this_node.return_value = "node1" + mock_get_peer.return_value = None + + assert bootstrap.is_online("text") is True + + mock_this_node.assert_called_once_with() + mock_get_peer.assert_called_once_with() + mock_search.assert_called_once_with("Online: .* node1 ", "text") + + @mock.patch('crmsh.bootstrap.error') + @mock.patch('crmsh.bootstrap.stop_service') + @mock.patch('crmsh.bootstrap.csync2_update') + @mock.patch('crmsh.corosync.conf') + @mock.patch('shutil.copy') + @mock.patch('crmsh.utils.this_node') + @mock.patch('re.search') + @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') + def test_is_online_peer_offline(self, mock_get_peer, mock_search, mock_this_node, + mock_copy, mock_corosync_conf, mock_csync2, mock_stop_service, mock_error): + bootstrap.COROSYNC_CONF_ORIG = "/tmp/crmsh_tmpfile" + mock_search.side_effect = [ mock.Mock(), None ] + mock_this_node.return_value = "node2" + mock_get_peer.return_value = "node1" + mock_corosync_conf.side_effect = [ "/etc/corosync/corosync.conf", + "/etc/corosync/corosync.conf"] + + bootstrap.is_online("text") + + mock_this_node.assert_called_once_with() + mock_get_peer.assert_called_once_with() + mock_search.assert_has_calls([ + mock.call("Online: .* node2 ", "text"), + mock.call("Online: .* node1 ", "text") + ]) + mock_corosync_conf.assert_has_calls([ + mock.call(), + mock.call() + ]) + mock_copy.assert_called_once_with(bootstrap.COROSYNC_CONF_ORIG, "/etc/corosync/corosync.conf") + mock_csync2.assert_called_once_with("/etc/corosync/corosync.conf") + mock_stop_service.assert_called_once_with("corosync") + mock_error.assert_called_once_with("Cannot see peer node \"node1\", please check the communication IP") + + @mock.patch('crmsh.bootstrap.error') + @mock.patch('crmsh.bootstrap.stop_service') + @mock.patch('crmsh.bootstrap.csync2_update') + @mock.patch('crmsh.corosync.conf') + @mock.patch('shutil.copy') + @mock.patch('crmsh.utils.this_node') + @mock.patch('re.search') + @mock.patch('crmsh.bootstrap.get_cluster_node_hostname') + def test_is_online_both_online(self, mock_get_peer, mock_search, mock_this_node, + mock_copy, mock_corosync_conf, mock_csync2, mock_stop_service, mock_error): + mock_search.side_effect = [ mock.Mock(), mock.Mock() ] + mock_this_node.return_value = "node2" + mock_get_peer.return_value = "node1" + + assert bootstrap.is_online("text") is True + + mock_this_node.assert_called_once_with() + mock_get_peer.assert_called_once_with() + mock_search.assert_has_calls([ + mock.call("Online: .* node2 ", "text"), + mock.call("Online: .* node1 ", "text") + ]) + mock_corosync_conf.assert_not_called() + mock_copy.assert_not_called() + mock_csync2.assert_not_called() + mock_stop_service.assert_not_called() + mock_error.assert_not_called() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1580544897.c42c9530/test/unittests/test_corosync.py new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/unittests/test_corosync.py --- old/crmsh-4.2.0+git.1580544897.c42c9530/test/unittests/test_corosync.py 2020-02-01 09:14:57.000000000 +0100 +++ new/crmsh-4.2.0+git.1584013187.b45cfcb6/test/unittests/test_corosync.py 2020-03-12 12:39:47.000000000 +0100 @@ -93,6 +93,60 @@ make_value('nodelist.node.nodeid', str(nid)))) _valid(p) self.assertEqual(p.count('nodelist.node'), nid - 1) + + @mock.patch("crmsh.utils.is_ipv6") + @mock.patch("crmsh.utils.ip_in_local") + @mock.patch("re.search") + @mock.patch("crmsh.corosync.Parser") + @mock.patch("crmsh.corosync.conf") + @mock.patch("builtins.open", new_callable=mock.mock_open, read_data="corosync conf data") + def test_find_configured_ip_no_exception(self, mock_open_file, mock_conf, mock_parser, mock_search, mock_ip_local, mock_isv6): + mock_conf.return_value = "/etc/corosync/corosync.conf" + mock_parser_inst = mock.Mock() + mock_parser.return_value = mock_parser_inst + mock_parser_inst.all_paths.return_value = ["nodelist.node.ring0_addr"] + mock_search.return_value = mock.Mock() + mock_parser_inst.get_all.return_value = ["10.10.10.1"] + mock_isv6.return_value = False + mock_ip_local.return_value = ["192.168.1.1", "10.10.10.2", "20.20.20.2"] + + corosync.find_configured_ip(["10.10.10.2"]) + + mock_conf.assert_called_once_with() + mock_parser_inst.all_paths.assert_called_once_with() + mock_parser_inst.get_all.assert_called_once_with("nodelist.node.ring0_addr") + mock_open_file.assert_called_once_with(mock_conf.return_value) + mock_isv6.assert_called_once_with("10.10.10.2") + mock_ip_local.assert_called_once_with(False) + mock_search.assert_called_once_with("nodelist.node.ring[0-9]*_addr", "nodelist.node.ring0_addr") + + @mock.patch("crmsh.utils.is_ipv6") + @mock.patch("crmsh.utils.ip_in_local") + @mock.patch("re.search") + @mock.patch("crmsh.corosync.Parser") + @mock.patch("crmsh.corosync.conf") + @mock.patch("builtins.open", new_callable=mock.mock_open, read_data="corosync conf data") + def test_find_configured_ip_exception(self, mock_open_file, mock_conf, mock_parser, mock_search, mock_ip_local, mock_isv6): + mock_conf.return_value = "/etc/corosync/corosync.conf" + mock_parser_inst = mock.Mock() + mock_parser.return_value = mock_parser_inst + mock_parser_inst.all_paths.return_value = ["nodelist.node.ring0_addr"] + mock_search.return_value = mock.Mock() + mock_parser_inst.get_all.return_value = ["10.10.10.1", "10.10.10.2"] + mock_isv6.return_value = False + mock_ip_local.return_value = ["192.168.1.1", "10.10.10.2", "20.20.20.2"] + + with self.assertRaises(corosync.IPAlreadyConfiguredError) as err: + corosync.find_configured_ip(["10.10.10.2"]) + self.assertEqual("IP 10.10.10.2 was already configured", str(err.exception)) + + mock_conf.assert_called_once_with() + mock_parser_inst.all_paths.assert_called_once_with() + mock_parser_inst.get_all.assert_called_once_with("nodelist.node.ring0_addr") + mock_open_file.assert_called_once_with(mock_conf.return_value) + mock_isv6.assert_called_once_with("10.10.10.2") + mock_ip_local.assert_called_once_with(False) + mock_search.assert_called_once_with("nodelist.node.ring[0-9]*_addr", "nodelist.node.ring0_addr") def test_add_node_ucast(self): from crmsh.corosync import add_node_ucast, get_values
