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-04-21 12:44:27
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.11940 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Tue Apr 21 12:44:27 2026 rev:405 rq:1348352 version:5.0.0+20260420.d13e03ac

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2026-04-12 
17:53:08.025173387 +0200
+++ /work/SRC/openSUSE:Factory/.crmsh.new.11940/crmsh.changes   2026-04-21 
12:47:12.588370388 +0200
@@ -1,0 +2,36 @@
+Mon Apr 20 06:53:40 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260420.d13e03ac:
+  * Fix: ui_cluster: rename: Sync corosync.conf when at least one peer exists
+  * Dev: utils: Improve check_port_open to concurrently try all addresses 
(bsc#1262094)
+
+-------------------------------------------------------------------
+Fri Apr 17 09:39:19 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260417.27bde1ec:
+  * Dev: corosync: Check qdevice vote while checking corosync status
+  * Dev: ui_cluster: Sleep 1s before checking qdevice vote
+
+-------------------------------------------------------------------
+Thu Apr 16 10:09:24 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260416.1ff83b5b:
+  * Fix: watchdog: use cluster_shell() for remote watchdog query (#2078)
+
+-------------------------------------------------------------------
+Wed Apr 15 06:00:58 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260415.cbb6a8b2:
+  * Dev: unittests: Adjust unit test for previous commit
+  * Fix: qdevice: Change suggestion message when detecting cluster name 
conflict (bsc#1261884)
+  * Dev: ui_cluster: Improve 'rename' subcommand
+
+-------------------------------------------------------------------
+Wed Apr 15 01:39:31 UTC 2026 - [email protected]
+
+- Update to version 5.0.0+20260415.89daedf5:
+  * Dev: unittests: Adjust unit test for previous commit
+  * Dev: behave: Adjust functional test for previous commit
+  * Dev: Use xmlutil.CrmMonXmlParser.get_node_list instead of parsing output 
of 'crm_node -l'
+
+-------------------------------------------------------------------

Old:
----
  crmsh-5.0.0+20260412.db68d024.tar.bz2

New:
----
  crmsh-5.0.0+20260420.d13e03ac.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.sKfQeX/_old  2026-04-21 12:47:14.696457833 +0200
+++ /var/tmp/diff_new_pack.sKfQeX/_new  2026-04-21 12:47:14.696457833 +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+20260412.db68d024
+Version:        5.0.0+20260420.d13e03ac
 Release:        0
 URL:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.sKfQeX/_old  2026-04-21 12:47:14.756460322 +0200
+++ /var/tmp/diff_new_pack.sKfQeX/_new  2026-04-21 12:47:14.760460488 +0200
@@ -9,7 +9,7 @@
 </service>
 <service name="tar_scm">
   <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
-  <param 
name="changesrevision">cf3a81412188c120b0b69ee59c2066143aec463f</param>
+  <param 
name="changesrevision">d13e03ac0144d910af7fdfcf06faffe71cfdffe8</param>
 </service>
 </servicedata>
 (No newline at EOF)

++++++ crmsh-5.0.0+20260412.db68d024.tar.bz2 -> 
crmsh-5.0.0+20260420.d13e03ac.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/bootstrap.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/bootstrap.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/bootstrap.py        2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/bootstrap.py        2026-04-20 
08:35:08.000000000 +0200
@@ -1487,8 +1487,8 @@
 
     init_cluster_local()
 
-    _rc, nnodes = ShellUtils().get_stdout("crm_node -l")
-    nnodes = len(nnodes.splitlines())
+    node_list = xmlutil.CrmMonXmlParser().get_node_list(online=True, 
node_type="member")
+    nnodes = len(node_list)
     if nnodes < 1:
         utils.fatal("No nodes found in cluster")
     if nnodes > 1:
@@ -1837,7 +1837,8 @@
         stderr=subprocess.DEVNULL,
     )
 
-    hosts = utils.fetch_cluster_node_list_from_node(cluster_node) + 
[utils.this_node()]
+    node_list = 
xmlutil.CrmMonXmlParser(cluster_node).get_node_list(online=True, 
node_type="member")
+    hosts = node_list + [utils.this_node()]
     known_hosts_new: set[str] = set()
     cat_cmd = "[ -e ~/.ssh/known_hosts ] && cat ~/.ssh/known_hosts || true"
     for host in hosts:
@@ -2871,7 +2872,7 @@
     """
     node_list = []
     if peer_node:
-        node_list = utils.fetch_cluster_node_list_from_node(peer_node)
+        node_list = 
xmlutil.CrmMonXmlParser(peer_node).get_node_list(online=True, 
node_type="member")
     utils.cluster_copy_path(path, nodes=node_list)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/corosync.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/corosync.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/corosync.py 2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/corosync.py 2026-04-20 
08:35:08.000000000 +0200
@@ -16,6 +16,7 @@
 from . import log
 from . import corosync_config_format
 from . import xmlutil
+from . import qdevice
 from .sh import ShellUtils
 
 
@@ -114,6 +115,7 @@
         out = sh.cluster_shell().get_stdout_or_raise_error("crm_node -l")
         print(f"{out}\n")
         print(status_func_dict[status_type]())
+        qdevice.QDevice.check_qdevice_vote()
     else:
         raise ValueError("Wrong type \"{}\" to query 
status".format(status_type))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/crmsh/crash_test/task.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/crash_test/task.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/crash_test/task.py  2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/crash_test/task.py  2026-04-20 
08:35:08.000000000 +0200
@@ -199,8 +199,8 @@
             if rc != 0 and err:
                 raise TaskError(err)
 
-        if not utils.check_node_status(self.target_node, 'member'):
-            raise TaskError("Node \"{}\" not in 
cluster!".format(self.target_node))
+        if self.target_node not in utils.online_nodes():
+            raise TaskError(f"Node \"{self.target_node}\" is not in cluster!")
 
     def run(self):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/crmsh/crash_test/utils.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/crash_test/utils.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/crash_test/utils.py 2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/crash_test/utils.py 2026-04-20 
08:35:08.000000000 +0200
@@ -135,20 +135,6 @@
         return config.FENCE_TIMEOUT
 
 
-def check_node_status(node, state):
-    """
-    Check whether the node has expected state
-    """
-    rc, stdout, stderr = ShellUtils().get_stdout_stderr('crm_node -l')
-    if rc != 0:
-        msg_error(stderr)
-        return False
-    pattern = re.compile(r'^.* {} {}'.format(node, state), re.MULTILINE)
-    if not pattern.search(stdout):
-        return False
-    return True
-
-
 def online_nodes():
     """
     Get online node list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/lock.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/lock.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/lock.py     2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/lock.py     2026-04-20 
08:35:08.000000000 +0200
@@ -9,6 +9,7 @@
 from . import sh
 from . import config
 from . import log
+from . import xmlutil
 from .sh import ShellUtils
 
 
@@ -134,15 +135,6 @@
             raise ValueError("Minimum value of core.lock_timeout should be 
{}".format(self.MIN_LOCK_TIMEOUT))
         return value
 
-    def _get_online_nodelist(self):
-        """
-        Get the online node list from remote node
-        """
-        rc, out, err = self._run("crm_node -l")
-        if rc != 0 and err:
-            raise ValueError(err)
-        return re.findall('[0-9]+ (.*) member', out)
-
     def _lock_or_wait(self):
         """
         Try to claim lock on remote node, wait if failed to claim
@@ -164,7 +156,7 @@
 
             if self.for_join:
                 # Might lose claiming lock again, start to wait again
-                online_list = self._get_online_nodelist()
+                online_list = 
xmlutil.CrmMonXmlParser(self.remote_node).get_node_list(online=True, 
node_type="member")
                 if pre_online_list and pre_online_list != online_list:
                     timeout = current_time + self.lock_timeout
                 pre_online_list = online_list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/qdevice.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/qdevice.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/qdevice.py  2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/qdevice.py  2026-04-20 
08:35:08.000000000 +0200
@@ -271,7 +271,11 @@
             cmd = f"corosync-qnetd-tool -l -c {self.cluster_name}"
             if shell.get_stdout_or_raise_error(cmd, self.qnetd_addr):
                 exception_msg = f"This cluster's name \"{self.cluster_name}\" 
already exists on qnetd server!"
-                suggestion_msg = "Please consider to use the different 
cluster-name property"
+                if self.is_stage:
+                    suggestion_msg = "Please consider to use `crm cluster 
rename` to change a different cluster name."
+                else:
+                    suggestion_msg = "Please consider to use -n option to 
specify a different cluster name."
+                suggestion_msg += "\nOr, run `crm cluster remove --qdevice` on 
the existing cluster beforehand."
         else:
             exception_msg = f"Package \"corosync-qnetd\" not installed on 
{self.qnetd_addr}!"
             suggestion_msg = f"Please install \"corosync-qnetd\" on 
{self.qnetd_addr}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/ui_cluster.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/ui_cluster.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/ui_cluster.py       2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/ui_cluster.py       2026-04-20 
08:35:08.000000000 +0200
@@ -11,6 +11,7 @@
 import tarfile
 import subprocess
 import glob
+import time
 from argparse import ArgumentParser, RawDescriptionHelpFormatter
 
 import crmsh.parallax
@@ -200,6 +201,7 @@
             logger.info("The cluster stack started on %s", node)
 
         if start_qdevice and success_list:
+            time.sleep(1)
             qdevice.QDevice.check_qdevice_vote()
 
         return success_flag
@@ -611,23 +613,19 @@
             logger.info(suggestion)
             return
 
-        cib_factory = cibconfig.cib_factory_instance()
-        old_name = cib_factory.get_property('cluster-name')
+        old_name = corosync.get_value('totem.cluster_name')
         if old_name and new_name == old_name:
-            context.fatal_error("Expected a different name")
-
-        # Update config file with the new name on all nodes
-        nodes = utils.list_cluster_nodes()
+            context.fatal_error("Expected a different cluster name")
+        logger.info("Setting totem.cluster_name to %s in corosync.conf", 
new_name)
         corosync.set_value('totem.cluster_name', new_name)
-        if len(nodes) > 1:
-            nodes.remove(utils.this_node())
-            context.info("Copy cluster config file to \"{}\"".format(' 
'.join(nodes)))
+
+        nodes = utils.list_cluster_nodes_except_me()
+        if len(nodes) >= 1:
+            logger.info("Syncing corosync.conf to other nodes in the cluster")
             corosync.push_configuration(nodes)
 
-        # Change the cluster-name property in the CIB
-        cib_factory.create_object("property", 
"cluster-name={}".format(new_name))
-        if not cib_factory.commit():
-            context.fatal_error("Change property cluster-name failed!")
+        logger.info("Setting cluster-name property to %s in CIB", new_name)
+        utils.set_property("cluster-name", new_name)
 
         if xmlutil.CrmMonXmlParser().is_non_stonith_resource_running():
             context.info("To apply the change, restart the cluster service at 
convenient time")
@@ -915,7 +913,6 @@
     @command.completers_repeating(compl.choice(['10', '60', '600']))
     def do_wait_for_startup(self, context, timeout='10'):
         "usage: wait_for_startup [<timeout>]"
-        import time
         t0 = time.time()
         timeout = float(timeout)
         cmd = 'crm_mon -bD1 >/dev/null 2>&1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/utils.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/utils.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/utils.py    2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/utils.py    2026-04-20 
08:35:08.000000000 +0200
@@ -25,6 +25,7 @@
 import lzma
 import json
 import socket
+import selectors
 from pathlib import Path
 from collections import defaultdict
 from contextlib import contextmanager, closing
@@ -2087,22 +2088,64 @@
     return rc != 0
 
 
-def check_port_open(host, port, timeout=3) -> bool:
+def check_port_open(host, port, timeout=1.0, retry=3) -> bool:
     """
     Check whether the port is open on the host
     Use getaddrinfo to support both IPv4 and IPv6
     """
     try:
-        addrinfo = socket.getaddrinfo(host, port, socket.AF_UNSPEC, 
socket.SOCK_STREAM)
-        af, socktype, proto, canonname, sa = addrinfo[0]
-        with closing(socket.socket(af, socktype, proto)) as sock:
-            sock.settimeout(timeout)
-            if sock.connect_ex(sa) == 0:
-                return True
-        return False
+        addrinfos = socket.getaddrinfo(host, port, socket.AF_UNSPEC, 
socket.SOCK_STREAM)
     except socket.error:
         return False
 
+    for i in range(retry):
+        start_time = time.time()
+        sel = selectors.DefaultSelector()
+        sockets = []
+
+        for addrinfo in addrinfos:
+            af, socktype, proto, canonname, sa = addrinfo
+            sock = None
+            try:
+                sock = socket.socket(af, socktype, proto)
+                sock.setblocking(False)
+                if hasattr(socket, 'TCP_SYNCNT'):
+                    try:
+                        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_SYNCNT, 
1)
+                    except OSError:
+                        pass
+
+                err = sock.connect_ex(sa)
+                if err == 0:
+                    sel.close()
+                    for s in sockets:
+                        s.close()
+                    sock.close()
+                    return True
+
+                sel.register(sock, selectors.EVENT_WRITE)
+                sockets.append(sock)
+            except socket.error:
+                if sock:
+                    sock.close()
+
+        try:
+            events = sel.select(timeout)
+            for key, mask in events:
+                if key.fileobj.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) 
== 0:
+                    return True
+        finally:
+            sel.close()
+            for sock in sockets:
+                sock.close()
+
+        if i < retry - 1:
+            elapsed = time.time() - start_time
+            if elapsed < timeout:
+                time.sleep(timeout - elapsed)
+
+    return False
+
 
 def valid_port(port):
     return int(port) >= 1024 and int(port) <= 65535
@@ -2149,16 +2192,10 @@
 
 
 def get_nodeid_from_name(name):
-    if xmlutil.CrmMonXmlParser().is_node_remote(name):
+    crm_mon_xml_parser = xmlutil.CrmMonXmlParser()
+    if crm_mon_xml_parser.is_node_remote(name):
         return name
-    rc, out = ShellUtils().get_stdout('crm_node -l')
-    if rc != 0:
-        return None
-    res = re.search(r'^([0-9]+) {} '.format(name), out, re.M)
-    if res:
-        return res.group(1)
-    else:
-        return None
+    return crm_mon_xml_parser.get_node_id_from_name(name)
 
 
 def check_empty_option_value(options):
@@ -3091,26 +3128,6 @@
     return asyncio.run(asyncio.wait_for(wrapper(), timeout_sec))
 
 
-def fetch_cluster_node_list_from_node(init_node):
-    """
-    Fetch cluster member list from one known cluster node
-    """
-    cluster_nodes_list = []
-    out = sh.cluster_shell().get_stdout_or_raise_error("crm_node -l", 
init_node)
-    for line in out.splitlines():
-        # Parse line in format: <id> <nodename> <state>, and collect the 
nodename.
-        tokens = line.split()
-        if len(tokens) == 0:
-            pass  # Skip any spurious empty line.
-        elif len(tokens) < 3:
-            logger.warning("The node '%s' has no known name and/or state 
information", tokens[0])
-        elif tokens[2] != "member":
-            logger.warning("The node '%s'(state '%s') is not a current 
member", tokens[1], tokens[2])
-        else:
-            cluster_nodes_list.append(tokens[1])
-    return cluster_nodes_list
-
-
 def has_sudo_access():
     """
     Check if current user has sudo access
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/watchdog.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/watchdog.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/watchdog.py 2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/watchdog.py 2026-04-20 
08:35:08.000000000 +0200
@@ -1,7 +1,7 @@
 import re
 from . import utils
-from .constants import SSH_OPTION
 from .sh import ShellUtils
+from . import sh
 from . import sbd
 
 
@@ -90,9 +90,7 @@
         """
         Given watchdog device name, get driver name on remote node
         """
-        # FIXME
-        cmd = "ssh {} {}@{} {}".format(SSH_OPTION, self._remote_user, 
self._peer_host, self.QUERY_CMD)
-        rc, out, err = ShellUtils().get_stdout_stderr(cmd)
+        rc, out, err = 
sh.cluster_shell().get_rc_stdout_stderr_without_input(self._peer_host, 
self.QUERY_CMD)
         if rc == 0 and out:
             # output format might like:
             #   [1] /dev/watchdog\nIdentity: Software Watchdog\nDriver: 
softdog\n
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.0.0+20260412.db68d024/crmsh/xmlutil.py 
new/crmsh-5.0.0+20260420.d13e03ac/crmsh/xmlutil.py
--- old/crmsh-5.0.0+20260412.db68d024/crmsh/xmlutil.py  2026-04-12 
04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/crmsh/xmlutil.py  2026-04-20 
08:35:08.000000000 +0200
@@ -1681,4 +1681,9 @@
             'completed': last_event.get('completed', '')
         }
         return fence_event_info
+
+    def get_node_id_from_name(self, name) -> str|None:
+        xpath = f'//node[@name="{name}"]'
+        res = self.xml_elem.xpath(xpath)
+        return res[0].get('id') if res else None
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/features/qdevice_setup_remove.feature 
new/crmsh-5.0.0+20260420.d13e03ac/test/features/qdevice_setup_remove.feature
--- 
old/crmsh-5.0.0+20260412.db68d024/test/features/qdevice_setup_remove.feature    
    2026-04-12 04:54:18.000000000 +0200
+++ 
new/crmsh-5.0.0+20260420.d13e03ac/test/features/qdevice_setup_remove.feature    
    2026-04-20 08:35:08.000000000 +0200
@@ -93,6 +93,7 @@
     When    Run "crm cluster restart" on "hanode1"
     Then    Cluster service is "started" on "hanode1"
     And     Service "corosync-qdevice" is "started" on "hanode1"
+    When    Wait "10" seconds
     When    Run "crm cluster join -c hanode1 -y" on "hanode2"
     Then    Cluster service is "started" on "hanode2"
     And     Service "corosync-qdevice" is "started" on "hanode2"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_bootstrap.py 
new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_bootstrap.py
--- old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_bootstrap.py  
2026-04-12 04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_bootstrap.py  
2026-04-20 08:35:08.000000000 +0200
@@ -1550,11 +1550,14 @@
         mock_adj_fence.assert_called_once_with(True)
 
     @mock.patch('crmsh.utils.cluster_copy_path')
-    @mock.patch('crmsh.utils.fetch_cluster_node_list_from_node')
-    def test_sync_path_peer(self, mock_fetch_nodes, mock_cluster_copy):
-        mock_fetch_nodes.return_value = ["node1", "node2"]
+    @mock.patch('crmsh.xmlutil.CrmMonXmlParser')
+    def test_sync_path_peer(self, mock_parser, mock_cluster_copy):
+        mock_parser_inst = mock.Mock()
+        mock_parser.return_value = mock_parser_inst
+        mock_parser_inst.get_node_list.return_value = ["node1", "node2"]
         bootstrap.sync_path("/file1", peer_node="node3")
-        mock_fetch_nodes.assert_called_once_with("node3")
+        mock_parser.assert_called_once_with("node3")
+        mock_parser_inst.get_node_list.assert_called_once_with(online=True, 
node_type="member")
         mock_cluster_copy.assert_called_once_with("/file1", nodes=["node1", 
"node2"])
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_crashtest_task.py 
new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_crashtest_task.py
--- old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_crashtest_task.py     
2026-04-12 04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_crashtest_task.py     
2026-04-20 08:35:08.000000000 +0200
@@ -490,21 +490,20 @@
         mock_run.assert_called_once_with("which crm_node")
         mock_pre_check.assert_called_once_with()
 
-    @mock.patch('crmsh.crash_test.utils.check_node_status')
+    @mock.patch('crmsh.crash_test.utils.online_nodes')
     @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr')
     @mock.patch('crmsh.crash_test.task.Task.task_pre_check')
-    def test_pre_check_error(self, mock_pre_check, mock_run, mock_node_status):
+    def test_pre_check_error(self, mock_pre_check, mock_run, 
mock_online_nodes):
         mock_run.side_effect = [(0, None, None), (0, None, None), (0, None, 
None)]
-        mock_node_status.return_value = False
+        mock_online_nodes.return_value = []
         with self.assertRaises(task.TaskError) as err:
             self.task_fence_inst.pre_check()
-        self.assertEqual("Node \"node1\" not in cluster!", str(err.exception))
+        self.assertEqual("Node \"node1\" is not in cluster!", 
str(err.exception))
         mock_run.assert_has_calls([
             mock.call("which crm_node"),
             mock.call("which stonith_admin"),
             mock.call("which crm_attribute")
             ])
-        mock_node_status.assert_called_once_with("node1", "member")
 
     @mock.patch('crmsh.crash_test.task.Task.fence_action_monitor')
     @mock.patch('threading.Thread')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_crashtest_utils.py 
new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_crashtest_utils.py
--- old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_crashtest_utils.py    
2026-04-12 04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_crashtest_utils.py    
2026-04-20 08:35:08.000000000 +0200
@@ -322,35 +322,6 @@
             mock.call(b'/usr/sbin/sbd\x00')
             ])
 
