Merge "Add support for VPP Bond"
This commit is contained in:
commit
3cbe5c9215
23
etc/os-net-config/samples/vpp_bond.json
Normal file
23
etc/os-net-config/samples/vpp_bond.json
Normal file
@ -0,0 +1,23 @@
|
||||
{ "network_config": [
|
||||
{
|
||||
"type": "vpp_bond",
|
||||
"name": "net_bonding0",
|
||||
"addresses": [
|
||||
{
|
||||
"ip_netmask": "192.0.2.1/24"
|
||||
}
|
||||
],
|
||||
"bonding_options": "mode=2,xmit_policy=l34",
|
||||
"members": [
|
||||
{
|
||||
"type": "vpp_interface",
|
||||
"name": "eth1"
|
||||
},
|
||||
{
|
||||
"type": "vpp_interface",
|
||||
"name": "eth2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
17
etc/os-net-config/samples/vpp_bond.yaml
Normal file
17
etc/os-net-config/samples/vpp_bond.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
network_config:
|
||||
-
|
||||
type: vpp_bond
|
||||
# name must be in the format of eth_bondX or net_bondingX, where X is a
|
||||
# unique number for each bond.
|
||||
name: net_bonding0
|
||||
addresses:
|
||||
-
|
||||
ip_netmask: 192.0.2.1/24
|
||||
bonding_options: "mode=2,xmit_policy=l34"
|
||||
members:
|
||||
-
|
||||
type: vpp_interface
|
||||
name: eth1
|
||||
-
|
||||
type: vpp_interface
|
||||
name: eth2
|
@ -102,6 +102,10 @@ class NetConfig(object):
|
||||
self.add_ovs_dpdk_bond(obj)
|
||||
elif isinstance(obj, objects.VppInterface):
|
||||
self.add_vpp_interface(obj)
|
||||
elif isinstance(obj, objects.VppBond):
|
||||
self.add_vpp_bond(obj)
|
||||
for member in obj.members:
|
||||
self.add_object(member)
|
||||
elif isinstance(obj, objects.ContrailVrouter):
|
||||
self.add_contrail_vrouter(obj)
|
||||
elif isinstance(obj, objects.ContrailVrouterDpdk):
|
||||
@ -219,6 +223,13 @@ class NetConfig(object):
|
||||
"""
|
||||
raise NotImplementedError("add_vpp_interface is not implemented.")
|
||||
|
||||
def add_vpp_bond(self, vpp_bond):
|
||||
"""Add a VppBond object to the net config object.
|
||||
|
||||
:param vpp_bond: The VppBond object to add.
|
||||
"""
|
||||
raise NotImplementedError("add_vpp_bond is not implemented.")
|
||||
|
||||
def add_contrail_vrouter(self, contrail_vrouter):
|
||||
"""Add a ContrailVrouter object to the net config object.
|
||||
|
||||
|
@ -121,6 +121,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
||||
self.ib_interface_data = {}
|
||||
self.linuxteam_data = {}
|
||||
self.vpp_interface_data = {}
|
||||
self.vpp_bond_data = {}
|
||||
self.member_names = {}
|
||||
self.renamed_interfaces = {}
|
||||
self.bond_primary_ifaces = {}
|
||||
@ -676,6 +677,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
||||
"""
|
||||
vpp_interface.pci_dev = utils.get_pci_address(vpp_interface.name,
|
||||
False)
|
||||
if not vpp_interface.pci_dev:
|
||||
vpp_interface.pci_dev = utils.get_stored_pci_address(
|
||||
vpp_interface.name, False)
|
||||
vpp_interface.hwaddr = utils.interface_mac(vpp_interface.name)
|
||||
if not self.noop:
|
||||
self.ifdown(vpp_interface.name)
|
||||
@ -684,6 +688,14 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
||||
% (vpp_interface.name, vpp_interface.pci_dev))
|
||||
self.vpp_interface_data[vpp_interface.name] = vpp_interface
|
||||
|
||||
def add_vpp_bond(self, vpp_bond):
|
||||
"""Add a VppInterface object to the net config object
|
||||
|
||||
:param vpp_bond: The VPPBond object to add
|
||||
"""
|
||||
logger.info('adding vpp bond: %s' % vpp_bond.name)
|
||||
self.vpp_bond_data[vpp_bond.name] = vpp_bond
|
||||
|
||||
def add_contrail_vrouter(self, contrail_vrouter):
|
||||
"""Add a ContraiVrouter object to the net config object
|
||||
|
||||
@ -806,6 +818,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
||||
stop_dhclient_interfaces = []
|
||||
ovs_needs_restart = False
|
||||
vpp_interfaces = self.vpp_interface_data.values()
|
||||
vpp_bonds = self.vpp_bond_data.values()
|
||||
|
||||
for interface_name, iface_data in self.interface_data.items():
|
||||
route_data = self.route_data.get(interface_name, '')
|
||||
@ -1014,9 +1027,10 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
||||
logger.info('No changes required for InfiniBand iface: %s' %
|
||||
interface_name)
|
||||
|
||||
if self.vpp_interface_data:
|
||||
if self.vpp_interface_data or self.vpp_bond_data:
|
||||
vpp_path = self.root_dir + vpp_config_path()
|
||||
vpp_config = utils.generate_vpp_config(vpp_path, vpp_interfaces)
|
||||
vpp_config = utils.generate_vpp_config(vpp_path, vpp_interfaces,
|
||||
vpp_bonds)
|
||||
if utils.diff(vpp_path, vpp_config):
|
||||
restart_vpp = True
|
||||
update_files[vpp_path] = vpp_config
|
||||
@ -1139,7 +1153,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
||||
|
||||
if self.vpp_interface_data:
|
||||
logger.info('Updating VPP mapping')
|
||||
utils.update_vpp_mapping(vpp_interfaces)
|
||||
utils.update_vpp_mapping(vpp_interfaces, vpp_bonds)
|
||||
|
||||
if self.errors:
|
||||
message = 'Failure(s) occurred when applying configuration'
|
||||
|
@ -76,6 +76,8 @@ def object_from_json(json):
|
||||
return OvsDpdkBond.from_json(json)
|
||||
elif obj_type == "vpp_interface":
|
||||
return VppInterface.from_json(json)
|
||||
elif obj_type == "vpp_bond":
|
||||
return VppBond.from_json(json)
|
||||
elif obj_type == "contrail_vrouter":
|
||||
return ContrailVrouter.from_json(json)
|
||||
elif obj_type == "contrail_vrouter_dpdk":
|
||||
@ -1225,6 +1227,61 @@ class VppInterface(_BaseOpts):
|
||||
options=options)
|
||||
|
||||
|
||||
class VppBond(_BaseOpts):
|
||||
"""Base class for VPP Bond."""
|
||||
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, onboot=True,
|
||||
members=None, bonding_options=None):
|
||||
addresses = addresses or []
|
||||
members = members or []
|
||||
|
||||
super(VppBond, self).__init__(name, use_dhcp, use_dhcpv6,
|
||||
addresses, routes, mtu, primary,
|
||||
nic_mapping, persist_mapping,
|
||||
defroute, dhclient_args,
|
||||
dns_servers, nm_controlled, onboot)
|
||||
self.members = members
|
||||
self.bonding_options = bonding_options
|
||||
|
||||
@staticmethod
|
||||
def from_json(json):
|
||||
name = _get_required_field(json, 'name', 'VppBond')
|
||||
bonding_options = json.get('bonding_options', '')
|
||||
|
||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
||||
persist_mapping, defroute, dhclient_args,
|
||||
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
|
||||
json, include_primary=False)
|
||||
|
||||
members = []
|
||||
members_json = json.get('members', None)
|
||||
if members_json:
|
||||
if isinstance(members_json, list):
|
||||
for member in members_json:
|
||||
if not member.get('nic_mapping'):
|
||||
member.update({'nic_mapping': nic_mapping})
|
||||
member.update({'persist_mapping': persist_mapping})
|
||||
obj = object_from_json(member)
|
||||
if isinstance(obj, VppInterface):
|
||||
members.append(obj)
|
||||
else:
|
||||
msg = 'Members must be of type vpp_interface'
|
||||
raise InvalidConfigException(msg)
|
||||
else:
|
||||
msg = 'Members must be a list.'
|
||||
raise InvalidConfigException(msg)
|
||||
|
||||
return VppBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
||||
addresses=addresses, routes=routes, mtu=mtu,
|
||||
members=members, nic_mapping=nic_mapping,
|
||||
persist_mapping=persist_mapping,
|
||||
defroute=defroute, dhclient_args=dhclient_args,
|
||||
dns_servers=dns_servers, nm_controlled=nm_controlled,
|
||||
onboot=onboot, bonding_options=bonding_options)
|
||||
|
||||
|
||||
class ContrailVrouter(_BaseOpts):
|
||||
"""Base class for Contrail Interface.
|
||||
|
||||
|
@ -669,6 +669,54 @@ definitions:
|
||||
- name
|
||||
additionalProperties: False
|
||||
|
||||
vpp_bond:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
enum: ["vpp_bond"]
|
||||
name:
|
||||
$ref: "#/definitions/string_or_param"
|
||||
uio_driver:
|
||||
$ref: "#/definitions/string_or_param"
|
||||
options:
|
||||
$ref: "#/definitions/string_or_param"
|
||||
# common options:
|
||||
use_dhcp:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
use_dhcp6:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
addresses:
|
||||
$ref: "#/definitions/list_of_address"
|
||||
routes:
|
||||
$ref: "#/definitions/list_of_route"
|
||||
mtu:
|
||||
$ref: "#/definitions/int_or_param"
|
||||
nic_mapping:
|
||||
$ref: "#/definitions/nic_mapping"
|
||||
persist_mapping:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
defroute:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
dhclient_args:
|
||||
$ref: "#/definitions/string_or_param"
|
||||
dns_servers:
|
||||
$ref: "#/definitions/list_of_ip_address_string_or_param"
|
||||
nm_controlled:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
onboot:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
members:
|
||||
type: array
|
||||
items:
|
||||
oneOf:
|
||||
- $ref: "#/definitions/vpp_interface"
|
||||
bonding_options:
|
||||
$ref: "#/definitions/string_or_param"
|
||||
required:
|
||||
- type
|
||||
- name
|
||||
additionalProperties: False
|
||||
|
||||
contrail_vrouter:
|
||||
type: object
|
||||
properties:
|
||||
@ -1135,6 +1183,7 @@ items:
|
||||
- $ref: "#/definitions/nfvswitch_internal"
|
||||
- $ref: "#/definitions/ib_interface"
|
||||
- $ref: "#/definitions/vpp_interface"
|
||||
- $ref: "#/definitions/vpp_bond"
|
||||
- $ref: "#/definitions/contrail_vrouter"
|
||||
- $ref: "#/definitions/contrail_vrouter_dpdk"
|
||||
minItems: 1
|
||||
|
@ -1256,3 +1256,55 @@ class TestVppInterface(base.TestCase):
|
||||
self.assertEqual("em1", vpp_interface.name)
|
||||
self.assertEqual("uio_pci_generic", vpp_interface.uio_driver)
|
||||
self.assertEqual("vlan-strip-offload off", vpp_interface.options)
|
||||
|
||||
|
||||
class TestVppBond(base.TestCase):
|
||||
def test_vpp_interface_from_json(self):
|
||||
data = """{
|
||||
"type": "vpp_bond",
|
||||
"name": "net_bonding0",
|
||||
"members": [
|
||||
{
|
||||
"type": "vpp_interface",
|
||||
"name": "eth1"
|
||||
},
|
||||
{
|
||||
"type": "vpp_interface",
|
||||
"name": "eth2"
|
||||
}
|
||||
],
|
||||
"bonding_options": "mode=2,xmit_policy=l34"
|
||||
}
|
||||
"""
|
||||
|
||||
vpp_bond = objects.object_from_json(json.loads(data))
|
||||
self.assertEqual("net_bonding0", vpp_bond.name)
|
||||
self.assertEqual("mode=2,xmit_policy=l34", vpp_bond.bonding_options)
|
||||
vpp_int1 = vpp_bond.members[0]
|
||||
self.assertEqual("eth1", vpp_int1.name)
|
||||
vpp_int2 = vpp_bond.members[1]
|
||||
self.assertEqual("eth2", vpp_int2.name)
|
||||
|
||||
def test_invalid_vpp_interface_from_json(self):
|
||||
data = """{
|
||||
"type": "vpp_bond",
|
||||
"name": "net_bonding0",
|
||||
"members": [
|
||||
{
|
||||
"type": "vpp_interface",
|
||||
"name": "eth1"
|
||||
},
|
||||
{
|
||||
"type": "interface",
|
||||
"name": "eth2"
|
||||
}
|
||||
],
|
||||
"bonding_options": "mode=2,xmit_policy=l34"
|
||||
}
|
||||
"""
|
||||
|
||||
err = self.assertRaises(objects.InvalidConfigException,
|
||||
objects.object_from_json,
|
||||
json.loads(data))
|
||||
expected = 'Members must be of type vpp_interface'
|
||||
self.assertIn(expected, six.text_type(err))
|
||||
|
@ -47,6 +47,13 @@ local0 0 down
|
||||
|
||||
'''
|
||||
|
||||
_VPPBOND_OUTPUT = """
|
||||
Name Idx Link Hardware
|
||||
BondEthernet0 3 up Slave-Idx: 1 2
|
||||
TenGigabitEthernet2/0/0 1 slave TenGigabitEthernet2/0/0
|
||||
TenGigabitEthernet2/0/1 2 slave TenGigabitEthernet2/0/1
|
||||
"""
|
||||
|
||||
_INITIAL_VPP_CONFIG = '''
|
||||
unix {
|
||||
nodaemon
|
||||
@ -339,8 +346,8 @@ class TestUtils(base.TestCase):
|
||||
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
def test_get_vpp_interface_name(self):
|
||||
def test_execute(name, dummy1, dummy2=None, dummy3=None):
|
||||
def test_get_vpp_interface(self):
|
||||
def test_execute(name, *args, **kwargs):
|
||||
if 'systemctl' in name:
|
||||
return None, None
|
||||
if 'vppctl' in name:
|
||||
@ -348,19 +355,37 @@ class TestUtils(base.TestCase):
|
||||
|
||||
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'))
|
||||
int_info = utils._get_vpp_interface('0000:00:09.0')
|
||||
self.assertIsNotNone(int_info)
|
||||
self.assertEqual('GigabitEthernet0/9/0', int_info['name'])
|
||||
self.assertEqual('1', int_info['index'])
|
||||
self.assertIsNone(utils._get_vpp_interface(None))
|
||||
self.assertIsNone(utils._get_vpp_interface('0000:01:09.0'))
|
||||
self.assertRaises(utils.VppException,
|
||||
utils._get_vpp_interface_name, '0000:09.0')
|
||||
utils._get_vpp_interface, '0000:09.0')
|
||||
|
||||
@mock.patch('os_net_config.utils.processutils.execute',
|
||||
return_value=('', None))
|
||||
def test_get_vpp_interface_name_multiple_iterations(self, mock_execute):
|
||||
self.assertIsNone(utils._get_vpp_interface_name('0000:00:09.0', 2, 1))
|
||||
self.assertIsNone(utils._get_vpp_interface('0000:00:09.0', 2, 1))
|
||||
self.assertEqual(4, mock_execute.call_count)
|
||||
|
||||
def test_get_vpp_bond(self):
|
||||
def test_execute(name, *args, **kwargs):
|
||||
if 'systemctl' in name:
|
||||
return None, None
|
||||
if 'vppctl' in name:
|
||||
return _VPPBOND_OUTPUT, None
|
||||
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
bond_info = utils._get_vpp_bond(['1', '2'])
|
||||
self.assertIsNotNone(bond_info)
|
||||
self.assertEqual('BondEthernet0', bond_info['name'])
|
||||
self.assertEqual('3', bond_info['index'])
|
||||
self.assertIsNone(utils._get_vpp_bond(['1']))
|
||||
self.assertIsNone(utils._get_vpp_bond(['1', '2', '3']))
|
||||
self.assertIsNone(utils._get_vpp_bond([]))
|
||||
|
||||
def test_generate_vpp_config(self):
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
config_path = os.path.join(tmpdir, 'startup.conf')
|
||||
@ -374,6 +399,7 @@ class TestUtils(base.TestCase):
|
||||
int2 = objects.VppInterface('em2')
|
||||
int2.pci_dev = '0000:00:09.1'
|
||||
interfaces = [int1, int2]
|
||||
bonds = []
|
||||
expected_config = '''
|
||||
unix {
|
||||
exec %s
|
||||
@ -399,9 +425,45 @@ dpdk {
|
||||
}
|
||||
''' % vpp_exec_path
|
||||
self.assertEqual(expected_config,
|
||||
utils.generate_vpp_config(config_path, interfaces))
|
||||
utils.generate_vpp_config(config_path, interfaces,
|
||||
bonds))
|
||||
|
||||
bonds = [objects.VppBond('net_bonding0', members=interfaces,
|
||||
bonding_options='mode=2,xmit_policy=l3')]
|
||||
expected_config = '''
|
||||
unix {
|
||||
exec %s
|
||||
nodaemon
|
||||
log /tmp/vpp.log
|
||||
full-coredump
|
||||
}
|
||||
|
||||
|
||||
api-trace {
|
||||
on
|
||||
}
|
||||
|
||||
api-segment {
|
||||
gid vpp
|
||||
}
|
||||
|
||||
dpdk {
|
||||
vdev net_bonding0,slave=0000:00:09.0,slave=0000:00:09.1,mode=2,xmit_policy=l3
|
||||
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,
|
||||
bonds))
|
||||
|
||||
def test_update_vpp_mapping(self):
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
vpp_exec_path = os.path.join(tmpdir, 'vpp-exec')
|
||||
utils._VPP_EXEC_FILE = vpp_exec_path
|
||||
|
||||
def test_get_dpdk_map():
|
||||
return [{'name': 'eth1', 'pci_address': '0000:00:09.0',
|
||||
'mac_address': '01:02:03:04:05:06',
|
||||
@ -409,15 +471,15 @@ dpdk {
|
||||
|
||||
self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map)
|
||||
|
||||
def test_execute(name, dummy1, dummy2=None, dummy3=None):
|
||||
def test_execute(name, *args, **kwargs):
|
||||
return None, None
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
|
||||
def test_get_vpp_interface_name(pci_dev, tries, timeout):
|
||||
return 'GigabitEthernet0/9/0'
|
||||
def test_get_vpp_interface(pci_dev, tries, timeout):
|
||||
return {'name': 'GigabitEthernet0/9/0', 'index': '1'}
|
||||
|
||||
self.stubs.Set(utils, '_get_vpp_interface_name',
|
||||
test_get_vpp_interface_name)
|
||||
self.stubs.Set(utils, '_get_vpp_interface',
|
||||
test_get_vpp_interface)
|
||||
|
||||
int1 = objects.VppInterface('eth1', options="vlan-strip-offload off")
|
||||
int1.pci_dev = '0000:00:09.0'
|
||||
@ -427,7 +489,7 @@ dpdk {
|
||||
int2.hwaddr = '01:02:03:04:05:07'
|
||||
interfaces = [int1, int2]
|
||||
|
||||
utils.update_vpp_mapping(interfaces)
|
||||
utils.update_vpp_mapping(interfaces, [])
|
||||
|
||||
contents = utils.get_file_data(utils._DPDK_MAPPING_FILE)
|
||||
|
||||
|
@ -355,11 +355,12 @@ def restart_vpp(vpp_interfaces):
|
||||
processutils.execute('systemctl', 'restart', 'vpp')
|
||||
|
||||
|
||||
def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
||||
"""Get VPP interface name from a given PCI address
|
||||
def _get_vpp_interface(pci_addr, tries=1, timeout=5):
|
||||
"""Get VPP interface information from a given PCI address
|
||||
|
||||
From a running VPP instance, attempt to find the interface name from
|
||||
a given PCI address of a NIC.
|
||||
From a running VPP instance, attempt to find the interface name and index
|
||||
from a given PCI address of a NIC. The index is used to identify VPP bond
|
||||
interface associated with the VPP interface.
|
||||
|
||||
:param pci_addr: PCI address to lookup, in the form of DDDD:BB:SS.F, where
|
||||
- DDDD = Domain
|
||||
@ -377,7 +378,8 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
||||
try:
|
||||
timestamp = time.time()
|
||||
processutils.execute('systemctl', 'is-active', 'vpp')
|
||||
out, err = processutils.execute('vppctl', 'show', 'interface')
|
||||
out, err = processutils.execute('vppctl', 'show', 'interface',
|
||||
check_exit_code=False)
|
||||
logger.debug("vppctl show interface\n%s\n%s\n" % (out, err))
|
||||
m = re.search(r':([0-9a-fA-F]{2}):([0-9a-fA-F]{2}).([0-9a-fA-F])',
|
||||
pci_addr)
|
||||
@ -388,10 +390,12 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
||||
else:
|
||||
raise VppException('Invalid PCI address format: %s' % pci_addr)
|
||||
|
||||
m = re.search(r'^(\w+%s)\s+' % formatted_pci, out, re.MULTILINE)
|
||||
m = re.search(r'^(\w+%s)\s+(\d+)' % formatted_pci, out,
|
||||
re.MULTILINE)
|
||||
if m:
|
||||
logger.debug('VPP interface found: %s' % m.group(1))
|
||||
return m.group(1)
|
||||
logger.debug('VPP interface found: %s, index: %s' %
|
||||
(m.group(1), m.group(2)))
|
||||
return {'name': m.group(1), 'index': m.group(2)}
|
||||
except processutils.ProcessExecutionError:
|
||||
pass
|
||||
|
||||
@ -402,7 +406,36 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
||||
return None
|
||||
|
||||
|
||||
def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
||||
def _get_vpp_bond(member_ids):
|
||||
"""Get VPP bond information from a given list of VPP interface indices
|
||||
|
||||
:param member_ids: list of VPP interfaces indices for the bond
|
||||
:return: VPP bond name and index. None if an interface is not found.
|
||||
"""
|
||||
if not member_ids:
|
||||
return None
|
||||
|
||||
member_ids.sort()
|
||||
member_ids_str = ' '.join(member_ids)
|
||||
|
||||
out, err = processutils.execute('vppctl', 'show',
|
||||
'hardware-interfaces', 'bond', 'brief',
|
||||
check_exit_code=False)
|
||||
logger.debug('vppctl show hardware-interfaces bond brief\n%s' % out)
|
||||
m = re.search(r'^\s*(BondEthernet\d+)\s+(\d+)\s+.+Slave-Idx:\s+%s\s*$' %
|
||||
member_ids_str,
|
||||
out,
|
||||
re.MULTILINE)
|
||||
if m:
|
||||
logger.debug('Bond found: %s, index: %s' % (m.group(1), m.group(2)))
|
||||
return {'name': m.group(1), 'index': m.group(2)}
|
||||
else:
|
||||
logger.debug('Bond with member indices "%s" not found in VPP'
|
||||
% member_ids_str)
|
||||
return None
|
||||
|
||||
|
||||
def generate_vpp_config(vpp_config_path, vpp_interfaces, vpp_bonds):
|
||||
"""Generate configuration content for VPP
|
||||
|
||||
Generate interface related configuration content for VPP. Current
|
||||
@ -419,6 +452,7 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
||||
|
||||
:param vpp_config_path: VPP Configuration file path
|
||||
:param vpp_interfaces: List of VPP interface objects
|
||||
:param vpp_bonds: List of VPP bond objects
|
||||
:return: updated VPP config content.
|
||||
"""
|
||||
|
||||
@ -473,8 +507,32 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
||||
data,
|
||||
flags=re.MULTILINE)
|
||||
else:
|
||||
logger.debug('pci address not found for interface %s, may have'
|
||||
'already been bound to vpp' % vpp_interface.name)
|
||||
raise VppException('Interface %s has no PCI address and is not'
|
||||
' found in mapping file' % vpp_interface.name)
|
||||
|
||||
# Add bond config to 'dpdk' section
|
||||
for vpp_bond in vpp_bonds:
|
||||
slave_str = ''
|
||||
for member in vpp_bond.members:
|
||||
slave_str += ",slave=%s" % member.pci_dev
|
||||
if vpp_bond.bonding_options:
|
||||
options_str = ',' + vpp_bond.bonding_options.strip(' ,')
|
||||
else:
|
||||
options_str = ''
|
||||
|
||||
if slave_str:
|
||||
m = re.search(r'^\s*vdev\s+%s.*$' % vpp_bond.name,
|
||||
data, re.MULTILINE)
|
||||
if m:
|
||||
data = re.sub(m.group(0), r' vdev %s%s%s'
|
||||
% (vpp_bond.name, slave_str, options_str),
|
||||
data)
|
||||
else:
|
||||
data = re.sub(r'(^\s*dpdk\s*\{)',
|
||||
r'\1\n vdev %s%s%s'
|
||||
% (vpp_bond.name, slave_str, options_str),
|
||||
data,
|
||||
flags=re.MULTILINE)
|
||||
|
||||
# Add start up script for VPP to config. This script will be executed by
|
||||
# VPP on service start.
|
||||
@ -497,36 +555,29 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
||||
return data
|
||||
|
||||
|
||||
def update_vpp_mapping(vpp_interfaces):
|
||||
def update_vpp_mapping(vpp_interfaces, vpp_bonds):
|
||||
"""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
|
||||
:param vpp_bonds: List of VPP bond objects
|
||||
"""
|
||||
vpp_start_cli = ""
|
||||
cli_list = []
|
||||
|
||||
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,
|
||||
int_info = _get_vpp_interface(vpp_int.pci_dev,
|
||||
tries=12, timeout=5)
|
||||
if not vpp_name:
|
||||
if not int_info:
|
||||
restart_vpp(vpp_interfaces)
|
||||
else:
|
||||
vpp_int.vpp_name = int_info['name']
|
||||
vpp_int.vpp_idx = int_info['index']
|
||||
break
|
||||
else:
|
||||
raise VppException('Interface %s with pci address %s not '
|
||||
@ -534,10 +585,13 @@ def update_vpp_mapping(vpp_interfaces):
|
||||
% (vpp_int.name, vpp_int.pci_dev))
|
||||
|
||||
# Generate content of startup script for VPP
|
||||
if not vpp_bonds:
|
||||
cli_list.append('set interface state %s up'
|
||||
% int_info['name'])
|
||||
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)
|
||||
cli_list.append('set interface ip address %s %s/%s\n'
|
||||
% (int_info['name'], address.ip,
|
||||
address.prefixlen))
|
||||
|
||||
logger.info('Updating mapping for vpp interface %s:'
|
||||
'pci_dev: %s mac address: %s uio driver: %s'
|
||||
@ -545,9 +599,30 @@ def update_vpp_mapping(vpp_interfaces):
|
||||
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')
|
||||
|
||||
for vpp_bond in vpp_bonds:
|
||||
bond_ids = [member.vpp_idx for member in vpp_bond.members]
|
||||
bond_info = _get_vpp_bond(bond_ids)
|
||||
if bond_info:
|
||||
cli_list.append('set interface state %s up'
|
||||
% bond_info['name'])
|
||||
for address in vpp_bond.addresses:
|
||||
cli_list.append('set interface ip address %s %s/%s'
|
||||
% (bond_info['name'], address.ip,
|
||||
address.prefixlen))
|
||||
else:
|
||||
raise VppException('Bond %s not found in VPP.' % vpp_bond.name)
|
||||
|
||||
vpp_start_cli = get_file_data(_VPP_EXEC_FILE)
|
||||
for cli_line in cli_list:
|
||||
if not re.search(r'^\s*%s\s*$' % cli_line,
|
||||
vpp_start_cli, re.MULTILINE):
|
||||
vpp_start_cli += cli_line + '\n'
|
||||
|
||||
if diff(_VPP_EXEC_FILE, vpp_start_cli):
|
||||
write_config(_VPP_EXEC_FILE, vpp_start_cli)
|
||||
restart_vpp(vpp_interfaces)
|
||||
|
||||
# Enable VPP service to make the VPP interface configuration
|
||||
# persistent.
|
||||
processutils.execute('systemctl', 'enable', 'vpp')
|
||||
|
Loading…
Reference in New Issue
Block a user