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