Пример #1
0
def _validate_tag_create_idref_list_not_empty(
    idref_list: Iterable[str], ) -> ReportItemList:
    """
    Validate that list of reference ids for tag create is not empty.

    idref_list -- reference ids to validate
    """
    # list for emptiness check (issue with some iterables like iterator)
    idref_list = list(idref_list)
    if not idref_list:
        return [
            ReportItem.error(
                reports.messages.TagCannotCreateEmptyTagNoIdsSpecified())
        ]
    return []
Пример #2
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 = resource.common.find_one_resource(
        cib, stonith_id, resource_tags=[TAG_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_RESTARLESS_UPDATE):
        report_list.append(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnsupportedAgent(
                    stonith_id,
                    stonith_type,
                    SUPPORTED_RESOURCE_TYPES_FOR_RESTARLESS_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
Пример #3
0
    def _validate_ids_can_be_added_or_moved(
        self,
        resources_section: _Element,
    ) -> ReportItemList:
        """
        Validate that ids can be added or moved:
        - there are no duplicate ids specified
        - ids belong to elements allowed to be put in a tag in case of adding
        - ids do not exist in the tag if no adjacent id was specified
        Save a list of created (for ids newly added to the tag) and found (for
        ids already existing in the tag) obj_ref elements keeping the order of
        given ids to add or move them in case of success validation.
        """
        report_list: ReportItemList = []
        unique_add_ids = list(OrderedDict.fromkeys(self._add_idref_list))

        if self._add_idref_list:
            # report duplicate ids
            report_list.extend(
                _validate_add_remove_duplicate_reference_ids(
                    self._add_idref_list, ))
            # report if references not found or belongs to unexpected types
            report_list.extend(
                _validate_reference_ids_are_resources(
                    resources_section,
                    unique_add_ids,
                ))
        if self._tag_element is not None and self._add_idref_list:
            existing_element_id_list = []
            for id_ref in unique_add_ids:
                obj_ref = self._find_obj_ref_in_tag(id_ref)
                if obj_ref is None:
                    obj_ref = etree.Element(TAG_OBJREF, id=id_ref)
                else:
                    existing_element_id_list.append(id_ref)
                self._add_obj_ref_element_list.append(obj_ref)
            # report if a reference id exists in tag and no adjacent reference
            # id was specified
            if self._adjacent_idref is None and existing_element_id_list:
                report_list.append(
                    ReportItem.error(
                        # pylint: disable=line-too-long
                        reports.messages.
                        TagCannotAddReferenceIdsAlreadyInTheTag(
                            self._tag_id,
                            sorted(existing_element_id_list),
                        )))
        return report_list
Пример #4
0
 def validate(self, option_dict):
     return [
         ReportItem.error(
             reports.messages.PrerequisiteOptionIsMissing(
                 option_name,
                 self._prerequisite_name,
                 self._option_type,
                 self._prerequisite_type,
             )
         )
         for option_name in self._option_name_list
         if (
             option_name in option_dict
             and self._prerequisite_name not in option_dict
         )
     ]
Пример #5
0
def booth_ticket_operation_failed(operation, reason, site_ip, ticket_name):
    """
    Pcs uses external booth tools for some ticket_name operations. For example
        grand and revoke. But the external command failed.
    string operatin determine what was intended perform with ticket_name
    string reason is taken from external booth command
    string site_ip specifiy what site had to run the command
    string ticket_name specify with which ticket had to run the command
    """
    return ReportItem.error(report_codes.BOOTH_TICKET_OPERATION_FAILED,
                            info={
                                "operation": operation,
                                "reason": reason,
                                "site_ip": site_ip,
                                "ticket_name": ticket_name,
                            })
Пример #6
0
 def test_file_error(self, mock_config):
     node = "node"
     reason = "reason"
     mock_config.side_effect = LibraryError(
         ReportItem.error(
             reports.messages.UnableToGetSbdConfig(node, reason)))
     assert_raise_library_error(
         lambda: cmd_sbd.get_local_sbd_config(self.mock_env),
         (
             Severities.ERROR,
             reports.codes.UNABLE_TO_GET_SBD_CONFIG,
             {
                 "node": node,
                 "reason": reason,
             },
         ),
     )
Пример #7
0
 def validate(self, option_dict: TypeOptionMap) -> ReportItemList:
     not_valid_options = [
         name for name in option_dict
         if corosync_constants.OPTION_NAME_RE.fullmatch(name) is None
     ]
     if not_valid_options:
         # We must be strict and do not allow to override this validation,
         # otherwise setting a cratfed option name could be misused for
         # setting arbitrary corosync.conf settings.
         return [
             ReportItem.error(
                 reports.messages.InvalidUserdefinedOptions(
                     sorted(not_valid_options),
                     "a-z A-Z 0-9 /_-",
                     self._option_type,
                 ))
         ]
     return []
Пример #8
0
 def test_file_error(self):
     node = "node"
     reason = "reason"
     self.config.fs.open(
         settings.sbd_config,
         side_effect=LibraryError(
             ReportItem.error(
                 reports.messages.UnableToGetSbdConfig(node, reason))),
     )
     assert_raise_library_error(
         lambda: cmd_sbd.get_local_sbd_config(self.env_assist.get_env()),
         (
             Severities.ERROR,
             reports.codes.UNABLE_TO_GET_SBD_CONFIG,
             {
                 "node": node,
                 "reason": reason,
             },
         ),
     )
Пример #9
0
def _validate_add_remove_duplicate_reference_ids(
    idref_list: Iterable[str], add_or_not_remove: bool = True,
) -> ReportItemList:
    """
    Validate that idref_list does not contain duplicates.

    idref_list -- reference ids which we want to tag
    add_or_not_remove -- flag for add/remove action
    """
    duplicate_ids_list = [
        id for id, count in Counter(idref_list).items() if count > 1
    ]
    if duplicate_ids_list:
        return [
            ReportItem.error(
                reports.messages.TagAddRemoveIdsDuplication(
                    sorted(duplicate_ids_list), add_or_not_remove,
                )
            )
        ]
    return []
Пример #10
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
Пример #11
0
 def _validate_value(self, value: ValuePair) -> ReportItemList:
     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 []
Пример #12
0
def update_scsi_devices_without_restart(
    runner: CommandRunner,
    cluster_state: _Element,
    resource_el: _Element,
    id_provider: IdProvider,
    devices_list: Iterable[str],
) -> None:
    """
    Update scsi devices without restart of stonith resource or other resources.

    runner -- command runner instance
    cluster_state -- status of the cluster
    resource_el -- resource element being updated
    id_provider -- elements' ids generator
    device_list -- list of updated scsi devices
    """
    resource_id = resource_el.get("id", "")
    roles_with_nodes = get_resource_state(cluster_state, resource_id)
    if "Started" not in roles_with_nodes:
        raise LibraryError(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnableToPerform(
                    f"resource '{resource_id}' is not running on any node",
                    reason_type=reports.const.
                    STONITH_RESTARTLESS_UPDATE_UNABLE_TO_PERFORM_REASON_NOT_RUNNING,
                )))
    if len(roles_with_nodes["Started"]) != 1:
        # TODO: do we want to be able update cloned fence_scsi? Or just case
        # when it's running on more than 1 node? It is possible but we need to
        # update more lrm_rsc_op elements
        raise LibraryError(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnableToPerform(
                    f"resource '{resource_id}' is running on more than 1 node")
            ))
    node_name = roles_with_nodes["Started"][0]

    new_instance_attrs = {"devices": ",".join(sorted(devices_list))}
    arrange_first_instance_attributes(resource_el, new_instance_attrs,
                                      id_provider)

    lrm_rsc_op_start_list = _get_lrm_rsc_op_elements(get_root(resource_el),
                                                     resource_id, node_name,
                                                     "start")
    if len(lrm_rsc_op_start_list) == 1:
        _update_digest_attrs_in_lrm_rsc_op(
            lrm_rsc_op_start_list[0],
            get_resource_digests(
                runner,
                resource_id,
                node_name,
                new_instance_attrs,
            ),
        )
    else:
        raise LibraryError(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnableToPerform(
                    "lrm_rsc_op element for start operation was not found")))

    monitor_attrs_list = _get_monitor_attrs(resource_el)
    lrm_rsc_op_monitor_list = _get_lrm_rsc_op_elements(get_root(resource_el),
                                                       resource_id, node_name,
                                                       "monitor")
    if len(lrm_rsc_op_monitor_list) != len(monitor_attrs_list):
        raise LibraryError(
            ReportItem.error(
                reports.messages.StonithRestartlessUpdateUnableToPerform(
                    ("number of lrm_rsc_op and op elements for monitor "
                     "operation differs"))))

    for monitor_attrs in monitor_attrs_list:
        lrm_rsc_op_list = _get_lrm_rsc_op_elements(
            get_root(resource_el),
            resource_id,
            node_name,
            "monitor",
            monitor_attrs["interval"],
        )
        if len(lrm_rsc_op_list) == 1:
            _update_digest_attrs_in_lrm_rsc_op(
                lrm_rsc_op_list[0],
                get_resource_digests(
                    runner,
                    resource_id,
                    node_name,
                    new_instance_attrs,
                    crm_meta_attributes=monitor_attrs,
                ),
            )
        else:
            raise LibraryError(
                ReportItem.error(
                    reports.messages.StonithRestartlessUpdateUnableToPerform(
                        ("monitor lrm_rsc_op element for resource "
                         f"'{resource_id}', node '{node_name}' and interval "
                         f"'{monitor_attrs['interval']}' not found"))))
