diff options
author | 2017-02-18 14:47:08 +0000 | |
---|---|---|
committer | 2017-02-18 14:47:08 +0000 | |
commit | da0d8e06bb9a55fb9b15b34cabb19c681667d383 (patch) | |
tree | ff9e2ff0b8f9d62111d33d2b7fec7bd22775b466 /build/neutron/agent/interface/interface.py | |
parent | 645949cbac2bb766d18dfd9f818b6c14545a35e2 (diff) | |
parent | c7e6d15b4d2570c7f9d2bc8dcfc57d8a68fede71 (diff) |
Merge "Add support for odl-fdio scenario"
Diffstat (limited to 'build/neutron/agent/interface/interface.py')
-rw-r--r-- | build/neutron/agent/interface/interface.py | 552 |
1 files changed, 0 insertions, 552 deletions
diff --git a/build/neutron/agent/interface/interface.py b/build/neutron/agent/interface/interface.py deleted file mode 100644 index 709fd677..00000000 --- a/build/neutron/agent/interface/interface.py +++ /dev/null @@ -1,552 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc -import eventlet -import netaddr -from oslo_config import cfg -from oslo_log import log as logging -import six - -from neutron._i18n import _, _LE, _LI, _LW -from neutron.agent.common import ovs_lib -from neutron.agent.linux import ip_lib -from neutron.agent.linux import utils -from neutron.common import constants as n_const -from neutron.common import exceptions -from neutron.common import ipv6_utils - - -LOG = logging.getLogger(__name__) - -OPTS = [ - cfg.StrOpt('ovs_integration_bridge', - default='br-int', - help=_('Name of Open vSwitch bridge to use')), - cfg.BoolOpt('ovs_use_veth', - default=False, - help=_('Uses veth for an OVS interface or not. ' - 'Support kernels with limited namespace support ' - '(e.g. RHEL 6.5) so long as ovs_use_veth is set to ' - 'True.')), - cfg.IntOpt('network_device_mtu', - deprecated_for_removal=True, - help=_('MTU setting for device. This option will be removed in ' - 'Newton. Please use the system-wide segment_mtu setting ' - 'which the agents will take into account when wiring ' - 'VIFs.')), -] - - -@six.add_metaclass(abc.ABCMeta) -class LinuxInterfaceDriver(object): - - # from linux IF_NAMESIZE - DEV_NAME_LEN = 14 - DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX - - def __init__(self, conf): - self.conf = conf - if self.conf.network_device_mtu: - self._validate_network_device_mtu() - - def _validate_network_device_mtu(self): - if (ipv6_utils.is_enabled() and - self.conf.network_device_mtu < n_const.IPV6_MIN_MTU): - LOG.error(_LE("IPv6 protocol requires a minimum MTU of " - "%(min_mtu)s, while the configured value is " - "%(current_mtu)s"), {'min_mtu': n_const.IPV6_MIN_MTU, - 'current_mtu': self.conf.network_device_mtu}) - raise SystemExit(1) - - @property - def use_gateway_ips(self): - """Whether to use gateway IPs instead of unique IP allocations. - - In each place where the DHCP agent runs, and for each subnet for - which DHCP is handling out IP addresses, the DHCP port needs - - at the Linux level - to have an IP address within that subnet. - Generally this needs to be a unique Neutron-allocated IP - address, because the subnet's underlying L2 domain is bridged - across multiple compute hosts and network nodes, and for HA - there may be multiple DHCP agents running on that same bridged - L2 domain. - - However, if the DHCP ports - on multiple compute/network nodes - but for the same network - are _not_ bridged to each other, - they do not need each to have a unique IP address. Instead - they can all share the same address from the relevant subnet. - This works, without creating any ambiguity, because those - ports are not all present on the same L2 domain, and because - no data within the network is ever sent to that address. - (DHCP requests are broadcast, and it is the network's job to - ensure that such a broadcast will reach at least one of the - available DHCP servers. DHCP responses will be sent _from_ - the DHCP port address.) - - Specifically, for networking backends where it makes sense, - the DHCP agent allows all DHCP ports to use the subnet's - gateway IP address, and thereby to completely avoid any unique - IP address allocation. This behaviour is selected by running - the DHCP agent with a configured interface driver whose - 'use_gateway_ips' property is True. - - When an operator deploys Neutron with an interface driver that - makes use_gateway_ips True, they should also ensure that a - gateway IP address is defined for each DHCP-enabled subnet, - and that the gateway IP address doesn't change during the - subnet's lifetime. - """ - return False - - def init_l3(self, device_name, ip_cidrs, namespace=None, - preserve_ips=None, clean_connections=False): - """Set the L3 settings for the interface using data from the port. - - ip_cidrs: list of 'X.X.X.X/YY' strings - preserve_ips: list of ip cidrs that should not be removed from device - clean_connections: Boolean to indicate if we should cleanup connections - associated to removed ips - """ - preserve_ips = preserve_ips or [] - device = ip_lib.IPDevice(device_name, namespace=namespace) - - # The LLA generated by the operating system is not known to - # Neutron, so it would be deleted if we added it to the 'previous' - # list here - default_ipv6_lla = ip_lib.get_ipv6_lladdr(device.link.address) - previous = {addr['cidr'] for addr in device.addr.list( - filters=['permanent'])} - {default_ipv6_lla} - - # add new addresses - for ip_cidr in ip_cidrs: - - net = netaddr.IPNetwork(ip_cidr) - # Convert to compact IPv6 address because the return values of - # "ip addr list" are compact. - if net.version == 6: - ip_cidr = str(net) - if ip_cidr in previous: - previous.remove(ip_cidr) - continue - - device.addr.add(ip_cidr) - - # clean up any old addresses - for ip_cidr in previous: - if ip_cidr not in preserve_ips: - if clean_connections: - device.delete_addr_and_conntrack_state(ip_cidr) - else: - device.addr.delete(ip_cidr) - - def init_router_port(self, - device_name, - ip_cidrs, - namespace, - preserve_ips=None, - extra_subnets=None, - clean_connections=False): - """Set the L3 settings for a router interface using data from the port. - - ip_cidrs: list of 'X.X.X.X/YY' strings - preserve_ips: list of ip cidrs that should not be removed from device - clean_connections: Boolean to indicate if we should cleanup connections - associated to removed ips - extra_subnets: An iterable of cidrs to add as routes without address - """ - LOG.debug("init_router_port: device_name(%s), namespace(%s)", - device_name, namespace) - self.init_l3(device_name=device_name, - ip_cidrs=ip_cidrs, - namespace=namespace, - preserve_ips=preserve_ips or [], - clean_connections=clean_connections) - - device = ip_lib.IPDevice(device_name, namespace=namespace) - - # Manage on-link routes (routes without an associated address) - new_onlink_cidrs = set(s['cidr'] for s in extra_subnets or []) - - v4_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_4) - v6_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_6) - existing_onlink_cidrs = set(r['cidr'] for r in v4_onlink + v6_onlink) - - for route in new_onlink_cidrs - existing_onlink_cidrs: - LOG.debug("adding onlink route(%s)", route) - device.route.add_onlink_route(route) - for route in (existing_onlink_cidrs - new_onlink_cidrs - - set(preserve_ips or [])): - LOG.debug("deleting onlink route(%s)", route) - device.route.delete_onlink_route(route) - - def add_ipv6_addr(self, device_name, v6addr, namespace, scope='global'): - device = ip_lib.IPDevice(device_name, - namespace=namespace) - net = netaddr.IPNetwork(v6addr) - device.addr.add(str(net), scope) - - def delete_ipv6_addr(self, device_name, v6addr, namespace): - device = ip_lib.IPDevice(device_name, - namespace=namespace) - device.delete_addr_and_conntrack_state(v6addr) - - def delete_ipv6_addr_with_prefix(self, device_name, prefix, namespace): - """Delete the first listed IPv6 address that falls within a given - prefix. - """ - device = ip_lib.IPDevice(device_name, namespace=namespace) - net = netaddr.IPNetwork(prefix) - for address in device.addr.list(scope='global', filters=['permanent']): - ip_address = netaddr.IPNetwork(address['cidr']) - if ip_address in net: - device.delete_addr_and_conntrack_state(address['cidr']) - break - - def get_ipv6_llas(self, device_name, namespace): - device = ip_lib.IPDevice(device_name, - namespace=namespace) - - return device.addr.list(scope='link', ip_version=6) - - def check_bridge_exists(self, bridge): - if not ip_lib.device_exists(bridge): - raise exceptions.BridgeDoesNotExist(bridge=bridge) - - def get_device_name(self, port): - return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN] - - @staticmethod - def configure_ipv6_ra(namespace, dev_name): - """Configure acceptance of IPv6 route advertisements on an intf.""" - # Learn the default router's IP address via RAs - ip_lib.IPWrapper(namespace=namespace).netns.execute( - ['sysctl', '-w', 'net.ipv6.conf.%s.accept_ra=2' % dev_name]) - - @abc.abstractmethod - def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - """Plug in the interface only for new devices that don't exist yet.""" - - def plug(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - if not ip_lib.device_exists(device_name, - namespace=namespace): - try: - self.plug_new(network_id, port_id, device_name, mac_address, - bridge, namespace, prefix, mtu) - except TypeError: - self.plug_new(network_id, port_id, device_name, mac_address, - bridge, namespace, prefix) - else: - LOG.info(_LI("Device %s already exists"), device_name) - - @abc.abstractmethod - def unplug(self, device_name, bridge=None, namespace=None, prefix=None): - """Unplug the interface.""" - - @property - def bridged(self): - """Whether the DHCP port is bridged to the VM TAP interfaces. - - When the DHCP port is bridged to the TAP interfaces for the - VMs for which it is providing DHCP service - as is the case - for most Neutron network implementations - the DHCP server - only needs to listen on the DHCP port, and will still receive - DHCP requests from all the relevant VMs. - - If the DHCP port is not bridged to the relevant VM TAP - interfaces, the DHCP server needs to listen explicitly on - those TAP interfaces, and to treat those as aliases of the - DHCP port where the IP subnet is defined. - """ - return True - - -class NullDriver(LinuxInterfaceDriver): - def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - pass - - def unplug(self, device_name, bridge=None, namespace=None, prefix=None): - pass - -class NSDriver(LinuxInterfaceDriver): - """Device independent driver enabling creation of a non device specific - interface in network spaces. Attachment to the device is not performed. - """ - MAX_TIME_FOR_DEVICE_EXISTENCE = 30 - - @classmethod - def _device_is_created_in_time(cls, device_name): - """See if device is created, within time limit.""" - attempt = 0 - while attempt < NSDriver.MAX_TIME_FOR_DEVICE_EXISTENCE: - if ip_lib.device_exists(device_name): - return True - attempt += 1 - eventlet.sleep(1) - LOG.error(_LE("Device %(dev)s was not created in %(time)d seconds"), - {'dev': device_name, - 'time': NSDriver.MAX_TIME_FOR_DEVICE_EXISTENCE}) - return False - - def _configure_mtu(self, ns_dev, mtu=None): - # Need to set MTU, after added to namespace. See review - # https://review.openstack.org/327651 - try: - # Note: network_device_mtu will be deprecated in future - mtu_override = self.conf.network_device_mtu - except cfg.NoSuchOptError: - LOG.warning(_LW("Config setting for MTU deprecated - any " - "override will be ignored.")) - mtu_override = None - if mtu_override: - mtu = mtu_override - LOG.debug("Overriding MTU to %d", mtu) - if mtu: - ns_dev.link.set_mtu(mtu) - else: - LOG.debug("No MTU provided - skipping setting value") - - def plug(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - - # Overriding this, we still want to add an existing device into the - # namespace. - self.plug_new(network_id, port_id, device_name, mac_address, - bridge, namespace, prefix, mtu) - - def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - - ip = ip_lib.IPWrapper() - ns_dev = ip.device(device_name) - - LOG.debug("Plugging dev: '%s' into namespace: '%s' ", - device_name, namespace) - - # Wait for device creation - if not self._device_is_created_in_time(device_name): - return - - ns_dev.link.set_address(mac_address) - - if namespace: - namespace_obj = ip.ensure_namespace(namespace) - namespace_obj.add_device_to_namespace(ns_dev) - - self._configure_mtu(ns_dev, mtu) - - ns_dev.link.set_up() - - def unplug(self, device_name, bridge=None, namespace=None, prefix=None): - # Device removal is done externally. Just remove the namespace - LOG.debug("Removing namespace: '%s'", namespace) - ip_lib.IPWrapper(namespace).garbage_collect_namespace() - - -class OVSInterfaceDriver(LinuxInterfaceDriver): - """Driver for creating an internal interface on an OVS bridge.""" - - DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX - - def __init__(self, conf): - super(OVSInterfaceDriver, self).__init__(conf) - if self.conf.ovs_use_veth: - self.DEV_NAME_PREFIX = 'ns-' - - def _get_tap_name(self, dev_name, prefix=None): - if self.conf.ovs_use_veth: - dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX, - n_const.TAP_DEVICE_PREFIX) - return dev_name - - def _ovs_add_port(self, bridge, device_name, port_id, mac_address, - internal=True): - attrs = [('external_ids', {'iface-id': port_id, - 'iface-status': 'active', - 'attached-mac': mac_address})] - if internal: - attrs.insert(0, ('type', 'internal')) - - ovs = ovs_lib.OVSBridge(bridge) - ovs.replace_port(device_name, *attrs) - - def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - """Plug in the interface.""" - if not bridge: - bridge = self.conf.ovs_integration_bridge - - self.check_bridge_exists(bridge) - - ip = ip_lib.IPWrapper() - tap_name = self._get_tap_name(device_name, prefix) - - if self.conf.ovs_use_veth: - # Create ns_dev in a namespace if one is configured. - root_dev, ns_dev = ip.add_veth(tap_name, - device_name, - namespace2=namespace) - root_dev.disable_ipv6() - else: - ns_dev = ip.device(device_name) - - internal = not self.conf.ovs_use_veth - self._ovs_add_port(bridge, tap_name, port_id, mac_address, - internal=internal) - - ns_dev.link.set_address(mac_address) - - # Add an interface created by ovs to the namespace. - if not self.conf.ovs_use_veth and namespace: - namespace_obj = ip.ensure_namespace(namespace) - namespace_obj.add_device_to_namespace(ns_dev) - - # NOTE(ihrachys): the order here is significant: we must set MTU after - # the device is moved into a namespace, otherwise OVS bridge does not - # allow to set MTU that is higher than the least of all device MTUs on - # the bridge - mtu = self.conf.network_device_mtu or mtu - if mtu: - ns_dev.link.set_mtu(mtu) - if self.conf.ovs_use_veth: - root_dev.link.set_mtu(mtu) - else: - LOG.warning(_LW("No MTU configured for port %s"), port_id) - - ns_dev.link.set_up() - if self.conf.ovs_use_veth: - root_dev.link.set_up() - - def unplug(self, device_name, bridge=None, namespace=None, prefix=None): - """Unplug the interface.""" - if not bridge: - bridge = self.conf.ovs_integration_bridge - - tap_name = self._get_tap_name(device_name, prefix) - self.check_bridge_exists(bridge) - ovs = ovs_lib.OVSBridge(bridge) - - try: - ovs.delete_port(tap_name) - if self.conf.ovs_use_veth: - device = ip_lib.IPDevice(device_name, namespace=namespace) - device.link.delete() - LOG.debug("Unplugged interface '%s'", device_name) - except RuntimeError: - LOG.error(_LE("Failed unplugging interface '%s'"), - device_name) - - -class IVSInterfaceDriver(LinuxInterfaceDriver): - """Driver for creating an internal interface on an IVS bridge.""" - - DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX - - def __init__(self, conf): - super(IVSInterfaceDriver, self).__init__(conf) - self.DEV_NAME_PREFIX = 'ns-' - - def _get_tap_name(self, dev_name, prefix=None): - dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX, - n_const.TAP_DEVICE_PREFIX) - return dev_name - - def _ivs_add_port(self, device_name, port_id, mac_address): - cmd = ['ivs-ctl', 'add-port', device_name] - utils.execute(cmd, run_as_root=True) - - def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - """Plug in the interface.""" - ip = ip_lib.IPWrapper() - tap_name = self._get_tap_name(device_name, prefix) - - root_dev, ns_dev = ip.add_veth(tap_name, device_name) - root_dev.disable_ipv6() - - self._ivs_add_port(tap_name, port_id, mac_address) - - ns_dev = ip.device(device_name) - ns_dev.link.set_address(mac_address) - - mtu = self.conf.network_device_mtu or mtu - if mtu: - ns_dev.link.set_mtu(mtu) - root_dev.link.set_mtu(mtu) - else: - LOG.warning(_LW("No MTU configured for port %s"), port_id) - - if namespace: - namespace_obj = ip.ensure_namespace(namespace) - namespace_obj.add_device_to_namespace(ns_dev) - - ns_dev.link.set_up() - root_dev.link.set_up() - - def unplug(self, device_name, bridge=None, namespace=None, prefix=None): - """Unplug the interface.""" - tap_name = self._get_tap_name(device_name, prefix) - try: - cmd = ['ivs-ctl', 'del-port', tap_name] - utils.execute(cmd, run_as_root=True) - device = ip_lib.IPDevice(device_name, namespace=namespace) - device.link.delete() - LOG.debug("Unplugged interface '%s'", device_name) - except RuntimeError: - LOG.error(_LE("Failed unplugging interface '%s'"), - device_name) - - -class BridgeInterfaceDriver(LinuxInterfaceDriver): - """Driver for creating bridge interfaces.""" - - DEV_NAME_PREFIX = 'ns-' - - def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None, mtu=None): - """Plugin the interface.""" - ip = ip_lib.IPWrapper() - - # Enable agent to define the prefix - tap_name = device_name.replace(prefix or self.DEV_NAME_PREFIX, - n_const.TAP_DEVICE_PREFIX) - # Create ns_veth in a namespace if one is configured. - root_veth, ns_veth = ip.add_veth(tap_name, device_name, - namespace2=namespace) - root_veth.disable_ipv6() - ns_veth.link.set_address(mac_address) - - mtu = self.conf.network_device_mtu or mtu - if mtu: - root_veth.link.set_mtu(mtu) - ns_veth.link.set_mtu(mtu) - else: - LOG.warning(_LW("No MTU configured for port %s"), port_id) - - root_veth.link.set_up() - ns_veth.link.set_up() - - def unplug(self, device_name, bridge=None, namespace=None, prefix=None): - """Unplug the interface.""" - device = ip_lib.IPDevice(device_name, namespace=namespace) - try: - device.link.delete() - LOG.debug("Unplugged interface '%s'", device_name) - except RuntimeError: - LOG.error(_LE("Failed unplugging interface '%s'"), - device_name) |