Exemplo n.º 1
0
Arquivo: env.py Projeto: nrwahl2/pcs
    def push_cib(self, custom_cib=None, wait=False):
        """
        Push previously loaded instance of CIB or a custom CIB

        etree custom_cib -- push a custom CIB instead of a loaded instance
            (allows to push an externally provided CIB and replace the one in
            the cluster completely)
        mixed wait -- how many seconds to wait for pacemaker to process new CIB
            or False for not waiting at all
        """
        if custom_cib is not None:
            if self.__loaded_cib_diff_source is not None:
                raise AssertionError(
                    "CIB has been loaded, cannot push custom CIB")
            return self.__push_cib_full(custom_cib, wait)
        if self.__loaded_cib_diff_source is None:
            raise AssertionError("CIB has not been loaded")
        # Push by diff works with crm_feature_set > 3.0.8, see
        # https://bugzilla.redhat.com/show_bug.cgi?id=1488044 for details. We
        # only check the version if a CIB has been loaded, otherwise the push
        # fails anyway. By my testing it seems that only the source CIB's
        # version matters.
        if (self.__loaded_cib_diff_source_feature_set <
                MIN_FEATURE_SET_VERSION_FOR_DIFF):
            current_set = str(
                self.__loaded_cib_diff_source_feature_set.normalize())
            self.report_processor.report(
                ReportItem.warning(
                    reports.messages.CibPushForcedFullDueToCrmFeatureSet(
                        str(MIN_FEATURE_SET_VERSION_FOR_DIFF.normalize()),
                        current_set,
                    )))
            return self.__push_cib_full(self.__loaded_cib_to_modify, wait=wait)
        return self.__push_cib_diff(wait=wait)
Exemplo n.º 2
0
 def _log_response_failure(self, response):
     msg = "Unable to connect to {node} ({reason})"
     self._logger.debug(
         msg.format(
             node=response.request.host_label, reason=response.error_msg
         )
     )
     self._reporter.report(
         ReportItem.debug(
             reports.messages.NodeCommunicationNotConnected(
                 response.request.host_label,
                 response.error_msg,
             )
         )
     )
     if is_proxy_set(os.environ):
         self._logger.warning("Proxy is set")
         self._reporter.report(
             ReportItem.warning(
                 reports.messages.NodeCommunicationProxyIsSet(
                     response.request.host_label,
                     response.request.dest.addr,
                 )
             )
         )
Exemplo n.º 3
0
def _make_unique_intervals(
    report_processor: ReportProcessor,
    operation_list: Iterable[ResourceOperationFilteredIn],
) -> List[ResourceOperationFilteredOut]:
    """
    Return operation list similar to operation_list where intervals for the same
        operation are unique

    report_processor -- tool for warning/info/error reporting
    operation_list -- contains operation definitions
    """
    get_unique_interval = get_interval_uniquer()
    adapted_operation_list = []
    for operation in operation_list:
        adapted = dict(operation)
        if "interval" in adapted:
            adapted["interval"] = get_unique_interval(
                operation["name"], operation["interval"]
            )
            if adapted["interval"] != operation["interval"]:
                report_processor.report(
                    ReportItem.warning(
                        reports.messages.ResourceOperationIntervalAdapted(
                            operation["name"],
                            operation["interval"],
                            adapted["interval"],
                        )
                    )
                )
        adapted_operation_list.append(adapted)
    return adapted_operation_list
Exemplo n.º 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,
        ))
Exemplo n.º 5
0
def _validate_generic_container_options(container_options,
                                        force_options=False):
    validators = [
        validate.NamesIn(
            GENERIC_CONTAINER_OPTIONS,
            option_type="container",
            **validate.set_warning(report_codes.FORCE_OPTIONS, force_options),
        ),
        validate.IsRequiredAll(["image"], option_type="container"),
        validate.ValueNotEmpty("image", "image name"),
        validate.ValueNonnegativeInteger("masters"),
        validate.ValueNonnegativeInteger("promoted-max"),
        validate.MutuallyExclusive(
            ["masters", "promoted-max"],
            option_type="container",
        ),
        validate.ValuePositiveInteger("replicas"),
        validate.ValuePositiveInteger("replicas-per-host"),
    ]

    deprecation_reports = []
    if "masters" in container_options:
        deprecation_reports.append(
            ReportItem.warning(
                reports.messages.DeprecatedOption(
                    "masters",
                    ["promoted-max"],
                    "container",
                )))

    return (validate.ValidatorAll(validators).validate(container_options) +
            deprecation_reports)