Пример #13
0
def validate_add_remove_items(
    add_item_list: Iterable[str],
    remove_item_list: Iterable[str],
    current_item_list: Iterable[str],
    container_type: reports.types.AddRemoveContainerType,
    item_type: reports.types.AddRemoveItemType,
    container_id: str,
    adjacent_item_id: Optional[str] = None,
    container_can_be_empty: bool = False,
) -> ReportItemList:
    """
    Validate if items can be added or removed to or from a container.

    add_item_list -- items to be added
    remove_item_list -- items to be removed
    current_item_list -- items currently in the container
    container_type -- container type
    item_type -- item type
    container_id -- id of the container
    adjacent_item_id -- an adjacent item in the container
    container_can_be_empty -- flag to decide if container can be left empty
    """
    # pylint: disable=too-many-locals
    report_list: ReportItemList = []
    if not add_item_list and not remove_item_list:
        report_list.append(
            ReportItem.error(
                reports.messages.AddRemoveItemsNotSpecified(
                    container_type, item_type, container_id)))

    def _get_duplicate_items(item_list: Iterable[str]) -> Set[str]:
        return {
            item
            for item, count in Counter(item_list).items() if count > 1
        }

    duplicate_items_list = _get_duplicate_items(
        add_item_list) | _get_duplicate_items(remove_item_list)
    if duplicate_items_list:
        report_list.append(
            ReportItem.error(
                reports.messages.AddRemoveItemsDuplication(
                    container_type,
                    item_type,
                    container_id,
                    sorted(duplicate_items_list),
                )))
    already_present = set(add_item_list).intersection(current_item_list)
    # report only if an adjacent id is not defined, because we want to allow
    # to move items when adjacent_item_id is specified
    if adjacent_item_id is None and already_present:
        report_list.append(
            ReportItem.error(
                reports.messages.AddRemoveCannotAddItemsAlreadyInTheContainer(
                    container_type,
                    item_type,
                    container_id,
                    sorted(already_present),
                )))
    missing_items = set(remove_item_list).difference(current_item_list)
    if missing_items:
        report_list.append(
            ReportItem.error(
                reports.messages.AddRemoveCannotRemoveItemsNotInTheContainer(
                    container_type,
                    item_type,
                    container_id,
                    sorted(missing_items),
                )))
    common_items = set(add_item_list) & set(remove_item_list)
    if common_items:
        report_list.append(
            ReportItem.error(
                reports.messages.AddRemoveCannotAddAndRemoveItemsAtTheSameTime(
                    container_type,
                    item_type,
                    container_id,
                    sorted(common_items),
                )))
    if not container_can_be_empty and not add_item_list:
        remaining_items = set(current_item_list).difference(remove_item_list)
        if not remaining_items:
            report_list.append(
                ReportItem.error(
                    reports.messages.
                    AddRemoveCannotRemoveAllItemsFromTheContainer(
                        container_type,
                        item_type,
                        container_id,
                        list(current_item_list),
                    )))
    if adjacent_item_id:
        if adjacent_item_id not in current_item_list:
            report_list.append(
                ReportItem.error(
                    reports.messages.AddRemoveAdjacentItemNotInTheContainer(
                        container_type,
                        item_type,
                        container_id,
                        adjacent_item_id,
                    )))
        if adjacent_item_id in add_item_list:
            report_list.append(
                ReportItem.error(
                    reports.messages.AddRemoveCannotPutItemNextToItself(
                        container_type,
                        item_type,
                        container_id,
                        adjacent_item_id,
                    )))
        if not add_item_list:
            report_list.append(
                ReportItem.error(
                    reports.messages.
                    AddRemoveCannotSpecifyAdjacentItemWithoutItemsToAdd(
                        container_type,
                        item_type,
                        container_id,
                        adjacent_item_id,
                    )))
    return report_list
Пример #14
0
 def test_file_error(self, mock_config):
     mock_config.side_effect = LibraryError(
         ReportItem.error(report_codes.UNABLE_TO_GET_SBD_CONFIG, ))
     assert_raise_library_error(
         lambda: cmd_sbd.get_local_sbd_config(self.mock_env),
         (Severities.ERROR, report_codes.UNABLE_TO_GET_SBD_CONFIG, {}))