-    @mock.patch('crmsh.crash_test.utils.msg_error')
-    @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr')
-    def test_check_node_status_error_cmd(self, mock_run, mock_error):
-        mock_run.return_value = (1, None, "error")
-        res = utils.check_node_status("node1", "member")
-        self.assertEqual(res, False)
-        mock_run.assert_called_once_with("crm_node -l")
-        mock_error.assert_called_once_with("error")
-
-    @mock.patch('crmsh.crash_test.utils.msg_error')
-    @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr')
-    def test_check_node_status(self, mock_run, mock_error):
-        output = """
-1084783297 15sp2-1 member
-1084783193 15sp2-2 lost
-        """
-        mock_run.return_value = (0, output, None)
-
-        res = utils.check_node_status("15sp2-2", "member")
-        self.assertEqual(res, False)
-        res = utils.check_node_status("15sp2-1", "member")
-        self.assertEqual(res, True)
-
-        mock_run.assert_has_calls([
-            mock.call("crm_node -l"),
-            mock.call("crm_node -l")
-            ])
-        mock_error.assert_not_called()
-
     @mock.patch('crmsh.xmlutil.CrmMonXmlParser')
     def test_online_nodes(self, mock_crmmon_parser):
         mock_inst = mock.Mock()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_lock.py 
