Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2026-06-29 17:31:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new.11887 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Mon Jun 29 17:31:08 2026 rev:414 rq:1362400 version:5.1.0+20260629.0b862e06 Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2026-06-25 10:58:30.796081890 +0200 +++ /work/SRC/openSUSE:Factory/.crmsh.new.11887/crmsh.changes 2026-06-29 17:32:26.834318042 +0200 @@ -1,0 +2,12 @@ +Mon Jun 29 11:03:02 UTC 2026 - [email protected] + +- Update to version 5.1.0+20260629.0b862e06: + * Chore: Changelog: Update ChangeLog for release 5.1.0 rc2 + +------------------------------------------------------------------- +Thu Jun 25 08:24:05 UTC 2026 - [email protected] + +- Update to version 5.1.0+20260625.11a77666: + * Dev: unittests: Add dedicated unit tests for storage_utils + +------------------------------------------------------------------- Old: ---- crmsh-5.1.0+20260624.349562dd.tar.bz2 New: ---- crmsh-5.1.0+20260629.0b862e06.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.AcftQ9/_old 2026-06-29 17:32:27.638346369 +0200 +++ /var/tmp/diff_new_pack.AcftQ9/_new 2026-06-29 17:32:27.638346369 +0200 @@ -41,7 +41,7 @@ Summary: High Availability cluster command-line interface License: GPL-2.0-or-later Group: %{pkg_group} -Version: 5.1.0+20260624.349562dd +Version: 5.1.0+20260629.0b862e06 Release: 0 URL: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.AcftQ9/_old 2026-06-29 17:32:27.694348343 +0200 +++ /var/tmp/diff_new_pack.AcftQ9/_new 2026-06-29 17:32:27.698348483 +0200 @@ -9,7 +9,7 @@ </service> <service name="tar_scm"> <param name="url">https://github.com/ClusterLabs/crmsh.git</param> - <param name="changesrevision">349562dd6312bfaef9755ef04f626b496c66cc7e</param> + <param name="changesrevision">72564250a68429f7122ef2cebca824495bc9f3d9</param> </service> </servicedata> (No newline at EOF) ++++++ crmsh-5.1.0+20260624.349562dd.tar.bz2 -> crmsh-5.1.0+20260629.0b862e06.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.1.0+20260624.349562dd/ChangeLog new/crmsh-5.1.0+20260629.0b862e06/ChangeLog --- old/crmsh-5.1.0+20260624.349562dd/ChangeLog 2026-06-24 06:16:33.000000000 +0200 +++ new/crmsh-5.1.0+20260629.0b862e06/ChangeLog 2026-06-29 12:31:02.000000000 +0200 @@ -1,3 +1,39 @@ +* Mon Jun 29 2026 Xin Liang <[email protected]> +- Release 5.1.0 rc2 +- Dev: Refactor storage helpers out of utils.py (#2132) +- Chore: unittests: reduce output verbosity and stop printing coverage report (#2139) +- Dev: storage_utils: Rename blkid UUID helper +- Dev: storage_utils: Refactor storage helpers out of utils.py +- Dev: bootstrap: Warn about deprecating legacy and unencrypted/unauthenticated knet transports (jsc#PED-16433) (#2131) +- Dev: bootstrap: add a warning about deprecating unencrypted/unauthenticated knet transport (jsc#PED-16433) +- Dev: bootstrap: update warning about deprecating legacy transports (jsc#PED-16433) +- Dev: help: Show alias command info (#2128) +- Dev: help: Refactor to encapsulate alias help logic in subclass +- Dev: help: Give the correct level index to fuzzy search for topic help +- Dev: crm.8.adoc: Improve crm.8.adoc +- Dev: utils: Add class MultipathInspector to inspect multipath devices (#2116) +- Fix: sbd: Verify sbd devices on all nodes (#2118) +- Dev: ui_sbd: Call SBDUtils.verify_sbd_device in multiple places +- Dev: sbd: Introduce function sbd.SBDUtils.get_sbd_device_metadata_raw +- Dev: sbd: Compare SBD device UUID optionally +- Dev: sbd: Call SBDUtils.verify_sbd_device when doing sbd health check +- Dev: bootstrap: Call join_sbd before changing corosync.conf +- Dev: log: Lower corosync config tokenizer log level to reduce noise and decouple logging from config (#2123) +- Refactor: crash_test: Use log filters to control output instead of removing handlers +- Refactor: log: replace crmsh.log.setup_logger with logging.setupLogger +- Dev: log: Use filters to control handler output and simplify logger setup +- Dev: corosync_config_format: lower the level of debug logs from tokenizer to DEBUG2 +- Refactor: log: Decouple logging from config module +- Fix: sbd: Compare SBD device UUID when adding SBD via sbd stage (#2120) +- Chore: github-actions: update to node.js 24 actions (#2119) +- Dev: qdevice: Configure "-p <port>" for /etc/sysconfig/corosync-qnetd on qnetd server (#2094) +- Dev: ui_cluster: Refactor qdevice options validation +- Dev: ui_cluster: Add --qnetd-port option and deprecate --qdevice-port option +- Dev: bootstrap: Find and show failed services (#2104) +- Dev: Separately check if corosync-qdevice.service is successfully started +- Dev: service_manager: Use systemctl is-active to make sure the service is really active +- Fix: sbd: Set bootstrap_context.diskless_sbd to True when diskless SBD is enabled on interactive mode (#2106) + * Thu May 7 2026 Xin Liang <[email protected]> - Release 5.1.0 rc1 - Dev: bootstrap: Load profiles if needed (#2102) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.1.0+20260624.349562dd/data-manifest new/crmsh-5.1.0+20260629.0b862e06/data-manifest --- old/crmsh-5.1.0+20260624.349562dd/data-manifest 2026-06-24 06:16:33.000000000 +0200 +++ new/crmsh-5.1.0+20260629.0b862e06/data-manifest 2026-06-29 12:31:02.000000000 +0200 @@ -215,6 +215,7 @@ test/unittests/test_scripts.py test/unittests/test_service_manager.py test/unittests/test_sh.py +test/unittests/test_storage_utils.py test/unittests/test_time.py test/unittests/test_ui_cluster.py test/unittests/test_ui_corosync.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.1.0+20260624.349562dd/test/unittests/test_storage_utils.py new/crmsh-5.1.0+20260629.0b862e06/test/unittests/test_storage_utils.py --- old/crmsh-5.1.0+20260624.349562dd/test/unittests/test_storage_utils.py 1970-01-01 01:00:00.000000000 +0100 +++ new/crmsh-5.1.0+20260629.0b862e06/test/unittests/test_storage_utils.py 2026-06-29 12:31:02.000000000 +0200 @@ -0,0 +1,389 @@ +# Copyright (C) 2026 SUSE LLC +# See COPYING for license information. +# +# unit tests for storage_utils.py + +import unittest +import pytest +from pathlib import Path +from unittest import mock + +from crmsh import storage_utils, constants + + [email protected]('crmsh.storage_utils.get_dev_info') +def test_has_dev_partitioned(mock_get_dev_info): + mock_get_dev_info.return_value = """ +disk +part + """ + res = storage_utils.has_dev_partitioned("/dev/sda1") + assert res is True + mock_get_dev_info.assert_called_once_with("/dev/sda1", "NAME", peer=None) + + [email protected]('crmsh.storage_utils.get_dev_uuid') +def test_compare_uuid_with_peer_dev_cannot_find_local(mock_get_dev_uuid): + mock_get_dev_uuid.return_value = "" + with pytest.raises(ValueError) as err: + storage_utils.compare_uuid_with_peer_dev(["/dev/sdb1"], "node2") + assert str(err.value) == "Cannot find UUID for /dev/sdb1 on local" + mock_get_dev_uuid.assert_called_once_with("/dev/sdb1") + + [email protected]('crmsh.storage_utils.get_dev_uuid') +def test_compare_uuid_with_peer_dev_cannot_find_peer(mock_get_dev_uuid): + mock_get_dev_uuid.side_effect = ["1234", ""] + with pytest.raises(ValueError) as err: + storage_utils.compare_uuid_with_peer_dev(["/dev/sdb1"], "node2") + assert str(err.value) == "Cannot find UUID for /dev/sdb1 on node2" + mock_get_dev_uuid.assert_has_calls([ + mock.call("/dev/sdb1"), + mock.call("/dev/sdb1", "node2") + ]) + + [email protected]('crmsh.storage_utils.get_dev_uuid') +def test_compare_uuid_with_peer_dev(mock_get_dev_uuid): + mock_get_dev_uuid.side_effect = ["1234", "5678"] + with pytest.raises(ValueError) as err: + storage_utils.compare_uuid_with_peer_dev(["/dev/sdb1"], "node2") + assert str(err.value) == "UUID of /dev/sdb1 not same with peer node2" + mock_get_dev_uuid.assert_has_calls([ + mock.call("/dev/sdb1"), + mock.call("/dev/sdb1", "node2") + ]) + + [email protected]('crmsh.storage_utils.get_dev_info') +def test_is_dev_used_for_lvm(mock_dev_info): + mock_dev_info.return_value = "lvm" + res = storage_utils.is_dev_used_for_lvm("/dev/sda1") + assert res is True + mock_dev_info.assert_called_once_with("/dev/sda1", "TYPE", peer=None) + + [email protected]('crmsh.storage_utils.get_dev_info') +def test_is_dev_a_plain_raw_disk_or_partition(mock_dev_info): + mock_dev_info.return_value = "raid1\nlvm" + res = storage_utils.is_dev_a_plain_raw_disk_or_partition("/dev/md127") + assert res is False + mock_dev_info.assert_called_once_with("/dev/md127", "TYPE", peer=None) + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_get_dev_info(mock_run): + mock_run.return_value = "data" + res = storage_utils.get_dev_info("/dev/sda1", "TYPE") + assert res == "data" + mock_run.assert_called_once_with("lsblk -fno TYPE /dev/sda1", None) + + [email protected]('crmsh.storage_utils.get_dev_info') +def test_get_dev_fs_type(mock_get_info): + mock_get_info.return_value = "data" + res = storage_utils.get_dev_fs_type("/dev/sda1") + assert res == "data" + mock_get_info.assert_called_once_with("/dev/sda1", "FSTYPE", peer=None) + + [email protected]('crmsh.storage_utils.get_dev_info') +def test_get_dev_uuid(mock_get_info): + mock_get_info.return_value = "uuid" + res = storage_utils.get_dev_uuid("/dev/sda1") + assert res == "uuid" + mock_get_info.assert_called_once_with("/dev/sda1", "UUID", peer=None) + + [email protected]('crmsh.storage_utils.get_dev_uuid_by_blkid') [email protected]('crmsh.storage_utils.get_dev_info') +def test_get_dev_uuid_falls_back_to_blkid(mock_get_info, mock_blkid): + mock_get_info.return_value = "" + mock_blkid.return_value = "blkid-uuid" + res = storage_utils.get_dev_uuid("/dev/sda1") + assert res == "blkid-uuid" + mock_get_info.assert_called_once_with("/dev/sda1", "UUID", peer=None) + mock_blkid.assert_called_once_with("/dev/sda1", None) + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_get_dev_uuid_by_blkid(mock_run): + mock_run.return_value = '/dev/sda1: UUID="abc-123" TYPE="ext4"' + res = storage_utils.get_dev_uuid_by_blkid("/dev/sda1") + assert res == "abc-123" + mock_run.assert_called_once_with("blkid /dev/sda1", None) + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_get_dev_uuid_by_blkid_no_uuid(mock_run): + mock_run.return_value = "/dev/sda1: TYPE=\"swap\"" + res = storage_utils.get_dev_uuid_by_blkid("/dev/sda1") + assert res is None + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_get_pe_number_except(mock_run): + mock_run.return_value = "data" + with pytest.raises(ValueError) as err: + storage_utils.get_pe_number("vg1") + assert str(err.value) == "Cannot find PE on VG(vg1)" + mock_run.assert_called_once_with("vgdisplay vg1") + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_get_pe_number(mock_run): + mock_run.return_value = """ +PE Size 4.00 MiB +Total PE 1534 +Alloc PE / Size 1534 / 5.99 GiB + """ + res = storage_utils.get_pe_number("vg1") + assert res == 1534 + mock_run.assert_called_once_with("vgdisplay vg1") + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_get_all_vg_name(mock_run): + mock_run.return_value = """ +--- Volume group --- + VG Name ocfs2-vg + System ID + """ + res = storage_utils.get_all_vg_name() + assert res == ["ocfs2-vg"] + mock_run.assert_called_once_with("vgdisplay") + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_has_mount_point_used(mock_run): + mock_run.return_value = """ +/dev/vda2 on /usr/local type btrfs (rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local) +/dev/vda2 on /opt type btrfs (rw,relatime,space_cache,subvolid=263,subvol=/@/opt) +/dev/vda2 on /var/lib/docker/btrfs type btrfs (rw,relatime,space_cache,subvolid=258,subvol=/@/var) + """ + res = storage_utils.has_mount_point_used("/opt") + assert res is True + mock_run.assert_called_once_with("mount") + + [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') +def test_has_disk_mounted(mock_run): + mock_run.return_value = """ +/dev/vda2 on /usr/local type btrfs (rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local) +/dev/vda2 on /opt type btrfs (rw,relatime,space_cache,subvolid=263,subvol=/@/opt) +/dev/vda2 on /var/lib/docker/btrfs type btrfs (rw,relatime,space_cache,subvolid=258,subvol=/@/var) + """ + res = storage_utils.has_disk_mounted("/dev/vda2") + assert res is True + mock_run.assert_called_once_with("mount") + + [email protected]('crmsh.sh.cluster_shell') +def test_get_non_block_device_nodes(mock_cluster_shell): + mock_cluster_shell_inst = mock.Mock() + mock_cluster_shell.return_value = mock_cluster_shell_inst + mock_cluster_shell_inst.get_rc_stdout_stderr_without_input.return_value = (1, None, None) + res = storage_utils.get_non_block_device_nodes("/dev/sda1", ["node1"]) + assert res == ["node1"] + mock_cluster_shell_inst.get_rc_stdout_stderr_without_input.assert_called_once_with("node1", "test -b /dev/sda1") + + +def test_detect_duplicate_device_path_no_duplicate(): + storage_utils.detect_duplicate_device_path(["/dev/sda1", "/dev/sdb1"]) + + [email protected]('pathlib.Path.resolve') +def test_detect_duplicate_device_path_raises(mock_resolve): + mock_resolve.return_value = Path("/dev/sda1") + with pytest.raises(ValueError) as err: + storage_utils.detect_duplicate_device_path(["/dev/sda1", "/dev/disk/by-id/sda1"]) + assert "Duplicated device path detected" in str(err.value) + assert "/dev/sda1" in str(err.value) + + [email protected]('crmsh.sh.cluster_shell') +def test_get_dlm_option_dict(mock_run): + mock_run_inst = mock.Mock() + mock_run.return_value = mock_run_inst + mock_run_inst.get_stdout_or_raise_error.return_value = """ +key1=value1 +key2=value2 + """ + res_dict = storage_utils.get_dlm_option_dict() + assert res_dict == { + "key1": "value1", + "key2": "value2" + } + mock_run_inst.get_stdout_or_raise_error.assert_called_once_with("dlm_tool dump_config", None) + + [email protected]('crmsh.storage_utils.get_dlm_option_dict') +def test_set_dlm_option_exception(mock_get_dict): + mock_get_dict.return_value = { + "key1": "value1", + "key2": "value2" + } + with pytest.raises(ValueError) as err: + storage_utils.set_dlm_option(name="xin") + assert str(err.value) == '"name" is not dlm config option' + + [email protected]('crmsh.sh.cluster_shell') [email protected]('crmsh.storage_utils.get_dlm_option_dict') +def test_set_dlm_option(mock_get_dict, mock_run): + mock_run_inst = mock.Mock() + mock_run.return_value = mock_run_inst + mock_get_dict.return_value = { + "key1": "value1", + "key2": "value2" + } + storage_utils.set_dlm_option(key2="test") + mock_run_inst.get_stdout_or_raise_error.assert_called_once_with('dlm_tool set_config "key2=test"', None) + + [email protected]('crmsh.xmlutil.CrmMonXmlParser') +def test_is_dlm_configured(mock_crmmon): + mock_crmmon_inst = mock.Mock() + mock_crmmon.return_value = mock_crmmon_inst + mock_crmmon_inst.is_resource_configured.return_value = True + assert storage_utils.is_dlm_configured() is True + mock_crmmon_inst.is_resource_configured.assert_called_once_with(constants.DLM_CONTROLD_RA) + + [email protected]('crmsh.xmlutil.CrmMonXmlParser') +def test_is_dlm_running(mock_crmmon): + mock_crmmon_inst = mock.Mock() + mock_crmmon.return_value = mock_crmmon_inst + mock_crmmon_inst.is_resource_started.return_value = True + assert storage_utils.is_dlm_running() is True + mock_crmmon_inst.is_resource_started.assert_called_once_with(constants.DLM_CONTROLD_RA, node=None) + + [email protected]('crmsh.xmlutil.CrmMonXmlParser') +def test_is_dlm_running_on_node(mock_crmmon): + mock_crmmon_inst = mock.Mock() + mock_crmmon.return_value = mock_crmmon_inst + mock_crmmon_inst.is_resource_started.return_value = False + assert storage_utils.is_dlm_running(on_node="node1") is False + mock_crmmon_inst.is_resource_started.assert_called_once_with(constants.DLM_CONTROLD_RA, node="node1") + + [email protected]('crmsh.storage_utils.is_dlm_configured') +def test_check_no_quorum_policy_with_dlm_return(mock_dlm): + mock_dlm.return_value = False + storage_utils.check_no_quorum_policy_with_dlm() + mock_dlm.assert_called_once_with() + + [email protected]('logging.Logger.warning') [email protected]('crmsh.utils.get_property') [email protected]('crmsh.storage_utils.is_dlm_configured') +def test_check_no_quorum_policy_with_dlm(mock_dlm, mock_get_property, mock_warn): + mock_dlm.return_value = True + mock_get_property.return_value = "stop" + storage_utils.check_no_quorum_policy_with_dlm() + mock_dlm.assert_called_once_with() + mock_get_property.assert_called_once_with("no-quorum-policy") + mock_warn.assert_called_once_with('The DLM cluster best practice suggests to set the cluster property "no-quorum-policy=freeze"') + + +class TestMultipathInspector(unittest.TestCase): + + @mock.patch('crmsh.sh.cluster_shell') + def test_init(self, mock_cluster_shell): + """Test MultipathInspector initialization""" + mock_shell_inst = mock.Mock() + mock_cluster_shell.return_value = mock_shell_inst + mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ + (0, "sda", ""), # lsblk output for parent device + (0, "dev multipath\nsda mpatha", "") # multipathd show paths output + ] + + inspector = storage_utils.MultipathInspector("/dev/sda1") + + assert inspector._shell == mock_shell_inst + assert inspector._device_info.device == "/dev/sda1" + assert inspector._device_info.parent_device == "sda" + assert inspector._device_info.under_multipath is True + + @mock.patch('crmsh.sh.cluster_shell') + def test_get_parent_device(self, mock_cluster_shell): + """Test _get_parent_device method""" + mock_shell_inst = mock.Mock() + mock_cluster_shell.return_value = mock_shell_inst + mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ + (0, "sda", ""), # lsblk output for __init__ + (0, "", ""), # multipathd show paths output for __init__ + (0, "sda", "") # lsblk output for test call + ] + + inspector = storage_utils.MultipathInspector("/dev/sda1") + parent = inspector._get_parent_device("/dev/sda1") + + assert parent == "sda" + + @mock.patch('crmsh.sh.cluster_shell') + def test_get_multipath_mapping(self, mock_cluster_shell): + """Test _get_multipath_mapping method with valid output""" + mock_shell_inst = mock.Mock() + mock_cluster_shell.return_value = mock_shell_inst + multipathd_output = """dev multipath +sda mpatha +sdb mpatha +sdc mpathb""" + mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ + (0, "sda", ""), # lsblk for __init__ + (0, multipathd_output, ""), # multipathd for __init__ + (0, multipathd_output, "") # multipathd for test call + ] + + inspector = storage_utils.MultipathInspector("/dev/sda1") + mapping = inspector._get_multipath_mapping() + + assert mapping == {"sda": "mpatha", "sdb": "mpatha", "sdc": "mpathb"} + + @mock.patch('crmsh.sh.cluster_shell') + def test_inspect_device_under_multipath(self, mock_cluster_shell): + """Test _inspect method when device is under multipath""" + mock_shell_inst = mock.Mock() + mock_cluster_shell.return_value = mock_shell_inst + mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ + (0, "sda", ""), + (0, "dev multipath\nsda mpatha", "") + ] + + inspector = storage_utils.MultipathInspector("/dev/sda1") + device_info = inspector._device_info + + assert device_info.device == "/dev/sda1" + assert device_info.parent_device == "sda" + assert device_info.under_multipath is True + + @mock.patch('crmsh.sh.cluster_shell') + def test_is_under_multipath_true(self, mock_cluster_shell): + """Test _is_under_multipath returns True""" + mock_shell_inst = mock.Mock() + mock_cluster_shell.return_value = mock_shell_inst + mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ + (0, "sda", ""), + (0, "dev multipath\nsda mpatha", "") + ] + + inspector = storage_utils.MultipathInspector("/dev/sda1") + + assert inspector._is_under_multipath() is True + + @mock.patch('crmsh.sh.cluster_shell') + def test_check_device_under_multipath_raises_error(self, mock_cluster_shell): + """Test check_device_under_multipath raises ValueError when device is under multipath""" + mock_shell_inst = mock.Mock() + mock_cluster_shell.return_value = mock_shell_inst + mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ + (0, "sda", ""), + (0, "dev multipath\nsda mpatha", "") + ] + + with pytest.raises(ValueError) as exc_info: + storage_utils.MultipathInspector.check_device_under_multipath("/dev/sda1") + + assert str(exc_info.value) == "Device /dev/sda1 is under multipath, please provide the multipath device instead" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.1.0+20260624.349562dd/test/unittests/test_utils.py new/crmsh-5.1.0+20260629.0b862e06/test/unittests/test_utils.py --- old/crmsh-5.1.0+20260624.349562dd/test/unittests/test_utils.py 2026-06-24 06:16:33.000000000 +0200 +++ new/crmsh-5.1.0+20260629.0b862e06/test/unittests/test_utils.py 2026-06-29 12:31:02.000000000 +0200 @@ -13,7 +13,7 @@ from itertools import chain import crmsh.utils -from crmsh import utils, storage_utils, config, tmpfiles, constants, options +from crmsh import utils, config, tmpfiles, constants, options logging.basicConfig(level=logging.DEBUG) @@ -756,123 +756,6 @@ assert utils.re_split_string('[; ]', "/dev/sda1 ") == ["/dev/sda1"] [email protected]('crmsh.storage_utils.get_dev_info') -def test_has_dev_partitioned(mock_get_dev_info): - mock_get_dev_info.return_value = """ -disk -part - """ - res = storage_utils.has_dev_partitioned("/dev/sda1") - assert res is True - mock_get_dev_info.assert_called_once_with("/dev/sda1", "NAME", peer=None) - - [email protected]('crmsh.storage_utils.get_dev_uuid') -def test_compare_uuid_with_peer_dev_cannot_find_local(mock_get_dev_uuid): - mock_get_dev_uuid.return_value = "" - with pytest.raises(ValueError) as err: - storage_utils.compare_uuid_with_peer_dev(["/dev/sdb1"], "node2") - assert str(err.value) == "Cannot find UUID for /dev/sdb1 on local" - mock_get_dev_uuid.assert_called_once_with("/dev/sdb1") - - [email protected]('crmsh.storage_utils.get_dev_uuid') -def test_compare_uuid_with_peer_dev_cannot_find_peer(mock_get_dev_uuid): - mock_get_dev_uuid.side_effect = ["1234", ""] - with pytest.raises(ValueError) as err: - storage_utils.compare_uuid_with_peer_dev(["/dev/sdb1"], "node2") - assert str(err.value) == "Cannot find UUID for /dev/sdb1 on node2" - mock_get_dev_uuid.assert_has_calls([ - mock.call("/dev/sdb1"), - mock.call("/dev/sdb1", "node2") - ]) - - [email protected]('crmsh.storage_utils.get_dev_uuid') -def test_compare_uuid_with_peer_dev(mock_get_dev_uuid): - mock_get_dev_uuid.side_effect = ["1234", "5678"] - with pytest.raises(ValueError) as err: - storage_utils.compare_uuid_with_peer_dev(["/dev/sdb1"], "node2") - assert str(err.value) == "UUID of /dev/sdb1 not same with peer node2" - mock_get_dev_uuid.assert_has_calls([ - mock.call("/dev/sdb1"), - mock.call("/dev/sdb1", "node2") - ]) - - [email protected]('crmsh.storage_utils.get_dev_info') -def test_is_dev_used_for_lvm(mock_dev_info): - mock_dev_info.return_value = "lvm" - res = storage_utils.is_dev_used_for_lvm("/dev/sda1") - assert res is True - mock_dev_info.assert_called_once_with("/dev/sda1", "TYPE", peer=None) - - [email protected]('crmsh.storage_utils.get_dev_info') -def test_is_dev_a_plain_raw_disk_or_partition(mock_dev_info): - mock_dev_info.return_value = "raid1\nlvm" - res = storage_utils.is_dev_a_plain_raw_disk_or_partition("/dev/md127") - assert res is False - mock_dev_info.assert_called_once_with("/dev/md127", "TYPE", peer=None) - - [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') -def test_get_dev_info(mock_run): - mock_run.return_value = "data" - res = storage_utils.get_dev_info("/dev/sda1", "TYPE") - assert res == "data" - mock_run.assert_called_once_with("lsblk -fno TYPE /dev/sda1", None) - - [email protected]('crmsh.storage_utils.get_dev_info') -def test_get_dev_fs_type(mock_get_info): - mock_get_info.return_value = "data" - res = storage_utils.get_dev_fs_type("/dev/sda1") - assert res == "data" - mock_get_info.assert_called_once_with("/dev/sda1", "FSTYPE", peer=None) - - [email protected]('crmsh.storage_utils.get_dev_info') -def test_get_dev_uuid(mock_get_info): - mock_get_info.return_value = "uuid" - res = storage_utils.get_dev_uuid("/dev/sda1") - assert res == "uuid" - mock_get_info.assert_called_once_with("/dev/sda1", "UUID", peer=None) - - [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') -def test_get_pe_number_except(mock_run): - mock_run.return_value = "data" - with pytest.raises(ValueError) as err: - storage_utils.get_pe_number("vg1") - assert str(err.value) == "Cannot find PE on VG(vg1)" - mock_run.assert_called_once_with("vgdisplay vg1") - - [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') -def test_get_pe_number(mock_run): - mock_run.return_value = """ -PE Size 4.00 MiB -Total PE 1534 -Alloc PE / Size 1534 / 5.99 GiB - """ - res = storage_utils.get_pe_number("vg1") - assert res == 1534 - mock_run.assert_called_once_with("vgdisplay vg1") - - [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') -def test_get_all_vg_name(mock_run): - mock_run.return_value = """ ---- Volume group --- - VG Name ocfs2-vg - System ID - """ - res = storage_utils.get_all_vg_name() - assert res == ["ocfs2-vg"] - mock_run.assert_called_once_with("vgdisplay") - - @mock.patch('crmsh.utils.randomword') def test_gen_unused_id(mock_rand): mock_rand.return_value = "1234xxxx" @@ -901,30 +784,6 @@ mock_cib.refresh.assert_called_once_with() [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') -def test_has_mount_point_used(mock_run): - mock_run.return_value = """ -/dev/vda2 on /usr/local type btrfs (rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local) -/dev/vda2 on /opt type btrfs (rw,relatime,space_cache,subvolid=263,subvol=/@/opt) -/dev/vda2 on /var/lib/docker/btrfs type btrfs (rw,relatime,space_cache,subvolid=258,subvol=/@/var) - """ - res = storage_utils.has_mount_point_used("/opt") - assert res is True - mock_run.assert_called_once_with("mount") - - [email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error') -def test_has_disk_mounted(mock_run): - mock_run.return_value = """ -/dev/vda2 on /usr/local type btrfs (rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local) -/dev/vda2 on /opt type btrfs (rw,relatime,space_cache,subvolid=263,subvol=/@/opt) -/dev/vda2 on /var/lib/docker/btrfs type btrfs (rw,relatime,space_cache,subvolid=258,subvol=/@/var) - """ - res = storage_utils.has_disk_mounted("/dev/vda2") - assert res is True - mock_run.assert_called_once_with("mount") - - @mock.patch('crmsh.sbd.SBDUtils.is_using_diskless_sbd') @mock.patch('crmsh.sh.ClusterShell.get_stdout_or_raise_error') def test_has_fence_device_registered(mock_run, mock_diskless): @@ -939,16 +798,6 @@ mock_diskless.assert_called_once_with() [email protected]('crmsh.sh.cluster_shell') -def test_get_non_block_device_nodes(mock_cluster_shell): - mock_cluster_shell_inst = mock.Mock() - mock_cluster_shell.return_value = mock_cluster_shell_inst - mock_cluster_shell_inst.get_rc_stdout_stderr_without_input.return_value = (1, None, None) - res = storage_utils.get_non_block_device_nodes("/dev/sda1", ["node1"]) - assert res == ["node1"] - mock_cluster_shell_inst.get_rc_stdout_stderr_without_input.assert_called_once_with("node1", "test -b /dev/sda1") - - @mock.patch('crmsh.utils.ssh_port_reachable_check') @mock.patch('crmsh.xmlutil.CrmMonXmlParser') def test_check_all_nodes_reachable_dead_nodes(mock_xml, mock_reachable): @@ -987,55 +836,6 @@ mock_run.assert_called_once_with("systemd-detect-virt") [email protected]('crmsh.sh.cluster_shell') -def test_get_dlm_option_dict(mock_run): - mock_run_inst = mock.Mock() - mock_run.return_value = mock_run_inst - mock_run_inst.get_stdout_or_raise_error.return_value = """ -key1=value1 -key2=value2 - """ - res_dict = storage_utils.get_dlm_option_dict() - assert res_dict == { - "key1": "value1", - "key2": "value2" - } - mock_run_inst.get_stdout_or_raise_error.assert_called_once_with("dlm_tool dump_config", None) - - [email protected]('crmsh.storage_utils.get_dlm_option_dict') -def test_set_dlm_option_exception(mock_get_dict): - mock_get_dict.return_value = { - "key1": "value1", - "key2": "value2" - } - with pytest.raises(ValueError) as err: - storage_utils.set_dlm_option(name="xin") - assert str(err.value) == '"name" is not dlm config option' - - [email protected]('crmsh.sh.cluster_shell') [email protected]('crmsh.storage_utils.get_dlm_option_dict') -def test_set_dlm_option(mock_get_dict, mock_run): - mock_run_inst = mock.Mock() - mock_run.return_value = mock_run_inst - mock_get_dict.return_value = { - "key1": "value1", - "key2": "value2" - } - storage_utils.set_dlm_option(key2="test") - mock_run_inst.get_stdout_or_raise_error.assert_called_once_with('dlm_tool set_config "key2=test"', None) - - [email protected]('crmsh.xmlutil.CrmMonXmlParser') -def test_is_dlm_configured(mock_crmmon): - mock_crmmon_inst = mock.Mock() - mock_crmmon.return_value = mock_crmmon_inst - mock_crmmon_inst.is_resource_configured.return_value = True - assert storage_utils.is_dlm_configured() is True - mock_crmmon_inst.is_resource_configured.assert_called_once_with(constants.DLM_CONTROLD_RA) - - @mock.patch('crmsh.xmlutil.CrmMonXmlParser') def test_cluster_with_quorum(mock_crmmon): mock_crmmon_inst = mock.Mock() @@ -1150,25 +950,6 @@ mock_msec.assert_has_calls([mock.call("10s"), mock.call("10")]) [email protected]('crmsh.storage_utils.is_dlm_configured') -def test_check_no_quorum_policy_with_dlm_return(mock_dlm): - mock_dlm.return_value = False - storage_utils.check_no_quorum_policy_with_dlm() - mock_dlm.assert_called_once_with() - - [email protected]('logging.Logger.warning') [email protected]('crmsh.utils.get_property') [email protected]('crmsh.storage_utils.is_dlm_configured') -def test_check_no_quorum_policy_with_dlm(mock_dlm, mock_get_property, mock_warn): - mock_dlm.return_value = True - mock_get_property.return_value = "stop" - storage_utils.check_no_quorum_policy_with_dlm() - mock_dlm.assert_called_once_with() - mock_get_property.assert_called_once_with("no-quorum-policy") - mock_warn.assert_called_once_with('The DLM cluster best practice suggests to set the cluster property "no-quorum-policy=freeze"') - - @mock.patch('crmsh.corosync.is_qdevice_configured') @mock.patch('crmsh.utils.list_cluster_nodes') def test_is_2node_cluster_without_qdevice(mock_list, mock_is_qdevice): @@ -1479,104 +1260,3 @@ mock_error.assert_called_once_with("From the view of node '%s', node '%s' is not a member of the cluster", 'node1', 'node2') - -class TestMultipathInspector(unittest.TestCase): - - @mock.patch('crmsh.sh.cluster_shell') - def test_init(self, mock_cluster_shell): - """Test MultipathInspector initialization""" - mock_shell_inst = mock.Mock() - mock_cluster_shell.return_value = mock_shell_inst - mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ - (0, "sda", ""), # lsblk output for parent device - (0, "dev multipath\nsda mpatha", "") # multipathd show paths output - ] - - inspector = storage_utils.MultipathInspector("/dev/sda1") - - assert inspector._shell == mock_shell_inst - assert inspector._device_info.device == "/dev/sda1" - assert inspector._device_info.parent_device == "sda" - assert inspector._device_info.under_multipath is True - - @mock.patch('crmsh.sh.cluster_shell') - def test_get_parent_device(self, mock_cluster_shell): - """Test _get_parent_device method""" - mock_shell_inst = mock.Mock() - mock_cluster_shell.return_value = mock_shell_inst - mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ - (0, "sda", ""), # lsblk output for __init__ - (0, "", ""), # multipathd show paths output for __init__ - (0, "sda", "") # lsblk output for test call - ] - - inspector = storage_utils.MultipathInspector("/dev/sda1") - parent = inspector._get_parent_device("/dev/sda1") - - assert parent == "sda" - - @mock.patch('crmsh.sh.cluster_shell') - def test_get_multipath_mapping(self, mock_cluster_shell): - """Test _get_multipath_mapping method with valid output""" - mock_shell_inst = mock.Mock() - mock_cluster_shell.return_value = mock_shell_inst - multipathd_output = """dev multipath -sda mpatha -sdb mpatha -sdc mpathb""" - mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ - (0, "sda", ""), # lsblk for __init__ - (0, multipathd_output, ""), # multipathd for __init__ - (0, multipathd_output, "") # multipathd for test call - ] - - inspector = storage_utils.MultipathInspector("/dev/sda1") - mapping = inspector._get_multipath_mapping() - - assert mapping == {"sda": "mpatha", "sdb": "mpatha", "sdc": "mpathb"} - - @mock.patch('crmsh.sh.cluster_shell') - def test_inspect_device_under_multipath(self, mock_cluster_shell): - """Test _inspect method when device is under multipath""" - mock_shell_inst = mock.Mock() - mock_cluster_shell.return_value = mock_shell_inst - mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ - (0, "sda", ""), - (0, "dev multipath\nsda mpatha", "") - ] - - inspector = storage_utils.MultipathInspector("/dev/sda1") - device_info = inspector._device_info - - assert device_info.device == "/dev/sda1" - assert device_info.parent_device == "sda" - assert device_info.under_multipath is True - - @mock.patch('crmsh.sh.cluster_shell') - def test_is_under_multipath_true(self, mock_cluster_shell): - """Test _is_under_multipath returns True""" - mock_shell_inst = mock.Mock() - mock_cluster_shell.return_value = mock_shell_inst - mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ - (0, "sda", ""), - (0, "dev multipath\nsda mpatha", "") - ] - - inspector = storage_utils.MultipathInspector("/dev/sda1") - - assert inspector._is_under_multipath() is True - - @mock.patch('crmsh.sh.cluster_shell') - def test_check_device_under_multipath_raises_error(self, mock_cluster_shell): - """Test check_device_under_multipath raises ValueError when device is under multipath""" - mock_shell_inst = mock.Mock() - mock_cluster_shell.return_value = mock_shell_inst - mock_shell_inst.get_rc_stdout_stderr_without_input.side_effect = [ - (0, "sda", ""), - (0, "dev multipath\nsda mpatha", "") - ] - - with pytest.raises(ValueError) as exc_info: - storage_utils.MultipathInspector.check_device_under_multipath("/dev/sda1") - - assert str(exc_info.value) == "Device /dev/sda1 is under multipath, please provide the multipath device instead"
