Пример #1
0
def _get_agent_facade(
    report_processor: reports.ReportProcessor,
    factory: ResourceAgentFacadeFactory,
    name: str,
    allow_absent_agent: bool,
) -> ResourceAgentFacade:
    try:
        if ":" in name:
            raise InvalidResourceAgentName(name)
        full_name = ResourceAgentName("stonith", None, name)
        return factory.facade_from_parsed_name(full_name)
    except (UnableToGetAgentMetadata, UnsupportedOcfVersion) as e:
        if allow_absent_agent:
            report_processor.report(
                resource_agent_error_to_report_item(
                    e, reports.ReportItemSeverity.warning(), is_stonith=True))
            return factory.void_facade_from_parsed_name(full_name)
        report_processor.report(
            resource_agent_error_to_report_item(
                e,
                reports.ReportItemSeverity.error(reports.codes.FORCE),
                is_stonith=True,
            ))
        raise LibraryError() from e
    except ResourceAgentError as e:
        report_processor.report(
            resource_agent_error_to_report_item(
                e, reports.ReportItemSeverity.error(), is_stonith=True))
        raise LibraryError() from e
Пример #2
0
def describe_agent(
    lib_env: LibraryEnvironment, agent_name: str
) -> Dict[str, Any]:
    """
    Get agent's description (metadata) in a structure

    agent_name -- name of the agent
    """
    runner = lib_env.cmd_runner()
    report_processor = lib_env.report_processor
    agent_factory = ResourceAgentFacadeFactory(runner, report_processor)
    try:
        found_name = (
            split_resource_agent_name(agent_name)
            if ":" in agent_name
            else find_one_resource_agent_by_type(
                runner, report_processor, agent_name
            )
        )
        return _agent_metadata_to_dict(
            agent_factory.facade_from_parsed_name(found_name).metadata,
            describe=True,
        )
    except ResourceAgentError as e:
        lib_env.report_processor.report(resource_agent_error_to_report_item(e))
        raise LibraryError() from e
Пример #3
0
def _complete_agent_list(
    runner: CommandRunner,
    report_processor: ReportProcessor,
    agent_names: Iterable[ResourceAgentName],
    describe: bool,
    search: Optional[str],
) -> List[Dict[str, Any]]:
    agent_factory = ResourceAgentFacadeFactory(runner, report_processor)
    search_lower = search.lower() if search else None
    agent_list = []
    for name in agent_names:
        if search_lower and search_lower not in name.full_name.lower():
            continue
        try:
            metadata = (
                agent_factory.facade_from_parsed_name(name).metadata
                if describe
                else name_to_void_metadata(name)
            )
            agent_list.append(_agent_metadata_to_dict(metadata, describe))
        except ResourceAgentError as e:
            report_processor.report(
                resource_agent_error_to_report_item(
                    e, ReportItemSeverity.warning()
                )
            )
    return agent_list
Пример #4
0
def _get_agent_facade(
    report_processor: reports.ReportProcessor,
    factory: ResourceAgentFacadeFactory,
    allow_absent_agent: bool,
    name: ResourceAgentName,
) -> ResourceAgentFacade:
    try:
        return factory.facade_from_parsed_name(name)
    except UnableToGetAgentMetadata as e:
        if allow_absent_agent:
            report_processor.report(
                resource_agent_error_to_report_item(
                    e, reports.ReportItemSeverity.warning()
                )
            )
            return factory.void_facade_from_parsed_name(name)
        report_processor.report(
            resource_agent_error_to_report_item(
                e, reports.ReportItemSeverity.error(reports.codes.FORCE)
            )
        )
        raise LibraryError() from e
    except ResourceAgentError as e:
        report_processor.report(
            resource_agent_error_to_report_item(
                e, reports.ReportItemSeverity.error()
            )
        )
        raise LibraryError() from e
Пример #5
0
def _get_agent_metadata(
    runner: CommandRunner,
    report_processor: ReportProcessor,
    agent_name: ResourceAgentNameDto,
) -> ResourceAgentMetadata:
    agent_factory = ResourceAgentFacadeFactory(runner, report_processor)
    try:
        return agent_factory.facade_from_parsed_name(
            ResourceAgentName.from_dto(agent_name)).metadata
    except ResourceAgentError as e:
        report_processor.report(resource_agent_error_to_report_item(e))
        raise LibraryError() from e