Exemplo n.º 6
0
def make_unique_intervals(report_processor: ReportProcessor, operation_list):
    """
    Return operation list similar to operation_list where intervals for the same
        operation are unique
    report_processor is tool for warning/info/error reporting
    list operation_list contains dictionaries with attributes of operation
    """
    get_unique_interval = get_interval_uniquer()
    adapted_operation_list = []
    for operation in operation_list:
        adapted = operation.copy()
        if "interval" in adapted:
            adapted["interval"] = get_unique_interval(
                operation["name"], operation["interval"]
            )
            if adapted["interval"] != operation["interval"]:
                report_processor.report(
                    ReportItem.warning(
                        reports.messages.ResourceOperationIntervalAdapted(
                            operation["name"],
                            operation["interval"],
                            adapted["interval"],
                        )
                    )
                )
        adapted_operation_list.append(adapted)
    return adapted_operation_list
Exemplo n.º 7
0
def get_authfile_name_and_data(booth_conf_facade):
    """
    Get booth auth filename, content and reports based on booth config facade

    pcs.lib.booth.config_facade.ConfigFacade booth_conf_facade -- booth config
    """
    authfile_name = None
    authfile_data = None
    report_list = []

    authfile_path = booth_conf_facade.get_authfile()
    if authfile_path:
        authfile_dir, authfile_name = os.path.split(authfile_path)
        if (authfile_dir == settings.booth_config_dir) and authfile_name:
            authfile_data = FileInstance.for_booth_key(
                authfile_name).read_raw()
        else:
            authfile_name = None
            report_list.append(
                ReportItem.warning(
                    reports.messages.BoothUnsupportedFileLocation(
                        authfile_path,
                        settings.booth_config_dir,
                        file_type_codes.BOOTH_KEY,
                    )))

    return authfile_name, authfile_data, report_list
Exemplo n.º 8
0
 def log_retry(self, response, previous_dest):
     old_port = _get_port(previous_dest.port)
     new_port = _get_port(response.request.dest.port)
     msg = (
         "Unable to connect to '{label}' via address '{old_addr}' and port "
         "'{old_port}'. Retrying request '{req}' via address '{new_addr}' "
         "and port '{new_port}'").format(
             label=response.request.host_label,
             old_addr=previous_dest.addr,
             old_port=old_port,
             new_addr=response.request.dest.addr,
             new_port=new_port,
             req=response.request.url,
         )
     self._logger.warning(msg)
     self._reporter.report(
         ReportItem.warning(
             reports.messages.NodeCommunicationRetrying(
                 response.request.host_label,
                 previous_dest.addr,
                 old_port,
                 response.request.dest.addr,
                 new_port,
                 response.request.url,
             )))
Exemplo n.º 9
0
 def _process_response(self, response):
     report_item = response_to_report_item(
         response, severity=reports.ReportItemSeverity.WARNING)
     node = response.request.target.label
     if report_item is not None:
         self._has_failure = True
         self._report(report_item)
         return self._get_next_list()
     if response.data.strip() == "Cannot initialize CMAP service":
         # corosync is not running on the node, this is OK
         return self._get_next_list()
     try:
         quorum_status = corosync_live.QuorumStatus.from_string(
             response.data)
         if not quorum_status.is_quorate:
             return self._get_next_list()
         self._quorum_status = quorum_status
     except corosync_live.QuorumStatusParsingException as e:
         self._has_failure = True
         self._report(
             ReportItem.warning(
                 reports.messages.CorosyncQuorumGetStatusError(
                     e.reason,
                     node=node,
                 )))
         return self._get_next_list()
     return []
Exemplo n.º 10
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,
        ))
Exemplo n.º 11
0
def _defaults_create(
    env: LibraryEnvironment,
    cib_section_name: str,
    validator_options: Mapping[str, Any],
    nvpairs: Mapping[str, str],
    nvset_options: Mapping[str, str],
    nvset_rule: Optional[str] = None,
    force_flags: Optional[Container] = None,
) -> None:
    if force_flags is None:
        force_flags = set()
    force = (reports.codes.FORCE
             in force_flags) or (reports.codes.FORCE_OPTIONS in force_flags)

    required_cib_version = None
    nice_to_have_cib_version = None
    if nvset_rule:
        # Parse the rule to see if we need to upgrade CIB schema. All errors
        # would be properly reported by a validator called bellow, so we can
        # safely ignore them here.
        try:
            rule_tree = parse_rule(nvset_rule)
            if has_rsc_or_op_expression(rule_tree):
                required_cib_version = Version(3, 4, 0)
            if has_node_attr_expr_with_type_integer(rule_tree):
                nice_to_have_cib_version = Version(3, 5, 0)
        except RuleParseError:
            pass

    cib = env.get_cib(
        minimal_version=required_cib_version,
        nice_to_have_version=nice_to_have_cib_version,
    )
    id_provider = IdProvider(cib)

    validator = nvpair_multi.ValidateNvsetAppendNew(
        id_provider,
        nvpairs,
        nvset_options,
        nvset_rule=nvset_rule,
        **validator_options,
    )
    if env.report_processor.report_list(
            validator.validate(force_options=force)).has_errors:
        raise LibraryError()

    nvpair_multi.nvset_append_new(
        sections.get(cib, cib_section_name),
        id_provider,
        get_pacemaker_version_by_which_cib_was_validated(cib),
        nvpair_multi.NVSET_META,
        nvpairs,
        nvset_options,
        nvset_rule=validator.get_parsed_rule(),
    )

    env.report_processor.report(
        ReportItem.warning(reports.messages.DefaultsCanBeOverriden()))
    env.push_cib()
