Hello community, here is the log from the commit of package python-cluster-preflight-check for openSUSE:Factory checked in at 2020-03-08 22:24:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-cluster-preflight-check (Old) and /work/SRC/openSUSE:Factory/.python-cluster-preflight-check.new.26092 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cluster-preflight-check" Sun Mar 8 22:24:28 2020 rev:4 rq:782607 version:0.0.34 Changes: -------- --- /work/SRC/openSUSE:Factory/python-cluster-preflight-check/python-cluster-preflight-check.changes 2019-09-25 08:11:38.962523606 +0200 +++ /work/SRC/openSUSE:Factory/.python-cluster-preflight-check.new.26092/python-cluster-preflight-check.changes 2020-03-08 22:24:46.076100805 +0100 @@ -1,0 +2,7 @@ +Sun Mar 8 01:40:52 UTC 2020 - XinLiang <xli...@suse.com> + +- version 0.0.34 + - Update README: package description + - Enable continue delivery and submit + +------------------------------------------------------------------- Old: ---- cluster_preflight_check-0.0.33.tar.gz New: ---- cluster_preflight_check-0.0.34.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-cluster-preflight-check.spec ++++++ --- /var/tmp/diff_new_pack.1fUSMZ/_old 2020-03-08 22:24:47.088101429 +0100 +++ /var/tmp/diff_new_pack.1fUSMZ/_new 2020-03-08 22:24:47.088101429 +0100 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-cluster-preflight-check -Version: 0.0.33 +Version: 0.0.34 Release: 0 Summary: Standardized Testing of Basic Cluster Functionality License: BSD-3-Clause ++++++ cluster_preflight_check-0.0.33.tar.gz -> cluster_preflight_check-0.0.34.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cluster_preflight_check-0.0.33/.travis.yml new/cluster_preflight_check-0.0.34/.travis.yml --- old/cluster_preflight_check-0.0.33/.travis.yml 2019-09-16 10:02:58.000000000 +0200 +++ new/cluster_preflight_check-0.0.34/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,33 +0,0 @@ -sudo: required -services: - - docker - -language: python - -stages: - - test - -jobs: - include: - - stage: test - python: 2.7 - env: TOXENV=py27-codeclimate - install: - - pip install tox - script: - - tox - - - stage: test - python: 3.6 - env: TOXENV=py36-codeclimate - before_script: - - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - - chmod +x ./cc-test-reporter - - ./cc-test-reporter before-build - install: - - pip install tox - script: - - tox - after_script: - - mv tests/coverage.xml . - - ./cc-test-reporter after-build -t coverage.py --exit-code $TRAVIS_TEST_RESULT tests/coverage.xml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cluster_preflight_check-0.0.33/ChangeLog new/cluster_preflight_check-0.0.34/ChangeLog --- old/cluster_preflight_check-0.0.33/ChangeLog 2019-09-16 10:02:58.000000000 +0200 +++ new/cluster_preflight_check-0.0.34/ChangeLog 2020-03-08 03:10:41.968003313 +0100 @@ -1,3 +1,8 @@ +* Sat Mar 7 2020 Xin Liang <xli...@suse.com> +- version 0.0.34 +- Update README: package description +- Enable continue delivery and submit + * Mon Sep 16 2019 Xin Liang <xli...@suse.com> - version 0.0.33 - Make sure this tool can only be executed as root diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cluster_preflight_check-0.0.33/README.md new/cluster_preflight_check-0.0.34/README.md --- old/cluster_preflight_check-0.0.33/README.md 2019-09-16 10:02:58.000000000 +0200 +++ new/cluster_preflight_check-0.0.34/README.md 2020-03-08 03:10:41.968003313 +0100 @@ -1,5 +1,33 @@ # cluster-preflight-check -Tool for Standardize Testing of Basic Cluster Functionality +**cluster-preflight-check** by its name targets to trigger cluster failures and verify some key configuration right before it goes to production. Cluster failure simulation by different people can go wild and the results can be different, even wrongly interpreted. This tool standardizes the steps as the simple CLI. It is carefully designed and must not change any configuration to harm the production. + +## Motivation +#### Convenient +Customers using tools like yast2-cluster/ha-cluster-bootstrap to setup a cluster. +Before really pushing into production environment, it's necessary to make sure everything in cluster works well and is configured correctly. +To archive above target, a lots of commands or steps will be included, like kill the primary process, check node/resource/cluster status, fence specific node, +simulate split brain scenarios, show these commands' results and how to recover.</br> +**So it will be more convenient if we can integrate these commands into one tool.** + +#### Standard +**Ship the standard commands.**</br> +Like how to kill process, how to check status, how to make split brain, there are lots ways to to that. +In the cluster-preflight-check, we only provide one way to do this, the way we make sure it do works and is recoverable. + +#### Reusable +**Results can be reused.**</br> +cluster-preflight-check generates JSON result after each test case. +The JSON result includes loggings, descriptions, timestamp, and whether this case has passed. +It can easily be utilized by other tools/applications and be friendly with developer. + +#### Traceable +**Process can be traceable.**</br> +cluster-preflight-check has loggings and reports. +For killing testcase, a report will be created. +Report will includes test case description, action logging and explanation for possible results. + +#### Recoverable +cluster-preflight-check will not bring new troubles or unrecoverable troubles after executing actions. ## Features #### Check environment @@ -17,7 +45,7 @@ * kill corosync daemon * kill pacemakerd daemon -When running killing test case, a report will be created at /var/lib/cluster-preflight-check;<br> +When running killing test case, a report will be created at /var/lib/ha-cluster-preflight-check;<br> Report will includes test case description, action logging and explanation for possible results. #### Fence specific node #### Make split brain @@ -30,11 +58,11 @@ pip install cluster-preflight-check ## Use -cluster-preflight-check --help +ha-cluster-preflight-check --help ``` -usage: cluster-preflight-check [-e] [-c] - [--kill-sbd | --kill-corosync | --kill-pacemakerd | --fence-node NODE | --split-brain-iptables] - [-l] [-y] [-h] +usage: ha-cluster-preflight-check [-e] [-c] + [--kill-sbd | --kill-corosync | --kill-pacemakerd | --fence-node NODE | --split-brain-iptables] + [-l] [-y] [-h] Cluster preflight check tool set @@ -52,16 +80,16 @@ -y, --yes Answer "yes" if asked to run the test -h, --help show this help message and exit -Log: /var/log/cluster-preflight-check.log -Json results: /var/lib/cluster-preflight-check/cluster-preflight-check.json -For each --kill-* testcase, report directory: /var/lib/cluster-preflight-check +Log: /var/log/ha-cluster-preflight-check.log +Json results: /var/lib/ha-cluster-preflight-check/ha-cluster-preflight-check.json +For each --kill-* testcase, report directory: /var/lib/ha-cluster-preflight-check ``` ## Demo -1. [using cluster-preflight-check to check environment and cluster status](https://asciinema.org/a/261409) -2. [using cluster-preflight-check to kill sbd process](https://asciinema.org/a/261411) -3. [using cluster-preflight-check to kill corosync process](https://asciinema.org/a/261412) -4. [using cluster-preflight-check to fence specific node](https://asciinema.org/a/261413) -5. [using cluster-preflight-check to simulate split brain](https://asciinema.org/a/261414) +1. [using cluster-preflight-check to check environment and cluster status](https://asciinema.org/a/273850) +2. [using cluster-preflight-check to kill sbd process](https://asciinema.org/a/273851) +3. [using cluster-preflight-check to kill corosync process](https://asciinema.org/a/273852) +4. [using cluster-preflight-check to fence specific node](https://asciinema.org/a/273853) +5. [using cluster-preflight-check to simulate split brain](https://asciinema.org/a/273854) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cluster_preflight_check-0.0.33/setup.py new/cluster_preflight_check-0.0.34/setup.py --- old/cluster_preflight_check-0.0.33/setup.py 2019-09-16 10:02:58.000000000 +0200 +++ new/cluster_preflight_check-0.0.34/setup.py 2020-03-08 03:10:41.968003313 +0100 @@ -2,12 +2,12 @@ setuptools.setup( name="cluster_preflight_check", - version="0.0.33", + version="0.0.34", author="Xin Liang", author_email="xli...@suse.com", url="https://github.com/liangxin1300/cluster_preflight_check.git", license="BSD", - packages=setuptools.find_packages(), + packages=setuptools.find_packages(exclude=["tests"]), description="Tool for Standardize Testing of Basic Cluster Functionality", entry_points={ 'console_scripts': ['ha-cluster-preflight-check=cluster_preflight_check.main:main'], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cluster_preflight_check-0.0.33/tests/unittest/test_utils.py new/cluster_preflight_check-0.0.34/tests/unittest/test_utils.py --- old/cluster_preflight_check-0.0.33/tests/unittest/test_utils.py 2019-09-16 10:02:58.000000000 +0200 +++ new/cluster_preflight_check-0.0.34/tests/unittest/test_utils.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,25 +0,0 @@ - -import unittest -from cluster_test_tool import utils - - -def get_output(cmd): - rc, out, err = utils.run_cmd(cmd) - if rc != 0: - raise RuntimeError(err) - return out - - -class TestUtils(unittest.TestCase): - def test_me(self): - self.assertEqual(utils.me(), get_output("hostname -s")) - - def test_now(self): - self.assertEqual(utils.now(), get_output("date +\"%Y/%m/%d %H:%M:%S\"")) - - def test_service_is_active(self): - self.assertTrue(utils.service_is_active("corosync")) - - -if __name__ == '__main__': - unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cluster_preflight_check-0.0.33/tests/utils_test.py new/cluster_preflight_check-0.0.34/tests/utils_test.py --- old/cluster_preflight_check-0.0.33/tests/utils_test.py 2019-09-16 10:02:58.000000000 +0200 +++ new/cluster_preflight_check-0.0.34/tests/utils_test.py 2020-03-08 03:10:41.968003313 +0100 @@ -1,5 +1,5 @@ """ -Unitary tests for cluster_test_tool/utils.py +Unitary tests for cluster_preflight_check/utils.py :author: Xin Liang :organization: SUSE Linux GmbH @@ -10,25 +10,24 @@ # pylint:disable=C0103,C0111,W0212,W0611 +import os +import sys +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + import unittest -from cluster_test_tool import utils +import logging +import datetime try: from unittest import mock except ImportError: import mock - -def get_output(cmd): - rc, out, err = utils.run_cmd(cmd) - if rc != 0: - raise RuntimeError(err) - return out - +from cluster_preflight_check import utils, main class TestUtils(unittest.TestCase): ''' - Unitary tests for cluster_test_tool/utils.py + Unitary tests for cluster_preflight_check/utils.py ''' @classmethod @@ -39,10 +38,18 @@ logging.basicConfig(level=logging.INFO) - def setUp(self): + @mock.patch('cluster_preflight_check.utils.now') + @mock.patch('cluster_preflight_check.main') + def setUp(self, mock_main, mock_now): """ Test setUp. """ + ''' + mock_now.return_value = "2019/07/10 01:15:15" + mock_main.return_value = mock.Mock() + self.task = utils.Task("project testing") + self.task_check = utils.TaskCheck("TaskCheck testing") + ''' def tearDown(self): """ @@ -55,19 +62,190 @@ Global tearDown. """ - def test_me(self): - self.assertEqual(utils.me(), get_output("hostname -s")) - - def test_now(self): - self.assertEqual(utils.now(), get_output("date +\"%Y/%m/%d %H:%M:%S\"")) - - def test_service_is_active(self): - self.assertTrue(utils.service_is_active("dbus")) + @mock.patch('socket.gethostname') + def test_me(self, mock_hostname): + mock_hostname.return_value = "node1.com" + result = utils.me() + self.assertEqual(result, "node1.com") + mock_hostname.assert_called_once_with() + + @mock.patch('cluster_preflight_check.utils.datetime') + def test_now(self, mock_datetime): + mock_datetime.now.return_value = datetime.datetime(2019, 7, 5, 14, 44, 55, 360460) + result = utils.now() + self.assertEqual(result, "2019/07/05 14:44:55") + mock_datetime.now.assert_called_once_with() + ''' + @mock.patch('cluster_preflight_check.main') + def test_msg_raw(self, mock_main): + utils.msg_raw(logging.INFO, "testing logger") + mock_main.ctx.logger.log.assert_called_once_with(logging.INFO, "testing logger") + + @mock.patch('cluster_preflight_check.main') + def test_msg_raw_disable_stdout(self, mock_main): + utils.msg_raw(logging.INFO, "testing logger", to_stdout=False) + context = mock_main.ctx + context.logger.removeHandler.assert_called_once_with(context.logger_stdout_handler) + context.logger.log.assert_called_once_with(logging.INFO, "testing logger") + context.logger.addHandler.assert_called_once_with(context.logger_stdout_handler) + + @mock.patch('cluster_preflight_check.utils.msg_raw') + def test_msg_info(self, mock_msg_raw): + mock_msg_raw.return_value = mock.Mock() + utils.msg_info("test") + mock_msg_raw.assert_called_once_with(logging.INFO, "test", True) + + @mock.patch('cluster_preflight_check.utils.msg_raw') + def test_msg_warn(self, mock_msg_raw): + mock_msg_raw.return_value = mock.Mock() + utils.msg_warn("test") + mock_msg_raw.assert_called_once_with(logging.WARNING, "test", True) + + @mock.patch('cluster_preflight_check.utils.msg_raw') + def test_msg_error(self, mock_msg_raw): + mock_msg_raw.return_value = mock.Mock() + utils.msg_error("test") + mock_msg_raw.assert_called_once_with(logging.ERROR, "test", True) + + @mock.patch('cluster_preflight_check.utils.msg_info') + def test_task_info(self, mock_info): + self.task.msg_append = mock.Mock() + self.task.info("test") + self.task.msg_append.assert_called_once_with("info", "test") + mock_info.assert_called_once_with("test", to_stdout=False) + + @mock.patch('cluster_preflight_check.utils.msg_warn') + def test_task_warn(self, mock_warn): + self.task.msg_append = mock.Mock() + self.task.warn("test") + self.task.msg_append.assert_called_once_with("warn", "test") + mock_warn.assert_called_once_with("test", to_stdout=False) + + @mock.patch('cluster_preflight_check.utils.msg_error') + def test_task_error(self, mock_error): + self.task.msg_append = mock.Mock() + self.task.error("test") + self.task.msg_append.assert_called_once_with("error", "test") + mock_error.assert_called_once_with("test", to_stdout=False) + + @mock.patch('cluster_preflight_check.utils.now') + def test_task_msg_append(self, mock_now): + mock_now.return_value = "2019/01/01" + self.task.msg_append("error", "test msg_append") + self.assertFalse(self.task.passed) + expected = ("error", "test msg_append", "2019/01/01") + self.assertTupleEqual(self.task.messages[-1], expected) + + def test_task_build_base_result(self): + self.task.build_base_result() + self.assertEqual(self.task.result["Timestamp"], "2019/07/10 01:15:15") + self.assertEqual(self.task.result["Description"], "project testing") + self.assertEqual(len(self.task.result["Messages"]), 0) + + @mock.patch('cluster_preflight_check.main') + def test_task_check_to_stdout(self, mock_main): + self.task_check.info("service1 is available") + self.task_check.warn("service2 is disabled") + self.task_check.to_stdout() + context = mock_main.ctx + context.logger.removeHandler.assert_called_once_with(context.logger_stdout_handler) + + @mock.patch('cluster_preflight_check.utils.run_cmd') + def test_get_property(self, mock_run_cmd): + mock_run_cmd.return_value = (0, '60s', None) + result = utils.get_property('stonith-timeout') + self.assertEqual(result, '60s') + mock_run_cmd.assert_called_once_with("crm configure get_property stonith-timeout") + + @mock.patch('cluster_preflight_check.utils.get_property') + def test_fence_enabled(self, mock_property): + mock_property.return_value = "true" + result = utils.fence_enabled() + self.assertTrue(result) + mock_property.assert_called_once_with('stonith-enabled') + + @mock.patch('cluster_preflight_check.utils.get_property') + @mock.patch('re.match') + def test_get_fence_timeout(self, mock_match, mock_property): + mock_property.return_value = '60s' + mock_match.return_value = True + result = utils.get_fence_timeout() + self.assertEqual(result, '60') + mock_property.assert_called_once_with('stonith-timeout') + mock_match.assert_called_once_with('[0-9]+(s|)$', '60s') + + @mock.patch('cluster_preflight_check.utils.fence_enabled') + @mock.patch('cluster_preflight_check.utils.get_fence_action') + @mock.patch('cluster_preflight_check.utils.get_fence_timeout') + def test_get_fence_info(self, mock_timeout, mock_action, mock_enabled): + mock_enabled.return_value = True + mock_action.return_value = "reboot" + mock_timeout.return_value = 60 + result = utils.get_fence_info() + expected = (True, "reboot", 60) + self.assertTupleEqual(result, expected) + mock_enabled.assert_called_once_with() + mock_action.assert_called_once_with() + mock_timeout.assert_called_once_with() + + @mock.patch('cluster_preflight_check.utils.run_cmd') + def test_service_is_active(self, mock_run_cmd): + mock_run_cmd.return_value = (0, None, None) + result = utils.service_is_active('corosync.service') + self.assertTrue(result) + mock_run_cmd.assert_called_once_with('systemctl -q is-active corosync.service') + + @mock.patch('cluster_preflight_check.utils.run_cmd') + def test_service_is_enabled(self, mock_run_cmd): + mock_run_cmd.return_value = (0, None, None) + result = utils.service_is_enabled('corosync.service') + self.assertTrue(result) + mock_run_cmd.assert_called_once_with('systemctl is-enabled corosync.service') + + @mock.patch('cluster_preflight_check.utils.run_cmd') + def test_grep_output(self, mock_run_cmd): + mock_run_cmd.return_value = (0, "Tue Jul 2 13:48:13 CST 2019", None) + result = utils.grep_output("date", 'Jul') + self.assertTrue(result) + mock_run_cmd.assert_called_once_with("date") + + @mock.patch('cluster_preflight_check.utils.grep_output') + def test_service_is_available(self, mock_grep): + mock_grep.return_value = "ntp.service" + result = utils.service_is_available("ntp.service") + self.assertEqual(result, "ntp.service") + mock_grep.assert_called_once_with("systemctl list-unit-files ntp.service", + "ntp.service") + + @mock.patch('cluster_preflight_check.utils.service_is_active') + def test_is_cluster_running(self, mock_is_active): + mock_is_active.side_effect = [True, False] + result = utils.is_cluster_running() + self.assertFalse(result) + mock_is_active.assert_has_calls([ + mock.call("corosync"), + mock.call("pacemaker") + ]) + + @mock.patch('os.uname') + def test_this_node(self, mock_uname): + mock_uname.return_value = (None, "node-1.com") + result = utils.this_node() + self.assertEqual(result, "node-1.com") + mock_uname.assert_called_once_with() + + @mock.patch('cluster_preflight_check.utils.run_cmd') + def test_package_is_installed(self, mock_run_cmd): + mock_run_cmd.return_value = (0, None, None) + result = utils.package_is_installed("crmsh") + self.assertEqual(result, True) + mock_run_cmd.assert_called_once_with("rpm -q --quiet crmsh") - @mock.patch('cluster_test_tool.utils.run_cmd') + @mock.patch('cluster_preflight_check.utils.run_cmd') def test_corosync_port(self, mock_run_cmd): - mock_run_cmd.return_value = "5045\n5047" + mock_run_cmd.return_value = (0, "5045\n5047", None) result = utils.corosync_port() - expected = [5045, 5047] + expected = ['5045', '5047'] self.assertListEqual(result, expected) mock_run_cmd.assert_called_once_with("corosync-cmapctl |awk -F'= ' 'BEGIN {rc=1}/mcastport/{print $2; rc=0}END{exit rc}'") + '''