Exemplo n.º 1
0
def get_rule_in_effect_status(
    runner: CommandRunner, cib_xml: str, rule_id: str
) -> CibRuleInEffectStatus:
    """
    Figure out if a rule is in effect, expired or not yet in effect

    runner -- a class for running external processes
    cib_xml -- CIB containing rules
    rule_id -- ID of the rule to be checked
    """
    # TODO Once crm_rule is capable of evaluating more than one rule per go, we
    # should make use of it. Running the tool for each rule may really slow pcs
    # down.
    translation_map = {
        0: CibRuleInEffectStatus.IN_EFFECT,
        110: CibRuleInEffectStatus.EXPIRED,
        111: CibRuleInEffectStatus.NOT_YET_IN_EFFECT,
        # 105:non-existent
        # 112: undetermined (rule is too complicated for current implementation)
    }
    dummy_stdout, dummy_stderr, retval = runner.run(
        [__exec("crm_rule"), "--check", "--rule", rule_id, "--xml-text", "-"],
        stdin_string=cib_xml,
    )
    return translation_map.get(retval, CibRuleInEffectStatus.UNKNOWN)
Exemplo n.º 2
0
def wrap_element_by_master(cib_file, resource_id, master_id=None):
    cib_tree = etree.parse(cib_file, etree.XMLParser(huge_tree=True)).getroot()
    element = cib_tree.find(f'.//*[@id="{resource_id}"]')
    final_master_id = (master_id
                       if master_id is not None else f"{resource_id}-master")
    master_element = _xml_to_element(f"""
        <master id="{final_master_id}">
        </master>
    """)
    element.getparent().append(master_element)
    master_element.append(element)
    final_xml = etree_to_str(cib_tree)

    environ = dict(os.environ)
    environ["CIB_file"] = cib_file
    runner = CommandRunner(mock.MagicMock(logging.Logger),
                           MockLibraryReportProcessor(), environ)
    stdout, stderr, retval = runner.run([
        os.path.join(settings.pacemaker_binaries, "cibadmin"),
        "--replace",
        "--scope",
        "resources",
        "--xml-pipe",
    ],
                                        stdin_string=final_xml)
    assert retval == 0, ("Error running wrap_element_by_master:\n" + stderr +
                         "\n" + stdout)
Exemplo n.º 3
0
def resource_cleanup(
    runner: CommandRunner,
    resource: Optional[str] = None,
    node: Optional[str] = None,
    operation: Optional[str] = None,
    interval: Optional[str] = None,
    strict: bool = False,
):
    cmd = [__exec("crm_resource"), "--cleanup"]
    if resource:
        cmd.extend(["--resource", resource])
    if node:
        cmd.extend(["--node", node])
    if operation:
        cmd.extend(["--operation", operation])
    if interval:
        cmd.extend(["--interval", interval])
    if strict:
        cmd.extend(["--force"])

    stdout, stderr, retval = runner.run(cmd)

    if retval != 0:
        raise LibraryError(
            ReportItem.error(
                reports.messages.ResourceCleanupError(
                    join_multilines([stderr, stdout]), resource, node
                )
            )
        )
    # usefull output (what has been done) goes to stderr
    return join_multilines([stdout, stderr])
Exemplo n.º 4
0
def get_cluster_status_text(
    runner: CommandRunner, hide_inactive_resources: bool, verbose: bool,
) -> Tuple[str, List[str]]:
    cmd = [__exec("crm_mon"), "--one-shot"]
    if not hide_inactive_resources:
        cmd.append("--inactive")
    if verbose:
        cmd.extend(["--show-detail", "--show-node-attributes", "--failcounts"])
        # by default, pending and failed actions are displayed
        # with verbose==True, we display the whole history
        if is_fence_history_supported_status(runner):
            cmd.append("--fence-history=3")
    stdout, stderr, retval = runner.run(cmd)

    if retval != 0:
        raise CrmMonErrorException(
            ReportItem.error(
                reports.messages.CrmMonError(join_multilines([stderr, stdout]))
            )
        )
    warnings: List[str] = []
    if stderr.strip():
        warnings = [
            line
            for line in stderr.strip().splitlines()
            if verbose or not line.startswith("DEBUG: ")
        ]

    return stdout.strip(), warnings
