Example #1
0
def _validate_map_ids_exist(bundle_el, map_type, map_label, id_list):
    report_list = []
    for id in id_list:
        try:
            find_element_by_tag_and_id(
                map_type, bundle_el, id, id_description=map_label
            )
        except LibraryError as e:
            report_list.extend(e.args)
    return report_list
Example #2
0
def _find_resources_or_raise(
    resources_section, resource_ids, additional_search=None
):
    if not additional_search:
        additional_search = lambda x: [x]
    report_list = []
    resource_el_list = []
    resource_tags = (
        resource.clone.ALL_TAGS
        +
        [resource.group.TAG, resource.primitive.TAG, resource.bundle.TAG]
    )
    for res_id in resource_ids:
        try:
            resource_el_list.extend(
                additional_search(
                    find_element_by_tag_and_id(
                        resource_tags,
                        resources_section,
                        res_id
                    )
                )
            )
        except LibraryError as e:
            report_list.extend(e.args)
    if report_list:
        raise LibraryError(*report_list)
    return resource_el_list
Example #3
0
def find_valid_resource_id(report_processor, cib, in_clone_allowed, id):
    parent_tags = resource.clone.ALL_TAGS + [resource.bundle.TAG]
    resource_element = find_element_by_tag_and_id(
        parent_tags + [resource.primitive.TAG, resource.group.TAG],
        cib,
        id,
    )

    if resource_element.tag in parent_tags:
        return resource_element.attrib["id"]

    clone = find_parent(resource_element, parent_tags)
    if clone is None:
        return resource_element.attrib["id"]

    if in_clone_allowed:
        report_processor.process(
            reports.resource_for_constraint_is_multiinstance(
                resource_element.attrib["id"],
                "clone" if clone.tag == "master" else clone.tag,
                clone.attrib["id"],
                ReportItemSeverity.WARNING,
            ))
        return resource_element.attrib["id"]

    raise LibraryError(
        reports.resource_for_constraint_is_multiinstance(
            resource_element.attrib["id"],
            "clone" if clone.tag == "master" else clone.tag,
            clone.attrib["id"],
            ReportItemSeverity.ERROR,
            #repair to clone is workaround for web ui, so we put only information
            #about one forceable possibility
            forceable=report_codes.FORCE_CONSTRAINT_MULTIINSTANCE_RESOURCE))
Example #4
0
def find_valid_resource_id(report_processor: ReportProcessor, cib,
                           in_clone_allowed, _id):
    parent_tags = resource.clone.ALL_TAGS + [resource.bundle.TAG]
    resource_element = find_element_by_tag_and_id(
        sorted(parent_tags + [resource.primitive.TAG, resource.group.TAG]),
        cib,
        _id,
    )

    if resource_element.tag in parent_tags:
        return resource_element.attrib["id"]

    clone = find_parent(resource_element, parent_tags)
    if clone is None:
        return resource_element.attrib["id"]

    report_msg = reports.messages.ResourceForConstraintIsMultiinstance(
        resource_element.attrib["id"],
        "clone" if clone.tag == "master" else clone.tag,
        clone.attrib["id"],
    )
    if in_clone_allowed:
        if report_processor.report(ReportItem.warning(report_msg)).has_errors:
            raise LibraryError()
        return resource_element.attrib["id"]

    raise LibraryError(
        ReportItem.error(
            report_msg,
            force_code=reports.codes.FORCE,
        ))
Example #5
0
def _find_resources_or_raise(resources_section,
                             resource_ids,
                             additional_search=None):
    if not additional_search:
        additional_search = lambda x: [x]
    report_list = []
    resource_el_list = []
    resource_tags = (
        resource.clone.ALL_TAGS +
        [resource.group.TAG, resource.primitive.TAG, resource.bundle.TAG])
    for res_id in resource_ids:
        try:
            resource_el_list.extend(
                additional_search(
                    find_element_by_tag_and_id(
                        resource_tags,
                        resources_section,
                        res_id,
                        # pacemaker-2.0 deprecated masters. We treat masters as
                        # clones. Do not report we were looking for a master,
                        # say we were looking for a clone instead. Since we
                        # look for all resource types including clones, just
                        # drop the master.
                        id_types=[
                            tag for tag in resource_tags
                            if tag != resource.clone.TAG_MASTER
                        ])))
        except LibraryError as e:
            report_list.extend(e.args)
    if report_list:
        raise LibraryError(*report_list)
    return resource_el_list
