pcs/SOURCES/bz1843079-01-upgrade-CIB-sc...

1308 lines
52 KiB
Diff

From 62b970d5e9edbdd68dc006193b0e606fb7ae7cdd Mon Sep 17 00:00:00 2001
From: Tomas Jelinek <tojeline@redhat.com>
Date: Thu, 2 Jul 2020 15:18:29 +0200
Subject: [PATCH 1/3] upgrade CIB schema for on-fail=demote
---
pcs/lib/cib/resource/operations.py | 9 +-
pcs/lib/commands/remote_node.py | 73 +++--
pcs/lib/commands/resource.py | 269 ++++++++++--------
pcs/resource.py | 51 +++-
pcs_test/resources/cib-empty-3.3.xml | 2 +-
pcs_test/resources/cib-empty-3.4.xml | 2 +-
.../tier0/lib/cib/test_resource_operations.py | 9 +-
.../remote_node/test_node_add_remote.py | 54 +++-
.../commands/resource/test_resource_create.py | 237 ++++++++++++++-
pcs_test/tier1/cib_resource/test_create.py | 2 +-
pcs_test/tier1/legacy/test_resource.py | 23 ++
pcs_test/tools/misc.py | 6 +
pcsd/capabilities.xml | 42 +++
13 files changed, 594 insertions(+), 185 deletions(-)
diff --git a/pcs/lib/cib/resource/operations.py b/pcs/lib/cib/resource/operations.py
index 131e0a49..79d00685 100644
--- a/pcs/lib/cib/resource/operations.py
+++ b/pcs/lib/cib/resource/operations.py
@@ -39,13 +39,14 @@ ATTRIBUTES = [
]
ON_FAIL_VALUES = [
- "ignore",
"block",
- "stop",
- "restart",
- "standby",
+ "demote",
"fence",
+ "ignore",
+ "restart",
"restart-container",
+ "standby",
+ "stop",
]
BOOLEAN_VALUES = [
diff --git a/pcs/lib/commands/remote_node.py b/pcs/lib/commands/remote_node.py
index 6a2656a5..575e8044 100644
--- a/pcs/lib/commands/remote_node.py
+++ b/pcs/lib/commands/remote_node.py
@@ -1,3 +1,10 @@
+from typing import (
+ Iterable,
+ Mapping,
+ Optional,
+ Union,
+)
+
from pcs import settings
from pcs.common import reports
from pcs.common.file import RawFileError
@@ -13,6 +20,10 @@ from pcs.lib.cib.tools import (
ElementSearcher,
get_resources,
)
+
+# TODO lib.commands should never import each other. This is to be removed when
+# the 'resource create' commands are overhauled.
+from pcs.lib.commands.resource import get_required_cib_version_for_primitive
from pcs.lib.communication.nodes import (
DistributeFiles,
GetHostInfo,
@@ -24,6 +35,7 @@ from pcs.lib.communication.tools import (
run as run_com,
run_and_raise,
)
+from pcs.lib.corosync.config_facade import ConfigFacade as CorosyncConfigFacade
from pcs.lib.env import LibraryEnvironment
from pcs.lib.errors import LibraryError
from pcs.lib.file.instance import FileInstance
@@ -33,6 +45,9 @@ from pcs.lib.pacemaker import state
from pcs.lib.pacemaker.live import remove_node
+WaitType = Union[None, bool, int]
+
+
def _reports_skip_new_node(new_node_name, reason_type):
assert reason_type in {"unreachable", "not_live_cib"}
return [
@@ -220,19 +235,19 @@ def _ensure_resource_running(env: LibraryEnvironment, resource_id):
def node_add_remote(
- env,
- node_name,
- node_addr,
- operations,
- meta_attributes,
- instance_attributes,
- skip_offline_nodes=False,
- allow_incomplete_distribution=False,
- allow_pacemaker_remote_service_fail=False,
- allow_invalid_operation=False,
- allow_invalid_instance_attributes=False,
- use_default_operations=True,
- wait=False,
+ env: LibraryEnvironment,
+ node_name: str,
+ node_addr: Optional[str],
+ operations: Iterable[Mapping[str, str]],
+ meta_attributes: Mapping[str, str],
+ instance_attributes: Mapping[str, str],
+ skip_offline_nodes: bool = False,
+ allow_incomplete_distribution: bool = False,
+ allow_pacemaker_remote_service_fail: bool = False,
+ allow_invalid_operation: bool = False,
+ allow_invalid_instance_attributes: bool = False,
+ use_default_operations: bool = True,
+ wait: WaitType = False,
):
# pylint: disable=too-many-arguments
# pylint: disable=too-many-branches
@@ -241,34 +256,36 @@ def node_add_remote(
"""
create an ocf:pacemaker:remote resource and use it as a remote node
- LibraryEnvironment env -- provides all for communication with externals
- string node_name -- the name of the new node
- mixed node_addr -- the address of the new node or None for default
- list of dict operations -- attributes for each entered operation
- dict meta_attributes -- attributes for primitive/meta_attributes
- dict instance_attributes -- attributes for primitive/instance_attributes
- bool skip_offline_nodes -- if True, ignore when some nodes are offline
- bool allow_incomplete_distribution -- if True, allow this command to
+ env -- provides all for communication with externals
+ node_name -- the name of the new node
+ node_addr -- the address of the new node or None for default
+ operations -- attributes for each entered operation
+ meta_attributes -- attributes for primitive/meta_attributes
+ instance_attributes -- attributes for primitive/instance_attributes
+ skip_offline_nodes -- if True, ignore when some nodes are offline
+ allow_incomplete_distribution -- if True, allow this command to
finish successfully even if file distribution did not succeed
- bool allow_pacemaker_remote_service_fail -- if True, allow this command to
+ allow_pacemaker_remote_service_fail -- if True, allow this command to
finish successfully even if starting/enabling pacemaker_remote did not
succeed
- bool allow_invalid_operation -- if True, allow to use operations that
+ allow_invalid_operation -- if True, allow to use operations that
are not listed in a resource agent metadata
- bool allow_invalid_instance_attributes -- if True, allow to use instance
+ allow_invalid_instance_attributes -- if True, allow to use instance
attributes that are not listed in a resource agent metadata and allow to
omit required instance_attributes
- bool use_default_operations -- if True, add operations specified in
+ use_default_operations -- if True, add operations specified in
a resource agent metadata to the resource
- mixed wait -- a flag for controlling waiting for pacemaker idle mechanism
+ wait -- a flag for controlling waiting for pacemaker idle mechanism
"""
env.ensure_wait_satisfiable(wait)
report_processor = env.report_processor
- cib = env.get_cib()
+ cib = env.get_cib(
+ minimal_version=get_required_cib_version_for_primitive(operations)
+ )
id_provider = IdProvider(cib)
if env.is_cib_live:
- corosync_conf = env.get_corosync_conf()
+ corosync_conf: Optional[CorosyncConfigFacade] = env.get_corosync_conf()
else:
corosync_conf = None
report_processor.report(
diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py
index 75826c9d..db4b7bb3 100644
--- a/pcs/lib/commands/resource.py
+++ b/pcs/lib/commands/resource.py
@@ -61,6 +61,9 @@ from pcs.lib.resource_agent import (
from pcs.lib.validate import ValueTimeInterval
+WaitType = Union[None, bool, int]
+
+
@contextmanager
def resource_environment(
env,
@@ -262,44 +265,43 @@ def _get_required_cib_version_for_container(
def create(
- env,
- resource_id,
- resource_agent_name,
- operation_list,
- meta_attributes,
- instance_attributes,
- allow_absent_agent=False,
- allow_invalid_operation=False,
- allow_invalid_instance_attributes=False,
- use_default_operations=True,
- ensure_disabled=False,
- wait=False,
- allow_not_suitable_command=False,
+ env: LibraryEnvironment,
+ resource_id: str,
+ resource_agent_name: str,
+ operation_list: Iterable[Mapping[str, str]],
+ meta_attributes: Mapping[str, str],
+ instance_attributes: Mapping[str, str],
+ allow_absent_agent: bool = False,
+ allow_invalid_operation: bool = False,
+ allow_invalid_instance_attributes: bool = False,
+ use_default_operations: bool = True,
+ ensure_disabled: bool = False,
+ wait: WaitType = False,
+ allow_not_suitable_command: bool = False,
):
# pylint: disable=too-many-arguments, too-many-locals
"""
- Create resource in a cib.
+ Create a primitive resource in a cib.
- LibraryEnvironment env provides all for communication with externals
- string resource_id is identifier of resource
- string resource_agent_name contains name for the identification of agent
- list of dict operation_list contains attributes for each entered operation
- dict meta_attributes contains attributes for primitive/meta_attributes
- dict instance_attributes contains attributes for
- primitive/instance_attributes
- bool allow_absent_agent is a flag for allowing agent that is not installed
+ env -- provides all for communication with externals
+ resource_id -- is identifier of resource
+ resource_agent_name -- contains name for the identification of agent
+ operation_list -- contains attributes for each entered operation
+ meta_attributes -- contains attributes for primitive/meta_attributes
+ instance_attributes -- contains attributes for primitive/instance_attributes
+ allow_absent_agent -- is a flag for allowing agent that is not installed
in a system
- bool allow_invalid_operation is a flag for allowing to use operations that
+ allow_invalid_operation -- is a flag for allowing to use operations that
are not listed in a resource agent metadata
- bool allow_invalid_instance_attributes is a flag for allowing to use
+ allow_invalid_instance_attributes -- is a flag for allowing to use
instance attributes that are not listed in a resource agent metadata
or for allowing to not use the instance_attributes that are required in
resource agent metadata
- bool use_default_operations is a flag for stopping stopping of adding
+ use_default_operations -- is a flag for stopping stopping of adding
default cib operations (specified in a resource agent)
- bool ensure_disabled is flag that keeps resource in target-role "Stopped"
- mixed wait is flag for controlling waiting for pacemaker idle mechanism
- bool allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
+ ensure_disabled -- is flag that keeps resource in target-role "Stopped"
+ wait -- is flag for controlling waiting for pacemaker idle mechanism
+ allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
"""
resource_agent = get_agent(
env.report_processor,
@@ -315,6 +317,9 @@ def create(
ensure_disabled
or resource.common.are_meta_disabled(meta_attributes)
),
+ required_cib_version=get_required_cib_version_for_primitive(
+ operation_list
+ ),
) as resources_section:
id_provider = IdProvider(resources_section)
_check_special_cases(
@@ -345,46 +350,45 @@ def create(
def create_as_clone(
- env,
- resource_id,
- resource_agent_name,
- operation_list,
- meta_attributes,
- instance_attributes,
- clone_meta_options,
- allow_absent_agent=False,
- allow_invalid_operation=False,
- allow_invalid_instance_attributes=False,
- use_default_operations=True,
- ensure_disabled=False,
- wait=False,
- allow_not_suitable_command=False,
+ env: LibraryEnvironment,
+ resource_id: str,
+ resource_agent_name: str,
+ operation_list: Iterable[Mapping[str, str]],
+ meta_attributes: Mapping[str, str],
+ instance_attributes: Mapping[str, str],
+ clone_meta_options: Mapping[str, str],
+ allow_absent_agent: bool = False,
+ allow_invalid_operation: bool = False,
+ allow_invalid_instance_attributes: bool = False,
+ use_default_operations: bool = True,
+ ensure_disabled: bool = False,
+ wait: WaitType = False,
+ allow_not_suitable_command: bool = False,
):
# pylint: disable=too-many-arguments, too-many-locals
"""
- Create resource in a clone
+ Create a primitive resource in a clone
- LibraryEnvironment env provides all for communication with externals
- string resource_id is identifier of resource
- string resource_agent_name contains name for the identification of agent
- list of dict operation_list contains attributes for each entered operation
- dict meta_attributes contains attributes for primitive/meta_attributes
- dict instance_attributes contains attributes for
- primitive/instance_attributes
- dict clone_meta_options contains attributes for clone/meta_attributes
- bool allow_absent_agent is a flag for allowing agent that is not installed
+ env -- provides all for communication with externals
+ resource_id -- is identifier of resource
+ resource_agent_name -- contains name for the identification of agent
+ operation_list -- contains attributes for each entered operation
+ meta_attributes -- contains attributes for primitive/meta_attributes
+ instance_attributes -- contains attributes for primitive/instance_attributes
+ clone_meta_options -- contains attributes for clone/meta_attributes
+ allow_absent_agent -- is a flag for allowing agent that is not installed
in a system
- bool allow_invalid_operation is a flag for allowing to use operations that
+ allow_invalid_operation -- is a flag for allowing to use operations that
are not listed in a resource agent metadata
- bool allow_invalid_instance_attributes is a flag for allowing to use
+ allow_invalid_instance_attributes -- is a flag for allowing to use
instance attributes that are not listed in a resource agent metadata
or for allowing to not use the instance_attributes that are required in
resource agent metadata
- bool use_default_operations is a flag for stopping stopping of adding
+ use_default_operations -- is a flag for stopping stopping of adding
default cib operations (specified in a resource agent)
- bool ensure_disabled is flag that keeps resource in target-role "Stopped"
- mixed wait is flag for controlling waiting for pacemaker idle mechanism
- bool allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
+ ensure_disabled -- is flag that keeps resource in target-role "Stopped"
+ wait -- is flag for controlling waiting for pacemaker idle mechanism
+ allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
"""
resource_agent = get_agent(
env.report_processor,
@@ -401,6 +405,9 @@ def create_as_clone(
or resource.common.are_meta_disabled(meta_attributes)
or resource.common.is_clone_deactivated_by_meta(clone_meta_options)
),
+ required_cib_version=get_required_cib_version_for_primitive(
+ operation_list
+ ),
) as resources_section:
id_provider = IdProvider(resources_section)
_check_special_cases(
@@ -437,49 +444,50 @@ def create_as_clone(
def create_in_group(
- env,
- resource_id,
- resource_agent_name,
- group_id,
- operation_list,
- meta_attributes,
- instance_attributes,
- allow_absent_agent=False,
- allow_invalid_operation=False,
- allow_invalid_instance_attributes=False,
- use_default_operations=True,
- ensure_disabled=False,
- adjacent_resource_id=None,
- put_after_adjacent=False,
- wait=False,
- allow_not_suitable_command=False,
+ env: LibraryEnvironment,
+ resource_id: str,
+ resource_agent_name: str,
+ group_id: str,
+ operation_list: Iterable[Mapping[str, str]],
+ meta_attributes: Mapping[str, str],
+ instance_attributes: Mapping[str, str],
+ allow_absent_agent: bool = False,
+ allow_invalid_operation: bool = False,
+ allow_invalid_instance_attributes: bool = False,
+ use_default_operations: bool = True,
+ ensure_disabled: bool = False,
+ adjacent_resource_id: Optional[str] = None,
+ put_after_adjacent: bool = False,
+ wait: WaitType = False,
+ allow_not_suitable_command: bool = False,
):
# pylint: disable=too-many-arguments, too-many-locals
"""
Create resource in a cib and put it into defined group
- LibraryEnvironment env provides all for communication with externals
- string resource_id is identifier of resource
- string resource_agent_name contains name for the identification of agent
- string group_id is identificator for group to put primitive resource inside
- list of dict operation_list contains attributes for each entered operation
- dict meta_attributes contains attributes for primitive/meta_attributes
- bool allow_absent_agent is a flag for allowing agent that is not installed
+ env -- provides all for communication with externals
+ resource_id -- is identifier of resource
+ resource_agent_name -- contains name for the identification of agent
+ group_id -- is identificator for group to put primitive resource inside
+ operation_list -- contains attributes for each entered operation
+ meta_attributes -- contains attributes for primitive/meta_attributes
+ instance_attributes -- contains attributes for primitive/instance_attributes
+ allow_absent_agent -- is a flag for allowing agent that is not installed
in a system
- bool allow_invalid_operation is a flag for allowing to use operations that
+ allow_invalid_operation -- is a flag for allowing to use operations that
are not listed in a resource agent metadata
- bool allow_invalid_instance_attributes is a flag for allowing to use
+ allow_invalid_instance_attributes -- is a flag for allowing to use
instance attributes that are not listed in a resource agent metadata
or for allowing to not use the instance_attributes that are required in
resource agent metadata
- bool use_default_operations is a flag for stopping stopping of adding
+ use_default_operations -- is a flag for stopping stopping of adding
default cib operations (specified in a resource agent)
- bool ensure_disabled is flag that keeps resource in target-role "Stopped"
- string adjacent_resource_id identify neighbor of a newly created resource
- bool put_after_adjacent is flag to put a newly create resource befor/after
+ ensure_disabled -- is flag that keeps resource in target-role "Stopped"
+ adjacent_resource_id -- identify neighbor of a newly created resource
+ put_after_adjacent -- is flag to put a newly create resource befor/after
adjacent resource
- mixed wait is flag for controlling waiting for pacemaker idle mechanism
- bool allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
+ wait -- is flag for controlling waiting for pacemaker idle mechanism
+ allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
"""
resource_agent = get_agent(
env.report_processor,
@@ -495,6 +503,9 @@ def create_in_group(
ensure_disabled
or resource.common.are_meta_disabled(meta_attributes)
),
+ required_cib_version=get_required_cib_version_for_primitive(
+ operation_list
+ ),
) as resources_section:
id_provider = IdProvider(resources_section)
_check_special_cases(
@@ -532,48 +543,48 @@ def create_in_group(
def create_into_bundle(
- env,
- resource_id,
- resource_agent_name,
- operation_list,
- meta_attributes,
- instance_attributes,
- bundle_id,
- allow_absent_agent=False,
- allow_invalid_operation=False,
- allow_invalid_instance_attributes=False,
- use_default_operations=True,
- ensure_disabled=False,
- wait=False,
- allow_not_suitable_command=False,
- allow_not_accessible_resource=False,
+ env: LibraryEnvironment,
+ resource_id: str,
+ resource_agent_name: str,
+ operation_list: Iterable[Mapping[str, str]],
+ meta_attributes: Mapping[str, str],
+ instance_attributes: Mapping[str, str],
+ bundle_id: str,
+ allow_absent_agent: bool = False,
+ allow_invalid_operation: bool = False,
+ allow_invalid_instance_attributes: bool = False,
+ use_default_operations: bool = True,
+ ensure_disabled: bool = False,
+ wait: WaitType = False,
+ allow_not_suitable_command: bool = False,
+ allow_not_accessible_resource: bool = False,
):
# pylint: disable=too-many-arguments, too-many-locals
"""
Create a new resource in a cib and put it into an existing bundle
- LibraryEnvironment env provides all for communication with externals
- string resource_id is identifier of resource
- string resource_agent_name contains name for the identification of agent
- list of dict operation_list contains attributes for each entered operation
- dict meta_attributes contains attributes for primitive/meta_attributes
- dict instance_attributes contains attributes for
+ env -- provides all for communication with externals
+ resource_id -- is identifier of resource
+ resource_agent_name -- contains name for the identification of agent
+ operation_list -- contains attributes for each entered operation
+ meta_attributes -- contains attributes for primitive/meta_attributes
+ instance_attributes -- contains attributes for
primitive/instance_attributes
- string bundle_id is id of an existing bundle to put the created resource in
- bool allow_absent_agent is a flag for allowing agent that is not installed
+ bundle_id -- is id of an existing bundle to put the created resource in
+ allow_absent_agent -- is a flag for allowing agent that is not installed
in a system
- bool allow_invalid_operation is a flag for allowing to use operations that
+ allow_invalid_operation -- is a flag for allowing to use operations that
are not listed in a resource agent metadata
- bool allow_invalid_instance_attributes is a flag for allowing to use
+ allow_invalid_instance_attributes -- is a flag for allowing to use
instance attributes that are not listed in a resource agent metadata
or for allowing to not use the instance_attributes that are required in
resource agent metadata
- bool use_default_operations is a flag for stopping stopping of adding
+ use_default_operations -- is a flag for stopping stopping of adding
default cib operations (specified in a resource agent)
- bool ensure_disabled is flag that keeps resource in target-role "Stopped"
- mixed wait is flag for controlling waiting for pacemaker idle mechanism
- bool allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
- bool allow_not_accessible_resource -- flag for
+ ensure_disabled -- is flag that keeps resource in target-role "Stopped"
+ wait -- is flag for controlling waiting for pacemaker idle mechanism
+ allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
+ allow_not_accessible_resource -- flag for
FORCE_RESOURCE_IN_BUNDLE_NOT_ACCESSIBLE
"""
resource_agent = get_agent(
@@ -582,6 +593,11 @@ def create_into_bundle(
resource_agent_name,
allow_absent_agent,
)
+ required_cib_version = get_required_cib_version_for_primitive(
+ operation_list
+ )
+ if not required_cib_version:
+ required_cib_version = Version(2, 8, 0)
with resource_environment(
env,
wait,
@@ -590,7 +606,7 @@ def create_into_bundle(
ensure_disabled
or resource.common.are_meta_disabled(meta_attributes)
),
- required_cib_version=Version(2, 8, 0),
+ required_cib_version=required_cib_version,
) as resources_section:
id_provider = IdProvider(resources_section)
_check_special_cases(
@@ -1070,9 +1086,7 @@ def disable_simulate(
def enable(
- env: LibraryEnvironment,
- resource_or_tag_ids: Iterable[str],
- wait: Optional[Union[bool, int]],
+ env: LibraryEnvironment, resource_or_tag_ids: Iterable[str], wait: WaitType,
):
"""
Allow specified resources to be started by the cluster
@@ -1689,3 +1703,12 @@ def _find_resources_expand_tags_or_raise(
return resource.common.expand_tags_to_resources(
resources_section, resource_or_tag_el_list,
)
+
+
+def get_required_cib_version_for_primitive(
+ op_list: Iterable[Mapping[str, str]]
+) -> Optional[Version]:
+ for op in op_list:
+ if op.get("on-fail", "") == "demote":
+ return Version(3, 4, 0)
+ return None
diff --git a/pcs/resource.py b/pcs/resource.py
index e835fc99..9a3bd0ee 100644
--- a/pcs/resource.py
+++ b/pcs/resource.py
@@ -355,6 +355,21 @@ def resource_op_add_cmd(lib, argv, modifiers):
if not argv:
raise CmdLineInputError()
res_id = argv.pop(0)
+
+ # Check if we need to upgrade cib schema.
+ # To do that, argv must be parsed, which is duplication of parsing in
+ # resource_operation_add. But we need to upgrade the cib first before
+ # calling that function. Hopefully, this will be fixed in the new pcs
+ # architecture.
+
+ # argv[0] is an operation name
+ op_properties = utils.convert_args_to_tuples(argv[1:])
+ for key, value in op_properties:
+ if key == "on-fail" and value == "demote":
+ utils.checkAndUpgradeCIB(3, 4, 0)
+ break
+
+ # add the requested operation
utils.replace_cib_configuration(
resource_operation_add(utils.get_cib_dom(), res_id, argv)
)
@@ -895,8 +910,6 @@ def resource_update(lib, args, modifiers, deal_with_guest_change=True):
if len(args) < 2:
raise CmdLineInputError()
res_id = args.pop(0)
- cib_xml = utils.get_cib()
- dom = utils.get_cib_dom(cib_xml=cib_xml)
# Extract operation arguments
ra_values, op_values, meta_values = parse_resource_options(args)
@@ -907,6 +920,28 @@ def resource_update(lib, args, modifiers, deal_with_guest_change=True):
wait_timeout = utils.validate_wait_get_timeout()
wait = True
+ # Check if we need to upgrade cib schema.
+ # To do that, argv must be parsed, which is duplication of parsing below.
+ # But we need to upgrade the cib first before calling that function.
+ # Hopefully, this will be fixed in the new pcs architecture.
+
+ cib_upgraded = False
+ for op_argv in op_values:
+ if cib_upgraded:
+ break
+ if len(op_argv) < 2:
+ continue
+ # argv[0] is an operation name
+ op_vars = utils.convert_args_to_tuples(op_argv[1:])
+ for k, v in op_vars:
+ if k == "on-fail" and v == "demote":
+ utils.checkAndUpgradeCIB(3, 4, 0)
+ cib_upgraded = True
+ break
+
+ cib_xml = utils.get_cib()
+ dom = utils.get_cib_dom(cib_xml=cib_xml)
+
resource = utils.dom_get_resource(dom, res_id)
if not resource:
clone = utils.dom_get_clone(dom, res_id)
@@ -994,21 +1029,21 @@ def resource_update(lib, args, modifiers, deal_with_guest_change=True):
else:
operations = operations[0]
- for element in op_values:
- if not element:
+ for op_argv in op_values:
+ if not op_argv:
continue
- op_name = element[0]
+ op_name = op_argv[0]
if op_name.find("=") != -1:
utils.err(
"%s does not appear to be a valid operation action" % op_name
)
- if len(element) < 2:
+ if len(op_argv) < 2:
continue
op_role = ""
- op_vars = utils.convert_args_to_tuples(element[1:])
+ op_vars = utils.convert_args_to_tuples(op_argv[1:])
for k, v in op_vars:
if k == "role":
@@ -1032,7 +1067,7 @@ def resource_update(lib, args, modifiers, deal_with_guest_change=True):
dom = resource_operation_add(
dom,
res_id,
- element,
+ op_argv,
validate_strict=False,
before_op=updating_op_before,
)
diff --git a/pcs_test/resources/cib-empty-3.3.xml b/pcs_test/resources/cib-empty-3.3.xml
index 3a44fe08..4de94b6e 100644
--- a/pcs_test/resources/cib-empty-3.3.xml
+++ b/pcs_test/resources/cib-empty-3.3.xml
@@ -1,4 +1,4 @@
-<cib epoch="557" num_updates="122" admin_epoch="0" validate-with="pacemaker-3.3" crm_feature_set="3.1.0" update-origin="rh7-3" update-client="crmd" cib-last-written="Thu Aug 23 16:49:17 2012" have-quorum="0" dc-uuid="2">
+<cib epoch="557" num_updates="122" admin_epoch="0" validate-with="pacemaker-3.3" crm_feature_set="3.4.0" update-origin="rh7-3" update-client="crmd" cib-last-written="Thu Aug 23 16:49:17 2012" have-quorum="0" dc-uuid="2">
<configuration>
<crm_config/>
<nodes>
diff --git a/pcs_test/resources/cib-empty-3.4.xml b/pcs_test/resources/cib-empty-3.4.xml
index dcd4ff44..e677462d 100644
--- a/pcs_test/resources/cib-empty-3.4.xml
+++ b/pcs_test/resources/cib-empty-3.4.xml
@@ -1,4 +1,4 @@
-<cib epoch="557" num_updates="122" admin_epoch="0" validate-with="pacemaker-3.4" crm_feature_set="3.1.0" update-origin="rh7-3" update-client="crmd" cib-last-written="Thu Aug 23 16:49:17 2012" have-quorum="0" dc-uuid="2">
+<cib epoch="557" num_updates="122" admin_epoch="0" validate-with="pacemaker-3.4" crm_feature_set="3.4.0" update-origin="rh7-3" update-client="crmd" cib-last-written="Thu Aug 23 16:49:17 2012" have-quorum="0" dc-uuid="2">
<configuration>
<crm_config/>
<nodes>
diff --git a/pcs_test/tier0/lib/cib/test_resource_operations.py b/pcs_test/tier0/lib/cib/test_resource_operations.py
index 5e556cf4..e5be7a54 100644
--- a/pcs_test/tier0/lib/cib/test_resource_operations.py
+++ b/pcs_test/tier0/lib/cib/test_resource_operations.py
@@ -316,13 +316,14 @@ class ValidateOperation(TestCase):
option_value="b",
option_name="on-fail",
allowed_values=[
- "ignore",
"block",
- "stop",
- "restart",
- "standby",
+ "demote",
"fence",
+ "ignore",
+ "restart",
"restart-container",
+ "standby",
+ "stop",
],
cannot_be_empty=False,
forbidden_characters=None,
diff --git a/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py b/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py
index 725ec68a..4da3fa0a 100644
--- a/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py
+++ b/pcs_test/tier0/lib/commands/remote_node/test_node_add_remote.py
@@ -118,7 +118,7 @@ FIXTURE_RESOURCES_TEMPLATE = """
interval="0s" name="migrate_to" timeout="60"
/>
<op id="node-name-monitor-interval-60s"
- interval="60s" name="monitor" timeout="30"
+ interval="60s" name="monitor" timeout="30" {onfail}
/>
<op id="node-name-reload-interval-0s"
interval="0s" name="reload" timeout="60"
@@ -133,7 +133,9 @@ FIXTURE_RESOURCES_TEMPLATE = """
</primitive>
</resources>
"""
-FIXTURE_RESOURCES = FIXTURE_RESOURCES_TEMPLATE.format(server="remote-host")
+FIXTURE_RESOURCES = FIXTURE_RESOURCES_TEMPLATE.format(
+ server="remote-host", onfail=""
+)
class AddRemote(TestCase):
@@ -178,12 +180,48 @@ class AddRemote(TestCase):
.local.push_existing_authkey_to_remote(NODE_NAME, NODE_DEST_LIST)
.local.run_pacemaker_remote(NODE_NAME, NODE_DEST_LIST)
.env.push_cib(
- resources=FIXTURE_RESOURCES_TEMPLATE.format(server=NODE_NAME)
+ resources=FIXTURE_RESOURCES_TEMPLATE.format(
+ server=NODE_NAME, onfail=""
+ )
)
)
node_add_remote(self.env_assist.get_env(), node_addr=NODE_NAME)
self.env_assist.assert_reports(REPORTS)
+ def test_cib_upgrade_on_onfail_demote(self):
+ self._config_success_base()
+ self.config.runner.cib.load(
+ filename="cib-empty-3.4.xml", instead="runner.cib.load",
+ )
+ self.config.runner.cib.upgrade(before="runner.cib.load")
+ self.config.runner.cib.load(
+ filename="cib-empty-3.3.xml",
+ name="load_cib_old_version",
+ before="runner.cib.upgrade",
+ )
+ self.config.env.push_cib(
+ resources=FIXTURE_RESOURCES_TEMPLATE.format(
+ server="remote-host", onfail='on-fail="demote"'
+ ),
+ instead="env.push_cib",
+ )
+ node_add_remote(
+ self.env_assist.get_env(),
+ operations=[
+ {
+ "name": "monitor",
+ "timeout": "30",
+ "interval": "60s",
+ "on-fail": "demote",
+ }
+ ],
+ )
+ self.env_assist.assert_reports(
+ REPORTS.info(
+ "cib_upgrade_successful", reports.codes.CIB_UPGRADE_SUCCESSFUL
+ )
+ )
+
def test_node_name_conflict_report_is_unique(self):
(
self.config.runner.cib.load(
@@ -623,7 +661,7 @@ class NotLive(TestCase):
.runner.pcmk.load_agent(agent_name="ocf:pacemaker:remote")
.env.push_cib(
resources=FIXTURE_RESOURCES_TEMPLATE.format(
- server=NODE_ADDR_PCSD
+ server=NODE_ADDR_PCSD, onfail=""
)
)
)
@@ -648,7 +686,9 @@ class NotLive(TestCase):
self.config.runner.cib.load()
.runner.pcmk.load_agent(agent_name="ocf:pacemaker:remote")
.env.push_cib(
- resources=FIXTURE_RESOURCES_TEMPLATE.format(server=NODE_NAME)
+ resources=FIXTURE_RESOURCES_TEMPLATE.format(
+ server=NODE_NAME, onfail=""
+ )
)
)
node_add_remote(self.env_assist.get_env(), no_node_addr=True)
@@ -672,7 +712,9 @@ class NotLive(TestCase):
self.config.runner.cib.load()
.runner.pcmk.load_agent(agent_name="ocf:pacemaker:remote")
.env.push_cib(
- resources=FIXTURE_RESOURCES_TEMPLATE.format(server="addr")
+ resources=FIXTURE_RESOURCES_TEMPLATE.format(
+ server="addr", onfail=""
+ )
)
)
node_add_remote(self.env_assist.get_env(), node_addr="addr")
diff --git a/pcs_test/tier0/lib/commands/resource/test_resource_create.py b/pcs_test/tier0/lib/commands/resource/test_resource_create.py
index dc70ce22..a040a9d9 100644
--- a/pcs_test/tier0/lib/commands/resource/test_resource_create.py
+++ b/pcs_test/tier0/lib/commands/resource/test_resource_create.py
@@ -35,13 +35,19 @@ def create(
)
-def create_group(env, wait=TIMEOUT, disabled=False, meta_attributes=None):
+def create_group(
+ env,
+ wait=TIMEOUT,
+ disabled=False,
+ meta_attributes=None,
+ operation_list=None,
+):
return resource.create_in_group(
env,
"A",
"ocf:heartbeat:Dummy",
"G",
- operation_list=[],
+ operation_list=operation_list if operation_list else [],
meta_attributes=meta_attributes if meta_attributes else {},
instance_attributes={},
wait=wait,
@@ -50,13 +56,18 @@ def create_group(env, wait=TIMEOUT, disabled=False, meta_attributes=None):
def create_clone(
- env, wait=TIMEOUT, disabled=False, meta_attributes=None, clone_options=None
+ env,
+ wait=TIMEOUT,
+ disabled=False,
+ meta_attributes=None,
+ clone_options=None,
+ operation_list=None,
):
return resource.create_as_clone(
env,
"A",
"ocf:heartbeat:Dummy",
- operation_list=[],
+ operation_list=operation_list if operation_list else [],
meta_attributes=meta_attributes if meta_attributes else {},
instance_attributes={},
clone_meta_options=clone_options if clone_options else {},
@@ -71,12 +82,13 @@ def create_bundle(
disabled=False,
meta_attributes=None,
allow_not_accessible_resource=False,
+ operation_list=None,
):
return resource.create_into_bundle(
env,
"A",
"ocf:heartbeat:Dummy",
- operation_list=[],
+ operation_list=operation_list if operation_list else [],
meta_attributes=meta_attributes if meta_attributes else {},
instance_attributes={},
bundle_id="B",
@@ -576,6 +588,60 @@ class Create(TestCase):
]
)
+ def test_cib_upgrade_on_onfail_demote(self):
+ self.config.runner.cib.load(
+ filename="cib-empty-3.3.xml",
+ instead="runner.cib.load",
+ name="load_cib_old_version",
+ )
+ self.config.runner.cib.upgrade()
+ self.config.runner.cib.load(filename="cib-empty-3.4.xml")
+ self.config.env.push_cib(
+ resources="""
+ <resources>
+ <primitive class="ocf" id="A" provider="heartbeat"
+ type="Dummy"
+ >
+ <operations>
+ <op id="A-migrate_from-interval-0s" interval="0s"
+ name="migrate_from" timeout="20"
+ />
+ <op id="A-migrate_to-interval-0s" interval="0s"
+ name="migrate_to" timeout="20"
+ />
+ <op id="A-monitor-interval-10" interval="10"
+ name="monitor" timeout="10" on-fail="demote"
+ />
+ <op id="A-reload-interval-0s" interval="0s"
+ name="reload" timeout="20"
+ />
+ <op id="A-start-interval-0s" interval="0s"
+ name="start" timeout="20"
+ />
+ <op id="A-stop-interval-0s" interval="0s"
+ name="stop" timeout="20"
+ />
+ </operations>
+ </primitive>
+ </resources>
+ """
+ )
+
+ create(
+ self.env_assist.get_env(),
+ operation_list=[
+ {
+ "name": "monitor",
+ "timeout": "10",
+ "interval": "10",
+ "on-fail": "demote",
+ }
+ ],
+ )
+ self.env_assist.assert_reports(
+ [fixture.info(report_codes.CIB_UPGRADE_SUCCESSFUL)]
+ )
+
class CreateWait(TestCase):
def setUp(self):
@@ -746,6 +812,66 @@ class CreateInGroup(TestCase):
create_group(self.env_assist.get_env(), wait=False)
+ def test_cib_upgrade_on_onfail_demote(self):
+ self.config.remove(name="runner.pcmk.can_wait")
+ self.config.runner.cib.load(
+ filename="cib-empty-3.3.xml",
+ instead="runner.cib.load",
+ name="load_cib_old_version",
+ )
+ self.config.runner.cib.upgrade()
+ self.config.runner.cib.load(filename="cib-empty-3.4.xml")
+ self.config.env.push_cib(
+ resources="""
+ <resources>
+ <group id="G">
+ <primitive class="ocf" id="A" provider="heartbeat"
+ type="Dummy"
+ >
+ <operations>
+ <op id="A-migrate_from-interval-0s"
+ interval="0s" name="migrate_from"
+ timeout="20"
+ />
+ <op id="A-migrate_to-interval-0s"
+ interval="0s" name="migrate_to"
+ timeout="20"
+ />
+ <op id="A-monitor-interval-10" interval="10"
+ name="monitor" timeout="10" on-fail="demote"
+ />
+ <op id="A-reload-interval-0s" interval="0s"
+ name="reload" timeout="20"
+ />
+ <op id="A-start-interval-0s" interval="0s"
+ name="start" timeout="20"
+ />
+ <op id="A-stop-interval-0s" interval="0s"
+ name="stop" timeout="20"
+ />
+ </operations>
+ </primitive>
+ </group>
+ </resources>
+ """
+ )
+
+ create_group(
+ self.env_assist.get_env(),
+ operation_list=[
+ {
+ "name": "monitor",
+ "timeout": "10",
+ "interval": "10",
+ "on-fail": "demote",
+ }
+ ],
+ wait=False,
+ )
+ self.env_assist.assert_reports(
+ [fixture.info(report_codes.CIB_UPGRADE_SUCCESSFUL)]
+ )
+
def test_fail_wait(self):
self.config.env.push_cib(
resources=fixture_cib_resources_xml_group_simplest,
@@ -859,6 +985,62 @@ class CreateAsClone(TestCase):
)
create_clone(self.env_assist.get_env(), wait=False)
+ def test_cib_upgrade_on_onfail_demote(self):
+ self.config.remove(name="runner.pcmk.can_wait")
+ self.config.runner.cib.load(
+ filename="cib-empty-3.3.xml",
+ instead="runner.cib.load",
+ name="load_cib_old_version",
+ )
+ self.config.runner.cib.upgrade()
+ self.config.runner.cib.load(filename="cib-empty-3.4.xml")
+ self.config.env.push_cib(
+ resources="""<resources>
+ <clone id="A-clone">
+ <primitive class="ocf" id="A" provider="heartbeat"
+ type="Dummy"
+ >
+ <operations>
+ <op id="A-migrate_from-interval-0s" interval="0s"
+ name="migrate_from" timeout="20"
+ />
+ <op id="A-migrate_to-interval-0s" interval="0s"
+ name="migrate_to" timeout="20"
+ />
+ <op id="A-monitor-interval-10" interval="10"
+ name="monitor" timeout="10" on-fail="demote"
+ />
+ <op id="A-reload-interval-0s" interval="0s"
+ name="reload" timeout="20"
+ />
+ <op id="A-start-interval-0s" interval="0s"
+ name="start" timeout="20"
+ />
+ <op id="A-stop-interval-0s" interval="0s"
+ name="stop" timeout="20"
+ />
+ </operations>
+ </primitive>
+ </clone>
+ </resources>"""
+ )
+
+ create_clone(
+ self.env_assist.get_env(),
+ operation_list=[
+ {
+ "name": "monitor",
+ "timeout": "10",
+ "interval": "10",
+ "on-fail": "demote",
+ }
+ ],
+ wait=False,
+ )
+ self.env_assist.assert_reports(
+ [fixture.info(report_codes.CIB_UPGRADE_SUCCESSFUL)]
+ )
+
def test_fail_wait(self):
self.config.env.push_cib(
resources=fixture_cib_resources_xml_clone_simplest,
@@ -1168,7 +1350,7 @@ class CreateInToBundle(TestCase):
name="migrate_to" timeout="20"
/>
<op id="A-monitor-interval-10" interval="10"
- name="monitor" timeout="20"
+ name="monitor" timeout="20" {onfail}
/>
<op id="A-reload-interval-0s" interval="0s" name="reload"
timeout="20"
@@ -1190,7 +1372,8 @@ class CreateInToBundle(TestCase):
fixture_resource_post_simple_without_network.format(
network="""
<network control-port="12345" ip-range-start="192.168.100.200"/>
- """
+ """,
+ onfail=""
)
)
# fmt: on
@@ -1290,6 +1473,42 @@ class CreateInToBundle(TestCase):
[fixture.info(report_codes.CIB_UPGRADE_SUCCESSFUL)]
)
+ def test_cib_upgrade_on_onfail_demote(self):
+ self.config.runner.pcmk.load_agent()
+ self.config.runner.cib.load(
+ filename="cib-empty-3.3.xml", name="load_cib_old_version",
+ )
+ self.config.runner.cib.upgrade()
+ self.config.runner.cib.load(
+ filename="cib-empty-3.4.xml", resources=self.fixture_resources_pre
+ )
+ self.config.env.push_cib(
+ resources=self.fixture_resource_post_simple_without_network.format(
+ network="""
+ <network
+ control-port="12345" ip-range-start="192.168.100.200"
+ />
+ """,
+ onfail='on-fail="demote"',
+ )
+ )
+
+ create_bundle(
+ self.env_assist.get_env(),
+ operation_list=[
+ {
+ "name": "monitor",
+ "timeout": "20",
+ "interval": "10",
+ "on-fail": "demote",
+ }
+ ],
+ wait=False,
+ )
+ self.env_assist.assert_reports(
+ [fixture.info(report_codes.CIB_UPGRADE_SUCCESSFUL)]
+ )
+
def test_simplest_resource(self):
(
self.config.runner.pcmk.load_agent()
@@ -1504,7 +1723,7 @@ class CreateInToBundle(TestCase):
.env.push_cib(
resources=(
self.fixture_resource_post_simple_without_network.format(
- network=""
+ network="", onfail=""
)
)
)
@@ -1540,7 +1759,7 @@ class CreateInToBundle(TestCase):
.env.push_cib(
resources=(
self.fixture_resource_post_simple_without_network.format(
- network=network
+ network=network, onfail=""
)
)
)
diff --git a/pcs_test/tier1/cib_resource/test_create.py b/pcs_test/tier1/cib_resource/test_create.py
index 977d627a..5ff83b71 100644
--- a/pcs_test/tier1/cib_resource/test_create.py
+++ b/pcs_test/tier1/cib_resource/test_create.py
@@ -1143,7 +1143,7 @@ class FailOrWarnOp(ResourceTest):
" monitor on-fail=Abc",
(
"Error: 'Abc' is not a valid on-fail value, use 'block', "
- "'fence', 'ignore', 'restart', 'restart-container', "
+ "'demote', 'fence', 'ignore', 'restart', 'restart-container', "
"'standby', 'stop'\n" + ERRORS_HAVE_OCURRED
),
)
diff --git a/pcs_test/tier1/legacy/test_resource.py b/pcs_test/tier1/legacy/test_resource.py
index 7ffcc83b..107ff406 100644
--- a/pcs_test/tier1/legacy/test_resource.py
+++ b/pcs_test/tier1/legacy/test_resource.py
@@ -22,6 +22,7 @@ from pcs_test.tools.misc import (
is_minimum_pacemaker_version,
outdent,
skip_unless_pacemaker_supports_bundle,
+ skip_unless_pacemaker_supports_op_onfail_demote,
skip_unless_crm_rule,
write_data_to_tmpfile,
write_file_to_tmpfile,
@@ -723,6 +724,28 @@ monitor interval=60s OCF_CHECK_LEVEL=1 (OPTest7-monitor-interval-60s)
),
)
+ @skip_unless_pacemaker_supports_op_onfail_demote()
+ def test_add_operation_onfail_demote_upgrade_cib(self):
+ write_file_to_tmpfile(rc("cib-empty-3.3.xml"), self.temp_cib)
+ self.assert_pcs_success(
+ "resource create --no-default-ops R ocf:pacemaker:Dummy"
+ )
+ self.assert_pcs_success(
+ "resource op add R start on-fail=demote",
+ stdout_full="Cluster CIB has been upgraded to latest version\n",
+ )
+
+ @skip_unless_pacemaker_supports_op_onfail_demote()
+ def test_update_add_operation_onfail_demote_upgrade_cib(self):
+ write_file_to_tmpfile(rc("cib-empty-3.3.xml"), self.temp_cib)
+ self.assert_pcs_success(
+ "resource create --no-default-ops R ocf:pacemaker:Dummy"
+ )
+ self.assert_pcs_success(
+ "resource update R op start on-fail=demote",
+ stdout_full="Cluster CIB has been upgraded to latest version\n",
+ )
+
def _test_delete_remove_operation(self, command):
assert command in {"delete", "remove"}
diff --git a/pcs_test/tools/misc.py b/pcs_test/tools/misc.py
index 33d78002..820f1e79 100644
--- a/pcs_test/tools/misc.py
+++ b/pcs_test/tools/misc.py
@@ -253,6 +253,12 @@ def skip_unless_pacemaker_supports_rsc_and_op_rules():
)
+def skip_unless_pacemaker_supports_op_onfail_demote():
+ return skip_unless_cib_schema_version(
+ (3, 4, 0), "resource operations with 'on-fail' option set to 'demote'"
+ )
+
+
def skip_if_service_enabled(service_name):
return skipUnless(
not is_service_enabled(runner, service_name),
diff --git a/pcsd/capabilities.xml b/pcsd/capabilities.xml
index 6e1886cb..09983354 100644
--- a/pcsd/capabilities.xml
+++ b/pcsd/capabilities.xml
@@ -465,6 +465,13 @@
pcs commands: cluster node ( add-remote | delete-remote | remove-remote )
</description>
</capability>
+ <capability id="node.remote.onfail-demote" in-pcs="1" in-pcsd="0">
+ <description>
+ Support for "demote" value of resource operation's "on-fail" option
+
+ pcs commands: cluster node add-remote
+ </description>
+ </capability>
@@ -1056,6 +1063,13 @@
pcs commands: resource create ... op
</description>
</capability>
+ <capability id="pcmk.resource.create.operations.onfail-demote" in-pcs="1" in-pcsd="0">
+ <description>
+ Support for "demote" value of resource operation's "on-fail" option
+
+ pcs commands: resource create ... op
+ </description>
+ </capability>
<capability id="pcmk.resource.create.wait" in-pcs="1" in-pcsd="0">
<description>
Wait for the created resource to start.
@@ -1105,6 +1119,13 @@
pcs commands: resource update ... op
</description>
</capability>
+ <capability id="pcmk.resource.update.operations.onfail-demote" in-pcs="1" in-pcsd="0">
+ <description>
+ Support for "demote" value of resource operation's "on-fail" option
+
+ pcs commands: resource update ... op
+ </description>
+ </capability>
<capability id="pcmk.resource.update.wait" in-pcs="1" in-pcsd="0">
<description>
Wait for the changes to take effect.
@@ -1143,6 +1164,13 @@
pcs commands: resource op ( add | delete | remove )
</description>
</capability>
+ <capability id="pcmk.resource.update-operations.onfail-demote" in-pcs="1" in-pcsd="0">
+ <description>
+ Support for "demote" value of resource operation's "on-fail" option
+
+ pcs commands: resource op add
+ </description>
+ </capability>
<capability id="pcmk.resource.group" in-pcs="1" in-pcsd="1">
<description>
@@ -1555,6 +1583,20 @@
pcs commands: stonith create ... op
</description>
</capability>
+ <capability id="pcmk.stonith.create.operations" in-pcs="1" in-pcsd="0">
+ <description>
+ Set resource operations when creating a stonith resource.
+
+ pcs commands: stonith create ... op
+ </description>
+ </capability>
+ <capability id="pcmk.stonith.create.operations.onfail-demote" in-pcs="1" in-pcsd="0">
+ <description>
+ Support for "demote" value of resource operation's "on-fail" option
+
+ pcs commands: stonith create ... op
+ </description>
+ </capability>
<capability id="pcmk.stonith.create.wait" in-pcs="1" in-pcsd="0">
<description>
Wait for the created resource to start.
--
2.25.4