Пример #6
0
def get_agent_metadata(
        lib_env: LibraryEnvironment,
        agent_name: ResourceAgentNameDto) -> ResourceAgentMetadataDto:
    """
    Return agent's metadata

    agent_name -- name of the agent
    """
    runner = lib_env.cmd_runner()
    report_processor = lib_env.report_processor
    agent_factory = ResourceAgentFacadeFactory(runner, report_processor)
    try:
        return agent_factory.facade_from_parsed_name(
            ResourceAgentName.from_dto(agent_name)).metadata.to_dto()
    except ResourceAgentError as e:
        lib_env.report_processor.report(resource_agent_error_to_report_item(e))
        raise LibraryError() from e
Пример #7
0
def describe_agent(lib_env: LibraryEnvironment,
                   agent_name: str) -> Dict[str, Any]:
    """
    Get agent's description (metadata) in a structure

    agent_name -- name of the agent (not containing "stonith:" prefix)
    """
    runner = lib_env.cmd_runner()
    agent_factory = ResourceAgentFacadeFactory(runner,
                                               lib_env.report_processor)
    try:
        if ":" in agent_name:
            raise InvalidResourceAgentName(agent_name)
        return _agent_metadata_to_dict(
            agent_factory.facade_from_parsed_name(
                ResourceAgentName("stonith", None, agent_name)).metadata,
            describe=True,
        )
    except ResourceAgentError as e:
        lib_env.report_processor.report(
            resource_agent_error_to_report_item(e, is_stonith=True))
        raise LibraryError() from e
Пример #8
0
def create(
    env: LibraryEnvironment,
    stonith_id: str,
    stonith_agent_name: str,
    operations: Iterable[Mapping[str, str]],
    meta_attributes: Mapping[str, str],
    instance_attributes: Mapping[str, str],
    allow_absent_agent: bool = False,
    allow_invalid_operation: bool = False,
    allow_invalid_instance_attributes: bool = False,
    use_default_operations: bool = True,
    ensure_disabled: bool = False,
    wait: WaitType = False,
):
    # pylint: disable=too-many-arguments, too-many-locals
    """
    Create stonith as resource in a cib.

    env -- provides all for communication with externals
    stonith_id -- an identifier of stonith resource
    stonith_agent_name -- contains name for the identification of agent
    operations -- contains attributes for each entered operation
    meta_attributes -- contains attributes for primitive/meta_attributes
    instance_attributes -- contains attributes for primitive/instance_attributes
    allow_absent_agent -- a flag for allowing agent not installed in a system
    allow_invalid_operation -- a flag for allowing to use operations that
        are not listed in a stonith agent metadata
    allow_invalid_instance_attributes -- a flag for allowing to use instance
        attributes that are not listed in a stonith agent metadata or for
        allowing to not use the instance_attributes that are required in
        stonith agent metadata
    use_default_operations -- a flag for stopping of adding default cib
        operations (specified in a stonith agent)
    ensure_disabled -- flag that keeps resource in target-role "Stopped"
    wait -- flag for controlling waiting for pacemaker idle mechanism
    """
    runner = env.cmd_runner()
    agent_factory = ResourceAgentFacadeFactory(runner, env.report_processor)
    stonith_agent = _get_agent_facade(
        env.report_processor,
        agent_factory,
        stonith_agent_name,
        allow_absent_agent,
    )
    if stonith_agent.metadata.provides_unfencing:
        meta_attributes = dict(meta_attributes, provides="unfencing")

    with resource_environment(
            env,
            wait,
        [stonith_id],
            _ensure_disabled_after_wait(
                ensure_disabled
                or resource.common.are_meta_disabled(meta_attributes), ),
    ) as resources_section:
        id_provider = IdProvider(resources_section)
        stonith_element = resource.primitive.create(
            env.report_processor,
            resources_section,
            id_provider,
            stonith_id,
            stonith_agent,
            raw_operation_list=operations,
            meta_attributes=meta_attributes,
            instance_attributes=instance_attributes,
            allow_invalid_operation=allow_invalid_operation,
            allow_invalid_instance_attributes=allow_invalid_instance_attributes,
            use_default_operations=use_default_operations,
            resource_type="stonith",
        )
        if ensure_disabled:
            resource.common.disable(stonith_element, id_provider)