Example #6
0
 def test_raises_when_is_under_another_context(self):
     tree = etree.fromstring("""
         <cib>
             <resources>
                 <group id="g1"><primitive id="a"/></group>
                 <group id="g2"><primitive id="b"/></group>
             </resources>
         </cib>
     """)
     assert_raise_library_error(
         lambda: lib.find_element_by_tag_and_id(
             "primitive",
             tree.find('.//resources/group[@id="g2"]'),
             "a"
         ),
         (
             severities.ERROR,
             report_codes.OBJECT_WITH_ID_IN_UNEXPECTED_CONTEXT,
             {
                 "type": "primitive",
                 "id": "a",
                 "expected_context_type": "group",
                 "expected_context_id": "g2",
             },
         ),
     )
Example #7
0
 def test_raises_when_is_under_another_context(self):
     tree = etree.fromstring("""
         <cib>
             <resources>
                 <group id="g1"><primitive id="a"/></group>
                 <group id="g2"><primitive id="b"/></group>
             </resources>
         </cib>
     """)
     assert_raise_library_error(
         lambda: lib.find_element_by_tag_and_id(
             "primitive",
             tree.find('.//resources/group[@id="g2"]'),
             "a"
         ),
         (
             severities.ERROR,
             report_codes.OBJECT_WITH_ID_IN_UNEXPECTED_CONTEXT,
             {
                 "type": "primitive",
                 "id": "a",
                 "expected_context_type": "group",
                 "expected_context_id": "g2",
             },
         ),
     )
Example #8
0
def node_add_guest(
    env,
    node_name,
    resource_id,
    options,
    allow_incomplete_distribution=False,
    allow_pacemaker_remote_service_fail=False,
    wait=False,
):
    """
    setup resource (resource_id) as guest node and setup node as guest

    LibraryEnvironment env provides all for communication with externals
    string resource_id -- specifies resource that should be guest node
    dict options could contain keys remote-node, remote-port, remote-addr,
        remote-connect-timeout
    bool allow_incomplete_distribution -- is a flag for allowing successfully
        finish this command even if is file distribution not succeeded
    bool allow_pacemaker_remote_service_fail -- is a flag for allowing
        successfully finish this command even if starting/enabling
        pacemaker_remote not succeeded
    mixed wait is flag for controlling waiting for pacemaker iddle mechanism
    """
    _ensure_consistently_live_env(env)
    env.ensure_wait_satisfiable(wait)

    cib = env.get_cib()
    current_nodes = get_nodes(env.get_corosync_conf(), cib)

    report_list = guest_node.validate_set_as_guest(cib, current_nodes,
                                                   node_name, options)
    try:
        resource_element = find_element_by_tag_and_id(primitive.TAG,
                                                      get_resources(cib),
                                                      resource_id)
        report_list.extend(guest_node.validate_is_not_guest(resource_element))
    except LibraryError as e:
        report_list.extend(e.args)

    env.report_processor.process_list(report_list)

    guest_node.set_as_guest(
        resource_element,
        node_name,
        options.get("remote-addr", None),
        options.get("remote-port", None),
        options.get("remote-connect-timeout", None),
    )

    _prepare_pacemaker_remote_environment(
        env,
        current_nodes,
        guest_node.get_host_from_options(node_name, options),
        allow_incomplete_distribution,
        allow_pacemaker_remote_service_fail,
    )

    env.push_cib(cib, wait)
    if wait:
        _ensure_resource_running(env, resource_id)