Exemplo n.º 12
0
 def log_no_more_addresses(self, response):
     msg = "No more addresses for node {label} to run '{req}'".format(
         label=response.request.host_label,
         req=response.request.url,
     )
     self._logger.warning(msg)
     self._reporter.report(
         ReportItem.warning(
             reports.messages.NodeCommunicationNoMoreAddresses(
                 response.request.host_label,
                 response.request.url,
             )))
Exemplo n.º 13
0
 def _process_response(self, response):
     report = response_to_report_item(response)
     if report is None:
         self._online_target_list.append(response.request.target)
         return
     if not response.was_connected:
         report = (ReportItem.warning(
             reports.messages.OmittingNode(response.request.target.label))
                   if self._ignore_offline_targets else
                   response_to_report_item(
                       response, forceable=report_codes.SKIP_OFFLINE_NODES))
     self._report(report)
Exemplo n.º 14
0
def _get_rule_evaluator(
    cib: _Element,
    runner: CommandRunner,
    report_processor: reports.ReportProcessor,
    evaluate_expired: bool,
) -> RuleInEffectEval:
    if evaluate_expired:
        if has_rule_in_effect_status_tool():
            return RuleInEffectEvalOneByOne(cib, runner)
        report_processor.report(
            ReportItem.warning(
                reports.messages.RuleInEffectStatusDetectionNotSupported()))
    return RuleInEffectEvalDummy()
Exemplo n.º 15
0
 def _process_response(self, response):
     report_item = response_to_report_item(
         response, severity=ReportItemSeverity.WARNING)
     node_label = response.request.target.label
     if report_item is not None:
         self._report_list([
             report_item,
             # reason is in previous report item, warning is there
             # implicit
             ReportItem.warning(
                 reports.messages.UnableToGetSbdStatus(node_label, "")),
         ])
         return
     try:
         self._status_list.append({
             "node": node_label,
             "status": json.loads(response.data)["sbd"]
         })
         self._successful_target_list.append(node_label)
     except (ValueError, KeyError) as e:
         self._report(
             ReportItem.warning(
                 reports.messages.UnableToGetSbdStatus(node_label, str(e))))
Exemplo n.º 16
0
    def _process_response(self, response):
        report_item = response_to_report_item(
            response, severity=ReportItemSeverity.WARNING)
        node = response.request.target.label
        if report_item is not None:
            self.__has_failures = True
            self._report(report_item)
            return self._get_next_list()
        try:
            output = json.loads(response.data)
            if output["code"] == "reloaded":
                self.__was_successful = True
                self._report(
                    ReportItem.info(
                        reports.messages.CorosyncConfigReloaded(node)))
                return []

            if output["code"] == "not_running":
                self._report(
                    ReportItem.warning(
                        reports.messages.CorosyncConfigReloadNotPossible(
                            node)))
            else:
                self.__has_failures = True
                self._report(
                    ReportItem.warning(
                        reports.messages.CorosyncConfigReloadError(
                            output["message"],
                            node=node,
                        )))
        except (ValueError, LookupError):
            self.__has_failures = True
            self._report(
                ReportItem.warning(
                    reports.messages.InvalidResponseFormat(node)))

        return self._get_next_list()
Exemplo n.º 17
0
    def _process_response(self, response):
        report_item = response_to_report_item(
            response, severity=ReportItemSeverity.WARNING)
        if report_item is not None:
            self._report(report_item)
            return self._get_next_list()

        node = response.request.target.label
        try:
            output = json.loads(response.data)
            if output["status"] == "success":
                self._was_successful = True
                self._cluster_status = output["data"]
                return []
            if output["status_msg"]:
                self._report(
                    ReportItem.error(
                        reports.messages.NodeCommunicationCommandUnsuccessful(
                            node,
                            response.request.action,
                            output["status_msg"],
                        )))
            # TODO Node name should be added to each received report item and
            # those modified report itemss should be reported. That, however,
            # requires reports overhaul which would add possibility to add a
            # node name to any report item. Also, infos and warnings should not
            # be ignored.
            if output["report_list"]:
                for report_data in output["report_list"]:
                    if (report_data["severity"] == ReportItemSeverity.ERROR
                            and report_data["report_text"]):
                        # pylint: disable=line-too-long
                        self._report(
                            ReportItem.error(
                                reports.messages.
                                NodeCommunicationCommandUnsuccessful(
                                    node,
                                    response.request.action,
                                    report_data["report_text"],
                                )))
        except (ValueError, LookupError, TypeError):
            self._report(
                ReportItem.warning(
                    reports.messages.InvalidResponseFormat(node)))

        return self._get_next_list()
