diff --git a/README.md b/README.md index d4b4a4f..d14e677 100644 --- a/README.md +++ b/README.md @@ -692,19 +692,149 @@ SSL certificates and keys must be deployed on the host prior to running the role ### `bond` -The `bond` setting configures the options of bonded interfaces -(type `bond`). It supports the following options: +The `bond` setting configures the options of bonded interfaces (type `bond`). +See the [kernel documentation for +bonding](https://www.kernel.org/doc/Documentation/networking/bonding.txt) or +your distribution `nmcli` documentation for valid values. It supports the +following options: - `mode` - Bonding mode. See the - [kernel documentation](https://www.kernel.org/doc/Documentation/networking/bonding.txt) - or your distribution `nmcli` documentation for valid values. - NetworkManager defaults to `balance-rr`. + Bonding mode. The possible values are `balance-rr` (default), `active-backup`, + `balance-xor`, `broadcast`, `802.3ad`, `balance-tlb`, or `balance-alb`. + +- `ad_actor_sys_prio` + + In `802.3ad` bonding mode, this specifies the system priority. The valid range is + 1 - 65535. + +- `ad_actor_system` + + In `802.3ad` bonding mode, this specifies the system mac-address for the actor in + protocol packet exchanges (LACPDUs). + +- `ad_select` + + This option specifies the 802.3ad aggregation selection logic to use. The possible + values are: `stable`, `bandwidth`, `count`. + +- `ad_user_port_key` + + In `802.3ad` bonding mode, this defines the upper 10 bits of the port key. The + allowed range for the value is 0 - 1023. + +- `all_ports_active` + + `all_slaves_active` in kernel and NetworkManager. The boolean value `False` drops + the duplicate frames (received on inactive ports) and the boolean value `True` + delivers the duplicate frames. + +- `arp_all_targets` + + This option specifies the quantity of arp_ip_targets that must be reachable in + order for the ARP monitor to consider a port as being up. The possible values are + `any` or `all`. + +- `arp_interval` + + This option specifies the ARP link monitoring frequency in milliseconds. A value of + 0 disables ARP monitoring. + +- `arp_validate` + + In any mode that supports arp monitoring, this option specifies whether or not ARP + probes and replies should be validated. Or for link monitoring purposes, whether + non-ARP traffic should be filtered (disregarded). The possible values are: `none`, + `active`, `backup`, `all`, `filter`, `filter_active`, `filter_backup`. + +- `arp_ip_target` + + When `arp_interval` is enabled, this option specifies the IP addresses to use as + ARP monitoring peers. + +- `downdelay` + + The time to wait (in milliseconds) before disabling a port after a link failure + has been detected. + +- `fail_over_mac` + + This option specifies the policy to select the MAC address for the bond interface + in active-backup mode. The possible values are: `none` (default), `active`, + `follow`. + +- `lacp_rate` + + In `802.3ad` bonding mode, this option defines the rate in which we requst link + partner to transmit LACPDU packets. The possible values are: `slow`, `fast`. + +- `lp_interval` + + This option specifies the number of seconds between instances where the bonding + driver sends learning packets to each ports peer switch. - `miimon` - Sets the MII link monitoring interval (in milliseconds) + Sets the MII link monitoring interval (in milliseconds). + +- `min_links` + + This option specifies the minimum number of links that must be active before + asserting the carrier. + +- `num_grat_arp` + + This option specify the number of peer notifications (gratuitious ARPs) to be + issued after a failover event. The allowed range for the value is 0 - 255. + +- `packets_per_port` + + In `balance-rr` bonding mode, this option specifies the number of packets allowed + for a port in network transmission before moving to the next one. The allowed + range for the value is 0 - 65535. + +- `peer_notif_delay` + + This option specifies the delay (in milliseconds) between each peer notification + when they are issued after a failover event. + +- `primary` + + This option defines the primary device. + +- `primary_reselect` + + This option specifies the reselection policy for the primary port. The possible + values are: `always`, `better`, `failure`. + +- `resend_igmp` + + This option specifies the number of IGMP membership reports to be issued after a + failover event. The allowed range for the value is 0 - 255. + +- `tlb_dynamic_lb` + + This option specifies if dynamic shuffling of flows is enabled in tlb mode. The + boolean value `True` enables the flow shuffling while the boolean value `False` + disables it. + +- `updelay` + + This option specifies the time (in milliseconds) to wait before enabling a port + after a link recovery has been detected. + +- `use_carrier` + + This options specifies whether or not miimon should use MII or ETHTOOL ioctls + versus netif_carrier_ok() to determine the link sattus. The boolean value `True` + enables the use of netif_carrier_ok() while the boolean value `False` uses MII or + ETHTOOL ioctls instead. + +- `xmit_hash_policy` + + This option specifies the transmit hash policy to use for port selection, the + possible values are: `layer2`, `layer3+4`, `layer2+3`, `encap2+3`, `encap3+4`, + `vlan+srcmac`. Examples of Options ------------------- diff --git a/examples/bond_options.yml b/examples/bond_options.yml new file mode 100644 index 0000000..d236998 --- /dev/null +++ b/examples/bond_options.yml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- hosts: network-test + vars: + network_connections: + # Specify the bond profile + - name: bond0 + state: up + type: bond + interface_name: bond0 + # ip configuration (optional) + ip: + address: + - "192.0.2.24/24" + - "2001:db8::23/64" + bond: + mode: 802.3ad + ad_actor_sys_prio: 65535 + ad_actor_system: 00:00:5e:00:53:5d + ad_select: stable + ad_user_port_key: 1023 + all_ports_active: True + arp_all_targets: all + downdelay: 0 + lacp_rate: slow + lp_interval: 128 + miimon: 110 + min_links: 0 + num_grat_arp: 64 + peer_notif_delay: 220 + primary_reselect: better + resend_igmp: 225 + updelay: 0 + use_carrier: True + xmit_hash_policy: encap2+3 + # add an ethernet profile to the bond + - name: member1 + state: up + type: ethernet + interface_name: eth1 + controller: bond0 + + # add a second ethernet profile to the bond + - name: member2 + state: up + type: ethernet + interface_name: eth2 + controller: bond0 + roles: + - linux-system-roles.network +... diff --git a/library/network_connections.py b/library/network_connections.py index 706b198..c661ad7 100644 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -867,8 +867,14 @@ class NMUtil: s_con.set_property(NM.SETTING_CONNECTION_TYPE, NM.SETTING_BOND_SETTING_NAME) s_bond = self.connection_ensure_setting(con, NM.SettingBond) s_bond.add_option("mode", connection["bond"]["mode"]) - if connection["bond"]["miimon"] is not None: - s_bond.add_option("miimon", str(connection["bond"]["miimon"])) + for option, value in connection["bond"].items(): + if value is None: + continue + if option in ["all_ports_active", "use_carrier", "tlb_dynamic_lb"]: + value = int(value) + if option in ["all_ports_active", "packets_per_port"]: + option = option.replace("port", "slave") + s_bond.add_option(option, str(value)) elif connection["type"] == "team": s_con.set_property(NM.SETTING_CONNECTION_TYPE, NM.SETTING_TEAM_SETTING_NAME) elif connection["type"] == "dummy": diff --git a/module_utils/network_lsr/argument_validator.py b/module_utils/network_lsr/argument_validator.py index 279ac58..7064227 100644 --- a/module_utils/network_lsr/argument_validator.py +++ b/module_utils/network_lsr/argument_validator.py @@ -1158,15 +1158,204 @@ class ArgValidator_DictBond(ArgValidatorDict): name="bond", nested=[ ArgValidatorStr("mode", enum_values=ArgValidator_DictBond.VALID_MODES), + ArgValidatorNum( + "ad_actor_sys_prio", val_min=1, val_max=65535, default_value=None + ), + ArgValidatorMac("ad_actor_system"), + ArgValidatorStr( + "ad_select", enum_values=["stable", "bandwidth", "count"] + ), + ArgValidatorNum( + "ad_user_port_key", val_min=0, val_max=1023, default_value=None + ), + ArgValidatorBool("all_ports_active", default_value=None), + ArgValidatorStr("arp_all_targets", enum_values=["any", "all"]), + ArgValidatorNum( + "arp_interval", val_min=0, val_max=1000000, default_value=None + ), + ArgValidatorStr( + "arp_validate", + enum_values=[ + "none", + "active", + "backup", + "all", + "filter", + "filter_active", + "filter_backup", + ], + ), + ArgValidatorStr("arp_ip_target"), + ArgValidatorNum( + "downdelay", val_min=0, val_max=1000000, default_value=None + ), + ArgValidatorStr( + "fail_over_mac", enum_values=["none", "active", "follow"] + ), + ArgValidatorStr("lacp_rate", enum_values=["slow", "fast"]), + ArgValidatorNum( + "lp_interval", val_min=1, val_max=1000000, default_value=None + ), ArgValidatorNum( "miimon", val_min=0, val_max=1000000, default_value=None ), + ArgValidatorNum( + "min_links", val_min=0, val_max=1000000, default_value=None + ), + ArgValidatorNum( + "num_grat_arp", val_min=0, val_max=255, default_value=None + ), + ArgValidatorNum( + "packets_per_port", val_min=0, val_max=65535, default_value=None + ), + ArgValidatorNum( + "peer_notif_delay", val_min=0, val_max=1000000, default_value=None + ), + ArgValidatorStr("primary"), + ArgValidatorStr( + "primary_reselect", enum_values=["always", "better", "failure"] + ), + ArgValidatorNum( + "resend_igmp", val_min=0, val_max=255, default_value=None + ), + ArgValidatorBool("tlb_dynamic_lb", default_value=None), + ArgValidatorNum( + "updelay", val_min=0, val_max=1000000, default_value=None + ), + ArgValidatorBool("use_carrier", default_value=None), + ArgValidatorStr( + "xmit_hash_policy", + enum_values=[ + "layer2", + "layer3+4", + "layer2+3", + "encap2+3", + "encap3+4", + "vlan+srcmac", + ], + ), ], default_value=ArgValidator.MISSING, ) + def _validate_post(self, value, name, result): + AD_OPTIONS = [ + "ad_actor_sys_prio", + "ad_actor_system", + "ad_user_port_key", + "lacp_rate", + ] + ARP_OPTIONS = ["arp_interval", "arp_ip_target", "arp_validate"] + ARP_ONLY_MODE = ["balance-rr", "active-backup", "balance-xor", "broadcast"] + + if result["mode"] != "802.3ad": + for option in AD_OPTIONS: + if result[option] is not None: + raise ValidationError( + name, + "the bond option {0} is only valid with mode 802.3ad".format( + option + ), + ) + + if result["packets_per_port"] is not None and result["mode"] != "balance-rr": + raise ValidationError( + name, + "the bond option packets_per_port is only valid with mode balance-rr", + ) + + if result["mode"] not in ARP_ONLY_MODE: + for option in ARP_OPTIONS: + if result[option] is not None: + raise ValidationError( + name, + "the bond option {0} is only valid with mode balance-rr, active-backup, balance-xor or broadcast".format( + option + ), + ) + + if result["tlb_dynamic_lb"] is not None and result["mode"] not in [ + "balance-tlb", + "balance-alb", + ]: + raise ValidationError( + name, + "the bond option tlb_dynamic_lb is only valid with mode balance-tlb or balance-alb", + ) + + if result["primary"] is not None and result["mode"] not in [ + "active-backup", + "balance-tlb", + "balance-alb", + ]: + raise ValidationError( + name, + "the bond option primary is only valid with mode active-backup, balance-tlb, balance-alb", + ) + + if ( + result["updelay"] is not None or result["downdelay"] is not None + ) and not result["miimon"]: + raise ValidationError( + name, + "the bond option downdelay or updelay is only valid with miimon enabled", + ) + if result["peer_notif_delay"] is not None: + if not result["miimon"] or result["peer_notif_delay"] % result["miimon"]: + raise ValidationError( + name, + "the bond option peer_notif_delay needs miimon enabled and must be miimon multiple", + ) + if result["arp_interval"]: + raise ValidationError( + name, + "the bond option peer_notif_delay needs arp_interval disabled", + ) + if result["arp_ip_target"]: + if not result["arp_interval"]: + raise ValidationError( + name, + "the bond option arp_ip_target requires arp_interval to be set", + ) + + if result["arp_interval"]: + if not result["arp_ip_target"]: + raise ValidationError( + name, + "the bond option arp_interval requires arp_ip_target to be set", + ) + + return result + def get_default_bond(self): - return {"mode": ArgValidator_DictBond.VALID_MODES[0], "miimon": None} + return { + "mode": ArgValidator_DictBond.VALID_MODES[0], + "ad_actor_sys_prio": None, + "ad_actor_system": None, + "ad_select": None, + "ad_user_port_key": None, + "all_ports_active": None, + "arp_all_targets": None, + "arp_interval": None, + "arp_ip_target": None, + "arp_validate": None, + "downdelay": None, + "fail_over_mac": None, + "lacp_rate": None, + "lp_interval": None, + "miimon": None, + "min_links": None, + "num_grat_arp": None, + "packets_per_port": None, + "peer_notif_delay": None, + "primary": None, + "primary_reselect": None, + "resend_igmp": None, + "tlb_dynamic_lb": None, + "updelay": None, + "use_carrier": None, + "xmit_hash_policy": None, + } class ArgValidator_DictInfiniband(ArgValidatorDict): @@ -1889,6 +2078,12 @@ class ArgValidator_ListConnections(ArgValidatorList): "not a controller " "type by '%s'" % (connection["controller"], c["type"]), ) + if connection["type"] == "infiniband": + if c["type"] == "bond" and c["bond"]["mode"] != "active_backup": + raise ValidationError( + name + "[" + str(idx) + "].controller", + "bond only supports infiniband ports in active-backup mode", + ) if connection["port_type"] is None: connection["port_type"] = c["type"] elif connection["port_type"] != c["type"]: @@ -2083,6 +2278,28 @@ class ArgValidator_ListConnections(ArgValidatorList): "match.path is not supported by the running version of " "NetworkManger.", ) + + if "bond" in connection: + if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS: + for option in connection["bond"]: + if connection["bond"][option] is not None and option not in [ + "mode", + "miimon", + ]: + raise ValidationError.from_connection( + idx, + "initscripts only supports the mode and miimon bond " + "options. All the other bond options are not supported by " + "initscripts.", + ) + # the `peer_notif_delay` bond option was supported in NM since NM 1.30 + if connection["bond"]["peer_notif_delay"]: + if not hasattr(Util.NM(), "SETTING_BOND_OPTION_PEER_NOTIF_DELAY"): + raise ValidationError.from_connection( + idx, + "the bond option peer_notif_delay is not supported in " + "NetworkManger until NM 1.30", + ) self.validate_route_tables(connection, idx) diff --git a/tests/ensure_provider_tests.py b/tests/ensure_provider_tests.py index 6f9b331..d53b3f5 100755 --- a/tests/ensure_provider_tests.py +++ b/tests/ensure_provider_tests.py @@ -67,6 +67,7 @@ ibution_major_version | int < 9", EXTRA_RUN_CONDITION: "ansible_distribution != 'RedHat' or\n ansible_distr\ ibution_major_version | int < 9", }, + "playbooks/tests_bond_options.yml": {}, "playbooks/tests_eth_dns_support.yml": {}, "playbooks/tests_dummy.yml": {}, "playbooks/tests_ipv6_disabled.yml": { diff --git a/tests/playbooks/tests_bond_options.yml b/tests/playbooks/tests_bond_options.yml new file mode 100644 index 0000000..36cbc97 --- /dev/null +++ b/tests/playbooks/tests_bond_options.yml @@ -0,0 +1,178 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- hosts: all + vars: + controller_profile: bond0 + controller_device: nm-bond + port1_profile: bond0.0 + dhcp_interface1: test1 + port2_profile: bond0.1 + dhcp_interface2: test2 + tasks: + - name: "INIT Prepare setup" + debug: + msg: "##################################################" + - import_tasks: tasks/create_test_interfaces_with_dhcp.yml + - import_tasks: tasks/assert_device_present.yml + vars: + interface: "{{ dhcp_interface1 }}" + - import_tasks: tasks/assert_device_present.yml + vars: + interface: "{{ dhcp_interface2 }}" + - block: + - name: "TEST Add Bond with 2 ports" + debug: + msg: "##################################################" + - name: Configure the bond options + import_role: + name: linux-system-roles.network + vars: + network_connections: + # Create a bond controller + - name: "{{ controller_profile }}" + state: up + type: bond + interface_name: "{{ controller_device }}" + bond: + mode: 802.3ad + ad_actor_sys_prio: 65535 + ad_actor_system: 00:00:5e:00:53:5d + ad_select: stable + ad_user_port_key: 1023 + all_ports_active: True + arp_all_targets: all + downdelay: 0 + lacp_rate: slow + lp_interval: 128 + miimon: 110 + min_links: 0 + num_grat_arp: 64 + primary_reselect: better + resend_igmp: 225 + updelay: 0 + use_carrier: True + xmit_hash_policy: encap2+3 + + # add an ethernet to the bond + - name: "{{ port1_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface1 }}" + controller: "{{ controller_profile }}" + # add a second ethernet to the bond + - name: "{{ port2_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface2 }}" + controller: "{{ controller_profile }}" + - import_tasks: tasks/assert_device_present.yml + vars: + interface: "{{ controller_device }}" + - include_tasks: tasks/assert_profile_present.yml + vars: + profile: "{{ item }}" + loop: + - "{{ controller_profile }}" + - "{{ port1_profile }}" + - "{{ port2_profile }}" + + - command: cat + /sys/class/net/{{ controller_device }}/bonding/'{{ item.key }}' + name: "** TEST check bond settings" + register: result + until: "'{{ item.value }}' in result.stdout" + loop: + - { key: 'mode', value: '802.3ad'} + - { key: 'ad_actor_sys_prio', value: '65535'} + - { key: 'ad_actor_system', value: '00:00:5e:00:53:5d'} + - { key: 'ad_select', value: 'stable'} + - { key: 'ad_user_port_key', value: '1023'} + - { key: 'all_slaves_active', value: '1'} + - { key: 'arp_all_targets', value: 'all'} + - { key: 'downdelay', value: '0'} + - { key: 'lacp_rate', value: 'slow'} + - { key: 'lp_interval', value: '128'} + - { key: 'miimon', value: '110'} + - { key: 'num_grat_arp', value: '64'} + - { key: 'resend_igmp', value: '225'} + - { key: 'updelay', value: '0'} + - { key: 'use_carrier', value: '1'} + - { key: 'xmit_hash_policy', value: 'encap2+3'} + + - command: ip -4 a s {{ controller_device }} + name: "** TEST check IPv4" + register: result + until: "'192.0.2' in result.stdout" + retries: 20 + delay: 2 + - command: ip -6 a s {{ controller_device }} + name: "** TEST check IPv6" + register: result + until: "'2001' in result.stdout" + retries: 20 + delay: 2 + + - name: Reconfigure the bond options + import_role: + name: linux-system-roles.network + vars: + network_connections: + # Create a bond controller + - name: "{{ controller_profile }}" + state: up + type: bond + interface_name: "{{ controller_device }}" + bond: + mode: active-backup + arp_interval: 60 + arp_ip_target: 192.0.2.128 + arp_validate: none + primary: "{{ dhcp_interface1 }}" + + - command: cat + /sys/class/net/{{ controller_device }}/bonding/'{{ item.key }}' + name: "** TEST check bond settings" + register: result + until: "'{{ item.value }}' in result.stdout" + loop: + - { key: 'mode', value: 'active-backup'} + - { key: 'arp_interval', value: '60'} + - { key: 'arp_ip_target', value: '192.0.2.128'} + - { key: 'arp_validate', value: 'none'} + - { key: 'primary', value: '{{ dhcp_interface1 }}'} + + - command: ip -4 a s {{ controller_device }} + name: "** TEST check IPv4" + register: result + until: "'192.0.2' in result.stdout" + retries: 20 + delay: 2 + - command: ip -6 a s {{ controller_device }} + name: "** TEST check IPv6" + register: result + until: "'2001' in result.stdout" + retries: 20 + delay: 2 + + always: + - block: + - name: Import network role + import_role: + name: linux-system-roles.network + vars: + network_connections: + - name: "{{ port2_profile }}" + persistent_state: absent + state: down + - name: "{{ port1_profile }}" + persistent_state: absent + state: down + - name: "{{ controller_profile }}" + persistent_state: absent + state: down + ignore_errors: true + - command: ip link del {{ controller_device }} + ignore_errors: true + - import_tasks: tasks/remove_test_interfaces_with_dhcp.yml + tags: + - "tests::cleanup" diff --git a/tests/tests_bond_options_nm.yml b/tests/tests_bond_options_nm.yml new file mode 100644 index 0000000..373d642 --- /dev/null +++ b/tests/tests_bond_options_nm.yml @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: BSD-3-Clause +# This file was generated by ensure_provider_tests.py +--- +# set network provider and gather facts +- hosts: all + name: Run playbook 'playbooks/tests_bond_options.yml' with nm as provider + tasks: + - include_tasks: tasks/el_repo_setup.yml + - name: Set network provider to 'nm' + set_fact: + network_provider: nm + tags: + - always + + +# The test requires or should run with NetworkManager, therefore it cannot run +# on RHEL/CentOS 6 +- import_playbook: playbooks/tests_bond_options.yml + when: + - ansible_distribution_major_version != '6' diff --git a/tests/unit/test_network_connections.py b/tests/unit/test_network_connections.py index 8d05f16..9bfa068 100644 --- a/tests/unit/test_network_connections.py +++ b/tests/unit/test_network_connections.py @@ -1737,7 +1737,34 @@ class TestValidator(Python26CompatTestCase): { "actions": ["present", "up"], "autoconnect": True, - "bond": {"mode": "balance-rr", "miimon": None}, + "bond": { + "mode": "balance-rr", + "ad_actor_sys_prio": None, + "ad_actor_system": None, + "ad_select": None, + "ad_user_port_key": None, + "all_ports_active": None, + "arp_all_targets": None, + "arp_interval": None, + "arp_ip_target": None, + "arp_validate": None, + "downdelay": None, + "fail_over_mac": None, + "lacp_rate": None, + "lp_interval": None, + "miimon": None, + "min_links": None, + "num_grat_arp": None, + "packets_per_port": None, + "peer_notif_delay": None, + "primary": None, + "primary_reselect": None, + "resend_igmp": None, + "tlb_dynamic_lb": None, + "updelay": None, + "use_carrier": None, + "xmit_hash_policy": None, + }, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, @@ -1788,7 +1815,34 @@ class TestValidator(Python26CompatTestCase): { "actions": ["present", "up"], "autoconnect": True, - "bond": {"mode": "active-backup", "miimon": None}, + "bond": { + "mode": "active-backup", + "ad_actor_sys_prio": None, + "ad_actor_system": None, + "ad_select": None, + "ad_user_port_key": None, + "all_ports_active": None, + "arp_all_targets": None, + "arp_interval": None, + "arp_ip_target": None, + "arp_validate": None, + "downdelay": None, + "fail_over_mac": None, + "lacp_rate": None, + "lp_interval": None, + "miimon": None, + "min_links": None, + "num_grat_arp": None, + "packets_per_port": None, + "peer_notif_delay": None, + "primary": None, + "primary_reselect": None, + "resend_igmp": None, + "tlb_dynamic_lb": None, + "updelay": None, + "use_carrier": None, + "xmit_hash_policy": None, + }, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, @@ -4312,6 +4366,188 @@ class TestValidatorRouteTable(Python26CompatTestCase): ) +class TestValidatorDictBond(Python26CompatTestCase): + def setUp(self): + self.validator = network_lsr.argument_validator.ArgValidator_ListConnections() + self.test_connections = [ + { + "name": "bond0", + "type": "bond", + "bond": { + "mode": "balance-rr", + }, + }, + ] + + def test_invalid_bond_option_ad(self): + + """ + Test the ad bond option restrictions + """ + self.test_connections[0]["bond"]["ad_actor_sys_prio"] = 65535 + self.assertRaisesRegex( + ValidationError, + "the bond option ad_actor_sys_prio is only valid with mode 802.3ad", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["mode"] = "802.3ad" + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_packets_per_port(self): + """ + Test the packets_per_port bond option restrictions + """ + self.test_connections[0]["bond"]["mode"] = "802.3ad" + self.test_connections[0]["bond"]["packets_per_port"] = 2 + self.assertRaisesRegex( + ValidationError, + "the bond option packets_per_port is only valid with mode balance-rr", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["mode"] = "balance-rr" + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_arp(self): + """ + Test the arp bond option restrictions + """ + self.test_connections[0]["bond"]["mode"] = "802.3ad" + self.test_connections[0]["bond"]["arp_interval"] = 2 + self.test_connections[0]["bond"]["arp_ip_target"] = "198.51.100.3" + + self.assertRaisesRegex( + ValidationError, + "the bond option arp_interval is only valid with mode balance-rr, active-backup, balance-xor or broadcast", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["mode"] = "balance-rr" + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_tlb_dynamic_lb(self): + """ + Test the tlb_dynamic_lb bond option restrictions + """ + self.test_connections[0]["bond"]["tlb_dynamic_lb"] = True + self.assertRaisesRegex( + ValidationError, + "the bond option tlb_dynamic_lb is only valid with mode balance-tlb or balance-alb", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["mode"] = "balance-tlb" + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_primary(self): + """ + Test the primary bond option restrictions + """ + self.test_connections[0]["bond"]["primary"] = "bond0.0" + self.assertRaisesRegex( + ValidationError, + "the bond option primary is only valid with mode active-backup, balance-tlb, balance-alb", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["mode"] = "balance-tlb" + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_downdelay_updelay(self): + """ + Test the downdelay or updelay bond option restrictions + """ + self.test_connections[0]["bond"]["downdelay"] = 5 + self.assertRaisesRegex( + ValidationError, + "the bond option downdelay or updelay is only valid with miimon enabled", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["miimon"] = 110 + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_peer_notif_delay(self): + """ + Test the peer_notif_delay bond option restrictions + """ + self.test_connections[0]["bond"]["miimon"] = 110 + self.test_connections[0]["bond"]["peer_notif_delay"] = 222 + self.assertRaisesRegex( + ValidationError, + "the bond option peer_notif_delay needs miimon enabled and must be miimon multiple", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["peer_notif_delay"] = 220 + self.test_connections[0]["bond"]["arp_interval"] = 110 + self.assertRaisesRegex( + ValidationError, + "the bond option peer_notif_delay needs arp_interval disabled", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["arp_interval"] = 0 + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_peer_arp_ip_target_arp_interval(self): + """ + Test the arp_ip_target or arp_interval bond option restrictions + """ + self.test_connections[0]["bond"]["arp_interval"] = 4 + self.assertRaisesRegex( + ValidationError, + "the bond option arp_interval requires arp_ip_target to be set", + self.validator.validate, + self.test_connections, + ) + + self.test_connections[0]["bond"]["arp_ip_target"] = "198.51.100.3" + self.test_connections[0]["bond"]["arp_interval"] = 0 + self.assertRaisesRegex( + ValidationError, + "the bond option arp_ip_target requires arp_interval to be set", + self.validator.validate, + self.test_connections, + ) + self.test_connections[0]["bond"]["arp_interval"] = 4 + self.validator.validate(self.test_connections) + + def test_invalid_bond_option_infiniband_port(self): + """ + Test that bond only supports infiniband ports in active-backup mode + """ + test_connections_with_infiniband_port = [ + { + "name": "bond0", + "type": "bond", + "bond": { + "mode": "balance-rr", + }, + }, + { + "name": "bond0.0", + "type": "infiniband", + "controller": "bond0", + }, + { + "name": "bond0.1", + "type": "infiniband", + "controller": "bond0", + }, + ] + + self.assertRaisesRegex( + ValidationError, + "bond only supports infiniband ports in active-backup mode", + self.validator.validate, + test_connections_with_infiniband_port, + ) + self.test_connections[0]["bond"]["mode"] = "active-backup" + self.validator.validate(self.test_connections) + + class TestSysUtils(unittest.TestCase): def test_link_read_permaddress(self): self.assertEqual(SysUtil._link_read_permaddress("lo"), "00:00:00:00:00:00")