aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/os-net-config/samples/ovs_dpdk_bond.json44
-rw-r--r--etc/os-net-config/samples/ovs_dpdk_bond.yaml36
-rw-r--r--os_net_config/__init__.py9
-rw-r--r--os_net_config/impl_ifcfg.py35
-rw-r--r--os_net_config/objects.py70
-rw-r--r--os_net_config/tests/test_cli.py17
-rw-r--r--os_net_config/tests/test_impl_ifcfg.py24
-rw-r--r--os_net_config/tests/test_objects.py46
8 files changed, 280 insertions, 1 deletions
diff --git a/etc/os-net-config/samples/ovs_dpdk_bond.json b/etc/os-net-config/samples/ovs_dpdk_bond.json
new file mode 100644
index 0000000..af82e7b
--- /dev/null
+++ b/etc/os-net-config/samples/ovs_dpdk_bond.json
@@ -0,0 +1,44 @@
+{ "network_config": [
+ {
+ "type": "ovs_user_bridge",
+ "name": "br-link",
+ "members": [
+ {
+ "type" : "ovs_dpdk_bond",
+ "name" : "dpdkbond0",
+ "members": [
+ {
+ "type" : "ovs_dpdk_port",
+ "name" : "dpdk0",
+ "members": [
+ {
+ "type": "interface",
+ "name": "eth1"
+ }
+ ]
+ },
+ {
+ "type" : "ovs_dpdk_port",
+ "name" : "dpdk1",
+ "members": [
+ {
+ "type": "interface",
+ "name": "eth2"
+ }
+ ]
+ },
+ ]
+ },
+ {
+ "type" : "vlan",
+ "vlan_id" : 16,
+ "addresses" : [
+ {
+ "ip_netmask" : "192.0.2.1/24"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/etc/os-net-config/samples/ovs_dpdk_bond.yaml b/etc/os-net-config/samples/ovs_dpdk_bond.yaml
new file mode 100644
index 0000000..d51fa4a
--- /dev/null
+++ b/etc/os-net-config/samples/ovs_dpdk_bond.yaml
@@ -0,0 +1,36 @@
+# ovs_user_bridge type refers to the OVSUserBridge OVS ifup type, which will
+# have the datapath type set as 'netdev' for DPDK processing.
+# ovs_dpdk_port type refers to the OVSDPDKPort OVS ifup type, which will
+# add the port to the bridge with the interface type as 'dpdk'.
+# ovs_dpdk_bond type refers to the OVSDPDKBond OVS ifup type, which will
+# create the bond with dpdk interface type for dpdk ports
+
+network_config:
+ -
+ type: ovs_user_bridge
+ name: br-link
+ members:
+ -
+ type: ovs_dpdk_bond
+ name: dpdkbond0
+ members:
+ -
+ type: ovs_dpdk_port
+ name: dpdk0
+ members:
+ -
+ type: interface
+ name: eth1
+ -
+ type: ovs_dpdk_port
+ name: dpdk1
+ members:
+ -
+ type: interface
+ name: eth2
+ -
+ type: vlan
+ vlan_id: 16
+ addresses:
+ -
+ ip_netmask: 192.0.2.1/24 \ No newline at end of file
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py
index d307980..19d4a1e 100644
--- a/os_net_config/__init__.py
+++ b/os_net_config/__init__.py
@@ -93,6 +93,8 @@ class NetConfig(object):
self.add_ib_interface(obj)
elif isinstance(obj, objects.OvsDpdkPort):
self.add_ovs_dpdk_port(obj)
+ elif isinstance(obj, objects.OvsDpdkBond):
+ self.add_ovs_dpdk_bond(obj)
def add_interface(self, interface):
"""Add an Interface object to the net config object.
@@ -192,6 +194,13 @@ class NetConfig(object):
"""
raise NotImplemented("add_ovs_dpdk_port is not implemented.")
+ def add_ovs_dpdk_bond(self, ovs_dpdk_bond):
+ """Add a OvsDpdkBond object to the net config object.
+
+ :param ovs_dpdk_bond: The OvsDpdkBond object to add.
+ """
+ raise NotImplemented("add_ovs_dpdk_bond is not implemented.")
+
def apply(self, cleanup=False):
"""Apply the network configuration.
diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py
index c3d45bf..79e3e42 100644
--- a/os_net_config/impl_ifcfg.py
+++ b/os_net_config/impl_ifcfg.py
@@ -242,6 +242,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "DEVICETYPE=ovs\n"
data += "TYPE=OVSDPDKPort\n"
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
+ elif isinstance(base_opt, objects.OvsDpdkBond):
+ ovs_extra.extend(base_opt.ovs_extra)
+ if base_opt.primary_interface_name:
+ primary_name = base_opt.primary_interface_name
+ self.bond_primary_ifaces[base_opt.name] = primary_name
+ data += "DEVICETYPE=ovs\n"
+ data += "TYPE=OVSDPDKBond\n"
+ data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
+ if base_opt.members:
+ members = [member.name for member in base_opt.members]
+ self.member_names[base_opt.name] = members
+ data += ("BOND_IFACES=\"%s\"\n" % " ".join(members))
+ if base_opt.ovs_options:
+ data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
+ ovs_extra.extend(base_opt.ovs_extra)
else:
if base_opt.use_dhcp:
data += "BOOTPROTO=dhcp\n"
@@ -524,6 +539,26 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.debug('ovs dpdk port data: %s' % data)
self.interface_data[ovs_dpdk_port.name] = data
+ def add_ovs_dpdk_bond(self, ovs_dpdk_bond):
+ """Add an OvsDPDKBond object to the net config object.
+
+ :param ovs_dpdk_bond: The OvsBond object to add.
+ """
+ logger.info('adding ovs dpdk bond: %s' % ovs_dpdk_bond.name)
+
+ # Bind the dpdk interface
+ for dpdk_port in ovs_dpdk_bond.members:
+ # DPDK Port will have only one member of type Interface, validation
+ # checks are added at the object creation stage.
+ ifname = dpdk_port.members[0].name
+ utils.bind_dpdk_interfaces(ifname, dpdk_port.driver, self.noop)
+
+ data = self._add_common(ovs_dpdk_bond)
+ logger.debug('ovs dpdk bond data: %s' % data)
+ self.interface_data[ovs_dpdk_bond.name] = data
+ if ovs_dpdk_bond.routes:
+ self._add_routes(ovs_dpdk_bond.name, ovs_dpdk_bond.routes)
+
def generate_ivs_config(self, ivs_uplinks, ivs_interfaces):
"""Generate configuration content for ivs."""
diff --git a/os_net_config/objects.py b/os_net_config/objects.py
index 725df38..154dccf 100644
--- a/os_net_config/objects.py
+++ b/os_net_config/objects.py
@@ -64,6 +64,8 @@ def object_from_json(json):
return IbInterface.from_json(json)
elif obj_type == "ovs_dpdk_port":
return OvsDpdkPort.from_json(json)
+ elif obj_type == "ovs_dpdk_bond":
+ return OvsDpdkBond.from_json(json)
def _get_required_field(json, name, object_name):
@@ -457,7 +459,8 @@ class OvsUserBridge(_BaseOpts):
for member in self.members:
member.bridge_name = name
if not isinstance(member, OvsTunnel) and \
- not isinstance(member, OvsDpdkPort):
+ not isinstance(member, OvsDpdkPort) and \
+ not isinstance(member, OvsDpdkBond):
member.ovs_port = True
if member.primary:
if self.primary_interface_name:
@@ -1005,3 +1008,68 @@ class OvsDpdkPort(_BaseOpts):
opts = _BaseOpts.base_opts_from_json(json)
return OvsDpdkPort(name, *opts, members=members, driver=driver,
ovs_options=ovs_options, ovs_extra=ovs_extra)
+
+
+class OvsDpdkBond(_BaseOpts):
+ """Base class for OVS DPDK bonds."""
+
+ def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
+ routes=None, mtu=None, primary=False, members=None,
+ ovs_options=None, ovs_extra=None, nic_mapping=None,
+ persist_mapping=False, defroute=True, dhclient_args=None,
+ dns_servers=None):
+ super(OvsDpdkBond, self).__init__(name, use_dhcp, use_dhcpv6,
+ addresses, routes, mtu, primary,
+ nic_mapping, persist_mapping,
+ defroute, dhclient_args, dns_servers)
+ self.members = members or []
+ self.ovs_options = ovs_options
+ self.ovs_extra = ovs_extra or []
+
+ for member in self.members:
+ if member.primary:
+ if self.primary_interface_name:
+ msg = 'Only one primary interface allowed per bond (dpdk).'
+ raise InvalidConfigException(msg)
+ if member.primary_interface_name:
+ self.primary_interface_name = member.primary_interface_name
+ else:
+ self.primary_interface_name = member.name
+ if not self.primary_interface_name:
+ bond_members = list(self.members)
+ bond_members.sort(key=lambda x: x.name)
+ self.primary_interface_name = bond_members[0].name
+
+ @staticmethod
+ def from_json(json):
+ name = _get_required_field(json, 'name', 'OvsDpdkBond')
+ (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
+ persist_mapping, defroute, dhclient_args,
+ dns_servers) = _BaseOpts.base_opts_from_json(
+ json, include_primary=False)
+ ovs_options = json.get('ovs_options')
+ ovs_extra = json.get('ovs_extra', [])
+ members = []
+
+ # members
+ members_json = json.get('members')
+ if members_json:
+ if isinstance(members_json, list):
+ for member in members_json:
+ obj = object_from_json(member)
+ if isinstance(obj, OvsDpdkPort):
+ members.append(obj)
+ else:
+ msg = 'Membrs must be of type ovs_dpdk_port'
+ raise InvalidConfigException(msg)
+ else:
+ msg = 'Members must be a list.'
+ raise InvalidConfigException(msg)
+
+ return OvsDpdkBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
+ addresses=addresses, routes=routes, mtu=mtu,
+ members=members, ovs_options=ovs_options,
+ ovs_extra=ovs_extra, nic_mapping=nic_mapping,
+ persist_mapping=persist_mapping,
+ defroute=defroute, dhclient_args=dhclient_args,
+ dns_servers=dns_servers)
diff --git a/os_net_config/tests/test_cli.py b/os_net_config/tests/test_cli.py
index 939b12e..c5c825f 100644
--- a/os_net_config/tests/test_cli.py
+++ b/os_net_config/tests/test_cli.py
@@ -147,6 +147,23 @@ class TestCli(base.TestCase):
'-c %s --detailed-exit-codes'
% interface_yaml, exitcodes=(0,))
+ def test_ovs_dpdk_bond_noop_output(self):
+ ivs_yaml = os.path.join(SAMPLE_BASE, 'ovs_dpdk_bond.yaml')
+ ivs_json = os.path.join(SAMPLE_BASE, 'ovs_dpdk_bond.json')
+ stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
+ '-c %s' % ivs_yaml)
+ self.assertEqual('', stderr)
+ stdout_json, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
+ '-c %s' % ivs_json)
+ self.assertEqual('', stderr)
+ sanity_devices = ['DEVICE=br-link',
+ 'TYPE=OVSUserBridge',
+ 'DEVICE=dpdkbond0',
+ 'TYPE=OVSDPDKBond']
+ for dev in sanity_devices:
+ self.assertIn(dev, stdout_yaml)
+ self.assertEqual(stdout_yaml, stdout_json)
+
def test_nfvswitch_noop_output(self):
nfvswitch_yaml = os.path.join(SAMPLE_BASE, 'nfvswitch.yaml')
nfvswitch_json = os.path.join(SAMPLE_BASE, 'nfvswitch.json')
diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py
index ad27d83..74dd1f9 100644
--- a/os_net_config/tests/test_impl_ifcfg.py
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -788,6 +788,30 @@ OVS_BRIDGE=br-link
self.provider.bridge_data['br-link'])
self.assertEqual(dpdk0_config, self.get_interface_config('dpdk0'))
+ def test_network_ovs_dpdk_bond(self):
+ iface0 = objects.Interface(name='eth1')
+ dpdk0 = objects.OvsDpdkPort(name='dpdk0', members=[iface0])
+ iface1 = objects.Interface(name='eth2')
+ dpdk1 = objects.OvsDpdkPort(name='dpdk1', members=[iface1])
+ bond = objects.OvsDpdkBond('dpdkbond0', members=[dpdk0, dpdk1])
+ bridge = objects.OvsUserBridge('br-link', members=[bond])
+ self.provider.add_bond(bond)
+ self.provider.add_bridge(bridge)
+
+ dpdk_bond_config = """# This file is autogenerated by os-net-config
+DEVICE=dpdkbond0
+ONBOOT=yes
+HOTPLUG=no
+NM_CONTROLLED=no
+PEERDNS=no
+DEVICETYPE=ovs
+TYPE=OVSDPDKBond
+OVS_BRIDGE=br-link
+BOND_IFACES="dpdk0 dpdk1"
+"""
+ self.assertEqual(dpdk_bond_config,
+ self.get_interface_config('dpdkbond0'))
+
class TestIfcfgNetConfigApply(base.TestCase):
diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py
index 7e8441c..92d43b9 100644
--- a/os_net_config/tests/test_objects.py
+++ b/os_net_config/tests/test_objects.py
@@ -830,3 +830,49 @@ class TestNicMapping(base.TestCase):
expected = {}
# This only emits a warning, so it should still work
self.assertEqual(expected, objects._mapped_nics())
+
+
+class TestOvsDpdkBond(base.TestCase):
+
+ def test_from_json_dhcp(self):
+ data = """{
+"type": "ovs_dpdk_bond",
+"name": "dpdkbond0",
+"use_dhcp": true,
+"members": [
+ {
+ "type": "ovs_dpdk_port",
+ "name": "dpdk0",
+ "members": [
+ {
+ "type": "interface",
+ "name": "eth1"
+ }
+ ]
+ },
+ {
+ "type": "ovs_dpdk_port",
+ "name": "dpdk1",
+ "members": [
+ {
+ "type": "interface",
+ "name": "eth2"
+ }
+ ]
+ }
+]
+}
+"""
+ bond = objects.object_from_json(json.loads(data))
+ self.assertEqual("dpdkbond0", bond.name)
+ self.assertTrue(bond.use_dhcp)
+ dpdk_port0 = bond.members[0]
+ self.assertEqual("dpdk0", dpdk_port0.name)
+ self.assertEqual("vfio-pci", dpdk_port0.driver)
+ iface1 = dpdk_port0.members[0]
+ self.assertEqual("eth1", iface1.name)
+ dpdk_port1 = bond.members[1]
+ self.assertEqual("dpdk1", dpdk_port1.name)
+ self.assertEqual("vfio-pci", dpdk_port1.driver)
+ iface2 = dpdk_port1.members[0]
+ self.assertEqual("eth2", iface2.name)