Exemplo n.º 18
0
 def _process_response(self, response):
     report_item = response_to_report_item(
         response, severity=ReportItemSeverity.WARNING)
     node_label = response.request.target.label
     if report_item is not None:
         if not response.was_connected:
             self._report(report_item)
         self._report(
             ReportItem.warning(
                 reports.messages.UnableToGetSbdConfig(node_label, "")))
         return
     self._config_list.append({
         "node":
         node_label,
         "config":
         environment_file_to_dict(response.data),
     })
     self._successful_target_list.append(node_label)
Exemplo n.º 19
0
def _defaults_create(
    env: LibraryEnvironment,
    cib_section_name: str,
    validator_options: Mapping[str, Any],
    nvpairs: Mapping[str, str],
    nvset_options: Mapping[str, str],
    nvset_rule: Optional[str] = None,
    force_flags: Optional[Container] = None,
) -> None:
    if force_flags is None:
        force_flags = set()
    force = (reports.codes.FORCE
             in force_flags) or (reports.codes.FORCE_OPTIONS in force_flags)

    required_cib_version = None
    if nvset_rule:
        required_cib_version = Version(3, 4, 0)
    cib = env.get_cib(required_cib_version)
    id_provider = IdProvider(cib)

    validator = nvpair_multi.ValidateNvsetAppendNew(
        id_provider,
        nvpairs,
        nvset_options,
        nvset_rule=nvset_rule,
        **validator_options,
    )
    if env.report_processor.report_list(
            validator.validate(force_options=force)).has_errors:
        raise LibraryError()

    nvpair_multi.nvset_append_new(
        sections.get(cib, cib_section_name),
        id_provider,
        nvpair_multi.NVSET_META,
        nvpairs,
        nvset_options,
        nvset_rule=validator.get_parsed_rule(),
    )

    env.report_processor.report(
        ReportItem.warning(reports.messages.DefaultsCanBeOverriden()))
    env.push_cib()
Exemplo n.º 20
0
def disable_sbd(lib_env, ignore_offline_nodes=False):
    """
    Disable SBD on all nodes in cluster.

    lib_env -- LibraryEnvironment
    ignore_offline_nodes -- if True, omit offline nodes
    """
    node_list, get_nodes_report_list = get_existing_nodes_names(
        lib_env.get_corosync_conf()
    )
    if not node_list:
        get_nodes_report_list.append(
            ReportItem.error(reports.messages.CorosyncConfigNoNodesDefined())
        )
    if lib_env.report_processor.report_list(get_nodes_report_list).has_errors:
        raise LibraryError()

    com_cmd = GetOnlineTargets(
        lib_env.report_processor,
        ignore_offline_targets=ignore_offline_nodes,
    )
    com_cmd.set_targets(
        lib_env.get_node_target_factory().get_target_list(
            node_list,
            skip_non_existing=ignore_offline_nodes,
        )
    )
    online_nodes = run_and_raise(lib_env.get_node_communicator(), com_cmd)

    com_cmd = SetStonithWatchdogTimeoutToZero(lib_env.report_processor)
    com_cmd.set_targets(online_nodes)
    run_and_raise(lib_env.get_node_communicator(), com_cmd)

    com_cmd = DisableSbdService(lib_env.report_processor)
    com_cmd.set_targets(online_nodes)
    run_and_raise(lib_env.get_node_communicator(), com_cmd)

    lib_env.report_processor.report(
        ReportItem.warning(
            reports.messages.ClusterRestartRequiredToApplyChanges()
        )
    )
