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_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 _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_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_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_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_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
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_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, **validate.set_warning(report_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: forcible, severity = self._validate_report_forcible_severity(force) report_items.append( reports.required_options_are_missing( sorted(missing_parameters), self._agent_type_label, severity=severity, forceable=forcible, )) return report_items
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"), **validate.set_warning(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_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 )