Пример #9
0
def create_in_group(
    env: LibraryEnvironment,
    stonith_id: str,
    stonith_agent_name: str,
    group_id: str,
    operations: Iterable[Mapping[str, str]],
    meta_attributes: Mapping[str, str],
    instance_attributes: Mapping[str, str],
    allow_absent_agent: bool = False,
    allow_invalid_operation: bool = False,
    allow_invalid_instance_attributes: bool = False,
    use_default_operations: bool = True,
    ensure_disabled: bool = False,
    adjacent_resource_id: Optional[str] = None,
    put_after_adjacent: bool = False,
    wait: WaitType = False,
):
    # pylint: disable=too-many-arguments, too-many-locals
    """
    DEPRECATED
    Create stonith as resource in a cib and put it into defined group.

    env -- provides all for communication with externals
    stonith_id --an identifier of stonith resource
    stonith_agent_name -- contains name for the identification of agent
    group_id -- identificator for group to put stonith inside
    operations -- contains attributes for each entered operation
    meta_attributes -- contains attributes for primitive/meta_attributes
    instance_attributes -- contains attributes for primitive/instance_attributes
    allow_absent_agent -- a flag for allowing agent not installed in a system
    allow_invalid_operation -- a flag for allowing to use operations that
        are not listed in a stonith agent metadata
    allow_invalid_instance_attributes -- a flag for allowing to use instance
        attributes that are not listed in a stonith agent metadata or for
        allowing to not use the instance_attributes that are required in
        stonith agent metadata
    use_default_operations -- a flag for stopping of adding default cib
        operations (specified in a stonith agent)
    ensure_disabled -- flag that keeps resource in target-role "Stopped"
    adjacent_resource_id -- identify neighbor of a newly created stonith
    put_after_adjacent -- is flag to put a newly create resource befor/after
        adjacent stonith
    wait -- flag for controlling waiting for pacemaker idle mechanism
    """
    runner = env.cmd_runner()
    agent_factory = ResourceAgentFacadeFactory(runner, env.report_processor)
    stonith_agent = _get_agent_facade(
        env.report_processor,
        agent_factory,
        stonith_agent_name,
        allow_absent_agent,
    )
    if stonith_agent.metadata.provides_unfencing:
        meta_attributes = dict(meta_attributes, provides="unfencing")

    with resource_environment(
            env,
            wait,
        [stonith_id],
            _ensure_disabled_after_wait(
                ensure_disabled
                or resource.common.are_meta_disabled(meta_attributes), ),
    ) as resources_section:
        id_provider = IdProvider(resources_section)

        adjacent_resource_element = None
        if adjacent_resource_id:
            try:
                adjacent_resource_element = get_element_by_id(
                    get_root(resources_section), adjacent_resource_id)
            except ElementNotFound:
                # We cannot continue without adjacent element because
                # the validator might produce misleading reports
                if env.report_processor.report(
                        ReportItem.error(
                            reports.messages.IdNotFound(
                                adjacent_resource_id, []))).has_errors:
                    raise LibraryError() from None

        try:
            group_element = get_element_by_id(get_root(resources_section),
                                              group_id)
        except ElementNotFound:
            group_id_reports: List[ReportItem] = []
            validate_id(group_id,
                        description="group name",
                        reporter=group_id_reports)
            env.report_processor.report_list(group_id_reports)
            group_element = resource.group.append_new(resources_section,
                                                      group_id)

        stonith_element = resource.primitive.create(
            env.report_processor,
            resources_section,
            id_provider,
            stonith_id,
            stonith_agent,
            operations,
            meta_attributes,
            instance_attributes,
            allow_invalid_operation,
            allow_invalid_instance_attributes,
            use_default_operations,
        )
        if ensure_disabled:
            resource.common.disable(stonith_element, id_provider)

        if env.report_processor.report_list(
                resource.validations.validate_move_resources_to_group(
                    group_element,
                    [stonith_element],
                    adjacent_resource_element,
                )).has_errors:
            raise LibraryError()

        resource.hierarchy.move_resources_to_group(
            group_element,
            [stonith_element],
            adjacent_resource_element,
            put_after_adjacent,
        )