Exemplo n.º 21
0
def update_device(
    lib_env: LibraryEnvironment,
    model_options,
    generic_options,
    heuristics_options,
    force_options=False,
    skip_offline_nodes=False,
):
    """
    Change quorum device settings, distribute and reload configs if live

    dict model_options -- model specific options
    dict generic_options -- generic quorum device options
    dict heuristics_options -- heuristics options
    bool force_options -- continue even if options are not valid
    bool skip_offline_nodes -- continue even if not all nodes are accessible
    """
    cfg = lib_env.get_corosync_conf()
    if not cfg.has_quorum_device():
        raise LibraryError(
            ReportItem.error(reports.messages.QdeviceNotDefined())
        )
    if lib_env.report_processor.report_list(
        corosync_conf_validators.update_quorum_device(
            cfg.get_quorum_device_model(),
            model_options,
            generic_options,
            heuristics_options,
            [node.nodeid for node in cfg.get_nodes()],
            force_options=force_options,
        )
    ).has_errors:
        raise LibraryError()
    cfg.update_quorum_device(model_options, generic_options, heuristics_options)
    if cfg.is_quorum_device_heuristics_enabled_with_no_exec():
        lib_env.report_processor.report(
            ReportItem.warning(
                reports.messages.CorosyncQuorumHeuristicsEnabledWithNoExec()
            )
        )
    lib_env.push_corosync_conf(cfg, skip_offline_nodes)
Exemplo n.º 22
0
def _set_any_defaults(section_name, env: LibraryEnvironment, options):
    """
    string section_name -- determine the section of defaults
    env -- provides access to outside environment
    dict options -- are desired options with its values; when value is empty the
        option have to be removed
    """
    # Do not ever remove the nvset element, even if it is empty. There may be
    # ACLs set in pacemaker which allow "write" for nvpairs (adding, changing
    # and removing) but not nvsets. In such a case, removing the nvset would
    # cause the whole change to be rejected by pacemaker with a "permission
    # denied" message.
    # https://bugzilla.redhat.com/show_bug.cgi?id=1642514
    env.report_processor.report(
        ReportItem.warning(reports.messages.DefaultsCanBeOverriden()))

    if not options:
        return

    cib = env.get_cib()

    # Do not create new defaults element if we are only removing values from it.
    only_removing = True
    for value in options.values():
        if value != "":
            only_removing = False
            break
    if only_removing and not sections.exists(cib, section_name):
        return

    defaults_section = sections.get(cib, section_name)
    arrange_first_meta_attributes(
        defaults_section,
        options,
        IdProvider(cib),
        new_id="{0}-options".format(section_name),
    )

    env.push_cib()
Exemplo n.º 23
0
def _defaults_config(env: LibraryEnvironment, cib_section_name: str,
                     evaluate_expired: bool) -> List[CibNvsetDto]:
    runner = env.cmd_runner()
    cib = env.get_cib()

    if evaluate_expired:
        if has_rule_in_effect_status_tool():
            in_effect_eval: RuleInEffectEval = RuleInEffectEvalOneByOne(
                cib, runner)
        else:
            in_effect_eval = RuleInEffectEvalDummy()
            env.report_processor.report(
                ReportItem.warning(reports.messages.
                                   RuleInEffectStatusDetectionNotSupported()))
    else:
        in_effect_eval = RuleInEffectEvalDummy()

    return [
        nvpair_multi.nvset_element_to_dto(nvset_el, in_effect_eval)
        for nvset_el in nvpair_multi.find_nvsets(
            sections.get(cib, cib_section_name))
    ]
Exemplo n.º 24
0
def uniquify_operations_intervals(
    operation_list: Iterable[CibResourceOperationDto],
) -> Tuple[reports.ReportItemList, List[CibResourceOperationDto]]:
    """
    Return list of operation where intervals for the same operation are unique

    operation_list -- operations the new operation list will be based on
    """
    get_unique_interval = _get_interval_uniquer()
    report_list = []
    new_operations = []
    for operation in operation_list:
        new_interval = get_unique_interval(operation.name, operation.interval)
        if new_interval != operation.interval:
            report_list.append(
                ReportItem.warning(
                    reports.messages.ResourceOperationIntervalAdapted(
                        operation.name,
                        operation.interval,
                        new_interval,
                    )))
            operation = dt_replace(operation, interval=new_interval)
        new_operations.append(operation)
    return report_list, new_operations
