From: Viktor Tikkanen Date: Thu, 9 May 2019 08:14:53 +0000 (+0300) Subject: Add seed code for os-net-config X-Git-Url: https://gerrit.akraino.org/r/gitweb?p=ta%2Fos-net-config.git;a=commitdiff_plain;h=ea37b033a88181deab9b7609acbaddf815decd06 Add seed code for os-net-config Change-Id: I7a839040f4ab4ed7652f2bd463c0e3256bdd8cde Signed-off-by: Viktor Tikkanen --- diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..1200d52 --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.akraino.org +port=29418 +project=rec/os-net-config +defaultremote=origin diff --git a/0001-initial.patch b/0001-initial.patch new file mode 100644 index 0000000..9ae6d24 --- /dev/null +++ b/0001-initial.patch @@ -0,0 +1,630 @@ +diff --git a/etc/os-net-config/samples/ovs_dpdk.json b/etc/os-net-config/samples/ovs_dpdk.json +index 5c84044..1dc523d 100644 +--- a/etc/os-net-config/samples/ovs_dpdk.json ++++ b/etc/os-net-config/samples/ovs_dpdk.json +@@ -9,6 +9,7 @@ + "driver": "igb_uio", + "mtu": 8192, + "rx_queue": 4, ++ "dpdk_lsc_interrupt": true, + "members": [ + { + "type": "interface", +diff --git a/etc/os-net-config/samples/ovs_dpdk.yaml b/etc/os-net-config/samples/ovs_dpdk.yaml +index 81aa212..9f0b0d3 100644 +--- a/etc/os-net-config/samples/ovs_dpdk.yaml ++++ b/etc/os-net-config/samples/ovs_dpdk.yaml +@@ -22,6 +22,7 @@ network_config: + # should be less than the PMD cores as each queue will have one PMD + # thread (CPU) associated with it. + rx_queue: 4 ++ dpdk_lsc_interrupt: true + members: + - type: interface + name: nic2 +diff --git a/etc/os-net-config/samples/ovs_dpdk_bond.json b/etc/os-net-config/samples/ovs_dpdk_bond.json +index 410d459..02948c1 100644 +--- a/etc/os-net-config/samples/ovs_dpdk_bond.json ++++ b/etc/os-net-config/samples/ovs_dpdk_bond.json +@@ -8,6 +8,7 @@ + "name" : "dpdkbond0", + "mtu" : 9000, + "rx_queue": 4, ++ "dpdk_lsc_interrupt": true, + "members": [ + { + "type" : "ovs_dpdk_port", +diff --git a/etc/os-net-config/samples/ovs_dpdk_bond.yaml b/etc/os-net-config/samples/ovs_dpdk_bond.yaml +index 17a73a3..896ca79 100644 +--- a/etc/os-net-config/samples/ovs_dpdk_bond.yaml ++++ b/etc/os-net-config/samples/ovs_dpdk_bond.yaml +@@ -23,6 +23,7 @@ network_config: + # than the number of PMD cores, as each queue will have one PMD thread + # (CPU) associated with it. + rx_queue: 4 ++ dpdk_lsc_interrupt: true + members: + - + type: ovs_dpdk_port +diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py +index 609d5dc..0523bfc 100644 +--- a/os_net_config/__init__.py ++++ b/os_net_config/__init__.py +@@ -253,7 +253,7 @@ class NetConfig(object): + msg = 'running ifdown on %s: %s' % (iftype, interface) + self.execute(msg, '/sbin/ifdown', interface, check_exit_code=False) + +- def ifup(self, interface, iftype='interface'): ++ def ifup(self, interface, iftype='interface', check_exit_code=True): + """Run 'ifup' on the specified interface + + If a failure occurs when bringing up the interface it will be saved +@@ -265,7 +265,7 @@ class NetConfig(object): + """ + msg = 'running ifup on %s: %s' % (iftype, interface) + try: +- self.execute(msg, '/sbin/ifup', interface) ++ self.execute(msg, '/sbin/ifup', interface, check_exit_code=check_exit_code) + except processutils.ProcessExecutionError as e: + self.errors.append(e) + +diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py +index 3a82597..3fd7eb5 100644 +--- a/os_net_config/impl_ifcfg.py ++++ b/os_net_config/impl_ifcfg.py +@@ -18,10 +18,13 @@ import glob + import logging + import os + import re ++import time + ++from netifaces import interfaces + import os_net_config + from os_net_config import objects + from os_net_config import utils ++from oslo_concurrency import processutils + + + logger = logging.getLogger(__name__) +@@ -162,6 +165,10 @@ class IfcfgNetConfig(os_net_config.NetConfig): + else: + if base_opt.linux_bond_name: + data += "PHYSDEV=%s\n" % base_opt.linux_bond_name ++ elif base_opt.device: ++ # vlan on OVS bridge with device, create linux vlan ++ data += "VLAN=yes\n" ++ data += "PHYSDEV=%s\n" % base_opt.device + elif isinstance(base_opt, objects.IvsInterface): + data += "TYPE=IVSIntPort\n" + elif isinstance(base_opt, objects.NfvswitchInternal): +@@ -187,9 +194,11 @@ class IfcfgNetConfig(os_net_config.NetConfig): + data += "NFVSWITCH_BRIDGE=%s\n" % base_opt.nfvswitch_bridge_name + if base_opt.ovs_port: + if not isinstance(base_opt, objects.LinuxTeam): +- data += "DEVICETYPE=ovs\n" ++ if not (isinstance(base_opt, objects.Vlan) ++ and base_opt.device): ++ data += "DEVICETYPE=ovs\n" + if base_opt.bridge_name: +- if isinstance(base_opt, objects.Vlan): ++ if isinstance(base_opt, objects.Vlan) and not base_opt.device: + data += "TYPE=OVSIntPort\n" + data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name + data += "OVS_OPTIONS=\"tag=%s\"\n" % base_opt.vlan_id +@@ -313,6 +322,10 @@ class IfcfgNetConfig(os_net_config.NetConfig): + data += "RX_QUEUE=%i\n" % base_opt.rx_queue + ovs_extra.append("set Interface $DEVICE " + + "options:n_rxq=$RX_QUEUE") ++ if base_opt.dpdk_lsc_interrupt: ++ data += "DPDK_LSC_INTERRUPT=true\n" ++ ovs_extra.append("set Interface $DEVICE " + ++ "options:dpdk-lsc-interrupt=$DPDK_LSC_INTERRUPT") + elif isinstance(base_opt, objects.OvsDpdkBond): + ovs_extra.extend(base_opt.ovs_extra) + # Referring to bug:1643026, the below commenting of the interfaces, +@@ -349,6 +362,11 @@ class IfcfgNetConfig(os_net_config.NetConfig): + for member in base_opt.members: + ovs_extra.append("set Interface %s options:n_rxq=" + "$RX_QUEUE" % member.name) ++ if base_opt.dpdk_lsc_interrupt: ++ data += "DPDK_LSC_INTERRUPT=true\n" ++ for member in base_opt.members: ++ ovs_extra.append("set Interface %s options:dpdk-lsc-interrupt=" ++ "$DPDK_LSC_INTERRUPT" % member.name) + if base_opt.ovs_options: + data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options + ovs_extra.extend(base_opt.ovs_extra) +@@ -1043,14 +1061,27 @@ class IfcfgNetConfig(os_net_config.NetConfig): + stop_dhclient_process(interface) + + for interface in restart_interfaces: +- self.ifup(interface) ++ check_exit_code = True ++ if interface not in interfaces(): ++ # Most DPDK drivers do not generate 'netdev' interfaces ++ logger.info('Device %s does not exist' % interface) ++ check_exit_code = False ++ self.ifup(interface, check_exit_code=check_exit_code) + + for linux_bond in restart_linux_bonds: + self.ifup(linux_bond) + + for bond in self.bond_primary_ifaces: +- self.ovs_appctl('bond/set-active-slave', bond, +- self.bond_primary_ifaces[bond]) ++ for i in range(61): ++ try: ++ self.ovs_appctl('bond/set-active-slave', bond, ++ self.bond_primary_ifaces[bond]) ++ except processutils.ProcessExecutionError: ++ if i >= 60: ++ raise ++ time.sleep(5) ++ continue ++ break + + if ivs_uplinks or ivs_interfaces: + logger.info("Attach to ivs with " +diff --git a/os_net_config/objects.py b/os_net_config/objects.py +index 417f34a..1b321f3 100644 +--- a/os_net_config/objects.py ++++ b/os_net_config/objects.py +@@ -993,7 +993,7 @@ class OvsDpdkPort(_BaseOpts): + 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, +- rx_queue=None): ++ rx_queue=None, dpdk_lsc_interrupt=False): + + super(OvsDpdkPort, self).__init__(name, use_dhcp, use_dhcpv6, + addresses, routes, mtu, primary, +@@ -1005,6 +1005,7 @@ class OvsDpdkPort(_BaseOpts): + self.ovs_extra = format_ovs_extra(self, ovs_extra) + self.driver = driver + self.rx_queue = rx_queue ++ self.dpdk_lsc_interrupt = dpdk_lsc_interrupt + + @staticmethod + def from_json(json): +@@ -1047,6 +1048,11 @@ class OvsDpdkPort(_BaseOpts): + raise InvalidConfigException(msg) + + rx_queue = json.get('rx_queue', None) ++ ++ dpdk_lsc_interrupt = strutils.bool_from_string(str(json.get('dpdk_lsc_interrupt', False))) ++ if not dpdk_lsc_interrupt and utils.is_mellanox(utils.get_pci_address(members[0].name, None), None): ++ dpdk_lsc_interrupt = True ++ + ovs_options = json.get('ovs_options', []) + ovs_options = ['options:%s' % opt for opt in ovs_options] + ovs_extra = json.get('ovs_extra', []) +@@ -1060,7 +1066,8 @@ class OvsDpdkPort(_BaseOpts): + dns_servers=dns_servers, + nm_controlled=nm_controlled, members=members, + driver=driver, ovs_options=ovs_options, +- ovs_extra=ovs_extra, rx_queue=rx_queue) ++ ovs_extra=ovs_extra, rx_queue=rx_queue, ++ dpdk_lsc_interrupt=dpdk_lsc_interrupt) + + + class OvsDpdkBond(_BaseOpts): +@@ -1070,7 +1077,8 @@ class OvsDpdkBond(_BaseOpts): + routes=None, mtu=None, primary=False, members=None, + ovs_options=None, ovs_extra=None, nic_mapping=None, + persist_mapping=False, defroute=True, dhclient_args=None, +- dns_servers=None, nm_controlled=False, rx_queue=None): ++ dns_servers=None, nm_controlled=False, rx_queue=None, ++ dpdk_lsc_interrupt=False): + super(OvsDpdkBond, self).__init__(name, use_dhcp, use_dhcpv6, + addresses, routes, mtu, primary, + nic_mapping, persist_mapping, +@@ -1080,6 +1088,7 @@ class OvsDpdkBond(_BaseOpts): + self.ovs_options = ovs_options + self.ovs_extra = format_ovs_extra(self, ovs_extra) + self.rx_queue = rx_queue ++ self.dpdk_lsc_interrupt = dpdk_lsc_interrupt + + for member in self.members: + if member.primary: +@@ -1127,6 +1136,11 @@ class OvsDpdkBond(_BaseOpts): + msg = 'Members must be a list.' + raise InvalidConfigException(msg) + ++ dpdk_lsc_interrupt = strutils.bool_from_string(str(json.get('dpdk_lsc_interrupt', False))) ++ if not dpdk_lsc_interrupt and \ ++ utils.is_mellanox(utils.get_pci_address(members[0].members[0].name, None), None): ++ dpdk_lsc_interrupt = True ++ + return OvsDpdkBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, + addresses=addresses, routes=routes, mtu=mtu, + members=members, ovs_options=ovs_options, +@@ -1134,7 +1148,8 @@ class OvsDpdkBond(_BaseOpts): + persist_mapping=persist_mapping, + defroute=defroute, dhclient_args=dhclient_args, + dns_servers=dns_servers, +- nm_controlled=nm_controlled, rx_queue=rx_queue) ++ nm_controlled=nm_controlled, rx_queue=rx_queue, ++ dpdk_lsc_interrupt=dpdk_lsc_interrupt) + + + class VppInterface(_BaseOpts): +diff --git a/os_net_config/schema.yaml b/os_net_config/schema.yaml +index 5060a34..62aeb41 100644 +--- a/os_net_config/schema.yaml ++++ b/os_net_config/schema.yaml +@@ -529,6 +529,8 @@ definitions: + $ref: "#/definitions/ovs_extra_or_param" + rx_queue: + $ref: "#/definitions/int_or_param" ++ dpdk_lsc_interrupt: ++ $ref: "#/definitions/bool_or_param" + # common options: + use_dhcp: + $ref: "#/definitions/bool_or_param" +@@ -581,6 +583,8 @@ definitions: + $ref: "#/definitions/ovs_extra_or_param" + rx_queue: + $ref: "#/definitions/int_or_param" ++ dpdk_lsc_interrupt: ++ $ref: "#/definitions/bool_or_param" + # common options: + use_dhcp: + $ref: "#/definitions/bool_or_param" +diff --git a/os_net_config/tests/test_cli.py b/os_net_config/tests/test_cli.py +index 32f9395..338f946 100644 +--- a/os_net_config/tests/test_cli.py ++++ b/os_net_config/tests/test_cli.py +@@ -17,10 +17,12 @@ + import os.path + import sys + ++from mock import patch + import os_net_config + from os_net_config import cli + from os_net_config import impl_ifcfg + from os_net_config.tests import base ++from os_net_config.tests.test_utils import _PCI_OUTPUT + import six + + +@@ -31,6 +33,22 @@ SAMPLE_BASE = os.path.join(REALPATH, '../../', 'etc', + + class TestCli(base.TestCase): + ++ def setUp(self): ++ patcher_get_pci_address = patch('os_net_config.utils.get_pci_address', ++ return_value=(_PCI_OUTPUT, None)) ++ self.mock_get_pci_address = patcher_get_pci_address.start() ++ self.addCleanup(patcher_get_pci_address.stop) ++ ++ interface_list = ['em1', 'em2', 'em3', 'vlan16', 'nic2', 'nic3', ++ 'bond0', 'bond1', 'dpdk0', 'dpdkbond0', ++ 'api201', 'storage202'] ++ patcher_interfaces = patch('os_net_config.impl_ifcfg.interfaces', ++ return_value=interface_list) ++ self.mock_interfaces = patcher_interfaces.start() ++ self.addCleanup(patcher_interfaces.stop) ++ ++ super(TestCli, self).setUp() ++ + def run_cli(self, argstr, exitcodes=(0,)): + orig = sys.stdout + orig_stderr = sys.stderr +diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py +index 337eeb3..a0abf93 100644 +--- a/os_net_config/tests/test_impl_ifcfg.py ++++ b/os_net_config/tests/test_impl_ifcfg.py +@@ -19,6 +19,7 @@ import tempfile + + from oslo_concurrency import processutils + ++from mock import patch + import os_net_config + from os_net_config import impl_ifcfg + from os_net_config import NetConfig +@@ -260,6 +261,13 @@ OVS_OPTIONS="tag=5" + BOOTPROTO=none + """ + ++_LINUX_VLAN_OVS_BRIDGE = _BASE_VLAN_OVS + """VLAN=yes ++PHYSDEV=em1 ++TYPE=OVSPort ++OVS_BRIDGE=br-ctlplane ++BOOTPROTO=none ++""" ++ + _VLAN_LINUX_BRIDGE = _BASE_VLAN_OVS + """VLAN=yes + PHYSDEV=em1 + BRIDGE=br-ctlplane +@@ -719,7 +727,7 @@ class TestIfcfgNetConfig(base.TestCase): + self.assertEqual(_VLAN_NO_IP, self.get_vlan_config('vlan5')) + + def test_add_vlan_ovs(self): +- vlan = objects.Vlan('em1', 5) ++ vlan = objects.Vlan(None, 5) + vlan.ovs_port = True + self.provider.add_vlan(vlan) + self.assertEqual(_VLAN_OVS, self.get_vlan_config('vlan5')) +@@ -731,13 +739,21 @@ class TestIfcfgNetConfig(base.TestCase): + self.assertEqual(expected, self.get_vlan_config('vlan5')) + + def test_add_ovs_bridge_with_vlan(self): +- vlan = objects.Vlan('em1', 5) ++ vlan = objects.Vlan(None, 5) + bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True, + members=[vlan]) + self.provider.add_vlan(vlan) + self.provider.add_bridge(bridge) + self.assertEqual(_VLAN_OVS_BRIDGE, self.get_vlan_config('vlan5')) + ++ def test_add_ovs_bridge_with_linux_vlan(self): ++ vlan = objects.Vlan('em1', 5) ++ bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True, ++ members=[vlan]) ++ self.provider.add_vlan(vlan) ++ self.provider.add_bridge(bridge) ++ self.assertEqual(_LINUX_VLAN_OVS_BRIDGE, self.get_vlan_config('vlan5')) ++ + def test_add_linux_bridge_with_vlan(self): + vlan = objects.Vlan('em1', 5) + bridge = objects.LinuxBridge('br-ctlplane', use_dhcp=True, +@@ -932,13 +948,14 @@ OVS_EXTRA="set Interface $DEVICE options:dpdk-devargs=0000:00:09.0" + 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): ++ def test_network_ovs_dpdk_bridge_and_port_with_mtu_rxqueue_dpdklscinterrupt(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, rx_queue=4) ++ mtu=9000, rx_queue=4, ++ dpdk_lsc_interrupt='true') + bridge = objects.OvsUserBridge('br-link', members=[dpdk_port]) + + def test_bind_dpdk_interfaces(ifname, driver, noop): +@@ -970,10 +987,12 @@ DEVICETYPE=ovs + TYPE=OVSDPDKPort + OVS_BRIDGE=br-link + RX_QUEUE=4 ++DPDK_LSC_INTERRUPT=true + MTU=9000 + OVS_EXTRA="set Interface $DEVICE options:dpdk-devargs=0000:00:09.0 \ + -- set Interface $DEVICE mtu_request=$MTU \ +--- set Interface $DEVICE options:n_rxq=$RX_QUEUE" ++-- set Interface $DEVICE options:n_rxq=$RX_QUEUE \ ++-- set Interface $DEVICE options:dpdk-lsc-interrupt=$DPDK_LSC_INTERRUPT" + """ + self.assertEqual(br_link_config, + self.provider.bridge_data['br-link']) +@@ -1068,7 +1087,8 @@ OVS_EXTRA="set Interface dpdk0 options:dpdk-devargs=0000:00:08.0 \ + iface1 = objects.Interface(name='nic3') + dpdk1 = objects.OvsDpdkPort(name='dpdk1', members=[iface1]) + bond = objects.OvsDpdkBond('dpdkbond0', rx_queue=4, +- members=[dpdk0, dpdk1]) ++ members=[dpdk0, dpdk1], ++ dpdk_lsc_interrupt='true') + bridge = objects.OvsUserBridge('br-link', members=[bond]) + + def test_bind_dpdk_interfaces(ifname, driver, noop): +@@ -1093,10 +1113,13 @@ TYPE=OVSDPDKBond + OVS_BRIDGE=br-link + BOND_IFACES="dpdk0 dpdk1" + RX_QUEUE=4 ++DPDK_LSC_INTERRUPT=true + OVS_EXTRA="set Interface dpdk0 options:dpdk-devargs=0000:00:08.0 \ + -- set Interface dpdk1 options:dpdk-devargs=0000:00:09.0 \ + -- set Interface dpdk0 options:n_rxq=$RX_QUEUE \ +--- set Interface dpdk1 options:n_rxq=$RX_QUEUE" ++-- set Interface dpdk1 options:n_rxq=$RX_QUEUE \ ++-- set Interface dpdk0 options:dpdk-lsc-interrupt=$DPDK_LSC_INTERRUPT \ ++-- set Interface dpdk1 options:dpdk-lsc-interrupt=$DPDK_LSC_INTERRUPT" + """ + self.assertEqual(dpdk_bond_config, + self.get_interface_config('dpdkbond0')) +@@ -1110,7 +1133,8 @@ OVS_EXTRA="set Interface dpdk0 options:dpdk-devargs=0000:00:08.0 \ + iface1 = objects.Interface(name='nic3') + dpdk1 = objects.OvsDpdkPort(name='dpdk1', members=[iface1]) + bond = objects.OvsDpdkBond('dpdkbond0', rx_queue=4, mtu=9000, +- members=[dpdk0, dpdk1]) ++ members=[dpdk0, dpdk1], ++ dpdk_lsc_interrupt='true') + bridge = objects.OvsUserBridge('br-link', members=[bond]) + + def test_bind_dpdk_interfaces(ifname, driver, noop): +@@ -1135,13 +1159,16 @@ TYPE=OVSDPDKBond + OVS_BRIDGE=br-link + BOND_IFACES="dpdk0 dpdk1" + RX_QUEUE=4 ++DPDK_LSC_INTERRUPT=true + MTU=9000 + OVS_EXTRA="set Interface dpdk0 options:dpdk-devargs=0000:00:08.0 \ + -- set Interface dpdk1 options:dpdk-devargs=0000:00:09.0 \ + -- set Interface dpdk0 mtu_request=$MTU \ + -- set Interface dpdk1 mtu_request=$MTU \ + -- set Interface dpdk0 options:n_rxq=$RX_QUEUE \ +--- set Interface dpdk1 options:n_rxq=$RX_QUEUE" ++-- set Interface dpdk1 options:n_rxq=$RX_QUEUE \ ++-- set Interface dpdk0 options:dpdk-lsc-interrupt=$DPDK_LSC_INTERRUPT \ ++-- set Interface dpdk1 options:dpdk-lsc-interrupt=$DPDK_LSC_INTERRUPT" + """ + self.assertEqual(dpdk_bond_config, + self.get_interface_config('dpdkbond0')) +@@ -1150,6 +1177,11 @@ OVS_EXTRA="set Interface dpdk0 options:dpdk-devargs=0000:00:08.0 \ + class TestIfcfgNetConfigApply(base.TestCase): + + def setUp(self): ++ interface_list = ['em1', 'em2', 'bond0', 'bond1', 'ib0'] ++ patcher_interfaces = patch('os_net_config.impl_ifcfg.interfaces', ++ return_value=interface_list) ++ self.mock_interfaces = patcher_interfaces.start() ++ self.addCleanup(patcher_interfaces.stop) + super(TestIfcfgNetConfigApply, self).setUp() + self.temp_ifcfg_file = tempfile.NamedTemporaryFile() + self.temp_bond_file = tempfile.NamedTemporaryFile() +diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py +index 1c07eee..39a7884 100644 +--- a/os_net_config/tests/test_objects.py ++++ b/os_net_config/tests/test_objects.py +@@ -17,8 +17,10 @@ + import json + import six + ++from mock import patch + from os_net_config import objects + from os_net_config.tests import base ++from os_net_config.tests.test_utils import _PCI_OUTPUT + from os_net_config import utils + + +@@ -917,6 +919,13 @@ class TestNicMapping(base.TestCase): + # We want to test the function, not the dummy.. + stub_mapped_nics = False + ++ def setUp(self): ++ patcher = patch('os_net_config.utils.get_pci_address', return_value=(_PCI_OUTPUT, None)) ++ self.mock_get_pci_address = patcher.start() ++ self.addCleanup(patcher.stop) ++ ++ super(TestNicMapping, self).setUp() ++ + def tearDown(self): + super(TestNicMapping, self).tearDown() + objects._MAPPED_NICS = None +@@ -1122,6 +1131,13 @@ class TestOvsDpdkBond(base.TestCase): + # We want to test the function, not the dummy.. + stub_mapped_nics = False + ++ def setUp(self): ++ patcher = patch('os_net_config.utils.get_pci_address', return_value=(_PCI_OUTPUT, None)) ++ self.mock_get_pci_address = patcher.start() ++ self.addCleanup(patcher.stop) ++ ++ super(TestOvsDpdkBond, self).setUp() ++ + def _stub_active_nics(self, nics): + def dummy_ordered_active_nics(): + return nics +@@ -1133,6 +1149,7 @@ class TestOvsDpdkBond(base.TestCase): + "type": "ovs_dpdk_bond", + "name": "dpdkbond0", + "use_dhcp": true, ++"dpdk_lsc_interrupt": true, + "members": [ + { + "type": "ovs_dpdk_port", +@@ -1160,6 +1177,7 @@ class TestOvsDpdkBond(base.TestCase): + bond = objects.object_from_json(json.loads(data)) + self.assertEqual("dpdkbond0", bond.name) + self.assertTrue(bond.use_dhcp) ++ self.assertTrue(bond.dpdk_lsc_interrupt) + dpdk_port0 = bond.members[0] + self.assertEqual("dpdk0", dpdk_port0.name) + self.assertEqual("vfio-pci", dpdk_port0.driver) +diff --git a/os_net_config/tests/test_utils.py b/os_net_config/tests/test_utils.py +index e09b6f7..66a0e59 100644 +--- a/os_net_config/tests/test_utils.py ++++ b/os_net_config/tests/test_utils.py +@@ -110,6 +110,8 @@ class TestUtils(base.TestCase): + if 'ethtool' in name: + out = _PCI_OUTPUT + return out, None ++ if 'lspci' in name: ++ return '', None + self.stubs.Set(processutils, 'execute', test_execute) + pci = utils.get_pci_address('nic2', False) + self.assertEqual('0000:00:19.0', pci) +@@ -153,6 +155,8 @@ class TestUtils(base.TestCase): + if 'ethtool' in name: + out = _PCI_OUTPUT + return out, None ++ if 'lspci' in name: ++ return '', None + if 'driverctl' in name: + return None, None + +@@ -171,6 +175,8 @@ class TestUtils(base.TestCase): + if 'ethtool' in name: + out = _PCI_OUTPUT + return out, None ++ if 'lspci' in name: ++ return '', None + if 'driverctl' in name: + return None, 'Error' + +diff --git a/os_net_config/tests/test_validator.py b/os_net_config/tests/test_validator.py +index 7991d9f..fd33ca2 100644 +--- a/os_net_config/tests/test_validator.py ++++ b/os_net_config/tests/test_validator.py +@@ -264,6 +264,7 @@ class TestDeviceTypes(base.TestCase): + "name": "dpdkbond0", + "mtu": 9000, + "rx_queue": 4, ++ "dpdk_lsc_interrupt": "true", + "members": [{ + "type": "ovs_dpdk_port", + "name": "dpdk0", +diff --git a/os_net_config/utils.py b/os_net_config/utils.py +index 27e888d..98e2741 100644 +--- a/os_net_config/utils.py ++++ b/os_net_config/utils.py +@@ -216,6 +216,17 @@ def bind_dpdk_interfaces(ifname, driver, noop): + pci_address = get_pci_address(ifname, noop) + if not noop: + if pci_address: ++ if is_mellanox(pci_address, noop): ++ # Mellanox is binded only with dpdk-devargs and does not need ++ # vfio-pci like e.g. Intel Niantic. Just update DPDK map here. ++ try: ++ mac_address = interface_mac(ifname) ++ _update_dpdk_map(ifname, pci_address, mac_address, driver) ++ except Exception as exp: ++ logger.info('DPDK map update failed: {}'.format(exp)) ++ raise ++ return ++ + # modbprobe of the driver has to be done before binding. + # for reboots, puppet will add the modprobe to /etc/rc.modules + if 'vfio-pci' in driver: +@@ -252,6 +263,19 @@ def bind_dpdk_interfaces(ifname, driver, noop): + {'name': ifname, 'driver': driver}) + + ++def is_mellanox(pci_address, noop): ++ if not noop: ++ try: ++ file_path = '/sys/bus/pci/devices/{}/vendor'.format(pci_address) ++ with open(file_path, 'r') as vendor_file: ++ if '0x15b3' in vendor_file.read(): ++ return True ++ else: ++ return False ++ except Exception: ++ return False ++ ++ + def get_pci_address(ifname, noop): + # TODO(skramaja): Validate if the given interface supports dpdk + if not noop: +diff --git a/pylintrc b/pylintrc +new file mode 100644 +index 0000000..81b4f50 +--- /dev/null ++++ b/pylintrc +@@ -0,0 +1,2 @@ ++[MESSAGES CONTROL] ++disable=E1101,F0401,E0211 +diff --git a/tox.ini b/tox.ini +index 1dd9da1..962540a 100644 +--- a/tox.ini ++++ b/tox.ini +@@ -28,6 +28,6 @@ commands = python setup.py build_sphinx + # E123, E125 skipped as they are invalid PEP-8. + + show-source = True +-ignore = E123,E125 ++ignore = E123,E125,E501 + builtins = _ + exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/os-net-config.spec b/os-net-config.spec new file mode 100644 index 0000000..351e7bd --- /dev/null +++ b/os-net-config.spec @@ -0,0 +1,57 @@ +Name: os-net-config +Version: 7.3.0 +Release: 0.20170914.77fe592%{?dist}.1 +Summary: Host network configuration tool + +License: %{_platform_licence} and ASL 2.0 +URL: http://pypi.python.org/pypi/%{name} +Source0: https://files.pythonhosted.org/packages/26/8d/c9ce44fd6a805a4ec56cba59c0a6743fba2230b7b79aa57a48709e94f47c/%{name}-%{version}.tar.gz +Patch0: 0001-initial.patch +Vendor: OpenStack Foundation and %{_platform_vendor} modified + +BuildArch: noarch +BuildRequires: python-setuptools +BuildRequires: python2-devel +BuildRequires: python2-pbr >= 2.0.0 +BuildRequires: python-sphinx +BuildRequires: python-oslo-sphinx + +Requires: python-anyjson >= 0.3.3 +Requires: python-eventlet >= 0.18.2 +Requires: python-oslo-concurrency >= 3.8.0 +Requires: python-oslo-config +Requires: python-oslo-utils >= 3.20.0 +Requires: python-netaddr >= 0.7.13 +Requires: python-netifaces >= 0.10.4 +Requires: python-iso8601 >= 0.1.11 +Requires: python-six >= 1.9.0 +Requires: initscripts +Requires: iproute +Requires: ethtool +Requires: openvswitch +Requires: dhclient +Requires: PyYAML >= 3.10 +Requires: python2-pbr >= 2.0.0 +Requires: python-jsonschema >= 2.0.0 +Requires: driverctl + +%description +Host network configuration tool for OpenStack. + +%prep + +%autosetup -n %{name}-%{version} -p 1 + +%build +%{__python} setup.py build +%{__python} setup.py build_sphinx + +%install +%{__python} setup.py install -O1 --skip-build --root %{buildroot} + +%files +%doc README.rst +%doc LICENSE +%doc doc/build/html +%{_bindir}/os-net-config +%{python_sitelib}/os_net_config*