Example #1
0
def validate_remove_tag(
    constraint_section: _Element,
    to_remove_tag_list: Iterable[str],
) -> ReportItemList:
    """
    Validation function for tag removal. List of tag elements is not empty and
    no tag is referenced in any constraint.

    constraint_section -- element constraints
    to_remove_tag_list -- list of tag ids for removal
    """
    # list for emptiness check (issue with some iterables like iterator)
    to_remove_tag_list = list(to_remove_tag_list)
    if not to_remove_tag_list:
        return [
            ReportItem.error(
                reports.messages.TagCannotRemoveTagsNoTagsSpecified())
        ]
    report_list = []
    for tag_id in to_remove_tag_list:
        constraint_list = find_constraints_referencing_tag(
            constraint_section,
            tag_id,
        )
        if constraint_list:
            report_list.append(
                ReportItem.error(
                    reports.messages.TagCannotRemoveTagReferencedInConstraints(
                        tag_id,
                        sorted([
                            str(elem.get("id", "")) for elem in constraint_list
                        ]),
                    )))
    return report_list
Example #2
0
def _find_resources_to_remove(
    cib,
    report_processor: ReportProcessor,
    node_type,
    node_identifier,
    allow_remove_multiple_nodes,
    find_resources,
):
    resource_element_list = find_resources(get_resources(cib), node_identifier)

    if not resource_element_list:
        raise LibraryError(
            ReportItem.error(
                reports.messages.NodeNotFound(node_identifier, [node_type])))

    if len(resource_element_list) > 1:
        if report_processor.report(
                ReportItem(
                    severity=reports.item.get_severity(
                        reports.codes.FORCE,
                        allow_remove_multiple_nodes,
                    ),
                    message=reports.messages.MultipleResultsFound(
                        "resource",
                        [
                            resource.attrib["id"]
                            for resource in resource_element_list
                        ],
                        node_identifier,
                    ),
                )).has_errors:
            raise LibraryError()

    return resource_element_list
Example #3
0
 def _validate_adjacent_id(self) -> ReportItemList:
     """
     Validate that element with adjacent id exists and it is not currently
     being added or removed.
     """
     report_list: ReportItemList = []
     if self._tag_element is None or self._adjacent_idref is None:
         return report_list
     self._adjacent_obj_ref_element = self._find_obj_ref_in_tag(
         self._adjacent_idref, )
     if self._adjacent_obj_ref_element is None:
         report_list.append(
             ReportItem.error(
                 reports.messages.TagAdjacentReferenceIdNotInTheTag(
                     self._adjacent_idref,
                     self._tag_id,
                 )))
     if self._adjacent_obj_ref_element is not None:
         if self._adjacent_idref in self._add_idref_list:
             report_list.append(
                 ReportItem.error(
                     reports.messages.TagCannotPutIdNextToItself(
                         self._adjacent_idref, )))
         if self._adjacent_idref in self._remove_idref_list:
             report_list.append(
                 ReportItem.error(
                     reports.messages.TagCannotRemoveAdjacentId(
                         self._adjacent_idref, )))
     return report_list
Example #4
0
    def validate(self, option_dict: TypeOptionMap) -> ReportItemList:
        name_set = set(option_dict.keys())
        banned_names = set()
        if not (self._severity.force_code is None
                and self._severity.level == ReportItemSeverity.ERROR):
            banned_names = name_set & self._banned_name_set
        invalid_names = name_set - set(self._option_name_list) - banned_names

        report_list = []
        if invalid_names:
            report_list.append(
                ReportItem(
                    severity=self._severity,
                    message=reports.messages.InvalidOptions(
                        sorted(invalid_names),
                        sorted(self._option_name_list),
                        self._option_type,
                        allowed_patterns=sorted(self._allowed_option_patterns),
                    ),
                ))
        if banned_names:
            report_list.append(
                ReportItem.error(
                    reports.messages.InvalidOptions(
                        sorted(banned_names),
                        sorted(self._option_name_list),
                        self._option_type,
                    )))
        return report_list
