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-01 18:04:27
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.1937 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Mon Jun  1 18:04:27 2026 rev:409 rq:1356243 version:5.1.0+20260528.b0973362

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2026-05-08 
16:47:14.913858069 +0200
+++ /work/SRC/openSUSE:Factory/.crmsh.new.1937/crmsh.changes    2026-06-01 
18:06:18.872049963 +0200
@@ -1,0 +2,20 @@
+Thu May 28 08:17:49 UTC 2026 - [email protected]
+
+- Update to version 5.1.0+20260528.b0973362:
+  * Chore: github-actions: update to node.js 24 actions
+  * Dev: behave: Add new test case for port conflict
+  * Dev: unittests: Adjust unit test for previous commit
+  * Dev: ui_cluster: Refactor qdevice options validation
+  * Dev: ui_cluster: Add --qnetd-port option and deprecate --qdevice-port 
option
+  * Dev: qdevice: Configure "-p <port>" for /etc/sysconfig/corosync-qnetd on 
qnetd server
+
+-------------------------------------------------------------------
+Mon May 11 03:42:49 UTC 2026 - [email protected]
+
+- Update to version 5.1.0+20260511.e4dd3500:
+  * Dev: unittests: Adjust unit test for previous commit
+  * 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
+  * Dev: bootstrap: Find and show failed services
+
+-------------------------------------------------------------------

Old:
----
  crmsh-5.1.0+20260508.3e8e1915.tar.bz2

New:
----
  crmsh-5.1.0+20260528.b0973362.tar.bz2

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

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.JtYj7w/_old  2026-06-01 18:06:20.156103215 +0200
+++ /var/tmp/diff_new_pack.JtYj7w/_new  2026-06-01 18:06:20.156103215 +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+20260508.3e8e1915
+Version:        5.1.0+20260528.b0973362
 Release:        0
 URL:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.JtYj7w/_old  2026-06-01 18:06:20.216105703 +0200
+++ /var/tmp/diff_new_pack.JtYj7w/_new  2026-06-01 18:06:20.220105869 +0200
@@ -9,7 +9,7 @@
 </service>
 <service name="tar_scm">
   <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
-  <param 
name="changesrevision">3e8e191562b01d457fd4b3a0656f6a1827172f7f</param>
+  <param 
name="changesrevision">b097336298cdaa1df6f28dd5b1b20ef75b5b54b9</param>
 </service>
 </servicedata>
 (No newline at EOF)

++++++ crmsh-5.1.0+20260508.3e8e1915.tar.bz2 -> 
crmsh-5.1.0+20260528.b0973362.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/.github/workflows/crmsh-cd.yml 
new/crmsh-5.1.0+20260528.b0973362/.github/workflows/crmsh-cd.yml
--- old/crmsh-5.1.0+20260508.3e8e1915/.github/workflows/crmsh-cd.yml    
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/.github/workflows/crmsh-cd.yml    
2026-05-28 09:45:27.000000000 +0200
@@ -25,7 +25,7 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 10
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: delivery process
       run: |
         docker pull "${CONTAINER_IMAGE}"
@@ -42,7 +42,7 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 10
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: submit process
       run: |
         docker pull "${CONTAINER_IMAGE}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/.github/workflows/crmsh-ci.yml 
new/crmsh-5.1.0+20260528.b0973362/.github/workflows/crmsh-ci.yml
--- old/crmsh-5.1.0+20260508.3e8e1915/.github/workflows/crmsh-ci.yml    
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/.github/workflows/crmsh-ci.yml    
2026-05-28 09:45:27.000000000 +0200
@@ -17,7 +17,7 @@
   general_check:
     runs-on: ubuntu-24.04
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: check data-manifest
       run: |
         ./update-data-manifest.sh
@@ -39,9 +39,9 @@
       fail-fast: false
     timeout-minutes: 5
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: Set up Python
-      uses: actions/setup-python@v5
+      uses: actions/setup-python@v6
       with:
         python-version: ${{ matrix.python-version }}
     - name: Install dependencies
@@ -51,7 +51,7 @@
     - name: Test with pytest in tox
       run: |
         tox -v -e${{ matrix.python-version }}
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: unit
@@ -60,12 +60,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for crm_report bugs
       run:  |
         index=`$GET_INDEX_OF crm_report_bugs`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -74,12 +74,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for crm_report normal
       run:  |
         index=`$GET_INDEX_OF crm_report_normal`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -88,12 +88,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap bugs
       run:  |
         index=`$GET_INDEX_OF bootstrap_bugs`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -102,12 +102,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap bugs, under non root user
       run:  |
         index=`$GET_INDEX_OF bootstrap_bugs`
         $CONTAINER_SCRIPT $index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -116,12 +116,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap common
       run:  |
         index=`$GET_INDEX_OF bootstrap_init_join_remove`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -130,12 +130,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap common, under non root user
       run:  |
         index=`$GET_INDEX_OF bootstrap_init_join_remove`
         $CONTAINER_SCRIPT $index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -144,12 +144,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap options
       run:  |
         index=`$GET_INDEX_OF bootstrap_options`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -158,12 +158,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap firewalld
       run:  |
         index=`$GET_INDEX_OF bootstrap_firewalld`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -172,12 +172,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap using password
       run:  |
         index=`$GET_INDEX_OF bootstrap_password`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -187,12 +187,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for crm corosync subcommand
       run:  |
         index=`$GET_INDEX_OF corosync_ui`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -201,12 +201,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for bootstrap options, under non root user
       run:  |
         index=`$GET_INDEX_OF bootstrap_options`
         $CONTAINER_SCRIPT $index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -215,12 +215,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for qdevice setup and remove
       run:  |
         index=`$GET_INDEX_OF qdevice_setup_remove`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -229,12 +229,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for qdevice setup and remove, under non root user
       run:  |
         index=`$GET_INDEX_OF qdevice_setup_remove`
         $CONTAINER_SCRIPT $index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -243,12 +243,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for qdevice options
       run:  |
         index=`$GET_INDEX_OF qdevice_options`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -257,12 +257,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for qdevice validate
       run:  |
         index=`$GET_INDEX_OF qdevice_validate`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -271,12 +271,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for qdevice validate, under non root user
       run:  |
         index=`$GET_INDEX_OF qdevice_validate`
         $CONTAINER_SCRIPT $index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -285,12 +285,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for qdevice user case
       run:  |
         index=`$GET_INDEX_OF qdevice_usercase`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -299,12 +299,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for resource failcount
       run:  |
         index=`$GET_INDEX_OF resource_failcount`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -313,12 +313,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for resource set
       run:  |
         index=`$GET_INDEX_OF resource_set`
         $CONTAINER_SCRIPT $index
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -327,12 +327,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for resource set, under non root user
       run:  |
         index=`$GET_INDEX_OF resource_set`
         $CONTAINER_SCRIPT $index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -341,12 +341,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for configure sublevel bugs
       run:  |
         index=`$GET_INDEX_OF configure_bugs`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -355,12 +355,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for constraints bugs
       run:  |
         index=`$GET_INDEX_OF constraints_bugs`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -369,12 +369,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for geo cluster
       run:  |
         index=`$GET_INDEX_OF geo_setup`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -383,12 +383,12 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for healthcheck
       run:  |
         index=`$GET_INDEX_OF healthcheck`
         $CONTAINER_SCRIPT $index && $CONTAINER_SCRIPT -d && $CONTAINER_SCRIPT 