Exemplo n.º 5
0
def resource_refresh(
    runner: CommandRunner,
    resource: Optional[str] = None,
    node: Optional[str] = None,
    strict: bool = False,
    force: bool = False,
):
    if not force and not node and not resource:
        summary = ClusterState(get_cluster_status_dom(runner)).summary
        operations = summary.nodes.attrs.count * summary.resources.attrs.count
        if operations > __RESOURCE_REFRESH_OPERATION_COUNT_THRESHOLD:
            raise LibraryError(
                ReportItem(
                    reports.item.ReportItemSeverity.error(reports.codes.FORCE),
                    reports.messages.ResourceRefreshTooTimeConsuming(
                        __RESOURCE_REFRESH_OPERATION_COUNT_THRESHOLD),
                ))

    cmd = [__exec("crm_resource"), "--refresh"]
    if resource:
        cmd.extend(["--resource", resource])
    if node:
        cmd.extend(["--node", node])
    if strict:
        cmd.extend(["--force"])

    stdout, stderr, retval = runner.run(cmd)

    if retval != 0:
        raise LibraryError(
            ReportItem.error(
                reports.messages.ResourceRefreshError(
                    join_multilines([stderr, stdout]), resource, node)))
    # usefull output (what has been done) goes to stderr
    return join_multilines([stdout, stderr])
Exemplo n.º 6
0
def wait_for_idle(runner: CommandRunner, timeout: int) -> None:
    """
    Run waiting command. Raise LibraryError if command failed.

    runner -- preconfigured object for running external programs
    timeout -- waiting timeout in seconds, wait indefinitely if non-positive
        integer
    """
    args = [__exec("crm_resource"), "--wait"]
    if timeout > 0:
        args.append("--timeout={0}".format(timeout))
    stdout, stderr, retval = runner.run(args)
    if retval != 0:
        # Usefull info goes to stderr - not only error messages, a list of
        # pending actions in case of timeout goes there as well.
        # We use stdout just to be sure if that's get changed.
        if retval == __EXITCODE_WAIT_TIMEOUT:
            raise LibraryError(
                ReportItem.error(
                    reports.messages.WaitForIdleTimedOut(
                        join_multilines([stderr, stdout]))))
        raise LibraryError(
            ReportItem.error(
                reports.messages.WaitForIdleError(
                    join_multilines([stderr, stdout]))))
Exemplo n.º 7
0
def _is_in_pcmk_tool_help(runner: CommandRunner, tool: str,
                          text_list: Iterable[str]) -> bool:
    stdout, stderr, dummy_retval = runner.run([__exec(tool), "--help-all"])
    # Help goes to stderr but we check stdout as well if that gets changed. Use
    # generators in all to return early.
    return (all(text in stderr for text in text_list)
            or all(text in stdout for text in text_list))
Exemplo n.º 8
0
Arquivo: xml.py Projeto: vvidic/pcs
def _load_metadata_xml(
    runner: CommandRunner, agent_name: ResourceAgentName
) -> str:
    """
    Run pacemaker tool to get raw metadata from an agent

    runner -- external processes runner
    agent_name -- name of an agent whose metadata we want to get
    """
    env_path = ":".join(
        [
            # otherwise pacemaker cannot run RHEL fence agents to get their
            # metadata
            settings.fence_agent_binaries,
            # otherwise heartbeat and cluster-glue agents don't work
            "/bin",
            # otherwise heartbeat and cluster-glue agents don't work
            "/usr/bin",
        ]
    )
    stdout, stderr, retval = runner.run(
        [settings.crm_resource_binary, "--show-metadata", agent_name.full_name],
        env_extend={"PATH": env_path},
    )
    if retval != 0:
        raise UnableToGetAgentMetadata(agent_name.full_name, stderr.strip())
    return stdout.strip()
Exemplo n.º 9
0
def list_resource_agents_standards(runner: CommandRunner) -> List[str]:
    """
    Return a list of resource agents standards (ocf, lsb, ...) on the local host
    """
    # retval is the number of standards found
    stdout, dummy_stderr, dummy_retval = runner.run(
        [settings.crm_resource_binary, "--list-standards"])
    return sorted(set(split_multiline(stdout)), key=str.lower)
Exemplo n.º 10
0
def list_resource_agents_ocf_providers(runner: CommandRunner) -> List[str]:
    """
    Return a list of resource agents ocf providers on the local host
    """
    # retval is the number of providers found
    stdout, dummy_stderr, dummy_retval = runner.run(
        [settings.crm_resource_binary, "--list-ocf-providers"])
    return sorted(set(split_multiline(stdout)), key=str.lower)
Exemplo n.º 11
0
def list_resource_agents_ocf_providers(runner: CommandRunner) -> List[str]:
    """
    Return list of resource agents ocf providers on the local host
    """
    # retval is number of providers found
    stdout, dummy_stderr, dummy_retval = runner.run(
        [settings.crm_resource_binary, "--list-ocf-providers"]
    )
    return _prepare_agent_list(stdout)