Example #5
0
def _update_digest_attrs_in_lrm_rsc_op(
        lrm_rsc_op: _Element, calculated_digests: Dict[str, Optional[str]]):
    """
    Update digest attributes in lrm_rsc_op elements. If there are missing
    digests values from pacemaker or missing digests attributes in lrm_rsc_op
    element then report an error.

    lrm_rsc_op -- element whose digests attributes needs to be updated in order
        to do restartless update of resource
    calculated_digests -- digests calculated by pacemaker for this lrm_rsc_op
        element
    """
    common_digests_attrs = set(DIGEST_ATTRS).intersection(
        lrm_rsc_op.attrib.keys())
    if not common_digests_attrs:
        # this should not happen and when it does it is pacemaker fault
        raise LibraryError(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnableToPerform(
                    "no digests attributes in lrm_rsc_op element", )))
    for attr in common_digests_attrs:
        new_digest = calculated_digests[DIGEST_ATTR_TO_TYPE_MAP[attr]]
        if new_digest is None:
            # this should not happen and when it does it is pacemaker fault
            raise LibraryError(
                ReportItem.error(
                    reports.messages.StonithRestartlessUpdateUnableToPerform(
                        (f"necessary digest for '{attr}' attribute is "
                         "missing"))))
        # update digest in cib
        lrm_rsc_op.attrib[attr] = new_digest
Example #6
0
    def validate(self, option_dict):
        name_set = set(option_dict.keys())
        banned_names = set()
        if not (self._code_for_warning is None and not self._produce_warning):
            banned_names = name_set & self._banned_name_set
        invalid_names = name_set - set(self._option_name_list) - banned_names

        report_list = []
        if invalid_names:
            report_list.append(
                ReportItem(
                    severity=reports.item.get_severity(
                        self._code_for_warning, self._produce_warning,
                    ),
                    message=reports.messages.InvalidOptions(
                        sorted(invalid_names),
                        sorted(self._option_name_list),
                        self._option_type,
                        allowed_patterns=sorted(self._allowed_option_patterns),
                    ),
                )
            )
        if banned_names:
            report_list.append(
                ReportItem.error(
                    reports.messages.InvalidOptions(
                        sorted(banned_names),
                        sorted(self._option_name_list),
                        self._option_type,
                    )
                )
            )
        return report_list
Example #7
0
def _host_check_remote_node(host_info_dict):
    # Version of services may not be the same across the existing cluster
    # nodes, so it's not easy to make this check properly.
    report_list = []
    required_service_list = ["pacemaker_remote"]
    required_as_stopped_service_list = required_service_list + [
        "pacemaker",
        "corosync",
    ]
    for host_name, host_info in host_info_dict.items():
        try:
            services = host_info["services"]
            missing_service_list = [
                service
                for service in required_service_list
                if not services[service]["installed"]
            ]
            if missing_service_list:
                report_list.append(
                    ReportItem.error(
                        reports.messages.ServiceNotInstalled(
                            host_name, sorted(missing_service_list)
                        )
                    )
                )
            cannot_be_running_service_list = [
                service
                for service in required_as_stopped_service_list
                if service in services and services[service]["running"]
            ]
            if cannot_be_running_service_list:
                report_list.append(
                    ReportItem.error(
                        reports.messages.HostAlreadyInClusterServices(
                            host_name,
                            sorted(cannot_be_running_service_list),
                        )
                    )
                )
            if host_info["cluster_configuration_exists"]:
                report_list.append(
                    ReportItem.error(
                        reports.messages.HostAlreadyInClusterConfig(host_name)
                    )
                )
        except (KeyError, TypeError):
            report_list.append(
                ReportItem.error(
                    reports.messages.InvalidResponseFormat(host_name)
                )
            )
    return report_list
