import os
-import netaddr
from oslo_log import log as logging
from neutron.agent.l3 import link_local_allocator as lla
namespace=ns_name,
prefix=FIP_EXT_DEV_PREFIX)
- self.driver.init_l3(interface_name,
- [ex_gw_port['ip_cidr']],
- namespace=ns_name)
-
- ip_address = str(netaddr.IPNetwork(ex_gw_port['ip_cidr']).ip)
- ip_lib.send_gratuitous_arp(ns_name,
- interface_name,
- ip_address,
- self.agent_conf.send_arp_for_ha)
-
- gw_ip = ex_gw_port['subnet']['gateway_ip']
- if gw_ip:
- ipd = ip_lib.IPDevice(interface_name, namespace=ns_name)
- ipd.route.add_gateway(gw_ip)
+ ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
+ self.driver.init_l3(interface_name, ip_cidrs, namespace=ns_name)
+
+ for fixed_ip in ex_gw_port['fixed_ips']:
+ ip_lib.send_gratuitous_arp(ns_name,
+ interface_name,
+ fixed_ip['ip_address'],
+ self.agent_conf.send_arp_for_ha)
+
+ for subnet in ex_gw_port['subnets']:
+ gw_ip = subnet.get('gateway_ip')
+ if gw_ip:
+ ipd = ip_lib.IPDevice(interface_name,
+ namespace=ns_name)
+ ipd.route.add_gateway(gw_ip)
cmd = ['sysctl', '-w', 'net.ipv4.conf.%s.proxy_arp=1' % interface_name]
# TODO(Carl) mlavelle's work has self.ip_wrapper
with excutils.save_and_reraise_exception():
LOG.exception(_LE("DVR: Failed updating arp entry"))
- def _set_subnet_arp_info(self, port):
+ def _set_subnet_arp_info(self, subnet_id):
"""Set ARP info retrieved from Plugin for existing ports."""
- if 'id' not in port['subnet']:
- return
-
- subnet_id = port['subnet']['id']
-
# TODO(Carl) Can we eliminate the need to make this RPC while
# processing a router.
subnet_ports = self.agent.get_ports_by_subnet(subnet_id)
snat_idx = net.value
return snat_idx
- def _snat_redirect_add(self, gateway, sn_port, sn_int):
- """Adds rules and routes for SNAT redirection."""
+ def _snat_redirect_modify(self, gateway, sn_port, sn_int, is_add):
+ """Adds or 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_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])
+ if is_add:
+ ns_ipwrapr = ip_lib.IPWrapper(namespace=self.ns_name)
+ for port_fixed_ip in sn_port['fixed_ips']:
+ # Find the first gateway IP address matching this IP version
+ port_ip_addr = port_fixed_ip['ip_address']
+ port_ip_vers = netaddr.IPAddress(port_ip_addr).version
+ for gw_fixed_ip in gateway['fixed_ips']:
+ gw_ip_addr = gw_fixed_ip['ip_address']
+ if netaddr.IPAddress(gw_ip_addr).version == port_ip_vers:
+ sn_port_cidr = common_utils.ip_to_cidr(
+ port_ip_addr, port_fixed_ip['prefixlen'])
+ snat_idx = self._get_snat_idx(sn_port_cidr)
+ if is_add:
+ ns_ipd.route.add_gateway(gw_ip_addr,
+ table=snat_idx)
+ ns_ipr.rule.add(sn_port_cidr, snat_idx, snat_idx)
+ ns_ipwrapr.netns.execute(
+ ['sysctl', '-w',
+ 'net.ipv4.conf.%s.send_redirects=0' % sn_int])
+ else:
+ ns_ipd.route.delete_gateway(gw_ip_addr,
+ table=snat_idx)
+ ns_ipr.rule.delete(sn_port_cidr, snat_idx,
+ snat_idx)
+ break
except Exception:
- LOG.exception(_LE('DVR: error adding redirection logic'))
+ if is_add:
+ exc = _LE('DVR: error adding redirection logic')
+ else:
+ exc = _LE('DVR: removed snat failed')
+ LOG.exception(exc)
+
+ def _snat_redirect_add(self, gateway, sn_port, sn_int):
+ """Adds rules and routes for SNAT redirection."""
+ self._snat_redirect_modify(gateway, sn_port, sn_int, is_add=True)
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'))
+ self._snat_redirect_modify(gateway, sn_port, sn_int, is_add=False)
def get_gw_port_host(self):
host = self.router.get('gw_port_host')
return
interface_name = self.get_internal_device_name(port['id'])
- self._snat_redirect_add(sn_port['fixed_ips'][0]['ip_address'],
- port,
- interface_name)
+ self._snat_redirect_add(sn_port, port, interface_name)
if not self._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['fixed_ips'],
sn_port['mac_address'],
interface_name,
dvr_snat_ns.SNAT_INT_DEV_PREFIX)
- self._set_subnet_arp_info(port)
+ for subnet in port['subnets']:
+ self._set_subnet_arp_info(subnet['id'])
def _dvr_internal_network_removed(self, port):
if not self.ex_gw_port:
# 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)
+ self._snat_redirect_remove(sn_port, port, interface_name)
mode = self.agent_conf.agent_mode
is_this_snat_host = (mode == l3_constants.L3_AGENT_MODE_DVR_SNAT
# 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['id'], port['fixed_ips'],
port['mac_address'], interface_name,
dvr_snat_ns.SNAT_INT_DEV_PREFIX)
self._external_gateway_added(ex_gw_port, gw_interface_name,
gateway = self._map_internal_interfaces(p, snat_ports)
id_name = self.get_internal_device_name(p['id'])
if gateway:
- self._snat_redirect_add(
- gateway['fixed_ips'][0]['ip_address'], p, id_name)
+ self._snat_redirect_add(gateway, p, id_name)
if self._is_this_snat_host():
self._create_dvr_gateway(ex_gw_port, interface_name, snat_ports)
for p in self.internal_ports:
gateway = self._map_internal_interfaces(p, snat_ports)
internal_interface = self.get_internal_device_name(p['id'])
- self._snat_redirect_remove(gateway['fixed_ips'][0]['ip_address'],
- p,
- internal_interface)
+ self._snat_redirect_remove(gateway, p, internal_interface)
if not self._is_this_snat_host():
# no centralized SNAT gateway for this node/agent
if floating_ips:
is_first = self.fip_ns.subscribe(self.router_id)
if is_first and fip_agent_port:
- if 'subnet' not in fip_agent_port:
+ if 'subnets' not in fip_agent_port:
LOG.error(_LE('Missing subnet/agent_gateway_port'))
else:
- self._set_subnet_info(fip_agent_port)
self.fip_ns.create_gateway_port(fip_agent_port)
if self.fip_ns.agent_gateway_port and floating_ips:
self.router_id)
return
- self._set_subnet_info(ha_port)
self.ha_port = ha_port
self._init_keepalived_manager(process_monitor)
self.ha_network_added()
config = self.keepalived_manager.config
interface_name = self.get_ha_device_name()
- ha_port_cidr = self.ha_port['subnet']['cidr']
+ subnets = self.ha_port.get('subnets', [])
+ ha_port_cidrs = [subnet['cidr'] for subnet in subnets]
instance = keepalived.KeepalivedInstance(
'BACKUP',
interface_name,
self.ha_vr_id,
- ha_port_cidr,
+ ha_port_cidrs,
nopreempt=True,
advert_int=self.agent_conf.ha_vrrp_advert_int,
priority=self.ha_priority)
self.ha_port['mac_address'],
namespace=self.ns_name,
prefix=HA_DEV_PREFIX)
- self.driver.init_l3(interface_name,
- [self.ha_port['ip_cidr']],
+ ip_cidrs = common_utils.fixed_ip_cidrs(self.ha_port['fixed_ips'])
+ self.driver.init_l3(interface_name, ip_cidrs,
namespace=self.ns_name,
preserve_ips=[self._get_primary_vip()])
self.routes = new_routes
def _add_default_gw_virtual_route(self, ex_gw_port, interface_name):
- gw_ip = ex_gw_port['subnet']['gateway_ip']
- if gw_ip:
- # TODO(Carl) This is repeated everywhere. A method would be nice.
- default_gw = (n_consts.IPv4_ANY if
- netaddr.IPAddress(gw_ip).version == 4 else
- n_consts.IPv6_ANY)
- instance = self._get_keepalived_instance()
- instance.virtual_routes = (
- [route for route in instance.virtual_routes
- if route.destination != default_gw])
- instance.virtual_routes.append(
- keepalived.KeepalivedVirtualRoute(
- default_gw, gw_ip, interface_name))
+ subnets = ex_gw_port.get('subnets', [])
+ for subnet in subnets:
+ gw_ip = subnet['gateway_ip']
+ if gw_ip:
+ # TODO(Carl) This is repeated everywhere. A method would
+ # be nice.
+ default_gw = (n_consts.IPv4_ANY if
+ netaddr.IPAddress(gw_ip).version == 4 else
+ n_consts.IPv6_ANY)
+ instance = self._get_keepalived_instance()
+ instance.virtual_routes = (
+ [route for route in instance.virtual_routes
+ if route.destination != default_gw])
+ instance.virtual_routes.append(
+ keepalived.KeepalivedVirtualRoute(
+ default_gw, gw_ip, interface_name))
def _should_delete_ipv6_lladdr(self, ipv6_lladdr):
"""Only the master should have any IP addresses configured.
self._add_vip(ipv6_lladdr, interface_name, scope='link')
def _add_gateway_vip(self, ex_gw_port, interface_name):
- self._add_vip(ex_gw_port['ip_cidr'], interface_name)
+ for ip_cidr in common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips']):
+ self._add_vip(ip_cidr, interface_name)
self._add_default_gw_virtual_route(ex_gw_port, interface_name)
def add_floating_ip(self, fip, interface_name, device):
prefix=router.INTERNAL_DEV_PREFIX)
self._disable_ipv6_addressing_on_interface(interface_name)
- self._add_vip(port['ip_cidr'], interface_name)
+ for ip_cidr in common_utils.fixed_ip_cidrs(port['fixed_ips']):
+ self._add_vip(ip_cidr, interface_name)
def internal_network_removed(self, port):
super(HaRouter, self).internal_network_removed(port)
def external_gateway_updated(self, ex_gw_port, interface_name):
self._plug_external_gateway(ex_gw_port, interface_name, self.ns_name)
- old_gateway_cidr = self.ex_gw_port['ip_cidr']
- self._remove_vip(old_gateway_cidr)
+ ip_cidrs = common_utils.fixed_ip_cidrs(self.ex_gw_port['fixed_ips'])
+ for old_gateway_cidr in ip_cidrs:
+ self._remove_vip(old_gateway_cidr)
self._add_gateway_vip(ex_gw_port, interface_name)
def external_gateway_removed(self, ex_gw_port, interface_name):
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 _LE, _LW
+from neutron.i18n import _LW
LOG = logging.getLogger(__name__)
INTERNAL_DEV_PREFIX = namespaces.INTERNAL_DEV_PREFIX
def get_external_device_interface_name(self, ex_gw_port):
return self.get_external_device_name(ex_gw_port['id'])
- 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.router_namespace.delete()
def _internal_network_added(self, ns_name, network_id, port_id,
- internal_cidr, mac_address,
+ fixed_ips, mac_address,
interface_name, prefix):
if not ip_lib.device_exists(interface_name,
namespace=ns_name):
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)
+ ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips)
+ self.driver.init_l3(interface_name, ip_cidrs, namespace=ns_name)
+ for fixed_ip in fixed_ips:
+ ip_lib.send_gratuitous_arp(ns_name,
+ interface_name,
+ fixed_ip['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']
+ fixed_ips = port['fixed_ips']
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,
+ fixed_ips,
mac_address,
interface_name,
INTERNAL_DEV_PREFIX)
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
+ if not new_ipv6_port:
+ for subnet in p['subnets']:
+ if netaddr.IPNetwork(subnet['cidr']).version == 6:
+ new_ipv6_port = True
+ break
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
+ if not old_ipv6_port:
+ for subnet in p['subnets']:
+ if netaddr.IPNetwork(subnet['cidr']).version == 6:
+ old_ipv6_port = True
+ break
# Enable RA
if new_ipv6_port or old_ipv6_port:
ns_name, preserve_ips):
self._plug_external_gateway(ex_gw_port, interface_name, ns_name)
+ # Build up the interface and gateway IP addresses that
+ # will be added to the interface.
+ ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
+ gateway_ips = [subnet['gateway_ip']
+ for subnet in ex_gw_port['subnets']
+ if subnet['gateway_ip']]
self.driver.init_l3(interface_name,
- [ex_gw_port['ip_cidr']],
+ ip_cidrs,
namespace=ns_name,
- gateway=ex_gw_port['subnet'].get('gateway_ip'),
+ gateway_ips=gateway_ips,
extra_subnets=ex_gw_port.get('extra_subnets', []),
preserve_ips=preserve_ips)
- ip_address = ex_gw_port['ip_cidr'].split('/')[0]
- ip_lib.send_gratuitous_arp(ns_name,
- interface_name,
- ip_address,
- self.agent_conf.send_arp_for_ha)
+ for fixed_ip in ex_gw_port['fixed_ips']:
+ ip_lib.send_gratuitous_arp(ns_name,
+ interface_name,
+ fixed_ip['ip_address'],
+ self.agent_conf.send_arp_for_ha)
def external_gateway_added(self, ex_gw_port, interface_name):
preserve_ips = self._list_floating_ip_cidrs()
port2_filtered = _get_filtered_dict(port2, keys_to_ignore)
return port1_filtered == port2_filtered
- self._set_subnet_info(ex_gw_port)
if not self.ex_gw_port:
self.external_gateway_added(ex_gw_port, interface_name)
elif not _gateway_ports_equal(ex_gw_port, self.ex_gw_port):
self.conf = conf
def init_l3(self, device_name, ip_cidrs, namespace=None,
- preserve_ips=[], gateway=None, extra_subnets=[]):
+ preserve_ips=[], gateway_ips=None, extra_subnets=[]):
"""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
+ gateway_ips: For gateway ports, list of external gateway ip addresses
"""
device = ip_lib.IPDevice(device_name, namespace=namespace)
device.addr.delete(ip_cidr)
self.delete_conntrack_state(namespace=namespace, ip=ip_cidr)
- if gateway:
- device.route.add_gateway(gateway)
+ for gateway_ip in gateway_ips or []:
+ device.route.add_gateway(gateway_ip)
new_onlink_routes = set(s['cidr'] for s in extra_subnets)
existing_onlink_routes = set(
return bool(address)
-def device_exists_with_ip_mac(device_name, ip_cidr, mac, namespace=None):
- """Return True if the device with the given IP and MAC addresses
+def device_exists_with_ips_and_mac(device_name, ip_cidrs, mac, namespace=None):
+ """Return True if the device with the given IP addresses and MAC address
exists in the namespace.
"""
try:
device = IPDevice(device_name, namespace=namespace)
if mac != device.link.address:
return False
- if ip_cidr not in (ip['cidr'] for ip in device.addr.list()):
- return False
+ device_ip_cidrs = [ip['cidr'] for ip in device.addr.list()]
+ for ip_cidr in ip_cidrs:
+ if ip_cidr not in device_ip_cidrs:
+ return False
except RuntimeError:
return False
else:
class KeepalivedInstance(object):
"""Instance section of a keepalived configuration."""
- def __init__(self, state, interface, vrouter_id, ha_cidr,
+ def __init__(self, state, interface, vrouter_id, ha_cidrs,
priority=HA_DEFAULT_PRIORITY, advert_int=None,
mcast_src_ip=None, nopreempt=False):
self.name = 'VR_%s' % vrouter_id
metadata_cidr = '169.254.169.254/32'
self.primary_vip_range = get_free_range(
parent_range='169.254.0.0/16',
- excluded_ranges=[metadata_cidr,
- FIP_LL_SUBNET,
- ha_cidr],
+ excluded_ranges=[metadata_cidr, FIP_LL_SUBNET] + ha_cidrs,
size=PRIMARY_VIP_RANGE_SIZE)
def set_authentication(self, auth_type, password):
True)
buf = six.StringIO()
for p in router_ports:
- prefix = p['subnet']['cidr']
- if netaddr.IPNetwork(prefix).version == 6:
- interface_name = self._dev_name_helper(p['id'])
- ra_mode = p['subnet']['ipv6_ra_mode']
- buf.write('%s' % CONFIG_TEMPLATE.render(
- ra_mode=ra_mode,
- interface_name=interface_name,
- prefix=prefix,
- constants=constants))
+ subnets = p.get('subnets', [])
+ for subnet in subnets:
+ prefix = subnet['cidr']
+ if netaddr.IPNetwork(prefix).version == 6:
+ interface_name = self._dev_name_helper(p['id'])
+ ra_mode = subnet['ipv6_ra_mode']
+ buf.write('%s' % CONFIG_TEMPLATE.render(
+ ra_mode=ra_mode,
+ interface_name=interface_name,
+ prefix=prefix,
+ constants=constants))
utils.replace_file(radvd_conf, buf.getvalue())
return radvd_conf
def enable(self, router_ports):
for p in router_ports:
- if netaddr.IPNetwork(p['subnet']['cidr']).version == 6:
- break
- else:
- # Kill the daemon if it's running
- self.disable()
- return
-
- LOG.debug("Enable IPv6 RA for router %s", self._router_id)
- radvd_conf = self._generate_radvd_conf(router_ports)
- self._spawn_radvd(radvd_conf)
+ for subnet in p['subnets']:
+ if netaddr.IPNetwork(subnet['cidr']).version == 6:
+ LOG.debug("Enable IPv6 RA for router %s", self._router_id)
+ radvd_conf = self._generate_radvd_conf(router_ports)
+ self._spawn_radvd(radvd_conf)
+ return
+
+ # Kill the daemon if it's running
+ self.disable()
def disable(self):
self._process_monitor.unregister(uuid=self._router_id,
return str(net)
+def fixed_ip_cidrs(fixed_ips):
+ """Create a list of a port's fixed IPs in cidr notation.
+
+ :param fixed_ips: A neutron port's fixed_ips dictionary
+ """
+ return [ip_to_cidr(fixed_ip['ip_address'], fixed_ip.get('prefixlen'))
+ for fixed_ip in fixed_ips]
+
+
def is_cidr_host(cidr):
"""Determines if the cidr passed in represents a single host network
filters = {'id': gw_port_ids}
gw_ports = self._core_plugin.get_ports(context, filters)
if gw_ports:
- self._populate_subnet_for_ports(context, gw_ports)
+ self._populate_subnets_for_ports(context, gw_ports)
return gw_ports
def get_sync_interfaces(self, context, router_ids, device_owners=None):
ports = [rp.port.id for rp in qry]
interfaces = self._core_plugin.get_ports(context, {'id': ports})
if interfaces:
- self._populate_subnet_for_ports(context, interfaces)
+ self._populate_subnets_for_ports(context, interfaces)
return interfaces
- def _populate_subnet_for_ports(self, context, ports):
- """Populate ports with subnet.
+ def _populate_subnets_for_ports(self, context, ports):
+ """Populate ports with subnets.
These ports already have fixed_ips populated.
"""
if not ports:
return
- def each_port_with_ip():
+ def each_port_having_fixed_ips():
for port in ports:
fixed_ips = port.get('fixed_ips', [])
- if len(fixed_ips) > 1:
- LOG.info(_LI("Ignoring multiple IPs on router port %s"),
- port['id'])
- continue
- elif not fixed_ips:
+ if not fixed_ips:
# Skip ports without IPs, which can occur if a subnet
# attached to a router is deleted
LOG.info(_LI("Skipping port %s as no IP is configure on "
"it"),
port['id'])
continue
- yield (port, fixed_ips[0])
+ yield port
- network_ids = set(p['network_id'] for p, _ in each_port_with_ip())
+ network_ids = set(p['network_id']
+ for p in each_port_having_fixed_ips())
filters = {'network_id': [id for id in network_ids]}
fields = ['id', 'cidr', 'gateway_ip',
'network_id', 'ipv6_ra_mode']
for subnet in self._core_plugin.get_subnets(context, filters, fields):
subnets_by_network[subnet['network_id']].append(subnet)
- for port, fixed_ip in each_port_with_ip():
+ for port in each_port_having_fixed_ips():
+
+ port['subnets'] = []
port['extra_subnets'] = []
for subnet in subnets_by_network[port['network_id']]:
+ # If this subnet is used by the port (has a matching entry
+ # in the port's fixed_ips), then add this subnet to the
+ # port's subnets list, and populate the fixed_ips entry
+ # entry with the subnet's prefix length.
subnet_info = {'id': subnet['id'],
'cidr': subnet['cidr'],
'gateway_ip': subnet['gateway_ip'],
'ipv6_ra_mode': subnet['ipv6_ra_mode']}
-
- if subnet['id'] == fixed_ip['subnet_id']:
- port['subnet'] = subnet_info
+ for fixed_ip in port['fixed_ips']:
+ if fixed_ip['subnet_id'] == subnet['id']:
+ port['subnets'].append(subnet_info)
+ prefixlen = netaddr.IPNetwork(
+ subnet['cidr']).prefixlen
+ fixed_ip['prefixlen'] = prefixlen
+ break
else:
+ # This subnet is not used by the port.
port['extra_subnets'].append(subnet_info)
def _process_floating_ips(self, context, routers_dict, floating_ips):
interfaces = self._core_plugin.get_ports(context, {'id': ports})
LOG.debug("Return the SNAT ports: %s", interfaces)
if interfaces:
- self._populate_subnet_for_ports(context, interfaces)
+ self._populate_subnets_for_ports(context, interfaces)
return interfaces
def _build_routers_list(self, context, routers, gw_ports):
interfaces = self._core_plugin.get_ports(context.elevated(), filters)
LOG.debug("Return the FIP ports: %s ", interfaces)
if interfaces:
- self._populate_subnet_for_ports(context, interfaces)
+ self._populate_subnets_for_ports(context, interfaces)
return interfaces
def get_sync_data(self, context, router_ids=None, active=None):
'admin_state_up': True,
'name': ''}})
if agent_port:
- self._populate_subnet_for_ports(context, [agent_port])
+ self._populate_subnets_for_ports(context, [agent_port])
return agent_port
msg = _("Unable to create the Agent Gateway Port")
raise n_exc.BadRequest(resource='router', msg=msg)
else:
- self._populate_subnet_for_ports(context, [f_port])
+ self._populate_subnets_for_ports(context, [f_port])
return f_port
def get_snat_interface_ports_for_router(self, context, router_id):
context.session.add(router_port)
if do_pop:
- return self._populate_subnet_for_ports(context, [snat_port])
+ return self._populate_subnets_for_ports(context, [snat_port])
return snat_port
def create_snat_intf_ports_if_not_exists(self, context, router):
port_list = self.get_snat_interface_ports_for_router(
context, router.id)
if port_list:
- self._populate_subnet_for_ports(context, port_list)
+ self._populate_subnets_for_ports(context, port_list)
return port_list
port_list = []
intf['fixed_ips'][0]['subnet_id'], do_pop=False)
port_list.append(snat_port)
if port_list:
- self._populate_subnet_for_ports(context, port_list)
+ self._populate_subnets_for_ports(context, port_list)
return port_list
def dvr_vmarp_table_update(self, context, port_dict, action):
for router in routers_dict.values():
interface = router.get(constants.HA_INTERFACE_KEY)
if interface:
- self._populate_subnet_for_ports(context, [interface])
+ self._populate_subnets_for_ports(context, [interface])
return routers_dict.values()
from neutron.tests.functional.agent.linux import base
LOG = logging.getLogger(__name__)
-Device = collections.namedtuple('Device', 'name ip_cidr mac_address namespace')
+Device = collections.namedtuple('Device',
+ 'name ip_cidrs mac_address namespace')
class IpLibTestFramework(base.BaseLinuxTestCase):
self.driver = importutils.import_object(cfg.CONF.interface_driver,
cfg.CONF)
- def generate_device_details(self, name=None, ip_cidr=None,
+ def generate_device_details(self, name=None, ip_cidrs=None,
mac_address=None, namespace=None):
return Device(name or base.get_rand_name(),
- ip_cidr or '240.0.0.1/24',
+ ip_cidrs or ['240.0.0.1/24'],
mac_address or
utils.get_random_mac('fa:16:3e:00:00:00'.split(':')),
namespace or base.get_rand_name())
tap_device = ip.add_tuntap(attr.name)
self.addCleanup(self._safe_delete_device, tap_device)
tap_device.link.set_address(attr.mac_address)
- self.driver.init_l3(attr.name, [attr.ip_cidr],
+ self.driver.init_l3(attr.name, attr.ip_cidrs,
namespace=attr.namespace)
tap_device.link.set_up()
return tap_device
self.assertFalse(
ip_lib.device_exists(attr.name, namespace=attr.namespace))
- def test_device_exists_with_ip_mac(self):
+ def test_device_exists_with_ips_and_mac(self):
attr = self.generate_device_details()
device = self.manage_device(attr)
self.assertTrue(
- ip_lib.device_exists_with_ip_mac(*attr))
+ ip_lib.device_exists_with_ips_and_mac(*attr))
wrong_ip_cidr = '10.0.0.1/8'
wrong_mac_address = 'aa:aa:aa:aa:aa:aa'
attr = self.generate_device_details(name='wrong_name')
self.assertFalse(
- ip_lib.device_exists_with_ip_mac(*attr))
+ ip_lib.device_exists_with_ips_and_mac(*attr))
- attr = self.generate_device_details(ip_cidr=wrong_ip_cidr)
- self.assertFalse(ip_lib.device_exists_with_ip_mac(*attr))
+ attr = self.generate_device_details(ip_cidrs=[wrong_ip_cidr])
+ self.assertFalse(ip_lib.device_exists_with_ips_and_mac(*attr))
attr = self.generate_device_details(mac_address=wrong_mac_address)
- self.assertFalse(ip_lib.device_exists_with_ip_mac(*attr))
+ self.assertFalse(ip_lib.device_exists_with_ips_and_mac(*attr))
attr = self.generate_device_details(namespace='wrong_namespace')
- self.assertFalse(ip_lib.device_exists_with_ip_mac(*attr))
+ self.assertFalse(ip_lib.device_exists_with_ips_and_mac(*attr))
device.link.delete()
def test_get_routing_table(self):
attr = self.generate_device_details()
device = self.manage_device(attr)
- device_ip = attr.ip_cidr.split('/')[0]
+ device_ip = attr.ip_cidrs[0].split('/')[0]
destination = '8.8.8.0/24'
device.route.add_route(destination, device_ip)
{'nexthop': None,
'device': attr.name,
'destination': str(
- netaddr.IPNetwork(attr.ip_cidr).cidr)}]
+ netaddr.IPNetwork(attr.ip_cidrs[0]).cidr)}]
routes = ip_lib.get_routing_table(namespace=attr.namespace)
self.assertEqual(expected_routes, routes)
return agent
def generate_router_info(self, enable_ha, ip_version=4, extra_routes=True,
- enable_fip=True, enable_snat=True):
- if ip_version == 6:
+ enable_fip=True, enable_snat=True,
+ dual_stack=False):
+ if ip_version == 6 and not dual_stack:
enable_snat = False
enable_fip = False
extra_routes = False
enable_snat=enable_snat,
enable_floating_ip=enable_fip,
enable_ha=enable_ha,
- extra_routes=extra_routes)
+ extra_routes=extra_routes,
+ dual_stack=dual_stack)
def manage_router(self, agent, router):
self.addCleanup(self._delete_router, agent, router['id'])
router.ns_name)
return pm.active
- def device_exists_with_ip_mac(self, expected_device, name_getter,
- namespace):
- return ip_lib.device_exists_with_ip_mac(
- name_getter(expected_device['id']), expected_device['ip_cidr'],
+ def device_exists_with_ips_and_mac(self, expected_device, name_getter,
+ namespace):
+ ip_cidrs = common_utils.fixed_ip_cidrs(expected_device['fixed_ips'])
+ return ip_lib.device_exists_with_ips_and_mac(
+ name_getter(expected_device['id']), ip_cidrs,
expected_device['mac_address'], namespace)
+ @staticmethod
+ def _port_first_ip_cidr(port):
+ fixed_ip = port['fixed_ips'][0]
+ return common_utils.ip_to_cidr(fixed_ip['ip_address'],
+ fixed_ip['prefixlen'])
+
def get_device_mtu(self, target_device, name_getter, namespace):
device = ip_lib.IPDevice(name_getter(target_device), namespace)
return device.link.mtu
def get_expected_keepalive_configuration(self, router):
router_id = router.router_id
ha_device_name = router.get_ha_device_name()
- ha_device_cidr = router.ha_port['ip_cidr']
+ ha_device_cidr = self._port_first_ip_cidr(router.ha_port)
external_port = router.get_ex_gw_port()
ex_port_ipv6 = ip_lib.get_ipv6_lladdr(external_port['mac_address'])
external_device_name = router.get_external_device_name(
external_port['id'])
- external_device_cidr = external_port['ip_cidr']
+ external_device_cidr = self._port_first_ip_cidr(external_port)
internal_port = router.router[l3_constants.INTERFACE_KEY][0]
int_port_ipv6 = ip_lib.get_ipv6_lladdr(internal_port['mac_address'])
internal_device_name = router.get_internal_device_name(
internal_port['id'])
- internal_device_cidr = internal_port['ip_cidr']
+ internal_device_cidr = self._port_first_ip_cidr(internal_port)
floating_ip_cidr = common_utils.ip_to_cidr(
router.get_floating_ips()[0]['floating_ip_address'])
- default_gateway_ip = external_port['subnet'].get('gateway_ip')
+ default_gateway_ip = external_port['subnets'][0].get('gateway_ip')
return """vrrp_instance VR_1 {
state BACKUP
internal_devices = router.router[l3_constants.INTERFACE_KEY]
self.assertTrue(len(internal_devices))
for device in internal_devices:
- self.assertTrue(self.device_exists_with_ip_mac(
+ self.assertTrue(self.device_exists_with_ips_and_mac(
device, router.get_internal_device_name, router.ns_name))
def _assert_extra_routes(self, router):
def floating_ips_configured(self, router):
floating_ips = router.router[l3_constants.FLOATINGIP_KEY]
external_port = router.get_ex_gw_port()
- return len(floating_ips) and all(ip_lib.device_exists_with_ip_mac(
- router.get_external_device_name(external_port['id']),
- '%s/32' % fip['floating_ip_address'],
- external_port['mac_address'],
- namespace=router.ns_name) for fip in floating_ips)
+ return len(floating_ips) and all(
+ ip_lib.device_exists_with_ips_and_mac(
+ router.get_external_device_name(external_port['id']),
+ ['%s/32' % fip['floating_ip_address']],
+ external_port['mac_address'],
+ namespace=router.ns_name) for fip in floating_ips)
def fail_ha_router(self, router):
device_name = router.get_ha_device_name()
calls)
def test_legacy_router_lifecycle(self):
- self._router_lifecycle(enable_ha=False)
+ self._router_lifecycle(enable_ha=False, dual_stack=True)
def test_ha_router_lifecycle(self):
self._router_lifecycle(enable_ha=True)
existing_fip = '19.4.4.2'
new_fip = '19.4.4.3'
self._add_fip(router, new_fip)
- router.router['gw_port']['subnet']['gateway_ip'] = '19.4.4.5'
- router.router['gw_port']['fixed_ips'][0]['ip_address'] = '19.4.4.10'
+ subnet_id = _uuid()
+ fixed_ips = [{'ip_address': '19.4.4.10',
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}]
+ subnets = [{'id': subnet_id,
+ 'cidr': '19.4.4.0/24',
+ 'gateway_ip': '19.4.4.5'}]
+ router.router['gw_port']['subnets'] = subnets
+ router.router['gw_port']['fixed_ips'] = fixed_ips
self.agent.process_router(router)
(new_external_device_ip, external_device_name),
new_config)
- def _router_lifecycle(self, enable_ha, ip_version=4):
- router_info = self.generate_router_info(enable_ha, ip_version)
+ def _router_lifecycle(self, enable_ha, ip_version=4, dual_stack=False):
+ router_info = self.generate_router_info(enable_ha, ip_version,
+ dual_stack=dual_stack)
router = self.manage_router(self.agent, router_info)
if enable_ha:
# device has an IP address.
device = router.router[l3_constants.INTERFACE_KEY][-1]
device_exists = functools.partial(
- self.device_exists_with_ip_mac,
+ self.device_exists_with_ips_and_mac,
device,
router.get_internal_device_name,
router.ns_name)
lambda: self._metadata_proxy_exists(self.agent.conf, router))
self._assert_internal_devices(router)
self._assert_external_device(router)
- if ip_version == 4:
+ if not (enable_ha and (ip_version == 6 or dual_stack)):
# Note(SridharG): enable the assert_gateway for IPv6 once
# keepalived on Ubuntu14.04 (i.e., check-neutron-dsvm-functional
# platform) is updated to 1.2.10 (or above).
def _assert_external_device(self, router):
external_port = router.get_ex_gw_port()
- self.assertTrue(self.device_exists_with_ip_mac(
+ self.assertTrue(self.device_exists_with_ips_and_mac(
external_port, router.get_external_device_name,
router.ns_name))
external_port['id'])
external_device = ip_lib.IPDevice(external_device_name,
namespace=router.ns_name)
- existing_gateway = (
- external_device.route.get_gateway().get('gateway'))
- expected_gateway = external_port['subnet']['gateway_ip']
- self.assertEqual(expected_gateway, existing_gateway)
+ for subnet in external_port['subnets']:
+ expected_gateway = subnet['gateway_ip']
+ ip_vers = netaddr.IPAddress(expected_gateway).version
+ existing_gateway = (external_device.route.get_gateway(
+ ip_version=ip_vers).get('gateway'))
+ self.assertEqual(expected_gateway, existing_gateway)
def _assert_ha_device(self, router):
- device = router.router[l3_constants.HA_INTERFACE_KEY]
- self.assertTrue(ip_lib.device_exists_with_ip_mac(
- router.get_ha_device_name(), device['ip_cidr'],
- device['mac_address'], router.ns_name))
+ def ha_router_dev_name_getter(not_used):
+ return router.get_ha_device_name()
+ self.assertTrue(self.device_exists_with_ips_and_mac(
+ router.router[l3_constants.HA_INTERFACE_KEY],
+ ha_router_dev_name_getter, router.ns_name))
@classmethod
def _get_addresses_on_device(cls, namespace, interface):
# Create and configure client namespace
client_ns = self._create_namespace()
- router_ip_cidr = router.internal_ports[0]['ip_cidr']
+ router_ip_cidr = self._port_first_ip_cidr(router.internal_ports[0])
ip_cidr = net_helpers.increment_ip_cidr(router_ip_cidr)
br_int = get_ovs_bridge(self.agent.conf.ovs_integration_bridge)
port = self.bind_namespace_to_cidr(client_ns, br_int, ip_cidr)
if not fip_gw_port_list and external_gw_port:
# Get values from external gateway port
fixed_ip = external_gw_port['fixed_ips'][0]
- float_subnet = external_gw_port['subnet']
+ float_subnet = external_gw_port['subnets'][0]
port_ip = fixed_ip['ip_address']
# Pick an ip address which is not the same as port_ip
fip_gw_port_ip = str(netaddr.IPAddress(port_ip) + 5)
# Add floatingip agent gateway port info to router
+ prefixlen = netaddr.IPNetwork(float_subnet['cidr']).prefixlen
router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = [
- {'subnet':
+ {'subnets': [
{'cidr': float_subnet['cidr'],
- 'gateway_ip': float_subnet['gateway_ip'],
- 'id': fixed_ip['subnet_id']},
- 'network_id': external_gw_port['network_id'],
- 'device_owner': 'network:floatingip_agent_gateway',
- 'mac_address': 'fa:16:3e:80:8d:89',
- 'binding:host_id': self.agent.conf.host,
- 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'],
- 'ip_address': fip_gw_port_ip}],
- 'id': _uuid(),
- 'device_id': _uuid()}
+ 'gateway_ip': float_subnet['gateway_ip'],
+ 'id': fixed_ip['subnet_id']}],
+ 'network_id': external_gw_port['network_id'],
+ 'device_owner': 'network:floatingip_agent_gateway',
+ 'mac_address': 'fa:16:3e:80:8d:89',
+ 'binding:host_id': self.agent.conf.host,
+ 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'],
+ 'ip_address': fip_gw_port_ip,
+ 'prefixlen': prefixlen}],
+ 'id': _uuid(),
+ 'device_id': _uuid()}
]
def _add_snat_port_info_to_router(self, router, internal_ports):
# Get values from internal port
port = internal_ports[0]
fixed_ip = port['fixed_ips'][0]
- snat_subnet = port['subnet']
+ snat_subnet = port['subnets'][0]
port_ip = fixed_ip['ip_address']
# Pick an ip address which is not the same as port_ip
snat_ip = str(netaddr.IPAddress(port_ip) + 5)
# Add the info to router as the first snat port
# in the list of snat ports
+ prefixlen = netaddr.IPNetwork(snat_subnet['cidr']).prefixlen
router[l3_constants.SNAT_ROUTER_INTF_KEY] = [
- {'subnet':
+ {'subnets': [
{'cidr': snat_subnet['cidr'],
- 'gateway_ip': snat_subnet['gateway_ip'],
- 'id': fixed_ip['subnet_id']},
- 'network_id': port['network_id'],
- 'device_owner': 'network:router_centralized_snat',
- 'mac_address': 'fa:16:3e:80:8d:89',
- 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'],
- 'ip_address': snat_ip}],
- 'id': _uuid(),
- 'device_id': _uuid()}
+ 'gateway_ip': snat_subnet['gateway_ip'],
+ 'id': fixed_ip['subnet_id']}],
+ 'network_id': port['network_id'],
+ 'device_owner': 'network:router_centralized_snat',
+ 'mac_address': 'fa:16:3e:80:8d:89',
+ 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'],
+ 'ip_address': snat_ip,
+ 'prefixlen': prefixlen}],
+ 'id': _uuid(),
+ 'device_id': _uuid()}
]
def _assert_dvr_external_device(self, router):
# that the correct ports and ip addresses exist in the
# snat_ns_name namespace
if self.agent.conf.agent_mode == 'dvr_snat':
- self.assertTrue(self.device_exists_with_ip_mac(
+ self.assertTrue(self.device_exists_with_ips_and_mac(
external_port, router.get_external_device_name,
snat_ns_name))
# if the agent is in dvr mode then the snat_ns_name namespace
namespace=namespace)
existing_gateway = (
external_device.route.get_gateway().get('gateway'))
- expected_gateway = external_port['subnet']['gateway_ip']
+ expected_gateway = external_port['subnets'][0]['gateway_ip']
self.assertEqual(expected_gateway, existing_gateway)
def _assert_snat_namespace_does_not_exist(self, router):
external_gw_port = floating_agent_gw_port[0]
fip_ns = self.agent.get_fip_ns(floating_ips[0]['floating_network_id'])
fip_ns_name = fip_ns.get_name()
- fg_port_created_successfully = ip_lib.device_exists_with_ip_mac(
+ fg_port_created_successfully = ip_lib.device_exists_with_ips_and_mac(
fip_ns.get_ext_device_name(external_gw_port['id']),
- external_gw_port['ip_cidr'],
+ [self._port_first_ip_cidr(external_gw_port)],
external_gw_port['mac_address'],
namespace=fip_ns_name)
self.assertTrue(fg_port_created_successfully)
router = mock.MagicMock()
ri = self._create_router(router)
ext_net_id = _uuid()
+ subnet_id = _uuid()
agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}],
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}],
'id': _uuid(),
'network_id': ext_net_id,
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
fip = {'id': _uuid(),
'host': HOSTNAME,
router = mock.MagicMock()
ri = self._create_router(router)
+ subnet_id = _uuid()
agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}],
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}],
'id': _uuid(),
'network_id': _uuid(),
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
fip_cidr = '11.22.33.44/24'
ri.dist_fip_count = 2
config = keepalived.KeepalivedConf()
instance1 = keepalived.KeepalivedInstance('MASTER', 'eth0', 1,
- '169.254.192.0/18',
+ ['169.254.192.0/18'],
advert_int=5)
instance1.set_authentication('AH', 'pass123')
instance1.track_interfaces.append("eth0")
instance1.virtual_routes.append(virtual_route)
instance2 = keepalived.KeepalivedInstance('MASTER', 'eth4', 2,
- '169.254.192.0/18',
+ ['169.254.192.0/18'],
mcast_src_ip='224.0.0.1')
instance2.track_interfaces.append("eth4")
invalid_vrrp_state = 'a seal walks'
self.assertRaises(keepalived.InvalidInstanceStateException,
keepalived.KeepalivedInstance,
- invalid_vrrp_state, 'eth0', 33, '169.254.192.0/18')
+ invalid_vrrp_state, 'eth0', 33,
+ ['169.254.192.0/18'])
invalid_auth_type = 'into a club'
instance = keepalived.KeepalivedInstance('MASTER', 'eth0', 1,
- '169.254.192.0/18')
+ ['169.254.192.0/18'])
self.assertRaises(keepalived.InvalidAuthenticationTypeException,
instance.set_authentication,
invalid_auth_type, 'some_password')
KeepalivedConfBaseMixin):
def test_get_primary_vip(self):
instance = keepalived.KeepalivedInstance('MASTER', 'ha0', 42,
- '169.254.192.0/18')
+ ['169.254.192.0/18'])
self.assertEqual('169.254.0.42/24', instance.get_primary_vip())
def test_remove_adresses_by_interface(self):
}
}"""
instance = keepalived.KeepalivedInstance(
- 'MASTER', 'eth0', 1, '169.254.192.0/18')
+ 'MASTER', 'eth0', 1, ['169.254.192.0/18'])
self.assertEqual(expected, '\n'.join(instance.build_config()))
@mock.patch.object(ip_lib, 'send_gratuitous_arp')
@mock.patch.object(ip_lib, 'device_exists')
def test_gateway_added(self, device_exists, send_arp, IPDevice, IPWrapper):
+ subnet_id = _uuid()
agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}],
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}],
'id': _uuid(),
'network_id': self.net_id,
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
device_exists.return_value = False
self.fip_ns._gateway_added(agent_gw_port,
self.assertEqual(constants.DEVICE_OWNER_ROUTER_HA_INTF,
interface['device_owner'])
- self.assertEqual(cfg.CONF.l3_ha_net_cidr, interface['subnet']['cidr'])
+
+ subnets = interface['subnets']
+ self.assertEqual(1, len(subnets))
+ self.assertEqual(cfg.CONF.l3_ha_net_cidr, subnets[0]['cidr'])
def test_unique_ha_network_per_tenant(self):
tenant1 = _uuid()
def router_append_interface(router, count=1, ip_version=4, ra_mode=None,
- addr_mode=None):
- if ip_version == 4:
- ip_pool = '35.4.%i.4'
- cidr_pool = '35.4.%i.0/24'
- gw_pool = '35.4.%i.1'
- elif ip_version == 6:
- ip_pool = 'fd01:%x:1::6'
- cidr_pool = 'fd01:%x:1::/64'
- gw_pool = 'fd01:%x:1::1'
- else:
- raise ValueError("Invalid ip_version: %s" % ip_version)
-
+ addr_mode=None, dual_stack=False):
interfaces = router[l3_constants.INTERFACE_KEY]
current = sum(
- [netaddr.IPNetwork(p['subnet']['cidr']).version == ip_version
- for p in interfaces])
+ [netaddr.IPNetwork(subnet['cidr']).version == ip_version
+ for p in interfaces for subnet in p['subnets']])
mac_address = netaddr.EUI('ca:fe:de:ad:be:ef')
mac_address.dialect = netaddr.mac_unix
for i in range(current, current + count):
+ fixed_ips = []
+ subnets = []
+ for loop_version in (4, 6):
+ if loop_version == 4 and (ip_version == 4 or dual_stack):
+ ip_pool = '35.4.%i.4'
+ cidr_pool = '35.4.%i.0/24'
+ prefixlen = 24
+ gw_pool = '35.4.%i.1'
+ elif loop_version == 6 and (ip_version == 6 or dual_stack):
+ ip_pool = 'fd01:%x:1::6'
+ cidr_pool = 'fd01:%x:1::/64'
+ prefixlen = 64
+ gw_pool = 'fd01:%x:1::1'
+ else:
+ continue
+ subnet_id = _uuid()
+ fixed_ips.append({'ip_address': ip_pool % i,
+ 'subnet_id': subnet_id,
+ 'prefixlen': prefixlen})
+ subnets.append({'id': subnet_id,
+ 'cidr': cidr_pool % i,
+ 'gateway_ip': gw_pool % i,
+ 'ipv6_ra_mode': ra_mode,
+ 'ipv6_address_mode': addr_mode})
+ if not fixed_ips:
+ raise ValueError("Invalid ip_version: %s" % ip_version)
+
interfaces.append(
{'id': _uuid(),
'network_id': _uuid(),
'admin_state_up': True,
- 'fixed_ips': [{'ip_address': ip_pool % i,
- 'subnet_id': _uuid()}],
+ 'fixed_ips': fixed_ips,
'mac_address': str(mac_address),
- 'subnet': {'cidr': cidr_pool % i,
- 'gateway_ip': gw_pool % i,
- 'ipv6_ra_mode': ra_mode,
- 'ipv6_address_mode': addr_mode}})
+ 'subnets': subnets})
mac_address.value += 1
def prepare_router_data(ip_version=4, enable_snat=None, num_internal_ports=1,
enable_floating_ip=False, enable_ha=False,
- extra_routes=False):
- if ip_version == 4:
- ip_addr = '19.4.4.4'
- cidr = '19.4.4.0/24'
- gateway_ip = '19.4.4.1'
- elif ip_version == 6:
- ip_addr = 'fd00::4'
- cidr = 'fd00::/64'
- gateway_ip = 'fd00::1'
- else:
+ extra_routes=False, dual_stack=False):
+ fixed_ips = []
+ subnets = []
+ for loop_version in (4, 6):
+ if loop_version == 4 and (ip_version == 4 or dual_stack):
+ ip_address = '19.4.4.4'
+ prefixlen = 24
+ subnet_cidr = '19.4.4.0/24'
+ gateway_ip = '19.4.4.1'
+ elif loop_version == 6 and (ip_version == 6 or dual_stack):
+ ip_address = 'fd00::4'
+ prefixlen = 64
+ subnet_cidr = 'fd00::/64'
+ gateway_ip = 'fd00::1'
+ else:
+ continue
+ subnet_id = _uuid()
+ fixed_ips.append({'ip_address': ip_address,
+ 'subnet_id': subnet_id,
+ 'prefixlen': prefixlen})
+ subnets.append({'id': subnet_id,
+ 'cidr': subnet_cidr,
+ 'gateway_ip': gateway_ip})
+ if not fixed_ips:
raise ValueError("Invalid ip_version: %s" % ip_version)
router_id = _uuid()
ex_gw_port = {'id': _uuid(),
'mac_address': 'ca:fe:de:ad:be:ee',
'network_id': _uuid(),
- 'fixed_ips': [{'ip_address': ip_addr,
- 'subnet_id': _uuid()}],
- 'subnet': {'cidr': cidr,
- 'gateway_ip': gateway_ip}}
+ 'fixed_ips': fixed_ips,
+ 'subnets': subnets}
routes = []
if extra_routes:
- routes = [{'destination': '8.8.8.0/24', 'nexthop': ip_addr}]
+ routes = [{'destination': '8.8.8.0/24', 'nexthop': '19.4.4.4'}]
router = {
'id': router_id,
'fixed_ip_address': '10.0.0.1'}]
router_append_interface(router, count=num_internal_ports,
- ip_version=ip_version)
+ ip_version=ip_version, dual_stack=dual_stack)
if enable_ha:
router['ha'] = True
router['ha_vr_id'] = 1
# and the functional tests, and should be moved elsewhere (probably
# neutron/tests/common/).
def get_ha_interface(ip='169.254.192.1', mac='12:34:56:78:2b:5d'):
+ subnet_id = _uuid()
return {'admin_state_up': True,
'device_id': _uuid(),
'device_owner': 'network:router_ha_interface',
'fixed_ips': [{'ip_address': ip,
- 'subnet_id': _uuid()}],
+ 'prefixlen': 18,
+ 'subnet_id': subnet_id}],
'id': _uuid(),
'mac_address': mac,
'name': u'L3 HA Admin port 0',
'network_id': _uuid(),
'status': u'ACTIVE',
- 'subnet': {'cidr': '169.254.192.0/18',
- 'gateway_ip': '169.254.255.254',
- 'id': _uuid()},
+ 'subnets': [{'cidr': '169.254.192.0/18',
+ 'gateway_ip': '169.254.255.254',
+ 'id': subnet_id}],
'tenant_id': '',
'agent_id': _uuid(),
'agent_host': 'aaa',
'neutron.openstack.common.loopingcall.FixedIntervalLoopingCall')
self.looping_call_p.start()
- self.snat_ports = [{'subnet': {'cidr': '152.2.0.0/16',
- 'gateway_ip': '152.2.0.1',
- 'id': _uuid()},
+ subnet_id_1 = _uuid()
+ subnet_id_2 = _uuid()
+ self.snat_ports = [{'subnets': [{'cidr': '152.2.0.0/16',
+ 'gateway_ip': '152.2.0.1',
+ 'id': subnet_id_1}],
'network_id': _uuid(),
'device_owner': 'network:router_centralized_snat',
- 'ip_cidr': '152.2.0.13/16',
'mac_address': 'fa:16:3e:80:8d:80',
- 'fixed_ips': [{'subnet_id': _uuid(),
- 'ip_address': '152.2.0.13'}],
+ 'fixed_ips': [{'subnet_id': subnet_id_1,
+ 'ip_address': '152.2.0.13',
+ 'prefixlen': 16}],
'id': _uuid(), 'device_id': _uuid()},
- {'subnet': {'cidr': '152.10.0.0/16',
- 'gateway_ip': '152.10.0.1',
- 'id': _uuid()},
+ {'subnets': [{'cidr': '152.10.0.0/16',
+ 'gateway_ip': '152.10.0.1',
+ 'id': subnet_id_2}],
'network_id': _uuid(),
'device_owner': 'network:router_centralized_snat',
- 'ip_cidr': '152.10.0.13/16',
'mac_address': 'fa:16:3e:80:8d:80',
- 'fixed_ips': [{'subnet_id': _uuid(),
- 'ip_address': '152.10.0.13'}],
+ 'fixed_ips': [{'subnet_id': subnet_id_2,
+ 'ip_address': '152.10.0.13',
+ 'prefixlen': 16}],
'id': _uuid(), 'device_id': _uuid()}]
self.ri_kwargs = {'agent_conf': self.conf,
self.assertTrue(ri.ns_name.endswith(id))
def test_router_info_create_with_router(self):
- id = _uuid()
+ ns_id = _uuid()
+ subnet_id = _uuid()
ex_gw_port = {'id': _uuid(),
'network_id': _uuid(),
'fixed_ips': [{'ip_address': '19.4.4.4',
- 'subnet_id': _uuid()}],
- 'subnet': {'cidr': '19.4.4.0/24',
- 'gateway_ip': '19.4.4.1'}}
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}],
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '19.4.4.0/24',
+ 'gateway_ip': '19.4.4.1'}]}
router = {
'id': _uuid(),
'enable_snat': True,
'routes': [],
'gw_port': ex_gw_port}
- ri = l3router.RouterInfo(id, router, **self.ri_kwargs)
- self.assertTrue(ri.ns_name.endswith(id))
+ ri = l3router.RouterInfo(ns_id, router, **self.ri_kwargs)
+ self.assertTrue(ri.ns_name.endswith(ns_id))
self.assertEqual(ri.router, router)
def test_agent_create(self):
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'}
+ 'mac_address': 'ca:fe:de:ad:be:ef',
+ 'fixed_ips': [{'subnet_id': _uuid(),
+ 'ip_address': '99.0.1.9',
+ 'prefixlen': 24}]}
interface_name = ri.get_internal_device_name(port['id'])
else:
raise Exception("Invalid action %s" % action)
+ @staticmethod
+ def _fixed_ip_cidr(fixed_ip):
+ return '%s/%s' % (fixed_ip['ip_address'], fixed_ip['prefixlen'])
+
def _test_internal_network_action_dist(self, action):
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)
+ subnet_id = _uuid()
port = {'network_id': _uuid(),
'id': _uuid(),
- 'ip_cidr': '99.0.1.9/24',
- 'mac_address': 'ca:fe:de:ad:be:ef'}
+ 'mac_address': 'ca:fe:de:ad:be:ef',
+ 'fixed_ips': [{'subnet_id': subnet_id,
+ 'ip_address': '99.0.1.9',
+ 'prefixlen': 24}],
+ 'subnets': [{'id': subnet_id}]}
ri.router['gw_port_host'] = HOSTNAME
agent.host = HOSTNAME
agent.conf.agent_mode = 'dvr_snat'
sn_port = {'fixed_ips': [{'ip_address': '20.0.0.31',
'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'subnets': [{'gateway_ip': '20.0.0.1'}],
'extra_subnets': [{'cidr': '172.16.0.0/24'}],
'id': _uuid(),
'network_id': _uuid(),
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.31/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
+ 'prefixlen': 24,
'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'subnets': [{'gateway_ip': '20.0.0.1'}],
'extra_subnets': [{'cidr': '172.16.0.0/24'}],
'id': _uuid(),
'binding:host_id': HOSTNAME,
'network_id': _uuid(),
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
ri.snat_ports = sn_port
ri.ex_gw_port = ex_gw_port
ri.snat_namespace = mock.Mock()
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._set_subnet_arp_info = 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._set_subnet_arp_info.assert_called_once_with(subnet_id)
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['fixed_ips'],
sn_port['mac_address'],
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
ri._map_internal_interfaces = mock.Mock(return_value=sn_port)
- ri._snat_redirect_remove = mock.Mock()
+ ri._snat_redirect_modify = 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']))
+ ri._snat_redirect_modify.assert_called_with(
+ sn_port, port,
+ ri.get_internal_device_name(port['id']),
+ is_add=False)
def test_agent_add_internal_network(self):
self._test_internal_network_action('add')
def test_agent_remove_internal_network_dist(self):
self._test_internal_network_action_dist('remove')
- def _test_external_gateway_action(self, action, router):
+ def _test_external_gateway_action(self, action, router, dual_stack=False):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ex_net_id = _uuid()
sn_port = self.snat_ports[1]
router['id'], router,
**self.ri_kwargs)
- ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ subnet_id = _uuid()
+ fixed_ips = [{'subnet_id': subnet_id,
+ 'ip_address': '20.0.0.30',
+ 'prefixlen': 24}]
+ subnets = [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}]
+ if dual_stack:
+ subnet_id_v6 = _uuid()
+ fixed_ips.append({'subnet_id': subnet_id_v6,
+ 'ip_address': '2001:192:168:100::2',
+ 'prefixlen': 64})
+ subnets.append({'id': subnet_id_v6,
+ 'cidr': '2001:192:168:100::/64',
+ 'gateway_ip': '2001:192:168:100::1'})
+ ex_gw_port = {'fixed_ips': fixed_ips,
+ 'subnets': subnets,
'extra_subnets': [{'cidr': '172.16.0.0/24'}],
'id': _uuid(),
'network_id': ex_net_id,
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
interface_name = ri.get_external_device_name(ex_gw_port['id'])
if action == 'add':
if not router.get('distributed'):
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,
- '20.0.0.30', mock.ANY)
+ exp_arp_calls = [mock.call(ri.ns_name, interface_name,
+ '20.0.0.30', mock.ANY)]
+ if dual_stack:
+ exp_arp_calls += [mock.call(ri.ns_name, interface_name,
+ '2001:192:168:100::2',
+ mock.ANY)]
+ self.send_arp.assert_has_calls(exp_arp_calls)
+ ip_cidrs = ['20.0.0.30/24']
+ gateway_ips = ['20.0.0.1']
+ if dual_stack:
+ ip_cidrs.append('2001:192:168:100::2/64')
+ gateway_ips.append('2001:192:168:100::1')
kwargs = {'preserve_ips': ['192.168.1.34/32'],
+ 'gateway_ips': gateway_ips,
'namespace': 'qrouter-' + router['id'],
- 'gateway': '20.0.0.1',
'extra_subnets': [{'cidr': '172.16.0.0/24'}]}
self.mock_driver.init_l3.assert_called_with(interface_name,
- ['20.0.0.30/24'],
+ ip_cidrs,
**kwargs)
else:
ri._create_dvr_gateway.assert_called_once_with(
prefix=mock.ANY)
else:
ri._snat_redirect_remove.assert_called_with(
- sn_port['fixed_ips'][0]['ip_address'],
- sn_port,
+ sn_port, sn_port,
ri.get_internal_device_name(sn_port['id']))
else:
raise Exception("Invalid action %s" % action)
- def _prepare_ext_gw_test(self, ri):
- ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ def _prepare_ext_gw_test(self, ri, dual_stack=False):
+ subnet_id = _uuid()
+ fixed_ips = [{'subnet_id': subnet_id,
+ 'ip_address': '20.0.0.30',
+ 'prefixlen': 24}]
+ subnets = [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}]
+ if dual_stack:
+ subnet_id_v6 = _uuid()
+ fixed_ips.append({'subnet_id': subnet_id_v6,
+ 'ip_address': '2001:192:168:100::2',
+ 'prefixlen': 64})
+ subnets.append({'id': subnet_id_v6,
+ 'cidr': '2001:192:168:100::/64',
+ 'gateway_ip': '2001:192:168:100::1'})
+ ex_gw_port = {'fixed_ips': fixed_ips,
+ 'subnets': subnets,
'extra_subnets': [{'cidr': '172.16.0.0/24'}],
'id': _uuid(),
'network_id': _uuid(),
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
interface_name = ri.get_external_device_name(ex_gw_port['id'])
self.device_exists.return_value = True
return interface_name, ex_gw_port
- def test_external_gateway_updated(self):
+ def _test_external_gateway_updated(self, dual_stack=False):
router = prepare_router_data(num_internal_ports=2)
ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs)
- interface_name, ex_gw_port = self._prepare_ext_gw_test(ri)
+ interface_name, ex_gw_port = self._prepare_ext_gw_test(
+ ri, dual_stack=dual_stack)
fake_fip = {'floatingips': [{'id': _uuid(),
'floating_ip_address': '192.168.1.34',
ri.external_gateway_updated(ex_gw_port, interface_name)
self.assertEqual(self.mock_driver.plug.call_count, 0)
self.assertEqual(self.mock_driver.init_l3.call_count, 1)
- self.send_arp.assert_called_once_with(ri.ns_name, interface_name,
- '20.0.0.30', mock.ANY)
+ exp_arp_calls = [mock.call(ri.ns_name, interface_name,
+ '20.0.0.30', mock.ANY)]
+ if dual_stack:
+ exp_arp_calls += [mock.call(ri.ns_name, interface_name,
+ '2001:192:168:100::2', mock.ANY)]
+ self.send_arp.assert_has_calls(exp_arp_calls)
+ ip_cidrs = ['20.0.0.30/24']
+ gateway_ips = ['20.0.0.1']
+ if dual_stack:
+ ip_cidrs.append('2001:192:168:100::2/64')
+ gateway_ips.append('2001:192:168:100::1')
kwargs = {'preserve_ips': ['192.168.1.34/32'],
+ 'gateway_ips': gateway_ips,
'namespace': 'qrouter-' + router['id'],
- 'gateway': '20.0.0.1',
'extra_subnets': [{'cidr': '172.16.0.0/24'}]}
self.mock_driver.init_l3.assert_called_with(interface_name,
- ['20.0.0.30/24'],
+ ip_cidrs,
**kwargs)
+ def test_external_gateway_updated(self):
+ self._test_external_gateway_updated()
+
+ def test_external_gateway_updated_dual_stack(self):
+ self._test_external_gateway_updated(dual_stack=True)
+
def _test_ext_gw_updated_dvr_agent_mode(self, host,
agent_mode, expected_call_count):
router = prepare_router_data(num_internal_ports=2)
router = prepare_router_data(num_internal_ports=2)
self._test_external_gateway_action('add', router)
+ def test_agent_add_external_gateway_dual_stack(self):
+ router = prepare_router_data(num_internal_ports=2)
+ self._test_external_gateway_action('add', router, dual_stack=True)
+
def test_agent_add_external_gateway_dist(self):
router = prepare_router_data(num_internal_ports=2)
router['distributed'] = True
router['gw_port_host'] = HOSTNAME
self._test_external_gateway_action('add', router)
+ def test_agent_add_external_gateway_dist_dual_stack(self):
+ router = prepare_router_data(num_internal_ports=2)
+ router['distributed'] = True
+ router['gw_port_host'] = HOSTNAME
+ self._test_external_gateway_action('add', router, dual_stack=True)
+
def test_agent_remove_external_gateway(self):
router = prepare_router_data(num_internal_ports=2)
self._test_external_gateway_action('remove', router)
+ def test_agent_remove_external_gateway_dual_stack(self):
+ router = prepare_router_data(num_internal_ports=2)
+ self._test_external_gateway_action('remove', router, dual_stack=True)
+
def test_agent_remove_external_gateway_dist(self):
router = prepare_router_data(num_internal_ports=2)
router['distributed'] = True
router['gw_port_host'] = HOSTNAME
self._test_external_gateway_action('remove', router)
+ def test_agent_remove_external_gateway_dist_dual_stack(self):
+ router = prepare_router_data(num_internal_ports=2)
+ router['distributed'] = True
+ router['gw_port_host'] = HOSTNAME
+ self._test_external_gateway_action('remove', router, dual_stack=True)
+
def _verify_snat_rules(self, rules, router, negate=False):
interfaces = router[l3_constants.INTERFACE_KEY]
source_cidrs = []
for iface in interfaces:
- prefix = iface['subnet']['cidr'].split('/')[1]
- source_cidr = "%s/%s" % (iface['fixed_ips'][0]['ip_address'],
- prefix)
- source_cidrs.append(source_cidr)
+ for subnet in iface['subnets']:
+ prefix = subnet['cidr'].split('/')[1]
+ source_cidr = "%s/%s" % (iface['fixed_ips'][0]['ip_address'],
+ prefix)
+ source_cidrs.append(source_cidr)
source_nat_ip = router['gw_port']['fixed_ips'][0]['ip_address']
interface_name = ('qg-%s' % router['gw_port']['id'])[:14]
expected_rules = [
ri = dvr_router.DvrRouter(
agent, HOSTNAME, router['id'], router, **self.ri_kwargs)
ports = ri.router.get(l3_constants.INTERFACE_KEY, [])
+ subnet_id = _get_subnet_id(ports[0])
test_ports = [{'mac_address': '00:11:22:33:44:55',
'device_owner': 'network:dhcp',
- 'subnet_id': _get_subnet_id(ports[0]),
- 'fixed_ips': [{'ip_address': '1.2.3.4'}]}]
+ 'fixed_ips': [{'ip_address': '1.2.3.4',
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}]}]
self.plugin_api.get_ports_by_subnet.return_value = test_ports
# Test basic case
- ports[0]['subnet']['id'] = _get_subnet_id(ports[0])
- ri._set_subnet_arp_info(ports[0])
+ ports[0]['subnets'] = [{'id': subnet_id,
+ 'cidr': '1.2.3.0/24'}]
+ ri._set_subnet_arp_info(subnet_id)
self.mock_ip_dev.neigh.add.assert_called_once_with(
'1.2.3.4', '00:11:22:33:44:55')
# Test negative case
router['distributed'] = False
- ri._set_subnet_arp_info(ports[0])
+ ri._set_subnet_arp_info(subnet_id)
self.mock_ip_dev.neigh.add.never_called()
def test_add_arp_entry(self):
def test_get_floating_agent_gw_interfaces(self):
fake_network_id = _uuid()
+ subnet_id = _uuid()
agent_gateway_port = (
[{'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
- 'id': _uuid(),
- 'binding:host_id': 'myhost',
- 'device_owner': 'network:floatingip_agent_gateway',
- 'network_id': fake_network_id,
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}]
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}],
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}],
+ 'id': _uuid(),
+ 'binding:host_id': 'myhost',
+ 'device_owner': 'network:floatingip_agent_gateway',
+ 'network_id': fake_network_id,
+ 'mac_address': 'ca:fe:de:ad:be:ef'}]
)
router = prepare_router_data(enable_snat=True)
@mock.patch.object(lla.LinkLocalAllocator, '_write')
def test_create_dvr_fip_interfaces(self, lla_write):
fake_network_id = _uuid()
+ subnet_id = _uuid()
fake_floatingips = {'floatingips': [
{'id': _uuid(),
'floating_ip_address': '20.0.0.3',
'port_id': _uuid(),
'host': HOSTNAME}]}
agent_gateway_port = (
- [{'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ [{'fixed_ips': [
+ {'ip_address': '20.0.0.30',
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}],
+ 'subnets': [
+ {'id': subnet_id,
+ 'gateway_ip': '20.0.0.1'}],
'id': _uuid(),
'network_id': fake_network_id,
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}]
+ 'mac_address': 'ca:fe:de:ad:be:ef'}]
)
router = prepare_router_data(enable_snat=True)
with contextlib.nested(mock.patch.object(ri,
'get_floating_ips'),
mock.patch.object(
- ri, 'get_floating_agent_gw_interface'),
- mock.patch.object(
- ri, '_set_subnet_info')
+ ri, 'get_floating_agent_gw_interface')
) as (fips,
- fip_gw_port,
- sub_info):
+ fip_gw_port):
fips.return_value = fake_floatingips
fip_gw_port.return_value = agent_gateway_port[0]
ri.create_dvr_fip_interfaces(ext_gw_port)
ri.iptables_manager.ipv4['nat'] = mock.MagicMock()
ri.dist_fip_count = 0
fip_ns = agent.get_fip_ns(mock.sentinel.ext_net_id)
+ subnet_id = _uuid()
fip_ns.agent_gateway_port = (
{'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'subnet_id': subnet_id}],
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}],
'id': _uuid(),
'network_id': _uuid(),
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
)
def test_process_router_snat_disabled(self):
# send_arp is called both times process_router is called
self.assertEqual(self.send_arp.call_count, 2)
- def test_process_ipv6_only_gw(self):
+ def _test_process_ipv6_only_or_dual_stack_gw(self, dual_stack=False):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
- router = prepare_router_data(ip_version=6)
+ router = prepare_router_data(ip_version=6, dual_stack=dual_stack)
# Get NAT rules without the gw_port
gw_port = router['gw_port']
router['gw_port'] = None
# Get NAT rules with the gw_port
router['gw_port'] = gw_port
ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs)
+ orig_ext_gw_nat_rules = ri.external_gateway_nat_rules
with mock.patch.object(
ri,
'external_gateway_nat_rules') as external_gateway_nat_rules:
+ external_gateway_nat_rules.side_effect = orig_ext_gw_nat_rules
self._process_router_instance_for_agent(agent, ri, router)
new_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:]
- # There should be no change with the NAT rules
- self.assertFalse(external_gateway_nat_rules.called)
- self.assertEqual(orig_nat_rules, new_nat_rules)
+ # NAT rules should only change for dual_stack operation
+ if dual_stack:
+ self.assertTrue(external_gateway_nat_rules.called)
+ self.assertNotEqual(orig_nat_rules, new_nat_rules)
+ else:
+ self.assertFalse(external_gateway_nat_rules.called)
+ self.assertEqual(orig_nat_rules, new_nat_rules)
+
+ def test_process_ipv6_only_gw(self):
+ self._test_process_ipv6_only_or_dual_stack_gw()
+
+ def test_process_dual_stack_gw(self):
+ self._test_process_ipv6_only_or_dual_stack_gw(dual_stack=True)
def _process_router_ipv6_interface_added(
self, router, ra_mode=None, addr_mode=None):
**self.ri_kwargs)
port_id = _uuid()
+ subnet_id = _uuid()
dvr_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
- 'subnet_id': _uuid()}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id}],
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}],
'id': port_id,
'network_id': _uuid(),
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
interface_name = ri.get_snat_int_device_name(port_id)
self.device_exists.return_value = False
nat.add_rule = mock.Mock()
if fip_ns:
ri.fip_ns = agent.get_fip_ns(external_net_id)
+ subnet_id = _uuid()
ri.fip_ns.agent_gateway_port = {
'fixed_ips': [{
- 'ip_address': '20.0.0.30', 'subnet_id': _uuid()
+ 'ip_address': '20.0.0.30',
+ 'prefixlen': 24,
+ 'subnet_id': subnet_id
}],
- 'subnet': {'gateway_ip': '20.0.0.1'},
+ 'subnets': [{'id': subnet_id,
+ 'cidr': '20.0.0.0/24',
+ 'gateway_ip': '20.0.0.1'}],
'id': _uuid(),
'network_id': external_net_id,
- 'mac_address': 'ca:fe:de:ad:be:ef',
- 'ip_cidr': '20.0.0.30/24'}
+ 'mac_address': 'ca:fe:de:ad:be:ef'}
vm_floating_ip = '19.4.4.2'
ri.floating_ips_dict[vm_floating_ip] = FIP_PRI
self.assertEqual(1, len(routers))
interfaces = routers[0][l3_constants.INTERFACE_KEY]
self.assertEqual(1, len(interfaces))
- subnet_id = interfaces[0]['subnet']['id']
+ subnets = interfaces[0]['subnets']
+ self.assertEqual(1, len(subnets))
+ subnet_id = subnets[0]['id']
wanted_subnetid = p['port']['fixed_ips'][0]['subnet_id']
self.assertEqual(wanted_subnetid, subnet_id)
# clean-up
context.get_admin_context(), [r['router']['id']])
self.assertEqual(1, len(routers))
gw_port = routers[0]['gw_port']
- self.assertEqual(s['subnet']['id'], gw_port['subnet']['id'])
+ subnets = gw_port.get('subnets')
+ self.assertEqual(1, len(subnets))
+ self.assertEqual(s['subnet']['id'], subnets[0]['id'])
self._remove_external_gateway_from_router(
r['router']['id'],
s['subnet']['network_id'])
mock.call().addr.add('192.168.1.2/24')])
self.assertFalse(self.ip_dev().addr.delete.called)
- def test_l3_init_with_ipv6(self):
+ def _test_l3_init_with_ipv6(self, include_gw_ip):
addresses = [dict(scope='global',
dynamic=False,
cidr='2001:db8:a::123/64')]
bc = BaseChild(self.conf)
ns = '12345678-1234-5678-90ab-ba0987654321'
- bc.init_l3('tap0', ['2001:db8:a::124/64'], namespace=ns,
- extra_subnets=[{'cidr': '2001:db8:b::/64'}])
+ new_cidr = '2001:db8:a::124/64'
+ kwargs = {'namespace': ns,
+ 'extra_subnets': [{'cidr': '2001:db8:b::/64'}]}
+ if include_gw_ip:
+ kwargs['gateway_ips'] = ['2001:db8:a::1']
+ bc.init_l3('tap0', [new_cidr], **kwargs)
+ expected_calls = (
+ [mock.call('tap0', namespace=ns),
+ mock.call().addr.list(scope='global', filters=['permanent']),
+ mock.call().addr.add('2001:db8:a::124/64'),
+ mock.call().addr.delete('2001:db8:a::123/64')])
+ if include_gw_ip:
+ expected_calls += (
+ [mock.call().route.add_gateway('2001:db8:a::1')])
+ expected_calls += (
+ [mock.call().route.list_onlink_routes(constants.IP_VERSION_4),
+ mock.call().route.list_onlink_routes(constants.IP_VERSION_6),
+ mock.call().route.add_onlink_route('2001:db8:b::/64')])
+ self.ip_dev.assert_has_calls(expected_calls)
+
+ def test_l3_init_ipv6_with_gw_ip(self):
+ self._test_l3_init_with_ipv6(include_gw_ip=True)
+
+ def test_l3_init_ipv6_without_gw_ip(self):
+ self._test_l3_init_with_ipv6(include_gw_ip=False)
+
+ def test_l3_init_ext_gw_with_dual_stack(self):
+ old_addrs = [dict(ip_version=4, scope='global',
+ dynamic=False, cidr='172.16.77.240/24'),
+ dict(ip_version=6, scope='global',
+ dynamic=False, cidr='2001:db8:a::123/64')]
+ self.ip_dev().addr.list = mock.Mock(return_value=old_addrs)
+ self.ip_dev().route.list_onlink_routes.return_value = []
+ bc = BaseChild(self.conf)
+ ns = '12345678-1234-5678-90ab-ba0987654321'
+ new_cidrs = ['192.168.1.2/24', '2001:db8:a::124/64']
+ bc.init_l3('tap0', new_cidrs, namespace=ns,
+ extra_subnets=[{'cidr': '172.20.0.0/24'}])
self.ip_dev.assert_has_calls(
[mock.call('tap0', namespace=ns),
mock.call().addr.list(scope='global', filters=['permanent']),
+ mock.call().addr.add('192.168.1.2/24'),
mock.call().addr.add('2001:db8:a::124/64'),
+ mock.call().addr.delete('172.16.77.240/24'),
mock.call().addr.delete('2001:db8:a::123/64'),
mock.call().route.list_onlink_routes(constants.IP_VERSION_4),
mock.call().route.list_onlink_routes(constants.IP_VERSION_6),
- mock.call().route.add_onlink_route('2001:db8:b::/64')])
+ mock.call().route.add_onlink_route('172.20.0.0/24')])
def test_l3_init_with_ipv6_delete_onlink_routes(self):
addresses = [dict(scope='global',