Exemplo n.º 25
0
def add_device(
    lib_env: LibraryEnvironment,
    model,
    model_options,
    generic_options,
    heuristics_options,
    force_model=False,
    force_options=False,
    skip_offline_nodes=False,
):
    # pylint: disable=too-many-locals
    """
    Add a quorum device to a cluster, distribute and reload configs if live

    string model -- quorum device model
    dict model_options -- model specific options
    dict generic_options -- generic quorum device options
    dict heuristics_options -- heuristics options
    bool force_model -- continue even if the model is not valid
    bool force_options -- continue even if options are not valid
    bool skip_offline_nodes -- continue even if not all nodes are accessible
    """
    cfg = lib_env.get_corosync_conf()
    if cfg.has_quorum_device():
        raise LibraryError(
            ReportItem.error(reports.messages.QdeviceAlreadyDefined()))

    report_processor = lib_env.report_processor
    report_processor.report_list(
        corosync_conf_validators.add_quorum_device(
            model,
            model_options,
            generic_options,
            heuristics_options,
            [node.nodeid for node in cfg.get_nodes()],
            force_model=force_model,
            force_options=force_options,
        ))

    if lib_env.is_corosync_conf_live:
        cluster_nodes_names, report_list = get_existing_nodes_names(
            cfg,
            # Pcs is unable to communicate with nodes missing names. It cannot
            # send new corosync.conf to them. That might break the cluster.
            # Hence we error out.
            error_on_missing_name=True,
        )
        report_processor.report_list(report_list)

    if report_processor.has_errors:
        raise LibraryError()

    cfg.add_quorum_device(
        model,
        model_options,
        generic_options,
        heuristics_options,
    )
    if cfg.is_quorum_device_heuristics_enabled_with_no_exec():
        lib_env.report_processor.report(
            ReportItem.warning(
                reports.messages.CorosyncQuorumHeuristicsEnabledWithNoExec()))

    # First setup certificates for qdevice, then send corosync.conf to nodes.
    # If anything fails, nodes will not have corosync.conf with qdevice in it,
    # so there is no effect on the cluster.
    if lib_env.is_corosync_conf_live:
        target_factory = lib_env.get_node_target_factory()
        target_list = target_factory.get_target_list(
            cluster_nodes_names,
            skip_non_existing=skip_offline_nodes,
        )
        # Do model specific configuration.
        # If the model is not known to pcs and was forced, do not configure
        # anything else than corosync.conf, as we do not know what to do
        # anyway.
        if model == "net":
            qdevice_net.set_up_client_certificates(
                lib_env.cmd_runner(),
                lib_env.report_processor,
                lib_env.communicator_factory,
                # We are sure the "host" key is there, it has been validated
                # above.
                target_factory.get_target_from_hostname(model_options["host"]),
                cfg.get_cluster_name(),
                target_list,
                skip_offline_nodes,
            )

        lib_env.report_processor.report(
            ReportItem.info(
                reports.messages.ServiceActionStarted(
                    reports.const.SERVICE_ACTION_ENABLE, "corosync-qdevice")))
        com_cmd = qdevice_com.Enable(lib_env.report_processor,
                                     skip_offline_nodes)
        com_cmd.set_targets(target_list)
        run_and_raise(lib_env.get_node_communicator(), com_cmd)

    # everything set up, it's safe to tell the nodes to use qdevice
    lib_env.push_corosync_conf(cfg, skip_offline_nodes)

    # Now, when corosync.conf has been reloaded, we can start qdevice service.
    if lib_env.is_corosync_conf_live:
        lib_env.report_processor.report(
            ReportItem.info(
                reports.messages.ServiceActionStarted(
                    reports.const.SERVICE_ACTION_START, "corosync-qdevice")))
        com_cmd_start = qdevice_com.Start(lib_env.report_processor,
                                          skip_offline_nodes)
        com_cmd_start.set_targets(target_list)
        run_and_raise(lib_env.get_node_communicator(), com_cmd_start)
Exemplo n.º 26
0
 def on_complete(self):
     if self._unreachable_nodes:
         self._report(
             ReportItem.warning(
                 reports.messages.NodesToRemoveUnreachable(
                     sorted(self._unreachable_nodes))))
Exemplo n.º 27
0
def _validate_generic_container_options_update(container_el, options,
                                               force_options):
    validators_optional_options = [
        validate.ValueNonnegativeInteger("masters"),
        validate.ValueNonnegativeInteger("promoted-max"),
        validate.ValuePositiveInteger("replicas"),
        validate.ValuePositiveInteger("replicas-per-host"),
    ]
    for val in validators_optional_options:
        val.empty_string_valid = True
    validators = [
        validate.NamesIn(
            # allow to remove options even if they are not allowed
            GENERIC_CONTAINER_OPTIONS | _options_to_remove(options),
            option_type="container",
            **validate.set_warning(report_codes.FORCE_OPTIONS, force_options),
        ),
        # image is a mandatory attribute and cannot be removed
        validate.ValueNotEmpty("image", "image name"),
    ] + validators_optional_options

    # CIB does not allow both to be set. Deleting both is not a problem,
    # though. Deleting one while setting another also works and is further
    # checked bellow.
    if not (options.get("masters", "") == ""
            or options.get("promoted-max", "") == ""):
        validators.append(
            validate.MutuallyExclusive(
                ["masters", "promoted-max"],
                option_type="container",
            ))

    deprecation_reports = []
    if options.get("masters"):
        # If the user wants to delete the masters option, do not report it is
        # deprecated. They may be removing it because they just found out it is
        # deprecated.
        deprecation_reports.append(
            ReportItem.warning(
                reports.messages.DeprecatedOption(
                    "masters",
                    ["promoted-max"],
                    "container",
                )))
    # Do not allow to set masters if promoted-max is set unless promoted-max is
    # going to be removed now. Do the same check also the other way around. CIB
    # only allows one of them to be set.
    if (options.get("masters") and container_el.get("promoted-max")
            and options.get("promoted-max") != ""):
        deprecation_reports.append(
            ReportItem.error(
                reports.messages.PrerequisiteOptionMustNotBeSet(
                    "masters", "promoted-max", "container", "container")))
    if (options.get("promoted-max") and container_el.get("masters")
            and options.get("masters") != ""):
        deprecation_reports.append(
            ReportItem.error(
                reports.messages.PrerequisiteOptionMustNotBeSet(
                    "promoted-max", "masters", "container", "container")))

    return (validate.ValidatorAll(validators).validate(options) +
            deprecation_reports)