$index -u
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -397,11 +397,11 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for cluster api
       run:  |
         $CONTAINER_SCRIPT `$GET_INDEX_OF cluster_api`
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -410,11 +410,11 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for user access
       run:  |
         $CONTAINER_SCRIPT `$GET_INDEX_OF user_access`
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -423,11 +423,11 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for ssh agent
       run:  |
         $CONTAINER_SCRIPT `$GET_INDEX_OF ssh_agent` && $CONTAINER_SCRIPT -d && 
$CONTAINER_SCRIPT -u `$GET_INDEX_OF ssh_agent`
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -436,11 +436,11 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for blocking ssh
       run:  |
         $CONTAINER_SCRIPT `$GET_INDEX_OF cluster_blocking_ssh`
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -449,13 +449,13 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for migration
       run:  |
         echo '{ "exec-opts": ["native.cgroupdriver=systemd"] }' | sudo tee 
/etc/docker/daemon.json
         sudo systemctl restart docker.service
         $CONTAINER_SCRIPT `$GET_INDEX_OF migration` && $CONTAINER_SCRIPT -d && 
$CONTAINER_SCRIPT -u `$GET_INDEX_OF migration`
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -464,11 +464,11 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: functional test for pacemaker remote
       run:  |
         $CONTAINER_SCRIPT `$GET_INDEX_OF pacemaker_remote`
-    - uses: codecov/codecov-action@v4
+    - uses: codecov/codecov-action@v6
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
@@ -477,7 +477,7 @@
     runs-on: ubuntu-24.04
     timeout-minutes: 40
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v6
     - name: original regression test
       run:  |
         $CONTAINER_SCRIPT `$GET_INDEX_OF "regression test"`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/.github/workflows/test-container-image.yml 
new/crmsh-5.1.0+20260528.b0973362/.github/workflows/test-container-image.yml
--- 
old/crmsh-5.1.0+20260508.3e8e1915/.github/workflows/test-container-image.yml    
    2026-05-08 08:10:10.000000000 +0200
+++ 
new/crmsh-5.1.0+20260528.b0973362/.github/workflows/test-container-image.yml    
    2026-05-28 09:45:27.000000000 +0200
@@ -16,7 +16,7 @@
       run:
         working-directory: ./test_container
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v6
       - name: build container image
         run: podman image build -t haleap:ci .
       - name: push container image
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.1.0+20260508.3e8e1915/crmsh/bootstrap.py 
new/crmsh-5.1.0+20260528.b0973362/crmsh/bootstrap.py
--- old/crmsh-5.1.0+20260508.3e8e1915/crmsh/bootstrap.py        2026-05-08 
08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/crmsh/bootstrap.py        2026-05-28 
09:45:27.000000000 +0200
@@ -112,6 +112,7 @@
         self.qdevice_inst = None
         self.qnetd_addr_input = None
         self.qdevice_port = None
+        self.qnetd_port = None
         self.qdevice_algo = None
         self.qdevice_tie_breaker = None
         self.qdevice_tls = None
@@ -155,16 +156,38 @@
         ctx.initialize_user()
         return ctx
 
+    def _any_qdevice_options_set(self):
+        return any([
+            self.qdevice_port,
+            self.qnetd_port,
+            self.qdevice_algo,
+            self.qdevice_tie_breaker,
+            self.qdevice_tls,
+            self.qdevice_heuristics,
+            self.qdevice_heuristics_mode,
+        ])
+
     def _initialize_qdevice(self):
         """
         Initialize qdevice instance
         """
         if not self.qnetd_addr_input:
+            if self._any_qdevice_options_set() or self.stage == "qdevice":
+                utils.fatal("Option --qnetd-hostname is required if want to 
configure qdevice")
             return
+
+        if self.qdevice_port is not None:
+            logger.warning("Option --qdevice-port is deprecated and will be 
removed in future release, please use --qnetd-port instead")
+            if self.qnetd_port is not None:
+                utils.fatal("Options --qdevice-port and --qnetd-port can't be 
used together")
+        if self.qdevice_heuristics_mode and not self.qdevice_heuristics:
+            utils.fatal("Option --qdevice-heuristics is required if want to 
configure heuristics mode")
+
+        qnetd_port = self.qnetd_port or self.qdevice_port
         ssh_user, qnetd_host = utils.parse_user_at_host(self.qnetd_addr_input)
         self.qdevice_inst = qdevice.QDevice(
                 qnetd_addr=qnetd_host,
-                port=self.qdevice_port,
+                port=qnetd_port,
                 algo=self.qdevice_algo,
                 tie_breaker=self.qdevice_tie_breaker,
                 tls=self.qdevice_tls,
@@ -726,12 +749,15 @@
         logger.warning("You should change the hacluster password to something 
more secure!")
 
     if not start_pacemaker(enable_flag=True):
-        utils.fatal("Failed to start cluster services")
+        failed_services = get_failed_services()
+        failed_services_str = f" Please check failed services: {', 
'.join(failed_services)}" if failed_services else ""
+        utils.fatal(f"Failed to start cluster services.{failed_services_str}")
 
     if _context and _context.type == "init":
         if corosync.is_qdevice_configured():
-            logger.info("Starting and enable corosync-qdevice.service on %s", 
utils.this_node())
-            service_manager.start_service("corosync-qdevice.service", 
enable=True)
+            logger.info("Starting and enable %s on %s", 
constants.COROSYNC_QDEVICE_SERVICE, utils.this_node())
+            if not 
service_manager.start_service(constants.COROSYNC_QDEVICE_SERVICE, enable=True):
+                logger.error("Failed to start %s on %s", 
constants.COROSYNC_QDEVICE_SERVICE, utils.this_node())
         elif service_manager.service_is_enabled("corosync-qdevice.service"):
             service_manager.disable_service("corosync-qdevice.service")
 
@@ -1556,8 +1582,8 @@
     ssh_user, qnetd_host = utils.parse_user_at_host(qnetd_addr_input)
     qdevice.QDevice.check_qnetd_addr(qnetd_host)
     _context.qnetd_addr_input = qnetd_addr_input
-    qdevice_port = prompt_for_string("TCP PORT of QNetd server", default=5403,
-            valid_func=qdevice.QDevice.check_qdevice_port)
+    qnetd_port = prompt_for_string("TCP PORT of QNetd server",
+            valid_func=qdevice.QDevice.check_qnetd_port)
     qdevice_algo = prompt_for_string("QNetd decision ALGORITHM (ffsplit/lms)", 
default="ffsplit",
             valid_func=qdevice.QDevice.check_qdevice_algo)
     qdevice_tie_breaker = prompt_for_string("QNetd TIE_BREAKER 
(lowest/highest/valid node id)", default="lowest",
@@ -1572,7 +1598,7 @@
 
     _context.qdevice_inst = qdevice.QDevice(
             qnetd_host,
-            port=qdevice_port,
+            port=qnetd_port,
             algo=qdevice_algo,
             tie_breaker=qdevice_tie_breaker,
             tls=qdevice_tls,
@@ -2902,4 +2928,19 @@
             sbd.SBDManager.SBD_SYSTEMD_DELAY_START_DISABLE_DIR
         ) + STATIC_FILES_TO_SYNC
     )
+
+
+def get_failed_services(peer=None) -> list[str]:
+    failed_services = []
+    shell = sh.cluster_shell()
+    for service in (
+        constants.COROSYNC_SERVICE,
+        constants.SBD_SERVICE,
+        constants.PCMK_SERVICE
+    ):
+        cmd = f"systemctl is-failed {service}"
+        rc, _, _ = shell.get_rc_stdout_stderr_without_input(peer, cmd)
+        if rc == 0:
+            failed_services.append(service)
+    return failed_services
 # EOF
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.1.0+20260508.3e8e1915/crmsh/constants.py 
new/crmsh-5.1.0+20260528.b0973362/crmsh/constants.py
--- old/crmsh-5.1.0+20260508.3e8e1915/crmsh/constants.py        2026-05-08 
08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/crmsh/constants.py        2026-05-28 
09:45:27.000000000 +0200
@@ -440,6 +440,8 @@
 
 NO_SSH_ERROR_MSG = "ssh-related operations are disabled. crmsh works in local 
mode."
 
+COROSYNC_SERVICE = "corosync.service"
+COROSYNC_QDEVICE_SERVICE = "corosync-qdevice.service"
 PCMK_SERVICE = "pacemaker.service"
 SBD_SERVICE = "sbd.service"
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.1.0+20260508.3e8e1915/crmsh/qdevice.py 
new/crmsh-5.1.0+20260528.b0973362/crmsh/qdevice.py
--- old/crmsh-5.1.0+20260508.3e8e1915/crmsh/qdevice.py  2026-05-08 
08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/crmsh/qdevice.py  2026-05-28 
09:45:27.000000000 +0200
@@ -111,6 +111,8 @@
     Call `certificate_process_on_init` to generate all of CA, server, and 
client certs.
     """
 
+    SYSCONFIG_QNETD = "/etc/sysconfig/corosync-qnetd"
+    QNETD_DEFAULT_PORT = 5403
     qnetd_service = "corosync-qnetd.service"
     qnetd_cacert_filename = "qnetd-cacert.crt"
     qdevice_crq_filename = "qdevice-net-node.crq"
@@ -119,19 +121,19 @@
     qdevice_path = "/etc/corosync/qdevice/net"
     qdevice_db_path = "/etc/corosync/qdevice/net/nssdb"
 
-    def __init__(self, qnetd_addr, port=5403, algo="ffsplit", 
tie_breaker="lowest",
-            tls="on", ssh_user=None, cmds=None, mode=None, cluster_name=None, 
is_stage=False):
+    def __init__(self, qnetd_addr, port=None, algo=None, tie_breaker=None,
+            tls=None, ssh_user=None, cmds=None, mode=None, cluster_name=None, 
is_stage=False):
         """
         Init function
         """
         self.qnetd_addr = qnetd_addr
         self.port = port
-        self.algo = algo
-        self.tie_breaker = tie_breaker
-        self.tls = tls
+        self.algo = algo or "ffsplit"
+        self.tie_breaker = tie_breaker or "lowest"
+        self.tls = tls or "on"
         self.ssh_user = ssh_user
         self.cmds = cmds
-        self.mode = mode
+        self.mode = mode or "sync"
         self.cluster_name = cluster_name
         self.qdevice_reload_policy = QdevicePolicy.QDEVICE_RESTART
         self.is_stage = is_stage
@@ -208,9 +210,9 @@
 
 
     @staticmethod
-    def check_qdevice_port(qdevice_port):
-        if not utils.valid_port(qdevice_port):
-            raise ValueError("invalid qdevice port range(1024 - 65535)")
+    def check_qnetd_port(qnetd_port):
+        if qnetd_port and not utils.valid_port(qnetd_port):
+            raise ValueError("invalid qnetd port range(1024 - 65535)")
 
     @staticmethod
     def check_qdevice_algo(qdevice_algo):
@@ -258,7 +260,7 @@
             utils.check_all_nodes_reachable("setup Qdevice")
         self.check_corosync_qdevice_available()
         self.check_qnetd_addr(self.qnetd_addr)
-        self.check_qdevice_port(self.port)
+        self.check_qnetd_port(self.port)
         self.check_qdevice_algo(self.algo)
         self.check_qdevice_tie_breaker(self.tie_breaker)
         self.check_qdevice_tls(self.tls)
@@ -289,8 +291,11 @@
             raise ValueError(f"{exception_msg}\n{suggestion_msg}")
 
     def start_qnetd(self):
+        service_manager = ServiceManager()
+        if service_manager.service_is_active(self.qnetd_service, 
self.qnetd_addr):
+            return
         logger.info("Starting and enable corosync-qnetd.service on %s" % 
self.qnetd_addr)
-        ServiceManager().start_service(self.qnetd_service, enable=True, 
remote_addr=self.qnetd_addr)
+        service_manager.start_service(self.qnetd_service, enable=True, 
remote_addr=self.qnetd_addr)
 
     def set_cluster_name(self):
         if not self.cluster_name:
@@ -511,10 +516,80 @@
         cmd = "test -f {crq_file} && rm -f 
{crq_file}".format(crq_file=cls_inst.qdevice_crq_on_qnetd)
         shell.get_stdout_or_raise_error(cmd, qnetd_host)
 
+    def _handle_port_when_qnetd_active(self):
+        cmd = "corosync-qnetd-tool -s"
+        out = sh.cluster_shell().get_stdout_or_raise_error(cmd, 
self.qnetd_addr)
+        res = re.search(r'QNetd address:\s+\S+:(\d+)', out)
+        if res:
+            port_in_qnetd = int(res.group(1))
+            if self.port is not None and self.port != port_in_qnetd:
+                error_msg = f"The port {self.port} is different from the port 
{port_in_qnetd} that corosync-qnetd is using"
+                suggestion_msg = f"Please use '--qnetd-port {port_in_qnetd}' 
to keep consistent"
+                raise ValueError(f"{error_msg}\n{suggestion_msg}")
+            else:
+                self.port = port_in_qnetd
+        else:
+            # this should not happen, just in case
+            raise ValueError(f"Failed to get qnetd port from 
corosync-qnetd-tool output on {self.qnetd_addr}")
+
+    def _handle_port_when_qnetd_inactive(self):
+        shell = sh.cluster_shell()
+        action_cmd = ""
+
+        cmd = f"test -f {self.SYSCONFIG_QNETD}"
+        rc, _, _ = shell.get_rc_stdout_stderr_without_input(self.qnetd_addr, 
cmd)
+        if rc != 0:
+            port_option = "" if self.port is None else f"-p {self.port}"
+            options = 
f"COROSYNC_QNETD_OPTIONS=\"{port_option}\"\nCOROSYNC_QNETD_RUNAS=\"\""
+            logger.info(f"Write qnetd options to {self.SYSCONFIG_QNETD} on 
{self.qnetd_addr}: {options.strip()}")
+            action_cmd = f"echo -e '{options}' > {self.SYSCONFIG_QNETD}"
+
+        else:
+            cmd = f"grep '^[[:space:]]*COROSYNC_QNETD_OPTIONS=' 
{self.SYSCONFIG_QNETD}"
+            rc, out, _ = 
shell.get_rc_stdout_stderr_without_input(self.qnetd_addr, cmd)
+            if rc == 0 and out:
+                res = re.search(r'-p\s+(\d+)', out)
+                if res:
+                    port_in_sysconfig = int(res.group(1))
+                    if self.port is not None and self.port != 
port_in_sysconfig:
+                        error_msg = f"The port {self.port} is different from 
the port {port_in_sysconfig} in {self.SYSCONFIG_QNETD}"
+                        suggestion_msg = f"Please use '--qnetd-port 
{port_in_sysconfig}' to keep consistent"
+                        raise ValueError(f"{error_msg}\n{suggestion_msg}")
+                    else:
+                        self.port = port_in_sysconfig
+
+                elif self.port is not None:
+                    value_of_options = out.strip().split('=', 1)[1].strip('"')
+                    if value_of_options:
+                        options = f'COROSYNC_QNETD_OPTIONS="{value_of_options} 
-p {self.port}"'
+                    else:
+                        options = f'COROSYNC_QNETD_OPTIONS="-p {self.port}"'
+                    logger.info(f"Update qnetd options in 
{self.SYSCONFIG_QNETD} on {self.qnetd_addr} to: {options}")
+                    action_cmd = f"sed -i 
's|COROSYNC_QNETD_OPTIONS=.*|{options}|' {self.SYSCONFIG_QNETD}"
+            else:
+                options = "COROSYNC_QNETD_OPTIONS=\"\"" if self.port is None 
else f"COROSYNC_QNETD_OPTIONS=\"-p {self.port}\""
+                logger.info(f"Add qnetd options to {self.SYSCONFIG_QNETD} on 
{self.qnetd_addr}: {options}")
+                action_cmd = f"echo '{options}' >> {self.SYSCONFIG_QNETD}"
+
+        if action_cmd:
+            shell.get_stdout_or_raise_error(action_cmd, self.qnetd_addr)
+
+    def config_qnetd_port_in_sysconfig(self):
+        if ServiceManager().service_is_active("corosync-qnetd.service", 
self.qnetd_addr):
+            self._handle_port_when_qnetd_active()
+        else:
+            self._handle_port_when_qnetd_inactive()
+
+        if self.port is None:
+            self.port = self.QNETD_DEFAULT_PORT
+        logger.info(f"Use port {self.port} for corosync-qnetd on 
{self.qnetd_addr}")
+
     def config_qnetd_port(self):
         """
         Enable qnetd port in firewalld
         """
+        self.config_qnetd_port_in_sysconfig()
+
         if not ServiceManager().service_is_active("firewalld.service", 
self.qnetd_addr):
             return
         if utils.check_port_open(self.qnetd_addr, self.port):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/crmsh/service_manager.py 
new/crmsh-5.1.0+20260528.b0973362/crmsh/service_manager.py
--- old/crmsh-5.1.0+20260508.3e8e1915/crmsh/service_manager.py  2026-05-08 
08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/crmsh/service_manager.py  2026-05-28 
09:45:27.000000000 +0200
@@ -37,9 +37,13 @@
         Return success node list
         """
         if enable:
-            cmd = "systemctl enable --now '{}'".format(name)
+            cmd = (
+                f"systemctl enable --now '{name}' && "
+                f"systemctl is-enabled '{name}' && "
+                f"systemctl is-active '{name}'"
+            )
         else:
-            cmd = "systemctl start '{}'".format(name)
+            cmd = f"systemctl start '{name}' && systemctl is-active '{name}'"
         return self._call(remote_addr, node_list, cmd)
 
     def _call(self, remote_addr: str, node_list: typing.List[str], cmd: str) 
-> typing.List[str]:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-5.1.0+20260508.3e8e1915/crmsh/ui_cluster.py 
new/crmsh-5.1.0+20260528.b0973362/crmsh/ui_cluster.py
--- old/crmsh-5.1.0+20260508.3e8e1915/crmsh/ui_cluster.py       2026-05-08 
08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/crmsh/ui_cluster.py       2026-05-28 
09:45:27.000000000 +0200
@@ -190,14 +190,19 @@
             return
 
         if start_qdevice:
-            service_manager.start_service("corosync-qdevice", 
node_list=node_list)
+            success_list = service_manager.start_service("corosync-qdevice", 
node_list=node_list)
+            for node in node_list:
+                if node not in success_list:
+                    logger.error("Failed to start %s on %s", 
constants.COROSYNC_QDEVICE_SERVICE, node)
 
         success_flag = True
         success_list = bootstrap.start_pacemaker(node_list=node_list)
         for node in node_list:
             if node not in success_list:
+                failed_services = bootstrap.get_failed_services(node)
+                failed_services_str = f" Please check failed services: {', 
'.join(failed_services)}" if failed_services else ""
+                logger.error("The cluster stack failed to start on %s.%s", 
node, failed_services_str)
                 success_flag = False
-                logger.error("The cluster stack failed to start on %s", node)
                 continue
             logger.info("The cluster stack started on %s", node)
 
@@ -454,13 +459,15 @@
         qdevice_group = parser.add_argument_group("QDevice configuration", 
re.sub('  ', '', constants.QDEVICE_HELP_INFO) + "\n\nOptions for configuring 
QDevice and QNetd.")
         qdevice_group.add_argument("--qnetd-hostname", 
dest="qnetd_addr_input", metavar="[USER@]HOST",
                                    help="User and host of the QNetd server. 
The host can be specified in either hostname or IP address.")
-        qdevice_group.add_argument("--qdevice-port", dest="qdevice_port", 
metavar="PORT", type=int, default=5403,
-                                   help="TCP PORT of QNetd server 
(default:5403)")
-        qdevice_group.add_argument("--qdevice-algo", dest="qdevice_algo", 
metavar="ALGORITHM", default="ffsplit", choices=['ffsplit', 'lms'],
+        qdevice_group.add_argument("--qdevice-port", dest="qdevice_port", 
metavar="PORT", type=int,
+                                   help=f"TCP PORT of QNetd server 
(default:{qdevice.QDevice.QNETD_DEFAULT_PORT}, deprecated, use --qnetd-port 
instead)")
+        qdevice_group.add_argument("--qnetd-port", dest="qnetd_port", 
metavar="PORT", type=int,
+                                   help=f"TCP PORT of QNetd server 
(default:{qdevice.QDevice.QNETD_DEFAULT_PORT})")
+        qdevice_group.add_argument("--qdevice-algo", dest="qdevice_algo", 
metavar="ALGORITHM", choices=['ffsplit', 'lms'],
                                    help="QNetd decision ALGORITHM 
(ffsplit/lms, default:ffsplit)")
-        qdevice_group.add_argument("--qdevice-tie-breaker", 
dest="qdevice_tie_breaker", metavar="TIE_BREAKER", default="lowest",
+        qdevice_group.add_argument("--qdevice-tie-breaker", 
dest="qdevice_tie_breaker", metavar="TIE_BREAKER",
                                    help="QNetd TIE_BREAKER 
(lowest/highest/valid_node_id, default:lowest)")
-        qdevice_group.add_argument("--qdevice-tls", dest="qdevice_tls", 
metavar="TLS", default="on", choices=['on', 'off', 'required'],
+        qdevice_group.add_argument("--qdevice-tls", dest="qdevice_tls", 
metavar="TLS", choices=['on', 'off', 'required'],
                                    help="Whether using TLS on QDevice 
(on/off/required, default:on)")
         qdevice_group.add_argument("--qdevice-heuristics", 
dest="qdevice_heuristics", metavar="COMMAND",
                                    help="COMMAND to run with absolute path. 
For multiple commands, use \";\" to separate (details about heuristics can see 
man 8 corosync-qdevice)")
@@ -489,13 +496,6 @@
         if len(args):
             stage = args[0]
 
-        if options.qnetd_addr_input:
-            if options.qdevice_heuristics_mode and not 
options.qdevice_heuristics:
-                parser.error("Option --qdevice-heuristics is required if want 
to configure heuristics mode")
-            options.qdevice_heuristics_mode = options.qdevice_heuristics_mode 
or "sync"
-        elif re.search("--qdevice-.*", ' '.join(sys.argv)) or (stage == 
"qdevice" and options.yes_to_all):
-            parser.error("Option --qnetd-hostname is required if want to 
configure qdevice")
-
         # if options.geo and options.name == "hacluster":
         #    parser.error("For a geo cluster, each cluster must have a unique 
name (use --name to set)")
         boot_context = bootstrap.Context.set_context(options)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/test/features/qdevice_validate.feature 
new/crmsh-5.1.0+20260528.b0973362/test/features/qdevice_validate.feature
--- old/crmsh-5.1.0+20260508.3e8e1915/test/features/qdevice_validate.feature    
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/test/features/qdevice_validate.feature    
2026-05-28 09:45:27.000000000 +0200
@@ -26,9 +26,14 @@
     Then    Except "ERROR: cluster.init: host "node-without-ssh" is 
unreachable via SSH"
 
   @clean
-  Scenario: Option "--qdevice-port" set wrong port
-    When    Try "crm cluster init --qnetd-hostname=qnetd-node --qdevice-port=1"
-    Then    Except "ERROR: cluster.init: invalid qdevice port range(1024 - 
65535)"
+  Scenario: Option "--qnetd-port" set wrong port
+    When    Try "crm cluster init --qnetd-hostname=qnetd-node --qnetd-port=1"
+    Then    Except "ERROR: cluster.init: invalid qnetd port range(1024 - 
65535)"
+
+  @clean
+  Scenario: Option "--qnetd-port" and "--qdevice-port" can't be used together
+    When    Try "crm cluster init --qnetd-hostname=qnetd-node 
--qnetd-port=1234 --qdevice-port=1234"
+    Then    Expected "Options --qdevice-port and --qnetd-port can't be used 
together" in stderr
 
   @clean
   Scenario: Option "--qdevice-tie-breaker" set wrong value
@@ -44,21 +49,13 @@
 
   @clean
   Scenario: Option "--qnetd-hostname" is required by other qdevice options
-    When    Try "crm cluster init --qdevice-port=1234"
-    Then    Expected multiple lines in stderr
-      """
-      usage: init [options] [STAGE]
-      crm: error: Option --qnetd-hostname is required if want to configure 
qdevice
-      """
+    When    Try "crm cluster init --qnetd-port=1234"
+    Then    Except "ERROR: cluster.init: Option --qnetd-hostname is required 
if want to configure qdevice"
 
   @clean
   Scenario: Option --qdevice-heuristics is required if want to configure 
heuristics mode
     When    Try "crm cluster init --qnetd-hostname=qnetd-node 
--qdevice-heuristics-mode="on""
-    Then    Expected multiple lines in stderr
-      """
-      usage: init [options] [STAGE]
-      crm: error: Option --qdevice-heuristics is required if want to configure 
heuristics mode
-      """
+    Then    Except "ERROR: cluster.init: Option --qdevice-heuristics is 
required if want to configure heuristics mode"
 
   @clean
   Scenario: Node for qnetd not installed corosync-qnetd
@@ -101,11 +98,7 @@
     When    Run "crm cluster init -y" on "hanode1"
     Then    Cluster service is "started" on "hanode1"
     When    Try "crm cluster init qdevice -y"
-    Then    Expected multiple lines in stderr
-      """
-      usage: init [options] [STAGE]
-      crm: error: Option --qnetd-hostname is required if want to configure 
qdevice
-      """
+    Then    Except "ERROR: cluster.init: Option --qnetd-hostname is required 
if want to configure qdevice"
 
   @clean
   Scenario: Setup qdevice on a single node cluster with RA running(bsc#1181415)
@@ -142,3 +135,36 @@
     When    Run "crm -F cluster remove --qdevice -y" on "hanode1"
     Then    Cluster service is "started" on "hanode1"
     And     Service "corosync-qdevice" is "stopped" on "hanode1"
+
+  @clean
+  Scenario: Port conflict between qnetd and corosync
+    Given   Service "corosync-qnetd" is "started" on "qnetd-node"
+    When    Run "crm cluster init -y" on "hanode1"
+    Then    Cluster service is "started" on "hanode1"
+    When    Try "crm cluster init qdevice --qnetd-hostname=qnetd-node 
--qnetd-port 5402 -y" on "hanode1"
+    Then    Expected multiple lines in stderr
+      """
+      The port 5402 is different from the port 5403 that corosync-qnetd is 
using
+      Please use '--qnetd-port 5403' to keep consistent
+      """
+    Then    Service "corosync-qdevice" is "stopped" on "hanode1"
+    When    Run "crm cluster init qdevice --qnetd-hostname=qnetd-node 
--qnetd-port 5403 -y" on "hanode1"
+    Then    Service "corosync-qdevice" is "started" on "hanode1"
+
+    When    Run "crm cluster remove --qdevice -y" on "hanode1"
+    Then    Service "corosync-qdevice" is "stopped" on "hanode1"
+    When    Run "systemctl stop corosync-qnetd" on "qnetd-node"
+    Then    Service "corosync-qnetd" is "stopped" on "qnetd-node"
+    When    Run "crm cluster init qdevice --qnetd-hostname=qnetd-node 
--qnetd-port 5402 -y" on "hanode1"
+    Then    Service "corosync-qdevice" is "started" on "hanode1"
+    And     Service "corosync-qnetd" is "started" on "qnetd-node"
+    And     Run "cat /etc/sysconfig/corosync-qnetd|grep "\-p 5402"" OK on 
"qnetd-node"
+
+    When    Run "crm cluster remove --qdevice -y" on "hanode1"
+    Then    Service "corosync-qdevice" is "stopped" on "hanode1"
+    When    Run "systemctl stop corosync-qnetd" on "qnetd-node"
+    Then    Service "corosync-qnetd" is "stopped" on "qnetd-node"
+    When    Run "crm cluster init qdevice --qnetd-hostname=qnetd-node -y" on 
"hanode1"
+    Then    Service "corosync-qdevice" is "started" on "hanode1"
+    And     Service "corosync-qnetd" is "started" on "qnetd-node"
+    And     Run "cat /etc/corosync/corosync.conf|grep "port: 5402"" OK on 
"hanode1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/test/features/steps/const.py 
new/crmsh-5.1.0+20260528.b0973362/test/features/steps/const.py
--- old/crmsh-5.1.0+20260508.3e8e1915/test/features/steps/const.py      
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/test/features/steps/const.py      
2026-05-28 09:45:27.000000000 +0200
@@ -116,7 +116,9 @@
   --qnetd-hostname [USER@]HOST
                         User and host of the QNetd server. The host can be
                         specified in either hostname or IP address.
-  --qdevice-port PORT   TCP PORT of QNetd server (default:5403)
+  --qdevice-port PORT   TCP PORT of QNetd server (default:5403, deprecated,
+                        use --qnetd-port instead)
+  --qnetd-port PORT     TCP PORT of QNetd server (default:5403)
   --qdevice-algo ALGORITHM
                         QNetd decision ALGORITHM (ffsplit/lms,
                         default:ffsplit)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_bootstrap.py 
new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_bootstrap.py
--- old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_bootstrap.py  
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_bootstrap.py  
2026-05-28 09:45:27.000000000 +0200
@@ -113,7 +113,7 @@
     def test_initialize_qdevice(self, mock_qdevice):
         ctx = crmsh.bootstrap.Context()
         ctx.qnetd_addr_input = "node3"
-        ctx.qdevice_port = 123
+        ctx.qnetd_port = 123
         ctx.stage = ""
         ctx._initialize_qdevice()
         mock_qdevice.assert_called_once_with(qnetd_addr='node3', port=123, 
ssh_user=None, algo=None, tie_breaker=None, tls=None, cmds=None, mode=None, 
is_stage=False)
@@ -122,7 +122,7 @@
     def test_initialize_qdevice_with_user(self, mock_qdevice):
         ctx = crmsh.bootstrap.Context()
         ctx.qnetd_addr_input = "alice@node3"
-        ctx.qdevice_port = 123
+        ctx.qnetd_port = 123
         ctx.stage = ""
         ctx._initialize_qdevice()
         mock_qdevice.assert_called_once_with(qnetd_addr='node3', port=123, 
ssh_user='alice', algo=None, tie_breaker=None, tls=None, cmds=None, mode=None, 
is_stage=False)
@@ -1357,8 +1357,8 @@
         mock_confirm.assert_called_once_with("Do you want to configure 
QDevice?")
         mock_prompt.assert_has_calls([
             mock.call("HOST or IP of the QNetd server to be used"),
-            mock.call("TCP PORT of QNetd server", default=5403,
-                valid_func=qdevice.QDevice.check_qdevice_port),
+            mock.call("TCP PORT of QNetd server",
+                valid_func=qdevice.QDevice.check_qnetd_port),
             mock.call("QNetd decision ALGORITHM (ffsplit/lms)", 
default="ffsplit",
                 valid_func=qdevice.QDevice.check_qdevice_algo),
             mock.call("QNetd TIE_BREAKER (lowest/highest/valid node id)", 
default="lowest",
@@ -1560,6 +1560,25 @@
         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"])
 
+    @mock.patch('crmsh.sh.cluster_shell')
+    def test_get_failed_services(self, 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.side_effect 
= [
+            (1, None, None),
+            (0, None, None),
+            (1, None, None)
+        ]
+
+        res = bootstrap.get_failed_services()
+        self.assertEqual(res, [constants.SBD_SERVICE])
+        mock_cluster_shell.assert_called_once_with()
+        
mock_cluster_shell_inst.get_rc_stdout_stderr_without_input.assert_has_calls([
+            mock.call(None, f"systemctl is-failed 
{constants.COROSYNC_SERVICE}"),
+            mock.call(None, f"systemctl is-failed {constants.SBD_SERVICE}"),
+            mock.call(None, f"systemctl is-failed {constants.PCMK_SERVICE}")
+        ])
+
 
 class TestValidation(unittest.TestCase):
     """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_qdevice.py 
new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_qdevice.py
--- old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_qdevice.py    
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_qdevice.py    
2026-05-28 09:45:27.000000000 +0200
@@ -226,11 +226,11 @@
         qdevice.QDevice.check_qnetd_addr("qnetd-node")
 
     @mock.patch('crmsh.utils.valid_port')
-    def test_check_qdevice_port(self, mock_port):
+    def test_check_qnetd_port(self, mock_port):
         mock_port.return_value = False
         with self.assertRaises(ValueError) as err:
-            qdevice.QDevice.check_qdevice_port("1")
-        excepted_err_string = "invalid qdevice port range(1024 - 65535)"
+            qdevice.QDevice.check_qnetd_port("1")
+        excepted_err_string = "invalid qnetd port range(1024 - 65535)"
         self.assertEqual(excepted_err_string, str(err.exception))
 
     def test_check_qdevice_algo(self):
@@ -278,7 +278,7 @@
     @mock.patch('crmsh.qdevice.QDevice.check_qdevice_tls')
     @mock.patch('crmsh.qdevice.QDevice.check_qdevice_tie_breaker')
     @mock.patch('crmsh.qdevice.QDevice.check_qdevice_algo')
-    @mock.patch('crmsh.qdevice.QDevice.check_qdevice_port')
+    @mock.patch('crmsh.qdevice.QDevice.check_qnetd_port')
     @mock.patch('crmsh.qdevice.QDevice.check_qnetd_addr')
     @mock.patch('crmsh.qdevice.QDevice.check_corosync_qdevice_available')
     def test_valid_qdevice_options(self, mock_installed, mock_check_qnetd, 
mock_check_port,
@@ -326,8 +326,10 @@
         mock_installed.assert_called_once_with("corosync-qnetd", 
remote_addr="10.10.10.123")
         mock_init_tls_certs_on_qnetd.assert_called_once()
 
+    @mock.patch("crmsh.service_manager.ServiceManager.service_is_active")
     @mock.patch("crmsh.service_manager.ServiceManager.start_service")
-    def test_start_qnetd(self, mock_start):
+    def test_start_qnetd(self, mock_start, mock_active):
+        mock_active.return_value = False
         self.qdevice_with_ip.start_qnetd()
         mock_start.assert_called_once_with("corosync-qnetd.service", 
enable=True, remote_addr="10.10.10.123")
 
@@ -685,6 +687,7 @@
         mock_service_instance = mock.Mock()
         mock_service.return_value = mock_service_instance
         mock_service_instance.service_is_active.return_value = False
+        self.qdevice_with_ip.config_qnetd_port_in_sysconfig = mock.Mock()
 
         self.qdevice_with_ip.config_qnetd_port()
 
@@ -704,6 +707,7 @@
         mock_cluster_shell.return_value = mock_cluster_shell_instance
         mock_cluster_shell_instance.get_rc_stdout_stderr_without_input = 
mock.Mock(return_value=(0, None, None))
         mock_cluster_shell_instance.get_stdout_or_raise_error = 
mock.Mock(return_value=None)
+        self.qdevice_with_ip.config_qnetd_port_in_sysconfig = mock.Mock()
         self.qdevice_with_ip.port = 5403
 
         self.qdevice_with_ip.config_qnetd_port()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_service_manager.py 
new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_service_manager.py
--- old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_service_manager.py    
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_service_manager.py    
2026-05-28 09:45:27.000000000 +0200
@@ -56,17 +56,17 @@
     def test_start_service(self, mock_call_with_parallax: mock.MagicMock):
         self.service_manager._call.return_value = ['node1']
         self.assertEqual(['node1'], 
self.service_manager.start_service('service1', remote_addr='node1'))
-        self.service_manager._call.assert_called_once_with('node1', [], 
"systemctl start 'service1'")
+        self.service_manager._call.assert_called_once_with('node1', [], 
"systemctl start 'service1' && systemctl is-active 'service1'")
 
     def test_start_service_on_multiple_host(self, mock_call_with_parallax: 
mock.MagicMock):
         self.service_manager._call.return_value = ['node1', 'node2']
         self.assertEqual(['node1', 'node2'], 
self.service_manager.start_service('service1', node_list=['node1', 'node2']))
-        self.service_manager._call.assert_called_once_with(None, ['node1', 
'node2'], "systemctl start 'service1'")
+        self.service_manager._call.assert_called_once_with(None, ['node1', 
'node2'], "systemctl start 'service1' && systemctl is-active 'service1'")
 
     def test_start_and_enable_service(self, mock_call_with_parallax: 
mock.MagicMock):
         self.service_manager._call.return_value = ['node1']
         self.assertEqual(['node1'], 
self.service_manager.start_service('service1', enable=True, 
remote_addr='node1'))
-        self.service_manager._call.assert_called_once_with('node1', [], 
"systemctl enable --now 'service1'")
+        self.service_manager._call.assert_called_once_with('node1', [], 
"systemctl enable --now 'service1' && systemctl is-enabled 'service1' && 
systemctl is-active 'service1'")
 
     def test_stop_service(self, mock_call_with_parallax: mock.MagicMock):
         self.service_manager._call.return_value = ['node1']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_ui_cluster.py 
new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_ui_cluster.py
--- old/crmsh-5.1.0+20260508.3e8e1915/test/unittests/test_ui_cluster.py 
2026-05-08 08:10:10.000000000 +0200
+++ new/crmsh-5.1.0+20260528.b0973362/test/unittests/test_ui_cluster.py 
2026-05-28 09:45:27.000000000 +0200
@@ -56,6 +56,7 @@
             mock.call("The cluster stack already started on node2")
             ])
 
+    @mock.patch('crmsh.bootstrap.get_failed_services')
     @mock.patch('crmsh.qdevice.QDevice.check_qdevice_vote')
     @mock.patch('crmsh.bootstrap.start_pacemaker')
     @mock.patch('logging.Logger.error')
@@ -64,12 +65,14 @@
     @mock.patch('crmsh.service_manager.ServiceManager.start_service')
     @mock.patch('crmsh.service_manager.ServiceManager.service_is_active')
     @mock.patch('crmsh.ui_utils.parse_and_validate_node_args')
-    def test_do_start(self, mock_parse_nodes, mock_active, mock_start, 
mock_qdevice_configured, mock_info, mock_error, mock_start_pacemaker, 
mock_check_qdevice):
+    def test_do_start(self, mock_parse_nodes, mock_active, mock_start, 
mock_qdevice_configured, mock_info, mock_error, mock_start_pacemaker, 
mock_check_qdevice, mock_get_failed_services):
         context_inst = mock.Mock()
         mock_start_pacemaker.return_value = ["node1"]
         mock_parse_nodes.return_value = ["node1", "node2"], False
         mock_active.side_effect = [False, False, False, False]
         mock_qdevice_configured.return_value = True
+        mock_get_failed_services.return_value = []
+        mock_start.return_value = ["node1", "node2"]
 
         self.ui_cluster_inst.do_start(context_inst, "node1", "node2")
 
@@ -82,7 +85,7 @@
         mock_start.assert_called_once_with("corosync-qdevice", 
node_list=["node1", "node2"])
         mock_qdevice_configured.assert_called_once_with()
         mock_info.assert_called_once_with("The cluster stack started on %s", 
"node1")
-        mock_error.assert_called_once_with("The cluster stack failed to start 
on %s", "node2")
+        mock_error.assert_called_once_with("The cluster stack failed to start 
on %s.%s", "node2", '')
 
     @mock.patch('crmsh.utils.wait_for_dc')
     @mock.patch('crmsh.ui_cluster.Cluster._node_ready_to_stop_cluster_service')

Reply via email to