Example #9
0
def find_valid_resource_id(report_processor: ReportProcessor, cib,
                           in_clone_allowed, _id):
    parent_tags = resource.clone.ALL_TAGS + [resource.bundle.TAG]
    resource_element = find_element_by_tag_and_id(
        sorted(parent_tags + [resource.primitive.TAG, resource.group.TAG]),
        cib,
        _id,
    )

    if resource_element.tag in parent_tags:
        return resource_element.attrib["id"]

    clone = find_parent(resource_element, parent_tags)
    if clone is None:
        return resource_element.attrib["id"]

    report_msg = reports.messages.ResourceForConstraintIsMultiinstance(
        resource_element.attrib["id"],
        "clone" if clone.tag == "master" else clone.tag,
        clone.attrib["id"],
    )
    if in_clone_allowed:
        if report_processor.report(ReportItem.warning(report_msg)).has_errors:
            raise LibraryError()
        return resource_element.attrib["id"]

    raise LibraryError(
        ReportItem.error(
            report_msg,
            # repair to clone is workaround for web ui, so we put only
            # information about one forceable possibility
            force_code=reports.codes.FORCE_CONSTRAINT_MULTIINSTANCE_RESOURCE,
        ))
Example #10
0
def find_valid_resource_id(report_processor, cib, in_clone_allowed, _id):
    parent_tags = resource.clone.ALL_TAGS + [resource.bundle.TAG]
    resource_element = find_element_by_tag_and_id(
        parent_tags + [resource.primitive.TAG, resource.group.TAG],
        cib,
        _id,
    )

    if resource_element.tag in parent_tags:
        return resource_element.attrib["id"]

    clone = find_parent(resource_element, parent_tags)
    if clone is None:
        return resource_element.attrib["id"]

    if in_clone_allowed:
        report_processor.process(
            reports.resource_for_constraint_is_multiinstance(
                resource_element.attrib["id"],
                "clone" if clone.tag == "master" else clone.tag,
                clone.attrib["id"],
                ReportItemSeverity.WARNING,
            )
        )
        return resource_element.attrib["id"]

    raise LibraryError(reports.resource_for_constraint_is_multiinstance(
        resource_element.attrib["id"],
        "clone" if clone.tag == "master" else clone.tag,
        clone.attrib["id"],
        ReportItemSeverity.ERROR,
        #repair to clone is workaround for web ui, so we put only information
        #about one forceable possibility
        forceable=report_codes.FORCE_CONSTRAINT_MULTIINSTANCE_RESOURCE
    ))
Example #11
0
def _find(
    tag, acl_section, element_id, none_if_id_unused=False, id_types=None
):
    return find_element_by_tag_and_id(
        tag,
        acl_section,
        element_id,
        id_types=id_types,
        none_if_id_unused=none_if_id_unused,
    )
Example #12
0
def _find(tag,
          acl_section,
          element_id,
          none_if_id_unused=False,
          id_types=None):
    return find_element_by_tag_and_id(
        tag,
        acl_section,
        element_id,
        id_types=id_types,
        none_if_id_unused=none_if_id_unused,
    )
Example #13
0
def bundle_update(
    env,
    bundle_id,
    container_options=None,
    network_options=None,
    port_map_add=None,
    port_map_remove=None,
    storage_map_add=None,
    storage_map_remove=None,
    force_options=False,
    wait=False,
):
    """
    Modify an existing bundle (does not touch encapsulated resources)

    LibraryEnvironment env -- provides communication with externals
    string bundle_id -- id of the bundle to modify
    dict container_options -- container options to modify
    dict network_options -- network options to modify
    list of dict port_map_add -- list of port mapping options to add
    list of string port_map_remove -- list of port mapping ids to remove
    list of dict storage_map_add -- list of storage mapping options to add
    list of string storage_map_remove -- list of storage mapping ids to remove
    bool force_options -- return warnings instead of forceable errors
    mixed wait -- False: no wait, None: wait default timeout, int: wait timeout
    """
    container_options = container_options or {}
    network_options = network_options or {}
    port_map_add = port_map_add or []
    port_map_remove = port_map_remove or []
    storage_map_add = storage_map_add or []
    storage_map_remove = storage_map_remove or []

    with resource_environment(
            env,
            wait,
        [bundle_id],
            # bundles are always enabled, currently there is no way to disable them
            disabled_after_wait=False,
            required_cib_version=(2, 8, 0)) as resources_section:
        id_provider = IdProvider(resources_section)
        bundle_element = find_element_by_tag_and_id(resource.bundle.TAG,
                                                    resources_section,
                                                    bundle_id)
        env.report_processor.process_list(
            resource.bundle.validate_update(id_provider, bundle_element,
                                            container_options, network_options,
                                            port_map_add, port_map_remove,
                                            storage_map_add,
                                            storage_map_remove, force_options))
        resource.bundle.update(id_provider, bundle_element, container_options,
                               network_options, port_map_add, port_map_remove,
                               storage_map_add, storage_map_remove)