Пример #10
0
def node_add_remote(
    env: LibraryEnvironment,
    node_name: str,
    node_addr: Optional[str],
    operations: Iterable[Mapping[str, str]],
    meta_attributes: Mapping[str, str],
    instance_attributes: Mapping[str, str],
    skip_offline_nodes: bool = False,
    allow_incomplete_distribution: bool = False,
    allow_pacemaker_remote_service_fail: bool = False,
    allow_invalid_operation: bool = False,
    allow_invalid_instance_attributes: bool = False,
    use_default_operations: bool = True,
    wait: WaitType = False,
):
    # pylint: disable=too-many-arguments
    # pylint: disable=too-many-branches
    # pylint: disable=too-many-locals
    # pylint: disable=too-many-statements
    """
    create an ocf:pacemaker:remote resource and use it as a remote node

    env -- provides all for communication with externals
    node_name -- the name of the new node
    node_addr -- the address of the new node or None for default
    operations -- attributes for each entered operation
    meta_attributes -- attributes for primitive/meta_attributes
    instance_attributes -- attributes for primitive/instance_attributes
    skip_offline_nodes -- if True, ignore when some nodes are offline
    allow_incomplete_distribution -- if True, allow this command to
        finish successfully even if file distribution did not succeed
    allow_pacemaker_remote_service_fail -- if True, allow this command to
        finish successfully even if starting/enabling pacemaker_remote did not
        succeed
    allow_invalid_operation -- if True, allow to use operations that
        are not listed in a resource agent metadata
    allow_invalid_instance_attributes -- if True, allow to use instance
        attributes that are not listed in a resource agent metadata and allow to
        omit required instance_attributes
    use_default_operations -- if True, add operations specified in
        a resource agent metadata to the resource
    wait -- a flag for controlling waiting for pacemaker idle mechanism
    """
    wait_timeout = env.ensure_wait_satisfiable(wait)

    report_processor = env.report_processor
    cib = env.get_cib(
        minimal_version=get_required_cib_version_for_primitive(operations))
    id_provider = IdProvider(cib)
    if env.is_cib_live:
        corosync_conf: Optional[CorosyncConfigFacade] = env.get_corosync_conf()
    else:
        corosync_conf = None
        report_processor.report(
            ReportItem.info(
                reports.messages.CorosyncNodeConflictCheckSkipped(
                    reports.const.REASON_NOT_LIVE_CIB, )))
    (
        existing_nodes_names,
        existing_nodes_addrs,
        report_list,
    ) = get_existing_nodes_names_addrs(corosync_conf, cib)
    if env.is_cib_live:
        # We just reported corosync checks are going to be skipped so we
        # shouldn't complain about errors related to corosync nodes
        report_processor.report_list(report_list)

    try:
        resource_agent_facade = ResourceAgentFacadeFactory(
            env.cmd_runner(),
            report_processor).facade_from_parsed_name(remote_node.AGENT_NAME)
    except ResourceAgentError as e:
        report_processor.report(resource_agent_error_to_report_item(e))
        raise LibraryError() from e

    existing_target_list = []
    if env.is_cib_live:
        target_factory = env.get_node_target_factory()
        existing_target_list, new_target_list = _get_targets_for_add(
            target_factory,
            report_processor,
            existing_nodes_names,
            [node_name],
            skip_offline_nodes,
        )
        new_target = new_target_list[0] if new_target_list else None
        # default node_addr to an address from known-hosts
        if node_addr is None:
            if new_target:
                node_addr = new_target.first_addr
                node_addr_source = (
                    reports.const.DEFAULT_ADDRESS_SOURCE_KNOWN_HOSTS)
            else:
                node_addr = node_name
                node_addr_source = (
                    reports.const.DEFAULT_ADDRESS_SOURCE_HOST_NAME)
            report_processor.report(
                ReportItem.info(
                    reports.messages.UsingDefaultAddressForHost(
                        node_name, node_addr, node_addr_source)))
    else:
        # default node_addr to an address from known-hosts
        if node_addr is None:
            known_hosts = env.get_known_hosts([node_name])
            if known_hosts:
                node_addr = known_hosts[0].dest.addr
                node_addr_source = (
                    reports.const.DEFAULT_ADDRESS_SOURCE_KNOWN_HOSTS)
            else:
                node_addr = node_name
                node_addr_source = (
                    reports.const.DEFAULT_ADDRESS_SOURCE_HOST_NAME)
            report_processor.report(
                ReportItem.info(
                    reports.messages.UsingDefaultAddressForHost(
                        node_name, node_addr, node_addr_source)))

    # validate inputs
    report_list = remote_node.validate_create(
        existing_nodes_names,
        existing_nodes_addrs,
        resource_agent_facade.metadata,
        node_name,
        node_addr,
        instance_attributes,
    )
    if report_processor.report_list(report_list).has_errors:
        raise LibraryError()
    # validation + cib setup
    # TODO extract the validation to a separate function
    try:
        remote_resource_element = remote_node.create(
            env.report_processor,
            resource_agent_facade,
            get_resources(cib),
            id_provider,
            node_addr,
            node_name,
            operations,
            meta_attributes,
            instance_attributes,
            allow_invalid_operation,
            allow_invalid_instance_attributes,
            use_default_operations,
        )
    except LibraryError as e:
        # Check unique id conflict with check against nodes. Until validation
        # resource create is not separated, we need to make unique post
        # validation.
        already_exists = []
        unified_report_list = []
        for report_item in report_list + list(e.args):
            # pylint: disable=no-member
            dto_obj = report_item.message.to_dto()
            if dto_obj.code not in (
                    reports.codes.ID_ALREADY_EXISTS,
                    reports.codes.RESOURCE_INSTANCE_ATTR_VALUE_NOT_UNIQUE,
            ):
                unified_report_list.append(report_item)
            elif ("id" in dto_obj.payload
                  and dto_obj.payload["id"] not in already_exists):
                unified_report_list.append(report_item)
                already_exists.append(dto_obj.payload["id"])
        report_list = unified_report_list

    report_processor.report_list(report_list)
    if report_processor.has_errors:
        raise LibraryError()

    # everything validated, let's set it up
    if env.is_cib_live:
        _prepare_pacemaker_remote_environment(
            env,
            report_processor,
            existing_target_list,
            new_target,
            node_name,
            skip_offline_nodes,
            allow_incomplete_distribution,
            allow_pacemaker_remote_service_fail,
        )
    else:
        report_processor.report_list(
            _reports_skip_new_node(node_name, "not_live_cib"))

    env.push_cib(wait_timeout=wait_timeout)
    if wait_timeout >= 0:
        _ensure_resource_running(env, remote_resource_element.attrib["id"])
