Merge "Move internal port processing to router classes"
This commit is contained in:
commit
da973cc5a7
|
@ -24,7 +24,6 @@ from oslo_utils import timeutils
|
|||
|
||||
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
|
||||
|
@ -323,68 +322,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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
|
||||
|
@ -404,7 +341,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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):
|
||||
|
@ -412,7 +349,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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]
|
||||
|
@ -426,7 +363,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
|
||||
# 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)
|
||||
|
||||
|
@ -486,7 +423,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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()
|
||||
|
@ -536,8 +473,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
|
||||
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:
|
||||
|
@ -546,7 +483,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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:
|
||||
|
@ -568,27 +505,21 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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:
|
||||
|
@ -617,7 +548,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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
|
||||
|
@ -665,16 +596,16 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
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
|
||||
|
@ -702,89 +633,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
|
|||
(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)
|
||||
|
|
|
@ -12,26 +12,19 @@
|
|||
# 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):
|
||||
|
@ -65,52 +58,6 @@ 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."""
|
||||
|
@ -118,12 +65,13 @@ class AgentMixin(object):
|
|||
# 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(
|
||||
|
@ -132,33 +80,6 @@ class AgentMixin(object):
|
|||
# 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']
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
# 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
|
||||
|
||||
|
@ -24,6 +27,8 @@ from neutron.common import utils as common_utils
|
|||
from neutron.i18n import _LE
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
# xor-folding mask used for IPv6 rule index
|
||||
MASK_30 = 0x3fffffff
|
||||
|
||||
|
||||
class DvrRouter(router.RouterInfo):
|
||||
|
@ -45,6 +50,13 @@ 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.
|
||||
|
||||
|
@ -139,7 +151,6 @@ class DvrRouter(router.RouterInfo):
|
|||
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
|
||||
|
@ -212,3 +223,144 @@ class DvrRouter(router.RouterInfo):
|
|||
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)
|
||||
|
|
|
@ -61,7 +61,7 @@ class AgentMixin(object):
|
|||
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'],
|
||||
|
|
|
@ -251,3 +251,24 @@ class HaRouter(router.RouterInfo):
|
|||
|
||||
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)
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
# 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
|
||||
|
@ -20,10 +22,10 @@ from neutron.agent.linux import iptables_manager
|
|||
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):
|
||||
|
@ -86,6 +88,16 @@ 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:
|
||||
|
@ -245,3 +257,95 @@ class RouterInfo(object):
|
|||
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)
|
||||
|
|
|
@ -184,3 +184,25 @@ class TestDvrRouterOperations(base.BaseTestCase):
|
|||
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)
|
||||
|
|
|
@ -279,23 +279,6 @@ class BasicRouterOperationsFramework(base.BaseTestCase):
|
|||
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:
|
||||
|
@ -361,26 +344,41 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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'
|
||||
|
@ -408,29 +406,30 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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']))
|
||||
|
@ -456,12 +455,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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
|
||||
|
@ -509,7 +508,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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(
|
||||
|
@ -518,8 +517,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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']))
|
||||
|
@ -643,32 +641,13 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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(
|
||||
|
@ -676,15 +655,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
'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)
|
||||
|
||||
|
@ -907,7 +882,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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):
|
||||
|
@ -945,9 +920,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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):
|
||||
|
@ -1205,7 +1180,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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
|
||||
|
@ -1233,7 +1208,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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
|
||||
|
@ -1384,9 +1359,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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'),
|
||||
|
@ -1402,8 +1377,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
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,
|
||||
|
@ -1767,7 +1741,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
|||
'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,
|
||||
|
|
Loading…
Reference in New Issue