Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2025-06-23 15:06:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new.7067 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Mon Jun 23 15:06:19 2025 rev:376 rq:1287917 version:5.0.0+20250623.50ad8e8f Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2025-06-20 16:50:59.656193125 +0200 +++ /work/SRC/openSUSE:Factory/.crmsh.new.7067/crmsh.changes 2025-06-23 15:07:24.003057911 +0200 @@ -1,0 +2,13 @@ +Mon Jun 23 11:41:49 UTC 2025 - xli...@suse.com + +- Update to version 5.0.0+20250623.50ad8e8f: + * Dev: unittests: Adjust unit test for previous commit + * Fix: crash_test: Correctly retrieve fence event information (bsc#1243786) + +------------------------------------------------------------------- +Mon Jun 23 03:11:08 UTC 2025 - xli...@suse.com + +- Update to version 5.0.0+20250623.b760f655: + * Dev: run-functional-tests: Fetch container's IP address correctly + +------------------------------------------------------------------- Old: ---- crmsh-5.0.0+20250619.5d3fc833.tar.bz2 New: ---- crmsh-5.0.0+20250623.50ad8e8f.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.RBNodP/_old 2025-06-23 15:07:26.379157004 +0200 +++ /var/tmp/diff_new_pack.RBNodP/_new 2025-06-23 15:07:26.379157004 +0200 @@ -41,7 +41,7 @@ Summary: High Availability cluster command-line interface License: GPL-2.0-or-later Group: %{pkg_group} -Version: 5.0.0+20250619.5d3fc833 +Version: 5.0.0+20250623.50ad8e8f Release: 0 URL: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.RBNodP/_old 2025-06-23 15:07:26.423158840 +0200 +++ /var/tmp/diff_new_pack.RBNodP/_new 2025-06-23 15:07:26.427159006 +0200 @@ -9,7 +9,7 @@ </service> <service name="tar_scm"> <param name="url">https://github.com/ClusterLabs/crmsh.git</param> - <param name="changesrevision">5d3fc8332df0226b8c6af6a9150204210aaae7d7</param> + <param name="changesrevision">50ad8e8fb153c925641f693a5f056f82e5a9c8a1</param> </service> </servicedata> (No newline at EOF) ++++++ crmsh-5.0.0+20250619.5d3fc833.tar.bz2 -> crmsh-5.0.0+20250623.50ad8e8f.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.0.0+20250619.5d3fc833/crmsh/crash_test/config.py new/crmsh-5.0.0+20250623.50ad8e8f/crmsh/crash_test/config.py --- old/crmsh-5.0.0+20250619.5d3fc833/crmsh/crash_test/config.py 2025-06-19 11:43:52.000000000 +0200 +++ new/crmsh-5.0.0+20250623.50ad8e8f/crmsh/crash_test/config.py 2025-06-23 13:15:35.000000000 +0200 @@ -4,6 +4,5 @@ iptables -{action} OUTPUT -d {peer_ip} -j DROP''' REMOVE_PORT = "firewall-cmd --zone=public --remove-port={port}/udp" ADD_PORT = "firewall-cmd --zone=public --add-port={port}/udp" -FENCE_HISTORY = "stonith_admin -h {node}" SBD_CONF = "/etc/sysconfig/sbd" SBD_CHECK_CMD = "sbd -d {dev} dump" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.0.0+20250619.5d3fc833/crmsh/crash_test/task.py new/crmsh-5.0.0+20250623.50ad8e8f/crmsh/crash_test/task.py --- old/crmsh-5.0.0+20250619.5d3fc833/crmsh/crash_test/task.py 2025-06-19 11:43:52.000000000 +0200 +++ new/crmsh-5.0.0+20250623.50ad8e8f/crmsh/crash_test/task.py 2025-06-23 13:15:35.000000000 +0200 @@ -1,11 +1,12 @@ import os -import re import time import threading import shutil import tempfile +from datetime import datetime from contextlib import contextmanager from crmsh import utils as crmshutils +from crmsh import xmlutil from crmsh import log from . import utils from . import config @@ -27,6 +28,7 @@ REBOOT_WARNING = """!!! WARNING WARNING WARNING !!! THIS CASE MAY LEAD TO NODE BE FENCED. TYPE Yes TO CONTINUE, OTHER INPUTS WILL CANCEL THIS CASE [Yes/No](No): """ + TIME_STR_FORMAT = '%Y/%m/%d %H:%M:%S' def __init__(self, description, flush=False, quiet=False): """ @@ -37,7 +39,7 @@ self.force = False self.quiet = quiet self.messages = [] - self.timestamp = utils.now() + self.timestamp = datetime.now() self.description = description utils.msg_info(self.description, to_stdout=False) self.flush = flush @@ -81,7 +83,7 @@ Build base results """ self.result = { - "Timestamp": self.timestamp, + "Timestamp": self.timestamp.strftime(self.TIME_STR_FORMAT), "Description": self.description, "Messages": ["{} {}:{}".format(m[2], m[0].upper(), m[1]) for m in self.messages] @@ -128,36 +130,24 @@ 1. There is one latest fence action successfully done 2. No fence action during fence timeout, thread_stop_event triggered by main thread """ - target_node = None - from_node = None - fence_timestamp = None - # Try to find out which node fire the fence action while not self.thread_stop_event.is_set(): - rc, out, _ = ShellUtils().get_stdout_stderr("crm_mon -1|grep -A1 \"Fencing Actions:\"") - if rc == 0 and out: - match = re.search(r"of (.*) pending: .*origin=(.*)$", out) - if match: - target_node, from_node = match.groups() - self.info("Node \"{}\" will be fenced by \"{}\"!".format(target_node, from_node)) + fence_event_dict = xmlutil.CrmMonXmlParser().get_last_fence_event_info() + if fence_event_dict: + target_node = fence_event_dict.get('target') + origin_node = fence_event_dict.get('origin') + complete_time = fence_event_dict.get('completed') + status = fence_event_dict.get('status') + if status == "pending" and not self.fence_start_event.is_set(): + self.info(f"Node \"{target_node}\" will be fenced by \"{origin_node}\"!") self.fence_start_event.set() - break - time.sleep(1) - - # Try to find out proof that fence happened - while not self.thread_stop_event.is_set(): - rc, out, _ = ShellUtils().get_stdout_stderr(config.FENCE_HISTORY.format(node=target_node)) - if rc == 0 and out: - match = re.search(r"Node {} last fenced at: (.*)".format(target_node), out) - if match: - fence_timestamp = match.group(1) - task_timestamp_dt = utils.str_to_datetime(self.timestamp, '%Y/%m/%d %H:%M:%S') - fence_timestamp_dt = utils.str_to_datetime(fence_timestamp, '%a %b %d %H:%M:%S %Y') - # If the fence action timestamp larger than this task's timestamp - # That is the proof - if task_timestamp_dt < fence_timestamp_dt: - self.info("Node \"{}\" was successfully fenced by \"{}\"".format(target_node, from_node)) - # Tell main thread fence happened + # Try to find out proof that fence happened + elif status == "success": + task_timestamp = self.timestamp.timestamp() + complete_timestamp = datetime.fromisoformat(complete_time).timestamp() + # This success event should after the task started + if task_timestamp < complete_timestamp: + self.info(f"Node \"{target_node}\" was fenced by \"{origin_node}\" at {complete_time}") self.fence_finish_event.set() break time.sleep(1) @@ -259,7 +249,7 @@ message = "{} [{}]".format(self.description, utils.CGREEN + "Pass" + utils.CEND) else: message = "{} [{}]".format(self.description, utils.CRED + "Fail" + utils.CEND) - logger.info(message, extra={'timestamp': '[{}]'.format(self.timestamp)}) + logger.info(message, extra={'timestamp': '[{}]'.format(self.timestamp.strftime(self.TIME_STR_FORMAT))}) for msg in self.messages: logger.log(utils.LEVEL[msg[0]], msg[1], extra={'timestamp': ' '}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.0.0+20250619.5d3fc833/crmsh/xmlutil.py new/crmsh-5.0.0+20250623.50ad8e8f/crmsh/xmlutil.py --- old/crmsh-5.0.0+20250619.5d3fc833/crmsh/xmlutil.py 2025-06-19 11:43:52.000000000 +0200 +++ new/crmsh-5.0.0+20250623.50ad8e8f/crmsh/xmlutil.py 2025-06-23 13:15:35.000000000 +0200 @@ -1574,4 +1574,19 @@ """ xpath = f'//resource[@resource_agent="{ra_type}"]' return [elem.get('id') for elem in self.xml_elem.xpath(xpath)] + + def get_last_fence_event_info(self) -> dict: + fence_event_info = {} + fence_events = self.xml_elem.xpath(r'//fence_history/fence_event') + if not fence_events: + return fence_event_info + last_event = fence_events[0] + fence_event_info = { + 'origin': last_event.get('origin', ''), + 'target': last_event.get('target', ''), + 'action': last_event.get('action', ''), + 'status': last_event.get('status', ''), + 'completed': last_event.get('completed', '') + } + return fence_event_info # vim:ts=4:sw=4:et: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.0.0+20250619.5d3fc833/test/run-functional-tests new/crmsh-5.0.0+20250623.50ad8e8f/test/run-functional-tests --- old/crmsh-5.0.0+20250619.5d3fc833/test/run-functional-tests 2025-06-19 11:43:52.000000000 +0200 +++ new/crmsh-5.0.0+20250623.50ad8e8f/test/run-functional-tests 2025-06-23 13:15:35.000000000 +0200 @@ -246,7 +246,10 @@ config_cluster() { node_num=$# insert_str="" - container_ip_array=(`podman network inspect $HA_NETWORK_ARRAY -f '{{range .Containers}}{{printf "%s " .IPv4Address}}{{end}}'`) + for node in $*;do + ip=`podman container inspect $node -f "{{.NetworkSettings.Networks.$HA_NETWORK_ARRAY.IPAddress}}"|tr -d "\r"` + container_ip_array+=($ip) + done for i in $(seq $node_num -1 1);do ip=`echo ${container_ip_array[$((i-1))]}|awk -F/ '{print $1}'` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-5.0.0+20250619.5d3fc833/test/unittests/test_crashtest_task.py new/crmsh-5.0.0+20250623.50ad8e8f/test/unittests/test_crashtest_task.py --- old/crmsh-5.0.0+20250619.5d3fc833/test/unittests/test_crashtest_task.py 2025-06-19 11:43:52.000000000 +0200 +++ new/crmsh-5.0.0+20250623.50ad8e8f/test/unittests/test_crashtest_task.py 2025-06-23 13:15:35.000000000 +0200 @@ -203,14 +203,14 @@ """ @mock.patch('crmsh.crash_test.utils.msg_info') - @mock.patch('crmsh.crash_test.utils.now') - def setUp(self, mock_now, mock_msg_info): + def setUp(self, mock_msg_info): """ Test setUp. """ - mock_now.return_value = "2019/07/10 01:15:15" + fake_timedate = datetime(2019, 7, 10, 1, 15, 15) main.ctx = mock.Mock(task_list=[{"process_name": "xin", "age": 38}]) self.task_check_inst = task.TaskCheck("task check job1", quiet=False) + self.task_check_inst.timestamp = fake_timedate self.task_check_inst_quiet = task.TaskCheck("task check job1", quiet=True) def tearDown(self): @@ -559,15 +559,14 @@ """ @mock.patch('crmsh.crash_test.utils.msg_info') - @mock.patch('crmsh.crash_test.utils.now') - def setUp(self, mock_now, mock_info): + def setUp(self, mock_info): """ Test setUp. """ - mock_now.return_value = "2019/07/10 01:15:15" + fake_now = datetime(2019, 7, 10, 1, 15, 15) main.ctx = mock.Mock(task_list={"process_name": "xin", "age": 38}) self.task_inst = task.Task("task description", flush=True) - mock_now.assert_called_once_with() + self.task_inst.timestamp = fake_now def tearDown(self): """ @@ -647,7 +646,7 @@ def test_build_base_result(self): self.task_inst.build_base_result() expected_result = { - "Timestamp": self.task_inst.timestamp, + "Timestamp": '2019/07/10 01:15:15', "Description": self.task_inst.description, "Messages": [] } @@ -666,45 +665,33 @@ mock_ask.assert_called_once_with(task.Task.REBOOT_WARNING) self.task_inst.info.assert_called_once_with("Testcase cancelled") - @mock.patch('crmsh.crash_test.utils.str_to_datetime') @mock.patch('time.sleep') @mock.patch('crmsh.crash_test.task.Task.info') - @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr') - def test_fence_action_monitor(self, mock_run, mock_info, mock_sleep, mock_datetime): + @mock.patch('crmsh.xmlutil.CrmMonXmlParser') + def test_fence_action_monitor(self, mock_parser, mock_info, mock_sleep): self.task_inst.thread_stop_event = mock.Mock() - self.task_inst.thread_stop_event.is_set.side_effect = [False, False, False, False] + self.task_inst.thread_stop_event.is_set.side_effect = [False, False] self.task_inst.fence_start_event = mock.Mock() + self.task_inst.fence_start_event.is_set.side_effect = [False, True] self.task_inst.fence_finish_event = mock.Mock() - output = "Pending Fencing Actions:\n * reboot of 15sp2-2 pending: client=pacemaker-controld.2430, origin=15sp2-1" - output2 = "Node 15sp2-2 last fenced at: Tue Jan 19 16:08:37 2021" - mock_run.side_effect = [(1, None, None), (0, output, None), (1, None, None), (0, output2, None)] - self.task_inst.timestamp = "2021/01/19 16:08:24" - mock_datetime.side_effect = [ - datetime.strptime(self.task_inst.timestamp, '%Y/%m/%d %H:%M:%S'), - datetime.strptime("Tue Jan 19 16:08:37 2021", '%a %b %d %H:%M:%S %Y') + mock_parser_inst = mock.Mock() + mock_parser.return_value = mock_parser_inst + mock_parser_inst.get_last_fence_event_info.side_effect = [ + {"target": "node2", "origin": "node1", "status": "pending", "completed": ""}, + {"target": "node2", "origin": "node1", "status": "success", "completed": "2025-05-30 10:41:58.376958 +08:00"}, ] self.task_inst.fence_action_monitor() - self.task_inst.thread_stop_event.is_set.assert_has_calls([ - mock.call(), - mock.call(), - mock.call(), - mock.call() - ]) - mock_run.assert_has_calls([ - mock.call("crm_mon -1|grep -A1 \"Fencing Actions:\""), - mock.call("crm_mon -1|grep -A1 \"Fencing Actions:\""), - mock.call(config.FENCE_HISTORY.format(node="15sp2-2")), - mock.call(config.FENCE_HISTORY.format(node="15sp2-2")) - ]) + self.task_inst.thread_stop_event.is_set.assert_has_calls([mock.call(), mock.call()]) mock_info.assert_has_calls([ - mock.call("Node \"15sp2-2\" will be fenced by \"15sp2-1\"!"), - mock.call("Node \"15sp2-2\" was successfully fenced by \"15sp2-1\"") + mock.call("Node \"node2\" will be fenced by \"node1\"!"), + mock.call("Node \"node2\" was fenced by \"node1\" at 2025-05-30 10:41:58.376958 +08:00") ]) self.task_inst.fence_start_event.set.assert_called_once_with() self.task_inst.fence_finish_event.set.assert_called_once_with() + class TestFixSBD(TestCase): """ Class to test TaskFixSBD of task.py