Example #8
0
def _reports_skip_new_node(new_node_name, reason_type):
    assert reason_type in {"unreachable", "not_live_cib"}
    return [
        ReportItem.info(
            reports.messages.FilesDistributionSkipped(reason_type,
                                                      ["pacemaker authkey"],
                                                      [new_node_name])),
        ReportItem.info(
            reports.messages.ServiceCommandsOnNodesSkipped(
                reason_type,
                ["pacemaker_remote start", "pacemaker_remote enable"],
                [new_node_name],
            )),
    ]
Example #9
0
def _report_skip_live_parts_in_remove(node_names_list):
    return [
        ReportItem.info(
            reports.messages.ServiceCommandsOnNodesSkipped(
                reports.const.REASON_NOT_LIVE_CIB,
                ["pacemaker_remote stop", "pacemaker_remote disable"],
                node_names_list,
            )),
        ReportItem.info(
            reports.messages.FilesRemoveFromNodesSkipped(
                reports.const.REASON_NOT_LIVE_CIB,
                ["pacemaker authkey"],
                node_names_list,
            )),
    ]
Example #10
0
def get_node_key_map_for_mpath(
    stonith_el: _Element, node_labels: Iterable[str]
) -> Dict[str, str]:
    library_error = lambda host_map, missing_nodes: LibraryError(
        ReportItem.error(
            reports.messages.StonithRestartlessUpdateMissingMpathKeys(
                host_map, sorted(missing_nodes)
            )
        )
    )
    pcmk_host_map_value = get_value(
        INSTANCE_ATTRIBUTES_TAG, stonith_el, "pcmk_host_map"
    )
    missing_nodes = set(node_labels)
    if not pcmk_host_map_value:
        raise library_error(pcmk_host_map_value, missing_nodes)
    node_key_map = {}
    pattern = re.compile(r"(?P<node>[^=:; \t]+)[=:](?P<key>[^=:; \t]+)[; \t]?")
    for match in pattern.finditer(pcmk_host_map_value):
        if match:
            group_dict = match.groupdict()
            node_key_map[group_dict["node"]] = group_dict["key"]
    missing_nodes -= set(node_key_map.keys())
    if missing_nodes:
        raise library_error(pcmk_host_map_value, missing_nodes)
    return node_key_map
Example #11
0
 def _validate_ids_for_update_are_specified(self) -> ReportItemList:
     """
     Validate that either of add or remove ids were specified or that add ids
     were specified in case of specified adjacent id.
     """
     report_list: ReportItemList = []
     if not self._add_idref_list and not self._remove_idref_list:
         report_list.append(
             ReportItem.error(
                 reports.messages.TagCannotUpdateTagNoIdsSpecified()))
     if not self._add_idref_list and self._adjacent_idref is not None:
         report_list.append(
             ReportItem.error(
                 reports.messages.TagCannotSpecifyAdjacentIdWithoutIdsToAdd(
                     self._adjacent_idref, )))
     return report_list
Example #12
0
 def _validate_value(self, value):
     if not isinstance(value.normalized, str):
         return []
     forbidden_characters = "{}\n\r"
     if set(value.normalized) & set(forbidden_characters):
         # We must be strict and do not allow to override this validation,
         # otherwise setting a cratfed option value could be misused for
         # setting arbitrary corosync.conf settings.
         return [
             ReportItem.error(
                 reports.messages.InvalidOptionValue(
                     self._get_option_name_for_report(),
                     value.original,
                     None,
                     # Make it actually print "\n" and "\r" strings instead
                     # of going to the next line.
                     # Let the user know all
                     # forbidden characters right away. Do not let them try
                     # them one by one by only reporting those actually used
                     # in the value.
                     forbidden_characters="{}\\n\\r",
                 )
             )
         ]
     return []
Example #13
0
 def _validate_value(self, value: ValuePair) -> ReportItemList:
     report_list = []
     if not is_score(value.normalized):
         report_list.append(
             ReportItem.error(reports.messages.InvalidScore(
                 value.original)))
     return report_list
