diff options
Diffstat (limited to 'os_net_config/objects.py')
-rw-r--r-- | os_net_config/objects.py | 246 |
1 files changed, 220 insertions, 26 deletions
diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 9815832..9b5523f 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -23,7 +23,7 @@ from os_net_config import utils logger = logging.getLogger(__name__) -_NUMBERED_NICS = None +_MAPPED_NICS = None class InvalidConfigException(ValueError): @@ -42,16 +42,24 @@ def object_from_json(json): return OvsBond.from_json(json) elif obj_type == "linux_bond": return LinuxBond.from_json(json) + elif obj_type == "team": + return LinuxTeam.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) + 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": return OvsPatchPort.from_json(json) + elif obj_type == "ib_interface": + return IbInterface.from_json(json) def _get_required_field(json, name, object_name): @@ -63,21 +71,17 @@ def _get_required_field(json, name, object_name): return field -def _numbered_nics(nic_mapping=None): +def _mapped_nics(nic_mapping=None): mapping = nic_mapping or {} - global _NUMBERED_NICS - if _NUMBERED_NICS: - return _NUMBERED_NICS - _NUMBERED_NICS = {} - count = 0 + global _MAPPED_NICS + if _MAPPED_NICS: + return _MAPPED_NICS + _MAPPED_NICS = {} active_nics = utils.ordered_active_nics() - for nic in active_nics: - count += 1 - nic_alias = "nic%i" % count - nic_mapped = mapping.get(nic_alias, nic) - - # The mapping is either invalid, or specifies a mac + for nic_alias, nic_mapped in mapping.items(): if nic_mapped not in active_nics: + # The mapping is either invalid, or specifies a mac + is_mapping_valid = False for active in active_nics: try: active_mac = utils.interface_mac(active) @@ -86,25 +90,39 @@ def _numbered_nics(nic_mapping=None): if nic_mapped == active_mac: logger.debug("%s matches device %s" % (nic_mapped, active)) nic_mapped = active + is_mapping_valid = True break - else: + + if not is_mapping_valid: # The mapping can't specify a non-active or non-existent nic - logger.warning('interface %s is not in an active nic (%s)' + logger.warning('interface %s is not an active nic (%s)' % (nic_mapped, ', '.join(active_nics))) continue # Duplicate mappings are not allowed - if nic_mapped in _NUMBERED_NICS.values(): + if nic_mapped in _MAPPED_NICS.values(): msg = ('interface %s already mapped, ' 'check mapping file for duplicates' % nic_mapped) raise InvalidConfigException(msg) - _NUMBERED_NICS[nic_alias] = nic_mapped + _MAPPED_NICS[nic_alias] = nic_mapped logger.info("%s mapped to: %s" % (nic_alias, nic_mapped)) - if not _NUMBERED_NICS: + + # Add default numbered mappings, but do not overwrite existing entries + for nic_mapped in set(active_nics).difference(set(_MAPPED_NICS.values())): + nic_alias = "nic%i" % (active_nics.index(nic_mapped) + 1) + if nic_alias in _MAPPED_NICS: + logger.warning("no mapping for interface %s because " + "%s is mapped to %s" + % (nic_mapped, nic_alias, _MAPPED_NICS[nic_alias])) + else: + _MAPPED_NICS[nic_alias] = nic_mapped + logger.info("%s mapped to: %s" % (nic_alias, nic_mapped)) + + if not _MAPPED_NICS: logger.warning('No active nics found.') - return _NUMBERED_NICS + return _MAPPED_NICS class Route(object): @@ -150,18 +168,18 @@ class _BaseOpts(object): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] - numbered_nic_names = _numbered_nics(nic_mapping) + mapped_nic_names = _mapped_nics(nic_mapping) self.hwaddr = None self.hwname = None self.renamed = False - if name in numbered_nic_names: + if name in mapped_nic_names: if persist_mapping: self.name = name - self.hwname = numbered_nic_names[name] + self.hwname = mapped_nic_names[name] self.hwaddr = utils.interface_mac(self.hwname) self.renamed = True else: - self.name = numbered_nic_names[name] + self.name = mapped_nic_names[name] else: self.name = name @@ -177,7 +195,9 @@ 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 self.primary_interface_name = None # internal @@ -287,9 +307,9 @@ class Vlan(_BaseOpts): dns_servers) self.vlan_id = int(vlan_id) - numbered_nic_names = _numbered_nics(nic_mapping) - if device in numbered_nic_names: - self.device = numbered_nic_names[device] + mapped_nic_names = _mapped_nics(nic_mapping) + if device in mapped_nic_names: + self.device = mapped_nic_names[device] else: self.device = device @@ -328,6 +348,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.""" @@ -505,6 +551,132 @@ 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.""" + + def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None, + routes=None, mtu=None, primary=False, members=None, + bonding_options=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(LinuxTeam, self).__init__(name, use_dhcp, use_dhcpv6, addresses, + routes, mtu, primary, nic_mapping, + persist_mapping, defroute, + dhclient_args, dns_servers) + self.members = members + self.bonding_options = bonding_options + for member in self.members: + member.linux_team_name = name + if member.primary: + if self.primary_interface_name: + msg = 'Only one primary interface allowed per team.' + raise InvalidConfigException(msg) + if member.primary_interface_name: + self.primary_interface_name = member.primary_interface_name + else: + self.primary_interface_name = member.name + + @staticmethod + def from_json(json): + name = _get_required_field(json, 'name', 'LinuxTeam') + (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) + bonding_options = json.get('bonding_options') + 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 LinuxTeam(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, + addresses=addresses, routes=routes, mtu=mtu, + members=members, bonding_options=bonding_options, + 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.""" @@ -689,3 +861,25 @@ class OvsPatchPort(_BaseOpts): opts = _BaseOpts.base_opts_from_json(json) return OvsPatchPort(name, *opts, bridge_name=bridge_name, peer=peer, ovs_options=ovs_options, ovs_extra=ovs_extra) + + +class IbInterface(_BaseOpts): + """Base class for InfiniBand network interfaces.""" + + 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): + addresses = addresses or [] + routes = routes or [] + dns_servers = dns_servers or [] + super(IbInterface, self).__init__(name, use_dhcp, use_dhcpv6, + addresses, routes, mtu, primary, + nic_mapping, persist_mapping, + defroute, dhclient_args, dns_servers) + + @staticmethod + def from_json(json): + name = _get_required_field(json, 'name', 'IbInterface') + opts = _BaseOpts.base_opts_from_json(json) + return IbInterface(name, *opts) |