def _get_quorum_options_validators(allow_empty_values=False): allowed_bool = ("0", "1") validators = { "auto_tie_breaker": validate.value_in( "auto_tie_breaker", allowed_bool ), "last_man_standing": validate.value_in( "last_man_standing", allowed_bool ), "last_man_standing_window": validate.value_positive_integer( "last_man_standing_window" ), "wait_for_all": validate.value_in( "wait_for_all", allowed_bool ), } if not allow_empty_values: # make sure to return a list even in python3 so we can call append # on it return list(validators.values()) return [ validate.value_empty_or_valid(option_name, validator) for option_name, validator in validators.items() ]
def _get_qdevice_model_net_options_validators(node_ids, allow_empty_values=False, force_options=False): allow_extra_values = validate.allow_extra_values( report_codes.FORCE_OPTIONS, force_options) validators = { "connect_timeout": validate.value_integer_in_range("connect_timeout", 1000, 2 * 60 * 1000, **allow_extra_values), "force_ip_version": validate.value_in("force_ip_version", ("0", "4", "6"), **allow_extra_values), "port": validate.value_port_number("port", **allow_extra_values), "tie_breaker": validate.value_in("tie_breaker", ["lowest", "highest"] + node_ids, **allow_extra_values), } if not allow_empty_values: return ([ validate.value_not_empty("host", "a qdevice host address"), _validate_qdevice_net_algorithm(**allow_extra_values) ] + # explicitely convert to a list for python 3 list(validators.values())) return ([ validate.value_not_empty("host", "a qdevice host address"), _validate_qdevice_net_algorithm(**allow_extra_values) ] + [ validate.value_empty_or_valid(option_name, validator) for option_name, validator in validators.items() ])
def create_transport_udp(generic_options, compression_options, crypto_options): """ Validate creating udp/udpu 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 allowed_options = [ "ip_version", "netmtu", ] validators = [ validate.value_in("ip_version", constants.IP_VERSION_VALUES), validate.value_positive_integer("netmtu"), ] report_items = (validate.run_collection_of_option_validators( generic_options, validators) + validate.names_in( allowed_options, generic_options.keys(), "udp/udpu transport")) if compression_options: report_items.append( reports.corosync_transport_unsupported_options( "compression", "udp/udpu", ("knet", ))) if crypto_options: report_items.append( reports.corosync_transport_unsupported_options( "crypto", "udp/udpu", ("knet", ))) return report_items
def __get_heuristics_options_validators( self, allow_empty_values=False, force_options=False ): validators = { "mode": validate.value_in( "mode", ("off", "on", "sync"), code_to_allow_extra_values=report_codes.FORCE_OPTIONS, allow_extra_values=force_options ), "interval": validate.value_positive_integer( "interval", code_to_allow_extra_values=report_codes.FORCE_OPTIONS, allow_extra_values=force_options ), "sync_timeout": validate.value_positive_integer( "sync_timeout", code_to_allow_extra_values=report_codes.FORCE_OPTIONS, allow_extra_values=force_options ), "timeout": validate.value_positive_integer( "timeout", code_to_allow_extra_values=report_codes.FORCE_OPTIONS, allow_extra_values=force_options ), } if not allow_empty_values: # make sure to return a list even in python3 so we can call append # on it return list(validators.values()) return [ validate.value_empty_or_valid(option_name, validator) for option_name, validator in validators.items() ]
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 test_returns_empty_report_on_valid_normalized_option(self): self.assertEqual( [], validate.value_in("a", ["b"])( {"a": validate.ValuePair(original="C", normalized="b")} ), )
def test_returns_empty_report_on_valid_normalized_option(self): self.assertEqual( [], validate.value_in("a", ["b"])({ "a": validate.ValuePair(original="C", normalized="b") }), )
def test_returns_report_about_invalid_option(self): assert_report_item_list_equal( validate.value_in("a", ["b"])({ "a": "c" }), [ (severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "c", "allowed_values": ["b"], }, None), ])
def test_supports_forceable_errors(self): assert_report_item_list_equal( validate.value_in("a", ["b"], code_to_allow_extra_values="FORCE")({ "a": "c" }), [ (severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "c", "allowed_values": ["b"], }, "FORCE"), ])
def test_support_OptionValuePair(self): assert_report_item_list_equal( validate.value_in("a", ["b"])({ "a": validate.ValuePair(original="C", normalized="c") }), [ (severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "C", "allowed_values": ["b"], }, None), ])
def test_supports_another_report_option_name(self): assert_report_item_list_equal( validate.value_in("a", ["b"], option_name_for_report="option a")({ "a": "c" }), [ (severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "option a", "option_value": "c", "allowed_values": ["b"], }, None), ])
def validate_operation_list(operation_list, allowed_operation_name_list, allow_invalid=False): options_validators = [ validate.is_required("name", "resource operation"), validate.value_in("role", ROLE_VALUES), validate.value_in("requires", REQUIRES_VALUES), validate.value_in("on-fail", ON_FAIL_VALUES), validate.value_in("record-pending", BOOLEAN_VALUES), validate.value_in("enabled", BOOLEAN_VALUES), validate.mutually_exclusive(["interval-origin", "start-delay"], "resource operation"), validate.value_in( "name", allowed_operation_name_list, option_name_for_report="operation name", code_to_allow_extra_values=report_codes.FORCE_OPTIONS, allow_extra_values=allow_invalid, ), validate.value_id("id", option_name_for_report="operation id"), ] report_list = [] for operation in operation_list: report_list.extend(validate_operation(operation, options_validators)) return report_list
def create_link_list_udp(link_list): """ Validate creating udp/udpu link (interface) list options iterable link_list -- list of link options """ 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. return [] allowed_options = [ "bindnetaddr", "broadcast", "mcastaddr", "mcastport", "ttl", ] validators = [ validate.value_ip_address("bindnetaddr"), validate.value_in("broadcast", ("0", "1")), validate.value_ip_address("mcastaddr"), validate.value_port_number("mcastport"), validate.value_integer_in_range("ttl", 0, 255), ] options = link_list[0] report_items = ( validate.run_collection_of_option_validators(options, validators) + validate.names_in(allowed_options, options.keys(), "link") ) # default values taken from `man corosync.conf` if options.get("broadcast", "0") == "1" and "mcastaddr" in options: report_items.append( reports.prerequisite_option_must_be_disabled( "mcastaddr", "broadcast", option_type="link", prerequisite_type="link" ) ) link_count = len(link_list) if link_count > constants.LINKS_UDP_MAX: report_items.append( reports.corosync_too_many_links( link_count, constants.LINKS_UDP_MAX, "udp/udpu" ) ) return report_items
def test_supports_warning(self): assert_report_item_list_equal( validate.value_in("a", ["b"], code_to_allow_extra_values="FORCE", allow_extra_values=True)({ "a": "c" }), [ (severities.WARNING, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "c", "allowed_values": ["b"], }, None), ])
def prepare( report_processor, raw_operation_list, default_operation_list, allowed_operation_name_list, allow_invalid=False ): """ Return operation_list prepared from raw_operation_list and default_operation_list. report_processor is tool for warning/info/error reporting list of dicts raw_operation_list are entered operations that require follow-up care list of dicts default_operation_list are operations defined as default by (most probably) resource agent bool allow_invalid is flag for validation skipping """ operations_to_validate = [ validate.values_to_pairs(op, normalize) for op in raw_operation_list ] report_list = [] options_validators = OPERATION_OPTIONS_VALIDATORS + [ validate.value_in( "name", allowed_operation_name_list, option_name_for_report="operation name", code_to_allow_extra_values=report_codes.FORCE_OPTIONS, allow_extra_values=allow_invalid, ) ] for operation in operations_to_validate: report_list.extend( validate_operation(operation, options_validators) ) operation_list = [ validate.pairs_to_values(op) for op in operations_to_validate ] report_list.extend(validate_different_intervals(operation_list)) #can raise LibraryError report_processor.process_list(report_list) return complete_all_intervals(operation_list) + get_remaining_defaults( report_processor, operation_list, default_operation_list )
def validate_func(option_dict): allowed_algorithms = ( "ffsplit", "lms", ) value = validate.ValuePair.get(option_dict["algorithm"]) if validate.is_empty_string(value.normalized): return [ reports.invalid_option_value("algorithm", value.original, allowed_algorithms) ] return validate.value_in( "algorithm", allowed_algorithms, code_to_allow_extra_values=code_to_allow_extra_values, extra_values_allowed=extra_values_allowed)(option_dict)
def test_returns_report_about_invalid_option(self): assert_report_item_list_equal( validate.value_in("a", ["b"])({"a": "c"}), [ ( severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "c", "allowed_values": ["b"], }, None ), ] )
def add_quorum_device( model, model_options, generic_options, heuristics_options, node_ids, force_model=False, force_options=False ): """ Validate adding a quorum device string model -- quorum device model dict model_options -- model specific options dict generic_options -- generic quorum device options dict heuristics_options -- heuristics options list node_ids -- list of existing node ids bool force_model -- continue even if the model is not valid bool force_options -- turn forceable errors into warnings """ report_items = [] model_validators = { "net": lambda: _qdevice_add_model_net_options( model_options, node_ids, force_options ), } if model in model_validators: report_items += model_validators[model]() else: report_items += validate.run_collection_of_option_validators( {"model": model}, [ validate.value_in( "model", list(model_validators.keys()), **validate.allow_extra_values( report_codes.FORCE_QDEVICE_MODEL, force_model ) ) ] ) return ( report_items + _qdevice_add_generic_options(generic_options, force_options) + _qdevice_add_heuristics_options(heuristics_options, force_options) )
def test_support_OptionValuePair(self): assert_report_item_list_equal( validate.value_in("a", ["b"])( {"a": validate.ValuePair(original="C", normalized="c")} ), [ ( severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "C", "allowed_values": ["b"], }, None ), ] )
def test_supports_another_report_option_name(self): assert_report_item_list_equal( validate.value_in("a", ["b"], option_name_for_report="option a")( {"a": "c"} ), [ ( severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "option a", "option_value": "c", "allowed_values": ["b"], }, None ), ] )
def test_supports_forceable_errors(self): assert_report_item_list_equal( validate.value_in("a", ["b"], code_to_allow_extra_values="FORCE")( {"a": "c"} ), [ ( severities.ERROR, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "c", "allowed_values": ["b"], }, "FORCE" ), ] )
def test_supports_warning(self): assert_report_item_list_equal( validate.value_in( "a", ["b"], code_to_allow_extra_values="FORCE", allow_extra_values=True )( {"a": "c"} ), [ ( severities.WARNING, report_codes.INVALID_OPTION_VALUE, { "option_name": "a", "option_value": "c", "allowed_values": ["b"], }, None ), ] )
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
"true", "false", ] #normalize(key, value) -> normalized_value normalize = validate.option_value_normalization({ "role": lambda value: value.lower().capitalize(), "requires": lambda value: value.lower(), "on-fail": lambda value: value.lower(), "record-pending": lambda value: value.lower(), "enabled": lambda value: value.lower(), }) OPERATION_OPTIONS_VALIDATORS = [ validate.is_required("name", "resource operation"), validate.value_in("role", ROLE_VALUES), validate.value_in("requires", REQUIRES_VALUES), validate.value_in("on-fail", ON_FAIL_VALUES), validate.value_in("record-pending", BOOLEAN_VALUES), validate.value_in("enabled", BOOLEAN_VALUES), validate.mutually_exclusive( ["interval-origin", "start-delay"], "resource operation" ) ] def prepare( report_processor, raw_operation_list, default_operation_list, allowed_operation_name_list, allow_invalid=False ): """
def _validate_container_type(container_type): return validate.value_in("type", ("docker", ), "container type")({ "type": container_type, })
def create(cluster_name, node_list, transport, force_unresolvable=False): """ Validate creating a new minimalistic corosync.conf string cluster_name -- the name of the new cluster list node_list -- nodes of the new cluster; dict: name, addrs string transport -- corosync transport used in the new cluster bool force_unresolvable -- if True, report unresolvable addresses as warnings instead of errors """ # cluster name and transport validation validators = [ validate.value_not_empty("name", "a non-empty string", "cluster name"), validate.value_in("transport", constants.TRANSPORTS_ALL) ] report_items = validate.run_collection_of_option_validators( { "name": cluster_name, "transport": transport }, validators ) # nodelist validation get_addr_type = _addr_type_analyzer() all_names_usable = True # can names be used to identifying nodes? all_names_count = defaultdict(int) all_addrs_count = defaultdict(int) addr_types_per_node = [] unresolvable_addresses = set() # First, validate each node on its own. Also extract some info which will # be needed when validating the nodelist and inter-node dependencies. for i, node in enumerate(node_list, 1): report_items.extend( validate.run_collection_of_option_validators( node, _get_node_name_validators(i) ) + validate.names_in(["addrs", "name"], node.keys(), "node") ) if "name" in node and node["name"]: # Count occurrences of each node name. Do not bother counting # missing or empty names. They must be fixed anyway. all_names_count[node["name"]] += 1 else: all_names_usable = False # Cannot use node.get("addrs", []) - if node["addrs"] == None then # the get returns None and len(None) raises an exception. addr_count = len(node.get("addrs") or []) if transport in (constants.TRANSPORTS_KNET + constants.TRANSPORTS_UDP): if transport in constants.TRANSPORTS_KNET: min_addr_count = constants.LINKS_KNET_MIN max_addr_count = constants.LINKS_KNET_MAX else: min_addr_count = constants.LINKS_UDP_MIN max_addr_count = constants.LINKS_UDP_MAX if ( addr_count < min_addr_count or addr_count > max_addr_count ): report_items.append( reports.corosync_bad_node_addresses_count( addr_count, min_addr_count, max_addr_count, node_name=node.get("name"), node_index=i ) ) addr_types = [] # Cannot use node.get("addrs", []) - if node["addrs"] == None then # the get returns None and len(None) raises an exception. for addr in (node.get("addrs") or []): all_addrs_count[addr] += 1 addr_types.append(get_addr_type(addr)) if get_addr_type(addr) == ADDR_UNRESOLVABLE: unresolvable_addresses.add(addr) addr_types_per_node.append(addr_types) # Report all unresolvable addresses at once instead on each own. if unresolvable_addresses: severity = ReportItemSeverity.ERROR forceable = report_codes.FORCE_NODE_ADDRESSES_UNRESOLVABLE if force_unresolvable: severity = ReportItemSeverity.WARNING forceable = None report_items.append( reports.node_addresses_unresolvable( unresolvable_addresses, severity, forceable ) ) # Reporting single-node errors finished. # Now report nodelist and inter-node errors. if len(node_list) < 1: report_items.append(reports.corosync_nodes_missing()) non_unique_names = set([ name for name, count in all_names_count.items() if count > 1 ]) if non_unique_names: all_names_usable = False report_items.append( reports.node_names_duplication(non_unique_names) ) non_unique_addrs = set([ addr for addr, count in all_addrs_count.items() if count > 1 ]) if non_unique_addrs: report_items.append( reports.node_addresses_duplication(non_unique_addrs) ) if all_names_usable: # Check for errors using node names in their reports. If node names are # ambiguous then such issues cannot be comprehensibly reported so the # checks are skipped. node_addr_count = {} for node in node_list: # Cannot use node.get("addrs", []) - if node["addrs"] == None then # the get returns None and len(None) raises an exception. node_addr_count[node["name"]] = len(node.get("addrs") or []) # Check if all nodes have the same number of addresses. No need to # check that if udp or udpu transport is used as they can only use one # address and that has already been checked above. if ( transport not in constants.TRANSPORTS_UDP and len(Counter(node_addr_count.values()).keys()) > 1 ): report_items.append( reports.corosync_node_address_count_mismatch(node_addr_count) ) # Check mixing IPv4 and IPv6 in one link, node names are not relevant links_ip_mismatch = [] for link, addr_types in enumerate(zip_longest(*addr_types_per_node)): if ADDR_IPV4 in addr_types and ADDR_IPV6 in addr_types: links_ip_mismatch.append(link) if links_ip_mismatch: report_items.append( reports.corosync_ip_version_mismatch_in_links(links_ip_mismatch) ) return report_items
def setUp(self): self.validators = { "a": validate.value_in("a", ["x", "y", "z"]), "b": validate.value_integer_in_range("b", 0, 9), }
def test_returns_empty_report_on_valid_option(self): self.assertEqual([], validate.value_in("a", ["b"])({"a": "b"}))
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 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_returns_empty_report_on_valid_option(self): self.assertEqual( [], validate.value_in("a", ["b"])({"a": "b"}) )
def _validate_container_type(container_type): return validate.value_in("type", GENERIC_CONTAINER_TYPES, "container type")({ "type": container_type, })