Adding IPoIB child interfaces to the os-net-config
This patch adds support for implementing IPoIB child interfaces using the os-net-config. It allows the user to create persistent IB childs using ifcfg implementation. It adds the type ib_child_interface to the os-net-config interfaces which needs the parent interface and the pkey id as required fields. The IPoIB child interface should be a seperate interface from the parent interface. Change-Id: I9ae739c892318c28ad6165a96644719fa1aa693d
This commit is contained in:
parent
c9d3ba4cc4
commit
fd9be8fdb5
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"network_config": [
|
||||
{
|
||||
"type": "ib_interface",
|
||||
"name": "ib0",
|
||||
"use_dhcp": false
|
||||
},
|
||||
{
|
||||
"type": "ib_child_interface",
|
||||
"parent": "ib0",
|
||||
"pkey_id": "100",
|
||||
"use_dhcp": false,
|
||||
"addresses": [
|
||||
{
|
||||
"ip_netmask": "10.20.30.40/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
network_config:
|
||||
# Note(abdallahyas): a parent InfiniBand interface is needed to be up for
|
||||
# the IPoIB pkey interface to work. The ib0 interface here is just there
|
||||
# to make sure that it is up, it can be configured separately.
|
||||
|
||||
-
|
||||
type: ib_interface
|
||||
name: ib0
|
||||
use_dhcp: false
|
||||
|
||||
-
|
||||
type: ib_child_interface
|
||||
parent: ib0
|
||||
pkey_id: "100"
|
||||
use_dhcp: false
|
||||
addresses:
|
||||
-
|
||||
ip_netmask: 10.20.30.40/24
|
|
@ -98,6 +98,8 @@ class NetConfig(object):
|
|||
self.add_ovs_patch_port(obj)
|
||||
elif isinstance(obj, objects.IbInterface):
|
||||
self.add_ib_interface(obj)
|
||||
elif isinstance(obj, objects.IbChildInterface):
|
||||
self.add_ib_child_interface(obj)
|
||||
elif isinstance(obj, objects.OvsDpdkPort):
|
||||
self.add_ovs_dpdk_port(obj)
|
||||
elif isinstance(obj, objects.OvsDpdkBond):
|
||||
|
@ -215,6 +217,14 @@ class NetConfig(object):
|
|||
"""
|
||||
raise NotImplementedError("add_ib_interface is not implemented.")
|
||||
|
||||
def add_ib_child_interface(self, ib_child_interface):
|
||||
"""Add an InfiniBand child interface object to the net config object.
|
||||
|
||||
:param ib_child_interface: The InfiniBand child
|
||||
interface object to add.
|
||||
"""
|
||||
raise NotImplementedError("add_ib_child_interface is not implemented.")
|
||||
|
||||
def add_ovs_dpdk_port(self, ovs_dpdk_port):
|
||||
"""Add a OvsDpdkPort object to the net config object.
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import os_net_config
|
|||
from os_net_config import objects
|
||||
from os_net_config import utils
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Import the raw NetConfig object so we can call its methods
|
||||
|
@ -134,6 +133,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
self.nfvswitch_intiface_data = {}
|
||||
self.nfvswitch_options = None
|
||||
self.vlan_data = {}
|
||||
self.ib_childs_data = {}
|
||||
self.route_data = {}
|
||||
self.route6_data = {}
|
||||
self.route_table_data = {}
|
||||
|
@ -374,6 +374,11 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
data += "TYPE=NFVSWITCHIntPort\n"
|
||||
elif isinstance(base_opt, objects.IbInterface):
|
||||
data += "TYPE=Infiniband\n"
|
||||
elif isinstance(base_opt, objects.IbChildInterface):
|
||||
data += "TYPE=Infiniband\n"
|
||||
data += "PKEY=yes\n"
|
||||
data += "PHYSDEV=%s\n" % base_opt.parent
|
||||
data += "PKEY_ID=%s\n" % base_opt.pkey_id
|
||||
elif re.match('\w+\.\d+$', base_opt.name):
|
||||
data += "VLAN=yes\n"
|
||||
elif isinstance(base_opt, objects.Interface):
|
||||
|
@ -887,6 +892,22 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
% (ib_interface.hwname, ib_interface.name))
|
||||
self.renamed_interfaces[ib_interface.hwname] = ib_interface.name
|
||||
|
||||
def add_ib_child_interface(self, ib_child_interface):
|
||||
"""Add an InfiniBand child interface object to the net config object.
|
||||
|
||||
:param ib_child_interface: The InfiniBand child
|
||||
interface object to add.
|
||||
"""
|
||||
logger.info('adding ib_child_interface: %s' % ib_child_interface.name)
|
||||
data = self._add_common(ib_child_interface)
|
||||
logger.debug('ib_child_interface data: %s' % data)
|
||||
self.ib_childs_data[ib_child_interface.name] = data
|
||||
if ib_child_interface.routes:
|
||||
self._add_routes(ib_child_interface.name,
|
||||
ib_child_interface.routes)
|
||||
if ib_child_interface.rules:
|
||||
self._add_rules(ib_child_interface.name, ib_child_interface.rules)
|
||||
|
||||
def add_ovs_dpdk_port(self, ovs_dpdk_port):
|
||||
"""Add a OvsDpdkPort object to the net config object.
|
||||
|
||||
|
@ -1153,6 +1174,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
logger.info('applying network configs...')
|
||||
restart_interfaces = []
|
||||
restart_vlans = []
|
||||
restart_ib_childs = []
|
||||
restart_bridges = []
|
||||
restart_linux_bonds = []
|
||||
restart_linux_teams = []
|
||||
|
@ -1514,6 +1536,51 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
if utils.diff(vlan_rule_path, rule_data):
|
||||
update_files[vlan_rule_path] = rule_data
|
||||
|
||||
for ib_child_name, ib_child_data in self.ib_childs_data.items():
|
||||
route_data = self.route_data.get(ib_child_name, '')
|
||||
route6_data = self.route6_data.get(ib_child_name, '')
|
||||
rule_data = self.rule_data.get(ib_child_name, '')
|
||||
ib_child_path = self.root_dir + ifcfg_config_path(ib_child_name)
|
||||
ib_child_route_path = \
|
||||
self.root_dir + route_config_path(ib_child_name)
|
||||
ib_child_route6_path = \
|
||||
self.root_dir + route6_config_path(ib_child_name)
|
||||
ib_child_rule_path = \
|
||||
self.root_dir + route_rule_config_path(ib_child_name)
|
||||
all_file_names.append(ib_child_path)
|
||||
all_file_names.append(ib_child_route_path)
|
||||
all_file_names.append(ib_child_route6_path)
|
||||
all_file_names.append(ib_child_rule_path)
|
||||
restarts_concatenated = itertools.chain(restart_interfaces,
|
||||
restart_bridges,
|
||||
restart_linux_bonds,
|
||||
restart_linux_teams)
|
||||
if (self.parse_ifcfg(ib_child_data).get('PHYSDEV') in
|
||||
restarts_concatenated):
|
||||
if ib_child_name not in restart_ib_childs:
|
||||
restart_ib_childs.append(ib_child_name)
|
||||
update_files[ib_child_path] = ib_child_data
|
||||
elif utils.diff(ib_child_path, ib_child_data):
|
||||
if self.ifcfg_requires_restart(ib_child_path, ib_child_data):
|
||||
restart_ib_childs.append(ib_child_name)
|
||||
else:
|
||||
apply_interfaces.append(
|
||||
(ib_child_name, ib_child_path, ib_child_data))
|
||||
update_files[ib_child_path] = ib_child_data
|
||||
else:
|
||||
logger.info('No changes required for the ib child interface: '
|
||||
'%s' % ib_child_name)
|
||||
if utils.diff(ib_child_route_path, route_data):
|
||||
update_files[ib_child_route_path] = route_data
|
||||
if ib_child_name not in restart_ib_childs:
|
||||
apply_routes.append((ib_child_name, route_data))
|
||||
if utils.diff(ib_child_route6_path, route6_data):
|
||||
update_files[ib_child_route6_path] = route6_data
|
||||
if ib_child_name not in restart_ib_childs:
|
||||
apply_routes.append((ib_child_name, route6_data))
|
||||
if utils.diff(ib_child_rule_path, rule_data):
|
||||
update_files[ib_child_rule_path] = rule_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,
|
||||
|
@ -1592,6 +1659,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
for vlan in restart_vlans:
|
||||
self.ifdown(vlan)
|
||||
|
||||
for ib_child in restart_ib_childs:
|
||||
self.ifdown(ib_child)
|
||||
|
||||
for interface in restart_interfaces:
|
||||
self.ifdown(interface)
|
||||
|
||||
|
@ -1691,6 +1761,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
|
|||
for nfvswitch_internal in nfvswitch_internal_ifaces:
|
||||
self.ifup(nfvswitch_internal)
|
||||
|
||||
for ib_child in restart_ib_childs:
|
||||
self.ifup(ib_child)
|
||||
|
||||
for vlan in restart_vlans:
|
||||
self.ifup(vlan)
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ def object_from_json(json):
|
|||
return OvsPatchPort.from_json(json)
|
||||
elif obj_type == "ib_interface":
|
||||
return IbInterface.from_json(json)
|
||||
elif obj_type == "ib_child_interface":
|
||||
return IbChildInterface.from_json(json)
|
||||
elif obj_type == "ovs_dpdk_port":
|
||||
return OvsDpdkPort.from_json(json)
|
||||
elif obj_type == "ovs_dpdk_bond":
|
||||
|
@ -1244,6 +1246,55 @@ class IbInterface(_BaseOpts):
|
|||
return IbInterface(name, *opts, ethtool_opts=ethtool_opts)
|
||||
|
||||
|
||||
class IbChildInterface(_BaseOpts):
|
||||
"""Base class for InfiniBand child network interfaces."""
|
||||
|
||||
def __init__(self, parent, pkey_id, use_dhcp=False, use_dhcpv6=False,
|
||||
addresses=None, routes=None, rules=None, mtu=None,
|
||||
primary=False, nic_mapping=None, persist_mapping=False,
|
||||
defroute=True, dhclient_args=None, dns_servers=None,
|
||||
nm_controlled=True, onboot=True, domain=None):
|
||||
addresses = addresses or []
|
||||
routes = routes or []
|
||||
rules = rules or []
|
||||
dns_servers = dns_servers or []
|
||||
self.pkey_id = pkey_id
|
||||
self.parent = parent
|
||||
full_pkey_id = 0x8000 | pkey_id
|
||||
name = "%s.%04x" % (parent, full_pkey_id)
|
||||
nm_controlled = True
|
||||
super(IbChildInterface, self).__init__(name, use_dhcp, use_dhcpv6,
|
||||
addresses, routes, rules, mtu,
|
||||
primary, nic_mapping,
|
||||
persist_mapping, defroute,
|
||||
dhclient_args, dns_servers,
|
||||
nm_controlled, onboot, domain)
|
||||
|
||||
@staticmethod
|
||||
def from_json(json):
|
||||
parent = _get_required_field(json, 'parent', 'IbChildInterface')
|
||||
pkey_id = _get_required_field(json, 'pkey_id', 'IbChildInterface')
|
||||
if type(pkey_id) == str:
|
||||
try:
|
||||
pkey_id = int(pkey_id)
|
||||
except ValueError:
|
||||
try:
|
||||
pkey_id = int(pkey_id, base=16)
|
||||
except Exception:
|
||||
# Note (Abdallahyas): We do not care for other
|
||||
# bases other than decimal and hexa
|
||||
msg = "pkey only support decimal and hex bases, not int"
|
||||
raise InvalidConfigException(msg)
|
||||
else:
|
||||
msg = "Invalid pkey type: got %s" % type(pkey_id)
|
||||
raise InvalidConfigException(msg)
|
||||
if (pkey_id < 0x0001 or pkey_id >= 0x7fff):
|
||||
msg = "Invalid pkey value 0x%X" % pkey_id
|
||||
raise InvalidConfigException(msg)
|
||||
opts = _BaseOpts.base_opts_from_json(json)
|
||||
return IbChildInterface(parent, pkey_id, *opts)
|
||||
|
||||
|
||||
class OvsDpdkPort(_BaseOpts):
|
||||
"""Base class for OVS Dpdk Ports."""
|
||||
|
||||
|
|
|
@ -1455,6 +1455,52 @@ definitions:
|
|||
- name
|
||||
additionalProperties: False
|
||||
|
||||
ib_child_interface:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
enum: ["ib_child_interface"]
|
||||
parent:
|
||||
$ref: "#/definitions/string_or_param"
|
||||
pkey_id:
|
||||
$ref: "#/definitions/string_or_param"
|
||||
primary:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
# common options:
|
||||
use_dhcp:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
use_dhcpv6:
|
||||
$ref: "#/definitions/bool_or_param"
|
||||
addresses:
|
||||
$ref: "#/definitions/list_of_address"
|
||||
routes:
|
||||
$ref: "#/definitions/list_of_route"
|
||||
rules:
|
||||
$ref: "#/definitions/list_of_rule"
|
||||
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"
|
||||
domain:
|
||||
$ref: "#/definitions/list_of_domain_name_string_or_domain_name_string"
|
||||
required:
|
||||
- type
|
||||
- parent
|
||||
- pkey_id
|
||||
additionalProperties: False
|
||||
|
||||
type: array
|
||||
items:
|
||||
oneOf:
|
||||
|
@ -1478,6 +1524,7 @@ items:
|
|||
- $ref: "#/definitions/nfvswitch_bridge"
|
||||
- $ref: "#/definitions/nfvswitch_internal"
|
||||
- $ref: "#/definitions/ib_interface"
|
||||
- $ref: "#/definitions/ib_child_interface"
|
||||
- $ref: "#/definitions/vpp_interface"
|
||||
- $ref: "#/definitions/vpp_bond"
|
||||
- $ref: "#/definitions/contrail_vrouter"
|
||||
|
|
|
@ -174,6 +174,19 @@ IPADDR2=10.0.0.2
|
|||
NETMASK2=255.0.0.0
|
||||
"""
|
||||
|
||||
_BASE_IB_CHILD_IFCFG = """# This file is autogenerated by os-net-config
|
||||
DEVICE=ib0.8001
|
||||
ONBOOT=yes
|
||||
HOTPLUG=no
|
||||
NM_CONTROLLED=yes
|
||||
PEERDNS=no
|
||||
TYPE=Infiniband
|
||||
PKEY=yes
|
||||
PHYSDEV=ib0
|
||||
PKEY_ID=1
|
||||
BOOTPROTO=none
|
||||
"""
|
||||
|
||||
_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes
|
||||
IPV6_AUTOCONF=no
|
||||
IPV6ADDR=2001:abc:a::/64
|
||||
|
@ -974,6 +987,13 @@ class TestIfcfgNetConfig(base.TestCase):
|
|||
self.get_interface_config('ib0'))
|
||||
self.assertEqual('', self.get_route_config())
|
||||
|
||||
def test_add_ib_child_interface(self):
|
||||
ib_child_interface = objects.IbChildInterface('ib0', 1)
|
||||
self.provider.add_interface(ib_child_interface)
|
||||
self.assertEqual(_BASE_IB_CHILD_IFCFG,
|
||||
self.get_interface_config('ib0.8001'))
|
||||
self.assertEqual('', self.get_route_config())
|
||||
|
||||
def test_add_contrail_vrouter(self):
|
||||
addresses = [objects.Address('10.0.0.30/24')]
|
||||
interface1 = objects.Interface('em3')
|
||||
|
|
|
@ -1597,6 +1597,67 @@ class TestIbInterface(base.TestCase):
|
|||
self.assertEqual("metric 10", route1.route_options)
|
||||
|
||||
|
||||
class TestIbChildInterface(base.TestCase):
|
||||
|
||||
def test_ib_child_interface_name(self):
|
||||
ib_child_interface = objects.IbChildInterface('foo', pkey_id=100)
|
||||
self.assertEqual("foo.8064", ib_child_interface.name)
|
||||
self.assertEqual("foo", ib_child_interface.parent)
|
||||
|
||||
def test_ib_child_interface_pkey_id_valid(self):
|
||||
ib_child_interface = objects.IbChildInterface('foo', pkey_id=100)
|
||||
self.assertEqual(100, ib_child_interface.pkey_id)
|
||||
|
||||
def test_ib_child_interface_pkek_id_str(self):
|
||||
data = '{"type": "ib_child_interface", ' \
|
||||
'"parent": "foo", "pkey_id": "100"}'
|
||||
ib_child_interface = \
|
||||
objects.IbChildInterface.from_json(json.loads(data))
|
||||
self.assertEqual(100, ib_child_interface.pkey_id)
|
||||
|
||||
def test_ib_child_interface_pkey_id_str_hexa(self):
|
||||
data = '{"type": "ib_child_interface", ' \
|
||||
'"parent": "foo", "pkey_id": "0x64"}'
|
||||
ib_child_interface = \
|
||||
objects.IbChildInterface.from_json(json.loads(data))
|
||||
self.assertEqual(100, ib_child_interface.pkey_id)
|
||||
|
||||
def test_ib_child_interface_pkey_id_str_invalid_base(self):
|
||||
data = '{"type": "ib_child_interface", ' \
|
||||
'"parent": "foo", "pkey_id": "0b01100100"}'
|
||||
self.assertRaises(objects.InvalidConfigException,
|
||||
objects.object_from_json,
|
||||
json.loads(data))
|
||||
|
||||
def test_ib_child_interface_nm_controlled_true(self):
|
||||
data = '{"type": "ib_child_interface", ' \
|
||||
'"parent": "foo", "pkey_id": "100"}'
|
||||
ib_child_interface = \
|
||||
objects.IbChildInterface.from_json(json.loads(data))
|
||||
self.assertEqual(True, ib_child_interface.nm_controlled)
|
||||
|
||||
def test_ib_child_interface_pkey_id_zero(self):
|
||||
data = '{"type": "ib_child_interface", ' \
|
||||
'"parent": "foo", "pkey_id": "0"}'
|
||||
self.assertRaises(objects.InvalidConfigException,
|
||||
objects.object_from_json,
|
||||
json.loads(data))
|
||||
|
||||
def test_ib_child_interface_pkey_id_managment(self):
|
||||
data = '{"type": "ib_child_interface", ' \
|
||||
'"parent": "foo", "pkey_id": "0x7fff"}'
|
||||
self.assertRaises(objects.InvalidConfigException,
|
||||
objects.object_from_json,
|
||||
json.loads(data))
|
||||
|
||||
def test_ib_child_interface_pkey_id_max(self):
|
||||
data = '{"type": "ib_child_interface", ' \
|
||||
'"parent": "foo", "pkey_id": "0x8001"}'
|
||||
self.assertRaises(objects.InvalidConfigException,
|
||||
objects.object_from_json,
|
||||
json.loads(data))
|
||||
|
||||
|
||||
class TestNicMapping(base.TestCase):
|
||||
|
||||
# We want to test the function, not the dummy..
|
||||
|
|
Loading…
Reference in New Issue