new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_lock.py
--- old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_lock.py       
2026-04-12 04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_lock.py       
2026-04-20 08:35:08.000000000 +0200
@@ -163,26 +163,6 @@
         config.core.lock_timeout = "130"
         self.assertEqual(self.lock_inst.lock_timeout, 130)
 
-    @mock.patch('crmsh.lock.RemoteLock._run')
-    def test_get_online_nodelist_error(self, mock_run):
-        mock_run.return_value = (1, None, "error data")
-        with self.assertRaises(ValueError) as err:
-            self.lock_inst._get_online_nodelist()
-        self.assertEqual("error data", str(err.exception))
-        mock_run.assert_called_once_with("crm_node -l")
-
-    @mock.patch('crmsh.lock.RemoteLock._run')
-    def test_get_online_nodelist(self, mock_run):
-        output = """
-        1084783297 15sp2-1 member
-        1084783193 15sp2-2 lost
-        1084783331 15sp2-3 member
-        """
-        mock_run.return_value = (0, output, None)
-        res = self.lock_inst._get_online_nodelist()
-        self.assertEqual(res, ["15sp2-1", "15sp2-3"])
-        mock_run.assert_called_once_with("crm_node -l")
-
     @mock.patch('crmsh.lock.Lock._create_lock_dir')
     @mock.patch('crmsh.lock.RemoteLock.lock_timeout', 
new_callable=mock.PropertyMock)
     @mock.patch('time.time')