Exemplo n.º 28
0
def _defaults_update(
    env: LibraryEnvironment,
    cib_section_name: str,
    nvset_id: Optional[str],
    nvpairs: Mapping[str, str],
    pcs_command: reports.types.PcsCommand,
) -> None:
    cib = env.get_cib()
    id_provider = IdProvider(cib)

    if nvset_id is None:
        # Backward compatibility code to support an old use case where no id
        # was requested and provided and the first meta_attributes nvset was
        # created / updated. However, we check that there is only one nvset
        # present in the CIB to prevent breaking the configuration with
        # multiple nvsets in place.

        # This is to be supported as it provides means of easily managing
        # defaults if only one set of defaults is needed.

        # TODO move this to a separate lib command.

        if not nvpairs:
            return

        # Do not create new defaults element if we are only removing values
        # from it.
        only_removing = True
        for value in nvpairs.values():
            if value != "":
                only_removing = False
                break
        if only_removing and not sections.exists(cib, cib_section_name):
            env.report_processor.report(
                ReportItem.warning(reports.messages.DefaultsCanBeOverriden()))
            return

        nvset_elements = nvpair_multi.find_nvsets(
            sections.get(cib, cib_section_name))
        if len(nvset_elements) > 1:
            env.report_processor.report(
                reports.item.ReportItem.error(
                    reports.messages.CibNvsetAmbiguousProvideNvsetId(
                        pcs_command)))
            raise LibraryError()
        env.report_processor.report(
            ReportItem.warning(reports.messages.DefaultsCanBeOverriden()))
        if len(nvset_elements) == 1:
            nvpair_multi.nvset_update(nvset_elements[0], id_provider, nvpairs)
        elif only_removing:
            # do not create new nvset if there is none and we are only removing
            # nvpairs
            return
        else:
            nvpair_multi.nvset_append_new(
                sections.get(cib, cib_section_name),
                id_provider,
                nvpair_multi.NVSET_META,
                nvpairs,
                {},
            )
        env.push_cib()
        return

    nvset_elements, report_list = nvpair_multi.find_nvsets_by_ids(
        sections.get(cib, cib_section_name), [nvset_id])
    if env.report_processor.report_list(report_list).has_errors:
        raise LibraryError()

    nvpair_multi.nvset_update(nvset_elements[0], id_provider, nvpairs)
    env.report_processor.report(
        ReportItem.warning(reports.messages.DefaultsCanBeOverriden()))
    env.push_cib()