Example #14
0
    def validate_parameters_create(
        self,
        parameters,
        force=False,
        # TODO remove this argument, see pcs.lib.cib.commands.remote_node.create
        # for details
        do_not_report_instance_attribute_server_exists=False,
    ):
        # This is just a basic validation checking that required parameters are
        # set and all set parameters are known to an agent. Missing checks are:
        # 1. values checks - if a param is an integer, then "abc" is not valid
        # 2. warnings should be emitted when a deprecated param is set
        # 3. errors should be emitted when a deprecated parameter and a
        #    parameter obsoleting it are set at the same time
        # 4. possibly some other checks
        # All of these have been missing in pcs since ever (ad 1. agents have
        # never provided enough info for us to do such validations, ad 2. and
        # 3. there were no deprecated parameters before). The checks should be
        # implemented in agents themselves, so I'm not adding them now either.
        report_items = []

        # report unknown parameters
        report_items.extend(
            validate.NamesIn(
                {param["name"]
                 for param in self.get_parameters()},
                option_type=self._agent_type_label,
                **validate.set_warning(reports.codes.FORCE_OPTIONS, force),
            ).validate(parameters))
        # TODO remove this "if", see pcs.lib.cib.commands.remote_node.create
        # for details
        if do_not_report_instance_attribute_server_exists:
            for report_item in report_items:
                if isinstance(report_item, ReportItem) and isinstance(
                        report_item.message, reports.messages.InvalidOptions):
                    report_msg = cast(reports.messages.InvalidOptions,
                                      report_item.message)
                    report_item.message = reports.messages.InvalidOptions(
                        report_msg.option_names,
                        sorted([
                            value for value in report_msg.allowed
                            if value != "server"
                        ]),
                        report_msg.option_type,
                        report_msg.allowed_patterns,
                    )

        # report missing required parameters
        missing_parameters = self._find_missing_required_parameters(parameters)
        if missing_parameters:
            report_items.append(
                ReportItem(
                    severity=self._validate_report_severity(force),
                    message=reports.messages.RequiredOptionsAreMissing(
                        sorted(missing_parameters),
                        self._agent_type_label,
                    ),
                ))

        return report_items
Example #15
0
def find_valid_resource_agent_by_name(
    report_processor: ReportProcessor,
    runner: CommandRunner,
    name: str,
    allowed_absent=False,
    absent_agent_supported=True,
):
    """
    Return instance of ResourceAgent corresponding to name

    report_processor -- tool for warning/info/error reporting
    runner -- tool for launching external commands
    name -- specifies a searched agent
    absent_agent_supported -- flag decides if is possible to allow to return
        absent agent: if is produced forceable/no-forcable error
    """
    if ":" not in name:
        agent = guess_exactly_one_resource_agent_full_name(runner, name)
        report_processor.report(
            ReportItem.info(
                reports.messages.AgentNameGuessed(name, agent.get_name())))
        return agent

    return _find_valid_agent_by_name(
        report_processor,
        runner,
        name,
        ResourceAgent,
        AbsentResourceAgent if allowed_absent else None,
        absent_agent_supported=absent_agent_supported,
    )
Example #16
0
File: tag.py Project: mirecheck/pcs
 def _validate_ids_can_be_removed(self) -> ReportItemList:
     """
     Validate that ids can be removed:
     - there are no duplicate ids specified
     - ids exist in the tag
     - tag would not be left empty after removal
     Saves found elements to remove in case of succes validation.
     """
     report_list: ReportItemList = []
     if self._tag_element is None or not self._remove_idref_list:
         return report_list
     report_list.extend(
         _validate_add_remove_duplicate_reference_ids(
             self._remove_idref_list,
             add_or_not_remove=False,
         ),
     )
     missing_id_list = []
     for id_ref in set(self._remove_idref_list):
         obj_ref = self._find_obj_ref_in_tag(id_ref)
         if obj_ref is not None:
             self._remove_obj_ref_element_list.append(obj_ref)
         else:
             missing_id_list.append(id_ref)
     if missing_id_list:
         report_list.append(
             ReportItem.error(
                 reports.messages.TagIdsNotInTheTag(
                     self._tag_id,
                     sorted(missing_id_list),
                 )
             )
         )
     if not report_list and not self._add_idref_list:
         remove_difference = set(
             self._tag_element.findall(TAG_OBJREF),
         ).difference(set(self._remove_obj_ref_element_list))
         if not remove_difference:
             report_list.append(
                 ReportItem.error(
                     # pylint: disable=line-too-long
                     reports.messages.TagCannotRemoveReferencesWithoutRemovingTag(
                         self._tag_id,
                     )
                 )
             )
     return report_list
