pcs/SOURCES/bz1793574-01-fix-detecting-...

542 lines
21 KiB
Diff

From ac0305a8b6bb040ef06dcbfff309c91321400d44 Mon Sep 17 00:00:00 2001
From: Tomas Jelinek <tojeline@redhat.com>
Date: Mon, 27 Jan 2020 17:05:42 +0100
Subject: [PATCH 3/7] fix detecting fence history support
---
pcs/lib/commands/stonith.py | 38 ++++++++------
pcs/lib/pacemaker/live.py | 45 +++++++++-------
.../crm_mon.rng.with_fence_history.xml | 13 -----
.../crm_mon.rng.without_fence_history.xml | 9 ----
pcs_test/tier0/lib/commands/test_status.py | 35 +++----------
.../lib/commands/test_stonith_history.py | 52 ++++++-------------
pcs_test/tier0/lib/pacemaker/test_live.py | 31 ++++++++++-
.../tools/command_env/config_runner_pcmk.py | 41 +++++++++++++++
pcs_test/tools/command_env/mock_runner.py | 1 +
9 files changed, 141 insertions(+), 124 deletions(-)
delete mode 100644 pcs_test/resources/crm_mon.rng.with_fence_history.xml
delete mode 100644 pcs_test/resources/crm_mon.rng.without_fence_history.xml
diff --git a/pcs/lib/commands/stonith.py b/pcs/lib/commands/stonith.py
index c0849a54..ff87c852 100644
--- a/pcs/lib/commands/stonith.py
+++ b/pcs/lib/commands/stonith.py
@@ -1,3 +1,7 @@
+from typing import (
+ Optional,
+)
+
from pcs.lib import reports
from pcs.lib.cib import resource
from pcs.lib.cib.resource.common import are_meta_disabled
@@ -6,13 +10,14 @@ from pcs.lib.commands.resource import (
_ensure_disabled_after_wait,
resource_environment
)
+from pcs.lib.env import LibraryEnvironment
from pcs.lib.errors import LibraryError
from pcs.lib.pacemaker.live import (
FenceHistoryCommandErrorException,
fence_history_cleanup,
fence_history_text,
fence_history_update,
- is_fence_history_supported,
+ is_fence_history_supported_management,
)
from pcs.lib.pacemaker.values import validate_id
from pcs.lib.resource_agent import find_valid_stonith_agent_by_name as get_agent
@@ -162,51 +167,54 @@ def create_in_group(
put_after_adjacent,
)
-def history_get_text(env, node=None):
+def history_get_text(env: LibraryEnvironment, node: Optional[str] = None):
"""
Get full fencing history in plain text
- LibraryEnvironment env
- string node -- get history for the specified node or all nodes if None
+ env
+ node -- get history for the specified node or all nodes if None
"""
- if not is_fence_history_supported():
+ runner = env.cmd_runner()
+ if not is_fence_history_supported_management(runner):
raise LibraryError(reports.fence_history_not_supported())
try:
- return fence_history_text(env.cmd_runner(), node)
+ return fence_history_text(runner, node)
except FenceHistoryCommandErrorException as e:
raise LibraryError(
reports.fence_history_command_error(str(e), "show")
)
-def history_cleanup(env, node=None):
+def history_cleanup(env: LibraryEnvironment, node: Optional[str] = None):
"""
Clear fencing history
- LibraryEnvironment env
- string node -- clear history for the specified node or all nodes if None
+ env
+ node -- clear history for the specified node or all nodes if None
"""
- if not is_fence_history_supported():
+ runner = env.cmd_runner()
+ if not is_fence_history_supported_management(runner):
raise LibraryError(reports.fence_history_not_supported())
try:
- return fence_history_cleanup(env.cmd_runner(), node)
+ return fence_history_cleanup(runner, node)
except FenceHistoryCommandErrorException as e:
raise LibraryError(
reports.fence_history_command_error(str(e), "cleanup")
)
-def history_update(env):
+def history_update(env: LibraryEnvironment):
"""
Update fencing history in a cluster (sync with other nodes)
- LibraryEnvironment env
+ env
"""
- if not is_fence_history_supported():
+ runner = env.cmd_runner()
+ if not is_fence_history_supported_management(runner):
raise LibraryError(reports.fence_history_not_supported())
try:
- return fence_history_update(env.cmd_runner())
+ return fence_history_update(runner)
except FenceHistoryCommandErrorException as e:
raise LibraryError(
reports.fence_history_command_error(str(e), "update")
diff --git a/pcs/lib/pacemaker/live.py b/pcs/lib/pacemaker/live.py
index 233f2e2d..d6741441 100644
--- a/pcs/lib/pacemaker/live.py
+++ b/pcs/lib/pacemaker/live.py
@@ -1,6 +1,7 @@
import os.path
import re
from typing import (
+ Iterable,
List,
Tuple,
)
@@ -56,7 +57,7 @@ def get_cluster_status_text(
cmd.extend(["--show-detail", "--show-node-attributes", "--failcounts"])
# by default, pending and failed actions are displayed
# with verbose==True, we display the whole history
- if is_fence_history_supported():
+ if is_fence_history_supported_status(runner):
cmd.append("--fence-history=3")
stdout, stderr, retval = runner.run(cmd)
@@ -523,25 +524,15 @@ def _resource_move_ban_clear(
### fence history
-def is_fence_history_supported():
- try:
- crm_mon_rng = xml_fromstring(open(settings.crm_mon_schema, "r").read())
- # Namespaces must be provided otherwise xpath won't match anything.
- # 'None' namespace is not supported, so we rename it.
- namespaces_map = {
- "ns": crm_mon_rng.nsmap.pop(None)
- }
- history_elements = crm_mon_rng.xpath(
- ".//ns:element[@name='fence_history']",
- namespaces=namespaces_map
- )
- if history_elements:
- return True
- except (EnvironmentError, etree.XMLSyntaxError):
- # if we cannot tell for sure fence_history is supported, we will
- # continue as if it was not supported
- pass
- return False
+def is_fence_history_supported_status(runner: CommandRunner) -> bool:
+ return _is_in_pcmk_tool_help(
+ runner, "crm_mon", ["--fence-history"]
+ )
+
+def is_fence_history_supported_management(runner: CommandRunner) -> bool:
+ return _is_in_pcmk_tool_help(
+ runner, "stonith_admin", ["--history", "--broadcast", "--cleanup"]
+ )
def fence_history_cleanup(runner, node=None):
return _run_fence_history_command(runner, "--cleanup", node)
@@ -583,3 +574,17 @@ def __is_in_crm_resource_help(runner, text):
)
# help goes to stderr but we check stdout as well if that gets changed
return text in stderr or text in stdout
+
+def _is_in_pcmk_tool_help(
+ runner: CommandRunner, tool: str, text_list: Iterable[str]
+) -> bool:
+ stdout, stderr, dummy_retval = runner.run(
+ [__exec(tool), "--help-all"]
+ )
+ # Help goes to stderr but we check stdout as well if that gets changed. Use
+ # generators in all to return early.
+ return (
+ all(text in stderr for text in text_list)
+ or
+ all(text in stdout for text in text_list)
+ )
diff --git a/pcs_test/resources/crm_mon.rng.with_fence_history.xml b/pcs_test/resources/crm_mon.rng.with_fence_history.xml
deleted file mode 100644
index 45b380bd..00000000
--- a/pcs_test/resources/crm_mon.rng.with_fence_history.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
- <start>
- <ref name="element-crm_mon"/>
- </start>
- <define name="element-crm_mon">
- <element name="crm_mon">
- <optional>
- <element name="fence_history"/>
- </optional>
- </element>
- </define>
-</grammar>
diff --git a/pcs_test/resources/crm_mon.rng.without_fence_history.xml b/pcs_test/resources/crm_mon.rng.without_fence_history.xml
deleted file mode 100644
index f7efe52c..00000000
--- a/pcs_test/resources/crm_mon.rng.without_fence_history.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
- <start>
- <ref name="element-crm_mon"/>
- </start>
- <define name="element-crm_mon">
- <element name="crm_mon"/>
- </define>
-</grammar>
diff --git a/pcs_test/tier0/lib/commands/test_status.py b/pcs_test/tier0/lib/commands/test_status.py
index 517aa908..06878668 100644
--- a/pcs_test/tier0/lib/commands/test_status.py
+++ b/pcs_test/tier0/lib/commands/test_status.py
@@ -1,15 +1,12 @@
from textwrap import dedent
-from unittest import mock, TestCase
+from unittest import TestCase
-from pcs import settings
from pcs.common import file_type_codes, report_codes
from pcs.lib.commands import status
from pcs_test.tools import fixture
from pcs_test.tools.command_env import get_env_tools
from pcs_test.tools.misc import read_test_resource as rc_read
-crm_mon_rng_with_history = rc_read("crm_mon.rng.with_fence_history.xml")
-crm_mon_rng_without_history = rc_read("crm_mon.rng.without_fence_history.xml")
class FullClusterStatusPlaintext(TestCase):
def setUp(self):
@@ -212,11 +209,7 @@ class FullClusterStatusPlaintext(TestCase):
def test_success_live_verbose(self):
(self.config
.env.set_known_nodes(self.node_name_list)
- .fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_without_history)(),
- name="fs.open.crm_mon_rng"
- )
+ .runner.pcmk.can_fence_history_status(stderr="not supported")
.runner.pcmk.load_state_plaintext(
verbose=True,
stdout="crm_mon cluster status",
@@ -288,11 +281,7 @@ class FullClusterStatusPlaintext(TestCase):
(self.config
.env.set_corosync_conf_data(rc_read("corosync.conf"))
.env.set_cib_data("<cib/>")
- .fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_without_history)(),
- name="fs.open.crm_mon_rng"
- )
+ .runner.pcmk.can_fence_history_status(stderr="not supported")
.runner.pcmk.load_state_plaintext(
verbose=True, stdout="crm_mon cluster status",
)
@@ -320,11 +309,7 @@ class FullClusterStatusPlaintext(TestCase):
def test_success_verbose_inactive_and_fence_history(self):
(self.config
.env.set_known_nodes(self.node_name_list)
- .fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_with_history)(),
- name="fs.open.crm_mon_rng"
- )
+ .runner.pcmk.can_fence_history_status()
.runner.pcmk.load_state_plaintext(
verbose=True,
inactive=False,
@@ -375,11 +360,7 @@ class FullClusterStatusPlaintext(TestCase):
def _assert_success_with_ticket_status_failure(self, stderr="", msg=""):
(self.config
.env.set_known_nodes(self.node_name_list)
- .fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_without_history)(),
- name="fs.open.crm_mon_rng"
- )
+ .runner.pcmk.can_fence_history_status(stderr="not supported")
.runner.pcmk.load_state_plaintext(
verbose=True,
stdout="crm_mon cluster status",
@@ -553,11 +534,7 @@ class FullClusterStatusPlaintext(TestCase):
(self.config
.env.set_known_nodes(self.node_name_list[1:])
- .fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_without_history)(),
- name="fs.open.crm_mon_rng"
- )
+ .runner.pcmk.can_fence_history_status(stderr="not supported")
.runner.pcmk.load_state_plaintext(
verbose=True,
stdout="crm_mon cluster status",
diff --git a/pcs_test/tier0/lib/commands/test_stonith_history.py b/pcs_test/tier0/lib/commands/test_stonith_history.py
index e1bd35cb..cfdef13c 100644
--- a/pcs_test/tier0/lib/commands/test_stonith_history.py
+++ b/pcs_test/tier0/lib/commands/test_stonith_history.py
@@ -1,25 +1,16 @@
-from unittest import mock, TestCase
+from unittest import TestCase
from pcs_test.tools import fixture
from pcs_test.tools.command_env import get_env_tools
-from pcs_test.tools.misc import read_test_resource as rc_read
-from pcs import settings
from pcs.common import report_codes
from pcs.lib.commands import stonith
-crm_mon_rng_with_history = rc_read("crm_mon.rng.with_fence_history.xml")
-crm_mon_rng_without_history = rc_read("crm_mon.rng.without_fence_history.xml")
-
class HistoryGetText(TestCase):
def setUp(self):
self.env_assist, self.config = get_env_tools(test_case=self)
- self.config.fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_with_history)(),
- name="fs.open.crm_mon_rng"
- )
+ self.config.runner.pcmk.can_fence_history_manage()
def test_success_all_nodes(self):
history = (
@@ -68,11 +59,10 @@ class HistoryGetText(TestCase):
)
def test_history_not_supported(self):
- self.config.fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_without_history)(),
- name="fs.open.crm_mon_rng",
- instead="fs.open.crm_mon_rng"
+ self.config.runner.pcmk.can_fence_history_manage(
+ stderr="not supported",
+ name="runner.pcmk.can_fence_history_manage",
+ instead="runner.pcmk.can_fence_history_manage",
)
self.env_assist.assert_raise_library_error(
lambda: stonith.history_get_text(self.env_assist.get_env()),
@@ -88,11 +78,7 @@ class HistoryGetText(TestCase):
class HistoryCleanup(TestCase):
def setUp(self):
self.env_assist, self.config = get_env_tools(test_case=self)
- self.config.fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_with_history)(),
- name="fs.open.crm_mon_rng"
- )
+ self.config.runner.pcmk.can_fence_history_manage()
def test_success_all_nodes(self):
msg = "cleaning up fencing-history for node *\n"
@@ -129,11 +115,10 @@ class HistoryCleanup(TestCase):
)
def test_history_not_supported(self):
- self.config.fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_without_history)(),
- name="fs.open.crm_mon_rng",
- instead="fs.open.crm_mon_rng"
+ self.config.runner.pcmk.can_fence_history_manage(
+ stderr="not supported",
+ name="runner.pcmk.can_fence_history_manage",
+ instead="runner.pcmk.can_fence_history_manage",
)
self.env_assist.assert_raise_library_error(
lambda: stonith.history_cleanup(self.env_assist.get_env()),
@@ -149,11 +134,7 @@ class HistoryCleanup(TestCase):
class HistoryUpdate(TestCase):
def setUp(self):
self.env_assist, self.config = get_env_tools(test_case=self)
- self.config.fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_with_history)(),
- name="fs.open.crm_mon_rng"
- )
+ self.config.runner.pcmk.can_fence_history_manage()
def test_success_all_nodes(self):
msg = "gather fencing-history from all nodes\n"
@@ -182,11 +163,10 @@ class HistoryUpdate(TestCase):
)
def test_history_not_supported(self):
- self.config.fs.open(
- settings.crm_mon_schema,
- mock.mock_open(read_data=crm_mon_rng_without_history)(),
- name="fs.open.crm_mon_rng",
- instead="fs.open.crm_mon_rng"
+ self.config.runner.pcmk.can_fence_history_manage(
+ stderr="not supported",
+ name="runner.pcmk.can_fence_history_manage",
+ instead="runner.pcmk.can_fence_history_manage",
)
self.env_assist.assert_raise_library_error(
lambda: stonith.history_update(self.env_assist.get_env()),
diff --git a/pcs_test/tier0/lib/pacemaker/test_live.py b/pcs_test/tier0/lib/pacemaker/test_live.py
index 1ea5454e..d69d8b34 100644
--- a/pcs_test/tier0/lib/pacemaker/test_live.py
+++ b/pcs_test/tier0/lib/pacemaker/test_live.py
@@ -79,7 +79,7 @@ class GetClusterStatusXmlTest(LibraryPacemakerTest):
class GetClusterStatusText(TestCase):
def setUp(self):
self.mock_fencehistory_supported = mock.patch(
- "pcs.lib.pacemaker.live.is_fence_history_supported",
+ "pcs.lib.pacemaker.live.is_fence_history_supported_status",
return_value=True
)
self.mock_fencehistory_supported.start()
@@ -125,7 +125,7 @@ class GetClusterStatusText(TestCase):
def test_success_no_fence_history(self):
self.mock_fencehistory_supported.stop()
self.mock_fencehistory_supported = mock.patch(
- "pcs.lib.pacemaker.live.is_fence_history_supported",
+ "pcs.lib.pacemaker.live.is_fence_history_supported_status",
return_value=False
)
self.mock_fencehistory_supported.start()
@@ -1399,3 +1399,30 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
mock_runner.run.assert_called_once_with(
[self.path("crm_resource"), "--wait"]
)
+
+
+class IsInPcmkToolHelp(TestCase):
+ # pylint: disable=protected-access
+ def test_all_in_stderr(self):
+ mock_runner = get_runner("", "ABCDE", 0)
+ self.assertTrue(
+ lib._is_in_pcmk_tool_help(mock_runner, "", ["A", "C", "E"])
+ )
+
+ def test_all_in_stdout(self):
+ mock_runner = get_runner("ABCDE", "", 0)
+ self.assertTrue(
+ lib._is_in_pcmk_tool_help(mock_runner, "", ["A", "C", "E"])
+ )
+
+ def test_some_in_stderr_all_in_stdout(self):
+ mock_runner = get_runner("ABCDE", "ABC", 0)
+ self.assertTrue(
+ lib._is_in_pcmk_tool_help(mock_runner, "", ["A", "C", "E"])
+ )
+
+ def test_some_in_stderr_some_in_stdout(self):
+ mock_runner = get_runner("CDE", "ABC", 0)
+ self.assertFalse(
+ lib._is_in_pcmk_tool_help(mock_runner, "", ["A", "C", "E"])
+ )
diff --git a/pcs_test/tools/command_env/config_runner_pcmk.py b/pcs_test/tools/command_env/config_runner_pcmk.py
index 5bb9755b..0580e8d6 100644
--- a/pcs_test/tools/command_env/config_runner_pcmk.py
+++ b/pcs_test/tools/command_env/config_runner_pcmk.py
@@ -70,11 +70,52 @@ def _fixture_state_node_xml(
class PcmkShortcuts():
+ #pylint: disable=too-many-public-methods
def __init__(self, calls):
self.__calls = calls
self.default_wait_timeout = DEFAULT_WAIT_TIMEOUT
self.default_wait_error_returncode = WAIT_TIMEOUT_EXPIRED_RETURNCODE
+ def can_fence_history_manage(
+ self,
+ name="runner.pcmk.can_fence_history_manage",
+ stderr="--history --cleanup --broadcast",
+ instead=None,
+ ):
+ """
+ Create a call to check if fence_history is supported by stonith_admin
+
+ string name -- key of the call
+ string stderr -- stonith_admin help text
+ string instead -- key of call instead of which this new call is to be
+ placed
+ """
+ self.__calls.place(
+ name,
+ RunnerCall("stonith_admin --help-all", stderr=stderr),
+ instead=instead,
+ )
+
+ def can_fence_history_status(
+ self,
+ name="runner.pcmk.can_fence_history_status",
+ stderr="--fence-history",
+ instead=None,
+ ):
+ """
+ Create a call to check if fence_history is supported by crm_mon
+
+ string name -- key of the call
+ string stderr -- crm_mon help text
+ string instead -- key of call instead of which this new call is to be
+ placed
+ """
+ self.__calls.place(
+ name,
+ RunnerCall("crm_mon --help-all", stderr=stderr),
+ instead=instead,
+ )
+
def fence_history_get(
self, name="runner.pcmk.fence_history_get", node=None, stdout="",
stderr="", returncode=0
diff --git a/pcs_test/tools/command_env/mock_runner.py b/pcs_test/tools/command_env/mock_runner.py
index 2fe43137..8b9cb771 100644
--- a/pcs_test/tools/command_env/mock_runner.py
+++ b/pcs_test/tools/command_env/mock_runner.py
@@ -61,6 +61,7 @@ COMMAND_COMPLETIONS = {
"crm_ticket": path.join(settings.pacemaker_binaries, "crm_ticket"),
"crm_verify": path.join(settings.pacemaker_binaries, "crm_verify"),
"sbd": settings.sbd_binary,
+ "stonith_admin": path.join(settings.pacemaker_binaries, "stonith_admin"),
}
def complete_command(command):
--
2.21.1