Exemplo n.º 12
0
def get_cluster_status_xml_raw(runner: CommandRunner) -> Tuple[str, str, int]:
    """
    Run pacemaker tool to get XML status. This function doesn't do any
    processing. Usually, using get_cluster_status_dom is preferred instead.

    runner -- a class for running external processes
    """
    return runner.run(
        [__exec("crm_mon"), "--one-shot", "--inactive", "--output-as", "xml"])
Exemplo n.º 13
0
def fixture_to_cib(cib_file, xml):
    environ = dict(os.environ)
    environ["CIB_file"] = cib_file
    runner = CommandRunner(mock.MagicMock(logging.Logger),
                           MockLibraryReportProcessor(), environ)
    stdout, stderr, retval = runner.run(
        ["cibadmin", "--create", "--scope", "resources", "--xml-text", xml])
    assert retval == 0, ("Error running fixture_to_cib:\n" + stderr + "\n" +
                         stdout)
Exemplo n.º 14
0
def diff_cibs_xml(
    runner: CommandRunner, reporter: ReportProcessor, cib_old_xml, cib_new_xml,
):
    """
    Return xml diff of two CIBs

    runner
    reporter
    string cib_old_xml -- original CIB
    string cib_new_xml -- modified CIB
    """
    try:
        cib_old_tmp_file = write_tmpfile(cib_old_xml)
        reporter.report(
            ReportItem.debug(
                reports.messages.TmpFileWrite(
                    cib_old_tmp_file.name, cib_old_xml
                )
            )
        )
        cib_new_tmp_file = write_tmpfile(cib_new_xml)
        reporter.report(
            ReportItem.debug(
                reports.messages.TmpFileWrite(
                    cib_new_tmp_file.name, cib_new_xml
                )
            )
        )
    except EnvironmentError as e:
        raise LibraryError(
            ReportItem.error(reports.messages.CibSaveTmpError(str(e)))
        ) from e
    command = [
        __exec("crm_diff"),
        "--original",
        cib_old_tmp_file.name,
        "--new",
        cib_new_tmp_file.name,
        "--no-version",
    ]
    #  0 (CRM_EX_OK) - success with no difference
    #  1 (CRM_EX_ERROR) - success with difference
    # 64 (CRM_EX_USAGE) - usage error
    # 65 (CRM_EX_DATAERR) - XML fragments not parseable
    stdout, stderr, retval = runner.run(command)
    if retval == 0:
        return ""
    if retval > 1:
        raise LibraryError(
            ReportItem.error(
                reports.messages.CibDiffError(
                    stderr.strip(), cib_old_xml, cib_new_xml
                )
            )
        )
    return stdout.strip()
Exemplo n.º 15
0
def list_resource_agents_standards(runner: CommandRunner) -> List[str]:
    """
    Return list of resource agents standards (ocf, lsb, ... ) on the local host
    """
    # retval is number of standards found
    stdout, dummy_stderr, dummy_retval = runner.run(
        [settings.crm_resource_binary, "--list-standards"]
    )
    # we are only interested in RESOURCE agents
    ignored_standards = frozenset(["stonith"])
    return _prepare_agent_list(stdout, ignored_standards)
Exemplo n.º 16
0
def list_resource_agents(
    runner: CommandRunner, standard_provider: str
) -> List[str]:
    """
    Return list of resource agents for specified standard on the local host

    runner
    standard_provider -- standard[:provider], e.g. lsb, ocf, ocf:pacemaker
    """
    # retval is 0 on success, anything else when no agents found
    stdout, dummy_stderr, retval = runner.run(
        [settings.crm_resource_binary, "--list-agents", standard_provider]
    )
    if retval != 0:
        return []
    return _prepare_agent_list(stdout)
Exemplo n.º 17
0
def list_resource_agents(
        runner: CommandRunner,
        standard_provider: StandardProviderTuple) -> List[str]:
    """
    Return a list of resource agents of the specified standard on the local host

    standard_provider -- standard[:provider], e.g. lsb, ocf, ocf:pacemaker
    """
    # retval is 0 on success, anything else when no agents were found
    stdout, dummy_stderr, retval = runner.run([
        settings.crm_resource_binary,
        "--list-agents",
        (f"{standard_provider.standard}:{standard_provider.provider}"
         if standard_provider.provider else standard_provider.standard),
    ])
    return (sorted(set(split_multiline(stdout)) - _IGNORED_AGENTS,
                   key=str.lower) if retval == 0 else [])
