Blame SOURCES/bz1833115-01-add-brief-to-resource-disable-simulate.patch

715401
From c73cbac945259429e18fcd457341f83f8c8f6e59 Mon Sep 17 00:00:00 2001
715401
From: Tomas Jelinek <tojeline@redhat.com>
715401
Date: Tue, 9 Jun 2020 13:39:35 +0200
715401
Subject: [PATCH 1/6] add --brief to 'resource disable --simulate'
715401
715401
---
715401
 pcs/cli/common/parse_args.py                  |   2 +-
715401
 pcs/lib/commands/resource.py                  | 116 ++++----
715401
 .../resource/test_resource_enable_disable.py  | 252 +++++++++++-------
715401
 pcs/pcs.8                                     |   8 +-
715401
 pcs/resource.py                               |  14 +-
715401
 pcs/test/test_resource.py                     | 110 +++++++-
715401
 pcs/usage.py                                  |  15 +-
715401
 pcs/utils.py                                  |   1 +
715401
 pcsd/capabilities.xml                         |  12 +-
715401
 9 files changed, 370 insertions(+), 160 deletions(-)
715401
715401
diff --git a/pcs/cli/common/parse_args.py b/pcs/cli/common/parse_args.py
715401
index 715e7643..19a658e5 100644
715401
--- a/pcs/cli/common/parse_args.py
715401
+++ b/pcs/cli/common/parse_args.py
715401
@@ -18,7 +18,7 @@ PCS_LONG_OPTIONS = [
715401
     "force", "skip-offline", "autocorrect", "interactive", "autodelete",
715401
     "all", "full", "groups", "local", "wait", "config", "async",
715401
     "start", "enable", "disabled", "off", "request-timeout=",
715401
-    "safe", "no-strict", "strict", "simulate",
715401
+    "safe", "no-strict", "strict", "simulate", "brief",
715401
     "pacemaker", "corosync",
715401
     "no-default-ops", "defaults", "nodesc",
715401
     "clone", "master", "name=", "group=", "node=",
715401
diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py
715401
index 1ad03a48..fff5f40b 100644
715401
--- a/pcs/lib/commands/resource.py
715401
+++ b/pcs/lib/commands/resource.py
715401
@@ -732,6 +732,48 @@ def _disable_validate_and_edit_cib(env, resources_section, resource_ids):
715401
             env.get_cluster_state()
715401
         )
715401
     )
715401
+    return resource_el_list
715401
+
715401
+def _disable_run_simulate(cmd_runner, cib, disabled_resource_el_list, strict):
715401
+    inner_resources_names_set = set()
715401
+    disabled_resource_ids = set()
715401
+    for resource_el in disabled_resource_el_list:
715401
+        disabled_resource_ids.add(resource_el.get("id"))
715401
+        inner_resources_names_set.update({
715401
+            inner_resource_el.get("id")
715401
+            for inner_resource_el
715401
+                in resource.common.get_all_inner_resources(resource_el)
715401
+        })
715401
+
715401
+    plaintext_status, transitions, dummy_cib = simulate_cib(cmd_runner, cib)
715401
+    simulated_operations = (
715401
+        simulate_tools.get_operations_from_transitions(transitions)
715401
+    )
715401
+    other_affected = set()
715401
+    if strict:
715401
+        other_affected = set(
715401
+            simulate_tools.get_resources_from_operations(
715401
+                simulated_operations,
715401
+                exclude=disabled_resource_ids,
715401
+            )
715401
+        )
715401
+    else:
715401
+        other_affected = set(
715401
+            simulate_tools.get_resources_left_stopped(
715401
+                simulated_operations,
715401
+                exclude=disabled_resource_ids,
715401
+            )
715401
+            +
715401
+            simulate_tools.get_resources_left_demoted(
715401
+                simulated_operations,
715401
+                exclude=disabled_resource_ids,
715401
+            )
715401
+        )
715401
+
715401
+    # Stopping a clone stops all its inner resources. That should not block
715401
+    # stopping the clone.
715401
+    other_affected = other_affected - inner_resources_names_set
715401
+    return plaintext_status, other_affected
715401
 
715401
 def disable(env, resource_ids, wait):
