From 1c47f0f5a6262a5945ba9262f3dce098655ff7af Mon Sep 17 00:00:00 2001
From: xin liang <xliang@suse.com>
Date: Mon, 26 Aug 2024 17:15:58 +0800
Subject: [PATCH] Dev: behave: Add functional test for ssh blocking case

After add
```
[core]
no_ssh = yes
```
in /etc/crmsh/crmsh.conf, crmsh should works in local mode
---
 .github/workflows/crmsh-ci.yml             | 15 ++++
 codecov.yml                                |  4 +-
 data-manifest                              |  1 +
 test/features/cluster_blocking_ssh.feature | 90 ++++++++++++++++++++++
 test/features/steps/step_implementation.py | 10 +++
 5 files changed, 118 insertions(+), 2 deletions(-)
 create mode 100644 test/features/cluster_blocking_ssh.feature

diff --git a/.github/workflows/crmsh-ci.yml b/.github/workflows/crmsh-ci.yml
index b8bb4d629e..4b78e14a44 100644
--- a/.github/workflows/crmsh-ci.yml
+++ b/.github/workflows/crmsh-ci.yml
@@ -403,6 +403,21 @@ jobs:
         token: ${{ secrets.CODECOV_TOKEN }}
         flags: integration
 
+  functional_test_blocking_ssh:
+    runs-on: ubuntu-20.04
+    timeout-minutes: 40
+    steps:
+    - uses: actions/checkout@v4
+    - name: functional test for blocking ssh
+      run:  |
+        echo '{ "exec-opts": ["native.cgroupdriver=systemd"] }' | sudo tee /etc/docker/daemon.json
+        sudo systemctl restart docker.service
+        $DOCKER_SCRIPT `$GET_INDEX_OF cluster_blocking_ssh`
+    - uses: codecov/codecov-action@v4
+      with:
+        token: ${{ secrets.CODECOV_TOKEN }}
+        flags: integration
+
   original_regression_test:
     runs-on: ubuntu-20.04
     timeout-minutes: 40
diff --git a/codecov.yml b/codecov.yml
index e45cf0f1f4..520acb9383 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -8,7 +8,7 @@ coverage:
         threshold: 0.1%
 codecov: 
   notify:
-    after_n_builds: 25
+    after_n_builds: 26
 comment:
-  after_n_builds: 25
+  after_n_builds: 26
   layout: "condensed_header, flags, files, condensed_footer"
diff --git a/data-manifest b/data-manifest
index 71e1ed6a01..2b0c6b2129 100644
--- a/data-manifest
+++ b/data-manifest
@@ -71,6 +71,7 @@ test/features/bootstrap_options.feature
 test/features/bootstrap_sbd_delay.feature
 test/features/bootstrap_sbd_normal.feature
 test/features/cluster_api.feature
+test/features/cluster_blocking_ssh.feature
 test/features/configure_bugs.feature
 test/features/constraints_bugs.feature
 test/features/coveragerc
diff --git a/test/features/cluster_blocking_ssh.feature b/test/features/cluster_blocking_ssh.feature
new file mode 100644
index 0000000000..47b7f8fc32
--- /dev/null
+++ b/test/features/cluster_blocking_ssh.feature
@@ -0,0 +1,90 @@
+Feature: cluster testing with ssh blocked
+
+  Need nodes: hanode1 hanode2
+
+  Scenario: Cluster testing with ssh blocked (bsc#1228899)
+    Given   Cluster service is "stopped" on "hanode1"
+    And     Cluster service is "stopped" on "hanode2"
+    # without bootstrap, write corosync.conf and authkey directly
+    When    Run "dd if=/dev/urandom of=/etc/corosync/authkey bs=1 count=256" on "hanode1"
+    And     Write multi lines to file "/etc/corosync/corosync.conf" on "hanode1"
+      """
+      totem {
+        version: 2
+        cluster_name: hacluster
+        clear_node_high_bit: yes
+        interface {
+                ringnumber: 0
+                mcastport: 5405
+                ttl: 1
+        }
+
+        transport: udpu
+        crypto_hash: sha1
+        crypto_cipher: aes256
+        token: 5000
+        join: 60
+        max_messages: 20
+        token_retransmits_before_loss_const: 10
+      }
+
+      logging {
+        fileline: off
+        to_stderr: no
+        to_logfile: yes
+        logfile: /var/log/cluster/corosync.log
+        to_syslog: yes
+        debug: off
+        timestamp: on
+        logger_subsys {
+                subsys: QUORUM
+                debug: off
+        }
+
+      }
+
+      nodelist {
+        node {
+                ring0_addr: @hanode1.ip.default
+                nodeid: 1
+        }
+
+        node {
+                ring0_addr: @hanode2.ip.default
+                nodeid: 2
+        }
+      }
+
+      quorum {
+        provider: corosync_votequorum
+        expected_votes: 2
+        two_node: 1
+      }
+      """
+    And     Run "scp /etc/corosync/authkey /etc/corosync/corosync.conf hanode2:/etc/corosync/" on "hanode1"
+    And     Run "systemctl start pacemaker" on "hanode1"
+    And     Run "systemctl start pacemaker" on "hanode2"
+    And     Wait for DC
+    Then    Cluster service is "started" on "hanode1"
+    And     Cluster service is "started" on "hanode2"
+    And     Online nodes are "hanode1 hanode2"
+    # block ssh between nodes
+    When    Run "systemctl start firewalld" on "hanode2"
+    And     Run "firewall-cmd --zone=public --add-rich-rule='rule port port=22 protocol=tcp drop' --permanent && firewall-cmd --reload" on "hanode2"
+    And     Try "ssh -o ConnectTimeout=5 hanode2" on "hanode1"
+    Then    Except "ssh: connect to host hanode2 port 22: Connection timed out" in stderr
+    When    Run "timeout 5s crm report || echo "timeout"" on "hanode1"
+    Then    Expected "timeout" in stdout
+    When    Write multi lines to file "/etc/crm/crm.conf" on "hanode1"
+      """
+      [core]
+      no_ssh = yes
+      """
+    When    Run "crm configure property stonith-enabled=false" on "hanode1"
+    And     Run "crm report -d /tmp/report" on "hanode1"
+    Then    Directory "/tmp/report/hanode1" created
+    Then    Directory "/tmp/report/hanode2" not created
+    Then    Expected "ERROR: ssh-related operations are disabled. crm report works in local mode." in stderr
+    Then    Run "crm status" OK on "hanode1"
+    When    Run "crm cluster stop --all" on "hanode1"
+    Then    Expected "ERROR: ssh-related operations are disabled. crmsh works in local mode." in stderr
diff --git a/test/features/steps/step_implementation.py b/test/features/steps/step_implementation.py
index 540e9ceaee..b20dd3fb04 100644
--- a/test/features/steps/step_implementation.py
+++ b/test/features/steps/step_implementation.py
@@ -376,6 +376,16 @@ def step_impl(context, f, archive):
     assert file_in_archive(f, archive) is True
 
 
+@then('Directory "{directory}" created')
+def step_impl(context, directory):
+    assert os.path.isdir(directory) is True
+
+
+@then('Directory "{directory}" not created')
+def step_impl(context, directory):
+    assert os.path.isdir(directory) is False
+
+
 @then('File "{f}" not in "{archive}"')
 def step_impl(context, f, archive):
     assert file_in_archive(f, archive) is False
