Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2020-07-24 10:07:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new.3592 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Fri Jul 24 10:07:32 2020 rev:188 rq:822449 version:4.2.0+git.1595517298.a06e892f Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2020-07-17 20:50:10.372877903 +0200 +++ /work/SRC/openSUSE:Factory/.crmsh.new.3592/crmsh.changes 2020-07-24 10:09:12.138124654 +0200 @@ -1,0 +2,7 @@ +Thu Jul 23 15:28:41 UTC 2020 - [email protected] + +- Update to version 4.2.0+git.1595517298.a06e892f: + * Dev: unittest: append unit test for corosync status related codes + * Low: ui_corosync: copy ssh key to qnetd while detect need password(bsc#1174385) + +------------------------------------------------------------------- Old: ---- crmsh-4.2.0+git.1594286044.7a596d12.tar.bz2 New: ---- crmsh-4.2.0+git.1595517298.a06e892f.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.wvBXfz/_old 2020-07-24 10:09:16.502129030 +0200 +++ /var/tmp/diff_new_pack.wvBXfz/_new 2020-07-24 10:09:16.506129034 +0200 @@ -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.1594286044.7a596d12 +Version: 4.2.0+git.1595517298.a06e892f Release: 0 Url: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.wvBXfz/_old 2020-07-24 10:09:16.546129074 +0200 +++ /var/tmp/diff_new_pack.wvBXfz/_new 2020-07-24 10:09:16.546129074 +0200 @@ -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">78ce77e47b195fc2ef4a21d5d83886b178bbc0e1</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">314ca14d291ce6c6856ff4571b6e59b3d4df9f10</param></service></servicedata> \ No newline at end of file ++++++ crmsh-4.2.0+git.1594286044.7a596d12.tar.bz2 -> crmsh-4.2.0+git.1595517298.a06e892f.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1594286044.7a596d12/crmsh/corosync.py new/crmsh-4.2.0+git.1595517298.a06e892f/crmsh/corosync.py --- old/crmsh-4.2.0+git.1594286044.7a596d12/crmsh/corosync.py 2020-07-09 11:14:04.000000000 +0200 +++ new/crmsh-4.2.0+git.1595517298.a06e892f/crmsh/corosync.py 2020-07-23 17:14:58.000000000 +0200 @@ -36,6 +36,73 @@ return utils.get_stdout(['corosync-quorumtool'] + list(args), shell=False) +def query_status(status_type): + """ + Query status of corosync + + Possible types could be ring/quorum/qnetd + """ + if status_type == "ring": + query_ring_status() + elif status_type == "quorum": + query_quorum_status() + elif status_type == "qnetd": + query_qnetd_status() + else: + raise ValueError("Wrong type \"{}\" to query status".format(status_type)) + + +def query_ring_status(): + """ + Query corosync ring status + """ + rc, out, err = utils.get_stdout_stderr("corosync-cfgtool -s") + if rc != 0 and err: + raise ValueError(err) + if rc == 0 and out: + print(out) + + +def query_quorum_status(): + """ + Query corosync quorum status + """ + utils.print_cluster_nodes() + rc, out, err = utils.get_stdout_stderr("corosync-quorumtool -s") + if rc != 0 and err: + raise ValueError(err) + if rc == 0 and out: + print(out) + + +def query_qnetd_status(): + """ + Query qnetd status + """ + if not utils.is_qdevice_configured(): + raise ValueError("QDevice/QNetd not configured!") + cluster_name = get_value('totem.cluster_name') + if not cluster_name: + raise ValueError("cluster_name not configured!") + qnetd_addr = get_value('quorum.device.net.host') + if not qnetd_addr: + raise ValueError("host for qnetd not configured!") + + # Configure ssh passwordless to qnetd if detect password is needed + if utils.check_ssh_passwd_need(qnetd_addr): + print("Copy ssh key to qnetd node({})".format(qnetd_addr)) + rc, _, err = utils.get_stdout_stderr("ssh-copy-id -i /root/.ssh/id_rsa.pub root@{}".format(qnetd_addr)) + if rc != 0: + raise ValueError(err) + + cmd = "corosync-qnetd-tool -lv -c {}".format(cluster_name) + result = parallax.parallax_call([qnetd_addr], cmd) + _, qnetd_result_stdout, _ = result[0][1] + if qnetd_result_stdout: + utils.print_cluster_nodes() + print(utils.to_ascii(qnetd_result_stdout)) + + _tCOMMENT = 0 _tBEGIN = 1 _tEND = 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1594286044.7a596d12/crmsh/ui_corosync.py new/crmsh-4.2.0+git.1595517298.a06e892f/crmsh/ui_corosync.py --- old/crmsh-4.2.0+git.1594286044.7a596d12/crmsh/ui_corosync.py 2020-07-09 11:14:04.000000000 +0200 +++ new/crmsh-4.2.0+git.1595517298.a06e892f/crmsh/ui_corosync.py 2020-07-23 17:14:58.000000000 +0200 @@ -8,6 +8,7 @@ from .msg import err_buf from . import corosync from . import parallax +from . import bootstrap def _push_completer(args): @@ -62,47 +63,15 @@ ''' Quick cluster health status. Corosync status or QNetd status ''' - def print_cluster_nodes(): - rc, out = utils.get_stdout("crm_node -l") - if rc == 0 and out: - print("{}\n".format(out)) - - rc, _ = utils.get_stdout('systemctl -q is-active corosync.service') - if rc != 0: + if not bootstrap.service_is_active("corosync.service"): err_buf.error("corosync.service is not running!") return False - if status_type == "ring": - print(corosync.cfgtool('-s')[1]) - return - if status_type == "quorum": - print_cluster_nodes() - print(corosync.quorumtool('-s')[1]) - return - if status_type == "qnetd": - if not utils.is_qdevice_configured(): - err_buf.error("QDevice/QNetd not configured!") - return False - cluster_name = corosync.get_value('totem.cluster_name') - if not cluster_name: - err_buf.error("cluster_name not configured!") - return False - qnetd_addr = corosync.get_value('quorum.device.net.host') - if not qnetd_addr: - err_buf.error("host for qnetd not configured!") - return False - - cmd = "corosync-qnetd-tool -lv -c {}".format(cluster_name) - try: - result = parallax.parallax_call([qnetd_addr], cmd) - except ValueError as err: - err_buf.error(err) - return False - _, qnetd_result_stdout, _ = result[0][1] - if qnetd_result_stdout: - print_cluster_nodes() - print(utils.to_ascii(qnetd_result_stdout)) - return + try: + corosync.query_status(status_type) + except ValueError as err: + err_buf.error(str(err)) + return False @command.skill_level('administrator') def do_reload(self, context): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1594286044.7a596d12/crmsh/utils.py new/crmsh-4.2.0+git.1595517298.a06e892f/crmsh/utils.py --- old/crmsh-4.2.0+git.1594286044.7a596d12/crmsh/utils.py 2020-07-09 11:14:04.000000000 +0200 +++ new/crmsh-4.2.0+git.1595517298.a06e892f/crmsh/utils.py 2020-07-23 17:14:58.000000000 +0200 @@ -1664,6 +1664,15 @@ return [] +def print_cluster_nodes(): + """ + Print the output of crm_node -l + """ + rc, out, _ = get_stdout_stderr("crm_node -l") + if rc == 0 and out: + print("{}\n".format(out)) + + def list_cluster_nodes(): ''' Returns a list of nodes in the cluster. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1594286044.7a596d12/test/unittests/test_corosync.py new/crmsh-4.2.0+git.1595517298.a06e892f/test/unittests/test_corosync.py --- old/crmsh-4.2.0+git.1594286044.7a596d12/test/unittests/test_corosync.py 2020-07-09 11:14:04.000000000 +0200 +++ new/crmsh-4.2.0+git.1595517298.a06e892f/test/unittests/test_corosync.py 2020-07-23 17:14:58.000000000 +0200 @@ -9,6 +9,7 @@ from builtins import object import os import unittest +import pytest from unittest import mock from crmsh import corosync from crmsh.corosync import Parser, make_section, make_value @@ -40,6 +41,149 @@ print(parser.to_string()) [email protected]("crmsh.corosync.query_ring_status") +def test_query_status_ring(mock_ring_status): + corosync.query_status("ring") + mock_ring_status.assert_called_once_with() + + [email protected]("crmsh.corosync.query_quorum_status") +def test_query_status_quorum(mock_quorum_status): + corosync.query_status("quorum") + mock_quorum_status.assert_called_once_with() + + [email protected]("crmsh.corosync.query_qnetd_status") +def test_query_status_qnetd(mock_qnetd_status): + corosync.query_status("qnetd") + mock_qnetd_status.assert_called_once_with() + + +def test_query_status_except(): + with pytest.raises(ValueError) as err: + corosync.query_status("xxx") + assert str(err.value) == "Wrong type \"xxx\" to query status" + + [email protected]("crmsh.utils.get_stdout_stderr") +def test_query_ring_status_except(mock_run): + mock_run.return_value = (1, None, "error") + with pytest.raises(ValueError) as err: + corosync.query_ring_status() + assert str(err.value) == "error" + mock_run.assert_called_once_with("corosync-cfgtool -s") + + [email protected]("crmsh.utils.get_stdout_stderr") +def test_query_ring_status(mock_run): + mock_run.return_value = (0, "data", None) + corosync.query_ring_status() + mock_run.assert_called_once_with("corosync-cfgtool -s") + + [email protected]("crmsh.utils.print_cluster_nodes") [email protected]("crmsh.utils.get_stdout_stderr") +def test_query_quorum_status_except(mock_run, mock_print_nodes): + mock_run.return_value = (1, None, "error") + with pytest.raises(ValueError) as err: + corosync.query_quorum_status() + assert str(err.value) == "error" + mock_run.assert_called_once_with("corosync-quorumtool -s") + mock_print_nodes.assert_called_once_with() + + [email protected]("crmsh.utils.print_cluster_nodes") [email protected]("crmsh.utils.get_stdout_stderr") +def test_query_quorum_status(mock_run, mock_print_nodes): + mock_run.return_value = (0, "data", None) + corosync.query_quorum_status() + mock_run.assert_called_once_with("corosync-quorumtool -s") + mock_print_nodes.assert_called_once_with() + + [email protected]("crmsh.utils.is_qdevice_configured") +def test_query_qnetd_status_no_qdevice(mock_qdevice_configured): + mock_qdevice_configured.return_value = False + with pytest.raises(ValueError) as err: + corosync.query_qnetd_status() + assert str(err.value) == "QDevice/QNetd not configured!" + mock_qdevice_configured.assert_called_once_with() + + [email protected]("crmsh.corosync.get_value") [email protected]("crmsh.utils.is_qdevice_configured") +def test_query_qnetd_status_no_cluster_name(mock_qdevice_configured, mock_get_value): + mock_qdevice_configured.return_value = True + mock_get_value.return_value = None + with pytest.raises(ValueError) as err: + corosync.query_qnetd_status() + assert str(err.value) == "cluster_name not configured!" + mock_qdevice_configured.assert_called_once_with() + mock_get_value.assert_called_once_with("totem.cluster_name") + + [email protected]("crmsh.corosync.get_value") [email protected]("crmsh.utils.is_qdevice_configured") +def test_query_qnetd_status_no_cluster_name(mock_qdevice_configured, mock_get_value): + mock_qdevice_configured.return_value = True + mock_get_value.side_effect = ["hacluster", None] + with pytest.raises(ValueError) as err: + corosync.query_qnetd_status() + assert str(err.value) == "host for qnetd not configured!" + mock_qdevice_configured.assert_called_once_with() + mock_get_value.assert_has_calls([ + mock.call("totem.cluster_name"), + mock.call("quorum.device.net.host") + ]) + + [email protected]("crmsh.utils.get_stdout_stderr") [email protected]("crmsh.utils.check_ssh_passwd_need") [email protected]("crmsh.corosync.get_value") [email protected]("crmsh.utils.is_qdevice_configured") +def test_query_qnetd_status_copy_id_failed(mock_qdevice_configured, mock_get_value, mock_check_passwd, mock_run): + mock_qdevice_configured.return_value = True + mock_get_value.side_effect = ["hacluster", "10.10.10.123"] + mock_check_passwd.return_value = True + mock_run.return_value = [1, None, "error"] + with pytest.raises(ValueError) as err: + corosync.query_qnetd_status() + assert str(err.value) == "error" + mock_qdevice_configured.assert_called_once_with() + mock_get_value.assert_has_calls([ + mock.call("totem.cluster_name"), + mock.call("quorum.device.net.host") + ]) + mock_check_passwd.assert_called_once_with("10.10.10.123") + mock_run.assert_called_once_with("ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]") + + [email protected]("crmsh.utils.print_cluster_nodes") [email protected]("crmsh.parallax.parallax_call") [email protected]("crmsh.utils.get_stdout_stderr") [email protected]("crmsh.utils.check_ssh_passwd_need") [email protected]("crmsh.corosync.get_value") [email protected]("crmsh.utils.is_qdevice_configured") +def test_query_qnetd_status_copy(mock_qdevice_configured, mock_get_value, mock_check_passwd, mock_run, mock_parallax_call, mock_print_nodes): + mock_qdevice_configured.return_value = True + mock_get_value.side_effect = ["hacluster", "10.10.10.123"] + mock_check_passwd.return_value = True + mock_run.return_value = [0, "data", None] + mock_parallax_call.return_value = [("node1", (0, "data", None)), ] + + corosync.query_qnetd_status() + + mock_qdevice_configured.assert_called_once_with() + mock_get_value.assert_has_calls([ + mock.call("totem.cluster_name"), + mock.call("quorum.device.net.host") + ]) + mock_check_passwd.assert_called_once_with("10.10.10.123") + mock_run.assert_called_once_with("ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]") + mock_parallax_call.assert_called_once_with(["10.10.10.123"], "corosync-qnetd-tool -lv -c hacluster") + mock_print_nodes.assert_called_once_with() + + class TestCorosyncParser(unittest.TestCase): def test_parse(self): p = Parser(F1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.2.0+git.1594286044.7a596d12/test/unittests/test_utils.py new/crmsh-4.2.0+git.1595517298.a06e892f/test/unittests/test_utils.py --- old/crmsh-4.2.0+git.1594286044.7a596d12/test/unittests/test_utils.py 2020-07-09 11:14:04.000000000 +0200 +++ new/crmsh-4.2.0+git.1595517298.a06e892f/test/unittests/test_utils.py 2020-07-23 17:14:58.000000000 +0200 @@ -22,6 +22,13 @@ imp.reload(utils) [email protected]("crmsh.utils.get_stdout_stderr") +def test_print_cluster_nodes(mock_run): + mock_run.return_value = (0, "data", None) + utils.print_cluster_nodes() + mock_run.assert_called_once_with("crm_node -l") + + @mock.patch('os.path.exists') def test_check_file_content_included_target_not_exist(mock_exists): mock_exists.side_effect = [True, False]
