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.is_required("image", "container"), validate.value_not_empty("image", "image name"), validate.value_nonnegative_integer("masters"), validate.value_nonnegative_integer("promoted-max"), validate.mutually_exclusive(["masters", "promoted-max"], "container"), validate.value_positive_integer("replicas"), validate.value_positive_integer("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.run_collection_of_option_validators( container_options, validators) + deprecation_reports + validate.names_in(GENERIC_CONTAINER_OPTIONS, container_options.keys(), "container", report_codes.FORCE_OPTIONS, force_options))
def _validate_generic_container_options_update(docker_el, options, force_options): validators = [ # image is a mandatory attribute and cannot be removed validate.value_not_empty("image", "image name"), validate.value_empty_or_valid( "masters", validate.value_nonnegative_integer("masters")), validate.value_empty_or_valid( "promoted-max", validate.value_nonnegative_integer("promoted-max")), validate.value_empty_or_valid( "replicas", validate.value_positive_integer("replicas")), validate.value_empty_or_valid( "replicas-per-host", validate.value_positive_integer("replicas-per-host")), ] # 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.mutually_exclusive(["masters", "promoted-max"], "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 docker_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 docker_el.get("masters") and options.get("masters") != ""): deprecation_reports.append( reports.prerequisite_option_must_not_be_set( "promoted-max", "masters", "container", "container")) return (validate.run_collection_of_option_validators(options, validators) + deprecation_reports + validate.names_in( # allow to remove options even if they are not allowed _generic_container_options | _options_to_remove(options), options.keys(), "container", report_codes.FORCE_OPTIONS, force_options))
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_option_is_missing(["device"])) supported_options = sbd.DEVICE_INITIALIZATION_OPTIONS_MAPPING.keys() report_item_list += names_in(supported_options, option_dict.keys()) validator_list = [ value_nonnegative_integer(key) for key in supported_options ] report_item_list += run_collection_of_option_validators( option_dict, validator_list ) lib_env.report_processor.process_list(report_item_list) sbd.initialize_block_devices( lib_env.report_processor, lib_env.cmd_runner(), device_list, option_dict )
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.value_nonnegative_integer("SBD_WATCHDOG_TIMEOUT"), validate.value_in( "SBD_TIMEOUT_ACTION", TIMEOUT_ACTION_ALLOWED_VALUE_LIST, code_to_allow_extra_values=report_codes.FORCE_OPTIONS, extra_values_allowed=allow_invalid_option_values, ), ] return (validate.names_in( ALLOWED_SBD_OPTION_LIST, sbd_config.keys(), option_type=None, banned_name_list=UNSUPPORTED_SBD_OPTION_LIST, code_to_allow_extra_names=report_codes.FORCE_OPTIONS, extra_names_allowed=allow_unknown_opts, ) + validate.run_collection_of_option_validators(sbd_config, validators))
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.value_nonnegative_integer("SBD_WATCHDOG_TIMEOUT"), validate.value_in( "SBD_TIMEOUT_ACTION", TIMEOUT_ACTION_ALLOWED_VALUE_LIST, code_to_allow_extra_values=report_codes.FORCE_OPTIONS, extra_values_allowed=allow_invalid_option_values, ), ] return ( validate.names_in( ALLOWED_SBD_OPTION_LIST, sbd_config.keys(), option_type=None, banned_name_list=UNSUPPORTED_SBD_OPTION_LIST, code_to_allow_extra_names=report_codes.FORCE_OPTIONS, extra_names_allowed=allow_unknown_opts, ) + validate.run_collection_of_option_validators(sbd_config, validators) )
def _validate_container_docker_options_update( docker_el, options, force_options ): validators = [ # image is a mandatory attribute and cannot be removed validate.value_not_empty("image", "image name"), validate.value_empty_or_valid( "masters", validate.value_nonnegative_integer("masters") ), validate.value_empty_or_valid( "replicas", validate.value_positive_integer("replicas") ), validate.value_empty_or_valid( "replicas-per-host", validate.value_positive_integer("replicas-per-host") ), ] return ( validate.run_collection_of_option_validators(options, validators) + validate.names_in( # allow to remove options even if they are not allowed _docker_options | _options_to_remove(options), options.keys(), "container", report_codes.FORCE_OPTIONS, force_options ) )
def test_report_invalid_value(self): assert_report_item_list_equal( validate.value_nonnegative_integer("key")({ "key": "-10" }), [ (severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "key", "option_value": "-10", "allowed_values": "a non-negative integer", }, None), ])
def _validate_container_docker_options_new(options, force_options): validators = [ validate.is_required("image", "container"), validate.value_not_empty("image", "image name"), validate.value_nonnegative_integer("masters"), validate.value_positive_integer("replicas"), validate.value_positive_integer("replicas-per-host"), ] return (validate.run_collection_of_option_validators(options, validators) + validate.names_in(_docker_options, options.keys(), "container", report_codes.FORCE_OPTIONS, force_options))
def _validate_generic_container_options_new(options, force_options): validators = [ validate.is_required("image", "container"), validate.value_not_empty("image", "image name"), validate.value_nonnegative_integer("masters"), validate.value_nonnegative_integer("promoted-max"), validate.mutually_exclusive(["masters", "promoted-max"], "container"), validate.value_positive_integer("replicas"), validate.value_positive_integer("replicas-per-host"), ] deprecation_reports = [] if "masters" in options: deprecation_reports.append( reports.deprecated_option("masters", ["promoted-max"], "container", severity=ReportItemSeverity.WARNING)) return (validate.run_collection_of_option_validators(options, validators) + deprecation_reports + validate.names_in( _generic_container_options, options.keys(), "container", report_codes.FORCE_OPTIONS, force_options))
def test_report_invalid_value(self): assert_report_item_list_equal( validate.value_nonnegative_integer("key")({"key": "-10"}), [ ( severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "key", "option_value": "-10", "allowed_values": "a non-negative integer", }, None ), ] )
def create_link_list_knet(link_list, max_link_number): """ Validate creating knet link (interface) list options iterable link_list -- list of link options integer max_link_number -- highest allowed linknumber """ if not link_list: # It is not mandatory to set link options. If an empty link list is # provided, everything is fine and we have nothing to validate. It is # also possible to set link options for only some of the links. return [] max_link_number = max(0, min(constants.LINKS_KNET_MAX - 1, max_link_number)) max_link_count = max_link_number + 1 allowed_options = [ "ip_version", # It tells knet which IP to prefer. "linknumber", "link_priority", "mcastport", "ping_interval", "ping_precision", "ping_timeout", "pong_count", "transport", ] validators = [ validate.value_in("ip_version", ("ipv4", "ipv6")), validate.value_integer_in_range("linknumber", 0, max_link_number), validate.value_integer_in_range("link_priority", 0, 255), validate.value_port_number("mcastport"), validate.value_nonnegative_integer("ping_interval"), validate.value_nonnegative_integer("ping_precision"), validate.value_nonnegative_integer("ping_timeout"), validate.depends_on_option( "ping_interval", "ping_timeout", option_type="link", prerequisite_type="link" ), validate.depends_on_option( "ping_timeout", "ping_interval", option_type="link", prerequisite_type="link" ), validate.value_nonnegative_integer("pong_count"), validate.value_in("transport", ("sctp", "udp")), ] report_items = [] used_link_number = defaultdict(int) for options in link_list: if "linknumber" in options: used_link_number[options["linknumber"]] += 1 report_items += ( validate.run_collection_of_option_validators(options, validators) + validate.names_in(allowed_options, options.keys(), "link") ) non_unique_linknumbers = [ number for number, count in used_link_number.items() if count > 1 ] if non_unique_linknumbers: report_items.append( reports.corosync_link_number_duplication(non_unique_linknumbers) ) link_count = len(link_list) if link_count > max_link_count: report_items.append( reports.corosync_too_many_links(link_count, max_link_count, "knet") ) return report_items
def test_empty_report_on_valid_option(self): assert_report_item_list_equal( validate.value_nonnegative_integer("key")({ "key": "10" }), [])
def create_totem(options): """ Validate creating the "totem" section dict options -- totem options """ # No need to support force: # * values are either bool or numbers with no range set - nothing to force # * names are strictly set as we cannot risk the user overwrites some # setting they should not to # * changes to names and values in corosync are very rare allowed_options = [ "consensus", "downcheck", "fail_recv_const", "heartbeat_failures_allowed", "hold", "join", "max_messages", "max_network_delay", "merge", "miss_count_const", "send_join", "seqno_unchanged_const", "token", "token_coefficient", "token_retransmit", "token_retransmits_before_loss_const", "window_size", ] validators = [ validate.value_nonnegative_integer("consensus"), validate.value_nonnegative_integer("downcheck"), validate.value_nonnegative_integer("fail_recv_const"), validate.value_nonnegative_integer("heartbeat_failures_allowed"), validate.value_nonnegative_integer("hold"), validate.value_nonnegative_integer("join"), validate.value_nonnegative_integer("max_messages"), validate.value_nonnegative_integer("max_network_delay"), validate.value_nonnegative_integer("merge"), validate.value_nonnegative_integer("miss_count_const"), validate.value_nonnegative_integer("send_join"), validate.value_nonnegative_integer("seqno_unchanged_const"), validate.value_nonnegative_integer("token"), validate.value_nonnegative_integer("token_coefficient"), validate.value_nonnegative_integer("token_retransmit"), validate.value_nonnegative_integer( "token_retransmits_before_loss_const" ), validate.value_nonnegative_integer("window_size"), ] report_items = ( validate.run_collection_of_option_validators(options, validators) + validate.names_in(allowed_options, options.keys(), "totem") ) return report_items
def create_transport_knet(generic_options, compression_options, crypto_options): """ Validate creating knet transport options dict generic_options -- generic transport options dict compression_options -- compression options dict crypto_options -- crypto options """ # No need to support force: # * values are either an enum or numbers with no range set - nothing to force # * names are strictly set as we cannot risk the user overwrites some # setting they should not to # * changes to names and values in corosync are very rare generic_allowed = [ "ip_version", # It tells knet which IP to prefer. "knet_pmtud_interval", "link_mode", ] generic_validators = [ validate.value_in("ip_version", ("ipv4", "ipv6")), validate.value_nonnegative_integer("knet_pmtud_interval"), validate.value_in("link_mode", ("active", "passive", "rr")), ] compression_allowed = [ "level", "model", "threshold", ] compression_validators = [ validate.value_nonnegative_integer("level"), validate.value_not_empty( "model", "a compression model e.g. zlib, lz4 or bzip2" ), validate.value_nonnegative_integer("threshold"), ] crypto_type = "crypto" crypto_allowed = [ "cipher", "hash", "model", ] crypto_validators = [ validate.value_in( "cipher", ("none", "aes256", "aes192", "aes128", "3des") ), validate.value_in( "hash", ("none", "md5", "sha1", "sha256", "sha384", "sha512") ), validate.value_in("model", ("nss", "openssl")), ] report_items = ( validate.run_collection_of_option_validators( generic_options, generic_validators ) + validate.names_in( generic_allowed, generic_options.keys(), "knet transport" ) + validate.run_collection_of_option_validators( compression_options, compression_validators ) + validate.names_in( compression_allowed, compression_options.keys(), "compression" ) + validate.run_collection_of_option_validators( crypto_options, crypto_validators ) + validate.names_in( crypto_allowed, crypto_options.keys(), crypto_type ) ) if ( # default values taken from `man corosync.conf` crypto_options.get("cipher", "aes256") != "none" and crypto_options.get("hash", "sha1") == "none" ): report_items.append( reports.prerequisite_option_must_be_enabled_as_well( "cipher", "hash", option_type="crypto", prerequisite_type="crypto" ) ) return report_items
def create_link_list_knet(link_list, max_allowed_link_count): """ Validate creating knet link (interface) list options iterable link_list -- list of link options integer max_allowed_link_count -- how many links is defined by addresses """ if not link_list: # It is not mandatory to set link options. If an empty link list is # provided, everything is fine and we have nothing to validate. It is # also possible to set link options for only some of the links. return [] allowed_options = [ "linknumber", "link_priority", "mcastport", "ping_interval", "ping_precision", "ping_timeout", "pong_count", "transport", ] validators = [ validate.value_integer_in_range("link_priority", 0, 255), validate.value_port_number("mcastport"), validate.value_nonnegative_integer("ping_interval"), validate.value_nonnegative_integer("ping_precision"), validate.value_nonnegative_integer("ping_timeout"), validate.depends_on_option("ping_interval", "ping_timeout", option_type="link", prerequisite_type="link"), validate.depends_on_option("ping_timeout", "ping_interval", option_type="link", prerequisite_type="link"), validate.value_nonnegative_integer("pong_count"), validate.value_in("transport", ("sctp", "udp")), ] report_items = [] used_link_number = defaultdict(int) for options in link_list: if "linknumber" in options: used_link_number[options["linknumber"]] += 1 if validate.is_integer(options["linknumber"], 0, constants.LINKS_KNET_MAX - 1): if int(options["linknumber"]) >= max_allowed_link_count: # first link is link0, hence >= report_items.append( reports.corosync_link_does_not_exist_cannot_update( options["linknumber"], link_count=max_allowed_link_count)) else: report_items.append( reports.invalid_option_value( "linknumber", options["linknumber"], f"0..{constants.LINKS_KNET_MAX - 1}")) report_items += ( validate.run_collection_of_option_validators(options, validators) + validate.names_in(allowed_options, options.keys(), "link")) non_unique_linknumbers = [ number for number, count in used_link_number.items() if count > 1 ] if non_unique_linknumbers: report_items.append( reports.corosync_link_number_duplication(non_unique_linknumbers)) report_items.extend( _check_link_options_count(len(link_list), max_allowed_link_count)) return report_items
def test_empty_report_on_valid_option(self): assert_report_item_list_equal( validate.value_nonnegative_integer("key")({"key": "10"}), [] )