diff options
-rw-r--r-- | etc/os-net-config/samples/ovs_dpdk.json | 1 | ||||
-rw-r--r-- | etc/os-net-config/samples/ovs_dpdk.yaml | 6 | ||||
-rw-r--r-- | os_net_config/impl_ifcfg.py | 4 | ||||
-rw-r--r-- | os_net_config/objects.py | 144 | ||||
-rw-r--r-- | os_net_config/tests/test_impl_ifcfg.py | 45 | ||||
-rw-r--r-- | os_net_config/tests/test_objects.py | 107 |
6 files changed, 220 insertions, 87 deletions
diff --git a/etc/os-net-config/samples/ovs_dpdk.json b/etc/os-net-config/samples/ovs_dpdk.json index 37b79e0..5c84044 100644 --- a/etc/os-net-config/samples/ovs_dpdk.json +++ b/etc/os-net-config/samples/ovs_dpdk.json @@ -8,6 +8,7 @@ "name": "dpdk0", "driver": "igb_uio", "mtu": 8192, + "rx_queue": 4, "members": [ { "type": "interface", diff --git a/etc/os-net-config/samples/ovs_dpdk.yaml b/etc/os-net-config/samples/ovs_dpdk.yaml index 47fd3ab..81aa212 100644 --- a/etc/os-net-config/samples/ovs_dpdk.yaml +++ b/etc/os-net-config/samples/ovs_dpdk.yaml @@ -16,6 +16,12 @@ network_config: driver: igb_uio # MTU is optional, used for jumbo frames mtu: 8192 + # rx_queue is optional, used for multi-queue option. It configures the + # maximum number of queues for a physical interface. If not defined, + # the physical interface will have single queue. The number of queues + # should be less than the PMD cores as each queue will have one PMD + # thread (CPU) associated with it. + rx_queue: 4 members: - type: interface name: nic2 diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index d33ebd2..997b695 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -301,6 +301,10 @@ class IfcfgNetConfig(os_net_config.NetConfig): data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name if base_opt.mtu: ovs_extra.append("set Interface $DEVICE mtu_request=$MTU") + if base_opt.rx_queue: + data += "RX_QUEUE=%i\n" % base_opt.rx_queue + ovs_extra.append("set Interface $DEVICE " + + "options:n_rxq=$RX_QUEUE") elif isinstance(base_opt, objects.OvsDpdkBond): ovs_extra.extend(base_opt.ovs_extra) # Referring to bug:1643026, the below commenting of the interfaces, diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 5124a1e..5fe6e49 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -81,6 +81,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 @@ -466,17 +492,8 @@ class OvsBridge(_BaseOpts): if not isinstance(ovs_extra, list): ovs_extra = [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, @@ -534,17 +551,8 @@ class OvsUserBridge(_BaseOpts): if not isinstance(ovs_extra, list): ovs_extra = [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, @@ -592,17 +600,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, @@ -655,17 +654,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, @@ -715,16 +705,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) options = json.get('options') if not options: @@ -778,17 +759,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, @@ -837,17 +809,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, @@ -902,17 +865,8 @@ class OvsBond(_BaseOpts): ovs_extra = json.get('ovs_extra', []) if not isinstance(ovs_extra, list): ovs_extra = [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, @@ -1025,7 +979,8 @@ class OvsDpdkPort(_BaseOpts): routes=None, mtu=None, primary=False, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, dns_servers=None, nm_controlled=False, members=None, - driver='vfio-pci', ovs_options=None, ovs_extra=None): + driver='vfio-pci', ovs_options=None, ovs_extra=None, + rx_queue=None): super(OvsDpdkPort, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, @@ -1036,11 +991,16 @@ class OvsDpdkPort(_BaseOpts): self.ovs_options = ovs_options or [] self.ovs_extra = format_ovs_extra(self, ovs_extra) self.driver = driver + self.rx_queue = rx_queue @staticmethod 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' @@ -1051,7 +1011,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 @@ -1069,14 +1033,21 @@ class OvsDpdkPort(_BaseOpts): msg = 'DPDK Port should have one member as Interface' raise InvalidConfigException(msg) + rx_queue = json.get('rx_queue', None) ovs_options = json.get('ovs_options', []) ovs_options = ['options:%s' % opt for opt in ovs_options] ovs_extra = json.get('ovs_extra', []) if not isinstance(ovs_extra, list): ovs_extra = [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, rx_queue=rx_queue) class OvsDpdkBond(_BaseOpts): @@ -1130,6 +1101,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_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index 2d25a39..80f83f1 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -885,8 +885,47 @@ DNS2=5.6.7.8 self.stubbed_mapped_nics = nic_mapping interface = objects.Interface(name='nic3') + dpdk_port = objects.OvsDpdkPort(name='dpdk0', members=[interface]) + bridge = objects.OvsUserBridge('br-link', members=[dpdk_port]) + + def test_bind_dpdk_interfaces(ifname, driver, noop): + self.assertEqual(ifname, 'eth2') + self.assertEqual(driver, 'vfio-pci') + self.stubs.Set(utils, 'bind_dpdk_interfaces', + test_bind_dpdk_interfaces) + + self.provider.add_ovs_dpdk_port(dpdk_port) + self.provider.add_ovs_user_bridge(bridge) + br_link_config = """# This file is autogenerated by os-net-config +DEVICE=br-link +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +DEVICETYPE=ovs +TYPE=OVSUserBridge +""" + dpdk0_config = """# This file is autogenerated by os-net-config +DEVICE=dpdk0 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +DEVICETYPE=ovs +TYPE=OVSDPDKPort +OVS_BRIDGE=br-link +""" + self.assertEqual(br_link_config, + self.provider.bridge_data['br-link']) + self.assertEqual(dpdk0_config, self.get_interface_config('dpdk0')) + + def test_network_ovs_dpdk_bridge_and_port_with_mtu_rxqueue(self): + nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} + self.stubbed_mapped_nics = nic_mapping + + interface = objects.Interface(name='nic3') dpdk_port = objects.OvsDpdkPort(name='dpdk0', members=[interface], - mtu=9000) + mtu=9000, rx_queue=4) bridge = objects.OvsUserBridge('br-link', members=[dpdk_port]) def test_bind_dpdk_interfaces(ifname, driver, noop): @@ -915,8 +954,10 @@ PEERDNS=no DEVICETYPE=ovs TYPE=OVSDPDKPort OVS_BRIDGE=br-link +RX_QUEUE=4 MTU=9000 -OVS_EXTRA="set Interface $DEVICE mtu_request=$MTU" +OVS_EXTRA="set Interface $DEVICE mtu_request=$MTU \ +-- set Interface $DEVICE options:n_rxq=$RX_QUEUE" """ self.assertEqual(br_link_config, self.provider.bridge_data['br-link']) diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index 1199bb2..eb5adaf 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -621,6 +621,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): @@ -1007,6 +1018,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): |