715401
     """
715401
@@ -762,57 +804,15 @@ def disable_safe(env, resource_ids, strict, wait):
715401
     with resource_environment(
715401
         env, wait, resource_ids, _ensure_disabled_after_wait(True)
715401
     ) as resources_section:
715401
-        resource_el_list = _find_resources_or_raise(
715401
-            resources_section,
715401
-            resource_ids
715401
-        )
715401
-        env.report_processor.process_list(
715401
-            _resource_list_enable_disable(
715401
-                resource_el_list,
715401
-                resource.common.disable,
715401
-                env.get_cluster_state()
715401
-            )
715401
+        resource_el_list =_disable_validate_and_edit_cib(
715401
+            env, resources_section, resource_ids
715401
         )
715401
-
715401
-        inner_resources_names_set = set()
715401
-        for resource_el in resource_el_list:
715401
-            inner_resources_names_set.update({
715401
-                inner_resource_el.get("id")
715401
-                for inner_resource_el
715401
-                    in resource.common.get_all_inner_resources(resource_el)
715401
-            })
715401
-
715401
-        plaintext_status, transitions, dummy_cib = simulate_cib(
715401
+        plaintext_status, other_affected = _disable_run_simulate(
715401
             env.cmd_runner(),
715401
-            get_root(resources_section)
715401
-        )
715401
-        simulated_operations = (
715401
-            simulate_tools.get_operations_from_transitions(transitions)
715401
+            get_root(resources_section),
715401
+            resource_el_list,
715401
+            strict,
715401
         )
715401
-        other_affected = set()
715401
-        if strict:
715401
-            other_affected = set(
715401
-                simulate_tools.get_resources_from_operations(
715401
-                    simulated_operations,
715401
-                    exclude=resource_ids
715401
-                )
715401
-            )
715401
-        else:
715401
-            other_affected = set(
715401
-                simulate_tools.get_resources_left_stopped(
715401
-                    simulated_operations,
715401
-                    exclude=resource_ids
715401
-                )
715401
-                +
715401
-                simulate_tools.get_resources_left_demoted(
715401
-                    simulated_operations,
715401
-                    exclude=resource_ids
715401
-                )
715401
-            )
715401
-
715401
-        # Stopping a clone stops all its inner resources. That should not block
715401
-        # stopping the clone.
715401
-        other_affected = other_affected - inner_resources_names_set
715401
         if other_affected:
715401
             raise LibraryError(
715401
                 reports.resource_disable_affects_other_resources(
715401
@@ -822,23 +822,31 @@ def disable_safe(env, resource_ids, strict, wait):
715401
                 )
715401
             )
715401
 
715401
-def disable_simulate(env, resource_ids):
715401
+def disable_simulate(env, resource_ids, strict):
715401
     """
715401
     Simulate disallowing specified resource to be started by the cluster
715401
 
715401
     LibraryEnvironment env --
715401
     strings resource_ids -- ids of the resources to be disabled
715401
+    bool strict -- if False, allow resources to be migrated
715401
     """
715401
     if not env.is_cib_live:
715401
         raise LibraryError(reports.live_environment_required(["CIB"]))
715401
 
715401
-    resources_section = get_resources(env.get_cib())
715401
-    _disable_validate_and_edit_cib(env, resources_section, resource_ids)
715401
-    plaintext_status, dummy_transitions, dummy_cib = simulate_cib(
715401
+    cib = env.get_cib()
715401
+    resource_el_list = _disable_validate_and_edit_cib(
715401
+        env, get_resources(cib), resource_ids
715401
+    )
715401
+    plaintext_status, other_affected = _disable_run_simulate(
715401
         env.cmd_runner(),
715401
-        get_root(resources_section)
715401
+        cib,
715401
+        resource_el_list,
715401
+        strict,
715401
+    )
715401
+    return dict(
715401
+        plaintext_simulated_status=plaintext_status,
715401
+        other_affected_resource_list=sorted(other_affected),
715401
     )
715401
-    return plaintext_status
715401
 
715401
 def enable(env, resource_ids, wait):
715401
     """
715401
diff --git a/pcs/lib/commands/test/resource/test_resource_enable_disable.py b/pcs/lib/commands/test/resource/test_resource_enable_disable.py
715401
index 6514d9fc..07d86850 100644
715401
--- a/pcs/lib/commands/test/resource/test_resource_enable_disable.py
715401
+++ b/pcs/lib/commands/test/resource/test_resource_enable_disable.py
715401
@@ -1649,96 +1649,7 @@ class EnableBundle(TestCase):
715401
         ])
715401
 
715401
 