@@ -198,16 +178,18 @@
 
     @mock.patch('time.sleep')
     @mock.patch('logging.Logger.warning')
-    @mock.patch('crmsh.lock.RemoteLock._get_online_nodelist')
+    @mock.patch('crmsh.xmlutil.CrmMonXmlParser')
     @mock.patch('crmsh.lock.Lock._create_lock_dir')
     @mock.patch('crmsh.lock.RemoteLock.lock_timeout', 
new_callable=mock.PropertyMock)
     @mock.patch('time.time')
     def test_lock_or_wait_timed_out(self, mock_time, mock_time_out, 
mock_create,
-            mock_get_nodelist, mock_warn, mock_sleep):
+            mock_parser, mock_warn, mock_sleep):
         mock_time.side_effect = [10000, 10121]
         mock_time_out.return_value = 120
         mock_create.return_value = False
-        mock_get_nodelist.return_value = ["node2"]
+        mock_parser_inst = mock.Mock()
+        mock_parser.return_value = mock_parser_inst
+        mock_parser_inst.get_nodelist.return_value = ["node2"]
 
         with self.assertRaises(lock.ClaimLockError) as err:
             self.lock_inst._lock_or_wait()
@@ -216,29 +198,31 @@
         mock_time.assert_has_calls([ mock.call(), mock.call()])
         mock_time_out.assert_has_calls([mock.call(), mock.call(), mock.call()])
         mock_create.assert_called_once_with()