Example #17
0
def booth_cannot_determine_local_site_ip():
    """
    Some booth operations are performed on specific site and requires to specify
        site ip. When site specification omitted pcs can try determine local ip.
        But determine local site ip failed.
    """
    return ReportItem.error(report_codes.BOOTH_CANNOT_DETERMINE_LOCAL_SITE_IP,
                            info={})
Example #18
0
def booth_daemon_status_error(reason):
    """
    Unable to get status of booth daemon because of error.

    reason -- reason
    """
    return ReportItem.error(report_codes.BOOTH_DAEMON_STATUS_ERROR,
                            info={"reason": reason})
Example #19
0
def booth_not_exists_in_cib(name):
    """
    Remove booth instance from cib required. But no such instance found in cib.
    string name is booth instance name
    """
    return ReportItem.error(report_codes.BOOTH_NOT_EXISTS_IN_CIB,
                            info={
                                "name": name,
                            })
Example #20
0
def booth_even_peers_num(number):
    """
    Booth requires odd number of peers. But even number of peers was entered.
    integer number determines how many peers was entered
    """
    return ReportItem.error(report_codes.BOOTH_EVEN_PEERS_NUM,
                            info={
                                "number": number,
                            })
Example #21
0
def report_item_to_dict(report_item: ReportItem):
    dto_obj: ReportItemDto = report_item.to_dto()
    return dict(
        severity=dto_obj.severity.level,
        code=dto_obj.message.code,
        info=dto_obj.message.payload,
        forceable=dto_obj.severity.force_code,
        report_text=dto_obj.message.message,
    )
Example #22
0
def booth_address_duplication(duplicate_addresses):
    """
    Address of each peer must unique. But address duplication appeared.
    set duplicate_addresses contains addreses entered multiple times
    """
    return ReportItem.error(report_codes.BOOTH_ADDRESS_DUPLICATION,
                            info={
                                "addresses": sorted(duplicate_addresses),
                            })
Example #23
0
def booth_lack_of_sites(site_list):
    """
    Less than 2 booth sites entered. But it does not make sense.
    list site_list contains currently entered sites
    """
    return ReportItem.error(report_codes.BOOTH_LACK_OF_SITES,
                            info={
                                "sites": sorted(site_list),
                            })
Example #24
0
    def validate_parameters_update(self,
                                   current_parameters,
                                   new_parameters,
                                   force=False):
        # This is just a basic validation checking that required parameters are
        # set and all set parameters are known to an agent. Missing checks are:
        # 1. values checks - if a param is an integer, then "abc" is not valid
        # 2. warnings should be emitted when a deprecated param is set
        # 3. errors should be emitted when a deprecated parameter and a
        #    parameter obsoleting it are set at the same time
        # 4. possibly some other checks
        # All of these have been missing in pcs since ever (ad 1. agents have
        # never provided enough info for us to do such validations, ad 2. and
        # 3. there were no deprecated parameters before). The checks should be
        # implemented in agents themselves, so I'm not adding them now either.
        report_items = []

        # get resulting set of agent's parameters
        final_parameters = dict(current_parameters)
        for name, value in new_parameters.items():
            if value:
                final_parameters[name] = value
            else:
                if name in final_parameters:
                    del final_parameters[name]

        # report unknown parameters
        report_items.extend(
            validate.NamesIn(
                {param["name"]
                 for param in self.get_parameters()},
                option_type=self._agent_type_label,
                severity=reports.item.get_severity(reports.codes.FORCE_OPTIONS,
                                                   force),
            ).validate(
                # Do not report unknown parameters already set in the CIB. They
                # have been reported already when the were added to the CIB.
                {
                    name: value
                    for name, value in new_parameters.items()
                    if name not in current_parameters
                }))

        # report missing or removed required parameters
        missing_parameters = self._find_missing_required_parameters(
            final_parameters)
        if missing_parameters:
            report_items.append(
                ReportItem(
                    severity=self._validate_report_severity(force),
                    message=reports.messages.RequiredOptionsAreMissing(
                        sorted(missing_parameters),
                        self._agent_type_label,
                    ),
                ))

        return report_items
