def validate_resource_instance_attributes_create( resource_agent: ResourceAgentFacade, instance_attributes: Mapping[str, str], resources_section: _Element, force: bool = False, ) -> reports.ReportItemList: report_items: reports.ReportItemList = [] report_items += validate.ValidatorAll([ validate.ValueNotEmpty(name, None) for name in instance_attributes ]).validate(instance_attributes) if resource_agent.metadata.agent_exists: report_items += validate.ValidatorAll( resource_agent.get_validators_allowed_parameters(force) + resource_agent.get_validators_required_parameters(force)).validate( instance_attributes) report_items += validate.ValidatorAll( resource_agent.get_validators_deprecated_parameters()).validate({ name: value for name, value in instance_attributes.items() # we create a custom report for stonith parameter "action" if not (resource_agent.metadata.name.is_stonith and name == "action") }) if resource_agent.metadata.name.is_stonith: report_items += _validate_stonith_action(instance_attributes, force) if resource_agent.metadata.agent_exists: report_items += _validate_unique_instance_attributes( resource_agent.metadata, instance_attributes, resources_section, force=force, ) return report_items
def _validate_network_options_update(bundle_el, network_el, options, force_options): report_list = [] inner_primitive = get_inner_resource(bundle_el) if (inner_primitive is not None and not _is_pcmk_remote_acccessible_after_update(network_el, options)): report_list.append( reports.get_problem_creator( report_codes.FORCE_OPTIONS, force_options)(reports.resource_in_bundle_not_accessible, bundle_el.get("id"), inner_primitive.get("id"))) kwargs = validate.set_warning(report_codes.FORCE_OPTIONS, force_options) validators_optional_options = [ # TODO add validators for other keys (ip-range-start - IPv4) validate.ValuePortNumber("control-port"), # Leaving a possibility to force this validation for the case pacemaker # starts supporting IPv6 or other format of the netmask. ValueHostNetmask("host-netmask", **kwargs), ] for val in validators_optional_options: val.empty_string_valid = True validators = [ validate.NamesIn( # allow to remove options even if they are not allowed NETWORK_OPTIONS | _options_to_remove(options), option_type="network", **kwargs) ] + validators_optional_options return (report_list + validate.ValidatorAll(validators).validate(options))
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)
def _validate_port_map_list(options_list, id_provider, force_options): kwargs = validate.set_warning(report_codes.FORCE_OPTIONS, force_options) option_type = "port-map" validators = [ validate.NamesIn(PORT_MAP_OPTIONS, option_type=option_type, **kwargs), validate.ValueId("id", option_name_for_report="port-map id", id_provider=id_provider), validate.DependsOnOption( ["internal-port"], "port", option_type=option_type, prerequisite_type=option_type, ), validate.IsRequiredSome(["port", "range"], option_type=option_type), validate.MutuallyExclusive(["port", "range"], option_type=option_type), validate.ValuePortNumber("port"), validate.ValuePortNumber("internal-port"), validate.ValuePortRange("range", **kwargs), ] validator_all = validate.ValidatorAll(validators) report_list = [] for options in options_list: report_list.extend(validator_all.validate(options)) return report_list
def _validate_storage_map_list(options_list, id_provider, force_options): kwargs = validate.set_warning(report_codes.FORCE_OPTIONS, force_options) option_type = "storage-map" validators = [ validate.NamesIn(STORAGE_MAP_OPTIONS, option_type=option_type, **kwargs), validate.ValueId( "id", option_name_for_report="storage-map id", id_provider=id_provider, ), validate.IsRequiredSome( ["source-dir", "source-dir-root"], option_type=option_type, ), validate.MutuallyExclusive( ["source-dir", "source-dir-root"], option_type=option_type, ), validate.IsRequiredAll(["target-dir"], option_type=option_type), ] validator_all = validate.ValidatorAll(validators) report_list = [] for options in options_list: report_list.extend(validator_all.validate(options)) return report_list
def initialize_block_devices(lib_env, device_list, option_dict): """ Initialize SBD devices in device_list with options_dict. lib_env -- LibraryEnvironment device_list -- list of strings option_dict -- dictionary """ report_item_list = [] if not device_list: report_item_list.append( reports.required_options_are_missing(["device"])) supported_options = sbd.DEVICE_INITIALIZATION_OPTIONS_MAPPING.keys() report_item_list += ( validate.NamesIn(supported_options).validate(option_dict)) report_item_list += validate.ValidatorAll([ validate.ValueNonnegativeInteger(key) for key in supported_options ]).validate(option_dict) if lib_env.report_processor.report_list(report_item_list).has_errors: raise LibraryError() sbd.initialize_block_devices(lib_env.report_processor, lib_env.cmd_runner(), device_list, option_dict)
def validate_operation_list(operation_list, allowed_operation_name_list, allow_invalid=False): kwargs = validate.set_warning(report_codes.FORCE_OPTIONS, allow_invalid) option_type = "resource operation" validators = [ validate.NamesIn(ATTRIBUTES, option_type=option_type), validate.IsRequiredAll(["name"], option_type=option_type), validate.ValueIn( "name", allowed_operation_name_list, option_name_for_report="operation name", **kwargs, ), validate.ValueIn("role", RESOURCE_ROLES), validate.ValueIn("on-fail", ON_FAIL_VALUES), validate.ValueIn("record-pending", BOOLEAN_VALUES), validate.ValueIn("enabled", BOOLEAN_VALUES), validate.MutuallyExclusive(["interval-origin", "start-delay"], option_type=option_type), validate.ValueId("id", option_name_for_report="operation id"), ] validator_all = validate.ValidatorAll(validators) report_list = [] for operation in operation_list: report_list.extend(validator_all.validate(operation)) return report_list
def _validate_sbd_options(sbd_config, allow_unknown_opts=False, allow_invalid_option_values=False): """ Validate user SBD configuration. Options 'SBD_WATCHDOG_DEV' and 'SBD_OPTS' are restricted. Returns list of ReportItem sbd_config -- dictionary in format: <SBD config option>: <value> allow_unknown_opts -- if True, accept also unknown options. """ validators = [ validate.NamesIn( ALLOWED_SBD_OPTION_LIST, banned_name_list=UNSUPPORTED_SBD_OPTION_LIST, **validate.set_warning(report_codes.FORCE_OPTIONS, allow_unknown_opts), ), validate.ValueNonnegativeInteger("SBD_WATCHDOG_TIMEOUT"), validate.ValueIn( "SBD_TIMEOUT_ACTION", TIMEOUT_ACTION_ALLOWED_VALUE_LIST, **validate.set_warning(report_codes.FORCE_OPTIONS, allow_invalid_option_values), ), ] return validate.ValidatorAll(validators).validate(sbd_config)
def _validate_node_attr_expr(self, expr: NodeAttrExpr) -> reports.ReportItemList: if not self._allow_node_attr_expr: self._disallowed_expr_list.add(CibRuleExpressionType.EXPRESSION) return [] validator_list: List[validate.ValidatorInterface] = [] if expr.attr_type == NODE_ATTR_TYPE_INTEGER: validator_list.append( validate.ValueInteger( "attr_value", option_name_for_report="integer attribute")) elif expr.attr_type == NODE_ATTR_TYPE_NUMBER: # rhbz#1869399 # Originally, pacemaker only supported 'number', treated it as an # integer and documented it as 'integer'. With CIB schema 3.5.0+, # 'integer' is supported as well. With crm_feature_set 3.5.0+, # 'number' is treated as a floating point number. # Since pcs never supported 'number' until the above changes in # pacemaker happened and pacemaker was able to handle floating # point numbers before (even though truncating them to integers), # we'll just check for a float here. If that's not good enough, we # can fix it later and validate the value as integer when # crm_feature_set < 3.5.0. validator_list.append( validate.ValueFloat("attr_value", option_name_for_report="number attribute")) elif expr.attr_type == NODE_ATTR_TYPE_VERSION: validator_list.append( validate.ValueVersion( "attr_value", option_name_for_report="version attribute")) return validate.ValidatorAll(validator_list).validate( dataclasses.asdict(expr))
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( reports.deprecated_option("masters", ["promoted-max"], "container", severity=ReportItemSeverity.WARNING)) # 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( reports.prerequisite_option_must_not_be_set( "masters", "promoted-max", "container", "container")) if (options.get("promoted-max") and container_el.get("masters") and options.get("masters") != ""): deprecation_reports.append( reports.prerequisite_option_must_not_be_set( "promoted-max", "masters", "container", "container")) return (validate.ValidatorAll(validators).validate(options) + deprecation_reports)
def _validate_network_options_new(options, force_options): kwargs = validate.set_warning(report_codes.FORCE_OPTIONS, force_options) validators = [ # TODO add validators for other keys (ip-range-start - IPv4) validate.NamesIn(NETWORK_OPTIONS, option_type="network", **kwargs), validate.ValuePortNumber("control-port"), # Leaving a possibility to force this validation for the case pacemaker # starts supporting IPv6 or other format of the netmask. ValueHostNetmask("host-netmask", **kwargs), ] return validate.ValidatorAll(validators).validate(options)
def _validate_ticket_options(options, allow_unknown_options): validator_list = ([ validate.NamesIn(constants.TICKET_KEYS, option_type="booth ticket", banned_name_list=constants.GLOBAL_KEYS, **validate.set_warning(report_codes.FORCE_OPTIONS, allow_unknown_options)), ] + [validate.ValueNotEmpty(option, None) for option in options]) normalized_options = validate.values_to_pairs( options, lambda key, value: value.strip()) return validate.ValidatorAll(validator_list).validate(normalized_options)
def validate(self, force_options: bool = False) -> reports.ReportItemList: report_list: reports.ReportItemList = [] # Nvpair dict is intentionally not validated: it may contain any keys # and values. This can change in the future and then we add a # validation. Until then there is really nothing to validate there. # validate nvset options validators = [ validate.NamesIn( ("id", "score"), severity=reports.item.get_severity(reports.codes.FORCE_OPTIONS, force_options), ), # with id_provider it validates that the id is available as well validate.ValueId("id", option_name_for_report="id", id_provider=self._id_provider), validate.ValueScore("score"), ] report_list.extend( validate.ValidatorAll(validators).validate(self._nvset_options)) # parse and validate rule # TODO write and call parsed rule validation and cleanup and tests if self._nvset_rule: try: # Allow flags are set to True always, the parsed rule tree is # checked in the validator instead. That gives us better error # messages, such as "op expression cannot be used in this # context" instead of a universal "parse error". self._nvset_rule_parsed = parse_rule(self._nvset_rule, allow_rsc_expr=True, allow_op_expr=True) report_list.extend( RuleValidator( self._nvset_rule_parsed, allow_rsc_expr=self._allow_rsc_expr, allow_op_expr=self._allow_op_expr, ).get_reports()) except RuleParseError as e: report_list.append( reports.ReportItem.error( reports.messages.RuleExpressionParseError( e.rule_string, e.msg, e.rule_line, e.lineno, e.colno, e.pos, ))) return report_list
def validate_set_as_guest(tree, existing_nodes_names, existing_nodes_addrs, node_name, options): validator_list = [ validate.NamesIn(GUEST_OPTIONS, option_type="guest"), validate.ValueTimeInterval("remote-connect-timeout"), validate.ValuePortNumber("remote-port"), ] return (validate.ValidatorAll(validator_list).validate(options) + validate.ValueNotEmpty("node name", None).validate( {"node name": node_name.strip()}) + validate_conflicts(tree, existing_nodes_names, existing_nodes_addrs, node_name, options))
def _validate_ticket_options(options, allow_unknown_options): validator_list = [ validate.NamesIn( constants.TICKET_KEYS, option_type="booth ticket", banned_name_list=constants.GLOBAL_KEYS, severity=report.item.get_severity( report_codes.FORCE, allow_unknown_options ), ), ] + [validate.ValueNotEmpty(option, None) for option in options] normalized_options = validate.values_to_pairs( options, lambda key, value: value.strip() ) return validate.ValidatorAll(validator_list).validate(normalized_options)
def validate(self, force_options: bool = False) -> reports.ReportItemList: report_list: reports.ReportItemList = [] # Nvpair dict is intentionally not validated: it may contain any keys # and values. This can change in the future and then we add a # validation. Until then there is really nothing to validate there. # validate nvset options validators = [ validate.NamesIn( ("id", "score"), severity=reports.item.get_severity(reports.codes.FORCE, force_options), ), # with id_provider it validates that the id is available as well validate.ValueId("id", option_name_for_report="id", id_provider=self._id_provider), validate.ValueScore("score"), ] report_list.extend( validate.ValidatorAll(validators).validate(self._nvset_options)) # parse and validate rule if self._nvset_rule: try: self._nvset_rule_parsed = parse_rule(self._nvset_rule) report_list.extend( RuleValidator( self._nvset_rule_parsed, allow_rsc_expr=self._allow_rsc_expr, allow_op_expr=self._allow_op_expr, allow_node_attr_expr=self._allow_node_attr_expr, ).get_reports()) except RuleParseError as e: report_list.append( reports.ReportItem.error( reports.messages.RuleExpressionParseError( e.rule_string, e.msg, e.rule_line, e.lineno, e.colno, e.pos, ))) return report_list
def _validate_ticket_options(options, allow_unknown_options): severity = reports.item.get_severity(reports.codes.FORCE, allow_unknown_options) validator_list = ([ validate.NamesIn( constants.TICKET_KEYS, option_type="booth ticket", banned_name_list=constants.GLOBAL_KEYS, severity=severity, ), ] + [ validate.ValueNotEmpty(option, None) for option in options if option != "mode" ] + [validate.ValueIn("mode", ["auto", "manual"], severity=severity)]) normalized_options = validate.values_to_pairs( options, lambda key, value: value.strip()) return validate.ValidatorAll(validator_list).validate(normalized_options)
def test_collect_all_errors_from_specifications(self): assert_report_item_list_equal( validate.ValidatorAll([ validate.NamesIn(["x", "y"]), validate.MutuallyExclusive(["x", "y"]), validate.ValuePositiveInteger("x"), validate.ValueIn("y", ["a", "b"]), ]).validate({ "x": "abcd", "y": "defg", "z": "hijk", }), [ fixture.error( reports.codes.INVALID_OPTIONS, option_names=["z"], option_type=None, allowed=["x", "y"], allowed_patterns=[], ), fixture.error( reports.codes.MUTUALLY_EXCLUSIVE_OPTIONS, option_names=["x", "y"], option_type=None, ), fixture.error( reports.codes.INVALID_OPTION_VALUE, option_value="abcd", option_name="x", allowed_values="a positive integer", cannot_be_empty=False, forbidden_characters=None, ), fixture.error( reports.codes.INVALID_OPTION_VALUE, option_value="defg", option_name="y", allowed_values=["a", "b"], cannot_be_empty=False, forbidden_characters=None, ), ], )
def validate_stonith_watchdog_timeout( stonith_watchdog_timeout: str, force: bool = False ) -> reports.ReportItemList: """ Check sbd status and config when user is setting stonith-watchdog-timeout Returns error message if the value is unacceptable, otherwise return nothing to set the property stonith_watchdog_timeout -- value to be validated """ severity = reports.get_severity(reports.codes.FORCE, force) if _is_device_set_local(): return ( [ reports.ReportItem( severity, reports.messages.StonithWatchdogTimeoutCannotBeSet( reports.const.SBD_SET_UP_WITH_DEVICES ), ) ] if stonith_watchdog_timeout not in ["", "0"] else [] ) if stonith_watchdog_timeout in ["", "0"]: return [ reports.ReportItem( severity, reports.messages.StonithWatchdogTimeoutCannotBeUnset( reports.const.SBD_SET_UP_WITHOUT_DEVICES ), ) ] return validate.ValidatorAll( [ _StonithWatchdogTimeoutValidator( "stonith-watchdog-timeout", _get_local_sbd_watchdog_timeout(), severity=severity, ) ] ).validate({"stonith-watchdog-timeout": stonith_watchdog_timeout})
def _validate_options(options) -> reports.ReportItemList: # Pacemaker does not care currently about meaningfulness for concrete # constraint, so we use all attribs. validators = [ validate.NamesIn(_ATTRIBUTES, option_type="set"), validate.ValueIn("action", const.PCMK_ACTIONS), validate.ValueIn("require-all", _BOOLEAN_VALUES), validate.ValueIn("role", const.PCMK_ROLES), validate.ValueIn("sequential", _BOOLEAN_VALUES), validate.ValueDeprecated( "role", { const.PCMK_ROLE_PROMOTED_LEGACY: const.PCMK_ROLE_PROMOTED, const.PCMK_ROLE_UNPROMOTED_LEGACY: const.PCMK_ROLE_UNPROMOTED, }, reports.ReportItemSeverity.deprecation(), ), ] return validate.ValidatorAll(validators).validate(options)
def _validate_network_options_update( bundle_el, network_el, options, force_options ): report_list = [] inner_primitive = get_inner_resource(bundle_el) if ( inner_primitive is not None and not _is_pcmk_remote_accessible_after_update(network_el, options) ): report_list.append( ReportItem( severity=reports.item.get_severity( reports.codes.FORCE, force_options, ), message=reports.messages.ResourceInBundleNotAccessible( bundle_el.get("id"), inner_primitive.get("id"), ), ) ) severity = reports.item.get_severity(reports.codes.FORCE, force_options) validators_optional_options = [ # TODO add validators for other keys (ip-range-start - IPv4) validate.ValuePortNumber("control-port"), # Leaving a possibility to force this validation for the case pacemaker # starts supporting IPv6 or other format of the netmask. ValueHostNetmask("host-netmask", severity=severity), ] for val in validators_optional_options: val.empty_string_valid = True validators = [ validate.NamesIn( # allow to remove options even if they are not allowed NETWORK_OPTIONS | _options_to_remove(options), option_type="network", severity=severity, ) ] + validators_optional_options return report_list + validate.ValidatorAll(validators).validate(options)
def _validate_container(container_type, container_options, force_options=False): if not container_type in GENERIC_CONTAINER_TYPES: return [ reports.invalid_option_value( "container type", container_type, GENERIC_CONTAINER_TYPES, ) ] 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( reports.deprecated_option( "masters", ["promoted-max"], "container", severity=ReportItemSeverity.WARNING ) ) return ( validate.ValidatorAll(validators).validate(container_options) + deprecation_reports )
def _validate_operation_list( operation_list, allowed_operation_name_list, allow_invalid=False ): severity = reports.item.get_severity(reports.codes.FORCE, allow_invalid) option_type = "resource operation" validators = [ validate.NamesIn(ATTRIBUTES, option_type=option_type), validate.IsRequiredAll(["name"], option_type=option_type), validate.ValueIn( "name", allowed_operation_name_list, option_name_for_report="operation name", severity=severity, ), validate.ValueIn("role", const.PCMK_ROLES), validate.ValueDeprecated( "role", { const.PCMK_ROLE_PROMOTED_LEGACY: const.PCMK_ROLE_PROMOTED, const.PCMK_ROLE_UNPROMOTED_LEGACY: const.PCMK_ROLE_UNPROMOTED, }, reports.ReportItemSeverity.deprecation(), ), validate.ValueIn("on-fail", ON_FAIL_VALUES), validate.ValueIn("record-pending", _BOOLEAN_VALUES), validate.ValueIn("enabled", _BOOLEAN_VALUES), validate.MutuallyExclusive( ["interval-origin", "start-delay"], option_type=option_type ), validate.ValueId("id", option_name_for_report="operation id"), ] validator_all = validate.ValidatorAll(validators) report_list = [] for operation in operation_list: report_list.extend(validator_all.validate(operation)) return report_list
def _validate_datespec_expr(expr: DatespecExpr) -> reports.ReportItemList: # TODO This is taken from the CIB schema. There is an ongoing # discussion that the schema doesn't match Pacemaker Explained. Based # on the result of the discussion, this might need to be updated. part_limits = { "hours": (0, 23), "monthdays": (1, 31), "weekdays": (1, 7), "yearsdays": (1, 366), "months": (1, 12), "weeks": (1, 53), "years": (None, None), "weekyears": (None, None), "moon": (0, 7), } duplicate_keys = { key for key, count in Counter([pair[0] for pair in expr.date_parts]).items() if count > 1 } validator_list: List[validate.ValidatorInterface] = [ validate.ValuePcmkDatespecPart(name, limits[0], limits[1]) for name, limits in sorted(part_limits.items()) ] validator_list.append( validate.NamesIn(part_limits.keys(), option_type="datespec")) report_list = validate.ValidatorAll(validator_list).validate( dict(expr.date_parts)) if duplicate_keys: report_list.append( reports.item.ReportItem.error( message=reports.messages.RuleExpressionOptionsDuplication( sorted(duplicate_keys)), )) return report_list
def prepare_options_plain( cib: _Element, report_processor: reports.ReportProcessor, options, ticket: str, resource_id, ): options = options.copy() report_processor.report_list(_validate_options_common(options)) if not ticket: report_processor.report( ReportItem.error( reports.messages.RequiredOptionsAreMissing(["ticket"]))) else: report_processor.report_list(validate_ticket_name(ticket)) options["ticket"] = ticket if not resource_id: report_processor.report( ReportItem.error( reports.messages.RequiredOptionsAreMissing(["rsc"]))) options["rsc"] = resource_id role_value_validator = validate.ValueIn("rsc-role", const.PCMK_ROLES, option_name_for_report="role") role_value_validator.empty_string_valid = True validators = [ role_value_validator, validate.ValueDeprecated( "rsc-role", { const.PCMK_ROLE_PROMOTED_LEGACY: const.PCMK_ROLE_PROMOTED, const.PCMK_ROLE_UNPROMOTED_LEGACY: const.PCMK_ROLE_UNPROMOTED, }, reports.ReportItemSeverity.deprecation(), option_name_for_report="role", ), ] report_processor.report_list( validate.ValidatorAll(validators).validate( validate.values_to_pairs( options, validate.option_value_normalization( {"rsc-role": lambda value: value.capitalize()}), ))) if report_processor.has_errors: raise LibraryError() if "rsc-role" in options: if options["rsc-role"]: options["rsc-role"] = pacemaker.role.get_value_for_cib( options["rsc-role"].capitalize(), tools.are_new_role_names_supported(cib), ) else: del options["rsc-role"] return constraint.prepare_options( tuple(list(ATTRIB) + list(ATTRIB_PLAIN)), options, partial( _create_id, cib, options["ticket"], resource_id, options.get("rsc-role", ""), ), partial(tools.check_new_id_applicable, cib, DESCRIPTION), )
def _validate_date_inrange_expr( expr: DateInRangeExpr, ) -> reports.ReportItemList: # TODO This is taken from the CIB schema. There is an ongoing # discussion that the schema doesn't match Pacemaker Explained. Based # on the result of the discussion, this might need to be updated. duration_parts = { "hours", "monthdays", "weekdays", "yearsdays", "months", "weeks", "years", "weekyears", "moon", } start_date, end_date = None, None report_list = [] if expr.date_start is not None: try: start_date = dateutil_parser.isoparse(expr.date_start) except ValueError: report_list.append( reports.item.ReportItem.error( message=reports.messages.InvalidOptionValue( "date", expr.date_start, "ISO 8601 date"), )) if expr.date_end is not None: try: end_date = dateutil_parser.isoparse(expr.date_end) except ValueError: report_list.append( reports.item.ReportItem.error( message=reports.messages.InvalidOptionValue( "date", expr.date_end, "ISO 8601 date"), )) if (start_date is not None and end_date is not None and start_date >= end_date): report_list.append( reports.item.ReportItem.error( message=reports.messages. RuleExpressionSinceGreaterThanUntil( expr.date_start, # If end_date is not None, then expr.date_end is not # None, but mypy does not see it. cast(str, expr.date_end), ), )) if expr.duration_parts: duplicate_keys = { key for key, count in Counter( [pair[0] for pair in expr.duration_parts]).items() if count > 1 } validator_list: List[validate.ValidatorInterface] = [ validate.ValuePositiveInteger(name) for name in sorted(duration_parts) ] validator_list.append( validate.NamesIn(duration_parts, option_type="duration")) report_list += validate.ValidatorAll(validator_list).validate( dict(expr.duration_parts)) if duplicate_keys: report_list.append( reports.item.ReportItem.error( message=reports. messages.RuleExpressionOptionsDuplication( sorted(duplicate_keys)), )) return report_list
def validate_resource_instance_attributes_update( resource_agent: ResourceAgentFacade, instance_attributes: Mapping[str, str], resource_id: str, resources_section: _Element, force: bool = False, ) -> reports.ReportItemList: # TODO This function currently accepts the updated resource as a string and # finds the corresponding xml element by itself. This is needed as the # function is called from old pcs code which uses dom while pcs.lib uses # lxml. Once resource update command is moved to pcs.lib, this function # will be fixed to accept the updated resource as an element instead of a # string. report_items: reports.ReportItemList = [] current_instance_attrs = get_nvset_as_dict( INSTANCE_ATTRIBUTES_TAG, find_element_by_tag_and_id(TAG, resources_section, resource_id), ) if resource_agent.metadata.agent_exists: report_items += validate.ValidatorAll( resource_agent.get_validators_allowed_parameters(force) ).validate( # Do not report unknown parameters already set in the CIB. It would # be confusing to report an error in an option not actually created # now. { name: value for name, value in instance_attributes.items() if name not in current_instance_attrs } ) report_items += validate.ValidatorAll( resource_agent.get_validators_deprecated_parameters() ).validate( { name: value for name, value in instance_attributes.items() # Allow removing deprecated parameters if value != "" # we create a custom report for stonith parameter "action" and not ( resource_agent.metadata.name.is_stonith and name == "action" ) } ) # Check that required parameters have not been removed. This is # complicated by two facts: # * parameters may by deprecated by other parameters, setting one # required parameter from such group is enough # * we only want to report errors related to attributes to be updated final_attrs = dict(current_instance_attrs) for name, value in instance_attributes.items(): if value == "": final_attrs.pop(name, None) else: final_attrs[name] = value report_items += validate.ValidatorAll( # Limit validation only to parameters entered now in an update # command. We don't want to report missing parameters not mentioned # in a command now, that would be confusing to users. resource_agent.get_validators_required_parameters( force, only_parameters=instance_attributes.keys() ) ).validate(final_attrs) if resource_agent.metadata.name.is_stonith: report_items += _validate_stonith_action(instance_attributes, force) if resource_agent.metadata.agent_exists: report_items += _validate_unique_instance_attributes( resource_agent.metadata, instance_attributes, resources_section, resource_id=resource_id, force=force, ) return report_items