-        mock_get_nodelist.assert_called_once_with()
         mock_warn.assert_called_once_with('Might have unfinished process on 
other nodes, wait %ss...', 120)
         mock_sleep.assert_called_once_with(10)
 
     @mock.patch('time.sleep')
     @mock.patch('logging.Logger.warning')
-    @mock.patch('crmsh.lock.RemoteLock._get_online_nodelist')
+    @mock.patch('crmsh.xmlutil.CrmMonXmlParser')
     @mock.patch('crmsh.lock.Lock._create_lock_dir')
     @mock.patch('crmsh.lock.RemoteLock.lock_timeout', 
new_callable=mock.PropertyMock)
     @mock.patch('time.time')
     def test_lock_or_wait_again(self, mock_time, mock_time_out, mock_create,
-            mock_get_nodelist, mock_warn, mock_sleep):
+            mock_parser, mock_warn, mock_sleep):
         mock_time.side_effect = [10000, 10010, 10020]
         mock_time_out.side_effect = [120, 120, 120]
         mock_create.side_effect = [False, False, True]
-        mock_get_nodelist.side_effect = [["node1"], ["node1", "node2"]]
+        mock_parser_inst1 = mock.Mock()
+        mock_parser_inst2 = mock.Mock()
+        mock_parser.side_effect = [mock_parser_inst1, mock_parser_inst2]
+        mock_parser_inst1.get_nodelist.return_value = ["node1"]
+        mock_parser_inst2.get_nodelist.return_value = ["node1", "node2"]
 
         self.lock_inst._lock_or_wait()
 
         mock_time.assert_has_calls([mock.call(), mock.call(), mock.call()])
         mock_time_out.assert_has_calls([mock.call(), mock.call(), mock.call()])
         mock_create.assert_has_calls([mock.call(), mock.call(), mock.call()])
