aboutsummaryrefslogtreecommitdiffstats
path: root/os_net_config
diff options
context:
space:
mode:
Diffstat (limited to 'os_net_config')
-rw-r--r--os_net_config/__init__.py14
-rw-r--r--os_net_config/impl_ifcfg.py230
-rw-r--r--os_net_config/objects.py94
-rw-r--r--os_net_config/tests/test_impl_ifcfg.py136
-rw-r--r--os_net_config/tests/test_objects.py121
5 files changed, 528 insertions, 67 deletions
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py
index 42131bb..a8fdf43 100644
--- a/os_net_config/__init__.py
+++ b/os_net_config/__init__.py
@@ -40,6 +40,7 @@ class NetConfig(object):
def add_object(self, obj):
"""Convenience method to add any type of object to the network config.
+
See objects.py.
:param obj: The object to add.
@@ -48,6 +49,8 @@ class NetConfig(object):
self.add_interface(obj)
elif isinstance(obj, objects.Vlan):
self.add_vlan(obj)
+ elif isinstance(obj, objects.IvsInterface):
+ self.add_ivs_interface(obj)
elif isinstance(obj, objects.OvsBridge):
self.add_bridge(obj)
for member in obj.members:
@@ -56,6 +59,10 @@ class NetConfig(object):
self.add_linux_bridge(obj)
for member in obj.members:
self.add_object(member)
+ elif isinstance(obj, objects.IvsBridge):
+ self.add_ivs_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:
@@ -93,6 +100,13 @@ class NetConfig(object):
"""
raise NotImplemented("add_linux_bridge is not implemented.")
+ def add_ivs_bridge(self, bridge):
+ """Add a IvsBridge object to the net config object.
+
+ :param bridge: The IvsBridge object to add.
+ """
+ raise NotImplemented("add_ivs_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 2e99f98..6459bcc 100644
--- a/os_net_config/impl_ifcfg.py
+++ b/os_net_config/impl_ifcfg.py
@@ -30,15 +30,23 @@ def ifcfg_config_path(name):
return "/etc/sysconfig/network-scripts/ifcfg-%s" % name
-#NOTE(dprince): added here for testability
+# NOTE(dprince): added here for testability
def bridge_config_path(name):
return ifcfg_config_path(name)
+def ivs_config_path():
+ return "/etc/sysconfig/ivs"
+
+
def route_config_path(name):
return "/etc/sysconfig/network-scripts/route-%s" % name
+def route6_config_path(name):
+ return "/etc/sysconfig/network-scripts/route6-%s" % name
+
+
def cleanup_pattern():
return "/etc/sysconfig/network-scripts/ifcfg-*"
@@ -49,12 +57,14 @@ class IfcfgNetConfig(os_net_config.NetConfig):
def __init__(self, noop=False, root_dir=''):
super(IfcfgNetConfig, self).__init__(noop, root_dir)
self.interface_data = {}
+ self.ivsinterface_data = {}
+ self.vlan_data = {}
self.route_data = {}
+ self.route6_data = {}
self.bridge_data = {}
self.linuxbridge_data = {}
self.linuxbond_data = {}
self.member_names = {}
- self.bond_slaves = {}
self.renamed_interfaces = {}
self.bond_primary_ifaces = {}
logger.info('Ifcfg net config provider created.')
@@ -78,14 +88,27 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "ONBOOT=yes\n"
data += "HOTPLUG=no\n"
data += "NM_CONTROLLED=no\n"
+ if not base_opt.dns_servers and not base_opt.use_dhcp:
+ data += "PEERDNS=no\n"
if isinstance(base_opt, objects.Vlan):
if not base_opt.ovs_port:
# vlans on OVS bridges are internal ports (no device, etc)
data += "VLAN=yes\n"
if base_opt.device:
data += "PHYSDEV=%s\n" % base_opt.device
+ else:
+ if base_opt.linux_bond_name:
+ data += "PHYSDEV=%s\n" % base_opt.linux_bond_name
+ elif isinstance(base_opt, objects.IvsInterface):
+ data += "TYPE=IVSIntPort\n"
elif re.match('\w+\.\d+$', base_opt.name):
data += "VLAN=yes\n"
+ if base_opt.linux_bond_name:
+ data += "MASTER=%s\n" % base_opt.linux_bond_name
+ data += "SLAVE=yes\n"
+ if base_opt.ivs_bridge_name:
+ data += "DEVICETYPE=ivs\n"
+ data += "IVS_BRIDGE=%s\n" % base_opt.ivs_bridge_name
if base_opt.ovs_port:
data += "DEVICETYPE=ovs\n"
if base_opt.bridge_name:
@@ -152,14 +175,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if base_opt.members:
members = [member.name for member in base_opt.members]
self.member_names[base_opt.name] = members
- for member in members:
- self.bond_slaves[member] = base_opt.name
if base_opt.bonding_options:
data += "BONDING_OPTS=\"%s\"\n" % base_opt.bonding_options
else:
- if base_opt.name in self.bond_slaves:
- data += "MASTER=%s\n" % self.bond_slaves[base_opt.name]
- data += "SLAVE=yes\n"
if base_opt.use_dhcp:
data += "BOOTPROTO=dhcp\n"
elif not base_opt.addresses:
@@ -211,16 +229,31 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding custom route for interface: %s' % interface_name)
data = ""
first_line = ""
+ data6 = ""
+ first_line6 = ""
for route in routes:
- if route.default:
- first_line = "default via %s dev %s\n" % (route.next_hop,
- interface_name)
+ if ":" not in route.next_hop:
+ # Route is an IPv4 route
+ if route.default:
+ first_line = "default via %s dev %s\n" % (route.next_hop,
+ interface_name)
+ else:
+ data += "%s via %s dev %s\n" % (route.ip_netmask,
+ route.next_hop,
+ interface_name)
else:
- data += "%s via %s dev %s\n" % (route.ip_netmask,
- route.next_hop,
- interface_name)
+ # Route is an IPv6 route
+ if route.default:
+ first_line6 = "default via %s dev %s\n" % (route.next_hop,
+ interface_name)
+ else:
+ data6 += "%s via %s dev %s\n" % (route.ip_netmask,
+ route.next_hop,
+ interface_name)
self.route_data[interface_name] = first_line + data
+ self.route6_data[interface_name] = first_line6 + data6
logger.debug('route data: %s' % self.route_data[interface_name])
+ logger.debug('ipv6 route data: %s' % self.route6_data[interface_name])
def add_interface(self, interface):
"""Add an Interface object to the net config object.
@@ -247,10 +280,22 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding vlan: %s' % vlan.name)
data = self._add_common(vlan)
logger.debug('vlan data: %s' % data)
- self.interface_data[vlan.name] = data
+ self.vlan_data[vlan.name] = data
if vlan.routes:
self._add_routes(vlan.name, vlan.routes)
+ def add_ivs_interface(self, ivs_interface):
+ """Add a ivs_interface object to the net config object.
+
+ :param ivs_interface: The ivs_interface object to add.
+ """
+ logger.info('adding ivs_interface: %s' % ivs_interface.name)
+ data = self._add_common(ivs_interface)
+ logger.debug('ivs_interface data: %s' % data)
+ self.ivsinterface_data[ivs_interface.name] = data
+ if ivs_interface.routes:
+ self._add_routes(ivs_interface.name, ivs_interface.routes)
+
def add_bridge(self, bridge):
"""Add an OvsBridge object to the net config object.
@@ -275,6 +320,18 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if bridge.routes:
self._add_routes(bridge.name, bridge.routes)
+ def add_ivs_bridge(self, bridge):
+ """Add a IvsBridge object to the net config object.
+
+ IVS can only support one virtual switch per node,
+ using "ivs" as its name. As long as the ivs service
+ is running, the ivs virtual switch will be there.
+ It is impossible to add multiple ivs virtual switches
+ per node.
+ :param bridge: The IvsBridge object to add.
+ """
+ pass
+
def add_bond(self, bond):
"""Add an OvsBond object to the net config object.
@@ -295,11 +352,30 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding linux bond: %s' % bond.name)
data = self._add_common(bond)
logger.debug('bond data: %s' % data)
- self.interface_data[bond.name] = data
self.linuxbond_data[bond.name] = data
if bond.routes:
self._add_routes(bond.name, bond.routes)
+ def generate_ivs_config(self, ivs_uplinks, ivs_interfaces):
+ """Generate configuration content for ivs."""
+
+ intfs = []
+ for intf in ivs_uplinks:
+ intfs.append(' -u ')
+ intfs.append(intf)
+ uplink_str = ''.join(intfs)
+
+ intfs = []
+ for intf in ivs_interfaces:
+ intfs.append(' --internal-port=')
+ intfs.append(intf)
+ intf_str = ''.join(intfs)
+
+ data = ("DAEMON_ARGS=\"--hitless --certificate /etc/ivs "
+ "--inband-vlan 4092%s%s\""
+ % (uplink_str, intf_str))
+ return data
+
def apply(self, cleanup=False, activate=True):
"""Apply the network configuration.
@@ -317,65 +393,137 @@ class IfcfgNetConfig(os_net_config.NetConfig):
"""
logger.info('applying network configs...')
restart_interfaces = []
+ restart_vlans = []
restart_bridges = []
+ restart_linux_bonds = []
update_files = {}
all_file_names = []
+ ivs_uplinks = [] # ivs physical uplinks
+ ivs_interfaces = [] # ivs internal ports
for interface_name, iface_data in self.interface_data.iteritems():
route_data = self.route_data.get(interface_name, '')
+ route6_data = self.route6_data.get(interface_name, '')
interface_path = self.root_dir + ifcfg_config_path(interface_name)
route_path = self.root_dir + route_config_path(interface_name)
+ route6_path = self.root_dir + route6_config_path(interface_name)
all_file_names.append(interface_path)
all_file_names.append(route_path)
+ all_file_names.append(route6_path)
+ if "IVS_BRIDGE" in iface_data:
+ ivs_uplinks.append(interface_name)
+ all_file_names.append(route6_path)
if (utils.diff(interface_path, iface_data) or
- utils.diff(route_path, route_data)):
+ utils.diff(route_path, route_data) or
+ utils.diff(route6_path, route6_data)):
restart_interfaces.append(interface_name)
restart_interfaces.extend(self.child_members(interface_name))
update_files[interface_path] = iface_data
update_files[route_path] = route_data
+ update_files[route6_path] = route6_data
+ else:
logger.info('No changes required for interface: %s' %
interface_name)
+ for interface_name, iface_data in self.ivsinterface_data.iteritems():
+ route_data = self.route_data.get(interface_name, '')
+ route6_data = self.route6_data.get(interface_name, '')
+ interface_path = self.root_dir + ifcfg_config_path(interface_name)
+ route_path = self.root_dir + route_config_path(interface_name)
+ route6_path = self.root_dir + route6_config_path(interface_name)
+ all_file_names.append(interface_path)
+ all_file_names.append(route_path)
+ all_file_names.append(route6_path)
+ ivs_interfaces.append(interface_name)
+ if (utils.diff(interface_path, iface_data) or
+ utils.diff(route_path, route_data)):
+ restart_interfaces.append(interface_name)
+ restart_interfaces.extend(self.child_members(interface_name))
+ update_files[interface_path] = iface_data
+ update_files[route_path] = route_data
+ update_files[route6_path] = route6_data
+ else:
+ logger.info('No changes required for ivs interface: %s' %
+ interface_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, '')
+ vlan_path = self.root_dir + ifcfg_config_path(vlan_name)
+ vlan_route_path = self.root_dir + route_config_path(vlan_name)
+ vlan_route6_path = self.root_dir + route6_config_path(vlan_name)
+ all_file_names.append(vlan_path)
+ all_file_names.append(vlan_route_path)
+ all_file_names.append(vlan_route6_path)
+ if (utils.diff(vlan_path, vlan_data) or
+ utils.diff(vlan_route_path, route_data)):
+ restart_vlans.append(vlan_name)
+ restart_vlans.extend(self.child_members(vlan_name))
+ update_files[vlan_path] = vlan_data
+ update_files[vlan_route_path] = route_data
+ update_files[vlan_route6_path] = route6_data
+ else:
+ logger.info('No changes required for vlan interface: %s' %
+ vlan_name)
+
for bridge_name, bridge_data in self.bridge_data.iteritems():
route_data = self.route_data.get(bridge_name, '')
+ route6_data = self.route6_data.get(bridge_name, '')
bridge_path = self.root_dir + bridge_config_path(bridge_name)
- bridge_route_path = self.root_dir + route_config_path(bridge_name)
+ br_route_path = self.root_dir + route_config_path(bridge_name)
+ br_route6_path = self.root_dir + route6_config_path(bridge_name)
all_file_names.append(bridge_path)
- all_file_names.append(bridge_route_path)
+ all_file_names.append(br_route_path)
+ all_file_names.append(br_route6_path)
if (utils.diff(bridge_path, bridge_data) or
- utils.diff(bridge_route_path, route_data)):
+ utils.diff(br_route_path, route_data) or
+ utils.diff(br_route6_path, route6_data)):
restart_bridges.append(bridge_name)
restart_interfaces.extend(self.child_members(bridge_name))
update_files[bridge_path] = bridge_data
- update_files[bridge_route_path] = route_data
+ update_files[br_route_path] = route_data
+ update_files[br_route6_path] = route6_data
+ else:
logger.info('No changes required for bridge: %s' % bridge_name)
for bridge_name, bridge_data in self.linuxbridge_data.iteritems():
route_data = self.route_data.get(bridge_name, '')
+ route6_data = self.route6_data.get(bridge_name, '')
bridge_path = self.root_dir + bridge_config_path(bridge_name)
- bridge_route_path = self.root_dir + route_config_path(bridge_name)
+ br_route_path = self.root_dir + route_config_path(bridge_name)
+ br_route6_path = self.root_dir + route6_config_path(bridge_name)
all_file_names.append(bridge_path)
- all_file_names.append(bridge_route_path)
+ all_file_names.append(br_route_path)
+ all_file_names.append(br_route6_path)
if (utils.diff(bridge_path, bridge_data) or
- utils.diff(bridge_route_path, route_data)):
+ utils.diff(br_route_path, route_data) or
+ utils.diff(br_route6_path, route6_data)):
restart_bridges.append(bridge_name)
restart_interfaces.extend(self.child_members(bridge_name))
update_files[bridge_path] = bridge_data
- update_files[bridge_route_path] = route_data
+ update_files[br_route_path] = route_data
+ update_files[br_route6_path] = route6_data
+ else:
logger.info('No changes required for bridge: %s' % bridge_name)
for bond_name, bond_data in self.linuxbond_data.iteritems():
route_data = self.route_data.get(bond_name, '')
+ route6_data = self.route6_data.get(bond_name, '')
bond_path = self.root_dir + bridge_config_path(bond_name)
bond_route_path = self.root_dir + route_config_path(bond_name)
+ bond_route6_path = self.root_dir + route6_config_path(bond_name)
all_file_names.append(bond_path)
all_file_names.append(bond_route_path)
+ all_file_names.append(bond_route6_path)
if (utils.diff(bond_path, bond_data) or
- utils.diff(bond_route_path, route_data)):
- restart_interfaces.append(bond_name)
+ utils.diff(bond_route_path, route_data) or
+ utils.diff(bond_route6_path, route6_data)):
+ restart_linux_bonds.append(bond_name)
restart_interfaces.extend(self.child_members(bond_name))
update_files[bond_path] = bond_data
update_files[bond_route_path] = route_data
+ update_files[bond_route6_path] = route6_data
+ else:
logger.info('No changes required for linux bond: %s' %
bond_name)
@@ -390,9 +538,15 @@ class IfcfgNetConfig(os_net_config.NetConfig):
self.remove_config(ifcfg_file)
if activate:
+ for vlan in restart_vlans:
+ self.ifdown(vlan)
+
for interface in restart_interfaces:
self.ifdown(interface)
+ for linux_bond in restart_linux_bonds:
+ self.ifdown(linux_bond)
+
for bridge in restart_bridges:
self.ifdown(bridge, iftype='bridge')
@@ -402,7 +556,15 @@ class IfcfgNetConfig(os_net_config.NetConfig):
for location, data in update_files.iteritems():
self.write_config(location, data)
+ if ivs_uplinks or ivs_interfaces:
+ location = ivs_config_path()
+ data = self.generate_ivs_config(ivs_uplinks, ivs_interfaces)
+ self.write_config(location, data)
+
if activate:
+ for linux_bond in restart_linux_bonds:
+ self.ifup(linux_bond)
+
for bridge in restart_bridges:
self.ifup(bridge, iftype='bridge')
@@ -413,4 +575,20 @@ class IfcfgNetConfig(os_net_config.NetConfig):
self.ovs_appctl('bond/set-active-slave', bond,
self.bond_primary_ifaces[bond])
+ if ivs_uplinks or ivs_interfaces:
+ logger.info("Attach to ivs with "
+ "uplinks: %s, "
+ "interfaces: %s" %
+ (ivs_uplinks, ivs_interfaces))
+ for ivs_uplink in ivs_uplinks:
+ self.ifup(ivs_uplink)
+ for ivs_interface in ivs_interfaces:
+ self.ifup(ivs_interface)
+ msg = "Restart ivs"
+ self.execute(msg, '/usr/bin/systemctl',
+ 'restart', 'ivs')
+
+ for vlan in restart_vlans:
+ self.ifup(vlan)
+
return update_files
diff --git a/os_net_config/objects.py b/os_net_config/objects.py
index b08dc46..4f3e0d5 100644
--- a/os_net_config/objects.py
+++ b/os_net_config/objects.py
@@ -44,6 +44,10 @@ def object_from_json(json):
return LinuxBond.from_json(json)
elif obj_type == "linux_bridge":
return LinuxBridge.from_json(json)
+ elif obj_type == "ivs_bridge":
+ return IvsBridge.from_json(json)
+ elif obj_type == "ivs_interface":
+ return IvsInterface.from_json(json)
def _get_required_field(json, name, object_name):
@@ -166,6 +170,8 @@ class _BaseOpts(object):
self.dns_servers = dns_servers
self.bridge_name = None # internal
self.linux_bridge_name = None # internal
+ self.ivs_bridge_name = None # internal
+ self.linux_bond_name = None # internal
self.ovs_port = False # internal
self.primary_interface_name = None # internal
@@ -290,6 +296,32 @@ class Vlan(_BaseOpts):
return Vlan(device, vlan_id, *opts)
+class IvsInterface(_BaseOpts):
+ """Base class for ivs interfaces."""
+
+ def __init__(self, vlan_id, name='ivs', 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(IvsInterface, 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', 'IvsInterface')
+ opts = _BaseOpts.base_opts_from_json(json)
+ return IvsInterface(vlan_id, name, *opts)
+
+
class OvsBridge(_BaseOpts):
"""Base class for OVS bridges."""
@@ -405,6 +437,67 @@ class LinuxBridge(_BaseOpts):
dns_servers=dns_servers)
+class IvsBridge(_BaseOpts):
+ """Base class for IVS bridges.
+
+ Indigo Virtual Switch (IVS) is a virtual switch for Linux.
+ It is compatible with the KVM hypervisor and leveraging the
+ Open vSwitch kernel module for packet forwarding. There are
+ three major differences between IVS and OVS:
+ 1. Each node can have at most one ivs, no name required.
+ 2. Bond is not allowed to attach to an ivs. It is the SDN
+ controller's job to dynamically form bonds on ivs.
+ 3. IP address can only be statically assigned.
+ """
+
+ def __init__(self, name='ivs', 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):
+ addresses = addresses or []
+ routes = routes or []
+ members = members or []
+ dns_servers = dns_servers or []
+ super(IvsBridge, self).__init__(name, use_dhcp, use_dhcpv6,
+ addresses, routes, mtu, False,
+ nic_mapping, persist_mapping,
+ defroute, dhclient_args, dns_servers)
+ self.members = members
+ for member in self.members:
+ if isinstance(member, OvsBond) or isinstance(member, LinuxBond):
+ msg = 'IVS does not support bond interfaces.'
+ raise InvalidConfigException(msg)
+ member.ivs_bridge_name = name
+ member.ovs_port = False
+ self.primary_interface_name = None # ivs doesn't use primary intf
+
+ @staticmethod
+ def from_json(json):
+ name = 'ivs'
+ (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)
+
+ return IvsBridge(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)
+
+
class LinuxBond(_BaseOpts):
"""Base class for Linux bonds."""
@@ -423,6 +516,7 @@ class LinuxBond(_BaseOpts):
self.members = members
self.bonding_options = bonding_options
for member in self.members:
+ member.linux_bond_name = name
if member.primary:
if self.primary_interface_name:
msg = 'Only one primary interface allowed per bond.'
diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py
index 8a60a8d..d68a96e 100644
--- a/os_net_config/tests/test_impl_ifcfg.py
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -30,6 +30,7 @@ DEVICE=em1
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
"""
_NO_IP = _BASE_IFCFG + "BOOTPROTO=none\n"
@@ -39,11 +40,20 @@ IPADDR=192.168.1.2
NETMASK=255.255.255.0
"""
+_V4_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes
+BOOTPROTO=static
+IPADDR=192.168.1.2
+NETMASK=255.255.255.0
+IPV6_AUTOCONF=no
+IPV6ADDR=2001:abc:a::/64
+"""
+
_IFCFG_VLAN = """# This file is autogenerated by os-net-config
DEVICE=em1.120
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
VLAN=yes
BOOTPROTO=none
"""
@@ -75,6 +85,11 @@ _ROUTES = """default via 192.168.1.1 dev em1
172.19.0.0/24 via 192.168.1.1 dev em1
"""
+_ROUTES_V6 = """default via 2001:db8::1 dev em1
+2001:db8:dead:beef:cafe::/56 via fd00:fd00:2000::1 dev em1
+"""
+
+
_OVS_INTERFACE = _BASE_IFCFG + """DEVICETYPE=ovs
TYPE=OVSPort
OVS_BRIDGE=br-ctlplane
@@ -107,6 +122,7 @@ DEVICE=br-ctlplane
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
DEVICETYPE=ovs
TYPE=OVSBridge
BOOTPROTO=static
@@ -119,6 +135,7 @@ DEVICE=br-ctlplane
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
TYPE=Bridge
DELAY=0
BOOTPROTO=static
@@ -140,6 +157,7 @@ DEVICE=vlan5
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
VLAN=yes
PHYSDEV=em1
"""
@@ -150,6 +168,7 @@ DEVICE=vlan5
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
"""
_VLAN_NO_IP = _BASE_VLAN + "BOOTPROTO=none\n"
@@ -165,6 +184,11 @@ OVS_OPTIONS="tag=5"
BOOTPROTO=none
"""
+_VLAN_LINUX_BRIDGE = _BASE_VLAN_OVS + """VLAN=yes
+PHYSDEV=em1
+BRIDGE=br-ctlplane
+BOOTPROTO=none
+"""
_OVS_BOND_DHCP = """# This file is autogenerated by os-net-config
DEVICE=bond0
@@ -177,7 +201,6 @@ OVSBOOTPROTO=dhcp
BOND_IFACES="em1 em2"
"""
-
_LINUX_BOND_DHCP = """# This file is autogenerated by os-net-config
DEVICE=bond0
ONBOOT=yes
@@ -187,6 +210,42 @@ BOOTPROTO=dhcp
"""
+_LINUX_BOND_INTERFACE = _BASE_IFCFG + """MASTER=bond0
+SLAVE=yes
+BOOTPROTO=none
+"""
+
+
+_IVS_UPLINK = """# This file is autogenerated by os-net-config
+DEVICE=em1
+ONBOOT=yes
+HOTPLUG=no
+NM_CONTROLLED=no
+PEERDNS=no
+DEVICETYPE=ivs
+IVS_BRIDGE=ivs
+BOOTPROTO=none
+"""
+
+_IVS_INTERFACE = """# This file is autogenerated by os-net-config
+DEVICE=storage5
+ONBOOT=yes
+HOTPLUG=no
+NM_CONTROLLED=no
+PEERDNS=no
+TYPE=IVSIntPort
+DEVICETYPE=ivs
+IVS_BRIDGE=ivs
+MTU=1500
+BOOTPROTO=static
+IPADDR=172.16.2.7
+NETMASK=255.255.255.0
+"""
+
+_IVS_CONFIG = ('DAEMON_ARGS=\"--hitless --certificate /etc/ivs '
+ '--inband-vlan 4092 -u em1 --internal-port=storage5\"')
+
+
class TestIfcfgNetConfig(base.TestCase):
def setUp(self):
@@ -200,9 +259,18 @@ class TestIfcfgNetConfig(base.TestCase):
def get_interface_config(self, name='em1'):
return self.provider.interface_data[name]
+ def get_vlan_config(self, name='vlan1'):
+ return self.provider.vlan_data[name]
+
+ def get_linux_bond_config(self, name='bond0'):
+ return self.provider.linuxbond_data[name]
+
def get_route_config(self, name='em1'):
return self.provider.route_data.get(name, '')
+ def get_route6_config(self, name='em1'):
+ return self.provider.route6_data.get(name, '')
+
def test_add_base_interface(self):
interface = objects.Interface('em1')
self.provider.add_interface(interface)
@@ -276,6 +344,20 @@ class TestIfcfgNetConfig(base.TestCase):
self.assertEqual(_V4_IFCFG, self.get_interface_config())
self.assertEqual(_ROUTES, self.get_route_config())
+ def test_network_with_ipv6_routes(self):
+ route1 = objects.Route('192.168.1.1', default=True)
+ route2 = objects.Route('192.168.1.1', '172.19.0.0/24')
+ route3 = objects.Route('2001:db8::1', default=True)
+ route4 = objects.Route('fd00:fd00:2000::1',
+ '2001:db8:dead:beef:cafe::/56')
+ v4_addr = objects.Address('192.168.1.2/24')
+ v6_addr = objects.Address('2001:abc:a::/64')
+ interface = objects.Interface('em1', addresses=[v4_addr, v6_addr],
+ routes=[route1, route2, route3, route4])
+ self.provider.add_interface(interface)
+ self.assertEqual(_V4_V6_IFCFG, self.get_interface_config())
+ self.assertEqual(_ROUTES_V6, self.get_route6_config())
+
def test_network_ovs_bridge_with_dhcp(self):
interface = objects.Interface('em1')
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
@@ -348,22 +430,38 @@ class TestIfcfgNetConfig(base.TestCase):
self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA,
self.provider.bridge_data['br-ctlplane'])
+ def test_network_ivs_with_uplink_and_interface(self):
+ interface = objects.Interface('em1')
+ v4_addr = objects.Address('172.16.2.7/24')
+ ivs_interface = objects.IvsInterface(vlan_id=5,
+ name='storage',
+ addresses=[v4_addr])
+ bridge = objects.IvsBridge(members=[interface, ivs_interface])
+ self.provider.add_interface(interface)
+ self.provider.add_ivs_interface(ivs_interface)
+ self.provider.add_bridge(bridge)
+ self.assertEqual(_IVS_UPLINK, self.get_interface_config())
+ self.assertEqual(_IVS_INTERFACE,
+ self.provider.ivsinterface_data[ivs_interface.name])
+ data = self.provider.generate_ivs_config(['em1'], ['storage5'])
+ self.assertEqual(_IVS_CONFIG, data)
+
def test_add_vlan(self):
vlan = objects.Vlan('em1', 5)
self.provider.add_vlan(vlan)
- self.assertEqual(_VLAN_NO_IP, self.get_interface_config('vlan5'))
+ self.assertEqual(_VLAN_NO_IP, self.get_vlan_config('vlan5'))
def test_add_vlan_ovs(self):
vlan = objects.Vlan('em1', 5)
vlan.ovs_port = True
self.provider.add_vlan(vlan)
- self.assertEqual(_VLAN_OVS, self.get_interface_config('vlan5'))
+ self.assertEqual(_VLAN_OVS, self.get_vlan_config('vlan5'))
def test_add_vlan_mtu_1500(self):
vlan = objects.Vlan('em1', 5, mtu=1500)
self.provider.add_vlan(vlan)
expected = _VLAN_NO_IP + 'MTU=1500\n'
- self.assertEqual(expected, self.get_interface_config('vlan5'))
+ self.assertEqual(expected, self.get_vlan_config('vlan5'))
def test_add_ovs_bridge_with_vlan(self):
vlan = objects.Vlan('em1', 5)
@@ -371,7 +469,15 @@ class TestIfcfgNetConfig(base.TestCase):
members=[vlan])
self.provider.add_vlan(vlan)
self.provider.add_bridge(bridge)
- self.assertEqual(_VLAN_OVS_BRIDGE, self.get_interface_config('vlan5'))
+ self.assertEqual(_VLAN_OVS_BRIDGE, self.get_vlan_config('vlan5'))
+
+ def test_add_linux_bridge_with_vlan(self):
+ vlan = objects.Vlan('em1', 5)
+ bridge = objects.LinuxBridge('br-ctlplane', use_dhcp=True,
+ members=[vlan])
+ self.provider.add_vlan(vlan)
+ self.provider.add_bridge(bridge)
+ self.assertEqual(_VLAN_LINUX_BRIDGE, self.get_vlan_config('vlan5'))
def test_ovs_bond(self):
interface1 = objects.Interface('em1')
@@ -388,6 +494,7 @@ DEVICE=em2
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
BOOTPROTO=none
"""
self.assertEqual(em2_config, self.get_interface_config('em2'))
@@ -400,8 +507,12 @@ BOOTPROTO=none
bond = objects.LinuxBond('bond0', use_dhcp=True,
members=[interface1, interface2])
self.provider.add_linux_bond(bond)
+ self.provider.add_interface(interface1)
+ self.provider.add_interface(interface2)
self.assertEqual(_LINUX_BOND_DHCP,
- self.get_interface_config('bond0'))
+ self.get_linux_bond_config('bond0'))
+ self.assertEqual(_LINUX_BOND_INTERFACE,
+ self.get_interface_config('em1'))
def test_interface_defroute(self):
interface1 = objects.Interface('em1')
@@ -413,6 +524,7 @@ DEVICE=em1
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
BOOTPROTO=none
"""
em2_config = """# This file is autogenerated by os-net-config
@@ -420,6 +532,7 @@ DEVICE=em2
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
BOOTPROTO=none
DEFROUTE=no
"""
@@ -434,6 +547,7 @@ DEVICE=em1
ONBOOT=yes
HOTPLUG=no
NM_CONTROLLED=no
+PEERDNS=no
BOOTPROTO=none
DHCLIENTARGS=--foobar
"""
@@ -474,6 +588,7 @@ class TestIfcfgNetConfigApply(base.TestCase):
super(TestIfcfgNetConfigApply, self).setUp()
self.temp_ifcfg_file = tempfile.NamedTemporaryFile()
self.temp_route_file = tempfile.NamedTemporaryFile()
+ self.temp_route6_file = tempfile.NamedTemporaryFile()
self.temp_bridge_file = tempfile.NamedTemporaryFile()
self.temp_cleanup_file = tempfile.NamedTemporaryFile(delete=False)
self.ifup_interface_names = []
@@ -487,6 +602,10 @@ class TestIfcfgNetConfigApply(base.TestCase):
return self.temp_route_file.name
self.stubs.Set(impl_ifcfg, 'route_config_path', test_routes_path)
+ def test_routes6_path(name):
+ return self.temp_route6_file.name
+ self.stubs.Set(impl_ifcfg, 'route6_config_path', test_routes6_path)
+
def test_bridge_path(name):
return self.temp_bridge_file.name
self.stubs.Set(impl_ifcfg, 'bridge_config_path', test_bridge_path)
@@ -508,6 +627,7 @@ class TestIfcfgNetConfigApply(base.TestCase):
def tearDown(self):
self.temp_ifcfg_file.close()
self.temp_route_file.close()
+ self.temp_route6_file.close()
self.temp_bridge_file.close()
if os.path.exists(self.temp_cleanup_file.name):
self.temp_cleanup_file.close()
@@ -553,7 +673,7 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.assertEqual([], self.ifup_interface_names)
def test_bond_active_slave(self):
- #setup and apply a bond
+ # setup and apply a bond
interface1 = objects.Interface('em1')
interface2 = objects.Interface('em2', primary=True)
bond = objects.OvsBond('bond1', use_dhcp=True,
@@ -566,7 +686,7 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.assertIn(ovs_appctl_cmds, self.ovs_appctl_cmds)
def test_bond_active_ordering(self):
- #setup and apply a bond
+ # setup and apply a bond
interface1 = objects.Interface('em1')
interface2 = objects.Interface('em2')
bond = objects.OvsBond('bond1', use_dhcp=True,
diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py
index a2e021b..d3e41df 100644
--- a/os_net_config/tests/test_objects.py
+++ b/os_net_config/tests/test_objects.py
@@ -29,7 +29,7 @@ class TestRoute(base.TestCase):
route = objects.Route.from_json(json.loads(data))
self.assertEqual("172.19.0.1", route.next_hop)
self.assertEqual("172.19.0.0/24", route.ip_netmask)
- self.assertEqual(False, route.default)
+ self.assertFalse(route.default)
def test_from_json_default_route(self):
data = '{"next_hop": "172.19.0.1", "ip_netmask": "172.19.0.0/24", ' \
@@ -37,14 +37,14 @@ class TestRoute(base.TestCase):
route = objects.Route.from_json(json.loads(data))
self.assertEqual("172.19.0.1", route.next_hop)
self.assertEqual("172.19.0.0/24", route.ip_netmask)
- self.assertEqual(True, route.default)
+ self.assertTrue(route.default)
data = '{"next_hop": "172.19.0.1", "ip_netmask": "172.19.0.0/24", ' \
'"default": "true"}'
route = objects.Route.from_json(json.loads(data))
self.assertEqual("172.19.0.1", route.next_hop)
self.assertEqual("172.19.0.0/24", route.ip_netmask)
- self.assertEqual(True, route.default)
+ self.assertTrue(route.default)
class TestAddress(base.TestCase):
@@ -85,14 +85,14 @@ class TestInterface(base.TestCase):
v4_addr = objects.Address('192.168.1.1/24')
v6_addr = objects.Address('2001:abc:a::/64')
interface = objects.Interface('foo', addresses=[v4_addr, v6_addr])
- self.assertEquals("192.168.1.1", interface.v4_addresses()[0].ip)
- self.assertEquals("2001:abc:a::", interface.v6_addresses()[0].ip)
+ self.assertEqual("192.168.1.1", interface.v4_addresses()[0].ip)
+ self.assertEqual("2001:abc:a::", interface.v6_addresses()[0].ip)
def test_from_json_dhcp(self):
data = '{"type": "interface", "name": "em1", "use_dhcp": true}'
interface = objects.object_from_json(json.loads(data))
self.assertEqual("em1", interface.name)
- self.assertEqual(True, interface.use_dhcp)
+ self.assertTrue(interface.use_dhcp)
def test_from_json_defroute(self):
data = '{"type": "interface", "name": "em1", "use_dhcp": true}'
@@ -105,8 +105,8 @@ class TestInterface(base.TestCase):
}
"""
interface2 = objects.object_from_json(json.loads(data))
- self.assertEqual(True, interface1.defroute)
- self.assertEqual(False, interface2.defroute)
+ self.assertTrue(interface1.defroute)
+ self.assertFalse(interface2.defroute)
def test_from_json_dhclient_args(self):
data = """{
@@ -138,7 +138,7 @@ class TestInterface(base.TestCase):
data = '{"type": "interface", "name": "nic1", "use_dhcp": true}'
interface = objects.object_from_json(json.loads(data))
self.assertEqual("em3", interface.name)
- self.assertEqual(True, interface.use_dhcp)
+ self.assertTrue(interface.use_dhcp)
def test_from_json_with_addresses(self):
data = """{
@@ -157,8 +157,8 @@ class TestInterface(base.TestCase):
"""
interface = objects.object_from_json(json.loads(data))
self.assertEqual("em1", interface.name)
- self.assertEqual(False, interface.use_dhcp)
- self.assertEqual(False, interface.use_dhcpv6)
+ self.assertFalse(interface.use_dhcp)
+ self.assertFalse(interface.use_dhcpv6)
self.assertEqual(1501, interface.mtu)
address1 = interface.v4_addresses()[0]
self.assertEqual("192.0.2.1", address1.ip)
@@ -176,7 +176,7 @@ class TestVlan(base.TestCase):
vlan = objects.object_from_json(json.loads(data))
self.assertEqual("em1", vlan.device)
self.assertEqual(16, vlan.vlan_id)
- self.assertEqual(True, vlan.use_dhcp)
+ self.assertTrue(vlan.use_dhcp)
def test_from_json_dhcp_nic1(self):
def dummy_numbered_nics(nic_mapping=None):
@@ -188,7 +188,7 @@ class TestVlan(base.TestCase):
vlan = objects.object_from_json(json.loads(data))
self.assertEqual("em4", vlan.device)
self.assertEqual(16, vlan.vlan_id)
- self.assertEqual(True, vlan.use_dhcp)
+ self.assertTrue(vlan.use_dhcp)
class TestBridge(base.TestCase):
@@ -206,10 +206,10 @@ class TestBridge(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("br-foo", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
- self.assertEqual(True, interface1.ovs_port)
+ self.assertTrue(interface1.ovs_port)
self.assertEqual("br-foo", interface1.bridge_name)
def test_from_json_dhcp_with_nic1(self):
@@ -229,10 +229,10 @@ class TestBridge(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("br-foo", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em5", interface1.name)
- self.assertEqual(True, interface1.ovs_port)
+ self.assertTrue(interface1.ovs_port)
self.assertEqual("br-foo", interface1.bridge_name)
def test_from_json_primary_interface(self):
@@ -254,16 +254,16 @@ class TestBridge(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("br-foo", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
self.assertEqual("em1", bridge.primary_interface_name)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
- self.assertEqual(True, interface1.ovs_port)
- self.assertEqual(True, interface1.primary)
+ self.assertTrue(interface1.ovs_port)
+ self.assertTrue(interface1.primary)
self.assertEqual("br-foo", interface1.bridge_name)
interface2 = bridge.members[1]
self.assertEqual("em2", interface2.name)
- self.assertEqual(True, interface2.ovs_port)
+ self.assertTrue(interface2.ovs_port)
self.assertEqual("br-foo", interface2.bridge_name)
@@ -282,10 +282,10 @@ class TestLinuxBridge(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("br-foo", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
- self.assertEqual(False, interface1.ovs_port)
+ self.assertFalse(interface1.ovs_port)
self.assertEqual("br-foo", interface1.linux_bridge_name)
def test_from_json_dhcp_with_nic1(self):
@@ -305,10 +305,10 @@ class TestLinuxBridge(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("br-foo", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em5", interface1.name)
- self.assertEqual(False, interface1.ovs_port)
+ self.assertFalse(interface1.ovs_port)
self.assertEqual("br-foo", interface1.linux_bridge_name)
def test_from_json_primary_interface(self):
@@ -330,19 +330,74 @@ class TestLinuxBridge(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("br-foo", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
self.assertEqual("em1", bridge.primary_interface_name)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
- self.assertEqual(False, interface1.ovs_port)
- self.assertEqual(True, interface1.primary)
+ self.assertFalse(interface1.ovs_port)
+ self.assertTrue(interface1.primary)
self.assertEqual("br-foo", interface1.linux_bridge_name)
interface2 = bridge.members[1]
self.assertEqual("em2", interface2.name)
- self.assertEqual(False, interface2.ovs_port)
+ self.assertFalse(interface2.ovs_port)
self.assertEqual("br-foo", interface2.linux_bridge_name)
+class TestIvsBridge(base.TestCase):
+
+ def test_interface_from_json(self):
+ data = """{
+"type": "ivs_bridge",
+"members": [{
+ "type": "interface",
+ "name": "nic2"
+}]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("ivs", bridge.name)
+ interface1 = bridge.members[0]
+ self.assertEqual("nic2", interface1.name)
+ self.assertEqual(False, interface1.ovs_port)
+ self.assertEqual("ivs", interface1.ivs_bridge_name)
+
+ def test_ivs_interface_from_json(self):
+ data = """{
+"type": "ivs_bridge",
+"members": [{
+ "type": "ivs_interface",
+ "name": "storage",
+ "vlan_id": 202
+}]
+}
+"""
+ bridge = objects.object_from_json(json.loads(data))
+ self.assertEqual("ivs", bridge.name)
+ interface1 = bridge.members[0]
+ self.assertEqual("storage202", interface1.name)
+ self.assertEqual(False, interface1.ovs_port)
+ self.assertEqual("ivs", interface1.ivs_bridge_name)
+
+ def test_bond_interface_from_json(self):
+ data = """{
+"type": "ivs_bridge",
+"members": [{
+ "type": "linux_bond",
+ "name": "bond1",
+ "members": [
+ {"type": "interface", "name": "nic2"},
+ {"type": "interface", "name": "nic3"}
+ ]
+}]
+}
+"""
+ err = self.assertRaises(objects.InvalidConfigException,
+ objects.IvsBridge.from_json,
+ json.loads(data))
+ expected = 'IVS does not support bond interfaces.'
+ self.assertIn(expected, err)
+
+
class TestBond(base.TestCase):
def test_from_json_dhcp(self):
@@ -364,7 +419,7 @@ class TestBond(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("bond1", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
interface2 = bridge.members[1]
@@ -394,7 +449,7 @@ class TestBond(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("bond1", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
interface2 = bridge.members[1]
@@ -422,7 +477,7 @@ class TestLinuxBond(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("bond1", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
interface2 = bridge.members[1]
@@ -452,7 +507,7 @@ class TestLinuxBond(base.TestCase):
"""
bridge = objects.object_from_json(json.loads(data))
self.assertEqual("bond1", bridge.name)
- self.assertEqual(True, bridge.use_dhcp)
+ self.assertTrue(bridge.use_dhcp)
interface1 = bridge.members[0]
self.assertEqual("em1", interface1.name)
interface2 = bridge.members[1]