|
|
15f218 |
From bc599f0f30c039a72540002d9a41a93c15626837 Mon Sep 17 00:00:00 2001
|
|
|
15f218 |
From: Ivan Devat <idevat@redhat.com>
|
|
|
15f218 |
Date: Wed, 14 Sep 2016 09:04:57 +0200
|
|
|
15f218 |
Subject: [PATCH] squash bz1158805 Add support for qdevice/qnetd pro
|
|
|
15f218 |
|
|
|
15f218 |
9c7f37ef37bb lib: do not merge external processes' stdout and stderr
|
|
|
15f218 |
|
|
|
15f218 |
db3ada5e27aa warn on stopping/destroying currently used qdevice
|
|
|
15f218 |
|
|
|
15f218 |
18df73397b54 handle SBD when removing qdevice from a cluster
|
|
|
15f218 |
|
|
|
15f218 |
766f86954b46 Allow to re-run "cluster node add" if failed due to qdevice
|
|
|
15f218 |
---
|
|
|
15f218 |
pcs/cluster.py | 44 ++--
|
|
|
15f218 |
pcs/common/report_codes.py | 6 +-
|
|
|
15f218 |
pcs/common/tools.py | 3 +
|
|
|
15f218 |
pcs/lib/booth/status.py | 27 +-
|
|
|
15f218 |
pcs/lib/booth/test/test_status.py | 26 +-
|
|
|
15f218 |
pcs/lib/cib/tools.py | 7 +-
|
|
|
15f218 |
pcs/lib/commands/booth.py | 5 +-
|
|
|
15f218 |
pcs/lib/commands/qdevice.py | 35 ++-
|
|
|
15f218 |
pcs/lib/commands/quorum.py | 12 +-
|
|
|
15f218 |
pcs/lib/commands/test/test_booth.py | 4 +-
|
|
|
15f218 |
pcs/lib/corosync/live.py | 26 +-
|
|
|
15f218 |
pcs/lib/corosync/qdevice_client.py | 9 +-
|
|
|
15f218 |
pcs/lib/corosync/qdevice_net.py | 77 ++++--
|
|
|
15f218 |
pcs/lib/external.py | 105 +++++---
|
|
|
15f218 |
pcs/lib/pacemaker.py | 71 ++++--
|
|
|
15f218 |
pcs/lib/reports.py | 97 ++++----
|
|
|
15f218 |
pcs/lib/resource_agent.py | 31 ++-
|
|
|
15f218 |
pcs/lib/sbd.py | 4 +-
|
|
|
15f218 |
pcs/qdevice.py | 4 +-
|
|
|
15f218 |
pcs/test/resources/corosync-qdevice.conf | 34 +++
|
|
|
15f218 |
pcs/test/test_common_tools.py | 32 +++
|
|
|
15f218 |
pcs/test/test_lib_cib_tools.py | 10 +-
|
|
|
15f218 |
pcs/test/test_lib_commands_qdevice.py | 155 +++++++++++-
|
|
|
15f218 |
pcs/test/test_lib_commands_quorum.py | 105 +++++++-
|
|
|
15f218 |
pcs/test/test_lib_corosync_live.py | 30 ++-
|
|
|
15f218 |
pcs/test/test_lib_corosync_qdevice_client.py | 8 +-
|
|
|
15f218 |
pcs/test/test_lib_corosync_qdevice_net.py | 110 +++++---
|
|
|
15f218 |
pcs/test/test_lib_external.py | 167 +++++++------
|
|
|
15f218 |
pcs/test/test_lib_pacemaker.py | 359 ++++++++++++++++++---------
|
|
|
15f218 |
pcs/test/test_lib_resource_agent.py | 39 ++-
|
|
|
15f218 |
pcs/test/test_lib_sbd.py | 12 +-
|
|
|
15f218 |
31 files changed, 1166 insertions(+), 488 deletions(-)
|
|
|
15f218 |
create mode 100644 pcs/test/resources/corosync-qdevice.conf
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/pcs/cluster.py b/pcs/cluster.py
|
|
|
15f218 |
index 577e08e..e5ad1ec 100644
|
|
|
15f218 |
--- a/pcs/cluster.py
|
|
|
15f218 |
+++ b/pcs/cluster.py
|
|
|
15f218 |
@@ -1414,7 +1414,6 @@ def cluster_node(argv):
|
|
|
15f218 |
"cluster is not configured for RRP, "
|
|
|
15f218 |
"you must not specify ring 1 address for the node"
|
|
|
15f218 |
)
|
|
|
15f218 |
- corosync_conf = None
|
|
|
15f218 |
(canAdd, error) = utils.canAddNodeToCluster(node0)
|
|
|
15f218 |
if not canAdd:
|
|
|
15f218 |
utils.err("Unable to add '%s' to cluster: %s" % (node0, error))
|
|
|
15f218 |
@@ -1422,7 +1421,29 @@ def cluster_node(argv):
|
|
|
15f218 |
report_processor = lib_env.report_processor
|
|
|
15f218 |
node_communicator = lib_env.node_communicator()
|
|
|
15f218 |
node_addr = NodeAddresses(node0, node1)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ # First set up everything else than corosync. Once the new node is
|
|
|
15f218 |
+ # present in corosync.conf / cluster.conf, it's considered part of a
|
|
|
15f218 |
+ # cluster and the node add command cannot be run again. So we need to
|
|
|
15f218 |
+ # minimize the amout of actions (and therefore possible failures) after
|
|
|
15f218 |
+ # adding the node to corosync.
|
|
|
15f218 |
try:
|
|
|
15f218 |
+ # qdevice setup
|
|
|
15f218 |
+ if not utils.is_rhel6():
|
|
|
15f218 |
+ conf_facade = corosync_conf_facade.from_string(
|
|
|
15f218 |
+ utils.getCorosyncConf()
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ qdevice_model, qdevice_model_options, _ = conf_facade.get_quorum_device_settings()
|
|
|
15f218 |
+ if qdevice_model == "net":
|
|
|
15f218 |
+ _add_device_model_net(
|
|
|
15f218 |
+ lib_env,
|
|
|
15f218 |
+ qdevice_model_options["host"],
|
|
|
15f218 |
+ conf_facade.get_cluster_name(),
|
|
|
15f218 |
+ [node_addr],
|
|
|
15f218 |
+ skip_offline_nodes=False
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ # sbd setup
|
|
|
15f218 |
if lib_sbd.is_sbd_enabled(utils.cmd_runner()):
|
|
|
15f218 |
if "--watchdog" not in utils.pcs_options:
|
|
|
15f218 |
watchdog = settings.sbd_watchdog_default
|
|
|
15f218 |
@@ -1463,6 +1484,7 @@ def cluster_node(argv):
|
|
|
15f218 |
report_processor, node_communicator, node_addr
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
+ # booth setup
|
|
|
15f218 |
booth_sync.send_all_config_to_node(
|
|
|
15f218 |
node_communicator,
|
|
|
15f218 |
report_processor,
|
|
|
15f218 |
@@ -1477,6 +1499,8 @@ def cluster_node(argv):
|
|
|
15f218 |
[node_communicator_exception_to_report_item(e)]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
+ # Now add the new node to corosync.conf / cluster.conf
|
|
|
15f218 |
+ corosync_conf = None
|
|
|
15f218 |
for my_node in utils.getNodesFromCorosyncConf():
|
|
|
15f218 |
retval, output = utils.addLocalNode(my_node, node0, node1)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
@@ -1512,24 +1536,6 @@ def cluster_node(argv):
|
|
|
15f218 |
except:
|
|
|
15f218 |
utils.err('Unable to communicate with pcsd')
|
|
|
15f218 |
|
|
|
15f218 |
- # set qdevice-net certificates if needed
|
|
|
15f218 |
- if not utils.is_rhel6():
|
|
|
15f218 |
- try:
|
|
|
15f218 |
- conf_facade = corosync_conf_facade.from_string(
|
|
|
15f218 |
- corosync_conf
|
|
|
15f218 |
- )
|
|
|
15f218 |
- qdevice_model, qdevice_model_options, _ = conf_facade.get_quorum_device_settings()
|
|
|
15f218 |
- if qdevice_model == "net":
|
|
|
15f218 |
- _add_device_model_net(
|
|
|
15f218 |
- lib_env,
|
|
|
15f218 |
- qdevice_model_options["host"],
|
|
|
15f218 |
- conf_facade.get_cluster_name(),
|
|
|
15f218 |
- [node_addr],
|
|
|
15f218 |
- skip_offline_nodes=False
|
|
|
15f218 |
- )
|
|
|
15f218 |
- except LibraryError as e:
|
|
|
15f218 |
- process_library_reports(e.args)
|
|
|
15f218 |
-
|
|
|
15f218 |
print("Setting up corosync...")
|
|
|
15f218 |
utils.setCorosyncConfig(node0, corosync_conf)
|
|
|
15f218 |
if "--enable" in utils.pcs_options:
|
|
|
15f218 |
diff --git a/pcs/common/report_codes.py b/pcs/common/report_codes.py
|
|
|
15f218 |
index e6a86ec..23e931f 100644
|
|
|
15f218 |
--- a/pcs/common/report_codes.py
|
|
|
15f218 |
+++ b/pcs/common/report_codes.py
|
|
|
15f218 |
@@ -8,17 +8,18 @@ from __future__ import (
|
|
|
15f218 |
# force cathegories
|
|
|
15f218 |
FORCE_ACTIVE_RRP = "ACTIVE_RRP"
|
|
|
15f218 |
FORCE_ALERT_RECIPIENT_VALUE_NOT_UNIQUE = "FORCE_ALERT_RECIPIENT_VALUE_NOT_UNIQUE"
|
|
|
15f218 |
-FORCE_BOOTH_REMOVE_FROM_CIB = "FORCE_BOOTH_REMOVE_FROM_CIB"
|
|
|
15f218 |
FORCE_BOOTH_DESTROY = "FORCE_BOOTH_DESTROY"
|
|
|
15f218 |
+FORCE_BOOTH_REMOVE_FROM_CIB = "FORCE_BOOTH_REMOVE_FROM_CIB"
|
|
|
15f218 |
FORCE_CONSTRAINT_DUPLICATE = "CONSTRAINT_DUPLICATE"
|
|
|
15f218 |
FORCE_CONSTRAINT_MULTIINSTANCE_RESOURCE = "CONSTRAINT_MULTIINSTANCE_RESOURCE"
|
|
|
15f218 |
FORCE_FILE_OVERWRITE = "FORCE_FILE_OVERWRITE"
|
|
|
15f218 |
FORCE_LOAD_THRESHOLD = "LOAD_THRESHOLD"
|
|
|
15f218 |
+FORCE_METADATA_ISSUE = "METADATA_ISSUE"
|
|
|
15f218 |
FORCE_OPTIONS = "OPTIONS"
|
|
|
15f218 |
FORCE_QDEVICE_MODEL = "QDEVICE_MODEL"
|
|
|
15f218 |
+FORCE_QDEVICE_USED = "QDEVICE_USED"
|
|
|
15f218 |
FORCE_UNKNOWN_AGENT = "UNKNOWN_AGENT"
|
|
|
15f218 |
FORCE_UNSUPPORTED_AGENT = "UNSUPPORTED_AGENT"
|
|
|
15f218 |
-FORCE_METADATA_ISSUE = "METADATA_ISSUE"
|
|
|
15f218 |
SKIP_OFFLINE_NODES = "SKIP_OFFLINE_NODES"
|
|
|
15f218 |
SKIP_UNREADABLE_CONFIG = "SKIP_UNREADABLE_CONFIG"
|
|
|
15f218 |
|
|
|
15f218 |
@@ -135,6 +136,7 @@ QDEVICE_NOT_DEFINED = "QDEVICE_NOT_DEFINED"
|
|
|
15f218 |
QDEVICE_NOT_INITIALIZED = "QDEVICE_NOT_INITIALIZED"
|
|
|
15f218 |
QDEVICE_CLIENT_RELOAD_STARTED = "QDEVICE_CLIENT_RELOAD_STARTED"
|
|
|
15f218 |
QDEVICE_REMOVE_OR_CLUSTER_STOP_NEEDED = "QDEVICE_REMOVE_OR_CLUSTER_STOP_NEEDED"
|
|
|
15f218 |
+QDEVICE_USED_BY_CLUSTERS = "QDEVICE_USED_BY_CLUSTERS"
|
|
|
15f218 |
REQUIRED_OPTION_IS_MISSING = "REQUIRED_OPTION_IS_MISSING"
|
|
|
15f218 |
RESOURCE_CLEANUP_ERROR = "RESOURCE_CLEANUP_ERROR"
|
|
|
15f218 |
RESOURCE_CLEANUP_TOO_TIME_CONSUMING = 'RESOURCE_CLEANUP_TOO_TIME_CONSUMING'
|
|
|
15f218 |
diff --git a/pcs/common/tools.py b/pcs/common/tools.py
|
|
|
15f218 |
index 275f6b9..01194a5 100644
|
|
|
15f218 |
--- a/pcs/common/tools.py
|
|
|
15f218 |
+++ b/pcs/common/tools.py
|
|
|
15f218 |
@@ -38,3 +38,6 @@ def format_environment_error(e):
|
|
|
15f218 |
if e.filename:
|
|
|
15f218 |
return "{0}: '{1}'".format(e.strerror, e.filename)
|
|
|
15f218 |
return e.strerror
|
|
|
15f218 |
+
|
|
|
15f218 |
+def join_multilines(strings):
|
|
|
15f218 |
+ return "\n".join([a.strip() for a in strings if a.strip()])
|
|
|
15f218 |
diff --git a/pcs/lib/booth/status.py b/pcs/lib/booth/status.py
|
|
|
15f218 |
index 4b93161..87cdc05 100644
|
|
|
15f218 |
--- a/pcs/lib/booth/status.py
|
|
|
15f218 |
+++ b/pcs/lib/booth/status.py
|
|
|
15f218 |
@@ -6,6 +6,7 @@ from __future__ import (
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
from pcs import settings
|
|
|
15f218 |
+from pcs.common.tools import join_multilines
|
|
|
15f218 |
from pcs.lib.booth import reports
|
|
|
15f218 |
from pcs.lib.errors import LibraryError
|
|
|
15f218 |
|
|
|
15f218 |
@@ -14,28 +15,36 @@ def get_daemon_status(runner, name=None):
|
|
|
15f218 |
cmd = [settings.booth_binary, "status"]
|
|
|
15f218 |
if name:
|
|
|
15f218 |
cmd += ["-c", name]
|
|
|
15f218 |
- output, return_value = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, return_value = runner.run(cmd)
|
|
|
15f218 |
# 7 means that there is no booth instance running
|
|
|
15f218 |
if return_value not in [0, 7]:
|
|
|
15f218 |
- raise LibraryError(reports.booth_daemon_status_error(output))
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ raise LibraryError(
|
|
|
15f218 |
+ reports.booth_daemon_status_error(join_multilines([stderr, stdout]))
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def get_tickets_status(runner, name=None):
|
|
|
15f218 |
cmd = [settings.booth_binary, "list"]
|
|
|
15f218 |
if name:
|
|
|
15f218 |
cmd += ["-c", name]
|
|
|
15f218 |
- output, return_value = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, return_value = runner.run(cmd)
|
|
|
15f218 |
if return_value != 0:
|
|
|
15f218 |
- raise LibraryError(reports.booth_tickets_status_error(output))
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ raise LibraryError(
|
|
|
15f218 |
+ reports.booth_tickets_status_error(
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def get_peers_status(runner, name=None):
|
|
|
15f218 |
cmd = [settings.booth_binary, "peers"]
|
|
|
15f218 |
if name:
|
|
|
15f218 |
cmd += ["-c", name]
|
|
|
15f218 |
- output, return_value = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, return_value = runner.run(cmd)
|
|
|
15f218 |
if return_value != 0:
|
|
|
15f218 |
- raise LibraryError(reports.booth_peers_status_error(output))
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ raise LibraryError(
|
|
|
15f218 |
+ reports.booth_peers_status_error(join_multilines([stderr, stdout]))
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
diff --git a/pcs/lib/booth/test/test_status.py b/pcs/lib/booth/test/test_status.py
|
|
|
15f218 |
index d47ffca..dfb7354 100644
|
|
|
15f218 |
--- a/pcs/lib/booth/test/test_status.py
|
|
|
15f218 |
+++ b/pcs/lib/booth/test/test_status.py
|
|
|
15f218 |
@@ -30,34 +30,34 @@ class GetDaemonStatusTest(TestCase):
|
|
|
15f218 |
self.mock_run = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
|
|
|
15f218 |
def test_no_name(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("output", 0)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("output", "", 0)
|
|
|
15f218 |
self.assertEqual("output", lib.get_daemon_status(self.mock_run))
|
|
|
15f218 |
self.mock_run.run.assert_called_once_with(
|
|
|
15f218 |
[settings.booth_binary, "status"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_with_name(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("output", 0)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("output", "", 0)
|
|
|
15f218 |
self.assertEqual("output", lib.get_daemon_status(self.mock_run, "name"))
|
|
|
15f218 |
self.mock_run.run.assert_called_once_with(
|
|
|
15f218 |
[settings.booth_binary, "status", "-c", "name"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_daemon_not_running(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("", 7)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("", "error", 7)
|
|
|
15f218 |
self.assertEqual("", lib.get_daemon_status(self.mock_run))
|
|
|
15f218 |
self.mock_run.run.assert_called_once_with(
|
|
|
15f218 |
[settings.booth_binary, "status"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_failure(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("out", 1)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("out", "error", 1)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_daemon_status(self.mock_run),
|
|
|
15f218 |
(
|
|
|
15f218 |
Severities.ERROR,
|
|
|
15f218 |
report_codes.BOOTH_DAEMON_STATUS_ERROR,
|
|
|
15f218 |
- {"reason": "out"}
|
|
|
15f218 |
+ {"reason": "error\nout"}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
self.mock_run.run.assert_called_once_with(
|
|
|
15f218 |
@@ -70,14 +70,14 @@ class GetTicketsStatusTest(TestCase):
|
|
|
15f218 |
self.mock_run = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
|
|
|
15f218 |
def test_no_name(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("output", 0)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("output", "", 0)
|
|
|
15f218 |
self.assertEqual("output", lib.get_tickets_status(self.mock_run))
|
|
|
15f218 |
self.mock_run.run.assert_called_once_with(
|
|
|
15f218 |
[settings.booth_binary, "list"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_with_name(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("output", 0)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("output", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"output", lib.get_tickets_status(self.mock_run, "name")
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -86,14 +86,14 @@ class GetTicketsStatusTest(TestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_failure(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("out", 1)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("out", "error", 1)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_tickets_status(self.mock_run),
|
|
|
15f218 |
(
|
|
|
15f218 |
Severities.ERROR,
|
|
|
15f218 |
report_codes.BOOTH_TICKET_STATUS_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": "out"
|
|
|
15f218 |
+ "reason": "error\nout"
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -107,28 +107,28 @@ class GetPeersStatusTest(TestCase):
|
|
|
15f218 |
self.mock_run = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
|
|
|
15f218 |
def test_no_name(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("output", 0)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("output", "", 0)
|
|
|
15f218 |
self.assertEqual("output", lib.get_peers_status(self.mock_run))
|
|
|
15f218 |
self.mock_run.run.assert_called_once_with(
|
|
|
15f218 |
[settings.booth_binary, "peers"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_with_name(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("output", 0)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("output", "", 0)
|
|
|
15f218 |
self.assertEqual("output", lib.get_peers_status(self.mock_run, "name"))
|
|
|
15f218 |
self.mock_run.run.assert_called_once_with(
|
|
|
15f218 |
[settings.booth_binary, "peers", "-c", "name"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_failure(self):
|
|
|
15f218 |
- self.mock_run.run.return_value = ("out", 1)
|
|
|
15f218 |
+ self.mock_run.run.return_value = ("out", "error", 1)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_peers_status(self.mock_run),
|
|
|
15f218 |
(
|
|
|
15f218 |
Severities.ERROR,
|
|
|
15f218 |
report_codes.BOOTH_PEERS_STATUS_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": "out"
|
|
|
15f218 |
+ "reason": "error\nout"
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
diff --git a/pcs/lib/cib/tools.py b/pcs/lib/cib/tools.py
|
|
|
15f218 |
index 8141360..6285931 100644
|
|
|
15f218 |
--- a/pcs/lib/cib/tools.py
|
|
|
15f218 |
+++ b/pcs/lib/cib/tools.py
|
|
|
15f218 |
@@ -11,6 +11,7 @@ import tempfile
|
|
|
15f218 |
from lxml import etree
|
|
|
15f218 |
|
|
|
15f218 |
from pcs import settings
|
|
|
15f218 |
+from pcs.common.tools import join_multilines
|
|
|
15f218 |
from pcs.lib import reports
|
|
|
15f218 |
from pcs.lib.errors import LibraryError
|
|
|
15f218 |
from pcs.lib.pacemaker_values import validate_id
|
|
|
15f218 |
@@ -181,7 +182,7 @@ def upgrade_cib(cib, runner):
|
|
|
15f218 |
temp_file = tempfile.NamedTemporaryFile("w+", suffix=".pcs")
|
|
|
15f218 |
temp_file.write(etree.tostring(cib).decode())
|
|
|
15f218 |
temp_file.flush()
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(
|
|
|
15f218 |
[
|
|
|
15f218 |
os.path.join(settings.pacemaker_binaries, "cibadmin"),
|
|
|
15f218 |
"--upgrade",
|
|
|
15f218 |
@@ -192,7 +193,9 @@ def upgrade_cib(cib, runner):
|
|
|
15f218 |
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
temp_file.close()
|
|
|
15f218 |
- raise LibraryError(reports.cib_upgrade_failed(output))
|
|
|
15f218 |
+ raise LibraryError(
|
|
|
15f218 |
+ reports.cib_upgrade_failed(join_multilines([stderr, stdout]))
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
temp_file.seek(0)
|
|
|
15f218 |
return etree.fromstring(temp_file.read())
|
|
|
15f218 |
diff --git a/pcs/lib/commands/booth.py b/pcs/lib/commands/booth.py
|
|
|
15f218 |
index 7a3d348..bea966c 100644
|
|
|
15f218 |
--- a/pcs/lib/commands/booth.py
|
|
|
15f218 |
+++ b/pcs/lib/commands/booth.py
|
|
|
15f218 |
@@ -10,6 +10,7 @@ import os.path
|
|
|
15f218 |
from functools import partial
|
|
|
15f218 |
|
|
|
15f218 |
from pcs import settings
|
|
|
15f218 |
+from pcs.common.tools import join_multilines
|
|
|
15f218 |
from pcs.lib import external, reports
|
|
|
15f218 |
from pcs.lib.booth import (
|
|
|
15f218 |
config_exchange,
|
|
|
15f218 |
@@ -185,7 +186,7 @@ def ticket_operation(operation, env, name, ticket, site_ip):
|
|
|
15f218 |
)
|
|
|
15f218 |
site_ip = site_ip_list[0]
|
|
|
15f218 |
|
|
|
15f218 |
- command_output, return_code = env.cmd_runner().run([
|
|
|
15f218 |
+ stdout, stderr, return_code = env.cmd_runner().run([
|
|
|
15f218 |
settings.booth_binary, operation,
|
|
|
15f218 |
"-s", site_ip,
|
|
|
15f218 |
ticket
|
|
|
15f218 |
@@ -195,7 +196,7 @@ def ticket_operation(operation, env, name, ticket, site_ip):
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
booth_reports.booth_ticket_operation_failed(
|
|
|
15f218 |
operation,
|
|
|
15f218 |
- command_output,
|
|
|
15f218 |
+ join_multilines([stderr, stdout]),
|
|
|
15f218 |
site_ip,
|
|
|
15f218 |
ticket
|
|
|
15f218 |
)
|
|
|
15f218 |
diff --git a/pcs/lib/commands/qdevice.py b/pcs/lib/commands/qdevice.py
|
|
|
15f218 |
index 1d1d85f..ca0ae86 100644
|
|
|
15f218 |
--- a/pcs/lib/commands/qdevice.py
|
|
|
15f218 |
+++ b/pcs/lib/commands/qdevice.py
|
|
|
15f218 |
@@ -8,9 +8,10 @@ from __future__ import (
|
|
|
15f218 |
import base64
|
|
|
15f218 |
import binascii
|
|
|
15f218 |
|
|
|
15f218 |
+from pcs.common import report_codes
|
|
|
15f218 |
from pcs.lib import external, reports
|
|
|
15f218 |
from pcs.lib.corosync import qdevice_net
|
|
|
15f218 |
-from pcs.lib.errors import LibraryError
|
|
|
15f218 |
+from pcs.lib.errors import LibraryError, ReportItemSeverity
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def qdevice_setup(lib_env, model, enable, start):
|
|
|
15f218 |
@@ -31,13 +32,20 @@ def qdevice_setup(lib_env, model, enable, start):
|
|
|
15f218 |
if start:
|
|
|
15f218 |
_service_start(lib_env, qdevice_net.qdevice_start)
|
|
|
15f218 |
|
|
|
15f218 |
-def qdevice_destroy(lib_env, model):
|
|
|
15f218 |
+def qdevice_destroy(lib_env, model, proceed_if_used=False):
|
|
|
15f218 |
"""
|
|
|
15f218 |
Stop and disable qdevice on local host and remove its configuration
|
|
|
15f218 |
string model qdevice model to destroy
|
|
|
15f218 |
+ bool procced_if_used destroy qdevice even if it is used by clusters
|
|
|
15f218 |
"""
|
|
|
15f218 |
_ensure_not_cman(lib_env)
|
|
|
15f218 |
_check_model(model)
|
|
|
15f218 |
+ _check_qdevice_not_used(
|
|
|
15f218 |
+ lib_env.report_processor,
|
|
|
15f218 |
+ lib_env.cmd_runner(),
|
|
|
15f218 |
+ model,
|
|
|
15f218 |
+ proceed_if_used
|
|
|
15f218 |
+ )
|
|
|
15f218 |
_service_stop(lib_env, qdevice_net.qdevice_stop)
|
|
|
15f218 |
_service_disable(lib_env, qdevice_net.qdevice_disable)
|
|
|
15f218 |
qdevice_net.qdevice_destroy()
|
|
|
15f218 |
@@ -83,12 +91,20 @@ def qdevice_start(lib_env, model):
|
|
|
15f218 |
_check_model(model)
|
|
|
15f218 |
_service_start(lib_env, qdevice_net.qdevice_start)
|
|
|
15f218 |
|
|
|
15f218 |
-def qdevice_stop(lib_env, model):
|
|
|
15f218 |
+def qdevice_stop(lib_env, model, proceed_if_used=False):
|
|
|
15f218 |
"""
|
|
|
15f218 |
stop qdevice now on local host
|
|
|
15f218 |
+ string model qdevice model to destroy
|
|
|
15f218 |
+ bool procced_if_used stop qdevice even if it is used by clusters
|
|
|
15f218 |
"""
|
|
|
15f218 |
_ensure_not_cman(lib_env)
|
|
|
15f218 |
_check_model(model)
|
|
|
15f218 |
+ _check_qdevice_not_used(
|
|
|
15f218 |
+ lib_env.report_processor,
|
|
|
15f218 |
+ lib_env.cmd_runner(),
|
|
|
15f218 |
+ model,
|
|
|
15f218 |
+ proceed_if_used
|
|
|
15f218 |
+ )
|
|
|
15f218 |
_service_stop(lib_env, qdevice_net.qdevice_stop)
|
|
|
15f218 |
|
|
|
15f218 |
def qdevice_kill(lib_env, model):
|
|
|
15f218 |
@@ -176,6 +192,19 @@ def _check_model(model):
|
|
|
15f218 |
reports.invalid_option_value("model", model, ["net"])
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
+def _check_qdevice_not_used(reporter, runner, model, force=False):
|
|
|
15f218 |
+ _check_model(model)
|
|
|
15f218 |
+ connected_clusters = []
|
|
|
15f218 |
+ if model == "net":
|
|
|
15f218 |
+ status = qdevice_net.qdevice_status_cluster_text(runner)
|
|
|
15f218 |
+ connected_clusters = qdevice_net.qdevice_connected_clusters(status)
|
|
|
15f218 |
+ if connected_clusters:
|
|
|
15f218 |
+ reporter.process(reports.qdevice_used_by_clusters(
|
|
|
15f218 |
+ connected_clusters,
|
|
|
15f218 |
+ ReportItemSeverity.WARNING if force else ReportItemSeverity.ERROR,
|
|
|
15f218 |
+ None if force else report_codes.FORCE_QDEVICE_USED
|
|
|
15f218 |
+ ))
|
|
|
15f218 |
+
|
|
|
15f218 |
def _service_start(lib_env, func):
|
|
|
15f218 |
lib_env.report_processor.process(
|
|
|
15f218 |
reports.service_start_started("quorum device")
|
|
|
15f218 |
diff --git a/pcs/lib/commands/quorum.py b/pcs/lib/commands/quorum.py
|
|
|
15f218 |
index 7fb7bb4..8390fc6 100644
|
|
|
15f218 |
--- a/pcs/lib/commands/quorum.py
|
|
|
15f218 |
+++ b/pcs/lib/commands/quorum.py
|
|
|
15f218 |
@@ -283,14 +283,23 @@ def remove_device(lib_env, skip_offline_nodes=False):
|
|
|
15f218 |
cfg = lib_env.get_corosync_conf()
|
|
|
15f218 |
model, dummy_options, dummy_options = cfg.get_quorum_device_settings()
|
|
|
15f218 |
cfg.remove_quorum_device()
|
|
|
15f218 |
+
|
|
|
15f218 |
+ if lib_env.is_corosync_conf_live:
|
|
|
15f218 |
+ # fix quorum options for SBD to work properly
|
|
|
15f218 |
+ if sbd.atb_has_to_be_enabled(lib_env.cmd_runner(), cfg):
|
|
|
15f218 |
+ lib_env.report_processor.process(reports.sbd_requires_atb())
|
|
|
15f218 |
+ cfg.set_quorum_options(
|
|
|
15f218 |
+ lib_env.report_processor, {"auto_tie_breaker": "1"}
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
lib_env.push_corosync_conf(cfg, skip_offline_nodes)
|
|
|
15f218 |
|
|
|
15f218 |
if lib_env.is_corosync_conf_live:
|
|
|
15f218 |
+ communicator = lib_env.node_communicator()
|
|
|
15f218 |
# disable qdevice
|
|
|
15f218 |
lib_env.report_processor.process(
|
|
|
15f218 |
reports.service_disable_started("corosync-qdevice")
|
|
|
15f218 |
)
|
|
|
15f218 |
- communicator = lib_env.node_communicator()
|
|
|
15f218 |
parallel_nodes_communication_helper(
|
|
|
15f218 |
qdevice_client.remote_client_disable,
|
|
|
15f218 |
[
|
|
|
15f218 |
@@ -304,7 +313,6 @@ def remove_device(lib_env, skip_offline_nodes=False):
|
|
|
15f218 |
lib_env.report_processor.process(
|
|
|
15f218 |
reports.service_stop_started("corosync-qdevice")
|
|
|
15f218 |
)
|
|
|
15f218 |
- communicator = lib_env.node_communicator()
|
|
|
15f218 |
parallel_nodes_communication_helper(
|
|
|
15f218 |
qdevice_client.remote_client_stop,
|
|
|
15f218 |
[
|
|
|
15f218 |
diff --git a/pcs/lib/commands/test/test_booth.py b/pcs/lib/commands/test/test_booth.py
|
|
|
15f218 |
index 08d2c79..6bcab2b 100644
|
|
|
15f218 |
--- a/pcs/lib/commands/test/test_booth.py
|
|
|
15f218 |
+++ b/pcs/lib/commands/test/test_booth.py
|
|
|
15f218 |
@@ -520,7 +520,7 @@ class TicketOperationTest(TestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_raises_when_command_fail(self):
|
|
|
15f218 |
- mock_run = mock.Mock(return_value=("some message", 1))
|
|
|
15f218 |
+ mock_run = mock.Mock(return_value=("some message", "error", 1))
|
|
|
15f218 |
mock_env = mock.MagicMock(
|
|
|
15f218 |
cmd_runner=mock.Mock(return_value=mock.MagicMock(run=mock_run))
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -533,7 +533,7 @@ class TicketOperationTest(TestCase):
|
|
|
15f218 |
report_codes.BOOTH_TICKET_OPERATION_FAILED,
|
|
|
15f218 |
{
|
|
|
15f218 |
"operation": "grant",
|
|
|
15f218 |
- "reason": "some message",
|
|
|
15f218 |
+ "reason": "error\nsome message",
|
|
|
15f218 |
"site_ip": "1.2.3.4",
|
|
|
15f218 |
"ticket_name": "ABC",
|
|
|
15f218 |
}
|
|
|
15f218 |
diff --git a/pcs/lib/corosync/live.py b/pcs/lib/corosync/live.py
|
|
|
15f218 |
index 1e68c31..67aa0e4 100644
|
|
|
15f218 |
--- a/pcs/lib/corosync/live.py
|
|
|
15f218 |
+++ b/pcs/lib/corosync/live.py
|
|
|
15f218 |
@@ -8,6 +8,7 @@ from __future__ import (
|
|
|
15f218 |
import os.path
|
|
|
15f218 |
|
|
|
15f218 |
from pcs import settings
|
|
|
15f218 |
+from pcs.common.tools import join_multilines
|
|
|
15f218 |
from pcs.lib import reports
|
|
|
15f218 |
from pcs.lib.errors import LibraryError
|
|
|
15f218 |
from pcs.lib.external import NodeCommunicator
|
|
|
15f218 |
@@ -41,42 +42,39 @@ def reload_config(runner):
|
|
|
15f218 |
"""
|
|
|
15f218 |
Ask corosync to reload its configuration
|
|
|
15f218 |
"""
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
os.path.join(settings.corosync_binaries, "corosync-cfgtool"),
|
|
|
15f218 |
"-R"
|
|
|
15f218 |
])
|
|
|
15f218 |
- if retval != 0 or "invalid option" in output:
|
|
|
15f218 |
- raise LibraryError(
|
|
|
15f218 |
- reports.corosync_config_reload_error(output.rstrip())
|
|
|
15f218 |
- )
|
|
|
15f218 |
+ message = join_multilines([stderr, stdout])
|
|
|
15f218 |
+ if retval != 0 or "invalid option" in message:
|
|
|
15f218 |
+ raise LibraryError(reports.corosync_config_reload_error(message))
|
|
|
15f218 |
|
|
|
15f218 |
def get_quorum_status_text(runner):
|
|
|
15f218 |
"""
|
|
|
15f218 |
Get runtime quorum status from the local node
|
|
|
15f218 |
"""
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
os.path.join(settings.corosync_binaries, "corosync-quorumtool"),
|
|
|
15f218 |
"-p"
|
|
|
15f218 |
])
|
|
|
15f218 |
# retval is 0 on success if node is not in partition with quorum
|
|
|
15f218 |
# retval is 1 on error OR on success if node has quorum
|
|
|
15f218 |
- if retval not in [0, 1]:
|
|
|
15f218 |
- raise LibraryError(
|
|
|
15f218 |
- reports.corosync_quorum_get_status_error(output)
|
|
|
15f218 |
- )
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ if retval not in [0, 1] or stderr.strip():
|
|
|
15f218 |
+ raise LibraryError(reports.corosync_quorum_get_status_error(stderr))
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
|
|
|
15f218 |
def set_expected_votes(runner, votes):
|
|
|
15f218 |
"""
|
|
|
15f218 |
set expected votes in live cluster to specified value
|
|
|
15f218 |
"""
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
os.path.join(settings.corosync_binaries, "corosync-quorumtool"),
|
|
|
15f218 |
# format votes to handle the case where they are int
|
|
|
15f218 |
"-e", "{0}".format(votes)
|
|
|
15f218 |
])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.corosync_quorum_set_expected_votes_error(output)
|
|
|
15f218 |
+ reports.corosync_quorum_set_expected_votes_error(stderr)
|
|
|
15f218 |
)
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
diff --git a/pcs/lib/corosync/qdevice_client.py b/pcs/lib/corosync/qdevice_client.py
|
|
|
15f218 |
index 98fbb0e..c9d0095 100644
|
|
|
15f218 |
--- a/pcs/lib/corosync/qdevice_client.py
|
|
|
15f218 |
+++ b/pcs/lib/corosync/qdevice_client.py
|
|
|
15f218 |
@@ -8,6 +8,7 @@ from __future__ import (
|
|
|
15f218 |
import os.path
|
|
|
15f218 |
|
|
|
15f218 |
from pcs import settings
|
|
|
15f218 |
+from pcs.common.tools import join_multilines
|
|
|
15f218 |
from pcs.lib import reports
|
|
|
15f218 |
from pcs.lib.errors import LibraryError
|
|
|
15f218 |
|
|
|
15f218 |
@@ -23,12 +24,14 @@ def get_status_text(runner, verbose=False):
|
|
|
15f218 |
]
|
|
|
15f218 |
if verbose:
|
|
|
15f218 |
cmd.append("-v")
|
|
|
15f218 |
- output, retval = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(cmd)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.corosync_quorum_get_status_error(output)
|
|
|
15f218 |
+ reports.corosync_quorum_get_status_error(
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
|
|
|
15f218 |
def remote_client_enable(reporter, node_communicator, node):
|
|
|
15f218 |
"""
|
|
|
15f218 |
diff --git a/pcs/lib/corosync/qdevice_net.py b/pcs/lib/corosync/qdevice_net.py
|
|
|
15f218 |
index 4054592..200e45a 100644
|
|
|
15f218 |
--- a/pcs/lib/corosync/qdevice_net.py
|
|
|
15f218 |
+++ b/pcs/lib/corosync/qdevice_net.py
|
|
|
15f218 |
@@ -15,6 +15,7 @@ import shutil
|
|
|
15f218 |
import tempfile
|
|
|
15f218 |
|
|
|
15f218 |
from pcs import settings
|
|
|
15f218 |
+from pcs.common.tools import join_multilines
|
|
|
15f218 |
from pcs.lib import external, reports
|
|
|
15f218 |
from pcs.lib.errors import LibraryError
|
|
|
15f218 |
|
|
|
15f218 |
@@ -41,12 +42,15 @@ def qdevice_setup(runner):
|
|
|
15f218 |
if external.is_dir_nonempty(settings.corosync_qdevice_net_server_certs_dir):
|
|
|
15f218 |
raise LibraryError(reports.qdevice_already_initialized(__model))
|
|
|
15f218 |
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
__qnetd_certutil, "-i"
|
|
|
15f218 |
])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.qdevice_initialization_error(__model, output.rstrip())
|
|
|
15f218 |
+ reports.qdevice_initialization_error(
|
|
|
15f218 |
+ __model,
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def qdevice_initialized():
|
|
|
15f218 |
@@ -78,10 +82,15 @@ def qdevice_status_generic_text(runner, verbose=False):
|
|
|
15f218 |
cmd = [__qnetd_tool, "-s"]
|
|
|
15f218 |
if verbose:
|
|
|
15f218 |
cmd.append("-v")
|
|
|
15f218 |
- output, retval = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(cmd)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- raise LibraryError(reports.qdevice_get_status_error(__model, output))
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ raise LibraryError(
|
|
|
15f218 |
+ reports.qdevice_get_status_error(
|
|
|
15f218 |
+ __model,
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
|
|
|
15f218 |
def qdevice_status_cluster_text(runner, cluster=None, verbose=False):
|
|
|
15f218 |
"""
|
|
|
15f218 |
@@ -94,10 +103,24 @@ def qdevice_status_cluster_text(runner, cluster=None, verbose=False):
|
|
|
15f218 |
cmd.append("-v")
|
|
|
15f218 |
if cluster:
|
|
|
15f218 |
cmd.extend(["-c", cluster])
|
|
|
15f218 |
- output, retval = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(cmd)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- raise LibraryError(reports.qdevice_get_status_error(__model, output))
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ raise LibraryError(
|
|
|
15f218 |
+ reports.qdevice_get_status_error(
|
|
|
15f218 |
+ __model,
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
+
|
|
|
15f218 |
+def qdevice_connected_clusters(status_cluster_text):
|
|
|
15f218 |
+ connected_clusters = []
|
|
|
15f218 |
+ regexp = re.compile(r'^Cluster "(?P<cluster>[^"]+)":$')
|
|
|
15f218 |
+ for line in status_cluster_text.splitlines():
|
|
|
15f218 |
+ match = regexp.search(line)
|
|
|
15f218 |
+ if match:
|
|
|
15f218 |
+ connected_clusters.append(match.group("cluster"))
|
|
|
15f218 |
+ return connected_clusters
|
|
|
15f218 |
|
|
|
15f218 |
def qdevice_enable(runner):
|
|
|
15f218 |
"""
|
|
|
15f218 |
@@ -143,17 +166,19 @@ def qdevice_sign_certificate_request(runner, cert_request, cluster_name):
|
|
|
15f218 |
reports.qdevice_certificate_sign_error
|
|
|
15f218 |
)
|
|
|
15f218 |
# sign the request
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
__qnetd_certutil, "-s", "-c", tmpfile.name, "-n", cluster_name
|
|
|
15f218 |
])
|
|
|
15f218 |
tmpfile.close() # temp file is deleted on close
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.qdevice_certificate_sign_error(output.strip())
|
|
|
15f218 |
+ reports.qdevice_certificate_sign_error(
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
# get signed certificate, corosync tool only works with files
|
|
|
15f218 |
return _get_output_certificate(
|
|
|
15f218 |
- output,
|
|
|
15f218 |
+ stdout,
|
|
|
15f218 |
reports.qdevice_certificate_sign_error
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -181,12 +206,15 @@ def client_setup(runner, ca_certificate):
|
|
|
15f218 |
reports.qdevice_initialization_error(__model, e.strerror)
|
|
|
15f218 |
)
|
|
|
15f218 |
# initialize client's certificate storage
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
__qdevice_certutil, "-i", "-c", ca_file_path
|
|
|
15f218 |
])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.qdevice_initialization_error(__model, output.rstrip())
|
|
|
15f218 |
+ reports.qdevice_initialization_error(
|
|
|
15f218 |
+ __model,
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def client_initialized():
|
|
|
15f218 |
@@ -217,15 +245,18 @@ def client_generate_certificate_request(runner, cluster_name):
|
|
|
15f218 |
"""
|
|
|
15f218 |
if not client_initialized():
|
|
|
15f218 |
raise LibraryError(reports.qdevice_not_initialized(__model))
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
__qdevice_certutil, "-r", "-n", cluster_name
|
|
|
15f218 |
])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.qdevice_initialization_error(__model, output.rstrip())
|
|
|
15f218 |
+ reports.qdevice_initialization_error(
|
|
|
15f218 |
+ __model,
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
return _get_output_certificate(
|
|
|
15f218 |
- output,
|
|
|
15f218 |
+ stdout,
|
|
|
15f218 |
functools.partial(reports.qdevice_initialization_error, __model)
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -243,17 +274,19 @@ def client_cert_request_to_pk12(runner, cert_request):
|
|
|
15f218 |
reports.qdevice_certificate_import_error
|
|
|
15f218 |
)
|
|
|
15f218 |
# transform it
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
__qdevice_certutil, "-M", "-c", tmpfile.name
|
|
|
15f218 |
])
|
|
|
15f218 |
tmpfile.close() # temp file is deleted on close
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.qdevice_certificate_import_error(output)
|
|
|
15f218 |
+ reports.qdevice_certificate_import_error(
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
# get resulting pk12, corosync tool only works with files
|
|
|
15f218 |
return _get_output_certificate(
|
|
|
15f218 |
- output,
|
|
|
15f218 |
+ stdout,
|
|
|
15f218 |
reports.qdevice_certificate_import_error
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -268,13 +301,15 @@ def client_import_certificate_and_key(runner, pk12_certificate):
|
|
|
15f218 |
pk12_certificate,
|
|
|
15f218 |
reports.qdevice_certificate_import_error
|
|
|
15f218 |
)
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
__qdevice_certutil, "-m", "-c", tmpfile.name
|
|
|
15f218 |
])
|
|
|
15f218 |
tmpfile.close() # temp file is deleted on close
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.qdevice_certificate_import_error(output)
|
|
|
15f218 |
+ reports.qdevice_certificate_import_error(
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def remote_qdevice_get_ca_certificate(node_communicator, host):
|
|
|
15f218 |
diff --git a/pcs/lib/external.py b/pcs/lib/external.py
|
|
|
15f218 |
index 08bf2bb..074d2aa 100644
|
|
|
15f218 |
--- a/pcs/lib/external.py
|
|
|
15f218 |
+++ b/pcs/lib/external.py
|
|
|
15f218 |
@@ -47,14 +47,15 @@ except ImportError:
|
|
|
15f218 |
URLError as urllib_URLError
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-from pcs.lib import reports
|
|
|
15f218 |
-from pcs.lib.errors import LibraryError, ReportItemSeverity
|
|
|
15f218 |
+from pcs import settings
|
|
|
15f218 |
from pcs.common import report_codes
|
|
|
15f218 |
from pcs.common.tools import (
|
|
|
15f218 |
+ join_multilines,
|
|
|
15f218 |
simple_cache,
|
|
|
15f218 |
run_parallel as tools_run_parallel,
|
|
|
15f218 |
)
|
|
|
15f218 |
-from pcs import settings
|
|
|
15f218 |
+from pcs.lib import reports
|
|
|
15f218 |
+from pcs.lib.errors import LibraryError, ReportItemSeverity
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
class ManageServiceError(Exception):
|
|
|
15f218 |
@@ -138,13 +139,17 @@ def disable_service(runner, service, instance=None):
|
|
|
15f218 |
if not is_service_installed(runner, service):
|
|
|
15f218 |
return
|
|
|
15f218 |
if is_systemctl():
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
"systemctl", "disable", _get_service_name(service, instance)
|
|
|
15f218 |
])
|
|
|
15f218 |
else:
|
|
|
15f218 |
- output, retval = runner.run(["chkconfig", service, "off"])
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(["chkconfig", service, "off"])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- raise DisableServiceError(service, output.rstrip(), instance)
|
|
|
15f218 |
+ raise DisableServiceError(
|
|
|
15f218 |
+ service,
|
|
|
15f218 |
+ join_multilines([stderr, stdout]),
|
|
|
15f218 |
+ instance
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def enable_service(runner, service, instance=None):
|
|
|
15f218 |
@@ -158,13 +163,17 @@ def enable_service(runner, service, instance=None):
|
|
|
15f218 |
If None no instance name will be used.
|
|
|
15f218 |
"""
|
|
|
15f218 |
if is_systemctl():
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
"systemctl", "enable", _get_service_name(service, instance)
|
|
|
15f218 |
])
|
|
|
15f218 |
else:
|
|
|
15f218 |
- output, retval = runner.run(["chkconfig", service, "on"])
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(["chkconfig", service, "on"])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- raise EnableServiceError(service, output.rstrip(), instance)
|
|
|
15f218 |
+ raise EnableServiceError(
|
|
|
15f218 |
+ service,
|
|
|
15f218 |
+ join_multilines([stderr, stdout]),
|
|
|
15f218 |
+ instance
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def start_service(runner, service, instance=None):
|
|
|
15f218 |
@@ -176,13 +185,17 @@ def start_service(runner, service, instance=None):
|
|
|
15f218 |
If None no instance name will be used.
|
|
|
15f218 |
"""
|
|
|
15f218 |
if is_systemctl():
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
"systemctl", "start", _get_service_name(service, instance)
|
|
|
15f218 |
])
|
|
|
15f218 |
else:
|
|
|
15f218 |
- output, retval = runner.run(["service", service, "start"])
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(["service", service, "start"])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- raise StartServiceError(service, output.rstrip(), instance)
|
|
|
15f218 |
+ raise StartServiceError(
|
|
|
15f218 |
+ service,
|
|
|
15f218 |
+ join_multilines([stderr, stdout]),
|
|
|
15f218 |
+ instance
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def stop_service(runner, service, instance=None):
|
|
|
15f218 |
@@ -194,13 +207,17 @@ def stop_service(runner, service, instance=None):
|
|
|
15f218 |
If None no instance name will be used.
|
|
|
15f218 |
"""
|
|
|
15f218 |
if is_systemctl():
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run([
|
|
|
15f218 |
"systemctl", "stop", _get_service_name(service, instance)
|
|
|
15f218 |
])
|
|
|
15f218 |
else:
|
|
|
15f218 |
- output, retval = runner.run(["service", service, "stop"])
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(["service", service, "stop"])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- raise StopServiceError(service, output.rstrip(), instance)
|
|
|
15f218 |
+ raise StopServiceError(
|
|
|
15f218 |
+ service,
|
|
|
15f218 |
+ join_multilines([stderr, stdout]),
|
|
|
15f218 |
+ instance
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def kill_services(runner, services):
|
|
|
15f218 |
@@ -210,15 +227,16 @@ def kill_services(runner, services):
|
|
|
15f218 |
iterable services service names
|
|
|
15f218 |
"""
|
|
|
15f218 |
# make killall not report that a process is not running
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(
|
|
|
15f218 |
["killall", "--quiet", "--signal", "9", "--"] + list(services)
|
|
|
15f218 |
)
|
|
|
15f218 |
# If a process isn't running, killall will still return 1 even with --quiet.
|
|
|
15f218 |
# We don't consider that an error, so we check for output string as well.
|
|
|
15f218 |
# If it's empty, no actuall error happened.
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- if output.strip():
|
|
|
15f218 |
- raise KillServicesError(list(services), output.rstrip())
|
|
|
15f218 |
+ message = join_multilines([stderr, stdout])
|
|
|
15f218 |
+ if message:
|
|
|
15f218 |
+ raise KillServicesError(list(services), message)
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def is_service_enabled(runner, service, instance=None):
|
|
|
15f218 |
@@ -229,11 +247,11 @@ def is_service_enabled(runner, service, instance=None):
|
|
|
15f218 |
service -- name of service
|
|
|
15f218 |
"""
|
|
|
15f218 |
if is_systemctl():
|
|
|
15f218 |
- _, retval = runner.run(
|
|
|
15f218 |
+ dummy_stdout, dummy_stderr, retval = runner.run(
|
|
|
15f218 |
["systemctl", "is-enabled", _get_service_name(service, instance)]
|
|
|
15f218 |
)
|
|
|
15f218 |
else:
|
|
|
15f218 |
- _, retval = runner.run(["chkconfig", service])
|
|
|
15f218 |
+ dummy_stdout, dummy_stderr, retval = runner.run(["chkconfig", service])
|
|
|
15f218 |
|
|
|
15f218 |
return retval == 0
|
|
|
15f218 |
|
|
|
15f218 |
@@ -246,13 +264,15 @@ def is_service_running(runner, service, instance=None):
|
|
|
15f218 |
service -- name of service
|
|
|
15f218 |
"""
|
|
|
15f218 |
if is_systemctl():
|
|
|
15f218 |
- _, retval = runner.run([
|
|
|
15f218 |
+ dummy_stdout, dummy_stderr, retval = runner.run([
|
|
|
15f218 |
"systemctl",
|
|
|
15f218 |
"is-active",
|
|
|
15f218 |
_get_service_name(service, instance)
|
|
|
15f218 |
])
|
|
|
15f218 |
else:
|
|
|
15f218 |
- _, retval = runner.run(["service", service, "status"])
|
|
|
15f218 |
+ dummy_stdout, dummy_stderr, retval = runner.run(
|
|
|
15f218 |
+ ["service", service, "status"]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
return retval == 0
|
|
|
15f218 |
|
|
|
15f218 |
@@ -279,12 +299,12 @@ def get_non_systemd_services(runner):
|
|
|
15f218 |
if is_systemctl():
|
|
|
15f218 |
return []
|
|
|
15f218 |
|
|
|
15f218 |
- output, return_code = runner.run(["chkconfig"], ignore_stderr=True)
|
|
|
15f218 |
+ stdout, dummy_stderr, return_code = runner.run(["chkconfig"])
|
|
|
15f218 |
if return_code != 0:
|
|
|
15f218 |
return []
|
|
|
15f218 |
|
|
|
15f218 |
service_list = []
|
|
|
15f218 |
- for service in output.splitlines():
|
|
|
15f218 |
+ for service in stdout.splitlines():
|
|
|
15f218 |
service = service.split(" ", 1)[0]
|
|
|
15f218 |
if service:
|
|
|
15f218 |
service_list.append(service)
|
|
|
15f218 |
@@ -300,12 +320,14 @@ def get_systemd_services(runner):
|
|
|
15f218 |
if not is_systemctl():
|
|
|
15f218 |
return []
|
|
|
15f218 |
|
|
|
15f218 |
- output, return_code = runner.run(["systemctl", "list-unit-files", "--full"])
|
|
|
15f218 |
+ stdout, dummy_stderr, return_code = runner.run([
|
|
|
15f218 |
+ "systemctl", "list-unit-files", "--full"
|
|
|
15f218 |
+ ])
|
|
|
15f218 |
if return_code != 0:
|
|
|
15f218 |
return []
|
|
|
15f218 |
|
|
|
15f218 |
service_list = []
|
|
|
15f218 |
- for service in output.splitlines():
|
|
|
15f218 |
+ for service in stdout.splitlines():
|
|
|
15f218 |
match = re.search(r'^([\S]*)\.service', service)
|
|
|
15f218 |
if match:
|
|
|
15f218 |
service_list.append(match.group(1))
|
|
|
15f218 |
@@ -322,13 +344,13 @@ def is_cman_cluster(runner):
|
|
|
15f218 |
# - corosync1 runs with cman on rhel6
|
|
|
15f218 |
# - corosync1 can be used without cman, but we don't support it anyways
|
|
|
15f218 |
# - corosync2 is the default result if errors occur
|
|
|
15f218 |
- output, retval = runner.run([
|
|
|
15f218 |
+ stdout, dummy_stderr, retval = runner.run([
|
|
|
15f218 |
os.path.join(settings.corosync_binaries, "corosync"),
|
|
|
15f218 |
"-v"
|
|
|
15f218 |
])
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
return False
|
|
|
15f218 |
- match = re.search(r"version\D+(\d+)", output)
|
|
|
15f218 |
+ match = re.search(r"version\D+(\d+)", stdout)
|
|
|
15f218 |
return match is not None and match.group(1) == "1"
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
@@ -340,8 +362,7 @@ class CommandRunner(object):
|
|
|
15f218 |
self._python2 = sys.version[0] == "2"
|
|
|
15f218 |
|
|
|
15f218 |
def run(
|
|
|
15f218 |
- self, args, ignore_stderr=False, stdin_string=None, env_extend=None,
|
|
|
15f218 |
- binary_output=False
|
|
|
15f218 |
+ self, args, stdin_string=None, env_extend=None, binary_output=False
|
|
|
15f218 |
):
|
|
|
15f218 |
#Reset environment variables by empty dict is desired here. We need to
|
|
|
15f218 |
#get rid of defaults - we do not know the context and environment of the
|
|
|
15f218 |
@@ -364,9 +385,7 @@ class CommandRunner(object):
|
|
|
15f218 |
# Some commands react differently if they get anything via stdin
|
|
|
15f218 |
stdin=(subprocess.PIPE if stdin_string is not None else None),
|
|
|
15f218 |
stdout=subprocess.PIPE,
|
|
|
15f218 |
- stderr=(
|
|
|
15f218 |
- subprocess.PIPE if ignore_stderr else subprocess.STDOUT
|
|
|
15f218 |
- ),
|
|
|
15f218 |
+ stderr=subprocess.PIPE,
|
|
|
15f218 |
preexec_fn=(
|
|
|
15f218 |
lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
|
15f218 |
),
|
|
|
15f218 |
@@ -376,7 +395,7 @@ class CommandRunner(object):
|
|
|
15f218 |
# decodes newlines and in python3 also converts bytes to str
|
|
|
15f218 |
universal_newlines=(not self._python2 and not binary_output)
|
|
|
15f218 |
)
|
|
|
15f218 |
- output, dummy_stderror = process.communicate(stdin_string)
|
|
|
15f218 |
+ out_std, out_err = process.communicate(stdin_string)
|
|
|
15f218 |
retval = process.returncode
|
|
|
15f218 |
except OSError as e:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
@@ -386,13 +405,19 @@ class CommandRunner(object):
|
|
|
15f218 |
self._logger.debug(
|
|
|
15f218 |
(
|
|
|
15f218 |
"Finished running: {args}\nReturn value: {retval}"
|
|
|
15f218 |
- + "\n--Debug Output Start--\n{output}\n--Debug Output End--"
|
|
|
15f218 |
- ).format(args=log_args, retval=retval, output=output)
|
|
|
15f218 |
- )
|
|
|
15f218 |
- self._reporter.process(
|
|
|
15f218 |
- reports.run_external_process_finished(log_args, retval, output)
|
|
|
15f218 |
+ + "\n--Debug Stdout Start--\n{out_std}\n--Debug Stdout End--"
|
|
|
15f218 |
+ + "\n--Debug Stderr Start--\n{out_err}\n--Debug Stderr End--"
|
|
|
15f218 |
+ ).format(
|
|
|
15f218 |
+ args=log_args,
|
|
|
15f218 |
+ retval=retval,
|
|
|
15f218 |
+ out_std=out_std,
|
|
|
15f218 |
+ out_err=out_err
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
- return output, retval
|
|
|
15f218 |
+ self._reporter.process(reports.run_external_process_finished(
|
|
|
15f218 |
+ log_args, retval, out_std, out_err
|
|
|
15f218 |
+ ))
|
|
|
15f218 |
+ return out_std, out_err, retval
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
class NodeCommunicationException(Exception):
|
|
|
15f218 |
diff --git a/pcs/lib/pacemaker.py b/pcs/lib/pacemaker.py
|
|
|
15f218 |
index fd6f97b..6747b22 100644
|
|
|
15f218 |
--- a/pcs/lib/pacemaker.py
|
|
|
15f218 |
+++ b/pcs/lib/pacemaker.py
|
|
|
15f218 |
@@ -9,6 +9,7 @@ import os.path
|
|
|
15f218 |
from lxml import etree
|
|
|
15f218 |
|
|
|
15f218 |
from pcs import settings
|
|
|
15f218 |
+from pcs.common.tools import join_multilines
|
|
|
15f218 |
from pcs.lib import reports
|
|
|
15f218 |
from pcs.lib.errors import LibraryError
|
|
|
15f218 |
from pcs.lib.pacemaker_state import ClusterState
|
|
|
15f218 |
@@ -26,28 +27,33 @@ def __exec(name):
|
|
|
15f218 |
return os.path.join(settings.pacemaker_binaries, name)
|
|
|
15f218 |
|
|
|
15f218 |
def get_cluster_status_xml(runner):
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(
|
|
|
15f218 |
[__exec("crm_mon"), "--one-shot", "--as-xml", "--inactive"]
|
|
|
15f218 |
)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise CrmMonErrorException(
|
|
|
15f218 |
- reports.cluster_state_cannot_load(retval, output)
|
|
|
15f218 |
+ reports.cluster_state_cannot_load(join_multilines([stderr, stdout]))
|
|
|
15f218 |
)
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
|
|
|
15f218 |
def get_cib_xml(runner, scope=None):
|
|
|
15f218 |
command = [__exec("cibadmin"), "--local", "--query"]
|
|
|
15f218 |
if scope:
|
|
|
15f218 |
command.append("--scope={0}".format(scope))
|
|
|
15f218 |
- output, retval = runner.run(command)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(command)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
if retval == __EXITCODE_CIB_SCOPE_VALID_BUT_NOT_PRESENT and scope:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.cib_load_error_scope_missing(scope, retval, output)
|
|
|
15f218 |
+ reports.cib_load_error_scope_missing(
|
|
|
15f218 |
+ scope,
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
else:
|
|
|
15f218 |
- raise LibraryError(reports.cib_load_error(retval, output))
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ raise LibraryError(
|
|
|
15f218 |
+ reports.cib_load_error(join_multilines([stderr, stdout]))
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ return stdout
|
|
|
15f218 |
|
|
|
15f218 |
def get_cib(xml):
|
|
|
15f218 |
try:
|
|
|
15f218 |
@@ -59,9 +65,9 @@ def replace_cib_configuration_xml(runner, xml, cib_upgraded=False):
|
|
|
15f218 |
cmd = [__exec("cibadmin"), "--replace", "--verbose", "--xml-pipe"]
|
|
|
15f218 |
if not cib_upgraded:
|
|
|
15f218 |
cmd += ["--scope", "configuration"]
|
|
|
15f218 |
- output, retval = runner.run(cmd, stdin_string=xml)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(cmd, stdin_string=xml)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- raise LibraryError(reports.cib_push_error(retval, output))
|
|
|
15f218 |
+ raise LibraryError(reports.cib_push_error(stderr, stdout))
|
|
|
15f218 |
|
|
|
15f218 |
def replace_cib_configuration(runner, tree, cib_upgraded=False):
|
|
|
15f218 |
#etree returns bytes: b'xml'
|
|
|
15f218 |
@@ -108,13 +114,18 @@ def resource_cleanup(runner, resource=None, node=None, force=False):
|
|
|
15f218 |
if node:
|
|
|
15f218 |
cmd.extend(["--node", node])
|
|
|
15f218 |
|
|
|
15f218 |
- output, retval = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(cmd)
|
|
|
15f218 |
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.resource_cleanup_error(retval, output, resource, node)
|
|
|
15f218 |
+ reports.resource_cleanup_error(
|
|
|
15f218 |
+ join_multilines([stderr, stdout]),
|
|
|
15f218 |
+ resource,
|
|
|
15f218 |
+ node
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
- return output
|
|
|
15f218 |
+ # usefull output (what has been done) goes to stderr
|
|
|
15f218 |
+ return join_multilines([stdout, stderr])
|
|
|
15f218 |
|
|
|
15f218 |
def nodes_standby(runner, node_list=None, all_nodes=False):
|
|
|
15f218 |
return __nodes_standby_unstandby(runner, True, node_list, all_nodes)
|
|
|
15f218 |
@@ -124,8 +135,11 @@ def nodes_unstandby(runner, node_list=None, all_nodes=False):
|
|
|
15f218 |
|
|
|
15f218 |
def has_resource_wait_support(runner):
|
|
|
15f218 |
# returns 1 on success so we don't care about retval
|
|
|
15f218 |
- output, dummy_retval = runner.run([__exec("crm_resource"), "-?"])
|
|
|
15f218 |
- return "--wait" in output
|
|
|
15f218 |
+ stdout, stderr, dummy_retval = runner.run(
|
|
|
15f218 |
+ [__exec("crm_resource"), "-?"]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ # help goes to stderr but we check stdout as well if that gets changed
|
|
|
15f218 |
+ return "--wait" in stderr or "--wait" in stdout
|
|
|
15f218 |
|
|
|
15f218 |
def ensure_resource_wait_support(runner):
|
|
|
15f218 |
if not has_resource_wait_support(runner):
|
|
|
15f218 |
@@ -135,15 +149,22 @@ def wait_for_resources(runner, timeout=None):
|
|
|
15f218 |
args = [__exec("crm_resource"), "--wait"]
|
|
|
15f218 |
if timeout is not None:
|
|
|
15f218 |
args.append("--timeout={0}".format(timeout))
|
|
|
15f218 |
- output, retval = runner.run(args)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(args)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
+ # Usefull info goes to stderr - not only error messages, a list of
|
|
|
15f218 |
+ # pending actions in case of timeout goes there as well.
|
|
|
15f218 |
+ # We use stdout just to be sure if that's get changed.
|
|
|
15f218 |
if retval == __EXITCODE_WAIT_TIMEOUT:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.resource_wait_timed_out(retval, output.strip())
|
|
|
15f218 |
+ reports.resource_wait_timed_out(
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
else:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
- reports.resource_wait_error(retval, output.strip())
|
|
|
15f218 |
+ reports.resource_wait_error(
|
|
|
15f218 |
+ join_multilines([stderr, stdout])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def __nodes_standby_unstandby(
|
|
|
15f218 |
@@ -178,9 +199,11 @@ def __nodes_standby_unstandby(
|
|
|
15f218 |
cmd_list.append(cmd_template)
|
|
|
15f218 |
report = []
|
|
|
15f218 |
for cmd in cmd_list:
|
|
|
15f218 |
- output, retval = runner.run(cmd)
|
|
|
15f218 |
+ stdout, stderr, retval = runner.run(cmd)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
- report.append(reports.common_error(output))
|
|
|
15f218 |
+ report.append(
|
|
|
15f218 |
+ reports.common_error(join_multilines([stderr, stdout]))
|
|
|
15f218 |
+ )
|
|
|
15f218 |
if report:
|
|
|
15f218 |
raise LibraryError(*report)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -189,21 +212,23 @@ def __get_local_node_name(runner):
|
|
|
15f218 |
# but it returns false names when cluster is not running (or we are on
|
|
|
15f218 |
# a remote node). Getting node id first is reliable since it fails in those
|
|
|
15f218 |
# cases.
|
|
|
15f218 |
- output, retval = runner.run([__exec("crm_node"), "--cluster-id"])
|
|
|
15f218 |
+ stdout, dummy_stderr, retval = runner.run(
|
|
|
15f218 |
+ [__exec("crm_node"), "--cluster-id"]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
reports.pacemaker_local_node_name_not_found("node id not found")
|
|
|
15f218 |
)
|
|
|
15f218 |
- node_id = output.strip()
|
|
|
15f218 |
+ node_id = stdout.strip()
|
|
|
15f218 |
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
+ stdout, dummy_stderr, retval = runner.run(
|
|
|
15f218 |
[__exec("crm_node"), "--name-for-id={0}".format(node_id)]
|
|
|
15f218 |
)
|
|
|
15f218 |
if retval != 0:
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
reports.pacemaker_local_node_name_not_found("node name not found")
|
|
|
15f218 |
)
|
|
|
15f218 |
- node_name = output.strip()
|
|
|
15f218 |
+ node_name = stdout.strip()
|
|
|
15f218 |
|
|
|
15f218 |
if node_name == "(null)":
|
|
|
15f218 |
raise LibraryError(
|
|
|
15f218 |
diff --git a/pcs/lib/reports.py b/pcs/lib/reports.py
|
|
|
15f218 |
index a701679..b9e9a66 100644
|
|
|
15f218 |
--- a/pcs/lib/reports.py
|
|
|
15f218 |
+++ b/pcs/lib/reports.py
|
|
|
15f218 |
@@ -262,21 +262,24 @@ def run_external_process_started(command, stdin):
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def run_external_process_finished(command, retval, stdout):
|
|
|
15f218 |
+def run_external_process_finished(command, retval, stdout, stderr):
|
|
|
15f218 |
"""
|
|
|
15f218 |
information about result of running an external process
|
|
|
15f218 |
command string the external process command
|
|
|
15f218 |
retval external process's return (exit) code
|
|
|
15f218 |
stdout string external process's stdout
|
|
|
15f218 |
+ stderr string external process's stderr
|
|
|
15f218 |
"""
|
|
|
15f218 |
return ReportItem.debug(
|
|
|
15f218 |
report_codes.RUN_EXTERNAL_PROCESS_FINISHED,
|
|
|
15f218 |
"Finished running: {command}\nReturn value: {return_value}"
|
|
|
15f218 |
- + "\n--Debug Output Start--\n{stdout}\n--Debug Output End--\n",
|
|
|
15f218 |
+ + "\n--Debug Stdout Start--\n{stdout}\n--Debug Stdout End--"
|
|
|
15f218 |
+ + "\n--Debug Stderr Start--\n{stderr}\n--Debug Stderr End--\n",
|
|
|
15f218 |
info={
|
|
|
15f218 |
"command": command,
|
|
|
15f218 |
"return_value": retval,
|
|
|
15f218 |
"stdout": stdout,
|
|
|
15f218 |
+ "stderr": stderr,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -854,6 +857,23 @@ def qdevice_get_status_error(model, reason):
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
+def qdevice_used_by_clusters(
|
|
|
15f218 |
+ clusters, severity=ReportItemSeverity.ERROR, forceable=None
|
|
|
15f218 |
+):
|
|
|
15f218 |
+ """
|
|
|
15f218 |
+ Qdevice is currently being used by clusters, cannot stop it unless forced
|
|
|
15f218 |
+ """
|
|
|
15f218 |
+ return ReportItem(
|
|
|
15f218 |
+ report_codes.QDEVICE_USED_BY_CLUSTERS,
|
|
|
15f218 |
+ severity,
|
|
|
15f218 |
+ "Quorum device is currently being used by cluster(s): {clusters_str}",
|
|
|
15f218 |
+ info={
|
|
|
15f218 |
+ "clusters": clusters,
|
|
|
15f218 |
+ "clusters_str": ", ".join(clusters),
|
|
|
15f218 |
+ },
|
|
|
15f218 |
+ forceable=forceable
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
def cman_unsupported_command():
|
|
|
15f218 |
"""
|
|
|
15f218 |
requested library command is not available as local cluster is CMAN based
|
|
|
15f218 |
@@ -903,35 +923,31 @@ def resource_does_not_exist(resource_id):
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def cib_load_error(retval, stdout):
|
|
|
15f218 |
+def cib_load_error(reason):
|
|
|
15f218 |
"""
|
|
|
15f218 |
cannot load cib from cibadmin, cibadmin exited with non-zero code
|
|
|
15f218 |
- retval external process's return (exit) code
|
|
|
15f218 |
- stdout string external process's stdout
|
|
|
15f218 |
+ string reason error description
|
|
|
15f218 |
"""
|
|
|
15f218 |
return ReportItem.error(
|
|
|
15f218 |
report_codes.CIB_LOAD_ERROR,
|
|
|
15f218 |
"unable to get cib",
|
|
|
15f218 |
info={
|
|
|
15f218 |
- "return_value": retval,
|
|
|
15f218 |
- "stdout": stdout,
|
|
|
15f218 |
+ "reason": reason,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def cib_load_error_scope_missing(scope, retval, stdout):
|
|
|
15f218 |
+def cib_load_error_scope_missing(scope, reason):
|
|
|
15f218 |
"""
|
|
|
15f218 |
cannot load cib from cibadmin, specified scope is missing in the cib
|
|
|
15f218 |
scope string requested cib scope
|
|
|
15f218 |
- retval external process's return (exit) code
|
|
|
15f218 |
- stdout string external process's stdout
|
|
|
15f218 |
+ string reason error description
|
|
|
15f218 |
"""
|
|
|
15f218 |
return ReportItem.error(
|
|
|
15f218 |
report_codes.CIB_LOAD_ERROR_SCOPE_MISSING,
|
|
|
15f218 |
"unable to get cib, scope '{scope}' not present in cib",
|
|
|
15f218 |
info={
|
|
|
15f218 |
"scope": scope,
|
|
|
15f218 |
- "return_value": retval,
|
|
|
15f218 |
- "stdout": stdout,
|
|
|
15f218 |
+ "reason": reason,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -957,33 +973,31 @@ def cib_missing_mandatory_section(section_name):
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def cib_push_error(retval, stdout):
|
|
|
15f218 |
+def cib_push_error(reason, pushed_cib):
|
|
|
15f218 |
"""
|
|
|
15f218 |
cannot push cib to cibadmin, cibadmin exited with non-zero code
|
|
|
15f218 |
- retval external process's return (exit) code
|
|
|
15f218 |
- stdout string external process's stdout
|
|
|
15f218 |
+ string reason error description
|
|
|
15f218 |
+ string pushed_cib cib which failed to be pushed
|
|
|
15f218 |
"""
|
|
|
15f218 |
return ReportItem.error(
|
|
|
15f218 |
report_codes.CIB_PUSH_ERROR,
|
|
|
15f218 |
- "Unable to update cib\n{stdout}",
|
|
|
15f218 |
+ "Unable to update cib\n{reason}\n{pushed_cib}",
|
|
|
15f218 |
info={
|
|
|
15f218 |
- "return_value": retval,
|
|
|
15f218 |
- "stdout": stdout,
|
|
|
15f218 |
+ "reason": reason,
|
|
|
15f218 |
+ "pushed_cib": pushed_cib,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def cluster_state_cannot_load(retval, stdout):
|
|
|
15f218 |
+def cluster_state_cannot_load(reason):
|
|
|
15f218 |
"""
|
|
|
15f218 |
cannot load cluster status from crm_mon, crm_mon exited with non-zero code
|
|
|
15f218 |
- retval external process's return (exit) code
|
|
|
15f218 |
- stdout string external process's stdout
|
|
|
15f218 |
+ string reason error description
|
|
|
15f218 |
"""
|
|
|
15f218 |
return ReportItem.error(
|
|
|
15f218 |
report_codes.CRM_MON_ERROR,
|
|
|
15f218 |
"error running crm_mon, is pacemaker running?",
|
|
|
15f218 |
info={
|
|
|
15f218 |
- "return_value": retval,
|
|
|
15f218 |
- "stdout": stdout,
|
|
|
15f218 |
+ "reason": reason,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -1005,57 +1019,50 @@ def resource_wait_not_supported():
|
|
|
15f218 |
"crm_resource does not support --wait, please upgrade pacemaker"
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def resource_wait_timed_out(retval, stdout):
|
|
|
15f218 |
+def resource_wait_timed_out(reason):
|
|
|
15f218 |
"""
|
|
|
15f218 |
waiting for resources (crm_resource --wait) failed, timeout expired
|
|
|
15f218 |
- retval external process's return (exit) code
|
|
|
15f218 |
- stdout string external process's stdout
|
|
|
15f218 |
+ string reason error description
|
|
|
15f218 |
"""
|
|
|
15f218 |
return ReportItem.error(
|
|
|
15f218 |
report_codes.RESOURCE_WAIT_TIMED_OUT,
|
|
|
15f218 |
- "waiting timeout\n\n{stdout}",
|
|
|
15f218 |
+ "waiting timeout\n\n{reason}",
|
|
|
15f218 |
info={
|
|
|
15f218 |
- "return_value": retval,
|
|
|
15f218 |
- "stdout": stdout,
|
|
|
15f218 |
+ "reason": reason,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def resource_wait_error(retval, stdout):
|
|
|
15f218 |
+def resource_wait_error(reason):
|
|
|
15f218 |
"""
|
|
|
15f218 |
waiting for resources (crm_resource --wait) failed
|
|
|
15f218 |
- retval external process's return (exit) code
|
|
|
15f218 |
- stdout string external process's stdout
|
|
|
15f218 |
+ string reason error description
|
|
|
15f218 |
"""
|
|
|
15f218 |
return ReportItem.error(
|
|
|
15f218 |
report_codes.RESOURCE_WAIT_ERROR,
|
|
|
15f218 |
- "{stdout}",
|
|
|
15f218 |
+ "{reason}",
|
|
|
15f218 |
info={
|
|
|
15f218 |
- "return_value": retval,
|
|
|
15f218 |
- "stdout": stdout,
|
|
|
15f218 |
+ "reason": reason,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
-def resource_cleanup_error(retval, stdout, resource=None, node=None):
|
|
|
15f218 |
+def resource_cleanup_error(reason, resource=None, node=None):
|
|
|
15f218 |
"""
|
|
|
15f218 |
an error occured when deleting resource history in pacemaker
|
|
|
15f218 |
- retval external process's return (exit) code
|
|
|
15f218 |
- stdout string external process's stdout
|
|
|
15f218 |
- resource string resource which has been cleaned up
|
|
|
15f218 |
- node string node which has been cleaned up
|
|
|
15f218 |
+ string reason error description
|
|
|
15f218 |
+ string resource resource which has been cleaned up
|
|
|
15f218 |
+ string node node which has been cleaned up
|
|
|
15f218 |
"""
|
|
|
15f218 |
if resource:
|
|
|
15f218 |
- text = "Unable to cleanup resource: {resource}\n{stdout}"
|
|
|
15f218 |
+ text = "Unable to cleanup resource: {resource}\n{reason}"
|
|
|
15f218 |
else:
|
|
|
15f218 |
text = (
|
|
|
15f218 |
- "Unexpected error occured. 'crm_resource -C' err_code: "
|
|
|
15f218 |
- + "{return_value}\n{stdout}"
|
|
|
15f218 |
+ "Unexpected error occured. 'crm_resource -C' error:\n{reason}"
|
|
|
15f218 |
)
|
|
|
15f218 |
return ReportItem.error(
|
|
|
15f218 |
report_codes.RESOURCE_CLEANUP_ERROR,
|
|
|
15f218 |
text,
|
|
|
15f218 |
info={
|
|
|
15f218 |
- "return_value": retval,
|
|
|
15f218 |
- "stdout": stdout,
|
|
|
15f218 |
+ "reason": reason,
|
|
|
15f218 |
"resource": resource,
|
|
|
15f218 |
"node": node,
|
|
|
15f218 |
}
|
|
|
15f218 |
diff --git a/pcs/lib/resource_agent.py b/pcs/lib/resource_agent.py
|
|
|
15f218 |
index ea93875..d49b5c0 100644
|
|
|
15f218 |
--- a/pcs/lib/resource_agent.py
|
|
|
15f218 |
+++ b/pcs/lib/resource_agent.py
|
|
|
15f218 |
@@ -125,14 +125,14 @@ def _get_pcmk_advanced_stonith_parameters(runner):
|
|
|
15f218 |
"""
|
|
|
15f218 |
@simple_cache
|
|
|
15f218 |
def __get_stonithd_parameters():
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
- [settings.stonithd_binary, "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ stdout, stderr, dummy_retval = runner.run(
|
|
|
15f218 |
+ [settings.stonithd_binary, "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
- if output.strip() == "":
|
|
|
15f218 |
- raise UnableToGetAgentMetadata("stonithd", output)
|
|
|
15f218 |
+ if stdout.strip() == "":
|
|
|
15f218 |
+ raise UnableToGetAgentMetadata("stonithd", stderr)
|
|
|
15f218 |
|
|
|
15f218 |
try:
|
|
|
15f218 |
- params = _get_agent_parameters(etree.fromstring(output))
|
|
|
15f218 |
+ params = _get_agent_parameters(etree.fromstring(stdout))
|
|
|
15f218 |
for param in params:
|
|
|
15f218 |
param["longdesc"] = "{0}\n{1}".format(
|
|
|
15f218 |
param["shortdesc"], param["longdesc"]
|
|
|
15f218 |
@@ -166,15 +166,15 @@ def get_fence_agent_metadata(runner, fence_agent):
|
|
|
15f218 |
):
|
|
|
15f218 |
raise AgentNotFound(fence_agent)
|
|
|
15f218 |
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
- [script_path, "-o", "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ stdout, stderr, dummy_retval = runner.run(
|
|
|
15f218 |
+ [script_path, "-o", "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
- if output.strip() == "":
|
|
|
15f218 |
- raise UnableToGetAgentMetadata(fence_agent, output)
|
|
|
15f218 |
+ if stdout.strip() == "":
|
|
|
15f218 |
+ raise UnableToGetAgentMetadata(fence_agent, stderr)
|
|
|
15f218 |
|
|
|
15f218 |
try:
|
|
|
15f218 |
- return etree.fromstring(output)
|
|
|
15f218 |
+ return etree.fromstring(stdout)
|
|
|
15f218 |
except etree.XMLSyntaxError as e:
|
|
|
15f218 |
raise UnableToGetAgentMetadata(fence_agent, str(e))
|
|
|
15f218 |
|
|
|
15f218 |
@@ -219,17 +219,16 @@ def _get_ocf_resource_agent_metadata(runner, provider, agent):
|
|
|
15f218 |
if not __is_path_abs(script_path) or not is_path_runnable(script_path):
|
|
|
15f218 |
raise AgentNotFound(agent_name)
|
|
|
15f218 |
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
+ stdout, stderr, dummy_retval = runner.run(
|
|
|
15f218 |
[script_path, "meta-data"],
|
|
|
15f218 |
- env_extend={"OCF_ROOT": settings.ocf_root},
|
|
|
15f218 |
- ignore_stderr=True
|
|
|
15f218 |
+ env_extend={"OCF_ROOT": settings.ocf_root}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
- if output.strip() == "":
|
|
|
15f218 |
- raise UnableToGetAgentMetadata(agent_name, output)
|
|
|
15f218 |
+ if stdout.strip() == "":
|
|
|
15f218 |
+ raise UnableToGetAgentMetadata(agent_name, stderr)
|
|
|
15f218 |
|
|
|
15f218 |
try:
|
|
|
15f218 |
- return etree.fromstring(output)
|
|
|
15f218 |
+ return etree.fromstring(stdout)
|
|
|
15f218 |
except etree.XMLSyntaxError as e:
|
|
|
15f218 |
raise UnableToGetAgentMetadata(agent_name, str(e))
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/pcs/lib/sbd.py b/pcs/lib/sbd.py
|
|
|
15f218 |
index 39de740..9b57400 100644
|
|
|
15f218 |
--- a/pcs/lib/sbd.py
|
|
|
15f218 |
+++ b/pcs/lib/sbd.py
|
|
|
15f218 |
@@ -115,11 +115,11 @@ def atb_has_to_be_enabled(runner, corosync_conf_facade, node_number_modifier=0):
|
|
|
15f218 |
node.
|
|
|
15f218 |
"""
|
|
|
15f218 |
return (
|
|
|
15f218 |
+ not corosync_conf_facade.is_enabled_auto_tie_breaker()
|
|
|
15f218 |
+ and
|
|
|
15f218 |
is_auto_tie_breaker_needed(
|
|
|
15f218 |
runner, corosync_conf_facade, node_number_modifier
|
|
|
15f218 |
)
|
|
|
15f218 |
- and
|
|
|
15f218 |
- not corosync_conf_facade.is_enabled_auto_tie_breaker()
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/pcs/qdevice.py b/pcs/qdevice.py
|
|
|
15f218 |
index 0037704..2591bae 100644
|
|
|
15f218 |
--- a/pcs/qdevice.py
|
|
|
15f218 |
+++ b/pcs/qdevice.py
|
|
|
15f218 |
@@ -92,7 +92,7 @@ def qdevice_destroy_cmd(lib, argv, modifiers):
|
|
|
15f218 |
if len(argv) != 1:
|
|
|
15f218 |
raise CmdLineInputError()
|
|
|
15f218 |
model = argv[0]
|
|
|
15f218 |
- lib.qdevice.destroy(model)
|
|
|
15f218 |
+ lib.qdevice.destroy(model, modifiers["force"])
|
|
|
15f218 |
|
|
|
15f218 |
def qdevice_start_cmd(lib, argv, modifiers):
|
|
|
15f218 |
if len(argv) != 1:
|
|
|
15f218 |
@@ -104,7 +104,7 @@ def qdevice_stop_cmd(lib, argv, modifiers):
|
|
|
15f218 |
if len(argv) != 1:
|
|
|
15f218 |
raise CmdLineInputError()
|
|
|
15f218 |
model = argv[0]
|
|
|
15f218 |
- lib.qdevice.stop(model)
|
|
|
15f218 |
+ lib.qdevice.stop(model, modifiers["force"])
|
|
|
15f218 |
|
|
|
15f218 |
def qdevice_kill_cmd(lib, argv, modifiers):
|
|
|
15f218 |
if len(argv) != 1:
|
|
|
15f218 |
diff --git a/pcs/test/resources/corosync-qdevice.conf b/pcs/test/resources/corosync-qdevice.conf
|
|
|
15f218 |
new file mode 100644
|
|
|
15f218 |
index 0000000..38998e7
|
|
|
15f218 |
--- /dev/null
|
|
|
15f218 |
+++ b/pcs/test/resources/corosync-qdevice.conf
|
|
|
15f218 |
@@ -0,0 +1,34 @@
|
|
|
15f218 |
+totem {
|
|
|
15f218 |
+ version: 2
|
|
|
15f218 |
+ secauth: off
|
|
|
15f218 |
+ cluster_name: test99
|
|
|
15f218 |
+ transport: udpu
|
|
|
15f218 |
+}
|
|
|
15f218 |
+
|
|
|
15f218 |
+nodelist {
|
|
|
15f218 |
+ node {
|
|
|
15f218 |
+ ring0_addr: rh7-1
|
|
|
15f218 |
+ nodeid: 1
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+
|
|
|
15f218 |
+ node {
|
|
|
15f218 |
+ ring0_addr: rh7-2
|
|
|
15f218 |
+ nodeid: 2
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+}
|
|
|
15f218 |
+
|
|
|
15f218 |
+quorum {
|
|
|
15f218 |
+ provider: corosync_votequorum
|
|
|
15f218 |
+
|
|
|
15f218 |
+ device {
|
|
|
15f218 |
+ model: net
|
|
|
15f218 |
+
|
|
|
15f218 |
+ net {
|
|
|
15f218 |
+ host: 127.0.0.1
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+}
|
|
|
15f218 |
+
|
|
|
15f218 |
+logging {
|
|
|
15f218 |
+ to_syslog: yes
|
|
|
15f218 |
+}
|
|
|
15f218 |
diff --git a/pcs/test/test_common_tools.py b/pcs/test/test_common_tools.py
|
|
|
15f218 |
index 5290e6d..d9b6af3 100644
|
|
|
15f218 |
--- a/pcs/test/test_common_tools.py
|
|
|
15f218 |
+++ b/pcs/test/test_common_tools.py
|
|
|
15f218 |
@@ -63,3 +63,35 @@ class RunParallelTestCase(TestCase):
|
|
|
15f218 |
elapsed_time = finish_time - start_time
|
|
|
15f218 |
self.assertTrue(elapsed_time > x)
|
|
|
15f218 |
self.assertTrue(elapsed_time < sum([i + 1 for i in range(x)]))
|
|
|
15f218 |
+
|
|
|
15f218 |
+
|
|
|
15f218 |
+class JoinMultilinesTest(TestCase):
|
|
|
15f218 |
+ def test_empty_input(self):
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ "",
|
|
|
15f218 |
+ tools.join_multilines([])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_two_strings(self):
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ "a\nb",
|
|
|
15f218 |
+ tools.join_multilines(["a", "b"])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_strip(self):
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ "a\nb",
|
|
|
15f218 |
+ tools.join_multilines([" a\n", " b\n"])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_skip_empty(self):
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ "a\nb",
|
|
|
15f218 |
+ tools.join_multilines([" a\n", " \n", " b\n"])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_multiline(self):
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ "a\nA\nb\nB",
|
|
|
15f218 |
+ tools.join_multilines(["a\nA\n", "b\nB\n"])
|
|
|
15f218 |
+ )
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_cib_tools.py b/pcs/test/test_lib_cib_tools.py
|
|
|
15f218 |
index ffc2642..ec9c312 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_cib_tools.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_cib_tools.py
|
|
|
15f218 |
@@ -383,7 +383,7 @@ class UpgradeCibTest(TestCase):
|
|
|
15f218 |
mock_file.name = "mock_file_name"
|
|
|
15f218 |
mock_file.read.return_value = "<cib/>"
|
|
|
15f218 |
mock_named_file.return_value = mock_file
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
assert_xml_equal(
|
|
|
15f218 |
"<cib/>",
|
|
|
15f218 |
etree.tostring(
|
|
|
15f218 |
@@ -408,13 +408,15 @@ class UpgradeCibTest(TestCase):
|
|
|
15f218 |
mock_file = mock.MagicMock()
|
|
|
15f218 |
mock_file.name = "mock_file_name"
|
|
|
15f218 |
mock_named_file.return_value = mock_file
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("reason", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("some info", "some error", 1)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.upgrade_cib(etree.XML("<old_cib/>"), self.mock_runner),
|
|
|
15f218 |
(
|
|
|
15f218 |
severities.ERROR,
|
|
|
15f218 |
report_codes.CIB_UPGRADE_FAILED,
|
|
|
15f218 |
- {"reason": "reason"}
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "reason": "some error\nsome info",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
mock_named_file.assert_called_once_with("w+", suffix=".pcs")
|
|
|
15f218 |
@@ -434,7 +436,7 @@ class UpgradeCibTest(TestCase):
|
|
|
15f218 |
mock_file.name = "mock_file_name"
|
|
|
15f218 |
mock_file.read.return_value = "not xml"
|
|
|
15f218 |
mock_named_file.return_value = mock_file
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.upgrade_cib(etree.XML("<old_cib/>"), self.mock_runner),
|
|
|
15f218 |
(
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_commands_qdevice.py b/pcs/test/test_lib_commands_qdevice.py
|
|
|
15f218 |
index 10841e9..756afa8 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_commands_qdevice.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_commands_qdevice.py
|
|
|
15f218 |
@@ -345,6 +345,7 @@ class QdeviceNetSetupTest(QdeviceTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
+@mock.patch("pcs.lib.corosync.qdevice_net.qdevice_status_cluster_text")
|
|
|
15f218 |
@mock.patch("pcs.lib.external.stop_service")
|
|
|
15f218 |
@mock.patch("pcs.lib.external.disable_service")
|
|
|
15f218 |
@mock.patch("pcs.lib.commands.qdevice.qdevice_net.qdevice_destroy")
|
|
|
15f218 |
@@ -355,7 +356,11 @@ class QdeviceNetSetupTest(QdeviceTestCase):
|
|
|
15f218 |
lambda self: "mock_runner"
|
|
|
15f218 |
)
|
|
|
15f218 |
class QdeviceNetDestroyTest(QdeviceTestCase):
|
|
|
15f218 |
- def test_success(self, mock_net_destroy, mock_net_disable, mock_net_stop):
|
|
|
15f218 |
+ def test_success_not_used(
|
|
|
15f218 |
+ self, mock_net_destroy, mock_net_disable, mock_net_stop, mock_status
|
|
|
15f218 |
+ ):
|
|
|
15f218 |
+ mock_status.return_value = ""
|
|
|
15f218 |
+
|
|
|
15f218 |
lib.qdevice_destroy(self.lib_env, "net")
|
|
|
15f218 |
|
|
|
15f218 |
mock_net_stop.assert_called_once_with("mock_runner", "corosync-qnetd")
|
|
|
15f218 |
@@ -398,9 +403,85 @@ class QdeviceNetDestroyTest(QdeviceTestCase):
|
|
|
15f218 |
]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
+ def test_success_used_forced(
|
|
|
15f218 |
+ self, mock_net_destroy, mock_net_disable, mock_net_stop, mock_status
|
|
|
15f218 |
+ ):
|
|
|
15f218 |
+ mock_status.return_value = 'Cluster "a_cluster":\n'
|
|
|
15f218 |
+
|
|
|
15f218 |
+ lib.qdevice_destroy(self.lib_env, "net", proceed_if_used=True)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ mock_net_stop.assert_called_once_with("mock_runner", "corosync-qnetd")
|
|
|
15f218 |
+ mock_net_disable.assert_called_once_with(
|
|
|
15f218 |
+ "mock_runner",
|
|
|
15f218 |
+ "corosync-qnetd"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_net_destroy.assert_called_once_with()
|
|
|
15f218 |
+ assert_report_item_list_equal(
|
|
|
15f218 |
+ self.mock_reporter.report_item_list,
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.WARNING,
|
|
|
15f218 |
+ report_codes.QDEVICE_USED_BY_CLUSTERS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "clusters": ["a_cluster"],
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_STOP_STARTED,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "quorum device",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_STOP_SUCCESS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "quorum device",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_DISABLE_SUCCESS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "quorum device",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.QDEVICE_DESTROY_SUCCESS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "model": "net",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ ]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_used_not_forced(
|
|
|
15f218 |
+ self, mock_net_destroy, mock_net_disable, mock_net_stop, mock_status
|
|
|
15f218 |
+ ):
|
|
|
15f218 |
+ mock_status.return_value = 'Cluster "a_cluster":\n'
|
|
|
15f218 |
+
|
|
|
15f218 |
+ assert_raise_library_error(
|
|
|
15f218 |
+ lambda: lib.qdevice_destroy(self.lib_env, "net"),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.ERROR,
|
|
|
15f218 |
+ report_codes.QDEVICE_USED_BY_CLUSTERS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "clusters": ["a_cluster"],
|
|
|
15f218 |
+ },
|
|
|
15f218 |
+ report_codes.FORCE_QDEVICE_USED
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ mock_net_stop.assert_not_called()
|
|
|
15f218 |
+ mock_net_disable.assert_not_called()
|
|
|
15f218 |
+ mock_net_destroy.assert_not_called()
|
|
|
15f218 |
+
|
|
|
15f218 |
def test_stop_failed(
|
|
|
15f218 |
- self, mock_net_destroy, mock_net_disable, mock_net_stop
|
|
|
15f218 |
+ self, mock_net_destroy, mock_net_disable, mock_net_stop, mock_status
|
|
|
15f218 |
):
|
|
|
15f218 |
+ mock_status.return_value = ""
|
|
|
15f218 |
mock_net_stop.side_effect = StopServiceError(
|
|
|
15f218 |
"test service",
|
|
|
15f218 |
"test error"
|
|
|
15f218 |
@@ -435,8 +516,9 @@ class QdeviceNetDestroyTest(QdeviceTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_disable_failed(
|
|
|
15f218 |
- self, mock_net_destroy, mock_net_disable, mock_net_stop
|
|
|
15f218 |
+ self, mock_net_destroy, mock_net_disable, mock_net_stop, mock_status
|
|
|
15f218 |
):
|
|
|
15f218 |
+ mock_status.return_value = ""
|
|
|
15f218 |
mock_net_disable.side_effect = DisableServiceError(
|
|
|
15f218 |
"test service",
|
|
|
15f218 |
"test error"
|
|
|
15f218 |
@@ -481,8 +563,9 @@ class QdeviceNetDestroyTest(QdeviceTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_destroy_failed(
|
|
|
15f218 |
- self, mock_net_destroy, mock_net_disable, mock_net_stop
|
|
|
15f218 |
+ self, mock_net_destroy, mock_net_disable, mock_net_stop, mock_status
|
|
|
15f218 |
):
|
|
|
15f218 |
+ mock_status.return_value = ""
|
|
|
15f218 |
mock_net_destroy.side_effect = LibraryError("mock_report_item")
|
|
|
15f218 |
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
@@ -755,6 +838,7 @@ class QdeviceNetStartTest(QdeviceTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
+@mock.patch("pcs.lib.corosync.qdevice_net.qdevice_status_cluster_text")
|
|
|
15f218 |
@mock.patch("pcs.lib.external.stop_service")
|
|
|
15f218 |
@mock.patch("pcs.lib.env.is_cman_cluster", lambda self: False)
|
|
|
15f218 |
@mock.patch.object(
|
|
|
15f218 |
@@ -763,13 +847,49 @@ class QdeviceNetStartTest(QdeviceTestCase):
|
|
|
15f218 |
lambda self: "mock_runner"
|
|
|
15f218 |
)
|
|
|
15f218 |
class QdeviceNetStopTest(QdeviceTestCase):
|
|
|
15f218 |
- def test_success(self, mock_net_stop):
|
|
|
15f218 |
- lib.qdevice_stop(self.lib_env, "net")
|
|
|
15f218 |
+ def test_success_not_used(self, mock_net_stop, mock_status):
|
|
|
15f218 |
+ mock_status.return_value = ""
|
|
|
15f218 |
+
|
|
|
15f218 |
+ lib.qdevice_stop(self.lib_env, "net", proceed_if_used=False)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ mock_net_stop.assert_called_once_with("mock_runner", "corosync-qnetd")
|
|
|
15f218 |
+ assert_report_item_list_equal(
|
|
|
15f218 |
+ self.mock_reporter.report_item_list,
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_STOP_STARTED,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "quorum device",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_STOP_SUCCESS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "quorum device",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ ]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_success_used_forced(self, mock_net_stop, mock_status):
|
|
|
15f218 |
+ mock_status.return_value = 'Cluster "a_cluster":\n'
|
|
|
15f218 |
+
|
|
|
15f218 |
+ lib.qdevice_stop(self.lib_env, "net", proceed_if_used=True)
|
|
|
15f218 |
+
|
|
|
15f218 |
mock_net_stop.assert_called_once_with("mock_runner", "corosync-qnetd")
|
|
|
15f218 |
assert_report_item_list_equal(
|
|
|
15f218 |
self.mock_reporter.report_item_list,
|
|
|
15f218 |
[
|
|
|
15f218 |
(
|
|
|
15f218 |
+ severity.WARNING,
|
|
|
15f218 |
+ report_codes.QDEVICE_USED_BY_CLUSTERS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "clusters": ["a_cluster"],
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
severity.INFO,
|
|
|
15f218 |
report_codes.SERVICE_STOP_STARTED,
|
|
|
15f218 |
{
|
|
|
15f218 |
@@ -786,7 +906,28 @@ class QdeviceNetStopTest(QdeviceTestCase):
|
|
|
15f218 |
]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
- def test_failed(self, mock_net_stop):
|
|
|
15f218 |
+ def test_used_not_forced(self, mock_net_stop, mock_status):
|
|
|
15f218 |
+ mock_status.return_value = 'Cluster "a_cluster":\n'
|
|
|
15f218 |
+
|
|
|
15f218 |
+ assert_raise_library_error(
|
|
|
15f218 |
+ lambda: lib.qdevice_stop(
|
|
|
15f218 |
+ self.lib_env,
|
|
|
15f218 |
+ "net",
|
|
|
15f218 |
+ proceed_if_used=False
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.ERROR,
|
|
|
15f218 |
+ report_codes.QDEVICE_USED_BY_CLUSTERS,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "clusters": ["a_cluster"],
|
|
|
15f218 |
+ },
|
|
|
15f218 |
+ report_codes.FORCE_QDEVICE_USED
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_net_stop.assert_not_called()
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_failed(self, mock_net_stop, mock_status):
|
|
|
15f218 |
+ mock_status.return_value = ""
|
|
|
15f218 |
mock_net_stop.side_effect = StopServiceError(
|
|
|
15f218 |
"test service",
|
|
|
15f218 |
"test error"
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_commands_quorum.py b/pcs/test/test_lib_commands_quorum.py
|
|
|
15f218 |
index d7701af..1487eb4 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_commands_quorum.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_commands_quorum.py
|
|
|
15f218 |
@@ -1579,10 +1579,14 @@ class RemoveDeviceTest(TestCase, CmanMixin):
|
|
|
15f218 |
mock_remote_stop.assert_not_called()
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.env.is_cman_cluster", lambda self: False)
|
|
|
15f218 |
- def test_success(
|
|
|
15f218 |
+ @mock.patch("pcs.lib.sbd.is_sbd_installed", lambda self: True)
|
|
|
15f218 |
+ @mock.patch("pcs.lib.sbd.is_sbd_enabled", lambda self: True)
|
|
|
15f218 |
+ def test_success_3nodes_sbd(
|
|
|
15f218 |
self, mock_remote_stop, mock_remote_disable, mock_remove_net,
|
|
|
15f218 |
mock_get_corosync, mock_push_corosync
|
|
|
15f218 |
):
|
|
|
15f218 |
+ # nothing special needs to be done in regards of SBD if a cluster
|
|
|
15f218 |
+ # consists of odd number of nodes
|
|
|
15f218 |
original_conf = open(rc("corosync-3nodes-qdevice.conf")).read()
|
|
|
15f218 |
no_device_conf = open(rc("corosync-3nodes.conf")).read()
|
|
|
15f218 |
mock_get_corosync.return_value = original_conf
|
|
|
15f218 |
@@ -1619,10 +1623,106 @@ class RemoveDeviceTest(TestCase, CmanMixin):
|
|
|
15f218 |
self.assertEqual(3, len(mock_remote_stop.mock_calls))
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.env.is_cman_cluster", lambda self: False)
|
|
|
15f218 |
- def test_success_file(
|
|
|
15f218 |
+ @mock.patch("pcs.lib.sbd.is_sbd_installed", lambda self: False)
|
|
|
15f218 |
+ @mock.patch("pcs.lib.sbd.is_sbd_enabled", lambda self: False)
|
|
|
15f218 |
+ def test_success_2nodes_no_sbd(
|
|
|
15f218 |
+ self, mock_remote_stop, mock_remote_disable, mock_remove_net,
|
|
|
15f218 |
+ mock_get_corosync, mock_push_corosync
|
|
|
15f218 |
+ ):
|
|
|
15f218 |
+ # cluster consists of two nodes, two_node must be set
|
|
|
15f218 |
+ original_conf = open(rc("corosync-qdevice.conf")).read()
|
|
|
15f218 |
+ no_device_conf = open(rc("corosync.conf")).read()
|
|
|
15f218 |
+ mock_get_corosync.return_value = original_conf
|
|
|
15f218 |
+ lib_env = LibraryEnvironment(self.mock_logger, self.mock_reporter)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ lib.remove_device(lib_env)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ self.assertEqual(1, len(mock_push_corosync.mock_calls))
|
|
|
15f218 |
+ ac(
|
|
|
15f218 |
+ mock_push_corosync.mock_calls[0][1][0].config.export(),
|
|
|
15f218 |
+ no_device_conf
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ assert_report_item_list_equal(
|
|
|
15f218 |
+ self.mock_reporter.report_item_list,
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_DISABLE_STARTED,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "corosync-qdevice",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_STOP_STARTED,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "corosync-qdevice",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ ]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ self.assertEqual(1, len(mock_remove_net.mock_calls))
|
|
|
15f218 |
+ self.assertEqual(2, len(mock_remote_disable.mock_calls))
|
|
|
15f218 |
+ self.assertEqual(2, len(mock_remote_stop.mock_calls))
|
|
|
15f218 |
+
|
|
|
15f218 |
+ @mock.patch("pcs.lib.env.is_cman_cluster", lambda self: False)
|
|
|
15f218 |
+ @mock.patch("pcs.lib.sbd.is_sbd_installed", lambda self: True)
|
|
|
15f218 |
+ @mock.patch("pcs.lib.sbd.is_sbd_enabled", lambda self: True)
|
|
|
15f218 |
+ def test_success_2nodes_sbd(
|
|
|
15f218 |
self, mock_remote_stop, mock_remote_disable, mock_remove_net,
|
|
|
15f218 |
mock_get_corosync, mock_push_corosync
|
|
|
15f218 |
):
|
|
|
15f218 |
+ # cluster consists of two nodes, but SBD is in use
|
|
|
15f218 |
+ # auto tie breaker must be enabled
|
|
|
15f218 |
+ original_conf = open(rc("corosync-qdevice.conf")).read()
|
|
|
15f218 |
+ no_device_conf = open(rc("corosync.conf")).read().replace(
|
|
|
15f218 |
+ "two_node: 1",
|
|
|
15f218 |
+ "auto_tie_breaker: 1"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_get_corosync.return_value = original_conf
|
|
|
15f218 |
+ lib_env = LibraryEnvironment(self.mock_logger, self.mock_reporter)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ lib.remove_device(lib_env)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ self.assertEqual(1, len(mock_push_corosync.mock_calls))
|
|
|
15f218 |
+ ac(
|
|
|
15f218 |
+ mock_push_corosync.mock_calls[0][1][0].config.export(),
|
|
|
15f218 |
+ no_device_conf
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ assert_report_item_list_equal(
|
|
|
15f218 |
+ self.mock_reporter.report_item_list,
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.WARNING,
|
|
|
15f218 |
+ report_codes.SBD_REQUIRES_ATB,
|
|
|
15f218 |
+ {}
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_DISABLE_STARTED,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "corosync-qdevice",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severity.INFO,
|
|
|
15f218 |
+ report_codes.SERVICE_STOP_STARTED,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "service": "corosync-qdevice",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ ),
|
|
|
15f218 |
+ ]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ self.assertEqual(1, len(mock_remove_net.mock_calls))
|
|
|
15f218 |
+ self.assertEqual(2, len(mock_remote_disable.mock_calls))
|
|
|
15f218 |
+ self.assertEqual(2, len(mock_remote_stop.mock_calls))
|
|
|
15f218 |
+
|
|
|
15f218 |
+ @mock.patch("pcs.lib.sbd.atb_has_to_be_enabled")
|
|
|
15f218 |
+ @mock.patch("pcs.lib.env.is_cman_cluster", lambda self: False)
|
|
|
15f218 |
+ def test_success_file(
|
|
|
15f218 |
+ self, mock_atb_check, mock_remote_stop, mock_remote_disable,
|
|
|
15f218 |
+ mock_remove_net, mock_get_corosync, mock_push_corosync
|
|
|
15f218 |
+ ):
|
|
|
15f218 |
original_conf = open(rc("corosync-3nodes-qdevice.conf")).read()
|
|
|
15f218 |
no_device_conf = open(rc("corosync-3nodes.conf")).read()
|
|
|
15f218 |
mock_get_corosync.return_value = original_conf
|
|
|
15f218 |
@@ -1643,6 +1743,7 @@ class RemoveDeviceTest(TestCase, CmanMixin):
|
|
|
15f218 |
mock_remove_net.assert_not_called()
|
|
|
15f218 |
mock_remote_disable.assert_not_called()
|
|
|
15f218 |
mock_remote_stop.assert_not_called()
|
|
|
15f218 |
+ mock_atb_check.assert_not_called()
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.commands.quorum.qdevice_net.remote_client_destroy")
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_corosync_live.py b/pcs/test/test_lib_corosync_live.py
|
|
|
15f218 |
index 3173195..f03d78b 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_corosync_live.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_corosync_live.py
|
|
|
15f218 |
@@ -69,9 +69,10 @@ class ReloadConfigTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
cmd_retval = 0
|
|
|
15f218 |
- cmd_output = "cmd output"
|
|
|
15f218 |
+ cmd_stdout = "cmd output"
|
|
|
15f218 |
+ cmd_stderr = ""
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (cmd_output, cmd_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (cmd_stdout, cmd_stderr, cmd_retval)
|
|
|
15f218 |
|
|
|
15f218 |
lib.reload_config(mock_runner)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -81,9 +82,10 @@ class ReloadConfigTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
cmd_retval = 1
|
|
|
15f218 |
- cmd_output = "cmd output"
|
|
|
15f218 |
+ cmd_stdout = "cmd output"
|
|
|
15f218 |
+ cmd_stderr = "cmd error"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (cmd_output, cmd_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (cmd_stdout, cmd_stderr, cmd_retval)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.reload_config(mock_runner),
|
|
|
15f218 |
@@ -91,7 +93,7 @@ class ReloadConfigTest(TestCase):
|
|
|
15f218 |
severity.ERROR,
|
|
|
15f218 |
report_codes.COROSYNC_CONFIG_RELOAD_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": cmd_output,
|
|
|
15f218 |
+ "reason": "\n".join([cmd_stderr, cmd_stdout]),
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -107,7 +109,7 @@ class GetQuorumStatusTextTest(TestCase):
|
|
|
15f218 |
self.quorum_tool = "/usr/sbin/corosync-quorumtool"
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.get_quorum_status_text(self.mock_runner)
|
|
|
15f218 |
@@ -117,7 +119,7 @@ class GetQuorumStatusTextTest(TestCase):
|
|
|
15f218 |
])
|
|
|
15f218 |
|
|
|
15f218 |
def test_success_with_retval_1(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 1)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.get_quorum_status_text(self.mock_runner)
|
|
|
15f218 |
@@ -127,7 +129,7 @@ class GetQuorumStatusTextTest(TestCase):
|
|
|
15f218 |
])
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status error", 2)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("some info", "status error", 2)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_quorum_status_text(self.mock_runner),
|
|
|
15f218 |
(
|
|
|
15f218 |
@@ -152,9 +154,10 @@ class SetExpectedVotesTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
cmd_retval = 0
|
|
|
15f218 |
- cmd_output = "cmd output"
|
|
|
15f218 |
+ cmd_stdout = "cmd output"
|
|
|
15f218 |
+ cmd_stderr = ""
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (cmd_output, cmd_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (cmd_stdout, cmd_stderr, cmd_retval)
|
|
|
15f218 |
|
|
|
15f218 |
lib.set_expected_votes(mock_runner, 3)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -164,9 +167,10 @@ class SetExpectedVotesTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
cmd_retval = 1
|
|
|
15f218 |
- cmd_output = "cmd output"
|
|
|
15f218 |
+ cmd_stdout = "cmd output"
|
|
|
15f218 |
+ cmd_stderr = "cmd stderr"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (cmd_output, cmd_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (cmd_stdout, cmd_stderr, cmd_retval)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.set_expected_votes(mock_runner, 3),
|
|
|
15f218 |
@@ -174,7 +178,7 @@ class SetExpectedVotesTest(TestCase):
|
|
|
15f218 |
severity.ERROR,
|
|
|
15f218 |
report_codes.COROSYNC_QUORUM_SET_EXPECTED_VOTES_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": cmd_output,
|
|
|
15f218 |
+ "reason": cmd_stderr,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_corosync_qdevice_client.py b/pcs/test/test_lib_corosync_qdevice_client.py
|
|
|
15f218 |
index 0b5bd67..8c32c36 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_corosync_qdevice_client.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_corosync_qdevice_client.py
|
|
|
15f218 |
@@ -23,7 +23,7 @@ class GetStatusTextTest(TestCase):
|
|
|
15f218 |
self.qdevice_tool = "/usr/sbin/corosync-qdevice-tool"
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.get_status_text(self.mock_runner)
|
|
|
15f218 |
@@ -33,7 +33,7 @@ class GetStatusTextTest(TestCase):
|
|
|
15f218 |
])
|
|
|
15f218 |
|
|
|
15f218 |
def test_success_verbose(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.get_status_text(self.mock_runner, True)
|
|
|
15f218 |
@@ -43,14 +43,14 @@ class GetStatusTextTest(TestCase):
|
|
|
15f218 |
])
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("some info", "status error", 1)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_status_text(self.mock_runner),
|
|
|
15f218 |
(
|
|
|
15f218 |
severity.ERROR,
|
|
|
15f218 |
report_codes.COROSYNC_QUORUM_GET_STATUS_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": "status error",
|
|
|
15f218 |
+ "reason": "status error\nsome info",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_corosync_qdevice_net.py b/pcs/test/test_lib_corosync_qdevice_net.py
|
|
|
15f218 |
index 340a8dc..21c526b 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_corosync_qdevice_net.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_corosync_qdevice_net.py
|
|
|
15f218 |
@@ -49,7 +49,7 @@ class QdeviceSetupTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self, mock_is_dir_nonempty):
|
|
|
15f218 |
mock_is_dir_nonempty.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("initialized", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("initialized", "", 0)
|
|
|
15f218 |
|
|
|
15f218 |
lib.qdevice_setup(self.mock_runner)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -73,7 +73,7 @@ class QdeviceSetupTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_init_tool_fail(self, mock_is_dir_nonempty):
|
|
|
15f218 |
mock_is_dir_nonempty.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("test error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "test error", 1)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.qdevice_setup(self.mock_runner),
|
|
|
15f218 |
@@ -82,7 +82,7 @@ class QdeviceSetupTest(TestCase):
|
|
|
15f218 |
report_codes.QDEVICE_INITIALIZATION_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
"model": "net",
|
|
|
15f218 |
- "reason": "test error",
|
|
|
15f218 |
+ "reason": "test error\nstdout",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -126,7 +126,7 @@ class QdeviceStatusGenericTest(TestCase):
|
|
|
15f218 |
self.mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.qdevice_status_generic_text(self.mock_runner)
|
|
|
15f218 |
@@ -134,7 +134,7 @@ class QdeviceStatusGenericTest(TestCase):
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([_qnetd_tool, "-s"])
|
|
|
15f218 |
|
|
|
15f218 |
def test_success_verbose(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.qdevice_status_generic_text(self.mock_runner, True)
|
|
|
15f218 |
@@ -142,7 +142,7 @@ class QdeviceStatusGenericTest(TestCase):
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([_qnetd_tool, "-s", "-v"])
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("some info", "status error", 1)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.qdevice_status_generic_text(self.mock_runner),
|
|
|
15f218 |
(
|
|
|
15f218 |
@@ -150,7 +150,7 @@ class QdeviceStatusGenericTest(TestCase):
|
|
|
15f218 |
report_codes.QDEVICE_GET_STATUS_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
"model": "net",
|
|
|
15f218 |
- "reason": "status error",
|
|
|
15f218 |
+ "reason": "status error\nsome info",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -162,7 +162,7 @@ class QdeviceStatusClusterTest(TestCase):
|
|
|
15f218 |
self.mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.qdevice_status_cluster_text(self.mock_runner)
|
|
|
15f218 |
@@ -170,7 +170,7 @@ class QdeviceStatusClusterTest(TestCase):
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([_qnetd_tool, "-l"])
|
|
|
15f218 |
|
|
|
15f218 |
def test_success_verbose(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.qdevice_status_cluster_text(self.mock_runner, verbose=True)
|
|
|
15f218 |
@@ -178,7 +178,7 @@ class QdeviceStatusClusterTest(TestCase):
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([_qnetd_tool, "-l", "-v"])
|
|
|
15f218 |
|
|
|
15f218 |
def test_success_cluster(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.qdevice_status_cluster_text(self.mock_runner, "cluster")
|
|
|
15f218 |
@@ -188,7 +188,7 @@ class QdeviceStatusClusterTest(TestCase):
|
|
|
15f218 |
])
|
|
|
15f218 |
|
|
|
15f218 |
def test_success_cluster_verbose(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status info", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("status info", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
"status info",
|
|
|
15f218 |
lib.qdevice_status_cluster_text(self.mock_runner, "cluster", True)
|
|
|
15f218 |
@@ -198,7 +198,7 @@ class QdeviceStatusClusterTest(TestCase):
|
|
|
15f218 |
])
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("status error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("some info", "status error", 1)
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.qdevice_status_cluster_text(self.mock_runner),
|
|
|
15f218 |
(
|
|
|
15f218 |
@@ -206,13 +206,63 @@ class QdeviceStatusClusterTest(TestCase):
|
|
|
15f218 |
report_codes.QDEVICE_GET_STATUS_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
"model": "net",
|
|
|
15f218 |
- "reason": "status error",
|
|
|
15f218 |
+ "reason": "status error\nsome info",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([_qnetd_tool, "-l"])
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
+class QdeviceConnectedClustersTest(TestCase):
|
|
|
15f218 |
+ def test_empty_status(self):
|
|
|
15f218 |
+ status = ""
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ [],
|
|
|
15f218 |
+ lib.qdevice_connected_clusters(status)
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_one_cluster(self):
|
|
|
15f218 |
+ status = """\
|
|
|
15f218 |
+Cluster "rhel72":
|
|
|
15f218 |
+ Algorithm: LMS
|
|
|
15f218 |
+ Tie-breaker: Node with lowest node ID
|
|
|
15f218 |
+ Node ID 2:
|
|
|
15f218 |
+ Client address: ::ffff:192.168.122.122:59738
|
|
|
15f218 |
+ Configured node list: 1, 2
|
|
|
15f218 |
+ Membership node list: 1, 2
|
|
|
15f218 |
+ Vote: ACK (ACK)
|
|
|
15f218 |
+ Node ID 1:
|
|
|
15f218 |
+ Client address: ::ffff:192.168.122.121:43420
|
|
|
15f218 |
+ Configured node list: 1, 2
|
|
|
15f218 |
+ Membership node list: 1, 2
|
|
|
15f218 |
+ Vote: ACK (ACK)
|
|
|
15f218 |
+"""
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ ["rhel72"],
|
|
|
15f218 |
+ lib.qdevice_connected_clusters(status)
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_more_clusters(self):
|
|
|
15f218 |
+ status = """\
|
|
|
15f218 |
+Cluster "rhel72":
|
|
|
15f218 |
+Cluster "rhel73":
|
|
|
15f218 |
+"""
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ ["rhel72", "rhel73"],
|
|
|
15f218 |
+ lib.qdevice_connected_clusters(status)
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_invalid_status(self):
|
|
|
15f218 |
+ status = """\
|
|
|
15f218 |
+Cluster:
|
|
|
15f218 |
+ Cluster "rhel72":
|
|
|
15f218 |
+"""
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ [],
|
|
|
15f218 |
+ lib.qdevice_connected_clusters(status)
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+
|
|
|
15f218 |
@mock.patch("pcs.lib.corosync.qdevice_net._get_output_certificate")
|
|
|
15f218 |
@mock.patch("pcs.lib.corosync.qdevice_net._store_to_tmpfile")
|
|
|
15f218 |
class QdeviceSignCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
@@ -222,7 +272,7 @@ class QdeviceSignCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_success(self, mock_tmp_store, mock_get_cert):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("tool output", "", 0)
|
|
|
15f218 |
mock_get_cert.return_value = "new certificate".encode("utf-8")
|
|
|
15f218 |
|
|
|
15f218 |
result = lib.qdevice_sign_certificate_request(
|
|
|
15f218 |
@@ -293,7 +343,7 @@ class QdeviceSignCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_sign_error(self, mock_tmp_store, mock_get_cert):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "tool output error", 1)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.qdevice_sign_certificate_request(
|
|
|
15f218 |
@@ -305,7 +355,7 @@ class QdeviceSignCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
severity.ERROR,
|
|
|
15f218 |
report_codes.QDEVICE_CERTIFICATE_SIGN_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": "tool output error",
|
|
|
15f218 |
+ "reason": "tool output error\nstdout",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -326,7 +376,7 @@ class QdeviceSignCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_output_read_error(self, mock_tmp_store, mock_get_cert):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("tool output", "", 0)
|
|
|
15f218 |
mock_get_cert.side_effect = LibraryError
|
|
|
15f218 |
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
@@ -399,7 +449,7 @@ class ClientSetupTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.corosync.qdevice_net.client_destroy")
|
|
|
15f218 |
def test_success(self, mock_destroy):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("tool output", "", 0)
|
|
|
15f218 |
|
|
|
15f218 |
lib.client_setup(self.mock_runner, "certificate data".encode("utf-8"))
|
|
|
15f218 |
|
|
|
15f218 |
@@ -414,7 +464,7 @@ class ClientSetupTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.corosync.qdevice_net.client_destroy")
|
|
|
15f218 |
def test_init_error(self, mock_destroy):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "tool output error", 1)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.client_setup(
|
|
|
15f218 |
@@ -426,7 +476,7 @@ class ClientSetupTest(TestCase):
|
|
|
15f218 |
report_codes.QDEVICE_INITIALIZATION_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
"model": "net",
|
|
|
15f218 |
- "reason": "tool output error",
|
|
|
15f218 |
+ "reason": "tool output error\nstdout",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -448,7 +498,7 @@ class ClientGenerateCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
lambda: True
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_success(self, mock_get_cert):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("tool output", "", 0)
|
|
|
15f218 |
mock_get_cert.return_value = "new certificate".encode("utf-8")
|
|
|
15f218 |
|
|
|
15f218 |
result = lib.client_generate_certificate_request(
|
|
|
15f218 |
@@ -492,7 +542,7 @@ class ClientGenerateCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
lambda: True
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_tool_error(self, mock_get_cert):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "tool output error", 1)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.client_generate_certificate_request(
|
|
|
15f218 |
@@ -504,7 +554,7 @@ class ClientGenerateCertificateRequestTest(CertificateTestCase):
|
|
|
15f218 |
report_codes.QDEVICE_INITIALIZATION_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
"model": "net",
|
|
|
15f218 |
- "reason": "tool output error",
|
|
|
15f218 |
+ "reason": "tool output error\nstdout",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -523,7 +573,7 @@ class ClientCertRequestToPk12Test(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_success(self, mock_tmp_store, mock_get_cert):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("tool output", "", 0)
|
|
|
15f218 |
mock_get_cert.return_value = "new certificate".encode("utf-8")
|
|
|
15f218 |
|
|
|
15f218 |
result = lib.client_cert_request_to_pk12(
|
|
|
15f218 |
@@ -594,7 +644,7 @@ class ClientCertRequestToPk12Test(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_transform_error(self, mock_tmp_store, mock_get_cert):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "tool output error", 1)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.client_cert_request_to_pk12(
|
|
|
15f218 |
@@ -605,7 +655,7 @@ class ClientCertRequestToPk12Test(CertificateTestCase):
|
|
|
15f218 |
severity.ERROR,
|
|
|
15f218 |
report_codes.QDEVICE_CERTIFICATE_IMPORT_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": "tool output error",
|
|
|
15f218 |
+ "reason": "tool output error\nstdout",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -625,7 +675,7 @@ class ClientCertRequestToPk12Test(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_output_read_error(self, mock_tmp_store, mock_get_cert):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("tool output", "", 0)
|
|
|
15f218 |
mock_get_cert.side_effect = LibraryError
|
|
|
15f218 |
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
@@ -657,7 +707,7 @@ class ClientImportCertificateAndKeyTest(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_success(self, mock_tmp_store):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("tool output", "", 0)
|
|
|
15f218 |
|
|
|
15f218 |
lib.client_import_certificate_and_key(
|
|
|
15f218 |
self.mock_runner,
|
|
|
15f218 |
@@ -721,7 +771,7 @@ class ClientImportCertificateAndKeyTest(CertificateTestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
def test_import_error(self, mock_tmp_store):
|
|
|
15f218 |
mock_tmp_store.return_value = self.mock_tmpfile
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("tool output error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "tool output error", 1)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.client_import_certificate_and_key(
|
|
|
15f218 |
@@ -732,7 +782,7 @@ class ClientImportCertificateAndKeyTest(CertificateTestCase):
|
|
|
15f218 |
severity.ERROR,
|
|
|
15f218 |
report_codes.QDEVICE_CERTIFICATE_IMPORT_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "reason": "tool output error",
|
|
|
15f218 |
+ "reason": "tool output error\nstdout",
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_external.py b/pcs/test/test_lib_external.py
|
|
|
15f218 |
index aafbe85..d37747a 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_external.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_external.py
|
|
|
15f218 |
@@ -57,19 +57,23 @@ class CommandRunnerTest(TestCase):
|
|
|
15f218 |
self.assertEqual(filtered_kwargs, kwargs)
|
|
|
15f218 |
|
|
|
15f218 |
def test_basic(self, mock_popen):
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected stdout"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
expected_retval = 123
|
|
|
15f218 |
command = ["a_command"]
|
|
|
15f218 |
command_str = "a_command"
|
|
|
15f218 |
mock_process = mock.MagicMock(spec_set=["communicate", "returncode"])
|
|
|
15f218 |
- mock_process.communicate.return_value = (expected_output, "dummy")
|
|
|
15f218 |
+ mock_process.communicate.return_value = (
|
|
|
15f218 |
+ expected_stdout, expected_stderr
|
|
|
15f218 |
+ )
|
|
|
15f218 |
mock_process.returncode = expected_retval
|
|
|
15f218 |
mock_popen.return_value = mock_process
|
|
|
15f218 |
|
|
|
15f218 |
runner = lib.CommandRunner(self.mock_logger, self.mock_reporter)
|
|
|
15f218 |
- real_output, real_retval = runner.run(command)
|
|
|
15f218 |
+ real_stdout, real_stderr, real_retval = runner.run(command)
|
|
|
15f218 |
|
|
|
15f218 |
- self.assertEqual(real_output, expected_output)
|
|
|
15f218 |
+ self.assertEqual(real_stdout, expected_stdout)
|
|
|
15f218 |
+ self.assertEqual(real_stderr, expected_stderr)
|
|
|
15f218 |
self.assertEqual(real_retval, expected_retval)
|
|
|
15f218 |
mock_process.communicate.assert_called_once_with(None)
|
|
|
15f218 |
self.assert_popen_called_with(
|
|
|
15f218 |
@@ -82,9 +86,14 @@ class CommandRunnerTest(TestCase):
|
|
|
15f218 |
mock.call("""\
|
|
|
15f218 |
Finished running: {0}
|
|
|
15f218 |
Return value: {1}
|
|
|
15f218 |
---Debug Output Start--
|
|
|
15f218 |
+--Debug Stdout Start--
|
|
|
15f218 |
{2}
|
|
|
15f218 |
---Debug Output End--""".format(command_str, expected_retval, expected_output))
|
|
|
15f218 |
+--Debug Stdout End--
|
|
|
15f218 |
+--Debug Stderr Start--
|
|
|
15f218 |
+{3}
|
|
|
15f218 |
+--Debug Stderr End--""".format(
|
|
|
15f218 |
+ command_str, expected_retval, expected_stdout, expected_stderr
|
|
|
15f218 |
+ ))
|
|
|
15f218 |
]
|
|
|
15f218 |
self.assertEqual(self.mock_logger.debug.call_count, len(logger_calls))
|
|
|
15f218 |
self.mock_logger.debug.assert_has_calls(logger_calls)
|
|
|
15f218 |
@@ -105,19 +114,23 @@ Return value: {1}
|
|
|
15f218 |
{
|
|
|
15f218 |
"command": command_str,
|
|
|
15f218 |
"return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_output,
|
|
|
15f218 |
+ "stdout": expected_stdout,
|
|
|
15f218 |
+ "stderr": expected_stderr,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_env(self, mock_popen):
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
expected_retval = 123
|
|
|
15f218 |
command = ["a_command"]
|
|
|
15f218 |
command_str = "a_command"
|
|
|
15f218 |
mock_process = mock.MagicMock(spec_set=["communicate", "returncode"])
|
|
|
15f218 |
- mock_process.communicate.return_value = (expected_output, "dummy")
|
|
|
15f218 |
+ mock_process.communicate.return_value = (
|
|
|
15f218 |
+ expected_stdout, expected_stderr
|
|
|
15f218 |
+ )
|
|
|
15f218 |
mock_process.returncode = expected_retval
|
|
|
15f218 |
mock_popen.return_value = mock_process
|
|
|
15f218 |
|
|
|
15f218 |
@@ -126,12 +139,13 @@ Return value: {1}
|
|
|
15f218 |
self.mock_reporter,
|
|
|
15f218 |
{"a": "a", "b": "b"}
|
|
|
15f218 |
)
|
|
|
15f218 |
- real_output, real_retval = runner.run(
|
|
|
15f218 |
+ real_stdout, real_stderr, real_retval = runner.run(
|
|
|
15f218 |
command,
|
|
|
15f218 |
env_extend={"b": "B", "c": "C"}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
- self.assertEqual(real_output, expected_output)
|
|
|
15f218 |
+ self.assertEqual(real_stdout, expected_stdout)
|
|
|
15f218 |
+ self.assertEqual(real_stderr, expected_stderr)
|
|
|
15f218 |
self.assertEqual(real_retval, expected_retval)
|
|
|
15f218 |
mock_process.communicate.assert_called_once_with(None)
|
|
|
15f218 |
self.assert_popen_called_with(
|
|
|
15f218 |
@@ -144,9 +158,14 @@ Return value: {1}
|
|
|
15f218 |
mock.call("""\
|
|
|
15f218 |
Finished running: {0}
|
|
|
15f218 |
Return value: {1}
|
|
|
15f218 |
---Debug Output Start--
|
|
|
15f218 |
+--Debug Stdout Start--
|
|
|
15f218 |
{2}
|
|
|
15f218 |
---Debug Output End--""".format(command_str, expected_retval, expected_output))
|
|
|
15f218 |
+--Debug Stdout End--
|
|
|
15f218 |
+--Debug Stderr Start--
|
|
|
15f218 |
+{3}
|
|
|
15f218 |
+--Debug Stderr End--""".format(
|
|
|
15f218 |
+ command_str, expected_retval, expected_stdout, expected_stderr
|
|
|
15f218 |
+ ))
|
|
|
15f218 |
]
|
|
|
15f218 |
self.assertEqual(self.mock_logger.debug.call_count, len(logger_calls))
|
|
|
15f218 |
self.mock_logger.debug.assert_has_calls(logger_calls)
|
|
|
15f218 |
@@ -167,27 +186,34 @@ Return value: {1}
|
|
|
15f218 |
{
|
|
|
15f218 |
"command": command_str,
|
|
|
15f218 |
"return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_output,
|
|
|
15f218 |
+ "stdout": expected_stdout,
|
|
|
15f218 |
+ "stderr": expected_stderr,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_stdin(self, mock_popen):
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
expected_retval = 123
|
|
|
15f218 |
command = ["a_command"]
|
|
|
15f218 |
command_str = "a_command"
|
|
|
15f218 |
stdin = "stdin string"
|
|
|
15f218 |
mock_process = mock.MagicMock(spec_set=["communicate", "returncode"])
|
|
|
15f218 |
- mock_process.communicate.return_value = (expected_output, "dummy")
|
|
|
15f218 |
+ mock_process.communicate.return_value = (
|
|
|
15f218 |
+ expected_stdout, expected_stderr
|
|
|
15f218 |
+ )
|
|
|
15f218 |
mock_process.returncode = expected_retval
|
|
|
15f218 |
mock_popen.return_value = mock_process
|
|
|
15f218 |
|
|
|
15f218 |
runner = lib.CommandRunner(self.mock_logger, self.mock_reporter)
|
|
|
15f218 |
- real_output, real_retval = runner.run(command, stdin_string=stdin)
|
|
|
15f218 |
+ real_stdout, real_stderr, real_retval = runner.run(
|
|
|
15f218 |
+ command, stdin_string=stdin
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
- self.assertEqual(real_output, expected_output)
|
|
|
15f218 |
+ self.assertEqual(real_stdout, expected_stdout)
|
|
|
15f218 |
+ self.assertEqual(real_stderr, expected_stderr)
|
|
|
15f218 |
self.assertEqual(real_retval, expected_retval)
|
|
|
15f218 |
mock_process.communicate.assert_called_once_with(stdin)
|
|
|
15f218 |
self.assert_popen_called_with(
|
|
|
15f218 |
@@ -204,9 +230,14 @@ Running: {0}
|
|
|
15f218 |
mock.call("""\
|
|
|
15f218 |
Finished running: {0}
|
|
|
15f218 |
Return value: {1}
|
|
|
15f218 |
---Debug Output Start--
|
|
|
15f218 |
+--Debug Stdout Start--
|
|
|
15f218 |
{2}
|
|
|
15f218 |
---Debug Output End--""".format(command_str, expected_retval, expected_output))
|
|
|
15f218 |
+--Debug Stdout End--
|
|
|
15f218 |
+--Debug Stderr Start--
|
|
|
15f218 |
+{3}
|
|
|
15f218 |
+--Debug Stderr End--""".format(
|
|
|
15f218 |
+ command_str, expected_retval, expected_stdout, expected_stderr
|
|
|
15f218 |
+ ))
|
|
|
15f218 |
]
|
|
|
15f218 |
self.assertEqual(self.mock_logger.debug.call_count, len(logger_calls))
|
|
|
15f218 |
self.mock_logger.debug.assert_has_calls(logger_calls)
|
|
|
15f218 |
@@ -227,7 +258,8 @@ Return value: {1}
|
|
|
15f218 |
{
|
|
|
15f218 |
"command": command_str,
|
|
|
15f218 |
"return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_output,
|
|
|
15f218 |
+ "stdout": expected_stdout,
|
|
|
15f218 |
+ "stderr": expected_stderr,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
]
|
|
|
15f218 |
@@ -957,7 +989,7 @@ class ParallelCommunicationHelperTest(TestCase):
|
|
|
15f218 |
class IsCmanClusterTest(TestCase):
|
|
|
15f218 |
def template_test(self, is_cman, corosync_output, corosync_retval=0):
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=lib.CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (corosync_output, corosync_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (corosync_output, "", corosync_retval)
|
|
|
15f218 |
self.assertEqual(is_cman, lib.is_cman_cluster(mock_runner))
|
|
|
15f218 |
mock_runner.run.assert_called_once_with([
|
|
|
15f218 |
os.path.join(settings.corosync_binaries, "corosync"),
|
|
|
15f218 |
@@ -1021,7 +1053,7 @@ class DisableServiceTest(TestCase):
|
|
|
15f218 |
def test_systemctl(self, mock_is_installed, mock_systemctl):
|
|
|
15f218 |
mock_is_installed.return_value = True
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Removed symlink", 0)
|
|
|
15f218 |
lib.disable_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "disable", self.service + ".service"]
|
|
|
15f218 |
@@ -1030,7 +1062,7 @@ class DisableServiceTest(TestCase):
|
|
|
15f218 |
def test_systemctl_failed(self, mock_is_installed, mock_systemctl):
|
|
|
15f218 |
mock_is_installed.return_value = True
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Failed", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.DisableServiceError,
|
|
|
15f218 |
lambda: lib.disable_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1042,7 +1074,7 @@ class DisableServiceTest(TestCase):
|
|
|
15f218 |
def test_not_systemctl(self, mock_is_installed, mock_systemctl):
|
|
|
15f218 |
mock_is_installed.return_value = True
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.disable_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["chkconfig", self.service, "off"]
|
|
|
15f218 |
@@ -1051,7 +1083,7 @@ class DisableServiceTest(TestCase):
|
|
|
15f218 |
def test_not_systemctl_failed(self, mock_is_installed, mock_systemctl):
|
|
|
15f218 |
mock_is_installed.return_value = True
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "error", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.DisableServiceError,
|
|
|
15f218 |
lambda: lib.disable_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1079,7 +1111,7 @@ class DisableServiceTest(TestCase):
|
|
|
15f218 |
def test_instance_systemctl(self, mock_is_installed, mock_systemctl):
|
|
|
15f218 |
mock_is_installed.return_value = True
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Removed symlink", 0)
|
|
|
15f218 |
lib.disable_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([
|
|
|
15f218 |
"systemctl",
|
|
|
15f218 |
@@ -1090,7 +1122,7 @@ class DisableServiceTest(TestCase):
|
|
|
15f218 |
def test_instance_not_systemctl(self, mock_is_installed, mock_systemctl):
|
|
|
15f218 |
mock_is_installed.return_value = True
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.disable_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["chkconfig", self.service, "off"]
|
|
|
15f218 |
@@ -1104,7 +1136,7 @@ class EnableServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Created symlink", 0)
|
|
|
15f218 |
lib.enable_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "enable", self.service + ".service"]
|
|
|
15f218 |
@@ -1112,7 +1144,7 @@ class EnableServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl_failed(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Failed", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.EnableServiceError,
|
|
|
15f218 |
lambda: lib.enable_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1123,7 +1155,7 @@ class EnableServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.enable_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["chkconfig", self.service, "on"]
|
|
|
15f218 |
@@ -1131,7 +1163,7 @@ class EnableServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl_failed(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "error", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.EnableServiceError,
|
|
|
15f218 |
lambda: lib.enable_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1142,7 +1174,7 @@ class EnableServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_instance_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Created symlink", 0)
|
|
|
15f218 |
lib.enable_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([
|
|
|
15f218 |
"systemctl",
|
|
|
15f218 |
@@ -1152,7 +1184,7 @@ class EnableServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_instance_not_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.enable_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["chkconfig", self.service, "on"]
|
|
|
15f218 |
@@ -1167,7 +1199,7 @@ class StartServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.start_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "start", self.service + ".service"]
|
|
|
15f218 |
@@ -1175,7 +1207,7 @@ class StartServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl_failed(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Failed", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.StartServiceError,
|
|
|
15f218 |
lambda: lib.start_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1186,7 +1218,7 @@ class StartServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("Starting...", "", 0)
|
|
|
15f218 |
lib.start_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["service", self.service, "start"]
|
|
|
15f218 |
@@ -1194,7 +1226,7 @@ class StartServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl_failed(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "unrecognized", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.StartServiceError,
|
|
|
15f218 |
lambda: lib.start_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1205,7 +1237,7 @@ class StartServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_instance_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.start_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([
|
|
|
15f218 |
"systemctl", "start", "{0}@{1}.service".format(self.service, "test")
|
|
|
15f218 |
@@ -1213,7 +1245,7 @@ class StartServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_instance_not_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("Starting...", "", 0)
|
|
|
15f218 |
lib.start_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["service", self.service, "start"]
|
|
|
15f218 |
@@ -1228,7 +1260,7 @@ class StopServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.stop_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "stop", self.service + ".service"]
|
|
|
15f218 |
@@ -1236,7 +1268,7 @@ class StopServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl_failed(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "Failed", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.StopServiceError,
|
|
|
15f218 |
lambda: lib.stop_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1247,7 +1279,7 @@ class StopServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("Stopping...", "", 0)
|
|
|
15f218 |
lib.stop_service(self.mock_runner, self.service)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["service", self.service, "stop"]
|
|
|
15f218 |
@@ -1255,7 +1287,7 @@ class StopServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl_failed(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "unrecognized", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.StopServiceError,
|
|
|
15f218 |
lambda: lib.stop_service(self.mock_runner, self.service)
|
|
|
15f218 |
@@ -1266,7 +1298,7 @@ class StopServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_instance_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.stop_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with([
|
|
|
15f218 |
"systemctl", "stop", "{0}@{1}.service".format(self.service, "test")
|
|
|
15f218 |
@@ -1274,7 +1306,7 @@ class StopServiceTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_instance_not_systemctl(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("Stopping...", "", 0)
|
|
|
15f218 |
lib.stop_service(self.mock_runner, self.service, instance="test")
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["service", self.service, "stop"]
|
|
|
15f218 |
@@ -1287,14 +1319,14 @@ class KillServicesTest(TestCase):
|
|
|
15f218 |
self.services = ["service1", "service2"]
|
|
|
15f218 |
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
lib.kill_services(self.mock_runner, self.services)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["killall", "--quiet", "--signal", "9", "--"] + self.services
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_failed(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("error", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "error", 1)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib.KillServicesError,
|
|
|
15f218 |
lambda: lib.kill_services(self.mock_runner, self.services)
|
|
|
15f218 |
@@ -1304,7 +1336,7 @@ class KillServicesTest(TestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_service_not_running(self):
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 1)
|
|
|
15f218 |
lib.kill_services(self.mock_runner, self.services)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["killall", "--quiet", "--signal", "9", "--"] + self.services
|
|
|
15f218 |
@@ -1348,7 +1380,7 @@ class IsServiceEnabledTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl_enabled(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("enabled\n", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("enabled\n", "", 0)
|
|
|
15f218 |
self.assertTrue(lib.is_service_enabled(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "is-enabled", self.service + ".service"]
|
|
|
15f218 |
@@ -1356,7 +1388,7 @@ class IsServiceEnabledTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl_disabled(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("disabled\n", 2)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("disabled\n", "", 2)
|
|
|
15f218 |
self.assertFalse(lib.is_service_enabled(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "is-enabled", self.service + ".service"]
|
|
|
15f218 |
@@ -1364,7 +1396,7 @@ class IsServiceEnabledTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl_enabled(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 0)
|
|
|
15f218 |
self.assertTrue(lib.is_service_enabled(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["chkconfig", self.service]
|
|
|
15f218 |
@@ -1372,7 +1404,7 @@ class IsServiceEnabledTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl_disabled(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 3)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", "", 3)
|
|
|
15f218 |
self.assertFalse(lib.is_service_enabled(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["chkconfig", self.service]
|
|
|
15f218 |
@@ -1387,7 +1419,7 @@ class IsServiceRunningTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl_running(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("active", "", 0)
|
|
|
15f218 |
self.assertTrue(lib.is_service_running(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "is-active", self.service + ".service"]
|
|
|
15f218 |
@@ -1395,7 +1427,7 @@ class IsServiceRunningTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemctl_not_running(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 2)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("inactive", "", 2)
|
|
|
15f218 |
self.assertFalse(lib.is_service_running(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["systemctl", "is-active", self.service + ".service"]
|
|
|
15f218 |
@@ -1403,7 +1435,7 @@ class IsServiceRunningTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl_running(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("is running", "", 0)
|
|
|
15f218 |
self.assertTrue(lib.is_service_running(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["service", self.service, "status"]
|
|
|
15f218 |
@@ -1411,7 +1443,7 @@ class IsServiceRunningTest(TestCase):
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemctl_not_running(self, mock_systemctl):
|
|
|
15f218 |
mock_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 3)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("is stopped", "", 3)
|
|
|
15f218 |
self.assertFalse(lib.is_service_running(self.mock_runner, self.service))
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
["service", self.service, "status"]
|
|
|
15f218 |
@@ -1484,7 +1516,7 @@ sbd.service enabled
|
|
|
15f218 |
pacemaker.service enabled
|
|
|
15f218 |
|
|
|
15f218 |
3 unit files listed.
|
|
|
15f218 |
-""", 0)
|
|
|
15f218 |
+""", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
lib.get_systemd_services(self.mock_runner),
|
|
|
15f218 |
["pcsd", "sbd", "pacemaker"]
|
|
|
15f218 |
@@ -1496,7 +1528,7 @@ pacemaker.service enabled
|
|
|
15f218 |
|
|
|
15f218 |
def test_failed(self, mock_is_systemctl):
|
|
|
15f218 |
mock_is_systemctl.return_value = True
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("failed", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "failed", 1)
|
|
|
15f218 |
self.assertEqual(lib.get_systemd_services(self.mock_runner), [])
|
|
|
15f218 |
self.assertEqual(mock_is_systemctl.call_count, 1)
|
|
|
15f218 |
self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
@@ -1505,10 +1537,9 @@ pacemaker.service enabled
|
|
|
15f218 |
|
|
|
15f218 |
def test_not_systemd(self, mock_is_systemctl):
|
|
|
15f218 |
mock_is_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
self.assertEqual(lib.get_systemd_services(self.mock_runner), [])
|
|
|
15f218 |
- self.assertEqual(mock_is_systemctl.call_count, 1)
|
|
|
15f218 |
- self.assertEqual(self.mock_runner.call_count, 0)
|
|
|
15f218 |
+ mock_is_systemctl.assert_called_once_with()
|
|
|
15f218 |
+ self.mock_runner.assert_not_called()
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.external.is_systemctl")
|
|
|
15f218 |
@@ -1522,24 +1553,20 @@ class GetNonSystemdServicesTest(TestCase):
|
|
|
15f218 |
pcsd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
|
|
|
15f218 |
sbd 0:off 1:on 2:on 3:on 4:on 5:on 6:off
|
|
|
15f218 |
pacemaker 0:off 1:off 2:off 3:off 4:off 5:off 6:off
|
|
|
15f218 |
-""", 0)
|
|
|
15f218 |
+""", "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
lib.get_non_systemd_services(self.mock_runner),
|
|
|
15f218 |
["pcsd", "sbd", "pacemaker"]
|
|
|
15f218 |
)
|
|
|
15f218 |
self.assertEqual(mock_is_systemctl.call_count, 1)
|
|
|
15f218 |
- self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- ["chkconfig"], ignore_stderr=True
|
|
|
15f218 |
- )
|
|
|
15f218 |
+ self.mock_runner.run.assert_called_once_with(["chkconfig"])
|
|
|
15f218 |
|
|
|
15f218 |
def test_failed(self, mock_is_systemctl):
|
|
|
15f218 |
mock_is_systemctl.return_value = False
|
|
|
15f218 |
- self.mock_runner.run.return_value = ("failed", 1)
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("stdout", "failed", 1)
|
|
|
15f218 |
self.assertEqual(lib.get_non_systemd_services(self.mock_runner), [])
|
|
|
15f218 |
self.assertEqual(mock_is_systemctl.call_count, 1)
|
|
|
15f218 |
- self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- ["chkconfig"], ignore_stderr=True
|
|
|
15f218 |
- )
|
|
|
15f218 |
+ self.mock_runner.run.assert_called_once_with(["chkconfig"])
|
|
|
15f218 |
|
|
|
15f218 |
def test_systemd(self, mock_is_systemctl):
|
|
|
15f218 |
mock_is_systemctl.return_value = True
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_pacemaker.py b/pcs/test/test_lib_pacemaker.py
|
|
|
15f218 |
index c475db6..7ca7b77 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_pacemaker.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_pacemaker.py
|
|
|
15f218 |
@@ -64,21 +64,31 @@ class LibraryPacemakerNodeStatusTest(LibraryPacemakerTest):
|
|
|
15f218 |
|
|
|
15f218 |
class GetClusterStatusXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
- expected_xml = "<xml />"
|
|
|
15f218 |
+ expected_stdout = "<xml />"
|
|
|
15f218 |
+ expected_stderr = ""
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_xml, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
real_xml = lib.get_cluster_status_xml(mock_runner)
|
|
|
15f218 |
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(self.crm_mon_cmd())
|
|
|
15f218 |
- self.assertEqual(expected_xml, real_xml)
|
|
|
15f218 |
+ self.assertEqual(expected_stdout, real_xml)
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_cluster_status_xml(mock_runner),
|
|
|
15f218 |
@@ -86,8 +96,7 @@ class GetClusterStatusXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.CRM_MON_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -96,23 +105,33 @@ class GetClusterStatusXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
|
|
|
15f218 |
class GetCibXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
- expected_xml = "<xml />"
|
|
|
15f218 |
+ expected_stdout = "<xml />"
|
|
|
15f218 |
+ expected_stderr = ""
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_xml, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
real_xml = lib.get_cib_xml(mock_runner)
|
|
|
15f218 |
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
[self.path("cibadmin"), "--local", "--query"]
|
|
|
15f218 |
)
|
|
|
15f218 |
- self.assertEqual(expected_xml, real_xml)
|
|
|
15f218 |
+ self.assertEqual(expected_stdout, real_xml)
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_cib_xml(mock_runner),
|
|
|
15f218 |
@@ -120,8 +139,7 @@ class GetCibXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.CIB_LOAD_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -131,11 +149,16 @@ class GetCibXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_success_scope(self):
|
|
|
15f218 |
- expected_xml = "<xml />"
|
|
|
15f218 |
+ expected_stdout = "<xml />"
|
|
|
15f218 |
+ expected_stderr = ""
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
scope = "test_scope"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_xml, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
real_xml = lib.get_cib_xml(mock_runner, scope)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -145,14 +168,19 @@ class GetCibXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
"--local", "--query", "--scope={0}".format(scope)
|
|
|
15f218 |
]
|
|
|
15f218 |
)
|
|
|
15f218 |
- self.assertEqual(expected_xml, real_xml)
|
|
|
15f218 |
+ self.assertEqual(expected_stdout, real_xml)
|
|
|
15f218 |
|
|
|
15f218 |
def test_scope_error(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 6
|
|
|
15f218 |
scope = "test_scope"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_cib_xml(mock_runner, scope=scope),
|
|
|
15f218 |
@@ -161,8 +189,7 @@ class GetCibXmlTest(LibraryPacemakerTest):
|
|
|
15f218 |
report_codes.CIB_LOAD_ERROR_SCOPE_MISSING,
|
|
|
15f218 |
{
|
|
|
15f218 |
"scope": scope,
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -194,10 +221,15 @@ class GetCibTest(LibraryPacemakerTest):
|
|
|
15f218 |
class ReplaceCibConfigurationTest(LibraryPacemakerTest):
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
xml = "<xml/>"
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = ""
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
lib.replace_cib_configuration(
|
|
|
15f218 |
mock_runner,
|
|
|
15f218 |
@@ -214,10 +246,15 @@ class ReplaceCibConfigurationTest(LibraryPacemakerTest):
|
|
|
15f218 |
|
|
|
15f218 |
def test_cib_upgraded(self):
|
|
|
15f218 |
xml = "<xml/>"
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = ""
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
lib.replace_cib_configuration(
|
|
|
15f218 |
mock_runner, XmlManipulation.from_str(xml).tree, True
|
|
|
15f218 |
@@ -230,10 +267,15 @@ class ReplaceCibConfigurationTest(LibraryPacemakerTest):
|
|
|
15f218 |
|
|
|
15f218 |
def test_error(self):
|
|
|
15f218 |
xml = "<xml/>"
|
|
|
15f218 |
- expected_error = "expected error"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.replace_cib_configuration(
|
|
|
15f218 |
@@ -245,8 +287,8 @@ class ReplaceCibConfigurationTest(LibraryPacemakerTest):
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.CIB_PUSH_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr,
|
|
|
15f218 |
+ "pushed_cib": expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -261,10 +303,15 @@ class ReplaceCibConfigurationTest(LibraryPacemakerTest):
|
|
|
15f218 |
|
|
|
15f218 |
class GetLocalNodeStatusTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
def test_offline(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
{"offline": True},
|
|
|
15f218 |
@@ -273,10 +320,15 @@ class GetLocalNodeStatusTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(self.crm_mon_cmd())
|
|
|
15f218 |
|
|
|
15f218 |
def test_invalid_status(self):
|
|
|
15f218 |
- expected_xml = "some error"
|
|
|
15f218 |
+ expected_stdout = "invalid xml"
|
|
|
15f218 |
+ expected_stderr = ""
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_xml, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.get_local_node_status(mock_runner),
|
|
|
15f218 |
@@ -310,9 +362,9 @@ class GetLocalNodeStatusTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
),
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (str(self.status), 0),
|
|
|
15f218 |
- (node_id, 0),
|
|
|
15f218 |
- (node_name, 0)
|
|
|
15f218 |
+ (str(self.status), "", 0),
|
|
|
15f218 |
+ (node_id, "", 0),
|
|
|
15f218 |
+ (node_name, "", 0)
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -339,9 +391,9 @@ class GetLocalNodeStatusTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
),
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (str(self.status), 0),
|
|
|
15f218 |
- (node_id, 0),
|
|
|
15f218 |
- (node_name_bad, 0)
|
|
|
15f218 |
+ (str(self.status), "", 0),
|
|
|
15f218 |
+ (node_id, "", 0),
|
|
|
15f218 |
+ (node_name_bad, "", 0)
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -370,8 +422,8 @@ class GetLocalNodeStatusTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
mock.call([self.path("crm_node"), "--cluster-id"]),
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (str(self.status), 0),
|
|
|
15f218 |
- ("some error", 1),
|
|
|
15f218 |
+ (str(self.status), "", 0),
|
|
|
15f218 |
+ ("", "some error", 1),
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -403,9 +455,9 @@ class GetLocalNodeStatusTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
),
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (str(self.status), 0),
|
|
|
15f218 |
- (node_id, 0),
|
|
|
15f218 |
- ("some error", 1),
|
|
|
15f218 |
+ (str(self.status), "", 0),
|
|
|
15f218 |
+ (node_id, "", 0),
|
|
|
15f218 |
+ ("", "some error", 1),
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -437,9 +489,9 @@ class GetLocalNodeStatusTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
),
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (str(self.status), 0),
|
|
|
15f218 |
- (node_id, 0),
|
|
|
15f218 |
- ("(null)", 0),
|
|
|
15f218 |
+ (str(self.status), "", 0),
|
|
|
15f218 |
+ (node_id, "", 0),
|
|
|
15f218 |
+ ("(null)", "", 0),
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -465,15 +517,16 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
return str(XmlManipulation(doc))
|
|
|
15f218 |
|
|
|
15f218 |
def test_basic(self):
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
call_list = [
|
|
|
15f218 |
mock.call(self.crm_mon_cmd()),
|
|
|
15f218 |
mock.call([self.path("crm_resource"), "--cleanup"]),
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (self.fixture_status_xml(1, 1), 0),
|
|
|
15f218 |
- (expected_output, 0),
|
|
|
15f218 |
+ (self.fixture_status_xml(1, 1), "", 0),
|
|
|
15f218 |
+ (expected_stdout, expected_stderr, 0),
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -482,11 +535,18 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
self.assertEqual(len(return_value_list), len(call_list))
|
|
|
15f218 |
self.assertEqual(len(return_value_list), mock_runner.run.call_count)
|
|
|
15f218 |
mock_runner.run.assert_has_calls(call_list)
|
|
|
15f218 |
- self.assertEqual(expected_output, real_output)
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ expected_stdout + "\n" + expected_stderr,
|
|
|
15f218 |
+ real_output
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
def test_threshold_exceeded(self):
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (self.fixture_status_xml(1000, 1000), 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ self.fixture_status_xml(1000, 1000),
|
|
|
15f218 |
+ "",
|
|
|
15f218 |
+ 0
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.resource_cleanup(mock_runner),
|
|
|
15f218 |
@@ -501,49 +561,62 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(self.crm_mon_cmd())
|
|
|
15f218 |
|
|
|
15f218 |
def test_forced(self):
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (expected_stdout, expected_stderr, 0)
|
|
|
15f218 |
|
|
|
15f218 |
real_output = lib.resource_cleanup(mock_runner, force=True)
|
|
|
15f218 |
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
[self.path("crm_resource"), "--cleanup"]
|
|
|
15f218 |
)
|
|
|
15f218 |
- self.assertEqual(expected_output, real_output)
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ expected_stdout + "\n" + expected_stderr,
|
|
|
15f218 |
+ real_output
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
def test_resource(self):
|
|
|
15f218 |
resource = "test_resource"
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (expected_stdout, expected_stderr, 0)
|
|
|
15f218 |
|
|
|
15f218 |
real_output = lib.resource_cleanup(mock_runner, resource=resource)
|
|
|
15f218 |
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
[self.path("crm_resource"), "--cleanup", "--resource", resource]
|
|
|
15f218 |
)
|
|
|
15f218 |
- self.assertEqual(expected_output, real_output)
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ expected_stdout + "\n" + expected_stderr,
|
|
|
15f218 |
+ real_output
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
def test_node(self):
|
|
|
15f218 |
node = "test_node"
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (expected_stdout, expected_stderr, 0)
|
|
|
15f218 |
|
|
|
15f218 |
real_output = lib.resource_cleanup(mock_runner, node=node)
|
|
|
15f218 |
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
[self.path("crm_resource"), "--cleanup", "--node", node]
|
|
|
15f218 |
)
|
|
|
15f218 |
- self.assertEqual(expected_output, real_output)
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ expected_stdout + "\n" + expected_stderr,
|
|
|
15f218 |
+ real_output
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
def test_node_and_resource(self):
|
|
|
15f218 |
node = "test_node"
|
|
|
15f218 |
resource = "test_resource"
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (expected_stdout, expected_stderr, 0)
|
|
|
15f218 |
|
|
|
15f218 |
real_output = lib.resource_cleanup(
|
|
|
15f218 |
mock_runner, resource=resource, node=node
|
|
|
15f218 |
@@ -555,13 +628,21 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
"--cleanup", "--resource", resource, "--node", node
|
|
|
15f218 |
]
|
|
|
15f218 |
)
|
|
|
15f218 |
- self.assertEqual(expected_output, real_output)
|
|
|
15f218 |
+ self.assertEqual(
|
|
|
15f218 |
+ expected_stdout + "\n" + expected_stderr,
|
|
|
15f218 |
+ real_output
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
def test_error_state(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.resource_cleanup(mock_runner),
|
|
|
15f218 |
@@ -569,8 +650,7 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.CRM_MON_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -578,7 +658,8 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(self.crm_mon_cmd())
|
|
|
15f218 |
|
|
|
15f218 |
def test_error_cleanup(self):
|
|
|
15f218 |
- expected_error = "expected error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
call_list = [
|
|
|
15f218 |
@@ -586,8 +667,8 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
mock.call([self.path("crm_resource"), "--cleanup"]),
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (self.fixture_status_xml(1, 1), 0),
|
|
|
15f218 |
- (expected_error, expected_retval),
|
|
|
15f218 |
+ (self.fixture_status_xml(1, 1), "", 0),
|
|
|
15f218 |
+ (expected_stdout, expected_stderr, expected_retval),
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -597,8 +678,7 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.RESOURCE_CLEANUP_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -609,10 +689,33 @@ class ResourceCleanupTest(LibraryPacemakerTest):
|
|
|
15f218 |
|
|
|
15f218 |
class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
def test_has_support(self):
|
|
|
15f218 |
- expected_output = "something --wait something else"
|
|
|
15f218 |
+ expected_stdout = ""
|
|
|
15f218 |
+ expected_stderr = "something --wait something else"
|
|
|
15f218 |
+ expected_retval = 1
|
|
|
15f218 |
+ mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ self.assertTrue(
|
|
|
15f218 |
+ lib.has_resource_wait_support(mock_runner)
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
+ [self.path("crm_resource"), "-?"]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_has_support_stdout(self):
|
|
|
15f218 |
+ expected_stdout = "something --wait something else"
|
|
|
15f218 |
+ expected_stderr = ""
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
self.assertTrue(
|
|
|
15f218 |
lib.has_resource_wait_support(mock_runner)
|
|
|
15f218 |
@@ -622,10 +725,15 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_doesnt_have_support(self):
|
|
|
15f218 |
- expected_output = "something something else"
|
|
|
15f218 |
+ expected_stdout = "something something else"
|
|
|
15f218 |
+ expected_stderr = "something something else"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
self.assertFalse(
|
|
|
15f218 |
lib.has_resource_wait_support(mock_runner)
|
|
|
15f218 |
@@ -652,10 +760,15 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_wait_success(self):
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
self.assertEqual(None, lib.wait_for_resources(mock_runner))
|
|
|
15f218 |
|
|
|
15f218 |
@@ -664,11 +777,16 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_wait_timeout_success(self):
|
|
|
15f218 |
- expected_output = "expected output"
|
|
|
15f218 |
+ expected_stdout = "expected output"
|
|
|
15f218 |
+ expected_stderr = "expected stderr"
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
timeout = 10
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_output, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
self.assertEqual(None, lib.wait_for_resources(mock_runner, timeout))
|
|
|
15f218 |
|
|
|
15f218 |
@@ -680,10 +798,15 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_wait_error(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.wait_for_resources(mock_runner),
|
|
|
15f218 |
@@ -691,8 +814,7 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.RESOURCE_WAIT_ERROR,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -702,10 +824,15 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_wait_error_timeout(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 62
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.wait_for_resources(mock_runner),
|
|
|
15f218 |
@@ -713,8 +840,7 @@ class ResourcesWaitingTest(LibraryPacemakerTest):
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.RESOURCE_WAIT_TIMED_OUT,
|
|
|
15f218 |
{
|
|
|
15f218 |
- "return_value": expected_retval,
|
|
|
15f218 |
- "stdout": expected_error,
|
|
|
15f218 |
+ "reason": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
}
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -727,7 +853,7 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
def test_standby_local(self):
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("dummy", expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("dummy", "", expected_retval)
|
|
|
15f218 |
|
|
|
15f218 |
output = lib.nodes_standby(mock_runner)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -739,7 +865,7 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
def test_unstandby_local(self):
|
|
|
15f218 |
expected_retval = 0
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("dummy", expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("dummy", "", expected_retval)
|
|
|
15f218 |
|
|
|
15f218 |
output = lib.nodes_unstandby(mock_runner)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -760,8 +886,8 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
mock.call([self.path("crm_standby"), "-v", "on", "-N", n])
|
|
|
15f218 |
for n in nodes
|
|
|
15f218 |
]
|
|
|
15f218 |
- return_value_list = [(str(self.status), 0)]
|
|
|
15f218 |
- return_value_list += [("dummy", 0) for n in nodes]
|
|
|
15f218 |
+ return_value_list = [(str(self.status), "", 0)]
|
|
|
15f218 |
+ return_value_list += [("dummy", "", 0) for n in nodes]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
output = lib.nodes_standby(mock_runner, all_nodes=True)
|
|
|
15f218 |
@@ -783,8 +909,8 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
mock.call([self.path("crm_standby"), "-D", "-N", n])
|
|
|
15f218 |
for n in nodes
|
|
|
15f218 |
]
|
|
|
15f218 |
- return_value_list = [(str(self.status), 0)]
|
|
|
15f218 |
- return_value_list += [("dummy", 0) for n in nodes]
|
|
|
15f218 |
+ return_value_list = [(str(self.status), "", 0)]
|
|
|
15f218 |
+ return_value_list += [("dummy", "", 0) for n in nodes]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
output = lib.nodes_unstandby(mock_runner, all_nodes=True)
|
|
|
15f218 |
@@ -806,8 +932,8 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
mock.call([self.path("crm_standby"), "-v", "on", "-N", n])
|
|
|
15f218 |
for n in nodes[1:]
|
|
|
15f218 |
]
|
|
|
15f218 |
- return_value_list = [(str(self.status), 0)]
|
|
|
15f218 |
- return_value_list += [("dummy", 0) for n in nodes[1:]]
|
|
|
15f218 |
+ return_value_list = [(str(self.status), "", 0)]
|
|
|
15f218 |
+ return_value_list += [("dummy", "", 0) for n in nodes[1:]]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
output = lib.nodes_standby(mock_runner, node_list=nodes[1:])
|
|
|
15f218 |
@@ -829,8 +955,8 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
mock.call([self.path("crm_standby"), "-D", "-N", n])
|
|
|
15f218 |
for n in nodes[:2]
|
|
|
15f218 |
]
|
|
|
15f218 |
- return_value_list = [(str(self.status), 0)]
|
|
|
15f218 |
- return_value_list += [("dummy", 0) for n in nodes[:2]]
|
|
|
15f218 |
+ return_value_list = [(str(self.status), "", 0)]
|
|
|
15f218 |
+ return_value_list += [("dummy", "", 0) for n in nodes[:2]]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
output = lib.nodes_unstandby(mock_runner, node_list=nodes[:2])
|
|
|
15f218 |
@@ -845,7 +971,7 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
self.fixture_get_node_status("node_1", "id_1")
|
|
|
15f218 |
)
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (str(self.status), 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (str(self.status), "", 0)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.nodes_standby(mock_runner, ["node_2"]),
|
|
|
15f218 |
@@ -863,7 +989,7 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
self.fixture_get_node_status("node_1", "id_1")
|
|
|
15f218 |
)
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (str(self.status), 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (str(self.status), "", 0)
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.nodes_unstandby(mock_runner, ["node_2", "node_3"]),
|
|
|
15f218 |
@@ -882,17 +1008,24 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(self.crm_mon_cmd())
|
|
|
15f218 |
|
|
|
15f218 |
def test_error_one_node(self):
|
|
|
15f218 |
- expected_error = "some error"
|
|
|
15f218 |
+ expected_stdout = "some info"
|
|
|
15f218 |
+ expected_stderr = "some error"
|
|
|
15f218 |
expected_retval = 1
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (expected_error, expected_retval)
|
|
|
15f218 |
+ mock_runner.run.return_value = (
|
|
|
15f218 |
+ expected_stdout,
|
|
|
15f218 |
+ expected_stderr,
|
|
|
15f218 |
+ expected_retval
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
assert_raise_library_error(
|
|
|
15f218 |
lambda: lib.nodes_unstandby(mock_runner),
|
|
|
15f218 |
(
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.COMMON_ERROR,
|
|
|
15f218 |
- {}
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "text": expected_stderr + "\n" + expected_stdout,
|
|
|
15f218 |
+ }
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@@ -913,11 +1046,11 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
for n in nodes
|
|
|
15f218 |
]
|
|
|
15f218 |
return_value_list = [
|
|
|
15f218 |
- (str(self.status), 0),
|
|
|
15f218 |
- ("dummy1", 0),
|
|
|
15f218 |
- ("dummy2", 1),
|
|
|
15f218 |
- ("dummy3", 0),
|
|
|
15f218 |
- ("dummy4", 1),
|
|
|
15f218 |
+ (str(self.status), "", 0),
|
|
|
15f218 |
+ ("dummy1", "", 0),
|
|
|
15f218 |
+ ("dummy2", "error2", 1),
|
|
|
15f218 |
+ ("dummy3", "", 0),
|
|
|
15f218 |
+ ("dummy4", "error4", 1),
|
|
|
15f218 |
]
|
|
|
15f218 |
mock_runner.run.side_effect = return_value_list
|
|
|
15f218 |
|
|
|
15f218 |
@@ -926,12 +1059,16 @@ class NodeStandbyTest(LibraryPacemakerNodeStatusTest):
|
|
|
15f218 |
(
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.COMMON_ERROR,
|
|
|
15f218 |
- {}
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "text": "error2\ndummy2",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
),
|
|
|
15f218 |
(
|
|
|
15f218 |
Severity.ERROR,
|
|
|
15f218 |
report_codes.COMMON_ERROR,
|
|
|
15f218 |
- {}
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "text": "error4\ndummy4",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_resource_agent.py b/pcs/test/test_lib_resource_agent.py
|
|
|
15f218 |
index 08f9061..a569e66 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_resource_agent.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_resource_agent.py
|
|
|
15f218 |
@@ -199,7 +199,7 @@ class GetFenceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
def test_execution_failed(self, mock_is_runnable):
|
|
|
15f218 |
mock_is_runnable.return_value = True
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("error", 1)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("", "error", 1)
|
|
|
15f218 |
agent_name = "fence_ipmi"
|
|
|
15f218 |
|
|
|
15f218 |
self.assert_raises(
|
|
|
15f218 |
@@ -210,13 +210,13 @@ class GetFenceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
|
|
|
15f218 |
script_path = os.path.join(settings.fence_agent_binaries, agent_name)
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- [script_path, "-o", "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ [script_path, "-o", "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.resource_agent.is_path_runnable")
|
|
|
15f218 |
def test_invalid_xml(self, mock_is_runnable):
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("not xml", 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("not xml", "", 0)
|
|
|
15f218 |
mock_is_runnable.return_value = True
|
|
|
15f218 |
agent_name = "fence_ipmi"
|
|
|
15f218 |
self.assert_raises(
|
|
|
15f218 |
@@ -227,7 +227,7 @@ class GetFenceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
|
|
|
15f218 |
script_path = os.path.join(settings.fence_agent_binaries, agent_name)
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- [script_path, "-o", "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ [script_path, "-o", "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.resource_agent.is_path_runnable")
|
|
|
15f218 |
@@ -235,14 +235,14 @@ class GetFenceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
agent_name = "fence_ipmi"
|
|
|
15f218 |
xml = "<xml />"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (xml, 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (xml, "", 0)
|
|
|
15f218 |
mock_is_runnable.return_value = True
|
|
|
15f218 |
|
|
|
15f218 |
out_dom = lib_ra.get_fence_agent_metadata(mock_runner, agent_name)
|
|
|
15f218 |
|
|
|
15f218 |
script_path = os.path.join(settings.fence_agent_binaries, agent_name)
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- [script_path, "-o", "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ [script_path, "-o", "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
assert_xml_equal(xml, str(XmlMan(out_dom)))
|
|
|
15f218 |
|
|
|
15f218 |
@@ -304,7 +304,7 @@ class GetOcfResourceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
provider = "provider"
|
|
|
15f218 |
agent = "agent"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("error", 1)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("", "error", 1)
|
|
|
15f218 |
mock_is_runnable.return_value = True
|
|
|
15f218 |
|
|
|
15f218 |
self.assert_raises(
|
|
|
15f218 |
@@ -318,8 +318,7 @@ class GetOcfResourceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
script_path = os.path.join(settings.ocf_resources, provider, agent)
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
[script_path, "meta-data"],
|
|
|
15f218 |
- env_extend={"OCF_ROOT": settings.ocf_root},
|
|
|
15f218 |
- ignore_stderr=True
|
|
|
15f218 |
+ env_extend={"OCF_ROOT": settings.ocf_root}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.resource_agent.is_path_runnable")
|
|
|
15f218 |
@@ -327,7 +326,7 @@ class GetOcfResourceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
provider = "provider"
|
|
|
15f218 |
agent = "agent"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("not xml", 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("not xml", "", 0)
|
|
|
15f218 |
mock_is_runnable.return_value = True
|
|
|
15f218 |
|
|
|
15f218 |
self.assert_raises(
|
|
|
15f218 |
@@ -341,8 +340,7 @@ class GetOcfResourceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
script_path = os.path.join(settings.ocf_resources, provider, agent)
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
[script_path, "meta-data"],
|
|
|
15f218 |
- env_extend={"OCF_ROOT": settings.ocf_root},
|
|
|
15f218 |
- ignore_stderr=True
|
|
|
15f218 |
+ env_extend={"OCF_ROOT": settings.ocf_root}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
@mock.patch("pcs.lib.resource_agent.is_path_runnable")
|
|
|
15f218 |
@@ -351,7 +349,7 @@ class GetOcfResourceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
agent = "agent"
|
|
|
15f218 |
xml = "<xml />"
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (xml, 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (xml, "", 0)
|
|
|
15f218 |
mock_is_runnable.return_value = True
|
|
|
15f218 |
|
|
|
15f218 |
out_dom = lib_ra._get_ocf_resource_agent_metadata(
|
|
|
15f218 |
@@ -361,8 +359,7 @@ class GetOcfResourceAgentMetadataTest(LibraryResourceTest):
|
|
|
15f218 |
script_path = os.path.join(settings.ocf_resources, provider, agent)
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
[script_path, "meta-data"],
|
|
|
15f218 |
- env_extend={"OCF_ROOT": settings.ocf_root},
|
|
|
15f218 |
- ignore_stderr=True
|
|
|
15f218 |
+ env_extend={"OCF_ROOT": settings.ocf_root}
|
|
|
15f218 |
)
|
|
|
15f218 |
assert_xml_equal(xml, str(XmlMan(out_dom)))
|
|
|
15f218 |
|
|
|
15f218 |
@@ -596,7 +593,7 @@ class GetPcmkAdvancedStonithParametersTest(LibraryResourceTest):
|
|
|
15f218 |
</resource-agent>
|
|
|
15f218 |
"""
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = (xml, 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = (xml, "", 0)
|
|
|
15f218 |
self.assertEqual(
|
|
|
15f218 |
[
|
|
|
15f218 |
{
|
|
|
15f218 |
@@ -623,12 +620,12 @@ class GetPcmkAdvancedStonithParametersTest(LibraryResourceTest):
|
|
|
15f218 |
lib_ra._get_pcmk_advanced_stonith_parameters(mock_runner)
|
|
|
15f218 |
)
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- [settings.stonithd_binary, "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ [settings.stonithd_binary, "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_failed_to_get_xml(self):
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("", 1)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("", "some error", 1)
|
|
|
15f218 |
self.assert_raises(
|
|
|
15f218 |
lib_ra.UnableToGetAgentMetadata,
|
|
|
15f218 |
lambda: lib_ra._get_pcmk_advanced_stonith_parameters(mock_runner),
|
|
|
15f218 |
@@ -636,19 +633,19 @@ class GetPcmkAdvancedStonithParametersTest(LibraryResourceTest):
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- [settings.stonithd_binary, "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ [settings.stonithd_binary, "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_invalid_xml(self):
|
|
|
15f218 |
mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
- mock_runner.run.return_value = ("invalid XML", 0)
|
|
|
15f218 |
+ mock_runner.run.return_value = ("invalid XML", "", 0)
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
lib_ra.InvalidMetadataFormat,
|
|
|
15f218 |
lambda: lib_ra._get_pcmk_advanced_stonith_parameters(mock_runner)
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
- [settings.stonithd_binary, "metadata"], ignore_stderr=True
|
|
|
15f218 |
+ [settings.stonithd_binary, "metadata"]
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_sbd.py b/pcs/test/test_lib_sbd.py
|
|
|
15f218 |
index 720d8b1..9b7b801 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_sbd.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_sbd.py
|
|
|
15f218 |
@@ -155,9 +155,8 @@ class AtbHasToBeEnabledTest(TestCase):
|
|
|
15f218 |
self.assertFalse(lib_sbd.atb_has_to_be_enabled(
|
|
|
15f218 |
self.mock_runner, self.mock_conf, 1
|
|
|
15f218 |
))
|
|
|
15f218 |
- mock_is_needed.assert_called_once_with(
|
|
|
15f218 |
- self.mock_runner, self.mock_conf, 1
|
|
|
15f218 |
- )
|
|
|
15f218 |
+ self.mock_conf.is_enabled_auto_tie_breaker.assert_called_once_with()
|
|
|
15f218 |
+ mock_is_needed.assert_not_called()
|
|
|
15f218 |
|
|
|
15f218 |
def test_atb_needed_is_disabled(self, mock_is_needed):
|
|
|
15f218 |
mock_is_needed.return_value = True
|
|
|
15f218 |
@@ -165,6 +164,7 @@ class AtbHasToBeEnabledTest(TestCase):
|
|
|
15f218 |
self.assertTrue(lib_sbd.atb_has_to_be_enabled(
|
|
|
15f218 |
self.mock_runner, self.mock_conf, -1
|
|
|
15f218 |
))
|
|
|
15f218 |
+ self.mock_conf.is_enabled_auto_tie_breaker.assert_called_once_with()
|
|
|
15f218 |
mock_is_needed.assert_called_once_with(
|
|
|
15f218 |
self.mock_runner, self.mock_conf, -1
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -175,9 +175,8 @@ class AtbHasToBeEnabledTest(TestCase):
|
|
|
15f218 |
self.assertFalse(lib_sbd.atb_has_to_be_enabled(
|
|
|
15f218 |
self.mock_runner, self.mock_conf, 2
|
|
|
15f218 |
))
|
|
|
15f218 |
- mock_is_needed.assert_called_once_with(
|
|
|
15f218 |
- self.mock_runner, self.mock_conf, 2
|
|
|
15f218 |
- )
|
|
|
15f218 |
+ self.mock_conf.is_enabled_auto_tie_breaker.assert_called_once_with()
|
|
|
15f218 |
+ mock_is_needed.assert_not_called()
|
|
|
15f218 |
|
|
|
15f218 |
def test_atb_not_needed_is_disabled(self, mock_is_needed):
|
|
|
15f218 |
mock_is_needed.return_value = False
|
|
|
15f218 |
@@ -185,6 +184,7 @@ class AtbHasToBeEnabledTest(TestCase):
|
|
|
15f218 |
self.assertFalse(lib_sbd.atb_has_to_be_enabled(
|
|
|
15f218 |
self.mock_runner, self.mock_conf, -2
|
|
|
15f218 |
))
|
|
|
15f218 |
+ self.mock_conf.is_enabled_auto_tie_breaker.assert_called_once_with()
|
|
|
15f218 |
mock_is_needed.assert_called_once_with(
|
|
|
15f218 |
self.mock_runner, self.mock_conf, -2
|
|
|
15f218 |
)
|
|
|
15f218 |
--
|
|
|
15f218 |
1.8.3.1
|
|
|
15f218 |
|