diff --git a/os_net_config/objects.py b/os_net_config/objects.py index bd5171cf..44a00232 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -79,6 +79,32 @@ def _get_required_field(json, name, object_name): return field +def _update_members(json, nic_mapping, persist_mapping): + """Update object's members fields and pass mapping info to each member. + + :param json: dictionary containing object values + :param nic_mapping: mapping of abstractions to actual nic names + :param persist_mapping: bool indicating mapping file should be permanent + :returns members: updated members + """ + members = [] + + members_json = json.get('members') + if members_json: + if isinstance(members_json, list): + for member in members_json: + # If this member already has a nic mapping, don't overwrite it + if not member.get('nic_mapping'): + member.update({'nic_mapping': nic_mapping}) + member.update({'persist_mapping': persist_mapping}) + members.append(object_from_json(member)) + else: + msg = 'Members must be a list.' + raise InvalidConfigException(msg) + + return members + + def _mapped_nics(nic_mapping=None): mapping = nic_mapping or {} global _MAPPED_NICS @@ -458,17 +484,8 @@ class OvsBridge(_BaseOpts): ovs_options = json.get('ovs_options') ovs_extra = json.get('ovs_extra') fail_mode = json.get('ovs_fail_mode', DEFAULT_OVS_BRIDGE_FAIL_MODE) - members = [] - # members - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) return OvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, addresses=addresses, routes=routes, mtu=mtu, @@ -524,17 +541,8 @@ class OvsUserBridge(_BaseOpts): ovs_options = json.get('ovs_options') ovs_extra = json.get('ovs_extra') fail_mode = json.get('ovs_fail_mode', DEFAULT_OVS_BRIDGE_FAIL_MODE) - members = [] - # members - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) return OvsUserBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, addresses=addresses, routes=routes, mtu=mtu, @@ -582,17 +590,8 @@ class LinuxBridge(_BaseOpts): persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) - members = [] - # members - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) return LinuxBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, addresses=addresses, routes=routes, mtu=mtu, @@ -645,17 +644,8 @@ class IvsBridge(_BaseOpts): persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) - members = [] - # members - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) return IvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, addresses=addresses, routes=routes, mtu=mtu, @@ -705,16 +695,7 @@ class NfvswitchBridge(_BaseOpts): dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) - # members - members = [] - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) cpus = '' cpus_json = json.get('cpus') @@ -776,17 +757,8 @@ class LinuxTeam(_BaseOpts): dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) bonding_options = json.get('bonding_options') - members = [] - # members - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) return LinuxTeam(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, addresses=addresses, routes=routes, mtu=mtu, @@ -835,17 +807,8 @@ class LinuxBond(_BaseOpts): dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) bonding_options = json.get('bonding_options') - members = [] - # members - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) return LinuxBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, addresses=addresses, routes=routes, mtu=mtu, @@ -898,17 +861,8 @@ class OvsBond(_BaseOpts): json, include_primary=False) ovs_options = json.get('ovs_options') ovs_extra = json.get('ovs_extra', []) - members = [] - # members - members_json = json.get('members') - if members_json: - if isinstance(members_json, list): - for member in members_json: - members.append(object_from_json(member)) - else: - msg = 'Members must be a list.' - raise InvalidConfigException(msg) + members = _update_members(json, nic_mapping, persist_mapping) return OvsBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, addresses=addresses, routes=routes, mtu=mtu, @@ -1033,6 +987,10 @@ class OvsDpdkPort(_BaseOpts): def from_json(json): name = _get_required_field(json, 'name', 'OvsDpdkPort') # driver name by default will be 'vfio-pci' if not specified + (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, + persist_mapping, defroute, dhclient_args, + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json(json) + driver = json.get('driver') if not driver: driver = 'vfio-pci' @@ -1043,7 +1001,11 @@ class OvsDpdkPort(_BaseOpts): if members_json: if isinstance(members_json, list): if len(members_json) == 1: - iface = object_from_json(members_json[0]) + member = members_json[0] + if not member.get('nic_mapping'): + member.update({'nic_mapping': nic_mapping}) + member.update({'persist_mapping': persist_mapping}) + iface = object_from_json(member) if isinstance(iface, Interface): # TODO(skramaja): Add checks for IP and route not to # be set in the interface part of DPDK Port @@ -1064,9 +1026,15 @@ class OvsDpdkPort(_BaseOpts): ovs_options = json.get('ovs_options', []) ovs_options = ['options:%s' % opt for opt in ovs_options] ovs_extra = json.get('ovs_extra', []) - opts = _BaseOpts.base_opts_from_json(json) - return OvsDpdkPort(name, *opts, members=members, driver=driver, - ovs_options=ovs_options, ovs_extra=ovs_extra) + return OvsDpdkPort(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, + addresses=addresses, routes=routes, mtu=mtu, + primary=primary, nic_mapping=nic_mapping, + persist_mapping=persist_mapping, defroute=defroute, + dhclient_args=dhclient_args, + dns_servers=dns_servers, + nm_controlled=nm_controlled, members=members, + driver=driver, ovs_options=ovs_options, + ovs_extra=ovs_extra) class OvsDpdkBond(_BaseOpts): @@ -1116,6 +1084,9 @@ class OvsDpdkBond(_BaseOpts): if members_json: if isinstance(members_json, list): for member in members_json: + if not member.get('nic_mapping'): + member.update({'nic_mapping': nic_mapping}) + member.update({'persist_mapping': persist_mapping}) obj = object_from_json(member) if isinstance(obj, OvsDpdkPort): members.append(obj) diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index 5d5e3dc0..0d1e1061 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -547,6 +547,17 @@ class TestBond(base.TestCase): interface2 = bridge.members[1] self.assertEqual("em2", interface2.name) + def _stub_active_nics(self, nics): + def dummy_ordered_active_nics(): + return nics + self.stubs.Set(utils, 'ordered_active_nics', dummy_ordered_active_nics) + + def _stub_available_nics(self, nics): + def dummy_ordered_available_nics(): + return nics + self.stubs.Set(utils, 'ordered_available_nics', + dummy_ordered_available_nics) + class TestLinuxTeam(base.TestCase): @@ -931,6 +942,102 @@ class TestNicMapping(base.TestCase): # This only emits a warning, so it should still work self.assertEqual(expected, objects._mapped_nics()) + # Test that mapping file is passed to interface members from parent object + def _test_mapped_nics_with_parent(self, type, name): + self._stub_available_nics(['foo', 'bar']) + mapping = {"nic1": "foo", "nic2": "bar"} + + data = """{ + "members": [{"type": "interface", "name": "nic1"}, + {"type": "interface", "name": "nic2"}] + } + """ + json_output = json.loads(data) + json_output.update({'type': type}) + json_output.update({'name': name}) + json_output.update({'nic_mapping': mapping}) + obj = objects.object_from_json(json_output) + + self.assertEqual("foo", obj.members[0].name) + self.assertEqual("bar", obj.members[1].name) + + def test_mapped_nics_ovs_bond(self): + self._test_mapped_nics_with_parent("ovs_bond", "bond1") + + def test_mapped_nics_linux_bond(self): + self._test_mapped_nics_with_parent("linux_bond", "bond1") + + def test_mapped_nics_ovs_bridge(self): + self._test_mapped_nics_with_parent("ovs_bridge", "br-foo") + + def test_mapped_nics_ovs_user_bridge(self): + self._test_mapped_nics_with_parent("ovs_user_bridge", "br-foo") + + def test_mapped_nics_linux_bridge(self): + self._test_mapped_nics_with_parent("linux_bridge", "br-foo") + + def test_mapped_nics_ivs_bridge(self): + self._test_mapped_nics_with_parent("ivs_bridge", "br-foo") + + def test_mapped_nics_linux_team(self): + self._test_mapped_nics_with_parent("team", "team-foo") + + def test_mapped_nics_bridge_and_bond(self): + self._stub_available_nics(['foo', 'bar']) + mapping = {"nic1": "foo", "nic2": "bar"} + + data = """{ +"type": "ovs_bridge", +"name": "br-foo", +"members": [ + { + "type": "ovs_bond", + "name": "bond0", + "members": [{"type": "interface", "name": "nic1"}, + {"type": "interface", "name": "nic2"}] + } +] +} +""" + json_output = json.loads(data) + json_output.update({'nic_mapping': mapping}) + obj = objects.object_from_json(json_output) + + interface1 = obj.members[0].members[0] + interface2 = obj.members[0].members[1] + self.assertEqual("foo", interface1.name) + self.assertEqual("bar", interface2.name) + + def test_mapped_nics_ovs_dpdk_bond(self): + self._stub_available_nics(['foo', 'bar']) + mapping = {"nic2": "foo", "nic3": "bar"} + + data = """{ +"type": "ovs_dpdk_bond", +"name": "dpdkbond0", +"members": [ + { + "type": "ovs_dpdk_port", + "name": "dpdk0", + "members": [{"type": "interface", "name": "nic2"}] + }, + { + "type": "ovs_dpdk_port", + "name": "dpdk1", + "members": [{"type": "interface", "name": "nic3"}] + } +] +} +""" + json_output = json.loads(data) + json_output.update({'nic_mapping': mapping}) + dpdk_port = objects.object_from_json(json_output) + interface1 = dpdk_port.members[0].members[0] + interface2 = dpdk_port.members[1].members[0] + + self.assertEqual("foo", interface1.name) + self.assertEqual("bar", interface2.name) + class TestOvsDpdkBond(base.TestCase):