Example #14
0
 def test_returns_element_when_exists_one_of_tags(self):
     tree = etree.fromstring("""
         <cib>
             <resources>
                 <group id="a"/>
                 <primitive id="b"/>
             </resources>
         </cib>
     """)
     element = lib.find_element_by_tag_and_id(["group", "primitive"],
                                              tree.find(".//resources"),
                                              "a")
     self.assertEqual("group", element.tag)
     self.assertEqual("a", element.attrib["id"])
Example #15
0
def _find(
    tag, acl_section, element_id, none_if_id_unused=False, id_description=None
):
    if tag not in TAG_DESCRIPTION_MAP.keys():
        raise AssertionError("Unknown acl tag '{0}'".format(tag))

    return find_element_by_tag_and_id(
        tag,
        acl_section,
        element_id,
        id_description=id_description if id_description
            else TAG_DESCRIPTION_MAP[tag]
        ,
        none_if_id_unused=none_if_id_unused,
    )
Example #16
0
def provide_group(resources_section, group_id):
    """
    Provide group with id=group_id. Create new group if group with id=group_id
    does not exists.

    etree.Element resources_section is place where new group will be appended
    string group_id is id of group
    """
    group_element = find_element_by_tag_and_id(TAG,
                                               resources_section,
                                               group_id,
                                               none_if_id_unused=True)
    if group_element is None:
        group_element = etree.SubElement(resources_section, TAG, id=group_id)
    return group_element
Example #17
0
def _find(
    tag, acl_section, element_id, none_if_id_unused=False, id_description=None
):
    if tag not in TAG_DESCRIPTION_MAP.keys():
        raise AssertionError("Unknown acl tag '{0}'".format(tag))

    return find_element_by_tag_and_id(
        tag,
        acl_section,
        element_id,
        id_description=id_description if id_description
            else TAG_DESCRIPTION_MAP[tag]
        ,
        none_if_id_unused=none_if_id_unused,
    )
Example #18
0
 def test_returns_element_when_exists_one_of_tags(self):
     tree = etree.fromstring("""
         <cib>
             <resources>
                 <group id="a"/>
                 <primitive id="b"/>
             </resources>
         </cib>
     """)
     element = lib.find_element_by_tag_and_id(
         ["group", "primitive"],
         tree.find(".//resources"),
         "a"
     )
     self.assertEqual("group", element.tag)
     self.assertEqual("a", element.attrib["id"])
Example #19
0
def provide_group(resources_section, group_id):
    """
    Provide group with id=group_id. Create new group if group with id=group_id
    does not exists.

    etree.Element resources_section is place where new group will be appended
    string group_id is id of group
    """
    group_element = find_element_by_tag_and_id(
        TAG,
        resources_section,
        group_id,
        none_if_id_unused=True
    )
    if group_element is None:
        group_element = etree.SubElement(resources_section, TAG, id=group_id)
    return group_element
Example #20
0
def place_resource(
    group_element,
    primitive_element,
    adjacent_resource_id=None,
    put_after_adjacent=False,
):
    """
    Add resource into group. This function is also applicable for a modification
    of the resource position because the primitive element is replanted from
    anywhere (including group itself) to concrete place inside group.

    etree.Element group_element is element where to put primitive_element
    etree.Element primitive_element is element for placement
    string adjacent_resource_id is id of the existing resource in group.
        primitive_element will be put beside adjacent_resource_id if specified.
    bool put_after_adjacent is flag where put primitive_element:
        before adjacent_resource_id if put_after_adjacent=False
        after adjacent_resource_id if put_after_adjacent=True
        Note that it make sense only if adjacent_resource_id is specified
    """
    if primitive_element.attrib["id"] == adjacent_resource_id:
        raise LibraryError(
            ReportItem.error(
                reports.messages.CannotGroupResourceNextToItself(
                    adjacent_resource_id)))

    if not adjacent_resource_id:
        group_element.append(primitive_element)
        return

    adjacent_resource = find_element_by_tag_and_id(
        "primitive",
        group_element,
        adjacent_resource_id,
    )

    if put_after_adjacent and adjacent_resource.getnext() is None:
        group_element.append(primitive_element)
        return

    index = group_element.index(adjacent_resource.getnext(
    ) if put_after_adjacent else adjacent_resource)
    group_element.insert(index, primitive_element)
