Add seed code for os-net-config 55/655/1
authorViktor Tikkanen <viktor.tikkanen@nokia.com>
Thu, 9 May 2019 08:14:53 +0000 (11:14 +0300)
committerViktor Tikkanen <viktor.tikkanen@nokia.com>
Thu, 9 May 2019 08:14:53 +0000 (11:14 +0300)
Change-Id: I7a839040f4ab4ed7652f2bd463c0e3256bdd8cde
Signed-off-by: Viktor Tikkanen <viktor.tikkanen@nokia.com>
.gitreview [new file with mode: 0644]
0001-initial.patch [new file with mode: 0644]
LICENSE [new file with mode: 0644]
os-net-config.spec [new file with mode: 0644]

diff --git a/.gitreview b/.gitreview
new file mode 100644 (file)
index 0000000..1200d52
--- /dev/null
@@ -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 (file)
index 0000000..9ae6d24
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..351e7bd
--- /dev/null
@@ -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*