Exemplo n.º 29
0
def config_destroy(
    env: LibraryEnvironment,
    instance_name=None,
    ignore_config_load_problems=False,
):
    # pylint: disable=too-many-branches
    """
    remove booth configuration files

    env
    string instance_name -- booth instance name
    bool ignore_config_load_problems -- delete as much as possible when unable
            to read booth configs for the given booth instance
    """
    report_processor = env.report_processor
    booth_env = env.get_booth_env(instance_name)
    instance_name = booth_env.instance_name
    _ensure_live_env(env, booth_env)

    # TODO use constants in reports
    if resource.find_for_config(
        get_resources(env.get_cib()), booth_env.config_path,
    ):
        report_processor.report(
            ReportItem.error(
                reports.messages.BoothConfigIsUsed(
                    instance_name, "in cluster resource",
                )
            )
        )
    # Only systemd is currently supported. Initd does not supports multiple
    # instances (here specified by name)
    if external.is_systemctl():
        if external.is_service_running(
            env.cmd_runner(), "booth", instance_name
        ):
            report_processor.report(
                ReportItem.error(
                    reports.messages.BoothConfigIsUsed(
                        instance_name, "(running in systemd)",
                    )
                )
            )

        if external.is_service_enabled(
            env.cmd_runner(), "booth", instance_name
        ):
            report_processor.report(
                ReportItem.error(
                    reports.messages.BoothConfigIsUsed(
                        instance_name, "(enabled in systemd)",
                    )
                )
            )
    if report_processor.has_errors:
        raise LibraryError()

    try:
        authfile_path = None
        booth_conf = booth_env.config.read_to_facade()
        authfile_path = booth_conf.get_authfile()
    except RawFileError as e:
        report_processor.report(
            raw_file_error_report(
                e,
                force_code=report_codes.FORCE_BOOTH_DESTROY,
                is_forced_or_warning=ignore_config_load_problems,
            )
        )
    except ParserErrorException as e:
        report_processor.report_list(
            booth_env.config.parser_exception_to_report_list(
                e,
                force_code=report_codes.FORCE_BOOTH_DESTROY,
                is_forced_or_warning=ignore_config_load_problems,
            )
        )
    if report_processor.has_errors:
        raise LibraryError()

    if authfile_path:
        authfile_dir, authfile_name = os.path.split(authfile_path)
        if (authfile_dir == settings.booth_config_dir) and authfile_name:
            try:
                key_file = FileInstance.for_booth_key(authfile_name)
                key_file.raw_file.remove(fail_if_file_not_found=False)
            except RawFileError as e:
                report_processor.report(
                    raw_file_error_report(
                        e,
                        force_code=report_codes.FORCE_BOOTH_DESTROY,
                        is_forced_or_warning=ignore_config_load_problems,
                    )
                )
        else:
            report_processor.report(
                ReportItem.warning(
                    reports.messages.BoothUnsupportedFileLocation(
                        authfile_path,
                        settings.booth_config_dir,
                        file_type_codes.BOOTH_KEY,
                    )
                )
            )
    if report_processor.has_errors:
        raise LibraryError()

    try:
        booth_env.config.raw_file.remove()
    except RawFileError as e:
        report_processor.report(raw_file_error_report(e))

    if report_processor.has_errors:
        raise LibraryError()
Exemplo n.º 30
0
def remove_device(lib_env: LibraryEnvironment, skip_offline_nodes=False):
    """
    Stop using quorum device, distribute and reload configs if live
    skip_offline_nodes continue even if not all nodes are accessible
    """
    cfg = lib_env.get_corosync_conf()
    if not cfg.has_quorum_device():
        raise LibraryError(
            ReportItem.error(reports.messages.QdeviceNotDefined()))
    model = cfg.get_quorum_device_model()
    cfg.remove_quorum_device()

    if lib_env.is_corosync_conf_live:
        report_processor = lib_env.report_processor
        # get nodes for communication
        cluster_nodes_names, report_list = get_existing_nodes_names(
            cfg,
            # Pcs is unable to communicate with nodes missing names. It cannot
            # send new corosync.conf to them. That might break the cluster.
            # Hence we error out.
            error_on_missing_name=True,
        )
        if report_processor.report_list(report_list).has_errors:
            raise LibraryError()
        target_list = lib_env.get_node_target_factory().get_target_list(
            cluster_nodes_names,
            skip_non_existing=skip_offline_nodes,
        )
        # fix quorum options for SBD to work properly
        if sbd.atb_has_to_be_enabled(lib_env.service_manager, cfg):
            lib_env.report_processor.report(
                ReportItem.warning(
                    reports.messages.CorosyncQuorumAtbWillBeEnabledDueToSbd()))
            cfg.set_quorum_options({"auto_tie_breaker": "1"})

        # disable qdevice
        lib_env.report_processor.report(
            ReportItem.info(
                reports.messages.ServiceActionStarted(
                    reports.const.SERVICE_ACTION_DISABLE, "corosync-qdevice")))
        com_cmd_disable = qdevice_com.Disable(lib_env.report_processor,
                                              skip_offline_nodes)
        com_cmd_disable.set_targets(target_list)
        run_and_raise(lib_env.get_node_communicator(), com_cmd_disable)
        # stop qdevice
        lib_env.report_processor.report(
            ReportItem.info(
                reports.messages.ServiceActionStarted(
                    reports.const.SERVICE_ACTION_STOP, "corosync-qdevice")))
        com_cmd_stop = qdevice_com.Stop(lib_env.report_processor,
                                        skip_offline_nodes)
        com_cmd_stop.set_targets(target_list)
        run_and_raise(lib_env.get_node_communicator(), com_cmd_stop)
        # handle model specific configuration
        if model == "net":
            lib_env.report_processor.report(
                ReportItem.info(
                    reports.messages.QdeviceCertificateRemovalStarted()))
            com_cmd_client_destroy = qdevice_net_com.ClientDestroy(
                lib_env.report_processor, skip_offline_nodes)
            com_cmd_client_destroy.set_targets(target_list)
            run_and_raise(lib_env.get_node_communicator(),
                          com_cmd_client_destroy)

    lib_env.push_corosync_conf(cfg, skip_offline_nodes)