715401
-@mock.patch("pcs.lib.pacemaker.live.write_tmpfile")
715401
-class DisableSimulate(TestCase):
715401
-    def setUp(self):
715401
-        self.env_assist, self.config = get_env_tools(test_case=self)
715401
-        self.tmpfile_new_cib = mock.MagicMock()
715401
-        self.tmpfile_new_cib.name = rc("new_cib.tmp")
715401
-        self.tmpfile_new_cib.read.return_value = "<new-cib/>"
715401
-        self.tmpfile_transitions = mock.MagicMock()
715401
-        self.tmpfile_transitions.name = rc("transitions.tmp")
715401
-        self.tmpfile_transitions.read.return_value = "<transitions/>"
715401
-
715401
-    def test_not_live(self, mock_write_tmpfile):
715401
-        mock_write_tmpfile.side_effect = [
715401
-            AssertionError("No other write_tmpfile call expected")
715401
-        ]
715401
-        self.config.env.set_cib_data("<cib />")
715401
-        self.env_assist.assert_raise_library_error(
715401
-            lambda: resource.disable_simulate(self.env_assist.get_env(), ["A"]),
715401
-            [
715401
-                fixture.error(
715401
-                    report_codes.LIVE_ENVIRONMENT_REQUIRED,
715401
-                    forbidden_options=["CIB"]
715401
-                ),
715401
-            ],
715401
-            expected_in_processor=False
715401
-        )
715401
-
715401
-    def test_nonexistent_resource(self, mock_write_tmpfile):
715401
-        mock_write_tmpfile.side_effect = [
715401
-            AssertionError("No other write_tmpfile call expected")
715401
-        ]
715401
-        self.config.runner.cib.load()
715401
-        self.env_assist.assert_raise_library_error(
715401
-            lambda: resource.disable_simulate(self.env_assist.get_env(), ["A"]),
715401
-            [
715401
-                fixture.report_not_found("A", "resources"),
715401
-            ],
715401
-            expected_in_processor=False
715401
-        )
715401
-
715401
-    def test_success(self, mock_write_tmpfile):
715401
-        mock_write_tmpfile.side_effect = [
715401
-            self.tmpfile_new_cib, self.tmpfile_transitions,
715401
-            AssertionError("No other write_tmpfile call expected")
715401
-        ]
715401
-        (self.config
715401
-            .runner.cib.load(resources=fixture_primitive_cib_enabled)
715401
-            .runner.pcmk.load_state(resources=fixture_primitive_status_managed)
715401
-            .runner.pcmk.simulate_cib(
715401
-                self.tmpfile_new_cib.name,
715401
-                self.tmpfile_transitions.name,
715401
-                stdout="simulate output",
715401
-                resources=fixture_primitive_cib_disabled,
715401
-            )
715401
-        )
715401
-
715401
-        result = resource.disable_simulate(self.env_assist.get_env(), ["A"])
715401
-        self.assertEqual("simulate output", result)
715401
-
715401
-    def test_simulate_error(self, mock_write_tmpfile):
715401
-        mock_write_tmpfile.side_effect = [
715401
-            self.tmpfile_new_cib, self.tmpfile_transitions,
715401
-            AssertionError("No other write_tmpfile call expected")
715401
-        ]
715401
-        (self.config
715401
-            .runner.cib.load(resources=fixture_primitive_cib_enabled)
715401
-            .runner.pcmk.load_state(resources=fixture_primitive_status_managed)
715401
-            .runner.pcmk.simulate_cib(
715401
-                self.tmpfile_new_cib.name,
715401
-                self.tmpfile_transitions.name,
715401
-                stdout="some stdout",
715401
-                stderr="some stderr",
715401
-                returncode=1,
715401
-                resources=fixture_primitive_cib_disabled,
715401
-            )
715401
-        )
715401
-
715401
-        self.env_assist.assert_raise_library_error(
715401
-            lambda: resource.disable_simulate(self.env_assist.get_env(), ["A"]),
715401
-            [
715401
-                fixture.error(
715401
-                    report_codes.CIB_SIMULATE_ERROR,
715401
-                    reason="some stderr",
715401
-                ),
715401
-            ],
715401
-            expected_in_processor=False
715401
-        )
715401
-
715401
-
715401
-class DisableSafeMixin(object):
715401
+class DisableSafeFixturesMixin(object):
715401
     fixture_transitions_both_stopped = """
715401
         <transition_graph>
715401
           <synapse>
715401
@@ -1925,6 +1836,167 @@ class DisableSafeMixin(object):
715401
             )
715401
         )
715401
 
715401
+
715401
+@mock.patch("pcs.lib.pacemaker.live.write_tmpfile")
715401
+class DisableSimulate(DisableSafeFixturesMixin, TestCase):
715401
+    def test_not_live(self, mock_write_tmpfile):
715401
+        mock_write_tmpfile.side_effect = [
715401
+            AssertionError("No other write_tmpfile call expected")
715401
+        ]
715401
+        self.config.env.set_cib_data("<cib />")
715401
+        self.env_assist.assert_raise_library_error(
715401
+            lambda: resource.disable_simulate(
715401
+                self.env_assist.get_env(),
715401
+                ["A"],
715401
+                True,
715401
+            ),
715401
+            [
715401
+                fixture.error(
715401
+                    report_codes.LIVE_ENVIRONMENT_REQUIRED,
715401
+                    forbidden_options=["CIB"]
715401
+                ),
715401
+            ],
715401
+            expected_in_processor=False
715401
+        )
715401
+
715401
+    def test_nonexistent_resource(self, mock_write_tmpfile):
715401
+        mock_write_tmpfile.side_effect = [
715401
+            AssertionError("No other write_tmpfile call expected")
715401
+        ]
715401
+        self.config.runner.cib.load()
715401
+        self.env_assist.assert_raise_library_error(
715401
+            lambda: resource.disable_simulate(
715401
+                self.env_assist.get_env(),
715401
+                ["A"],
715401
+                True,
715401
+            ),
715401
+            [
715401
+                fixture.report_not_found("A", "resources"),
715401
+            ],
715401
+            expected_in_processor=False
715401
+        )
715401
+
715401
+    def test_success_no_others_stopped(self, mock_write_tmpfile):
715401
+        self.fixture_disable_both_resources(mock_write_tmpfile)
715401
+        self.config.runner.pcmk.simulate_cib(
715401
+            self.tmpfile_new_cib.name,
715401
+            self.tmpfile_transitions.name,
715401
+            stdout="simulate output",
715401
+            resources=fixture_two_primitives_cib_disabled_both,
715401
+        )
715401
+
715401
+        result = resource.disable_simulate(
715401
+            self.env_assist.get_env(),
715401
+            ["A", "B"],
715401
+            True,
715401
+        )
715401
+        self.assertEqual(
715401
+            result,
715401
+            dict(
715401
+                plaintext_simulated_status="simulate output",
715401
+                other_affected_resource_list=[],
715401
+            ),
715401
+        )
715401
+
715401
+    def test_success_others_stopped(self, mock_write_tmpfile):
715401
+        self.fixture_disable_both_resources(mock_write_tmpfile)
715401
+        self.config.runner.pcmk.simulate_cib(
715401
+            self.tmpfile_new_cib.name,
715401
+            self.tmpfile_transitions.name,
715401
+            stdout="simulate output",
715401
+            resources=fixture_two_primitives_cib_disabled,
715401
+        )
715401
+
715401
+        result = resource.disable_simulate(
715401
+            self.env_assist.get_env(),
715401
+            ["A"],
715401
+            True,
715401
+        )
715401
+        self.assertEqual(
715401
+            result,
715401
+            dict(
715401
+                plaintext_simulated_status="simulate output",
715401
+                other_affected_resource_list=["B"],
715401
+            ),
715401
+        )
715401
+
715401
+    def test_success_others_migrated_strict(self, mock_write_tmpfile):
715401
+        self.fixture_migrate_one_resource(mock_write_tmpfile)
715401
+        self.config.runner.pcmk.simulate_cib(
715401
+            self.tmpfile_new_cib.name,
715401
+            self.tmpfile_transitions.name,
715401
+            stdout="simulate output",
715401
+            resources=fixture_two_primitives_cib_disabled,
715401
+        )
715401
+        result = resource.disable_simulate(
715401
+            self.env_assist.get_env(),
715401
+            ["A"],
715401
+            True,
715401
+        )
715401
+        self.assertEqual(
715401
+            result,
715401
+            dict(
715401
+                plaintext_simulated_status="simulate output",
715401
+                other_affected_resource_list=["B"],
715401
+            ),
715401
+        )
715401
+
715401
+    def test_success_others_migrated_no_strict(self, mock_write_tmpfile):
715401
+        self.fixture_migrate_one_resource(mock_write_tmpfile)
715401
+        self.config.runner.pcmk.simulate_cib(
715401
+            self.tmpfile_new_cib.name,
715401
+            self.tmpfile_transitions.name,
715401
+            stdout="simulate output",
715401
+            resources=fixture_two_primitives_cib_disabled,
715401
+        )
715401
+        result = resource.disable_simulate(
715401
+            self.env_assist.get_env(),
715401
+            ["A"],
715401
+            False,
715401
+        )
715401
+        self.assertEqual(
715401
+            result,
715401
+            dict(
715401
+                plaintext_simulated_status="simulate output",
715401
+                other_affected_resource_list=[],
715401
+            ),
715401
+        )
715401
+
715401
+    def test_simulate_error(self, mock_write_tmpfile):
715401
+        mock_write_tmpfile.side_effect = [
715401
+            self.tmpfile_new_cib, self.tmpfile_transitions,
715401
+            AssertionError("No other write_tmpfile call expected")
715401
+        ]
715401
+        (self.config
715401
+            .runner.cib.load(resources=fixture_primitive_cib_enabled)
715401
+            .runner.pcmk.load_state(resources=fixture_primitive_status_managed)
715401
+            .runner.pcmk.simulate_cib(
715401
+                self.tmpfile_new_cib.name,
715401
+                self.tmpfile_transitions.name,
715401
+                stdout="some stdout",
715401
+                stderr="some stderr",
715401
+                returncode=1,
715401
+                resources=fixture_primitive_cib_disabled,
715401
+            )
715401
+        )
715401
+
715401
+        self.env_assist.assert_raise_library_error(
715401
+            lambda: resource.disable_simulate(
715401
+                self.env_assist.get_env(),
715401
+                ["A"],
715401
+                True,
715401
+            ),
715401
+            [
715401
+                fixture.error(
715401
+                    report_codes.CIB_SIMULATE_ERROR,
715401
+                    reason="some stderr",
715401
+                ),
715401
+            ],
715401
+            expected_in_processor=False
715401
+        )
715401
+
715401
+
715401
+class DisableSafeMixin(DisableSafeFixturesMixin):
715401
     def test_not_live(self, mock_write_tmpfile):
715401
         mock_write_tmpfile.side_effect = [
715401
             AssertionError("No other write_tmpfile call expected")
715401
diff --git a/pcs/pcs.8 b/pcs/pcs.8
715401
index 80b80ef7..3367f979 100644
715401
--- a/pcs/pcs.8
715401
+++ b/pcs/pcs.8
715401
@@ -90,23 +90,23 @@ Deletes the resource, group, master or clone (and all resources within the group
715401
 enable <resource id>... [\fB\-\-wait\fR[=n]]
715401
 Allow the cluster to start the resources. Depending on the rest of the configuration (constraints, options, failures, etc), the resources may remain stopped. If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the resources to start and then return 0 if the resources are started, or 1 if the resources have not yet started. If 'n' is not specified it defaults to 60 minutes.
715401
 .TP
715401
-disable <resource id>... [\fB\-\-safe\fR [\fB\-\-no\-strict\fR]] [\fB\-\-simulate\fR] [\fB\-\-wait\fR[=n]]
715401
+disable <resource id>... [\fB\-\-safe\fR [\fB\-\-no\-strict\fR]] [\fB\-\-simulate\fR [\fB\-\-brief\fR]] [\fB\-\-wait\fR[=n]]
715401
 Attempt to stop the resources if they are running and forbid the cluster from starting them again. Depending on the rest of the configuration (constraints, options, failures, etc), the resources may remain started.
715401
 .br
715401
 If \fB\-\-safe\fR is specified, no changes to the cluster configuration will be made if other than specified resources would be affected in any way.
715401
 .br
715401
 If \fB\-\-no\-strict\fR is specified, no changes to the cluster configuration will be made if other than specified resources would get stopped or demoted. Moving resources between nodes is allowed.
715401
 .br
715401
-If \fB\-\-simulate\fR is specified, no changes to the cluster configuration will be made and the effect of the changes will be printed instead.
715401
+If \fB\-\-simulate\fR is specified, no changes to the cluster configuration will be made and the effect of the changes will be printed instead. If \fB\-\-brief\fR is also specified, only a list of affected resources will be printed.
715401
 .br
715401
 If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the resources to stop and then return 0 if the resources are stopped or 1 if the resources have not stopped. If 'n' is not specified it defaults to 60 minutes.
715401
 .TP
715401
-safe\-disable <resource id>... [\fB\-\-no\-strict\fR] [\fB\-\-simulate\fR] [\fB\-\-wait\fR[=n]] [\fB\-\-force\fR]
715401
+safe\-disable <resource id>... [\fB\-\-no\-strict\fR] [\fB\-\-simulate\fR [\fB\-\-brief\fR]] [\fB\-\-wait\fR[=n]] [\fB\-\-force\fR]
715401
 Attempt to stop the resources if they are running and forbid the cluster from starting them again. Depending on the rest of the configuration (constraints, options, failures, etc), the resources may remain started. No changes to the cluster configuration will be made if other than specified resources would be affected in any way.
715401
 .br
715401
 If \fB\-\-no\-strict\fR is specified, no changes to the cluster configuration will be made if other than specified resources would get stopped or demoted. Moving resources between nodes is allowed.
715401
 .br
715401
-If \fB\-\-simulate\fR is specified, no changes to the cluster configuration will be made and the effect of the changes will be printed instead.
715401
+If \fB\-\-simulate\fR is specified, no changes to the cluster configuration will be made and the effect of the changes will be printed instead. If \fB\-\-brief\fR is also specified, only a list of affected resources will be printed.
715401
 .br
715401
 If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the resources to stop and then return 0 if the resources are stopped or 1 if the resources have not stopped. If 'n' is not specified it defaults to 60 minutes.
715401
 .br
715401
diff --git a/pcs/resource.py b/pcs/resource.py
715401
index 33f76656..3274910a 100644
715401
--- a/pcs/resource.py
715401
+++ b/pcs/resource.py
715401
@@ -2041,6 +2041,7 @@ def resource_disable_cmd(lib, argv, modifiers):
715401
     """
