Blame SOURCES/bz1843079-01-upgrade-CIB-schema-for-on-fail-demote.patch

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