-        mock_get_nodelist.assert_has_calls([mock.call(), mock.call()])
         mock_warn.assert_called_once_with('Might have unfinished process on 
other nodes, wait %ss...', 120)
         mock_sleep.assert_has_calls([mock.call(10), mock.call(10)])
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_qdevice.py 
new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_qdevice.py
--- old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_qdevice.py    
2026-04-12 04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_qdevice.py    
2026-04-20 08:35:08.000000000 +0200
@@ -295,7 +295,7 @@
         mock_cluster_shell.return_value = mock_cluster_inst
         mock_cluster_inst.get_stdout_or_raise_error.return_value = "data"
         mock_installed.return_value = True
-        excepted_err_string = "This cluster's name \"cluster1\" already exists 
on qnetd server!\nPlease consider to use the different cluster-name property"
+        excepted_err_string = "This cluster's name \"cluster1\" already exists 
on qnetd server!\nPlease consider to use `crm cluster rename` to change a 
different cluster name.\nOr, run `crm cluster remove --qdevice` on the existing 
cluster beforehand."
 
         with self.assertRaises(ValueError) as err:
             self.qdevice_with_stage_cluster_name.validate_and_start_qnetd()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_utils.py 
new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_utils.py
--- old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_utils.py      
2026-04-12 04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_utils.py      
2026-04-20 08:35:08.000000000 +0200
@@ -26,53 +26,26 @@
     mock_run.assert_called_once_with("rpm -q --quiet crmsh")
 
 
[email protected]('re.search')
[email protected]('crmsh.sh.ShellUtils.get_stdout')
 @mock.patch('crmsh.xmlutil.CrmMonXmlParser')
-def test_get_nodeid_from_name_run_None1(mock_parser, mock_get_stdout, 
mock_re_search):
+def test_get_nodeid_from_name_remote(mock_parser):
     mock_parser_inst = mock.Mock()
     mock_parser.return_value = mock_parser_inst
-    mock_parser_inst.is_node_remote.return_value = False
-    mock_get_stdout.return_value = (1, None)
-    mock_re_search_inst = mock.Mock()
-    mock_re_search.return_value = mock_re_search_inst
-    res = utils.get_nodeid_from_name("node1")
-    assert res is None
-    mock_get_stdout.assert_called_once_with('crm_node -l')
-    mock_re_search.assert_not_called()
-
-
[email protected]('re.search')
[email protected]('crmsh.sh.ShellUtils.get_stdout')
[email protected]('crmsh.xmlutil.CrmMonXmlParser')
-def test_get_nodeid_from_name_run_None2(mock_parser, mock_get_stdout, 
mock_re_search):
-    mock_parser_inst = mock.Mock()
-    mock_parser.return_value = mock_parser_inst
-    mock_parser_inst.is_node_remote.return_value = False
-    mock_get_stdout.return_value = (0, "172167901 node1 member\n172168231 
node2 member")
-    mock_re_search.return_value = None
-    res = utils.get_nodeid_from_name("node111")
-    assert res is None
-    mock_get_stdout.assert_called_once_with('crm_node -l')
-    mock_re_search.assert_called_once_with(r'^([0-9]+) node111 ', 
mock_get_stdout.return_value[1], re.M)
+    mock_parser_inst.is_node_remote.return_value = True
+    assert utils.get_nodeid_from_name("node1") == "node1"
+    mock_parser.assert_called_once_with()
+    mock_parser_inst.is_node_remote.assert_called_once_with("node1")
 
 
[email protected]('re.search')
[email protected]('crmsh.sh.ShellUtils.get_stdout')
 @mock.patch('crmsh.xmlutil.CrmMonXmlParser')
-def test_get_nodeid_from_name(mock_parser, mock_get_stdout, mock_re_search):
+def test_get_nodeid_from_name(mock_parser):
     mock_parser_inst = mock.Mock()
     mock_parser.return_value = mock_parser_inst
     mock_parser_inst.is_node_remote.return_value = False
-    mock_get_stdout.return_value = (0, "172167901 node1 member\n172168231 
node2 member")
-    mock_re_search_inst = mock.Mock()
-    mock_re_search.return_value = mock_re_search_inst
-    mock_re_search_inst.group.return_value = '172168231'
-    res = utils.get_nodeid_from_name("node2")
-    assert res == '172168231'
-    mock_get_stdout.assert_called_once_with('crm_node -l')
-    mock_re_search.assert_called_once_with(r'^([0-9]+) node2 ', 
mock_get_stdout.return_value[1], re.M)
-    mock_re_search_inst.group.assert_called_once_with(1)
+    mock_parser_inst.get_node_id_from_name.return_value = "1"
+    assert utils.get_nodeid_from_name("node1") == "1"
+    mock_parser.assert_called_once_with()
+    mock_parser_inst.is_node_remote.assert_called_once_with("node1")
+    mock_parser_inst.get_node_id_from_name.assert_called_once_with("node1")
 
 
 @mock.patch('crmsh.sh.LocalShell.get_rc_and_error')
@@ -277,35 +250,43 @@
 
 @mock.patch("socket.getaddrinfo")
 @mock.patch("socket.socket")
[email protected]("crmsh.utils.closing")
-def test_check_port_open_false(mock_closing, mock_socket, mock_getaddrinfo):
[email protected]("selectors.DefaultSelector")
[email protected]("time.sleep")
+def test_check_port_open_false(mock_sleep, mock_selector_cls, mock_socket, 
mock_getaddrinfo):
     sock_inst = mock.Mock()
     mock_socket.return_value = sock_inst
-    mock_closing.return_value.__enter__.return_value = sock_inst
     sock_inst.connect_ex.return_value = 1
     mock_getaddrinfo.return_value = [(socket.AF_INET, socket.SOCK_STREAM, 6, 
"", ("127.0.0.1", 22))]
 
+    mock_selector = mock.Mock()
+    mock_selector_cls.return_value = mock_selector
+    mock_selector.select.return_value = []
+
     assert utils.check_port_open("localhost", 22) is False
 
-    mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM, 6)
-    mock_closing.assert_called_once_with(sock_inst)
-    sock_inst.connect_ex.assert_called_once_with(("127.0.0.1", 22))
+    assert mock_socket.call_count == 3
+    assert sock_inst.connect_ex.call_count == 3
+    assert mock_selector_cls.call_count == 3
+    assert mock_selector.select.call_count == 3
+    assert mock_sleep.call_count == 2
 
 @mock.patch("socket.getaddrinfo")
 @mock.patch("socket.socket")
[email protected]("crmsh.utils.closing")
-def test_check_port_open_true(mock_closing, mock_socket, mock_getaddrinfo):
[email protected]("selectors.DefaultSelector")
+def test_check_port_open_true(mock_selector_cls, mock_socket, 
mock_getaddrinfo):
     sock_inst = mock.Mock()
     mock_socket.return_value = sock_inst
-    mock_closing.return_value.__enter__.return_value = sock_inst
     sock_inst.connect_ex.return_value = 0
     mock_getaddrinfo.return_value = [(socket.AF_INET, socket.SOCK_STREAM, 6, 
"", ("127.0.0.1", 22))]
 
+    mock_selector = mock.Mock()
+    mock_selector_cls.return_value = mock_selector
+
     assert utils.check_port_open("localhost", 22) is True
 
-    mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM, 6)
-    mock_closing.assert_called_once_with(sock_inst)
-    sock_inst.connect_ex.assert_called_once_with(("127.0.0.1", 22))
+    assert mock_socket.call_count == 1
+    assert sock_inst.connect_ex.call_count == 1
+    assert mock_selector_cls.call_count == 1
 
 def test_valid_port():
     assert utils.valid_port(1) is False
@@ -1272,23 +1253,6 @@
     assert utils.compatible_role("Slave", "Unpromoted") is True
 
 
[email protected]('logging.Logger.warning')
[email protected]('crmsh.sh.ClusterShell.get_stdout_or_raise_error')
-def test_fetch_cluster_node_list_from_node(mock_run, mock_warn):
-    mock_run.return_value = """
-
-    1 node1
-    2 node2 lost
-    3 node3 member
-    """
-    assert utils.fetch_cluster_node_list_from_node("node1") == ["node3"]
-    mock_run.assert_called_once_with("crm_node -l", "node1")
-    mock_warn.assert_has_calls([
-        mock.call("The node '%s' has no known name and/or state information", 
"1"),
-        mock.call("The node '%s'(state '%s') is not a current member", 
"node2", "lost")
-        ])
-
-
 @mock.patch('crmsh.utils.list_cluster_nodes_except_me')
 def test_cluster_copy_path_return(mock_list_nodes):
     mock_list_nodes.return_value = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_watchdog.py 
new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_watchdog.py
--- old/crmsh-5.0.0+20260412.db68d024/test/unittests/test_watchdog.py   
2026-04-12 04:54:18.000000000 +0200
+++ new/crmsh-5.0.0+20260420.d13e03ac/test/unittests/test_watchdog.py   
2026-04-20 08:35:08.000000000 +0200
@@ -146,22 +146,22 @@
         mock_verify.assert_called_once_with("/dev/watchdog1")
 
     @mock.patch('crmsh.utils.fatal')
-    @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr')
-    def test_get_driver_through_device_remotely_error(self, mock_run, 
mock_error):
-        mock_run.return_value = (1, None, "error")
+    @mock.patch('crmsh.sh.cluster_shell')
+    def test_get_driver_through_device_remotely_error(self, 
mock_cluster_shell, mock_error):
+        mock_cluster_shell().get_rc_stdout_stderr_without_input.return_value = 
(1, None, "error")
         self.watchdog_join_inst._get_driver_through_device_remotely("test")
-        mock_run.assert_called_once_with("ssh {} alice@node1 sudo sbd 
query-watchdog".format(constants.SSH_OPTION))
+        
mock_cluster_shell().get_rc_stdout_stderr_without_input.assert_called_once_with("node1",
 watchdog.Watchdog.QUERY_CMD)
         mock_error.assert_called_once_with("Failed to run sudo sbd 
query-watchdog remotely: error")
 
-    @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr')
-    def test_get_driver_through_device_remotely_none(self, mock_run):
-        mock_run.return_value = (0, "data", None)
+    @mock.patch('crmsh.sh.cluster_shell')
+    def test_get_driver_through_device_remotely_none(self, mock_cluster_shell):
+        mock_cluster_shell().get_rc_stdout_stderr_without_input.return_value = 
(0, "data", None)
         res = 
self.watchdog_join_inst._get_driver_through_device_remotely("/dev/watchdog")
         self.assertEqual(res, None)
-        mock_run.assert_called_once_with("ssh {} alice@node1 sudo sbd 
query-watchdog".format(constants.SSH_OPTION))
+        
mock_cluster_shell().get_rc_stdout_stderr_without_input.assert_called_once_with("node1",
 watchdog.Watchdog.QUERY_CMD)
 
-    @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr')
-    def test_get_driver_through_device_remotely(self, mock_run):
+    @mock.patch('crmsh.sh.cluster_shell')
+    def test_get_driver_through_device_remotely(self, mock_cluster_shell):
         output = """
 Discovered 3 watchdog devices:
 
@@ -179,10 +179,10 @@
 Identity: iTCO_wdt
 Driver: iTCO_wdt
         """
-        mock_run.return_value = (0, output, None)
+        mock_cluster_shell().get_rc_stdout_stderr_without_input.return_value = 
(0, output, None)
         res = 
self.watchdog_join_inst._get_driver_through_device_remotely("/dev/watchdog")
         self.assertEqual(res, "softdog")
-        mock_run.assert_called_once_with("ssh {} alice@node1 sudo sbd 
query-watchdog".format(constants.SSH_OPTION))
+        
mock_cluster_shell().get_rc_stdout_stderr_without_input.assert_called_once_with("node1",
 watchdog.Watchdog.QUERY_CMD)
 
     def test_get_first_unused_device_none(self):
         res = self.watchdog_inst._get_first_unused_device()

Reply via email to