From 79d1fa2739e68685ed76c91549d8045ad61b769c Mon Sep 17 00:00:00 2001 From: Sylvain <35niavlys@gmail.com> Date: Tue, 23 Aug 2022 15:32:27 +0200 Subject: [PATCH] Support cloned MAC address The users want to create the bonding interface with the MAC address specified by themselves or specify the strategy (e.g. random, preserve) to get the default MAC for the bonding interface. Thus, add support for the cloned MAC address. Signed-off-by: Sylvain <35niavlys@gmail.com> --- .github/run_test.sh | 2 + .yamllint.yml | 3 + README.md | 13 ++ examples/cloned_mac.yml | 30 ++++ library/network_connections.py | 14 ++ .../network_lsr/argument_validator.py | 41 +++++- tests/playbooks/tests_bond_cloned_mac.yml | 128 ++++++++++++++++++ tests/playbooks/tests_bridge_cloned_mac.yml | 60 ++++++++ tests/tests_bond_cloned_mac_initscripts.yml | 16 +++ tests/tests_bond_cloned_mac_nm.yml | 20 +++ tests/tests_bridge_cloned_mac_initscripts.yml | 16 +++ tests/tests_bridge_cloned_mac_nm.yml | 20 +++ tests/unit/test_network_connections.py | 35 +++++ 13 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 examples/cloned_mac.yml create mode 100644 tests/playbooks/tests_bond_cloned_mac.yml create mode 100644 tests/playbooks/tests_bridge_cloned_mac.yml create mode 100644 tests/tests_bond_cloned_mac_initscripts.yml create mode 100644 tests/tests_bond_cloned_mac_nm.yml create mode 100644 tests/tests_bridge_cloned_mac_initscripts.yml create mode 100644 tests/tests_bridge_cloned_mac_nm.yml diff --git a/.github/run_test.sh b/.github/run_test.sh index a04c4a8..7a99a37 100755 --- a/.github/run_test.sh +++ b/.github/run_test.sh @@ -16,6 +16,7 @@ EXCLUDE_TESTS_C7=' -e tests/tests_auto_gateway_initscripts.yml -e tests/tests_bond_deprecated_initscripts.yml -e tests/tests_bond_initscripts.yml +-e tests/tests_bond_cloned_mac_initscripts.yml -e tests/tests_bond_removal_initscripts.yml -e tests/tests_infiniband_nm.yml -e tests/tests_team_nm.yml @@ -31,6 +32,7 @@ EXCLUDE_TESTS_C8S=' -e tests/tests_auto_gateway_initscripts.yml -e tests/tests_bond_deprecated_initscripts.yml -e tests/tests_bond_initscripts.yml +-e tests/tests_bond_cloned_mac_initscripts.yml -e tests/tests_bond_removal_initscripts.yml -e tests/tests_infiniband_nm.yml -e tests/tests_integration_pytest.yml diff --git a/.yamllint.yml b/.yamllint.yml index 55bd703..c7b4845 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -38,3 +38,6 @@ rules: /tests/playbooks/tests_eth_pci_address_match.yml /tests/tasks/setup_802_1x_server.yml /tests/tests_bond_removal_initscripts.yml + /tests/tests_bond_cloned_mac_initscripts.yml + /tests/tests_bridge_cloned_mac_initscripts.yml + /tests/tests_bridge_cloned_mac_nm.yml diff --git a/README.md b/README.md index a0ec968..a9f29fb 100644 --- a/README.md +++ b/README.md @@ -364,6 +364,19 @@ with double quotes and sometimes it is necessary. - For `initscripts`, `mac` is the currently configured MAC address of the device (`HWADDR`). +### `cloned_mac` + +The `cloned_mac` address is optional and allow to specify the strategy to get the default +mac or to set your own mac. The value of the `cloned_mac` address needs to be specified in +hexadecimal notation like `mac` property. Besides explicitly specifying the value as a MAC +address with hexadecimal notation, the following special values are also supported: + +- `default`: honor the default behavior in NetworkManager +- `permanent`: use the permanent MAC address of the device +- `preserve`: don't change the MAC address of the device upon activation +- `random`: generate a randomized value upon each connect +- `stable`: generate a stable, hashed MAC address + ### `mtu` The `mtu` option denotes the maximum transmission unit for the profile's diff --git a/examples/cloned_mac.yml b/examples/cloned_mac.yml new file mode 100644 index 0000000..11bce64 --- /dev/null +++ b/examples/cloned_mac.yml @@ -0,0 +1,30 @@ +# 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 + # define cloned_mac for the bond interface + cloned_mac: "12:23:34:45:56:60" + + # add an ethernet profile to the bond + - name: member1 + state: up + type: ethernet + interface_name: eth1 + controller: bond0 + # define cloned_mac for eth1 interface + cloned_mac: "21:23:34:45:56:60" + + # add a second ethernet profile to the bond without a cloned mac + - 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 31e6e90..ee9b752 100644 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -334,6 +334,8 @@ class IfcfgUtil: ifcfg["ONBOOT"] = "no" ifcfg["DEVICE"] = connection["interface_name"] + if connection["cloned_mac"] != "default": + ifcfg["MACADDR"] = connection["cloned_mac"] if connection["type"] == "ethernet": ifcfg["TYPE"] = "Ethernet" @@ -960,6 +962,18 @@ class NMUtil: else: raise MyError("unsupported type %s" % (connection["type"])) + if connection["cloned_mac"] != "default": + if connection["type"] == "wireless": + s_wireless = self.connection_ensure_setting(con, NM.SettingWireless) + s_wireless.set_property( + NM.SETTING_WIRELESS_CLONED_MAC_ADDRESS, connection["cloned_mac"] + ) + else: + s_wired = self.connection_ensure_setting(con, NM.SettingWired) + s_wired.set_property( + NM.SETTING_WIRED_CLONED_MAC_ADDRESS, connection["cloned_mac"] + ) + if "ethernet" in connection: if connection["ethernet"]["autoneg"] is not None: s_wired = self.connection_ensure_setting(con, NM.SettingWired) diff --git a/module_utils/network_lsr/argument_validator.py b/module_utils/network_lsr/argument_validator.py index df84cac..fb22f93 100644 --- a/module_utils/network_lsr/argument_validator.py +++ b/module_utils/network_lsr/argument_validator.py @@ -578,21 +578,34 @@ class ArgValidatorIP(ArgValidatorStr): class ArgValidatorMac(ArgValidatorStr): - def __init__(self, name, force_len=None, required=False, default_value=None): + def __init__( + self, name, force_len=None, required=False, default_value=None, enum_values=None + ): ArgValidatorStr.__init__(self, name, required, default_value, None) self.force_len = force_len + self.enum_values_mac = enum_values def _validate_impl(self, value, name): v = ArgValidatorStr._validate_impl(self, value, name) + + if self.enum_values_mac is not None and value in self.enum_values_mac: + return v + try: addr = Util.mac_aton(v, self.force_len) except MyError: + enum_ex = "" + if self.enum_values_mac is not None: + enum_ex = " nor one of %s" % (self.enum_values_mac) raise ValidationError( - name, "value '%s' is not a valid MAC address" % (value) + name, "value '%s' is not a valid MAC address%s" % (value, enum_ex) ) if not addr: + enum_ex = "" + if self.enum_values_mac is not None: + enum_ex = " nor one of %s" % (self.enum_values_mac) raise ValidationError( - name, "value '%s' is not a valid MAC address" % (value) + name, "value '%s' is not a valid MAC address%s" % (value, enum_ex) ) return Util.mac_ntoa(addr) @@ -1822,6 +1835,17 @@ class ArgValidator_DictConnection(ArgValidatorDict): ArgValidatorDeprecated("master", deprecated_by="controller"), ArgValidatorStr("interface_name", allow_empty=True), ArgValidatorMac("mac"), + ArgValidatorMac( + "cloned_mac", + enum_values=[ + "default", + "preserve", + "permanent", + "random", + "stable", + ], + default_value="default", + ), ArgValidatorNum( "mtu", val_min=0, val_max=0xFFFFFFFF, default_value=None ), @@ -2577,6 +2601,17 @@ class ArgValidator_ListConnections(ArgValidatorList): "NetworkManger until NM 1.34", ) + if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS and connection["cloned_mac"] in [ + "preserve", + "permanent", + "random", + "stable", + ]: + raise ValidationError.from_connection( + idx, + "Non-MAC argument is not supported by initscripts.", + ) + self.validate_route_tables(connection, idx) diff --git a/tests/playbooks/tests_bond_cloned_mac.yml b/tests/playbooks/tests_bond_cloned_mac.yml new file mode 100644 index 0000000..ff5dbd0 --- /dev/null +++ b/tests/playbooks/tests_bond_cloned_mac.yml @@ -0,0 +1,128 @@ +# 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 + - name: Backup the /etc/resolv.conf for initscript + command: cp -vf /etc/resolv.conf /etc/resolv.conf.bak + when: + - network_provider == "initscripts" + - block: + - name: TEST Add Bond with 2 ports + debug: + msg: "##################################################" + - name: Import network role + 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 + miimon: 110 + ip: + route_metric4: 65535 + cloned_mac: "12:23:34:45:56:60" + # add an ethernet to the bond + - name: "{{ port1_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface1 }}" + controller: "{{ controller_profile }}" + cloned_mac: "12:23:34:45:56:61" + # add a second ethernet to the bond + - name: "{{ port2_profile }}" + state: up + type: ethernet + interface_name: "{{ dhcp_interface2 }}" + controller: "{{ controller_profile }}" + + - name: Verify nmcli cloned-mac-address entry + command: >- + nmcli -f 802-3-ethernet.cloned-mac-address con show {{ item.name }} + register: cloned_mac_address + ignore_errors: yes + changed_when: false + loop: + - { name: "{{ controller_profile }}", mac: "12:23:34:45:56:60"} + - { name: "{{ port1_profile }}", mac: "12:23:34:45:56:61"} + - { name: "{{ port2_profile }}", mac: "--"} + when: network_provider == 'nm' + + - name: > + Assert that cloned-mac-address addresses are configured correctly + assert: + that: + - item.stdout.endswith(item.item.mac) + msg: cloned-mac-address is configured incorrectly + loop: "{{ cloned_mac_address.results }}" + when: network_provider == 'nm' + + - name: Verify the MAC address in {{ controller_profile }} + command: >- + grep 'MACADDR' + /etc/sysconfig/network-scripts/ifcfg-{{ controller_profile }} + register: mac_address_controller + ignore_errors: yes + changed_when: false + when: network_provider == 'initscripts' + + - name: Verify the MAC address in {{ port1_profile }} + command: >- + grep 'MACADDR' + /etc/sysconfig/network-scripts/ifcfg-{{ port1_profile }} + register: mac_address_port1 + ignore_errors: yes + changed_when: false + when: network_provider == 'initscripts' + + - name: Assert that MAC addresses are configured correctly for bonding + interface + assert: + that: + - mac_address_controller.stdout is search("12:23:34:45:56:60") + - mac_address_port1.stdout is search("12:23:34:45:56:61") + msg: the MAC addresses are configured incorrectly for bonding + interface + when: network_provider == 'initscripts' + + 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 + - name: Restore the /etc/resolv.conf for initscript + command: mv -vf /etc/resolv.conf.bak /etc/resolv.conf + when: + - network_provider == "initscripts" + tags: + - "tests::cleanup" diff --git a/tests/playbooks/tests_bridge_cloned_mac.yml b/tests/playbooks/tests_bridge_cloned_mac.yml new file mode 100644 index 0000000..806d2e5 --- /dev/null +++ b/tests/playbooks/tests_bridge_cloned_mac.yml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- name: Test configuring bridges + hosts: all + tasks: + - name: define vars + set_fact: + interface: "LSR-TST-br31" + cloned_mac: "12:23:34:45:56:70" + +- name: Add test bridge + hosts: all + vars: + network_connections: + - name: "{{ interface }}" + interface_name: "{{ interface }}" + state: up + type: bridge + ip: + dhcp4: no + auto6: yes + cloned_mac: "{{ cloned_mac }}" + roles: + - linux-system-roles.network + tasks: + - name: Verify ifcfg MACADDR entry + command: >- + grep ^MACADDR= /etc/sysconfig/network-scripts/ifcfg-{{ interface }} + register: cloned_mac_address_init + changed_when: false + when: + - network_provider == "initscripts" + + - name: Verify nmcli cloned-mac-address entry + command: >- + nmcli -f 802-3-ethernet.cloned-mac-address con show {{ interface }} + register: cloned_mac_address_nm + ignore_errors: yes + changed_when: false + when: + - network_provider == "nm" + + - name: Assert that cloned-mac-address addresses are configured correctly + assert: + that: + - >- + cloned_mac_address_init.stdout is not defined or + cloned_mac_address_init.stdout.find(cloned_mac) != -1 + - >- + cloned_mac_address_nm.stdout is not defined or + cloned_mac_address_nm.stdout.find(cloned_mac) != -1 + msg: "cloned-mac-address is configured incorrectly" + +- import_playbook: down_profile+delete_interface.yml + vars: + profile: "{{ interface }}" + +- import_playbook: remove_profile.yml + vars: + profile: "{{ interface }}" diff --git a/tests/tests_bond_cloned_mac_initscripts.yml b/tests/tests_bond_cloned_mac_initscripts.yml new file mode 100644 index 0000000..e26593f --- /dev/null +++ b/tests/tests_bond_cloned_mac_initscripts.yml @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-3-Clause +# This file was generated by ensure_provider_tests.py +--- +- hosts: all + name: Run playbook 'playbooks/tests_bond_cloned_mac.yml' with initscripts as provider + tasks: + - include_tasks: tasks/el_repo_setup.yml + - name: Set network provider to 'initscripts' + set_fact: + network_provider: initscripts + tags: + - always + +- import_playbook: playbooks/tests_bond_cloned_mac.yml + when: (ansible_distribution in ['CentOS','RedHat'] and + ansible_distribution_major_version | int < 9) diff --git a/tests/tests_bond_cloned_mac_nm.yml b/tests/tests_bond_cloned_mac_nm.yml new file mode 100644 index 0000000..8ce6d81 --- /dev/null +++ b/tests/tests_bond_cloned_mac_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_cloned_mac.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_cloned_mac.yml + when: + - ansible_distribution_major_version != '6' diff --git a/tests/tests_bridge_cloned_mac_initscripts.yml b/tests/tests_bridge_cloned_mac_initscripts.yml new file mode 100644 index 0000000..3f69cd3 --- /dev/null +++ b/tests/tests_bridge_cloned_mac_initscripts.yml @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-3-Clause +# This file was generated by ensure_provider_tests.py +--- +- hosts: all + name: Run playbook 'playbooks/tests_bridge_cloned_mac.yml' with initscripts as provider + tasks: + - include_tasks: tasks/el_repo_setup.yml + - name: Set network provider to 'initscripts' + set_fact: + network_provider: initscripts + tags: + - always + +- import_playbook: playbooks/tests_bridge_cloned_mac.yml + when: (ansible_distribution in ['CentOS','RedHat'] and + ansible_distribution_major_version | int < 9) diff --git a/tests/tests_bridge_cloned_mac_nm.yml b/tests/tests_bridge_cloned_mac_nm.yml new file mode 100644 index 0000000..b570414 --- /dev/null +++ b/tests/tests_bridge_cloned_mac_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_bridge_cloned_mac.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_bridge_cloned_mac.yml + when: + - ansible_distribution_major_version != '6' diff --git a/tests/unit/test_network_connections.py b/tests/unit/test_network_connections.py index bef7914..95c9437 100644 --- a/tests/unit/test_network_connections.py +++ b/tests/unit/test_network_connections.py @@ -202,6 +202,7 @@ class TestValidator(Python26CompatTestCase): "dns_search": [], }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -592,6 +593,7 @@ class TestValidator(Python26CompatTestCase): "dns_search": [], }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -650,6 +652,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -702,6 +705,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -798,6 +802,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": "52:54:00:44:9f:ba", + "cloned_mac": "default", "controller": None, "ieee802_1x": None, "wireless": None, @@ -866,6 +871,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -943,6 +949,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "controller": None, "ieee802_1x": None, "wireless": None, @@ -1043,6 +1050,7 @@ class TestValidator(Python26CompatTestCase): "dns": [], }, "mac": "52:54:00:44:9f:ba", + "cloned_mac": "default", "controller": None, "ieee802_1x": None, "wireless": None, @@ -1107,6 +1115,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "controller": None, "ieee802_1x": None, "wireless": None, @@ -1185,6 +1194,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -1263,6 +1273,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -1371,6 +1382,7 @@ class TestValidator(Python26CompatTestCase): "dns": [], }, "mac": "52:54:00:44:9f:ba", + "cloned_mac": "default", "controller": None, "ieee802_1x": None, "wireless": None, @@ -1435,6 +1447,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "controller": None, "ieee802_1x": None, "wireless": None, @@ -1520,6 +1533,7 @@ class TestValidator(Python26CompatTestCase): "dns": [], }, "mac": "33:24:10:24:2f:b9", + "cloned_mac": "default", "controller": None, "ieee802_1x": None, "wireless": None, @@ -1578,6 +1592,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "macvlan": {"mode": "bridge", "promiscuous": True, "tap": False}, "controller": None, "ieee802_1x": None, @@ -1637,6 +1652,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "macvlan": {"mode": "passthru", "promiscuous": False, "tap": True}, "controller": None, "ieee802_1x": None, @@ -1730,6 +1746,7 @@ class TestValidator(Python26CompatTestCase): "rule_append_only": False, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -1774,6 +1791,7 @@ class TestValidator(Python26CompatTestCase): "rule_append_only": False, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": "prod2", "ieee802_1x": None, @@ -1869,6 +1887,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -1949,6 +1968,7 @@ class TestValidator(Python26CompatTestCase): "routing_rule": [], }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2012,6 +2032,7 @@ class TestValidator(Python26CompatTestCase): "dns_search": [], }, "mac": "aa:bb:cc:dd:ee:ff", + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2063,6 +2084,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2142,6 +2164,7 @@ class TestValidator(Python26CompatTestCase): "rule_append_only": False, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2186,6 +2209,7 @@ class TestValidator(Python26CompatTestCase): "rule_append_only": False, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": "6643-controller", "ieee802_1x": None, @@ -2246,6 +2270,7 @@ class TestValidator(Python26CompatTestCase): "rule_append_only": False, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2324,6 +2349,7 @@ class TestValidator(Python26CompatTestCase): }, "mac": "11:22:33:44:55:66:77:88:99:00:" "11:22:33:44:55:66:77:88:99:00", + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2406,6 +2432,7 @@ class TestValidator(Python26CompatTestCase): "rule_append_only": False, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2450,6 +2477,7 @@ class TestValidator(Python26CompatTestCase): }, "mac": "11:22:33:44:55:66:77:88:99:00:" "11:22:33:44:55:66:77:88:99:00", + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2574,6 +2602,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2683,6 +2712,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -2802,6 +2832,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": { @@ -2883,6 +2914,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": { @@ -2964,6 +2996,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": { @@ -3043,6 +3076,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": None, @@ -3112,6 +3146,7 @@ class TestValidator(Python26CompatTestCase): "dhcp4_send_hostname": None, }, "mac": None, + "cloned_mac": "default", "match": {}, "controller": None, "ieee802_1x": {