715401
     Options:
715401
       * -f - CIB file
715401
+      * --brief - show brief output of --simulate
715401
       * --safe - only disable if no other resource gets stopped or demoted
715401
       * --simulate - do not push the CIB, print its effects
715401
       * --no-strict - allow disable if other resource is affected
715401
@@ -2050,7 +2051,17 @@ def resource_disable_cmd(lib, argv, modifiers):
715401
         raise CmdLineInputError("You must specify resource(s) to disable")
715401
 
715401
     if modifiers["simulate"]:
715401
-        print(lib.resource.disable_simulate(argv))
715401
+        result = lib.resource.disable_simulate(
715401
+            argv,
715401
+            not modifiers["no-strict"],
715401
+        )
715401
+        if modifiers["brief"]:
715401
+            # if the result is empty, printing it would produce a new line,
715401
+            # which is not wanted
715401
+            if result["other_affected_resource_list"]:
715401
+                print("\n".join(result["other_affected_resource_list"]))
715401
+            return
715401
+        print(result["plaintext_simulated_status"])
715401
         return
715401
     if modifiers["safe"] or modifiers["no-strict"]:
715401
         lib.resource.disable_safe(
715401
@@ -2065,6 +2076,7 @@ def resource_disable_cmd(lib, argv, modifiers):
715401
 def resource_safe_disable_cmd(lib, argv, modifiers):
715401
     """
715401
     Options:
715401
+      * --brief - show brief output of --simulate
715401
       * --force - skip checks for safe resource disable
715401
       * --no-strict - allow disable if other resource is affected
715401
       * --simulate - do not push the CIB, print its effects
715401
diff --git a/pcs/test/test_resource.py b/pcs/test/test_resource.py
715401
index 2a110683..972323f9 100644
715401
--- a/pcs/test/test_resource.py
715401
+++ b/pcs/test/test_resource.py
715401
@@ -6463,6 +6463,7 @@ class ResourceDisable(TestCase):
715401
 
715401
     def run_cmd(self, argv, modifiers=None):
715401
         default_modifiers = {
715401
+            "brief": False,
715401
             "safe": False,
715401
             "simulate": False,
715401
             "no-strict": False,
715401
@@ -6472,6 +6473,16 @@ class ResourceDisable(TestCase):
715401
             default_modifiers.update(modifiers)
715401
         resource.resource_disable_cmd(self.lib, argv, default_modifiers)
715401
 
715401
+    @staticmethod
715401
+    def _fixture_output(plaintext=None, resources=None):
715401
+        plaintext = plaintext if plaintext is not None else "simulate output"
715401
+        resources = resources if resources is not None else ["Rx", "Ry"]
715401
+        return dict(
715401
+            plaintext_simulated_status=plaintext,
715401
+            other_affected_resource_list=resources,
715401
+        )
715401
+
715401
+
715401
     def test_no_args(self):
715401
         with self.assertRaises(CmdLineInputError) as cm:
715401
             self.run_cmd([])
715401
@@ -6529,13 +6540,52 @@ class ResourceDisable(TestCase):
715401
 
715401
     @mock.patch("pcs.resource.print")
715401
     def test_simulate(self, mock_print):
715401
-        self.resource.disable_simulate.return_value = "simulate output"
715401
+        self.resource.disable_simulate.return_value = self._fixture_output()
715401
         self.run_cmd(["R1", "R2"], dict(simulate=True))
715401
-        self.resource.disable_simulate.assert_called_once_with(["R1", "R2"])
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], True
715401
+        )
715401
         self.resource.disable.assert_not_called()
715401
         self.resource.disable_safe.assert_not_called()
715401
         mock_print.assert_called_once_with("simulate output")
715401
 
715401
+    @mock.patch("pcs.resource.print")
715401
+    def test_simulate_brief(self, mock_print):
715401
+        self.resource.disable_simulate.return_value = self._fixture_output()
715401
+        self.run_cmd(["R1", "R2"], dict(simulate=True, brief=True))
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], True
715401
+        )
715401
+        self.resource.disable.assert_not_called()
715401
+        self.resource.disable_safe.assert_not_called()
715401
+        mock_print.assert_called_once_with("Rx\nRy")
715401
+
715401
+    @mock.patch("pcs.resource.print")
715401
+    def test_simulate_brief_nostrict(self, mock_print):
715401
+        self.resource.disable_simulate.return_value = self._fixture_output()
715401
+        self.run_cmd(
715401
+            ["R1", "R2"], {"simulate": True, "brief": True, "no-strict": True}
715401
+        )
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], False
715401
+        )
715401
+        self.resource.disable.assert_not_called()
715401
+        self.resource.disable_safe.assert_not_called()
715401
+        mock_print.assert_called_once_with("Rx\nRy")
715401
+
715401
+    @mock.patch("pcs.resource.print")
715401
+    def test_simulate_brief_nothing_affected(self, mock_print):
715401
+        self.resource.disable_simulate.return_value = self._fixture_output(
715401
+            resources=[]
715401
+        )
715401
+        self.run_cmd(["R1", "R2"], dict(simulate=True, brief=True))
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], True
715401
+        )
715401
+        self.resource.disable.assert_not_called()
715401
+        self.resource.disable_safe.assert_not_called()
715401
+        mock_print.assert_not_called()
715401
+
715401
     def test_wait(self):
715401
         self.run_cmd(["R1", "R2"], dict(wait="10"))
715401
         self.resource.disable.assert_called_once_with(["R1", "R2"], "10")
715401
@@ -6557,6 +6607,7 @@ class ResourceSafeDisable(TestCase):
715401
 
715401
     def run_cmd(self, argv, modifiers=None):
715401
         default_modifiers = {
715401
+            "brief": False,
715401
             "safe": False,
715401
             "simulate": False,
715401
             "no-strict": False,
715401
@@ -6567,6 +6618,15 @@ class ResourceSafeDisable(TestCase):
715401
             default_modifiers.update(modifiers)
715401
         resource.resource_safe_disable_cmd(self.lib, argv, default_modifiers)
715401
 
715401
+    @staticmethod
715401
+    def _fixture_output(plaintext=None, resources=None):
715401
+        plaintext = plaintext if plaintext is not None else "simulate output"
715401
+        resources = resources if resources is not None else ["Rx", "Ry"]
715401
+        return dict(
715401
+            plaintext_simulated_status=plaintext,
715401
+            other_affected_resource_list=resources,
715401
+        )
715401
+
715401
     def test_no_args(self):
715401
         with self.assertRaises(CmdLineInputError) as cm:
715401
             self.run_cmd([])
715401
@@ -6639,9 +6699,51 @@ class ResourceSafeDisable(TestCase):
715401
 
715401
     @mock.patch("pcs.resource.print")
715401
     def test_simulate(self, mock_print):
715401
-        self.resource.disable_simulate.return_value = "simulate output"
715401
+        self.resource.disable_simulate.return_value = self._fixture_output()
715401
         self.run_cmd(["R1", "R2"], dict(simulate=True))
715401
-        self.resource.disable_simulate.assert_called_once_with(["R1", "R2"])
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], True
715401
+        )
715401
         self.resource.disable.assert_not_called()
715401
         self.resource.disable_safe.assert_not_called()
715401
         mock_print.assert_called_once_with("simulate output")
715401
+
715401
+    @mock.patch("pcs.resource.print")
715401
+    def test_simulate_brief(self, mock_print):
715401
+        self.resource.disable_simulate.return_value = self._fixture_output()
715401
+        self.run_cmd(["R1", "R2"], dict(simulate=True, brief=True))
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], True
715401
+        )
715401
+        self.resource.disable.assert_not_called()
715401
+        self.resource.disable_safe.assert_not_called()
715401
+        mock_print.assert_called_once_with("Rx\nRy")
715401
+
715401
+    @mock.patch("pcs.resource.print")
715401
+    def test_simulate_brief_nostrict(self, mock_print):
715401
+        self.resource.disable_simulate.return_value = self._fixture_output()
715401
+        self.run_cmd(
715401
+            ["R1", "R2"], {"simulate": True, "brief": True, "no-strict": True}
715401
+        )
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], False
715401
+        )
715401
+        self.resource.disable.assert_not_called()
715401
+        self.resource.disable_safe.assert_not_called()
715401
+        mock_print.assert_called_once_with("Rx\nRy")
715401
+
715401
+    @mock.patch("pcs.resource.print")
715401
+    def test_simulate_brief_nothing_affected(self, mock_print):
715401
+        self.resource.disable_simulate.return_value = self._fixture_output(
715401
+            resources=[]
715401
+        )
715401
+        self.run_cmd(
715401
+            ["R1", "R2"],
715401
+            {"simulate": True, "brief": True, "no-strict": True}
715401
+        )
715401
+        self.resource.disable_simulate.assert_called_once_with(
715401
+            ["R1", "R2"], False
715401
+        )
715401
+        self.resource.disable.assert_not_called()
715401
+        self.resource.disable_safe.assert_not_called()
715401
+        mock_print.assert_not_called()
715401
diff --git a/pcs/usage.py b/pcs/usage.py
715401
index 61e6826e..408f9514 100644
715401
--- a/pcs/usage.py
715401
+++ b/pcs/usage.py
715401
@@ -252,7 +252,8 @@ Commands:
715401
         started, or 1 if the resources have not yet started. If 'n' is not
715401
         specified it defaults to 60 minutes.
715401
 
715401
-    disable <resource id>... [--safe [--no-strict]] [--simulate] [--wait[=n]]
715401
+    disable <resource id>... [--safe [--no-strict]] [--simulate [--brief]]
715401
+            [--wait[=n]]
715401
         Attempt to stop the resources if they are running and forbid the
715401
         cluster from starting them again. Depending on the rest of the
715401
         configuration (constraints, options, failures, etc), the resources may
715401
@@ -263,14 +264,16 @@ Commands:
715401
         will be made if other than specified resources would get stopped or
715401
         demoted. Moving resources between nodes is allowed.
715401
         If --simulate is specified, no changes to the cluster configuration
715401
-        will be made and the effect of the changes will be printed instead.
715401
+        will be made and the effect of the changes will be printed instead. If
715401
+        --brief is also specified, only a list of affected resources will be
715401
+        printed.
715401
         If --wait is specified, pcs will wait up to 'n' seconds for the
715401
         resources to stop and then return 0 if the resources are stopped or 1
715401
         if the resources have not stopped. If 'n' is not specified it defaults
715401
         to 60 minutes.
715401
 
715401
-    safe-disable <resource id>... [--no-strict] [--simulate] [--wait[=n]]
715401
-            [--force]
715401
+    safe-disable <resource id>... [--no-strict] [--simulate [--brief]]
715401
+            [--wait[=n]] [--force]
715401
         Attempt to stop the resources if they are running and forbid the
715401
         cluster from starting them again. Depending on the rest of the
715401
         configuration (constraints, options, failures, etc), the resources may
715401
@@ -280,7 +283,9 @@ Commands:
715401
         will be made if other than specified resources would get stopped or
715401
         demoted. Moving resources between nodes is allowed.
715401
         If --simulate is specified, no changes to the cluster configuration
715401
-        will be made and the effect of the changes will be printed instead.
715401
+        will be made and the effect of the changes will be printed instead. If
715401
+        --brief is also specified, only a list of affected resources will be
715401
+        printed.
715401
         If --wait is specified, pcs will wait up to 'n' seconds for the
715401
         resources to stop and then return 0 if the resources are stopped or 1
715401
         if the resources have not stopped. If 'n' is not specified it defaults
715401
diff --git a/pcs/utils.py b/pcs/utils.py
715401
index 66f7ebf1..e56a1e8b 100644
715401
--- a/pcs/utils.py
715401
+++ b/pcs/utils.py
715401
@@ -2960,6 +2960,7 @@ def get_modifiers():
715401
         "autocorrect": "--autocorrect" in pcs_options,
715401
         "autodelete": "--autodelete" in pcs_options,
715401
         "before": pcs_options.get("--before", None),
715401
+        "brief": "--brief" in pcs_options,
715401
         "corosync_conf": pcs_options.get("--corosync_conf", None),
715401
         "describe": "--nodesc" not in pcs_options,
715401
         "device": pcs_options.get("--device", []),
715401
diff --git a/pcsd/capabilities.xml b/pcsd/capabilities.xml
715401
index dafc771a..df1da355 100644
715401
--- a/pcsd/capabilities.xml
715401
+++ b/pcsd/capabilities.xml
715401
@@ -1145,7 +1145,17 @@
715401
       <description>
715401
         Show effects caused by disabling resources.
715401
 
715401
-        pcs commands: resource disable --simulate
715401
+        pcs commands: resource disable --simulate,
715401
+          resource safe-disable --simulate
715401
+      </description>
715401
+    </capability>
715401
+    <capability id="pcmk.resource.disable.simulate.brief" in-pcs="1" in-pcsd="0">
715401
+      <description>
715401
+        Show only a list of affected resources instead of the whole listing of
715401
+        effects caused by disabling resources.
715401
+
715401
+        pcs commands: resource disable --simulate --brief,
715401
+          resource safe-disable --simulate --brief
715401
       </description>
715401
     </capability>
715401
     <capability id="pcmk.resource.manage-unmanage" in-pcs="1" in-pcsd="1">
715401
-- 
715401
2.21.0
715401