Merge "Add support for VPP interface"
This commit is contained in:
commit
f26b56c8f2
|
@ -0,0 +1,14 @@
|
|||
{ "network_config": [
|
||||
{
|
||||
"type": "vpp_interface",
|
||||
"name": "nic2",
|
||||
"addresses": [
|
||||
{
|
||||
"ip_netmask": "192.0.2.1/24"
|
||||
}
|
||||
],
|
||||
"uio_driver": "uio_pci_generic",
|
||||
"options": "vlan-strip-offload off"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
network_config:
|
||||
-
|
||||
type: vpp_interface
|
||||
name: nic2
|
||||
addresses:
|
||||
-
|
||||
ip_netmask: 192.0.2.1/24
|
||||
# DPDK poll-mode driver name. Defaults to 'vfio-pci', other possible value
|
||||
# is 'uio_pci_generic'. It is also possible to specify other driver names
|
||||
# such as 'igb_uio', however, it is assumed that any required kernel
|
||||
# modules for those drivers are already loaded when os-net-config is
|
||||
# invoked.
|
||||
uio_driver: uio_pci_generic
|
||||
# Interface options such as vlan stripping and tx/rx transmit queues
|
||||
# specification. Reference for those configurations can
|
||||
# be found at https://wiki.fd.io/view/VPP/Command-line_Arguments
|
||||
# Example: 'vlan-strip-offload on num-rx-queues 3'
|
||||
#options: "vlan-strip-offload off"
|
|
@ -95,6 +95,8 @@ class NetConfig(object):
|
|||
self.add_ovs_dpdk_port(obj)
|
||||
elif isinstance(obj, objects.OvsDpdkBond):
|
||||
self.add_ovs_dpdk_bond(obj)
|
||||
elif isinstance(obj, objects.VppInterface):
|
||||
self.add_vpp_interface(obj)
|
||||
|
||||
def add_interface(self, interface):
|
||||
"""Add an Interface object to the net config object.
|
||||
|
@ -201,6 +203,13 @@ class NetConfig(object):
|
|||
"""
|
||||
raise NotImplementedError("add_ovs_dpdk_bond is not implemented.")
|
||||
|
||||
def add_vpp_interface(self, vpp_interface):
|
||||
"""Add a VppInterface object to the net config object.
|
||||
|
||||
:param vpp_interface: The VppInterface object to add.
|
||||
"""
|
||||
raise NotImplementedError("add_vpp_interface is not implemented.")
|
||||
|
||||
def apply(self, cleanup=False):
|
||||
"""Apply the network configuration.
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@ def nfvswitch_config_path():
|
|||
return "/etc/sysconfig/nfvswitch"
|
||||
|
||||
|
||||
def vpp_config_path():
|
||||
return "/etc/vpp/startup.conf"
|
||||
|
||||
|
||||
def route_config_path(name):
|
||||
return "/etc/sysconfig/network-scripts/route-%s" % name
|
||||
|
||||
|
@ -116,6 +120,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
self.linuxbond_data = {}
|
||||
self.ib_interface_data = {}
|
||||
self.linuxteam_data = {}
|
||||
self.vpp_interface_data = {}
|
||||
self.member_names = {}
|
||||
self.renamed_interfaces = {}
|
||||
self.bond_primary_ifaces = {}
|
||||
|
@ -626,6 +631,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
if ovs_dpdk_bond.routes:
|
||||
self._add_routes(ovs_dpdk_bond.name, ovs_dpdk_bond.routes)
|
||||
|
||||
def add_vpp_interface(self, vpp_interface):
|
||||
"""Add a VppInterface object to the net config object
|
||||
|
||||
:param vpp_interface: The VppInterface object to add
|
||||
"""
|
||||
vpp_interface.pci_dev = utils.get_pci_address(vpp_interface.name,
|
||||
False)
|
||||
vpp_interface.hwaddr = utils.interface_mac(vpp_interface.name)
|
||||
if not self.noop:
|
||||
self.ifdown(vpp_interface.name)
|
||||
remove_ifcfg_config(vpp_interface.name)
|
||||
logger.info('adding vpp interface: %s %s'
|
||||
% (vpp_interface.name, vpp_interface.pci_dev))
|
||||
self.vpp_interface_data[vpp_interface.name] = vpp_interface
|
||||
|
||||
def generate_ivs_config(self, ivs_uplinks, ivs_interfaces):
|
||||
"""Generate configuration content for ivs."""
|
||||
|
||||
|
@ -690,6 +710,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
restart_bridges = []
|
||||
restart_linux_bonds = []
|
||||
restart_linux_teams = []
|
||||
restart_vpp = False
|
||||
update_files = {}
|
||||
all_file_names = []
|
||||
ivs_uplinks = [] # ivs physical uplinks
|
||||
|
@ -698,6 +719,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
nfvswitch_internal_ifaces = [] # nfvswitch internal/management ports
|
||||
stop_dhclient_interfaces = []
|
||||
ovs_needs_restart = False
|
||||
vpp_interfaces = self.vpp_interface_data.values()
|
||||
|
||||
for interface_name, iface_data in self.interface_data.items():
|
||||
route_data = self.route_data.get(interface_name, '')
|
||||
|
@ -906,6 +928,15 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
logger.info('No changes required for InfiniBand iface: %s' %
|
||||
interface_name)
|
||||
|
||||
if self.vpp_interface_data:
|
||||
vpp_path = self.root_dir + vpp_config_path()
|
||||
vpp_config = utils.generate_vpp_config(vpp_path, vpp_interfaces)
|
||||
if utils.diff(vpp_path, vpp_config):
|
||||
restart_vpp = True
|
||||
update_files[vpp_path] = vpp_config
|
||||
else:
|
||||
logger.info('No changes required for VPP')
|
||||
|
||||
if cleanup:
|
||||
for ifcfg_file in glob.iglob(cleanup_pattern()):
|
||||
if ifcfg_file not in all_file_names:
|
||||
|
@ -932,6 +963,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
for bridge in restart_bridges:
|
||||
self.ifdown(bridge, iftype='bridge')
|
||||
|
||||
for vpp_interface in vpp_interfaces:
|
||||
self.ifdown(vpp_interface.name)
|
||||
|
||||
for oldname, newname in self.renamed_interfaces.items():
|
||||
self.ifrename(oldname, newname)
|
||||
|
||||
|
@ -1009,4 +1043,13 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
for vlan in restart_vlans:
|
||||
self.ifup(vlan)
|
||||
|
||||
if not self.noop:
|
||||
if restart_vpp:
|
||||
logger.info('Restarting VPP')
|
||||
utils.restart_vpp(vpp_interfaces)
|
||||
|
||||
if self.vpp_interface_data:
|
||||
logger.info('Updating VPP mapping')
|
||||
utils.update_vpp_mapping(vpp_interfaces)
|
||||
|
||||
return update_files
|
||||
|
|
|
@ -68,6 +68,8 @@ def object_from_json(json):
|
|||
return OvsDpdkPort.from_json(json)
|
||||
elif obj_type == "ovs_dpdk_bond":
|
||||
return OvsDpdkBond.from_json(json)
|
||||
elif obj_type == "vpp_interface":
|
||||
return VppInterface.from_json(json)
|
||||
|
||||
|
||||
def _get_required_field(json, name, object_name):
|
||||
|
@ -1131,3 +1133,57 @@ class OvsDpdkBond(_BaseOpts):
|
|||
defroute=defroute, dhclient_args=dhclient_args,
|
||||
dns_servers=dns_servers,
|
||||
nm_controlled=nm_controlled)
|
||||
|
||||
|
||||
class VppInterface(_BaseOpts):
|
||||
"""Base class for VPP Interface.
|
||||
|
||||
Vector Packet Processing (VPP) is a high performance packet processing
|
||||
stack that runs in user space in Linux. VPP is used as an alternative to
|
||||
kernel networking stack for accelerated network data path. VPP uses DPDK
|
||||
poll-mode drivers to bind system interfaces rather than kernel drivers.
|
||||
VPP bound interfaces are not visible to kernel networking stack, so we
|
||||
must handle them separately.
|
||||
|
||||
The following parameters can be specified in addition to base Interface:
|
||||
- uio_driver: DPDK poll-mode driver name. Defaults to 'vfio-pci', valid
|
||||
values are 'uio_pci_generic' and 'vfio-pci'.
|
||||
- options: Interface options such as vlan stripping and tx/rx transmit
|
||||
queues specification. Defaults to None. Reference for those
|
||||
configurations can be found at
|
||||
https://wiki.fd.io/view/VPP/Command-line_Arguments
|
||||
Example: 'vlan-strip-offload on num-rx-queues 3'
|
||||
|
||||
Note that 'name' attribute is used to indicate the kernel nic that should
|
||||
be bound to VPP. Once VPP binds the interface, a mapping file will be
|
||||
updated with the interface's information, and this file will be used in
|
||||
subsequent runs of os-net-config.
|
||||
"""
|
||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
||||
routes=None, mtu=None, primary=False, nic_mapping=None,
|
||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
||||
dns_servers=None, nm_controlled=False, uio_driver='vfio-pci',
|
||||
options=None):
|
||||
addresses = addresses or []
|
||||
|
||||
super(VppInterface, self).__init__(name, use_dhcp, use_dhcpv6,
|
||||
addresses, routes, mtu, primary,
|
||||
nic_mapping, persist_mapping,
|
||||
defroute, dhclient_args,
|
||||
dns_servers, nm_controlled)
|
||||
self.uio_driver = uio_driver
|
||||
self.options = options
|
||||
# pci_dev contains pci address for the interface, it will be populated
|
||||
# when interface is added to config object. It will be determined
|
||||
# either through ethtool or by looking up the dpdk mapping file.
|
||||
self.pci_dev = None
|
||||
|
||||
@staticmethod
|
||||
def from_json(json):
|
||||
name = _get_required_field(json, 'name', 'VppInterface')
|
||||
uio_driver = json.get('uio_driver', 'vfio-pci')
|
||||
options = json.get('options', '')
|
||||
|
||||
opts = _BaseOpts.base_opts_from_json(json)
|
||||
return VppInterface(name, *opts, uio_driver=uio_driver,
|
||||
options=options)
|
||||
|
|
|
@ -988,3 +988,19 @@ class TestOvsDpdkBond(base.TestCase):
|
|||
self.assertEqual("vfio-pci", dpdk_port1.driver)
|
||||
iface2 = dpdk_port1.members[0]
|
||||
self.assertEqual("eth2", iface2.name)
|
||||
|
||||
|
||||
class TestVppInterface(base.TestCase):
|
||||
def test_vpp_interface_from_json(self):
|
||||
data = """{
|
||||
"type": "vpp_interface",
|
||||
"name": "em1",
|
||||
"uio_driver": "uio_pci_generic",
|
||||
"options": "vlan-strip-offload off"
|
||||
}
|
||||
"""
|
||||
|
||||
vpp_interface = objects.object_from_json(json.loads(data))
|
||||
self.assertEqual("em1", vpp_interface.name)
|
||||
self.assertEqual("uio_pci_generic", vpp_interface.uio_driver)
|
||||
self.assertEqual("vlan-strip-offload off", vpp_interface.options)
|
||||
|
|
|
@ -21,6 +21,7 @@ import shutil
|
|||
import tempfile
|
||||
import yaml
|
||||
|
||||
from os_net_config import objects
|
||||
from os_net_config.tests import base
|
||||
from os_net_config import utils
|
||||
|
||||
|
@ -38,6 +39,33 @@ supports-register-dump: yes
|
|||
supports-priv-flags: no
|
||||
'''
|
||||
|
||||
_VPPCTL_OUTPUT = '''
|
||||
Name Idx State Counter Count
|
||||
GigabitEthernet0/9/0 1 down
|
||||
local0 0 down
|
||||
|
||||
'''
|
||||
|
||||
_INITIAL_VPP_CONFIG = '''
|
||||
unix {
|
||||
nodaemon
|
||||
log /tmp/vpp.log
|
||||
full-coredump
|
||||
}
|
||||
|
||||
|
||||
api-trace {
|
||||
on
|
||||
}
|
||||
|
||||
api-segment {
|
||||
gid vpp
|
||||
}
|
||||
|
||||
dpdk {
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class TestUtils(base.TestCase):
|
||||
|
||||
|
@ -83,7 +111,7 @@ class TestUtils(base.TestCase):
|
|||
out = _PCI_OUTPUT
|
||||
return out, None
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
pci = utils._get_pci_address('nic2', False)
|
||||
pci = utils.get_pci_address('nic2', False)
|
||||
self.assertEqual('0000:00:19.0', pci)
|
||||
|
||||
def test_get_pci_address_exception(self):
|
||||
|
@ -91,7 +119,7 @@ class TestUtils(base.TestCase):
|
|||
if 'ethtool' in name:
|
||||
raise processutils.ProcessExecutionError
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
pci = utils._get_pci_address('nic2', False)
|
||||
pci = utils.get_pci_address('nic2', False)
|
||||
self.assertEqual(None, pci)
|
||||
|
||||
def test_get_pci_address_error(self):
|
||||
|
@ -99,7 +127,7 @@ class TestUtils(base.TestCase):
|
|||
if 'ethtool' in name:
|
||||
return None, 'Error'
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
pci = utils._get_pci_address('nic2', False)
|
||||
pci = utils.get_pci_address('nic2', False)
|
||||
self.assertEqual(None, pci)
|
||||
|
||||
def test_bind_dpdk_interfaces(self):
|
||||
|
@ -241,3 +269,99 @@ class TestUtils(base.TestCase):
|
|||
self.assertEqual(utils._is_active_nic('enp129s2'), False)
|
||||
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
def test_get_vpp_interface_name(self):
|
||||
def test_execute(name, dummy1, dummy2=None, dummy3=None):
|
||||
if 'systemctl' in name:
|
||||
return None, None
|
||||
if 'vppctl' in name:
|
||||
return _VPPCTL_OUTPUT, None
|
||||
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
|
||||
self.assertEqual('GigabitEthernet0/9/0',
|
||||
utils._get_vpp_interface_name('0000:00:09.0'))
|
||||
self.assertIsNone(utils._get_vpp_interface_name(None))
|
||||
self.assertIsNone(utils._get_vpp_interface_name('0000:01:09.0'))
|
||||
self.assertRaises(utils.VppException,
|
||||
utils._get_vpp_interface_name, '0000:09.0')
|
||||
|
||||
def test_generate_vpp_config(self):
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
config_path = os.path.join(tmpdir, 'startup.conf')
|
||||
with open(config_path, 'w') as f:
|
||||
f.write(_INITIAL_VPP_CONFIG)
|
||||
vpp_exec_path = os.path.join(tmpdir, 'vpp-exec')
|
||||
utils._VPP_EXEC_FILE = vpp_exec_path
|
||||
|
||||
int1 = objects.VppInterface('em1', options="vlan-strip-offload off")
|
||||
int1.pci_dev = '0000:00:09.0'
|
||||
int2 = objects.VppInterface('em2')
|
||||
int2.pci_dev = '0000:00:09.1'
|
||||
interfaces = [int1, int2]
|
||||
expected_config = '''
|
||||
unix {
|
||||
exec %s
|
||||
nodaemon
|
||||
log /tmp/vpp.log
|
||||
full-coredump
|
||||
}
|
||||
|
||||
|
||||
api-trace {
|
||||
on
|
||||
}
|
||||
|
||||
api-segment {
|
||||
gid vpp
|
||||
}
|
||||
|
||||
dpdk {
|
||||
dev 0000:00:09.1
|
||||
uio-driver vfio-pci
|
||||
dev 0000:00:09.0 {vlan-strip-offload off}
|
||||
|
||||
}
|
||||
''' % vpp_exec_path
|
||||
self.assertEqual(expected_config,
|
||||
utils.generate_vpp_config(config_path, interfaces))
|
||||
|
||||
def test_update_vpp_mapping(self):
|
||||
def test_get_dpdk_map():
|
||||
return [{'name': 'eth1', 'pci_address': '0000:00:09.0',
|
||||
'mac_address': '01:02:03:04:05:06',
|
||||
'driver': 'vfio-pci'}]
|
||||
|
||||
self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map)
|
||||
|
||||
def test_execute(name, dummy1, dummy2=None, dummy3=None):
|
||||
return None, None
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
|
||||
def test_get_vpp_interface_name(pci_dev):
|
||||
return 'GigabitEthernet0/9/0'
|
||||
|
||||
self.stubs.Set(utils, '_get_vpp_interface_name',
|
||||
test_get_vpp_interface_name)
|
||||
|
||||
int1 = objects.VppInterface('eth1', options="vlan-strip-offload off")
|
||||
int1.pci_dev = '0000:00:09.0'
|
||||
int1.hwaddr = '01:02:03:04:05:06'
|
||||
int2 = objects.VppInterface('eth2')
|
||||
int2.pci_dev = '0000:00:09.1'
|
||||
int2.hwaddr = '01:02:03:04:05:07'
|
||||
interfaces = [int1, int2]
|
||||
|
||||
utils.update_vpp_mapping(interfaces)
|
||||
|
||||
contents = utils.get_file_data(utils._DPDK_MAPPING_FILE)
|
||||
|
||||
dpdk_test = [{'name': 'eth1', 'pci_address': '0000:00:09.0',
|
||||
'mac_address': '01:02:03:04:05:06',
|
||||
'driver': 'vfio-pci'},
|
||||
{'name': 'eth2', 'pci_address': '0000:00:09.1',
|
||||
'mac_address': '01:02:03:04:05:07',
|
||||
'driver': 'vfio-pci'}]
|
||||
dpdk_map = yaml.load(contents) if contents else []
|
||||
self.assertEqual(2, len(dpdk_map))
|
||||
self.assertListEqual(dpdk_test, dpdk_map)
|
||||
|
|
|
@ -35,11 +35,19 @@ _SYS_CLASS_NET = '/sys/class/net'
|
|||
# driver: vfio-pci
|
||||
_DPDK_MAPPING_FILE = '/var/lib/os-net-config/dpdk_mapping.yaml'
|
||||
|
||||
# VPP startup operational configuration file. The content of this file will
|
||||
# be executed when VPP starts as if typed from CLI.
|
||||
_VPP_EXEC_FILE = '/etc/vpp/vpp-exec'
|
||||
|
||||
|
||||
class OvsDpdkBindException(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class VppException(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def write_config(filename, data):
|
||||
with open(filename, 'w') as f:
|
||||
f.write(str(data))
|
||||
|
@ -82,7 +90,7 @@ def interface_mac(name):
|
|||
return f.read().rstrip()
|
||||
except IOError:
|
||||
# If the interface is bound to a DPDK driver, get the mac address from
|
||||
# the dpdk mapping file as /sys files will be removed after binding.
|
||||
# the DPDK mapping file as /sys files will be removed after binding.
|
||||
dpdk_mac_address = _get_dpdk_mac_address(name)
|
||||
if dpdk_mac_address:
|
||||
return dpdk_mac_address
|
||||
|
@ -187,7 +195,7 @@ def diff(filename, data):
|
|||
|
||||
|
||||
def bind_dpdk_interfaces(ifname, driver, noop):
|
||||
pci_address = _get_pci_address(ifname, noop)
|
||||
pci_address = get_pci_address(ifname, noop)
|
||||
if not noop:
|
||||
if pci_address:
|
||||
# modbprobe of the driver has to be done before binding.
|
||||
|
@ -218,7 +226,7 @@ def bind_dpdk_interfaces(ifname, driver, noop):
|
|||
{'name': ifname, 'driver': driver})
|
||||
|
||||
|
||||
def _get_pci_address(ifname, noop):
|
||||
def get_pci_address(ifname, noop):
|
||||
# TODO(skramaja): Validate if the given interface supports dpdk
|
||||
if not noop:
|
||||
try:
|
||||
|
@ -241,11 +249,10 @@ def _get_pci_address(ifname, noop):
|
|||
# Once the interface is bound to a DPDK driver, all the references to the
|
||||
# interface including '/sys' and '/proc', will be removed. And there is no
|
||||
# way to identify the nic name after it is bound. So, the DPDK bound nic info
|
||||
# is stored persistently in a file and is used to for nic numbering on
|
||||
# subsequent runs of os-net-config.
|
||||
# is stored persistently in _DPDK_MAPPING_FILE and is used to for nic numbering
|
||||
# on subsequent runs of os-net-config.
|
||||
def _update_dpdk_map(ifname, pci_address, mac_address, driver):
|
||||
contents = get_file_data(_DPDK_MAPPING_FILE)
|
||||
dpdk_map = yaml.load(contents) if contents else []
|
||||
dpdk_map = _get_dpdk_map()
|
||||
for item in dpdk_map:
|
||||
if item['pci_address'] == pci_address:
|
||||
item['name'] = ifname
|
||||
|
@ -263,9 +270,215 @@ def _update_dpdk_map(ifname, pci_address, mac_address, driver):
|
|||
write_yaml_config(_DPDK_MAPPING_FILE, dpdk_map)
|
||||
|
||||
|
||||
def _get_dpdk_map():
|
||||
contents = get_file_data(_DPDK_MAPPING_FILE)
|
||||
dpdk_map = yaml.load(contents) if contents else []
|
||||
return dpdk_map
|
||||
|
||||
|
||||
def _get_dpdk_mac_address(name):
|
||||
contents = get_file_data(_DPDK_MAPPING_FILE)
|
||||
dpdk_map = yaml.load(contents) if contents else []
|
||||
for item in dpdk_map:
|
||||
if item['name'] == name:
|
||||
return item['mac_address']
|
||||
|
||||
|
||||
def restart_vpp(vpp_interfaces):
|
||||
for vpp_int in vpp_interfaces:
|
||||
if 'vfio-pci' in vpp_int.uio_driver:
|
||||
processutils.execute('modprobe', 'vfio-pci')
|
||||
logger.info('Restarting VPP')
|
||||
processutils.execute('systemctl', 'restart', 'vpp')
|
||||
|
||||
|
||||
def _get_vpp_interface_name(pci_addr):
|
||||
"""Get VPP interface name from a given PCI address
|
||||
|
||||
From a running VPP instance, attempt to find the interface name from
|
||||
a given PCI address of a NIC.
|
||||
|
||||
VppException will be raised if pci_addr is not formatted correctly.
|
||||
ProcessExecutionError will be raised if VPP interface mapped to pci_addr
|
||||
is not found.
|
||||
|
||||
:param pci_addr: PCI address to lookup, in the form of DDDD:BB:SS.F, where
|
||||
- DDDD = Domain
|
||||
- BB = Bus Number
|
||||
- SS = Slot number
|
||||
- F = Function
|
||||
:return: VPP interface name. None if an interface is not found.
|
||||
"""
|
||||
if not pci_addr:
|
||||
return None
|
||||
|
||||
try:
|
||||
processutils.execute('systemctl', 'is-active', 'vpp')
|
||||
out, err = processutils.execute('vppctl', 'show', 'interfaces')
|
||||
m = re.search(r':([0-9a-fA-F]{2}):([0-9a-fA-F]{2}).([0-9a-fA-F])',
|
||||
pci_addr)
|
||||
if m:
|
||||
formatted_pci = "%x/%x/%x" % (int(m.group(1), 16),
|
||||
int(m.group(2), 16),
|
||||
int(m.group(3), 16))
|
||||
else:
|
||||
raise VppException('Invalid PCI address format: %s' % pci_addr)
|
||||
|
||||
m = re.search(r'^(\w+%s)\s+' % formatted_pci, out, re.MULTILINE)
|
||||
if m:
|
||||
logger.debug('VPP interface found: %s' % m.group(1))
|
||||
return m.group(1)
|
||||
else:
|
||||
logger.debug('Interface with pci address %s not bound to VPP'
|
||||
% pci_addr)
|
||||
return None
|
||||
except processutils.ProcessExecutionError:
|
||||
logger.debug('Interface with pci address %s not bound to vpp' %
|
||||
pci_addr)
|
||||
|
||||
|
||||
def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
||||
"""Generate configuration content for VPP
|
||||
|
||||
Generate interface related configuration content for VPP. Current
|
||||
configuration will be preserved, with interface related configurations
|
||||
updated or inserted. The config only affects 'dpdk' section of VPP config
|
||||
file, and only those lines affecting interfaces, specifically, lines
|
||||
containing the following:
|
||||
dpdk {
|
||||
...
|
||||
dev <pci_dev> {<options>}
|
||||
uio-driver <uio_driver_name>
|
||||
...
|
||||
}
|
||||
|
||||
:param vpp_config_path: VPP Configuration file path
|
||||
:param vpp_interfaces: List of VPP interface objects
|
||||
:return: updated VPP config content.
|
||||
"""
|
||||
|
||||
data = get_file_data(vpp_config_path)
|
||||
|
||||
# Add interface config to 'dpdk' section
|
||||
for vpp_interface in vpp_interfaces:
|
||||
if vpp_interface.pci_dev:
|
||||
logger.info('vpp interface %s pci dev: %s'
|
||||
% (vpp_interface.name, vpp_interface.pci_dev))
|
||||
|
||||
if vpp_interface.options:
|
||||
int_cfg = '%s {%s}' % (vpp_interface.pci_dev,
|
||||
vpp_interface.options)
|
||||
else:
|
||||
int_cfg = vpp_interface.pci_dev
|
||||
|
||||
# Make sure 'dpdk' section exists in the config
|
||||
if not re.search(r'^\s*dpdk\s*\{', data, re.MULTILINE):
|
||||
data += "\ndpdk {\n}\n"
|
||||
|
||||
# Find existing config line for the device we are trying to
|
||||
# configure, the line should look like 'dev <pci_dev> ...'
|
||||
# If such config line is found, we will replace the line with
|
||||
# appropriate configuration, otherwise, add a new config line
|
||||
# in 'dpdk' section of the config.
|
||||
m = re.search(r'^\s*dev\s+%s\s*(\{[^}]*\})?\s*'
|
||||
% vpp_interface.pci_dev, data,
|
||||
re.IGNORECASE | re.MULTILINE)
|
||||
if m:
|
||||
data = re.sub(m.group(0), ' dev %s\n' % int_cfg, data)
|
||||
else:
|
||||
data = re.sub(r'(^\s*dpdk\s*\{)',
|
||||
r'\1\n dev %s\n' % int_cfg,
|
||||
data,
|
||||
flags=re.MULTILINE)
|
||||
|
||||
if vpp_interface.uio_driver:
|
||||
# Check if there is existing uio-driver configuration, if
|
||||
# found, the line will be replaced with the appropriate
|
||||
# configuration, otherwise, add a new line in 'dpdk' section.
|
||||
m = re.search(r'^\s*uio-driver.*$', data, re.MULTILINE)
|
||||
if m:
|
||||
data = re.sub(m.group(0), r' uio-driver %s'
|
||||
% vpp_interface.uio_driver, data)
|
||||
else:
|
||||
data = re.sub(r'(dpdk\s*\{)',
|
||||
r'\1\n uio-driver %s'
|
||||
% vpp_interface.uio_driver,
|
||||
data)
|
||||
else:
|
||||
logger.debug('pci address not found for interface %s, may have'
|
||||
'already been bound to vpp' % vpp_interface.name)
|
||||
|
||||
# Add start up script for VPP to config. This script will be executed by
|
||||
# VPP on service start.
|
||||
if not re.search(r'^\s*unix\s*\{', data, re.MULTILINE):
|
||||
data += "\nunix {\n}\n"
|
||||
|
||||
m = re.search(r'^\s*(exec|startup-config).*$',
|
||||
data,
|
||||
re.IGNORECASE | re.MULTILINE)
|
||||
if m:
|
||||
data = re.sub(m.group(0), ' exec %s' % _VPP_EXEC_FILE, data)
|
||||
else:
|
||||
data = re.sub(r'(^\s*unix\s*\{)',
|
||||
r'\1\n exec %s' % _VPP_EXEC_FILE,
|
||||
data,
|
||||
flags=re.MULTILINE)
|
||||
# Make sure startup script exists to avoid VPP startup failure.
|
||||
open(_VPP_EXEC_FILE, 'a').close()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def update_vpp_mapping(vpp_interfaces):
|
||||
"""Verify VPP interface binding and update mapping file
|
||||
|
||||
VppException will be raised if interfaces are not properly bound.
|
||||
|
||||
:param vpp_interfaces: List of VPP interface objects
|
||||
"""
|
||||
vpp_start_cli = ""
|
||||
|
||||
for vpp_int in vpp_interfaces:
|
||||
if not vpp_int.pci_dev:
|
||||
dpdk_map = _get_dpdk_map()
|
||||
for dpdk_int in dpdk_map:
|
||||
if dpdk_int['name'] == vpp_int.name:
|
||||
vpp_int.pci_dev = dpdk_int['pci_address']
|
||||
break
|
||||
else:
|
||||
raise VppException('Interface %s has no PCI address and is not'
|
||||
' found in mapping file' % vpp_int.name)
|
||||
|
||||
# Try to get VPP interface name. In case VPP service is down
|
||||
# for some reason, we will restart VPP and try again. Currently
|
||||
# only trying one more time, can turn into a retry_counter if needed
|
||||
# in the future.
|
||||
for i in range(2):
|
||||
vpp_name = _get_vpp_interface_name(vpp_int.pci_dev)
|
||||
if not vpp_name:
|
||||
restart_vpp(vpp_interfaces)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise VppException('Interface %s with pci address %s not '
|
||||
'bound to vpp'
|
||||
% (vpp_int.name, vpp_int.pci_dev))
|
||||
|
||||
# Generate content of startup script for VPP
|
||||
for address in vpp_int.addresses:
|
||||
vpp_start_cli += 'set interface state %s up\n' % vpp_name
|
||||
vpp_start_cli += 'set interface ip address %s %s/%s\n' \
|
||||
% (vpp_name, address.ip, address.prefixlen)
|
||||
|
||||
logger.info('Updating mapping for vpp interface %s:'
|
||||
'pci_dev: %s mac address: %s uio driver: %s'
|
||||
% (vpp_int.name, vpp_int.pci_dev, vpp_int.hwaddr,
|
||||
vpp_int.uio_driver))
|
||||
_update_dpdk_map(vpp_int.name, vpp_int.pci_dev, vpp_int.hwaddr,
|
||||
vpp_int.uio_driver)
|
||||
# Enable VPP service to make the VPP interface configuration
|
||||
# persistent.
|
||||
processutils.execute('systemctl', 'enable', 'vpp')
|
||||
if diff(_VPP_EXEC_FILE, vpp_start_cli):
|
||||
write_config(_VPP_EXEC_FILE, vpp_start_cli)
|
||||
restart_vpp(vpp_interfaces)
|
||||
|
|
Loading…
Reference in New Issue