Example #21
0
def find_valid_resource_id(
    report_processor, cib, can_repair_to_clone, in_clone_allowed, id
):
    resource_element = find_element_by_tag_and_id(
        resource.clone.ALL_TAGS + [resource.primitive.TAG, resource.group.TAG],
        cib,
        id,
        id_description="resource"
    )

    if resource_element.tag in resource.clone.ALL_TAGS:
        return resource_element.attrib["id"]

    clone = find_parent(resource_element, resource.clone.ALL_TAGS)
    if clone is None:
        return resource_element.attrib["id"]

    if can_repair_to_clone:
        #this is workaround for web ui, console should not use it, so we do not
        #warn about it
        return clone.attrib["id"]

    if in_clone_allowed:
        report_processor.process(
            reports.resource_for_constraint_is_multiinstance(
                resource_element.attrib["id"],
                clone.tag,
                clone.attrib["id"],
                ReportItemSeverity.WARNING,
            )
        )
        return resource_element.attrib["id"]

    raise LibraryError(reports.resource_for_constraint_is_multiinstance(
        resource_element.attrib["id"],
        clone.tag,
        clone.attrib["id"],
        ReportItemSeverity.ERROR,
        #repair to clone is workaround for web ui, so we put only information
        #about one forceable possibility
        forceable=report_codes.FORCE_CONSTRAINT_MULTIINSTANCE_RESOURCE
    ))
Example #22
0
def place_resource(
    group_element, primitive_element,
    adjacent_resource_id=None, put_after_adjacent=False
):
    """
    Add resource into group. This function is also applicable for a modification
    of the resource position because the primitive element is replanted from
    anywhere (including group itself) to concrete place inside group.

    etree.Element group_element is element where to put primitive_element
    etree.Element primitive_element is element for placement
    string adjacent_resource_id is id of the existing resource in group.
        primitive_element will be put beside adjacent_resource_id if specified.
    bool put_after_adjacent is flag where put primitive_element:
        before adjacent_resource_id if put_after_adjacent=False
        after adjacent_resource_id if put_after_adjacent=True
        Note that it make sense only if adjacent_resource_id is specified
    """
    if primitive_element.attrib["id"] == adjacent_resource_id:
        raise LibraryError(reports.resource_cannot_be_next_to_itself_in_group(
            adjacent_resource_id,
            group_element.attrib["id"],
        ))

    if not adjacent_resource_id:
        return group_element.append(primitive_element)

    adjacent_resource = find_element_by_tag_and_id(
        "primitive",
        group_element,
        adjacent_resource_id,
        id_description="resource",
    )

    if put_after_adjacent and adjacent_resource.getnext() is None:
        return group_element.append(primitive_element)

    index = group_element.index(
        adjacent_resource.getnext() if put_after_adjacent
        else adjacent_resource
    )
    group_element.insert(index, primitive_element)
Example #23
0
def validate_resource_instance_attributes_update(
    resource_agent, instance_attributes, resource_id, resources_section,
    force=False
):
    return (
        resource_agent.validate_parameters_update(
            get_nvset_as_dict(
                "instance_attributes",
                find_element_by_tag_and_id(
                    "primitive", resources_section, resource_id
                )
            ),
            instance_attributes,
            force=force,
        )
        +
        validate_unique_instance_attributes(
            resource_agent, instance_attributes, resources_section,
            resource_id=resource_id, force=force,
        )
    )