Example #25
0
def guess_exactly_one_resource_agent_full_name(runner, search_agent_name):
    """
    Get one resource agent matching specified search term
    string search_agent_name last part of full agent name
    Raise LibraryError if zero or more than one agents found
    """
    agents = guess_resource_agent_full_name(runner, search_agent_name)
    if not agents:
        raise LibraryError(
            ReportItem.error(
                reports.messages.AgentNameGuessFoundNone(search_agent_name)))
    if len(agents) > 1:
        raise LibraryError(
            ReportItem.error(
                reports.messages.AgentNameGuessFoundMoreThanOne(
                    search_agent_name,
                    [agent.get_name() for agent in agents])))
    return agents[0]
Example #26
0
def validate_stonith_restartless_update(
    cib: _Element,
    stonith_id: str,
) -> Tuple[Optional[_Element], ReportItemList]:
    """
    Validate that stonith device exists and its type is supported for
    restartless update of scsi devices and has defined option 'devices'.

    cib -- cib element
    stonith_id -- id of a stonith resource
    """
    stonith_el, report_list = common.find_one_resource(
        cib, stonith_id, resource_tags=[TAG_RESOURCE_PRIMITIVE]
    )
    if stonith_el is None:
        return stonith_el, report_list

    stonith_type = stonith_el.get("type", "")
    if (
        stonith_el.get("class", "") != "stonith"
        or stonith_el.get("provider", "") != ""
        or stonith_type not in SUPPORTED_RESOURCE_TYPES_FOR_RESTARTLESS_UPDATE
    ):
        report_list.append(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnsupportedAgent(
                    stonith_id,
                    stonith_type,
                    SUPPORTED_RESOURCE_TYPES_FOR_RESTARTLESS_UPDATE,
                )
            )
        )
        return stonith_el, report_list

    if not get_value(INSTANCE_ATTRIBUTES_TAG, stonith_el, "devices"):
        report_list.append(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnableToPerform(
                    "no devices option configured for stonith device "
                    f"'{stonith_id}'"
                )
            )
        )
    return stonith_el, report_list
Example #27
0
def booth_ticket_duplicate(ticket_name):
    """
    Each booth ticket name must be uniqe. But duplicate booth ticket name
        was entered.
    string ticket_name is entered booth ticket name
    """
    return ReportItem.error(report_codes.BOOTH_TICKET_DUPLICATE,
                            info={
                                "ticket_name": ticket_name,
                            })
Example #28
0
def booth_ticket_name_invalid(ticket_name):
    """
    Name of booth ticket may consists of alphanumeric characters or dash.
        Entered ticket name violating this rule.
    string ticket_name is entered booth ticket name
    """
    return ReportItem.error(report_codes.BOOTH_TICKET_NAME_INVALID,
                            info={
                                "ticket_name": ticket_name,
                            })
Example #29
0
def booth_peers_status_error(reason=None):
    """
    Unable to get status of booth peers because of error.

    reason -- reason
    """
    return ReportItem.error(report_codes.BOOTH_PEERS_STATUS_ERROR,
                            info={
                                "reason": reason,
                            })
Example #30
0
def booth_ticket_does_not_exist(ticket_name):
    """
    Some operations (like ticket remove) expect the ticket name in booth
        configuration. But the ticket name not found in booth configuration.
    string ticket_name is entered booth ticket name
    """
    return ReportItem.error(report_codes.BOOTH_TICKET_DOES_NOT_EXIST,
                            info={
                                "ticket_name": ticket_name,
                            })