Пример #11
0
def create_in_cluster(
    env: LibraryEnvironment,
    ip: str,
    instance_name: Optional[str] = None,
    allow_absent_resource_agent: bool = False,
):
    """
    Create group with ip resource and booth resource

    env -- provides all for communication with externals
    ip -- float ip address for the operation of the booth
    instance_name -- booth instance name
    allow_absent_resource_agent -- allowing creating booth resource even
        if its agent is not installed
    """
    report_processor = env.report_processor
    booth_env = env.get_booth_env(instance_name)
    # Booth config path goes to CIB. Working with a mocked booth configs would
    # not work coorectly as the path would point to a mock file (the path to a
    # mock file is unknown to us in the lib anyway)
    # It makes sense to work with a mocked CIB, though. Users can do other
    # changes to the CIB and push them to the cluster at once.
    _ensure_live_booth_env(booth_env)
    resources_section = get_resources(env.get_cib())
    id_provider = IdProvider(resources_section)
    instance_name = booth_env.instance_name

    # validate
    if resource.find_for_config(resources_section, booth_env.config_path):
        report_processor.report(
            ReportItem.error(reports.messages.BoothAlreadyInCib(instance_name))
        )
    # verify the config exists and is readable
    try:
        booth_env.config.raw_file.read()
    except RawFileError as e:
        report_processor.report(raw_file_error_report(e))
    if report_processor.has_errors:
        raise LibraryError()
    # validation done

    create_id = partial(
        resource.create_resource_id, resources_section, instance_name
    )
    create_primitive = partial(
        primitive.create, env.report_processor, resources_section, id_provider
    )
    agent_factory = ResourceAgentFacadeFactory(
        env.cmd_runner(), report_processor
    )

    # Group id validation is not needed since create_id creates a new unique
    # booth group identifier
    hierarchy.move_resources_to_group(
        group.append_new(resources_section, create_id("group")),
        [
            create_primitive(
                create_id("ip"),
                _get_agent_facade(
                    env.report_processor,
                    agent_factory,
                    allow_absent_resource_agent,
                    ResourceAgentName("ocf", "heartbeat", "IPaddr2"),
                ),
                instance_attributes={"ip": ip},
            ),
            create_primitive(
                create_id("service"),
                _get_agent_facade(
                    env.report_processor,
                    agent_factory,
                    allow_absent_resource_agent,
                    ResourceAgentName("ocf", "pacemaker", "booth-site"),
                ),
                instance_attributes={"config": booth_env.config_path},
            ),
        ],
    )

    env.push_cib()