Example #24
0
def create_into_bundle(
    env, resource_id, resource_agent_name,
    operations, 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,
):
    """
    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 operations contains attributes for each entered operation
    dict meta_attributes contains attributes for primitive/meta_attributes
    dict 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
        in a system
    bool 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
        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
        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 iddle mechanism
    bool allow_not_suitable_command -- flag for FORCE_NOT_SUITABLE_COMMAND
    """
    resource_agent = get_agent(
        env.report_processor,
        env.cmd_runner(),
        resource_agent_name,
        allow_absent_agent,
    )
    with resource_environment(
        env,
        wait,
        [resource_id],
        _ensure_disabled_after_wait(
            ensure_disabled
            or
            resource.common.are_meta_disabled(meta_attributes)
        ),
        required_cib_version=Version(2, 8, 0)
    ) as resources_section:
        _check_special_cases(
            env,
            resource_agent,
            resources_section,
            resource_id,
            meta_attributes,
            instance_attributes,
            allow_not_suitable_command
        )

        primitive_element = resource.primitive.create(
            env.report_processor, resources_section,
            resource_id, resource_agent,
            operations, meta_attributes, instance_attributes,
            allow_invalid_operation,
            allow_invalid_instance_attributes,
            use_default_operations,
        )
        if ensure_disabled:
            resource.common.disable(primitive_element)
        resource.bundle.add_resource(
            find_element_by_tag_and_id(
                resource.bundle.TAG,
                resources_section,
                bundle_id
            ),
            primitive_element
        )
Example #25
0
def bundle_update(
    env, bundle_id, container_options=None, network_options=None,
    port_map_add=None, port_map_remove=None, storage_map_add=None,
    storage_map_remove=None, meta_attributes=None,
    force_options=False,
    wait=False,
):
    """
    Modify an existing bundle (does not touch encapsulated resources)

    LibraryEnvironment env -- provides communication with externals
    string bundle_id -- id of the bundle to modify
    dict container_options -- container options to modify
    dict network_options -- network options to modify
    list of dict port_map_add -- list of port mapping options to add
    list of string port_map_remove -- list of port mapping ids to remove
    list of dict storage_map_add -- list of storage mapping options to add
    list of string storage_map_remove -- list of storage mapping ids to remove
    dict meta_attributes -- meta attributes to update
    bool force_options -- return warnings instead of forceable errors
    mixed wait -- False: no wait, None: wait default timeout, int: wait timeout
    """
    container_options = container_options or {}
    network_options = network_options or {}
    port_map_add = port_map_add or []
    port_map_remove = port_map_remove or []
    storage_map_add = storage_map_add or []
    storage_map_remove = storage_map_remove or []
    meta_attributes = meta_attributes or {}

    with resource_environment(
        env,
        wait,
        [bundle_id],
        required_cib_version=Version(2, 8, 0)
    ) as resources_section:
        # no need to run validations related to remote and guest nodes as those
        # nodes can only be created from primitive resources
        id_provider = IdProvider(resources_section)
        bundle_element = find_element_by_tag_and_id(
            resource.bundle.TAG,
            resources_section,
            bundle_id
        )
        env.report_processor.process_list(
            resource.bundle.validate_update(
                id_provider,
                bundle_element,
                container_options,
                network_options,
                port_map_add,
                port_map_remove,
                storage_map_add,
                storage_map_remove,
                # TODO meta attributes - there is no validation for now
                force_options
            )
        )
        resource.bundle.update(
            id_provider,
            bundle_element,
            container_options,
            network_options,
            port_map_add,
            port_map_remove,
            storage_map_add,
            storage_map_remove,
            meta_attributes
        )
