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)
|
self.add_ovs_dpdk_bond(obj)
|
||||||
elif isinstance(obj, objects.VppInterface):
|
elif isinstance(obj, objects.VppInterface):
|
||||||
self.add_vpp_interface(obj)
|
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):
|
elif isinstance(obj, objects.ContrailVrouter):
|
||||||
self.add_contrail_vrouter(obj)
|
self.add_contrail_vrouter(obj)
|
||||||
elif isinstance(obj, objects.ContrailVrouterDpdk):
|
elif isinstance(obj, objects.ContrailVrouterDpdk):
|
||||||
@ -219,6 +223,13 @@ class NetConfig(object):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError("add_vpp_interface is not implemented.")
|
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):
|
def add_contrail_vrouter(self, contrail_vrouter):
|
||||||
"""Add a ContrailVrouter object to the net config object.
|
"""Add a ContrailVrouter object to the net config object.
|
||||||
|
|
||||||
|
@ -121,6 +121,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||||||
self.ib_interface_data = {}
|
self.ib_interface_data = {}
|
||||||
self.linuxteam_data = {}
|
self.linuxteam_data = {}
|
||||||
self.vpp_interface_data = {}
|
self.vpp_interface_data = {}
|
||||||
|
self.vpp_bond_data = {}
|
||||||
self.member_names = {}
|
self.member_names = {}
|
||||||
self.renamed_interfaces = {}
|
self.renamed_interfaces = {}
|
||||||
self.bond_primary_ifaces = {}
|
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,
|
vpp_interface.pci_dev = utils.get_pci_address(vpp_interface.name,
|
||||||
False)
|
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)
|
vpp_interface.hwaddr = utils.interface_mac(vpp_interface.name)
|
||||||
if not self.noop:
|
if not self.noop:
|
||||||
self.ifdown(vpp_interface.name)
|
self.ifdown(vpp_interface.name)
|
||||||
@ -684,6 +688,14 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||||||
% (vpp_interface.name, vpp_interface.pci_dev))
|
% (vpp_interface.name, vpp_interface.pci_dev))
|
||||||
self.vpp_interface_data[vpp_interface.name] = vpp_interface
|
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):
|
def add_contrail_vrouter(self, contrail_vrouter):
|
||||||
"""Add a ContraiVrouter object to the net config object
|
"""Add a ContraiVrouter object to the net config object
|
||||||
|
|
||||||
@ -806,6 +818,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||||||
stop_dhclient_interfaces = []
|
stop_dhclient_interfaces = []
|
||||||
ovs_needs_restart = False
|
ovs_needs_restart = False
|
||||||
vpp_interfaces = self.vpp_interface_data.values()
|
vpp_interfaces = self.vpp_interface_data.values()
|
||||||
|
vpp_bonds = self.vpp_bond_data.values()
|
||||||
|
|
||||||
for interface_name, iface_data in self.interface_data.items():
|
for interface_name, iface_data in self.interface_data.items():
|
||||||
route_data = self.route_data.get(interface_name, '')
|
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' %
|
logger.info('No changes required for InfiniBand iface: %s' %
|
||||||
interface_name)
|
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_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):
|
if utils.diff(vpp_path, vpp_config):
|
||||||
restart_vpp = True
|
restart_vpp = True
|
||||||
update_files[vpp_path] = vpp_config
|
update_files[vpp_path] = vpp_config
|
||||||
@ -1139,7 +1153,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||||||
|
|
||||||
if self.vpp_interface_data:
|
if self.vpp_interface_data:
|
||||||
logger.info('Updating VPP mapping')
|
logger.info('Updating VPP mapping')
|
||||||
utils.update_vpp_mapping(vpp_interfaces)
|
utils.update_vpp_mapping(vpp_interfaces, vpp_bonds)
|
||||||
|
|
||||||
if self.errors:
|
if self.errors:
|
||||||
message = 'Failure(s) occurred when applying configuration'
|
message = 'Failure(s) occurred when applying configuration'
|
||||||
|
@ -76,6 +76,8 @@ def object_from_json(json):
|
|||||||
return OvsDpdkBond.from_json(json)
|
return OvsDpdkBond.from_json(json)
|
||||||
elif obj_type == "vpp_interface":
|
elif obj_type == "vpp_interface":
|
||||||
return VppInterface.from_json(json)
|
return VppInterface.from_json(json)
|
||||||
|
elif obj_type == "vpp_bond":
|
||||||
|
return VppBond.from_json(json)
|
||||||
elif obj_type == "contrail_vrouter":
|
elif obj_type == "contrail_vrouter":
|
||||||
return ContrailVrouter.from_json(json)
|
return ContrailVrouter.from_json(json)
|
||||||
elif obj_type == "contrail_vrouter_dpdk":
|
elif obj_type == "contrail_vrouter_dpdk":
|
||||||
@ -1225,6 +1227,61 @@ class VppInterface(_BaseOpts):
|
|||||||
options=options)
|
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):
|
class ContrailVrouter(_BaseOpts):
|
||||||
"""Base class for Contrail Interface.
|
"""Base class for Contrail Interface.
|
||||||
|
|
||||||
|
@ -669,6 +669,54 @@ definitions:
|
|||||||
- name
|
- name
|
||||||
additionalProperties: False
|
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:
|
contrail_vrouter:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -1135,6 +1183,7 @@ items:
|
|||||||
- $ref: "#/definitions/nfvswitch_internal"
|
- $ref: "#/definitions/nfvswitch_internal"
|
||||||
- $ref: "#/definitions/ib_interface"
|
- $ref: "#/definitions/ib_interface"
|
||||||
- $ref: "#/definitions/vpp_interface"
|
- $ref: "#/definitions/vpp_interface"
|
||||||
|
- $ref: "#/definitions/vpp_bond"
|
||||||
- $ref: "#/definitions/contrail_vrouter"
|
- $ref: "#/definitions/contrail_vrouter"
|
||||||
- $ref: "#/definitions/contrail_vrouter_dpdk"
|
- $ref: "#/definitions/contrail_vrouter_dpdk"
|
||||||
minItems: 1
|
minItems: 1
|
||||||
|
@ -1256,3 +1256,55 @@ class TestVppInterface(base.TestCase):
|
|||||||
self.assertEqual("em1", vpp_interface.name)
|
self.assertEqual("em1", vpp_interface.name)
|
||||||
self.assertEqual("uio_pci_generic", vpp_interface.uio_driver)
|
self.assertEqual("uio_pci_generic", vpp_interface.uio_driver)
|
||||||
self.assertEqual("vlan-strip-offload off", vpp_interface.options)
|
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 = '''
|
_INITIAL_VPP_CONFIG = '''
|
||||||
unix {
|
unix {
|
||||||
nodaemon
|
nodaemon
|
||||||
@ -339,8 +346,8 @@ class TestUtils(base.TestCase):
|
|||||||
|
|
||||||
shutil.rmtree(tmpdir)
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
def test_get_vpp_interface_name(self):
|
def test_get_vpp_interface(self):
|
||||||
def test_execute(name, dummy1, dummy2=None, dummy3=None):
|
def test_execute(name, *args, **kwargs):
|
||||||
if 'systemctl' in name:
|
if 'systemctl' in name:
|
||||||
return None, None
|
return None, None
|
||||||
if 'vppctl' in name:
|
if 'vppctl' in name:
|
||||||
@ -348,19 +355,37 @@ class TestUtils(base.TestCase):
|
|||||||
|
|
||||||
self.stubs.Set(processutils, 'execute', test_execute)
|
self.stubs.Set(processutils, 'execute', test_execute)
|
||||||
|
|
||||||
self.assertEqual('GigabitEthernet0/9/0',
|
int_info = utils._get_vpp_interface('0000:00:09.0')
|
||||||
utils._get_vpp_interface_name('0000:00:09.0'))
|
self.assertIsNotNone(int_info)
|
||||||
self.assertIsNone(utils._get_vpp_interface_name(None))
|
self.assertEqual('GigabitEthernet0/9/0', int_info['name'])
|
||||||
self.assertIsNone(utils._get_vpp_interface_name('0000:01:09.0'))
|
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,
|
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',
|
@mock.patch('os_net_config.utils.processutils.execute',
|
||||||
return_value=('', None))
|
return_value=('', None))
|
||||||
def test_get_vpp_interface_name_multiple_iterations(self, mock_execute):
|
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)
|
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):
|
def test_generate_vpp_config(self):
|
||||||
tmpdir = tempfile.mkdtemp()
|
tmpdir = tempfile.mkdtemp()
|
||||||
config_path = os.path.join(tmpdir, 'startup.conf')
|
config_path = os.path.join(tmpdir, 'startup.conf')
|
||||||
@ -374,6 +399,7 @@ class TestUtils(base.TestCase):
|
|||||||
int2 = objects.VppInterface('em2')
|
int2 = objects.VppInterface('em2')
|
||||||
int2.pci_dev = '0000:00:09.1'
|
int2.pci_dev = '0000:00:09.1'
|
||||||
interfaces = [int1, int2]
|
interfaces = [int1, int2]
|
||||||
|
bonds = []
|
||||||
expected_config = '''
|
expected_config = '''
|
||||||
unix {
|
unix {
|
||||||
exec %s
|
exec %s
|
||||||
@ -399,9 +425,45 @@ dpdk {
|
|||||||
}
|
}
|
||||||
''' % vpp_exec_path
|
''' % vpp_exec_path
|
||||||
self.assertEqual(expected_config,
|
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):
|
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():
|
def test_get_dpdk_map():
|
||||||
return [{'name': 'eth1', 'pci_address': '0000:00:09.0',
|
return [{'name': 'eth1', 'pci_address': '0000:00:09.0',
|
||||||
'mac_address': '01:02:03:04:05:06',
|
'mac_address': '01:02:03:04:05:06',
|
||||||
@ -409,15 +471,15 @@ dpdk {
|
|||||||
|
|
||||||
self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map)
|
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
|
return None, None
|
||||||
self.stubs.Set(processutils, 'execute', test_execute)
|
self.stubs.Set(processutils, 'execute', test_execute)
|
||||||
|
|
||||||
def test_get_vpp_interface_name(pci_dev, tries, timeout):
|
def test_get_vpp_interface(pci_dev, tries, timeout):
|
||||||
return 'GigabitEthernet0/9/0'
|
return {'name': 'GigabitEthernet0/9/0', 'index': '1'}
|
||||||
|
|
||||||
self.stubs.Set(utils, '_get_vpp_interface_name',
|
self.stubs.Set(utils, '_get_vpp_interface',
|
||||||
test_get_vpp_interface_name)
|
test_get_vpp_interface)
|
||||||
|
|
||||||
int1 = objects.VppInterface('eth1', options="vlan-strip-offload off")
|
int1 = objects.VppInterface('eth1', options="vlan-strip-offload off")
|
||||||
int1.pci_dev = '0000:00:09.0'
|
int1.pci_dev = '0000:00:09.0'
|
||||||
@ -427,7 +489,7 @@ dpdk {
|
|||||||
int2.hwaddr = '01:02:03:04:05:07'
|
int2.hwaddr = '01:02:03:04:05:07'
|
||||||
interfaces = [int1, int2]
|
interfaces = [int1, int2]
|
||||||
|
|
||||||
utils.update_vpp_mapping(interfaces)
|
utils.update_vpp_mapping(interfaces, [])
|
||||||
|
|
||||||
contents = utils.get_file_data(utils._DPDK_MAPPING_FILE)
|
contents = utils.get_file_data(utils._DPDK_MAPPING_FILE)
|
||||||
|
|
||||||
|
@ -355,11 +355,12 @@ def restart_vpp(vpp_interfaces):
|
|||||||
processutils.execute('systemctl', 'restart', 'vpp')
|
processutils.execute('systemctl', 'restart', 'vpp')
|
||||||
|
|
||||||
|
|
||||||
def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
def _get_vpp_interface(pci_addr, tries=1, timeout=5):
|
||||||
"""Get VPP interface name from a given PCI address
|
"""Get VPP interface information from a given PCI address
|
||||||
|
|
||||||
From a running VPP instance, attempt to find the interface name from
|
From a running VPP instance, attempt to find the interface name and index
|
||||||
a given PCI address of a NIC.
|
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
|
:param pci_addr: PCI address to lookup, in the form of DDDD:BB:SS.F, where
|
||||||
- DDDD = Domain
|
- DDDD = Domain
|
||||||
@ -377,7 +378,8 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
|||||||
try:
|
try:
|
||||||
timestamp = time.time()
|
timestamp = time.time()
|
||||||
processutils.execute('systemctl', 'is-active', 'vpp')
|
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))
|
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])',
|
m = re.search(r':([0-9a-fA-F]{2}):([0-9a-fA-F]{2}).([0-9a-fA-F])',
|
||||||
pci_addr)
|
pci_addr)
|
||||||
@ -388,10 +390,12 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
|||||||
else:
|
else:
|
||||||
raise VppException('Invalid PCI address format: %s' % pci_addr)
|
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:
|
if m:
|
||||||
logger.debug('VPP interface found: %s' % m.group(1))
|
logger.debug('VPP interface found: %s, index: %s' %
|
||||||
return m.group(1)
|
(m.group(1), m.group(2)))
|
||||||
|
return {'name': m.group(1), 'index': m.group(2)}
|
||||||
except processutils.ProcessExecutionError:
|
except processutils.ProcessExecutionError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -402,7 +406,36 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
|||||||
return None
|
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 configuration content for VPP
|
||||||
|
|
||||||
Generate interface related configuration content for VPP. Current
|
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_config_path: VPP Configuration file path
|
||||||
:param vpp_interfaces: List of VPP interface objects
|
:param vpp_interfaces: List of VPP interface objects
|
||||||
|
:param vpp_bonds: List of VPP bond objects
|
||||||
:return: updated VPP config content.
|
:return: updated VPP config content.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -473,8 +507,32 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
|||||||
data,
|
data,
|
||||||
flags=re.MULTILINE)
|
flags=re.MULTILINE)
|
||||||
else:
|
else:
|
||||||
logger.debug('pci address not found for interface %s, may have'
|
raise VppException('Interface %s has no PCI address and is not'
|
||||||
'already been bound to vpp' % vpp_interface.name)
|
' 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
|
# Add start up script for VPP to config. This script will be executed by
|
||||||
# VPP on service start.
|
# VPP on service start.
|
||||||
@ -497,36 +555,29 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def update_vpp_mapping(vpp_interfaces):
|
def update_vpp_mapping(vpp_interfaces, vpp_bonds):
|
||||||
"""Verify VPP interface binding and update mapping file
|
"""Verify VPP interface binding and update mapping file
|
||||||
|
|
||||||
VppException will be raised if interfaces are not properly bound.
|
VppException will be raised if interfaces are not properly bound.
|
||||||
|
|
||||||
:param vpp_interfaces: List of VPP interface objects
|
: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:
|
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
|
# Try to get VPP interface name. In case VPP service is down
|
||||||
# for some reason, we will restart VPP and try again. Currently
|
# for some reason, we will restart VPP and try again. Currently
|
||||||
# only trying one more time, can turn into a retry_counter if needed
|
# only trying one more time, can turn into a retry_counter if needed
|
||||||
# in the future.
|
# in the future.
|
||||||
for i in range(2):
|
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)
|
tries=12, timeout=5)
|
||||||
if not vpp_name:
|
if not int_info:
|
||||||
restart_vpp(vpp_interfaces)
|
restart_vpp(vpp_interfaces)
|
||||||
else:
|
else:
|
||||||
|
vpp_int.vpp_name = int_info['name']
|
||||||
|
vpp_int.vpp_idx = int_info['index']
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise VppException('Interface %s with pci address %s not '
|
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))
|
% (vpp_int.name, vpp_int.pci_dev))
|
||||||
|
|
||||||
# Generate content of startup script for VPP
|
# 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:
|
for address in vpp_int.addresses:
|
||||||
vpp_start_cli += 'set interface state %s up\n' % vpp_name
|
cli_list.append('set interface ip address %s %s/%s\n'
|
||||||
vpp_start_cli += 'set interface ip address %s %s/%s\n' \
|
% (int_info['name'], address.ip,
|
||||||
% (vpp_name, address.ip, address.prefixlen)
|
address.prefixlen))
|
||||||
|
|
||||||
logger.info('Updating mapping for vpp interface %s:'
|
logger.info('Updating mapping for vpp interface %s:'
|
||||||
'pci_dev: %s mac address: %s uio driver: %s'
|
'pci_dev: %s mac address: %s uio driver: %s'
|
||||||
@ -545,9 +599,30 @@ def update_vpp_mapping(vpp_interfaces):
|
|||||||
vpp_int.uio_driver))
|
vpp_int.uio_driver))
|
||||||
_update_dpdk_map(vpp_int.name, vpp_int.pci_dev, vpp_int.hwaddr,
|
_update_dpdk_map(vpp_int.name, vpp_int.pci_dev, vpp_int.hwaddr,
|
||||||
vpp_int.uio_driver)
|
vpp_int.uio_driver)
|
||||||
# Enable VPP service to make the VPP interface configuration
|
|
||||||
# persistent.
|
for vpp_bond in vpp_bonds:
|
||||||
processutils.execute('systemctl', 'enable', 'vpp')
|
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):
|
if diff(_VPP_EXEC_FILE, vpp_start_cli):
|
||||||
write_config(_VPP_EXEC_FILE, vpp_start_cli)
|
write_config(_VPP_EXEC_FILE, vpp_start_cli)
|
||||||
restart_vpp(vpp_interfaces)
|
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