Exemplo n.º 18
0
def _load_fake_agent_metadata_xml(
    runner: CommandRunner, agent_name: FakeAgentName
) -> str:
    """
    Run pacemaker tool to get raw metadata from pacemaker

    runner -- external processes runner
    agent_name -- name of pacemaker part whose metadata we want to get
    """
    name_to_executable = {
        const.PACEMAKER_FENCED: settings.pacemaker_fenced,
    }
    if agent_name not in name_to_executable:
        raise UnableToGetAgentMetadata(agent_name, "Unknown agent")
    stdout, stderr, dummy_retval = runner.run(
        [name_to_executable[agent_name], "metadata"]
    )
    metadata = stdout.strip()
    if not metadata:
        raise UnableToGetAgentMetadata(agent_name, stderr.strip())
    return metadata
Exemplo n.º 19
0
def diff_cibs_xml(
    runner: CommandRunner,
    reporter: ReportProcessor,
    cib_old_xml: str,
    cib_new_xml: str,
) -> str:
    """
    Return xml diff of two CIBs

    runner
    reporter
    cib_old_xml -- original CIB
    cib_new_xml -- modified CIB
    """
    with tools.get_tmp_cib(reporter,
                           cib_old_xml) as cib_old_tmp_file, tools.get_tmp_cib(
                               reporter, cib_new_xml) as cib_new_tmp_file:
        stdout, stderr, retval = runner.run([
            __exec("crm_diff"),
            "--original",
            cib_old_tmp_file.name,
            "--new",
            cib_new_tmp_file.name,
            "--no-version",
        ])
    #  0 (CRM_EX_OK) - success with no difference
    #  1 (CRM_EX_ERROR) - success with difference
    # 64 (CRM_EX_USAGE) - usage error
    # 65 (CRM_EX_DATAERR) - XML fragments not parseable
    if retval == 0:
        return ""
    if retval > 1:
        raise LibraryError(
            ReportItem.error(
                reports.messages.CibDiffError(stderr.strip(), cib_old_xml,
                                              cib_new_xml)))
    return stdout.strip()
Exemplo n.º 20
0
def get_ticket_status_text(runner: CommandRunner) -> Tuple[str, str, int]:
    stdout, stderr, retval = runner.run([__exec("crm_ticket"), "--details"])
    return stdout.strip(), stderr.strip(), retval
Exemplo n.º 21
0
def get_resource_digests(
    runner: CommandRunner,
    resource_id: str,
    node_name: str,
    resource_options: Dict[str, str],
    crm_meta_attributes: Optional[Dict[str, Optional[str]]] = None,
) -> Dict[str, Optional[str]]:
    """
    Get set of digests for a resource using crm_resource utility. There are 3
    types of digests: all, nonreloadable and nonprivate. Resource can have one
    or more digests types depending on the resource parameters.

    runner -- command runner instance
    resource_id -- resource id
    node_name -- name of the node where resource is running
    resource_options -- resource options with updated values
    crm_meta_attributes -- parameters of a monitor operation
    """
    # pylint: disable=too-many-locals
    if crm_meta_attributes is None:
        crm_meta_attributes = {}
    command = [
        __exec("crm_resource"),
        "--digests",
        "--resource",
        resource_id,
        "--node",
        node_name,
        "--output-as",
        "xml",
        *[f"{key}={value}" for key, value in resource_options.items()],
        *[
            f"CRM_meta_{key}={value}"
            for key, value in crm_meta_attributes.items() if value is not None
        ],
    ]
    stdout, stderr, retval = runner.run(command)

    def error_exception(message):
        return LibraryError(
            ReportItem.error(
                reports.messages.UnableToGetResourceOperationDigests(message)))

    try:
        dom = _get_api_result_dom(stdout)
    except (etree.XMLSyntaxError, etree.DocumentInvalid) as e:
        raise error_exception(join_multilines([stderr, stdout])) from e

    if retval != 0:
        status = _get_status_from_api_result(dom)
        raise error_exception(
            join_multilines([status.message] + list(status.errors)))

    digests = {}
    for digest_type in ["all", "nonprivate", "nonreloadable"]:
        xpath_result = cast(
            List[str],
            dom.xpath(
                "./digests/digest[@type=$digest_type]/@hash",
                digest_type=digest_type,
            ),
        )
        digests[digest_type] = xpath_result[0] if xpath_result else None
    if not any(digests.values()):
        raise error_exception(join_multilines([stderr, stdout]))
    return digests