aboutsummaryrefslogtreecommitdiffstats
path: root/os_net_config
diff options
context:
space:
mode:
authorSarath Kumar <sarath.kutty@bigswitch.com>2016-07-21 11:14:39 -0700
committerSarath Kumar <sarath.kutty@bigswitch.com>2016-08-02 11:45:46 -0700
commit15974244f6212905540e1daf9ca596c83f510bdd (patch)
tree26bcd9086d93e4501e9387a126725a847ce3f9bb /os_net_config
parent311df0c3828fe7571079fad9763bca9e2414b51a (diff)
Enable os-net-config to support and configure NFVSwitch
These changes are to generate /etc/sysconf/network-scripts/ifcfg-* and /etc/sysconfig/nfvswitch configuration files for nfvswitch and its interfaces. NFVSwitch is a virtual switch implementation based on DPDK for datacenter workloads with very high throughput needs. Change-Id: If02edb9c4c54c014f67290fe0c34e2fc73cb95bd
Diffstat (limited to 'os_net_config')
-rw-r--r--os_net_config/__init__.py13
-rw-r--r--os_net_config/impl_ifcfg.py101
-rw-r--r--os_net_config/objects.py101
-rw-r--r--os_net_config/tests/test_cli.py18
-rw-r--r--os_net_config/tests/test_impl_ifcfg.py47
-rw-r--r--os_net_config/tests/test_objects.py81
6 files changed, 361 insertions, 0 deletions
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py
index 18c96a2..2aafc23 100644
--- a/os_net_config/__init__.py
+++ b/os_net_config/__init__.py
@@ -51,6 +51,8 @@ class NetConfig(object):
self.add_vlan(obj)
elif isinstance(obj, objects.IvsInterface):
self.add_ivs_interface(obj)
+ elif isinstance(obj, objects.NfvswitchInternal):
+ self.add_nfvswitch_internal(obj)
elif isinstance(obj, objects.OvsBridge):
self.add_bridge(obj)
for member in obj.members:
@@ -63,6 +65,10 @@ class NetConfig(object):
self.add_ivs_bridge(obj)
for member in obj.members:
self.add_object(member)
+ elif isinstance(obj, objects.NfvswitchBridge):
+ self.add_nfvswitch_bridge(obj)
+ for member in obj.members:
+ self.add_object(member)
elif isinstance(obj, objects.OvsBond):
self.add_bond(obj)
for member in obj.members:
@@ -117,6 +123,13 @@ class NetConfig(object):
"""
raise NotImplemented("add_ivs_bridge is not implemented.")
+ def add_nfvswitch_bridge(self, bridge):
+ """Add a NfvswitchBridge object to the net config object.
+
+ :param bridge: The NfvswitchBridge object to add.
+ """
+ raise NotImplemented("add_nfvswitch_bridge is not implemented.")
+
def add_bond(self, bond):
"""Add an OvsBond object to the net config object.
diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py
index 49ad317..a478e27 100644
--- a/os_net_config/impl_ifcfg.py
+++ b/os_net_config/impl_ifcfg.py
@@ -39,6 +39,10 @@ def ivs_config_path():
return "/etc/sysconfig/ivs"
+def nfvswitch_config_path():
+ return "/etc/sysconfig/nfvswitch"
+
+
def route_config_path(name):
return "/etc/sysconfig/network-scripts/route-%s" % name
@@ -58,6 +62,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
super(IfcfgNetConfig, self).__init__(noop, root_dir)
self.interface_data = {}
self.ivsinterface_data = {}
+ self.nfvswitch_intiface_data = {}
+ self.nfvswitch_cpus = None
self.vlan_data = {}
self.route_data = {}
self.route6_data = {}
@@ -103,6 +109,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "PHYSDEV=%s\n" % base_opt.linux_bond_name
elif isinstance(base_opt, objects.IvsInterface):
data += "TYPE=IVSIntPort\n"
+ elif isinstance(base_opt, objects.NfvswitchInternal):
+ data += "TYPE=NFVSWITCHIntPort\n"
elif isinstance(base_opt, objects.IbInterface):
data += "TYPE=Infiniband\n"
elif re.match('\w+\.\d+$', base_opt.name):
@@ -117,6 +125,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if base_opt.ivs_bridge_name:
data += "DEVICETYPE=ivs\n"
data += "IVS_BRIDGE=%s\n" % base_opt.ivs_bridge_name
+ if base_opt.nfvswitch_bridge_name:
+ data += "DEVICETYPE=nfvswitch\n"
+ data += "NFVSWITCH_BRIDGE=%s\n" % base_opt.nfvswitch_bridge_name
if base_opt.ovs_port:
if not isinstance(base_opt, objects.LinuxTeam):
data += "DEVICETYPE=ovs\n"
@@ -333,6 +344,19 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if ivs_interface.routes:
self._add_routes(ivs_interface.name, ivs_interface.routes)
+ def add_nfvswitch_internal(self, nfvswitch_internal):
+ """Add a nfvswitch_internal interface object to the net config object.
+
+ :param nfvswitch_internal: The nfvswitch_internal object to add.
+ """
+ iface_name = nfvswitch_internal.name
+ logger.info('adding nfvswitch_internal interface: %s' % iface_name)
+ data = self._add_common(nfvswitch_internal)
+ logger.debug('nfvswitch_internal interface data: %s' % data)
+ self.nfvswitch_intiface_data[iface_name] = data
+ if nfvswitch_internal.routes:
+ self._add_routes(iface_name, nfvswitch_internal.routes)
+
def add_bridge(self, bridge):
"""Add an OvsBridge object to the net config object.
@@ -369,6 +393,16 @@ class IfcfgNetConfig(os_net_config.NetConfig):
"""
pass
+ def add_nfvswitch_bridge(self, bridge):
+ """Add a NFVSwitchBridge object to the net config object.
+
+ NFVSwitch can only support one virtual switch per node,
+ using "nfvswitch" as its name. As long as the nfvswitch service
+ is running, the nfvswitch virtual switch will be available.
+ :param bridge: The NfvswitchBridge object to add.
+ """
+ self.nfvswitch_cpus = bridge.cpus
+
def add_bond(self, bond):
"""Add an OvsBond object to the net config object.
@@ -462,6 +496,29 @@ class IfcfgNetConfig(os_net_config.NetConfig):
% (uplink_str, intf_str))
return data
+ def generate_nfvswitch_config(self, nfvswitch_ifaces,
+ nfvswitch_internal_ifaces):
+ """Generate configuration content for nfvswitch."""
+
+ cpu_str = ""
+ if self.nfvswitch_cpus:
+ cpu_str = " -c " + self.nfvswitch_cpus
+
+ ifaces = []
+ for iface in nfvswitch_ifaces:
+ ifaces.append(' -u ')
+ ifaces.append(iface)
+ iface_str = ''.join(ifaces)
+
+ ifaces = []
+ for iface in nfvswitch_internal_ifaces:
+ ifaces.append(' -m ')
+ ifaces.append(iface)
+ internal_str = ''.join(ifaces)
+
+ data = ("SETUP_ARGS=\"%s%s%s\"" % (cpu_str, iface_str, internal_str))
+ return data
+
def apply(self, cleanup=False, activate=True):
"""Apply the network configuration.
@@ -487,6 +544,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
all_file_names = []
ivs_uplinks = [] # ivs physical uplinks
ivs_interfaces = [] # ivs internal ports
+ nfvswitch_interfaces = [] # nfvswitch physical interfaces
+ nfvswitch_internal_ifaces = [] # nfvswitch internal/management ports
for interface_name, iface_data in self.interface_data.iteritems():
route_data = self.route_data.get(interface_name, '')
@@ -499,6 +558,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
all_file_names.append(route6_path)
if "IVS_BRIDGE" in iface_data:
ivs_uplinks.append(interface_name)
+ if "NFVSWITCH_BRIDGE" in iface_data:
+ nfvswitch_interfaces.append(interface_name)
all_file_names.append(route6_path)
if (utils.diff(interface_path, iface_data) or
utils.diff(route_path, route_data) or
@@ -533,6 +594,27 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('No changes required for ivs interface: %s' %
interface_name)
+ for iface_name, iface_data in self.nfvswitch_intiface_data.iteritems():
+ route_data = self.route_data.get(iface_name, '')
+ route6_data = self.route6_data.get(iface_name, '')
+ iface_path = self.root_dir + ifcfg_config_path(iface_name)
+ route_path = self.root_dir + route_config_path(iface_name)
+ route6_path = self.root_dir + route6_config_path(iface_name)
+ all_file_names.append(iface_path)
+ all_file_names.append(route_path)
+ all_file_names.append(route6_path)
+ nfvswitch_internal_ifaces.append(iface_name)
+ if (utils.diff(iface_path, iface_data) or
+ utils.diff(route_path, route_data)):
+ restart_interfaces.append(iface_name)
+ restart_interfaces.extend(self.child_members(iface_name))
+ update_files[iface_path] = iface_data
+ update_files[route_path] = route_data
+ update_files[route6_path] = route6_data
+ else:
+ logger.info('No changes required for nfvswitch interface: %s' %
+ iface_name)
+
for vlan_name, vlan_data in self.vlan_data.iteritems():
route_data = self.route_data.get(vlan_name, '')
route6_data = self.route6_data.get(vlan_name, '')
@@ -698,6 +780,12 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data = self.generate_ivs_config(ivs_uplinks, ivs_interfaces)
self.write_config(location, data)
+ if nfvswitch_interfaces or nfvswitch_internal_ifaces:
+ location = nfvswitch_config_path()
+ data = self.generate_nfvswitch_config(nfvswitch_interfaces,
+ nfvswitch_internal_ifaces)
+ self.write_config(location, data)
+
if activate:
for linux_bond in restart_linux_bonds:
self.ifup(linux_bond)
@@ -728,6 +816,19 @@ class IfcfgNetConfig(os_net_config.NetConfig):
self.execute(msg, '/usr/bin/systemctl',
'restart', 'ivs')
+ if nfvswitch_interfaces or nfvswitch_internal_ifaces:
+ logger.info("Attach to nfvswitch with "
+ "interfaces: %s, "
+ "internal interfaces: %s" %
+ (nfvswitch_interfaces, nfvswitch_internal_ifaces))
+ for nfvswitch_interface in nfvswitch_interfaces:
+ self.ifup(nfvswitch_interface)
+ for nfvswitch_internal in nfvswitch_internal_ifaces:
+ self.ifup(nfvswitch_internal)
+ msg = "Restart nfvswitch"
+ self.execute(msg, '/usr/bin/systemctl',
+ 'restart', 'nfvswitch')
+
for vlan in restart_vlans:
self.ifup(vlan)
diff --git a/os_net_config/objects.py b/os_net_config/objects.py
index 5137263..4a15d05 100644
--- a/os_net_config/objects.py
+++ b/os_net_config/objects.py
@@ -50,6 +50,10 @@ def object_from_json(json):
return IvsBridge.from_json(json)
elif obj_type == "ivs_interface":
return IvsInterface.from_json(json)
+ elif obj_type == "nfvswitch_bridge":
+ return NfvswitchBridge.from_json(json)
+ elif obj_type == "nfvswitch_internal":
+ return NfvswitchInternal.from_json(json)
elif obj_type == "ovs_tunnel":
return OvsTunnel.from_json(json)
elif obj_type == "ovs_patch_port":
@@ -181,6 +185,7 @@ class _BaseOpts(object):
self.bridge_name = None # internal
self.linux_bridge_name = None # internal
self.ivs_bridge_name = None # internal
+ self.nfvswitch_bridge_name = None # internal
self.linux_bond_name = None # internal
self.linux_team_name = None # internal
self.ovs_port = False # internal
@@ -333,6 +338,32 @@ class IvsInterface(_BaseOpts):
return IvsInterface(vlan_id, name, *opts)
+class NfvswitchInternal(_BaseOpts):
+ """Base class for nfvswitch internal interfaces."""
+
+ def __init__(self, vlan_id, name='nfvswitch', use_dhcp=False,
+ use_dhcpv6=False, addresses=None, routes=None, mtu=1500,
+ primary=False, nic_mapping=None, persist_mapping=False,
+ defroute=True, dhclient_args=None, dns_servers=None):
+ addresses = addresses or []
+ routes = routes or []
+ dns_servers = dns_servers or []
+ name_vlan = '%s%i' % (name, vlan_id)
+ super(NfvswitchInternal, self).__init__(name_vlan, use_dhcp,
+ use_dhcpv6, addresses, routes,
+ mtu, primary, nic_mapping,
+ persist_mapping, defroute,
+ dhclient_args, dns_servers)
+ self.vlan_id = int(vlan_id)
+
+ @staticmethod
+ def from_json(json):
+ name = json.get('name')
+ vlan_id = _get_required_field(json, 'vlan_id', 'NfvswitchInternal')
+ opts = _BaseOpts.base_opts_from_json(json)
+ return NfvswitchInternal(vlan_id, name, *opts)
+
+
class OvsBridge(_BaseOpts):
"""Base class for OVS bridges."""
@@ -510,6 +541,76 @@ class IvsBridge(_BaseOpts):
dns_servers=dns_servers)
+class NfvswitchBridge(_BaseOpts):
+ """Base class for NFVSwitch bridges.
+
+ NFVSwitch is a virtual switch for Linux.
+ It is compatible with the KVM hypervisor and uses DPDK for packet
+ forwarding.
+ """
+
+ def __init__(self, name='nfvswitch', use_dhcp=False, use_dhcpv6=False,
+ addresses=None, routes=None, mtu=1500, members=None,
+ nic_mapping=None, persist_mapping=False, defroute=True,
+ dhclient_args=None, dns_servers=None, cpus=""):
+ addresses = addresses or []
+ routes = routes or []
+ members = members or []
+ dns_servers = dns_servers or []
+ super(NfvswitchBridge, self).__init__(name, use_dhcp, use_dhcpv6,
+ addresses, routes, mtu, False,
+ nic_mapping, persist_mapping,
+ defroute, dhclient_args,
+ dns_servers)
+ self.cpus = cpus
+ self.members = members
+ for member in self.members:
+ if isinstance(member, OvsBond) or isinstance(member, LinuxBond):
+ msg = 'NFVSwitch does not support bond interfaces.'
+ raise InvalidConfigException(msg)
+ member.nfvswitch_bridge_name = name
+ member.ovs_port = False
+ self.primary_interface_name = None
+
+ @staticmethod
+ def from_json(json):
+ name = 'nfvswitch'
+ (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)
+
+ # members
+ members = []
+ members_json = json.get('members')
+ if members_json:
+ if isinstance(members_json, list):
+ for member in members_json:
+ members.append(object_from_json(member))
+ else:
+ msg = 'Members must be a list.'
+ raise InvalidConfigException(msg)
+
+ cpus = ''
+ cpus_json = json.get('cpus')
+ if cpus_json:
+ if isinstance(cpus_json, basestring):
+ cpus = cpus_json
+ else:
+ msg = '"cpus" must be a string of numbers separated by commas.'
+ raise InvalidConfigException(msg)
+ else:
+ msg = 'Config "cpus" is mandatory.'
+ raise InvalidConfigException(msg)
+
+ return NfvswitchBridge(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, cpus=cpus)
+
+
class LinuxTeam(_BaseOpts):
"""Base class for Linux bonds using teamd."""
diff --git a/os_net_config/tests/test_cli.py b/os_net_config/tests/test_cli.py
index 98477fc..e598dcb 100644
--- a/os_net_config/tests/test_cli.py
+++ b/os_net_config/tests/test_cli.py
@@ -146,3 +146,21 @@ class TestCli(base.TestCase):
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
'-c %s --detailed-exit-codes'
% interface_yaml, exitcodes=(0,))
+
+ def test_nfvswitch_noop_output(self):
+ nfvswitch_yaml = os.path.join(SAMPLE_BASE, 'nfvswitch.yaml')
+ nfvswitch_json = os.path.join(SAMPLE_BASE, 'nfvswitch.json')
+ stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
+ '-c %s' % nfvswitch_yaml)
+ self.assertEqual('', stderr)
+ stdout_json, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
+ '-c %s' % nfvswitch_json)
+ self.assertEqual('', stderr)
+ sanity_devices = ['DEVICE=nic2',
+ 'DEVICE=nic3',
+ 'DEVICE=api201',
+ 'DEVICE=storage202',
+ 'DEVICETYPE=nfvswitch']
+ for dev in sanity_devices:
+ self.assertIn(dev, stdout_yaml)
+ self.assertEqual(stdout_yaml, stdout_json)
diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py
index b8a8402..467db65 100644
--- a/os_net_config/tests/test_impl_ifcfg.py
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -292,6 +292,34 @@ NETMASK=255.255.255.0
_IVS_CONFIG = ('DAEMON_ARGS=\"--hitless --certificate /etc/ivs '
'--inband-vlan 4092 -u em1 --internal-port=storage5\"')
+_NFVSWITCH_INTERFACE = """# This file is autogenerated by os-net-config
+DEVICE=em1
+ONBOOT=yes
+HOTPLUG=no
+NM_CONTROLLED=no
+PEERDNS=no
+DEVICETYPE=nfvswitch
+NFVSWITCH_BRIDGE=nfvswitch
+BOOTPROTO=none
+"""
+
+_NFVSWITCH_INTERNAL = """# This file is autogenerated by os-net-config
+DEVICE=storage5
+ONBOOT=yes
+HOTPLUG=no
+NM_CONTROLLED=no
+PEERDNS=no
+TYPE=NFVSWITCHIntPort
+DEVICETYPE=nfvswitch
+NFVSWITCH_BRIDGE=nfvswitch
+MTU=1500
+BOOTPROTO=static
+IPADDR=172.16.2.7
+NETMASK=255.255.255.0
+"""
+
+_NFVSWITCH_CONFIG = ('SETUP_ARGS=\" -c 2,3,4,5 -u em1 -m storage5\"')
+
_OVS_IFCFG_PATCH_PORT = """# This file is autogenerated by os-net-config
DEVICE=br-pub-patch
ONBOOT=yes
@@ -553,6 +581,25 @@ class TestIfcfgNetConfig(base.TestCase):
data = self.provider.generate_ivs_config(['em1'], ['storage5'])
self.assertEqual(_IVS_CONFIG, data)
+ def test_network_nfvswitch_with_interfaces_and_internal_interfaces(self):
+ interface = objects.Interface('em1')
+ v4_addr = objects.Address('172.16.2.7/24')
+ nfvswitch_internal = objects.NfvswitchInternal(vlan_id=5,
+ name='storage',
+ addresses=[v4_addr])
+ iface_name = nfvswitch_internal.name
+ bridge = objects.NfvswitchBridge(members=[interface,
+ nfvswitch_internal],
+ cpus="2,3,4,5")
+ self.provider.add_interface(interface)
+ self.provider.add_nfvswitch_internal(nfvswitch_internal)
+ self.provider.add_nfvswitch_bridge(bridge)
+ self.assertEqual(_NFVSWITCH_INTERFACE, self.get_interface_config())
+ self.assertEqual(_NFVSWITCH_INTERNAL,
+ self.provider.nfvswitch_intiface_data[iface_name])
+ data = self.provider.generate_nfvswitch_config(['em1'], ['storage5'])
+ self.assertEqual(_NFVSWITCH_CONFIG, data)
+
def test_add_ib_interface_with_v4_multiple(self):
addresses = [objects.Address('192.168.1.2/24'),
objects.Address('192.168.1.3/32'),
diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py
index 17b6927..bbff1f3 100644
--- a/os_net_config/tests/test_objects.py
+++ b/os_net_config/tests/test_objects.py
@@ -398,6 +398,87 @@ class TestIvsBridge(base.TestCase):
self.assertIn(expected, err)
+class TestNfvswitchBridge(base.TestCase):
+
+ def test_from_json(self):
+ data = """{
+"type": "nfvswitch_bridge",
+"cpus": "2,3,4,5",
+"members": [
+ {"type": "interface", "name": "nic2"}
+ ]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("nfvswitch", bridge.name)
+ self.assertEqual("2,3,4,5", bridge.cpus)
+ interface1 = bridge.members[0]
+ self.assertEqual("nic2", interface1.name)
+ self.assertEqual(False, interface1.ovs_port)
+ self.assertEqual("nfvswitch", interface1.nfvswitch_bridge_name)
+
+
+class TestNfvswitchInterface(base.TestCase):
+
+ def test_interface_from_json(self):
+ data = """{
+"type": "nfvswitch_bridge",
+"cpus": "2,3,4,5",
+"members": [
+ {"type": "interface","name": "nic1"},
+ {"type": "interface","name": "nic2"}
+ ]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("nfvswitch", bridge.name)
+ self.assertEqual("2,3,4,5", bridge.cpus)
+ interface1 = bridge.members[0]
+ self.assertEqual("nic1", interface1.name)
+ interface2 = bridge.members[1]
+ self.assertEqual("nic2", interface2.name)
+ self.assertEqual(False, interface2.ovs_port)
+ self.assertEqual("nfvswitch", interface1.nfvswitch_bridge_name)
+
+ def test_nfvswitch_internal_from_json(self):
+ data = """{
+"type": "nfvswitch_bridge",
+"cpus": "2,3,4,5",
+"members": [
+ {"type": "nfvswitch_internal", "name": "storage", "vlan_id": 202},
+ {"type": "nfvswitch_internal", "name": "api", "vlan_id": 201}
+ ]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("nfvswitch", bridge.name)
+ self.assertEqual("2,3,4,5", bridge.cpus)
+ interface1 = bridge.members[0]
+ self.assertEqual("storage202", interface1.name)
+ interface2 = bridge.members[1]
+ self.assertEqual("api201", interface2.name)
+ self.assertEqual(False, interface1.ovs_port)
+ self.assertEqual("nfvswitch", interface1.nfvswitch_bridge_name)
+
+ def test_bond_interface_from_json(self):
+ data = """{
+"type": "nfvswitch_bridge",
+"cpus": "2,3,4,5",
+"members": [{
+ "type": "linux_bond", "name": "bond1", "members":
+ [{"type": "interface", "name": "nic2"},
+ {"type": "interface", "name": "nic3"}]
+ }
+ ]
+}
+"""
+ err = self.assertRaises(objects.InvalidConfigException,
+ objects.NfvswitchBridge.from_json,
+ json.loads(data))
+ expected = 'NFVSwitch does not support bond interfaces.'
+ self.assertIn(expected, err)
+
+
class TestBond(base.TestCase):
def test_from_json_dhcp(self):