Example #26
0
def node_add_guest(
    env,
    node_name,
    resource_id,
    options,
    skip_offline_nodes=False,
    allow_incomplete_distribution=False,
    allow_pacemaker_remote_service_fail=False,
    wait=False,
):
    """
    Make a guest node from the specified resource

    LibraryEnvironment env -- provides all for communication with externals
    string resource_id -- specifies resource that should become a guest node
    dict options -- guest node options (remote-port, remote-addr,
        remote-connect-timeout)
    bool skip_offline_nodes -- if True, ignore when some nodes are offline
    bool 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
        finish successfully even if starting/enabling pacemaker_remote did not
        succeed
    mixed wait -- a flag for controlling waiting for pacemaker idle mechanism
    """
    # TODO
    # * make the node name mandatory and the node address optional
    #   * in this function interface and comment
    #   * in cli - do not fill the addr if not specified
    #   * in usage and man page
    # * get a target factory from lib.env
    # * use the factory to turn node names to targets
    #   * this will create reports for unknown node names (not authenticated)
    # * if the node addr is not specified, use the first addr from the matching
    #   target
    # * pass the target to communication functions instead of the node name
    #   * do not create targets again and again in each function

    _ensure_consistently_live_env(env)
    env.ensure_wait_satisfiable(wait)

    cib = env.get_cib()
    existing_nodes_names, existing_nodes_addrs = get_existing_nodes_names_addrs(
        env.get_corosync_conf(), cib)

    report_list = guest_node.validate_set_as_guest(cib, existing_nodes_names,
                                                   existing_nodes_addrs,
                                                   node_name, options)
    try:
        resource_element = find_element_by_tag_and_id(primitive.TAG,
                                                      get_resources(cib),
                                                      resource_id)
        report_list.extend(guest_node.validate_is_not_guest(resource_element))
    except LibraryError as e:
        report_list.extend(e.args)

    env.report_processor.process_list(report_list)

    guest_node.set_as_guest(
        resource_element,
        node_name,
        options.get("remote-addr", None),
        options.get("remote-port", None),
        options.get("remote-connect-timeout", None),
    )

    _prepare_pacemaker_remote_environment(
        env,
        existing_nodes_names,
        node_name,
        skip_offline_nodes,
        allow_incomplete_distribution,
        allow_pacemaker_remote_service_fail,
    )

    env.push_cib(wait=wait)
    if wait:
        _ensure_resource_running(env, resource_id)
Example #27
0
def validate_resource_instance_attributes_update(
    resource_agent: ResourceAgentFacade,
    instance_attributes: Mapping[str, str],
    resource_id: str,
    resources_section: _Element,
    force: bool = False,
) -> reports.ReportItemList:
    # TODO This function currently accepts the updated resource as a string and
    # finds the corresponding xml element by itself. This is needed as the
    # function is called from old pcs code which uses dom while pcs.lib uses
    # lxml. Once resource update command is moved to pcs.lib, this function
    # will be fixed to accept the updated resource as an element instead of a
    # string.
    report_items: reports.ReportItemList = []
    current_instance_attrs = get_nvset_as_dict(
        INSTANCE_ATTRIBUTES_TAG,
        find_element_by_tag_and_id(TAG, resources_section, resource_id),
    )

    if resource_agent.metadata.agent_exists:
        report_items += validate.ValidatorAll(
            resource_agent.get_validators_allowed_parameters(force)
        ).validate(
            # Do not report unknown parameters already set in the CIB. It would
            # be confusing to report an error in an option not actually created
            # now.
            {
                name: value
                for name, value in instance_attributes.items()
                if name not in current_instance_attrs
            }
        )
        report_items += validate.ValidatorAll(
            resource_agent.get_validators_deprecated_parameters()
        ).validate(
            {
                name: value
                for name, value in instance_attributes.items()
                # Allow removing deprecated parameters
                if value != ""
                # we create a custom report for stonith parameter "action"
                and not (
                    resource_agent.metadata.name.is_stonith and name == "action"
                )
            }
        )

        # Check that required parameters have not been removed. This is
        # complicated by two facts:
        # * parameters may by deprecated by other parameters, setting one
        #   required parameter from such group is enough
        # * we only want to report errors related to attributes to be updated
        final_attrs = dict(current_instance_attrs)
        for name, value in instance_attributes.items():
            if value == "":
                final_attrs.pop(name, None)
            else:
                final_attrs[name] = value
        report_items += validate.ValidatorAll(
            # Limit validation only to parameters entered now in an update
            # command. We don't want to report missing parameters not mentioned
            # in a command now, that would be confusing to users.
            resource_agent.get_validators_required_parameters(
                force, only_parameters=instance_attributes.keys()
            )
        ).validate(final_attrs)

    if resource_agent.metadata.name.is_stonith:
        report_items += _validate_stonith_action(instance_attributes, force)

    if resource_agent.metadata.agent_exists:
        report_items += _validate_unique_instance_attributes(
            resource_agent.metadata,
            instance_attributes,
            resources_section,
            resource_id=resource_id,
            force=force,
        )
    return report_items