from neutron.agent.l3 import dvr
from neutron.agent.l3 import dvr_router
-from neutron.agent.l3 import dvr_snat_ns
from neutron.agent.l3 import event_observers
from neutron.agent.l3 import ha
from neutron.agent.l3 import ha_router
self.event_observers.notify(
adv_svc.AdvancedService.after_router_removed, ri)
- def _set_subnet_info(self, port):
- ips = port['fixed_ips']
- if not ips:
- raise Exception(_("Router port %s has no IP address") % port['id'])
- if len(ips) > 1:
- LOG.error(_LE("Ignoring multiple IPs on router port %s"),
- port['id'])
- prefixlen = netaddr.IPNetwork(port['subnet']['cidr']).prefixlen
- port['ip_cidr'] = "%s/%s" % (ips[0]['ip_address'], prefixlen)
-
- def _get_existing_devices(self, ri):
- ip_wrapper = ip_lib.IPWrapper(namespace=ri.ns_name)
- ip_devs = ip_wrapper.get_devices(exclude_loopback=True)
- return [ip_dev.name for ip_dev in ip_devs]
-
- def _process_internal_ports(self, ri):
- internal_ports = ri.router.get(l3_constants.INTERFACE_KEY, [])
- existing_port_ids = set([p['id'] for p in ri.internal_ports])
- current_port_ids = set([p['id'] for p in internal_ports
- if p['admin_state_up']])
- new_ports = [p for p in internal_ports if
- p['id'] in current_port_ids and
- p['id'] not in existing_port_ids]
- old_ports = [p for p in ri.internal_ports if
- p['id'] not in current_port_ids]
-
- new_ipv6_port = False
- old_ipv6_port = False
- for p in new_ports:
- self._set_subnet_info(p)
- self.internal_network_added(ri, p)
- ri.internal_ports.append(p)
- if ri.router['distributed']:
- ri._set_subnet_arp_info(p)
- if (not new_ipv6_port and
- netaddr.IPNetwork(p['subnet']['cidr']).version == 6):
- new_ipv6_port = True
-
- for p in old_ports:
- self.internal_network_removed(ri, p)
- ri.internal_ports.remove(p)
- if (not old_ipv6_port and
- netaddr.IPNetwork(p['subnet']['cidr']).version == 6):
- old_ipv6_port = True
-
- # Enable RA
- if new_ipv6_port or old_ipv6_port:
- ri.radvd.enable(internal_ports)
-
- existing_devices = self._get_existing_devices(ri)
- current_internal_devs = set([n for n in existing_devices
- if n.startswith(INTERNAL_DEV_PREFIX)])
- current_port_devs = set([ri.get_internal_device_name(id) for
- id in current_port_ids])
- stale_devs = current_internal_devs - current_port_devs
- for stale_dev in stale_devs:
- LOG.debug('Deleting stale internal router device: %s',
- stale_dev)
- self.driver.unplug(stale_dev,
- namespace=ri.ns_name,
- prefix=INTERNAL_DEV_PREFIX)
-
def _process_external_gateway(self, ri):
ex_gw_port = ri.get_ex_gw_port()
ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or
port2_filtered = _get_filtered_dict(port2, keys_to_ignore)
return port1_filtered == port2_filtered
- self._set_subnet_info(ex_gw_port)
+ ri._set_subnet_info(ex_gw_port)
if not ri.ex_gw_port:
self.external_gateway_added(ri, ex_gw_port, interface_name)
elif not _gateway_ports_equal(ex_gw_port, ri.ex_gw_port):
elif not ex_gw_port and ri.ex_gw_port:
self.external_gateway_removed(ri, ri.ex_gw_port, interface_name)
- existing_devices = self._get_existing_devices(ri)
+ existing_devices = ri._get_existing_devices()
stale_devs = [dev for dev in existing_devices
if dev.startswith(EXTERNAL_DEV_PREFIX)
and dev != interface_name]
# Process SNAT rules for external gateway
if (not ri.router['distributed'] or
- ex_gw_port and self.get_gw_port_host(ri.router) == self.host):
+ ex_gw_port and ri.get_gw_port_host() == self.host):
ri.perform_snat_action(self._handle_router_snat_rules,
interface_name)
if ri.router.get('distributed') and ex_gw_port:
ri.fip_ns = self.get_fip_ns(ex_gw_port['network_id'])
ri.fip_ns.scan_fip_ports(ri)
- self._process_internal_ports(ri)
+ ri._process_internal_ports()
self._process_external(ri)
# Process static routes for router
ri.routes_updated()
def create_dvr_fip_interfaces(self, ri, ex_gw_port):
floating_ips = ri.get_floating_ips()
- fip_agent_port = self.get_floating_agent_gw_interface(
- ri, ex_gw_port['network_id'])
+ fip_agent_port = ri.get_floating_agent_gw_interface(
+ ex_gw_port['network_id'])
LOG.debug("FloatingIP agent gateway port received from the plugin: "
"%s", fip_agent_port)
if floating_ips:
if 'subnet' not in fip_agent_port:
LOG.error(_LE('Missing subnet/agent_gateway_port'))
else:
- self._set_subnet_info(fip_agent_port)
+ ri._set_subnet_info(fip_agent_port)
ri.fip_ns.create_gateway_port(fip_agent_port)
if ri.fip_ns.agent_gateway_port and floating_ips:
def get_external_device_name(self, port_id):
return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
- def get_floating_agent_gw_interface(self, ri, ext_net_id):
- """Filter Floating Agent GW port for the external network."""
- fip_ports = ri.router.get(l3_constants.FLOATINGIP_AGENT_INTF_KEY, [])
- return next(
- (p for p in fip_ports if p['network_id'] == ext_net_id), None)
-
def external_gateway_added(self, ri, ex_gw_port, interface_name):
if ri.router['distributed']:
ip_wrapr = ip_lib.IPWrapper(namespace=ri.ns_name)
ip_wrapr.netns.execute(['sysctl', '-w',
'net.ipv4.conf.all.send_redirects=0'])
- snat_ports = self.get_snat_interfaces(ri)
+ snat_ports = ri.get_snat_interfaces()
for p in ri.internal_ports:
- gateway = self._map_internal_interfaces(ri, p, snat_ports)
+ gateway = ri._map_internal_interfaces(p, snat_ports)
id_name = ri.get_internal_device_name(p['id'])
if gateway:
- self._snat_redirect_add(ri, gateway['fixed_ips'][0]
- ['ip_address'], p, id_name)
+ ri._snat_redirect_add(
+ gateway['fixed_ips'][0]['ip_address'], p, id_name)
if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT and
- self.get_gw_port_host(ri.router) == self.host):
+ ri.get_gw_port_host() == self.host):
self._create_dvr_gateway(ri, ex_gw_port, interface_name,
snat_ports)
for port in snat_ports:
preserve_ips = []
if ri.router['distributed']:
if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT and
- self.get_gw_port_host(ri.router) == self.host):
+ ri.get_gw_port_host() == self.host):
ns_name = ri.snat_namespace.name
else:
# no centralized SNAT gateway for this node/agent
to_fip_interface_name = (
self._get_external_device_interface_name(ri, ex_gw_port))
ri.process_floating_ip_addresses(to_fip_interface_name)
- snat_ports = self.get_snat_interfaces(ri)
+ snat_ports = ri.get_snat_interfaces()
for p in ri.internal_ports:
gateway = self._map_internal_interfaces(ri, p, snat_ports)
internal_interface = ri.get_internal_device_name(p['id'])
- self._snat_redirect_remove(ri, gateway['fixed_ips'][0]
- ['ip_address'],
- p, internal_interface)
+ ri._snat_redirect_remove(gateway['fixed_ips'][0]['ip_address'],
+ p,
+ internal_interface)
if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT
- and self.get_gw_port_host(ri.router) == self.host):
+ and ri.get_gw_port_host() == self.host):
ns_name = ri.snat_namespace.name
else:
# not hosting agent - no work to do
(interface_name, ex_gw_ip))]
return rules
- def _internal_network_added(self, ns_name, network_id, port_id,
- internal_cidr, mac_address,
- interface_name, prefix, is_ha=False):
- if not ip_lib.device_exists(interface_name, namespace=ns_name):
- self.driver.plug(network_id, port_id, interface_name, mac_address,
- namespace=ns_name,
- prefix=prefix)
-
- if not is_ha:
- self.driver.init_l3(interface_name, [internal_cidr],
- namespace=ns_name)
- ip_address = internal_cidr.split('/')[0]
- ip_lib.send_gratuitous_arp(ns_name,
- interface_name,
- ip_address,
- self.conf.send_arp_for_ha)
-
- def internal_network_added(self, ri, port):
- network_id = port['network_id']
- port_id = port['id']
- internal_cidr = port['ip_cidr']
- mac_address = port['mac_address']
-
- interface_name = ri.get_internal_device_name(port_id)
-
- self._internal_network_added(ri.ns_name, network_id, port_id,
- internal_cidr, mac_address,
- interface_name, INTERNAL_DEV_PREFIX,
- ri.is_ha)
-
- if ri.is_ha:
- ri._ha_disable_addressing_on_interface(interface_name)
- ri._add_vip(internal_cidr, interface_name)
-
- ex_gw_port = ri.get_ex_gw_port()
- if ri.router['distributed'] and ex_gw_port:
- snat_ports = self.get_snat_interfaces(ri)
- sn_port = self._map_internal_interfaces(ri, port, snat_ports)
- if sn_port:
- self._snat_redirect_add(ri, sn_port['fixed_ips'][0]
- ['ip_address'], port, interface_name)
- if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT
- and self.get_gw_port_host(ri.router) == self.host):
- ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(
- ri.router['id'])
- self._set_subnet_info(sn_port)
- interface_name = (
- self.get_snat_int_device_name(sn_port['id']))
- self._internal_network_added(ns_name,
- sn_port['network_id'],
- sn_port['id'],
- sn_port['ip_cidr'],
- sn_port['mac_address'],
- interface_name,
- dvr.SNAT_INT_DEV_PREFIX)
-
- def internal_network_removed(self, ri, port):
- port_id = port['id']
- interface_name = ri.get_internal_device_name(port_id)
- if ri.router['distributed'] and ri.ex_gw_port:
- sn_port = self._map_internal_interfaces(ri, port, ri.snat_ports)
- if sn_port:
- self._snat_redirect_remove(ri, sn_port['fixed_ips'][0]
- ['ip_address'], port,
- interface_name)
- if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT
- and ri.ex_gw_port['binding:host_id'] == self.host):
- snat_interface = (
- self.get_snat_int_device_name(sn_port['id'])
- )
- ns_name = ri.snat_namespace.name
- prefix = dvr.SNAT_INT_DEV_PREFIX
- if ip_lib.device_exists(snat_interface,
- namespace=ns_name):
- self.driver.unplug(snat_interface, namespace=ns_name,
- prefix=prefix)
-
- if ip_lib.device_exists(interface_name, namespace=ri.ns_name):
- if ri.is_ha:
- ri._clear_vips(interface_name)
- self.driver.unplug(interface_name, namespace=ri.ns_name,
- prefix=INTERNAL_DEV_PREFIX)
-
def router_deleted(self, context, router_id):
"""Deal with router deletion RPC message."""
LOG.debug('Got router deleted notification for %s', router_id)
# License for the specific language governing permissions and limitations
# under the License.
-import binascii
import weakref
-import netaddr
from oslo_log import log as logging
from neutron.agent.l3 import dvr_fip_ns
from neutron.agent.l3 import dvr_snat_ns
-from neutron.agent.linux import ip_lib
from neutron.agent.linux import iptables_manager
-from neutron.common import constants as l3_constants
-from neutron.i18n import _LE
LOG = logging.getLogger(__name__)
# TODO(Carl) Following constants retained to increase SNR during refactoring
SNAT_INT_DEV_PREFIX = dvr_snat_ns.SNAT_INT_DEV_PREFIX
SNAT_NS_PREFIX = dvr_snat_ns.SNAT_NS_PREFIX
-# xor-folding mask used for IPv6 rule index
-MASK_30 = 0x3fffffff
class AgentMixin(object):
def get_ports_by_subnet(self, subnet_id):
return self.plugin_rpc.get_ports_by_subnet(self.context, subnet_id)
- def get_snat_int_device_name(self, port_id):
- return (SNAT_INT_DEV_PREFIX +
- port_id)[:self.driver.DEV_NAME_LEN]
-
- def get_snat_interfaces(self, ri):
- return ri.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, [])
-
- def get_gw_port_host(self, router):
- host = router.get('gw_port_host')
- if not host:
- LOG.debug("gw_port_host missing from router: %s",
- router['id'])
- return host
-
- def _get_snat_idx(self, ip_cidr):
- """Generate index for DVR snat rules and route tables.
-
- The index value has to be 32 bits or less but more than the system
- generated entries i.e. 32768. For IPv4 use the numeric value of the
- cidr. For IPv6 generate a crc32 bit hash and xor-fold to 30 bits.
- Use the freed range to extend smaller values so that they become
- greater than system generated entries.
- """
- net = netaddr.IPNetwork(ip_cidr)
- if net.version == 6:
- # the crc32 & 0xffffffff is for Python 2.6 and 3.0 compatibility
- snat_idx = binascii.crc32(ip_cidr) & 0xffffffff
- # xor-fold the hash to reserve upper range to extend smaller values
- snat_idx = (snat_idx >> 30) ^ (snat_idx & MASK_30)
- if snat_idx < 32768:
- snat_idx = snat_idx + MASK_30
- else:
- snat_idx = net.value
- return snat_idx
-
- def _map_internal_interfaces(self, ri, int_port, snat_ports):
- """Return the SNAT port for the given internal interface port."""
- fixed_ip = int_port['fixed_ips'][0]
- subnet_id = fixed_ip['subnet_id']
- match_port = [p for p in snat_ports if
- p['fixed_ips'][0]['subnet_id'] == subnet_id]
- if match_port:
- return match_port[0]
- else:
- LOG.error(_LE('DVR: no map match_port found!'))
-
def _create_dvr_gateway(self, ri, ex_gw_port, gw_interface_name,
snat_ports):
"""Create SNAT namespace."""
# connect snat_ports to br_int from SNAT namespace
for port in snat_ports:
# create interface_name
- self._set_subnet_info(port)
- interface_name = self.get_snat_int_device_name(port['id'])
- self._internal_network_added(snat_ns.name, port['network_id'],
- port['id'], port['ip_cidr'],
- port['mac_address'], interface_name,
- SNAT_INT_DEV_PREFIX)
+ ri._set_subnet_info(port)
+ interface_name = ri.get_snat_int_device_name(port['id'])
+ # TODO(Carl) calling private method on router. Will fix soon.
+ ri._internal_network_added(snat_ns.name, port['network_id'],
+ port['id'], port['ip_cidr'],
+ port['mac_address'], interface_name,
+ SNAT_INT_DEV_PREFIX)
self._external_gateway_added(ri, ex_gw_port, gw_interface_name,
snat_ns.name, preserve_ips=[])
ri.snat_iptables_manager = iptables_manager.IptablesManager(
# kicks the FW Agent to add rules for the snat namespace
self.process_router_add(ri)
- def _snat_redirect_add(self, ri, gateway, sn_port, sn_int):
- """Adds rules and routes for SNAT redirection."""
- try:
- ip_cidr = sn_port['ip_cidr']
- snat_idx = self._get_snat_idx(ip_cidr)
- ns_ipr = ip_lib.IPRule(namespace=ri.ns_name)
- ns_ipd = ip_lib.IPDevice(sn_int, namespace=ri.ns_name)
- ns_ipwrapr = ip_lib.IPWrapper(namespace=ri.ns_name)
- ns_ipd.route.add_gateway(gateway, table=snat_idx)
- ns_ipr.rule.add(ip_cidr, snat_idx, snat_idx)
- ns_ipwrapr.netns.execute(['sysctl', '-w', 'net.ipv4.conf.%s.'
- 'send_redirects=0' % sn_int])
- except Exception:
- LOG.exception(_LE('DVR: error adding redirection logic'))
-
- def _snat_redirect_remove(self, ri, gateway, sn_port, sn_int):
- """Removes rules and routes for SNAT redirection."""
- try:
- ip_cidr = sn_port['ip_cidr']
- snat_idx = self._get_snat_idx(ip_cidr)
- ns_ipr = ip_lib.IPRule(namespace=ri.ns_name)
- ns_ipd = ip_lib.IPDevice(sn_int, namespace=ri.ns_name)
- ns_ipd.route.delete_gateway(gateway, table=snat_idx)
- ns_ipr.rule.delete(ip_cidr, snat_idx, snat_idx)
- except Exception:
- LOG.exception(_LE('DVR: removed snat failed'))
-
def add_arp_entry(self, context, payload):
"""Add arp entry into router namespace. Called from RPC."""
router_id = payload['router_id']
# License for the specific language governing permissions and limitations
# under the License.
+import binascii
+import netaddr
+
from oslo_log import log as logging
from oslo_utils import excutils
from neutron.i18n import _LE
LOG = logging.getLogger(__name__)
+# xor-folding mask used for IPv6 rule index
+MASK_30 = 0x3fffffff
class DvrRouter(router.RouterInfo):
floating_ips = super(DvrRouter, self).get_floating_ips()
return [i for i in floating_ips if i['host'] == self.host]
+ def get_snat_interfaces(self):
+ return self.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, [])
+
+ def get_snat_int_device_name(self, port_id):
+ long_name = dvr_snat_ns.SNAT_INT_DEV_PREFIX + port_id
+ return long_name[:self.driver.DEV_NAME_LEN]
+
def _handle_fip_nat_rules(self, interface_name, action):
"""Configures NAT rules for Floating IPs for DVR.
return l3_constants.FLOATINGIP_STATUS_ERROR
# Special Handling for DVR - update FIP namespace
- # and ri.namespace to handle DVR based FIP
ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address'])
self.floating_ip_added_dist(fip, ip_cidr)
return l3_constants.FLOATINGIP_STATUS_ACTIVE
p['mac_address'],
subnet_id,
'add')
+
+ def _map_internal_interfaces(self, int_port, snat_ports):
+ """Return the SNAT port for the given internal interface port."""
+ fixed_ip = int_port['fixed_ips'][0]
+ subnet_id = fixed_ip['subnet_id']
+ match_port = [p for p in snat_ports if
+ p['fixed_ips'][0]['subnet_id'] == subnet_id]
+ if match_port:
+ return match_port[0]
+ else:
+ LOG.error(_LE('DVR: no map match_port found!'))
+
+ @staticmethod
+ def _get_snat_idx(ip_cidr):
+ """Generate index for DVR snat rules and route tables.
+
+ The index value has to be 32 bits or less but more than the system
+ generated entries i.e. 32768. For IPv4 use the numeric value of the
+ cidr. For IPv6 generate a crc32 bit hash and xor-fold to 30 bits.
+ Use the freed range to extend smaller values so that they become
+ greater than system generated entries.
+ """
+ net = netaddr.IPNetwork(ip_cidr)
+ if net.version == 6:
+ # the crc32 & 0xffffffff is for Python 2.6 and 3.0 compatibility
+ snat_idx = binascii.crc32(ip_cidr) & 0xffffffff
+ # xor-fold the hash to reserve upper range to extend smaller values
+ snat_idx = (snat_idx >> 30) ^ (snat_idx & MASK_30)
+ if snat_idx < 32768:
+ snat_idx = snat_idx + MASK_30
+ else:
+ snat_idx = net.value
+ return snat_idx
+
+ def _snat_redirect_add(self, gateway, sn_port, sn_int):
+ """Adds rules and routes for SNAT redirection."""
+ try:
+ ip_cidr = sn_port['ip_cidr']
+ snat_idx = self._get_snat_idx(ip_cidr)
+ ns_ipr = ip_lib.IPRule(namespace=self.ns_name)
+ ns_ipd = ip_lib.IPDevice(sn_int, namespace=self.ns_name)
+ ns_ipwrapr = ip_lib.IPWrapper(namespace=self.ns_name)
+ ns_ipd.route.add_gateway(gateway, table=snat_idx)
+ ns_ipr.rule.add(ip_cidr, snat_idx, snat_idx)
+ ns_ipwrapr.netns.execute(['sysctl', '-w', 'net.ipv4.conf.%s.'
+ 'send_redirects=0' % sn_int])
+ except Exception:
+ LOG.exception(_LE('DVR: error adding redirection logic'))
+
+ def _snat_redirect_remove(self, gateway, sn_port, sn_int):
+ """Removes rules and routes for SNAT redirection."""
+ try:
+ ip_cidr = sn_port['ip_cidr']
+ snat_idx = self._get_snat_idx(ip_cidr)
+ ns_ipr = ip_lib.IPRule(namespace=self.ns_name)
+ ns_ipd = ip_lib.IPDevice(sn_int, namespace=self.ns_name)
+ ns_ipd.route.delete_gateway(gateway, table=snat_idx)
+ ns_ipr.rule.delete(ip_cidr, snat_idx, snat_idx)
+ except Exception:
+ LOG.exception(_LE('DVR: removed snat failed'))
+
+ def get_gw_port_host(self):
+ host = self.router.get('gw_port_host')
+ if not host:
+ LOG.debug("gw_port_host missing from router: %s",
+ self.router['id'])
+ return host
+
+ def internal_network_added(self, port):
+ super(DvrRouter, self).internal_network_added(port)
+
+ ex_gw_port = self.get_ex_gw_port()
+ if not ex_gw_port:
+ return
+
+ snat_ports = self.get_snat_interfaces()
+ sn_port = self._map_internal_interfaces(port, snat_ports)
+ if not sn_port:
+ return
+
+ interface_name = self.get_internal_device_name(port['id'])
+ self._snat_redirect_add(sn_port['fixed_ips'][0]['ip_address'],
+ port,
+ interface_name)
+
+ # TODO(Carl) This is a sign that dvr needs two router classes.
+ is_this_snat_host = (self.agent_conf.agent_mode == 'dvr_snat' and
+ self.get_gw_port_host() == self.host)
+ if not is_this_snat_host:
+ return
+
+ ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id'])
+ self._set_subnet_info(sn_port)
+ interface_name = self.get_snat_int_device_name(sn_port['id'])
+ self._internal_network_added(
+ ns_name,
+ sn_port['network_id'],
+ sn_port['id'],
+ sn_port['ip_cidr'],
+ sn_port['mac_address'],
+ interface_name,
+ dvr_snat_ns.SNAT_INT_DEV_PREFIX)
+
+ self._set_subnet_arp_info(port)
+
+ def _dvr_internal_network_removed(self, port):
+ if not self.ex_gw_port:
+ return
+
+ sn_port = self._map_internal_interfaces(port, self.snat_ports)
+ if not sn_port:
+ return
+
+ # DVR handling code for SNAT
+ interface_name = self.get_internal_device_name(port['id'])
+ self._snat_redirect_remove(sn_port['fixed_ips'][0]['ip_address'],
+ port,
+ interface_name)
+
+ is_this_snat_host = (self.agent_conf.agent_mode == 'dvr_snat' and
+ self.ex_gw_port['binding:host_id'] == self.host)
+ if not is_this_snat_host:
+ return
+
+ snat_interface = (
+ self.get_snat_int_device_name(sn_port['id']))
+ ns_name = self.snat_namespace.name
+ prefix = dvr_snat_ns.SNAT_INT_DEV_PREFIX
+ if ip_lib.device_exists(snat_interface, namespace=ns_name):
+ self.driver.unplug(snat_interface, namespace=ns_name,
+ prefix=prefix)
+
+ def internal_network_removed(self, port):
+ self._dvr_internal_network_removed(port)
+ super(DvrRouter, self).internal_network_removed(port)
+
+ def get_floating_agent_gw_interface(self, ext_net_id):
+ """Filter Floating Agent GW port for the external network."""
+ fip_ports = self.router.get(l3_constants.FLOATINGIP_AGENT_INTF_KEY, [])
+ return next(
+ (p for p in fip_ports if p['network_id'] == ext_net_id), None)
ri.router_id)
return
- self._set_subnet_info(ha_port)
+ ri._set_subnet_info(ha_port)
ri.ha_network_added(ha_port['network_id'],
ha_port['id'],
ha_port['ip_cidr'],
def remove_floating_ip(self, device, ip_cidr):
self._remove_vip(ip_cidr)
+
+ def internal_network_added(self, port):
+ port_id = port['id']
+ interface_name = self.get_internal_device_name(port_id)
+
+ if not ip_lib.device_exists(interface_name, namespace=self.ns_name):
+ self.driver.plug(port['network_id'],
+ port_id,
+ interface_name,
+ port['mac_address'],
+ namespace=self.ns_name,
+ prefix=router.INTERNAL_DEV_PREFIX)
+
+ self._ha_disable_addressing_on_interface(interface_name)
+ self._add_vip(port['ip_cidr'], interface_name)
+
+ def internal_network_removed(self, port):
+ super(HaRouter, self).internal_network_removed(port)
+
+ interface_name = self.get_internal_device_name(port['id'])
+ self._clear_vips(interface_name)
# License for the specific language governing permissions and limitations
# under the License.
+import netaddr
+
from oslo_log import log as logging
from neutron.agent.l3 import namespaces
from neutron.common import constants as l3_constants
from neutron.common import exceptions as n_exc
from neutron.common import utils as common_utils
-from neutron.i18n import _LW
+from neutron.i18n import _LE, _LW
LOG = logging.getLogger(__name__)
-INTERNAL_DEV_PREFIX = 'qr-'
+INTERNAL_DEV_PREFIX = namespaces.INTERNAL_DEV_PREFIX
class RouterInfo(object):
def get_internal_device_name(self, port_id):
return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
+ def _set_subnet_info(self, port):
+ ips = port['fixed_ips']
+ if not ips:
+ raise Exception(_("Router port %s has no IP address") % port['id'])
+ if len(ips) > 1:
+ LOG.error(_LE("Ignoring multiple IPs on router port %s"),
+ port['id'])
+ prefixlen = netaddr.IPNetwork(port['subnet']['cidr']).prefixlen
+ port['ip_cidr'] = "%s/%s" % (ips[0]['ip_address'], prefixlen)
+
def perform_snat_action(self, snat_callback, *args):
# Process SNAT rules for attached subnets
if self._snat_action:
self.radvd.disable()
if self.router_namespace:
self.router_namespace.delete()
+
+ def _internal_network_added(self, ns_name, network_id, port_id,
+ internal_cidr, mac_address,
+ interface_name, prefix):
+ if not ip_lib.device_exists(interface_name,
+ namespace=ns_name):
+ self.driver.plug(network_id, port_id, interface_name, mac_address,
+ namespace=ns_name,
+ prefix=prefix)
+
+ self.driver.init_l3(interface_name, [internal_cidr],
+ namespace=ns_name)
+ ip_address = internal_cidr.split('/')[0]
+ ip_lib.send_gratuitous_arp(ns_name,
+ interface_name,
+ ip_address,
+ self.agent_conf.send_arp_for_ha)
+
+ def internal_network_added(self, port):
+ network_id = port['network_id']
+ port_id = port['id']
+ internal_cidr = port['ip_cidr']
+ mac_address = port['mac_address']
+
+ interface_name = self.get_internal_device_name(port_id)
+
+ self._internal_network_added(self.ns_name,
+ network_id,
+ port_id,
+ internal_cidr,
+ mac_address,
+ interface_name,
+ INTERNAL_DEV_PREFIX)
+
+ def internal_network_removed(self, port):
+ interface_name = self.get_internal_device_name(port['id'])
+
+ if ip_lib.device_exists(interface_name, namespace=self.ns_name):
+ self.driver.unplug(interface_name, namespace=self.ns_name,
+ prefix=INTERNAL_DEV_PREFIX)
+
+ def _get_existing_devices(self):
+ ip_wrapper = ip_lib.IPWrapper(namespace=self.ns_name)
+ ip_devs = ip_wrapper.get_devices(exclude_loopback=True)
+ return [ip_dev.name for ip_dev in ip_devs]
+
+ def _process_internal_ports(self):
+ existing_port_ids = set(p['id'] for p in self.internal_ports)
+
+ internal_ports = self.router.get(l3_constants.INTERFACE_KEY, [])
+ current_port_ids = set(p['id'] for p in internal_ports
+ if p['admin_state_up'])
+
+ new_port_ids = current_port_ids - existing_port_ids
+ new_ports = [p for p in internal_ports if p['id'] in new_port_ids]
+ old_ports = [p for p in self.internal_ports
+ if p['id'] not in current_port_ids]
+
+ new_ipv6_port = False
+ old_ipv6_port = False
+ for p in new_ports:
+ self._set_subnet_info(p)
+ self.internal_network_added(p)
+ self.internal_ports.append(p)
+ if (not new_ipv6_port and
+ netaddr.IPNetwork(p['subnet']['cidr']).version == 6):
+ new_ipv6_port = True
+
+ for p in old_ports:
+ self.internal_network_removed(p)
+ self.internal_ports.remove(p)
+ if (not old_ipv6_port and
+ netaddr.IPNetwork(p['subnet']['cidr']).version == 6):
+ old_ipv6_port = True
+
+ # Enable RA
+ if new_ipv6_port or old_ipv6_port:
+ self.radvd.enable(internal_ports)
+
+ existing_devices = self._get_existing_devices()
+ current_internal_devs = set(n for n in existing_devices
+ if n.startswith(
+ INTERNAL_DEV_PREFIX))
+ current_port_devs = set(self.get_internal_device_name(port_id)
+ for port_id in current_port_ids)
+ stale_devs = current_internal_devs - current_port_devs
+ for stale_dev in stale_devs:
+ LOG.debug('Deleting stale internal router device: %s',
+ stale_dev)
+ self.driver.unplug(stale_dev,
+ namespace=self.ns_name,
+ prefix=INTERNAL_DEV_PREFIX)
router_ports = [port]
ri.router.get.return_value = router_ports
self.assertEqual(None, ri._get_internal_port(mock.sentinel.subnet_id2))
+
+ def test__get_snat_idx_ipv4(self):
+ ip_cidr = '101.12.13.00/24'
+ ri = self._create_router(mock.MagicMock())
+ snat_idx = ri._get_snat_idx(ip_cidr)
+ # 0x650C0D00 is numerical value of 101.12.13.00
+ self.assertEqual(0x650C0D00, snat_idx)
+
+ def test__get_snat_idx_ipv6(self):
+ ip_cidr = '2620:0:a03:e100::/64'
+ ri = self._create_router(mock.MagicMock())
+ snat_idx = ri._get_snat_idx(ip_cidr)
+ # 0x3D345705 is 30 bit xor folded crc32 of the ip_cidr
+ self.assertEqual(0x3D345705, snat_idx)
+
+ def test__get_snat_idx_ipv6_below_32768(self):
+ ip_cidr = 'd488::/30'
+ # crc32 of this ip_cidr is 0x1BD7
+ ri = self._create_router(mock.MagicMock())
+ snat_idx = ri._get_snat_idx(ip_cidr)
+ # 0x1BD7 + 0x3FFFFFFF = 0x40001BD6
+ self.assertEqual(0x40001BD6, snat_idx)
self.ri_kwargs = {'agent_conf': self.conf,
'interface_driver': self.mock_driver}
- def _prepare_internal_network_data(self):
- port_id = _uuid()
- router_id = _uuid()
- network_id = _uuid()
- router = prepare_router_data(num_internal_ports=2)
- router_id = router['id']
- agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
- ri = l3router.RouterInfo(router_id, router,
- **self.ri_kwargs)
- cidr = '99.0.1.9/24'
- mac = 'ca:fe:de:ad:be:ef'
- port = {'network_id': network_id,
- 'id': port_id, 'ip_cidr': cidr,
- 'mac_address': mac}
-
- return agent, ri, port
-
def _process_router_instance_for_agent(self, agent, ri, router):
ri.router = router
if not ri.radvd:
l3_agent.L3NATAgent(HOSTNAME, self.conf)
def _test_internal_network_action(self, action):
- agent, ri, port = self._prepare_internal_network_data()
+ router = prepare_router_data(num_internal_ports=2)
+ router_id = router['id']
+ ri = l3router.RouterInfo(router_id, router, **self.ri_kwargs)
+ port = {'network_id': _uuid(),
+ 'id': _uuid(),
+ 'ip_cidr': '99.0.1.9/24',
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
+
interface_name = ri.get_internal_device_name(port['id'])
if action == 'add':
self.device_exists.return_value = False
- agent.internal_network_added(ri, port)
+ ri.internal_network_added(port)
self.assertEqual(self.mock_driver.plug.call_count, 1)
self.assertEqual(self.mock_driver.init_l3.call_count, 1)
self.send_arp.assert_called_once_with(ri.ns_name, interface_name,
'99.0.1.9', mock.ANY)
elif action == 'remove':
self.device_exists.return_value = True
- agent.internal_network_removed(ri, port)
+ ri.internal_network_removed(port)
self.assertEqual(self.mock_driver.unplug.call_count, 1)
else:
raise Exception("Invalid action %s" % action)
def _test_internal_network_action_dist(self, action):
- agent, ri, port = self._prepare_internal_network_data()
- ri.router['distributed'] = True
+ router = prepare_router_data(num_internal_ports=2)
+ router_id = router['id']
+ agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
+ ri = dvr_router.DvrRouter(
+ agent, HOSTNAME, router_id, router, **self.ri_kwargs)
+ port = {'network_id': _uuid(),
+ 'id': _uuid(),
+ 'ip_cidr': '99.0.1.9/24',
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
+
ri.router['gw_port_host'] = HOSTNAME
agent.host = HOSTNAME
agent.conf.agent_mode = 'dvr_snat'
if action == 'add':
self.device_exists.return_value = False
- agent._map_internal_interfaces = mock.Mock(return_value=sn_port)
- agent._snat_redirect_add = mock.Mock()
- agent._set_subnet_info = mock.Mock()
- agent._internal_network_added = mock.Mock()
- agent.internal_network_added(ri, port)
- self.assertEqual(agent._snat_redirect_add.call_count, 1)
- self.assertEqual(agent._set_subnet_info.call_count, 1)
- self.assertEqual(agent._internal_network_added.call_count, 2)
- agent._internal_network_added.assert_called_with(
+ ri._map_internal_interfaces = mock.Mock(return_value=sn_port)
+ ri._snat_redirect_add = mock.Mock()
+ ri._set_subnet_info = mock.Mock()
+ ri._set_subnet_arp_info = mock.Mock()
+ ri._internal_network_added = mock.Mock()
+ ri.internal_network_added(port)
+ self.assertEqual(ri._snat_redirect_add.call_count, 1)
+ self.assertEqual(ri._set_subnet_info.call_count, 1)
+ self.assertEqual(ri._internal_network_added.call_count, 2)
+ ri._set_subnet_arp_info.assert_called_once_with(port)
+ ri._internal_network_added.assert_called_with(
dvr_snat_ns.SnatNamespace.get_snat_ns_name(ri.router['id']),
sn_port['network_id'],
sn_port['id'],
sn_port['ip_cidr'],
sn_port['mac_address'],
- agent.get_snat_int_device_name(sn_port['id']),
+ ri.get_snat_int_device_name(sn_port['id']),
dvr_snat_ns.SNAT_INT_DEV_PREFIX)
elif action == 'remove':
self.device_exists.return_value = False
- agent._map_internal_interfaces = mock.Mock(return_value=sn_port)
- agent._snat_redirect_remove = mock.Mock()
- agent.internal_network_removed(ri, port)
- agent._snat_redirect_remove.assert_called_with(
- ri,
+ ri._map_internal_interfaces = mock.Mock(return_value=sn_port)
+ ri._snat_redirect_remove = mock.Mock()
+ ri.internal_network_removed(port)
+ ri._snat_redirect_remove.assert_called_with(
sn_port['fixed_ips'][0]['ip_address'],
port,
ri.get_internal_device_name(port['id']))
agent.conf.agent_mode = 'dvr_snat'
agent.host = HOSTNAME
agent._create_dvr_gateway = mock.Mock()
- agent.get_snat_interfaces = mock.Mock(return_value=self.snat_ports)
ri = dvr_router.DvrRouter(agent,
HOSTNAME,
router['id'],
router,
**self.ri_kwargs)
+ ri.get_snat_interfaces = mock.Mock(return_value=self.snat_ports)
ri.create_snat_namespace()
ri.fip_ns = agent.get_fip_ns(ex_net_id)
ri.internal_ports = self.snat_ports
elif action == 'remove':
self.device_exists.return_value = True
agent._map_internal_interfaces = mock.Mock(return_value=sn_port)
- agent._snat_redirect_remove = mock.Mock()
+ ri._snat_redirect_remove = mock.Mock()
agent.external_gateway_removed(ri, ex_gw_port, interface_name)
if not router.get('distributed'):
self.mock_driver.unplug.assert_called_once_with(
namespace=mock.ANY,
prefix=mock.ANY)
else:
- agent._snat_redirect_remove.assert_called_with(
- ri,
+ ri._snat_redirect_remove.assert_called_with(
sn_port['fixed_ips'][0]['ip_address'],
sn_port,
ri.get_internal_device_name(sn_port['id']))
else:
self.assertIn(r.rule, expected_rules)
- def test__get_snat_idx_ipv4(self):
- ip_cidr = '101.12.13.00/24'
- agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
- snat_idx = agent._get_snat_idx(ip_cidr)
- # 0x650C0D00 is numerical value of 101.12.13.00
- self.assertEqual(0x650C0D00, snat_idx)
-
- def test__get_snat_idx_ipv6(self):
- ip_cidr = '2620:0:a03:e100::/64'
- agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
- snat_idx = agent._get_snat_idx(ip_cidr)
- # 0x3D345705 is 30 bit xor folded crc32 of the ip_cidr
- self.assertEqual(0x3D345705, snat_idx)
-
- def test__get_snat_idx_ipv6_below_32768(self):
- ip_cidr = 'd488::/30'
- # crc32 of this ip_cidr is 0x1BD7
- agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
- snat_idx = agent._get_snat_idx(ip_cidr)
- # 0x1BD7 + 0x3FFFFFFF = 0x40001BD6
- self.assertEqual(0x40001BD6, snat_idx)
-
def test__map_internal_interfaces(self):
- agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data(num_internal_ports=4)
- ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs)
+ ri = dvr_router.DvrRouter(mock.sentinel.agent,
+ HOSTNAME,
+ router['id'],
+ router,
+ **self.ri_kwargs)
test_port = {
'mac_address': '00:12:23:34:45:56',
'fixed_ips': [{'subnet_id': _get_subnet_id(
'ip_address': '101.12.13.14'}]}
internal_ports = ri.router.get(l3_constants.INTERFACE_KEY, [])
# test valid case
- res_port = agent._map_internal_interfaces(ri,
- internal_ports[0],
- [test_port])
+ res_port = ri._map_internal_interfaces(internal_ports[0], [test_port])
self.assertEqual(test_port, res_port)
# test invalid case
test_port['fixed_ips'][0]['subnet_id'] = 1234
- res_ip = agent._map_internal_interfaces(ri,
- internal_ports[0],
- [test_port])
+ res_ip = ri._map_internal_interfaces(internal_ports[0], [test_port])
self.assertNotEqual(test_port, res_ip)
self.assertIsNone(res_ip)
agent, HOSTNAME, router['id'], router, **self.ri_kwargs)
self.assertEqual(
agent_gateway_port[0],
- agent.get_floating_agent_gw_interface(ri, fake_network_id))
+ ri.get_floating_agent_gw_interface(fake_network_id))
@mock.patch.object(lla.LinkLocalAllocator, '_write')
def test_create_dvr_fip_interfaces(self, lla_write):
with contextlib.nested(mock.patch.object(ri,
'get_floating_ips'),
mock.patch.object(
- agent, 'get_floating_agent_gw_interface'),
+ ri, 'get_floating_agent_gw_interface'),
mock.patch.object(
- agent, '_set_subnet_info')
+ ri, '_set_subnet_info')
) as (fips,
fip_gw_port,
sub_info):
ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs)
agent.external_gateway_added = mock.Mock()
with mock.patch.object(
- l3_agent.L3NATAgent,
+ ri,
'internal_network_added') as internal_network_added:
# raise RuntimeError to simulate that an unexpected exception
# occurs
agent.process_router(ri)
with mock.patch.object(
- l3_agent.L3NATAgent,
+ ri,
'internal_network_removed') as internal_net_removed:
# raise RuntimeError to simulate that an unexpected exception
# occurs
self.assertEqual(len(internal_ports), 1)
internal_port = internal_ports[0]
- with contextlib.nested(mock.patch.object(l3_agent.L3NATAgent,
+ with contextlib.nested(mock.patch.object(ri,
'internal_network_removed'),
- mock.patch.object(l3_agent.L3NATAgent,
+ mock.patch.object(ri,
'internal_network_added'),
mock.patch.object(l3_agent.L3NATAgent,
'external_gateway_removed'),
self.assertEqual(external_gateway_added.call_count, 1)
self.assertFalse(external_gateway_removed.called)
self.assertFalse(internal_network_removed.called)
- internal_network_added.assert_called_once_with(
- ri, internal_port)
+ internal_network_added.assert_called_once_with(internal_port)
self.assertEqual(self.mock_driver.unplug.call_count,
len(stale_devnames))
calls = [mock.call(stale_devname,
'mac_address': 'ca:fe:de:ad:be:ef',
'ip_cidr': '20.0.0.30/24'}
- interface_name = agent.get_snat_int_device_name(port_id)
+ interface_name = ri.get_snat_int_device_name(port_id)
self.device_exists.return_value = False
agent._create_dvr_gateway(ri, dvr_gw_port, interface_name,