26707bdbe7
We have git to track authorship, so let's not pad source files with it as well. A hacking check has been added for this. The value is N322. Change-Id: Iab0b64d417e0bb41a6b455e2ac377deee64ec3ee
719 lines
31 KiB
Python
719 lines
31 KiB
Python
# Copyright 2014, Hewlett-Packard Development Company, L.P.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
|
|
from neutron.api.rpc.handlers import dvr_rpc
|
|
from neutron.common import constants as n_const
|
|
from neutron.common import utils as n_utils
|
|
from neutron.openstack.common import log as logging
|
|
from neutron.plugins.openvswitch.common import constants
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# A class to represent a DVR-hosted subnet including vif_ports resident on
|
|
# that subnet
|
|
class LocalDVRSubnetMapping:
|
|
def __init__(self, subnet, csnat_ofport=constants.OFPORT_INVALID):
|
|
# set of commpute ports on on this dvr subnet
|
|
self.compute_ports = {}
|
|
self.subnet = subnet
|
|
self.csnat_ofport = csnat_ofport
|
|
self.dvr_owned = False
|
|
|
|
def __str__(self):
|
|
return ("subnet = %s compute_ports = %s csnat_port = %s"
|
|
" is_dvr_owned = %s" %
|
|
(self.subnet, self.get_compute_ofports(),
|
|
self.get_csnat_ofport(), self.is_dvr_owned()))
|
|
|
|
def get_subnet_info(self):
|
|
return self.subnet
|
|
|
|
def set_dvr_owned(self, owned):
|
|
self.dvr_owned = owned
|
|
|
|
def is_dvr_owned(self):
|
|
return self.dvr_owned
|
|
|
|
def add_compute_ofport(self, vif_id, ofport):
|
|
self.compute_ports[vif_id] = ofport
|
|
|
|
def remove_compute_ofport(self, vif_id):
|
|
self.compute_ports.pop(vif_id, 0)
|
|
|
|
def remove_all_compute_ofports(self):
|
|
self.compute_ports.clear()
|
|
|
|
def get_compute_ofports(self):
|
|
return self.compute_ports
|
|
|
|
def set_csnat_ofport(self, ofport):
|
|
self.csnat_ofport = ofport
|
|
|
|
def get_csnat_ofport(self):
|
|
return self.csnat_ofport
|
|
|
|
|
|
class OVSPort:
|
|
def __init__(self, id, ofport, mac, device_owner):
|
|
self.id = id
|
|
self.mac = mac
|
|
self.ofport = ofport
|
|
self.subnets = set()
|
|
self.device_owner = device_owner
|
|
|
|
def __str__(self):
|
|
return ("OVSPort: id = %s, ofport = %s, mac = %s,"
|
|
"device_owner = %s, subnets = %s" %
|
|
(self.id, self.ofport, self.mac,
|
|
self.device_owner, self.subnets))
|
|
|
|
def add_subnet(self, subnet_id):
|
|
self.subnets.add(subnet_id)
|
|
|
|
def remove_subnet(self, subnet_id):
|
|
self.subnets.remove(subnet_id)
|
|
|
|
def remove_all_subnets(self):
|
|
self.subnets.clear()
|
|
|
|
def get_subnets(self):
|
|
return self.subnets
|
|
|
|
def get_device_owner(self):
|
|
return self.device_owner
|
|
|
|
def get_mac(self):
|
|
return self.mac
|
|
|
|
def get_ofport(self):
|
|
return self.ofport
|
|
|
|
|
|
class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
|
|
'''
|
|
Implements OVS-based DVR(Distributed Virtual Router), for overlay networks.
|
|
'''
|
|
# history
|
|
# 1.0 Initial version
|
|
|
|
def __init__(self, context, plugin_rpc, integ_br, tun_br,
|
|
patch_int_ofport=constants.OFPORT_INVALID,
|
|
patch_tun_ofport=constants.OFPORT_INVALID,
|
|
host=None, enable_tunneling=False,
|
|
enable_distributed_routing=False):
|
|
self.context = context
|
|
self.plugin_rpc = plugin_rpc
|
|
self.int_br = integ_br
|
|
self.tun_br = tun_br
|
|
self.patch_int_ofport = patch_int_ofport
|
|
self.patch_tun_ofport = patch_tun_ofport
|
|
self.host = host
|
|
self.enable_tunneling = enable_tunneling
|
|
self.enable_distributed_routing = enable_distributed_routing
|
|
|
|
def reset_ovs_parameters(self, integ_br, tun_br,
|
|
patch_int_ofport, patch_tun_ofport):
|
|
'''Reset the openvswitch parameters'''
|
|
if not (self.enable_tunneling and self.enable_distributed_routing):
|
|
return
|
|
self.int_br = integ_br
|
|
self.tun_br = tun_br
|
|
self.patch_int_ofport = patch_int_ofport
|
|
self.patch_tun_ofport = patch_tun_ofport
|
|
|
|
def setup_dvr_flows_on_integ_tun_br(self):
|
|
'''Setup up initial dvr flows into br-int and br-tun'''
|
|
if not (self.enable_tunneling and self.enable_distributed_routing):
|
|
return
|
|
LOG.debug("L2 Agent operating in DVR Mode")
|
|
self.dvr_mac_address = None
|
|
self.local_dvr_map = {}
|
|
self.local_csnat_map = {}
|
|
self.local_ports = {}
|
|
self.registered_dvr_macs = set()
|
|
# get the local DVR MAC Address
|
|
try:
|
|
details = self.plugin_rpc.get_dvr_mac_address_by_host(
|
|
self.context, self.host)
|
|
LOG.debug("L2 Agent DVR: Received response for "
|
|
"get_dvr_mac_address_by_host() from "
|
|
"plugin: %r", details)
|
|
self.dvr_mac_address = details['mac_address']
|
|
except Exception:
|
|
LOG.error(_("DVR: Failed to obtain local DVR Mac address"))
|
|
self.enable_distributed_routing = False
|
|
# switch all traffic using L2 learning
|
|
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
|
priority=1, actions="normal")
|
|
return
|
|
|
|
# Remove existing flows in integration bridge
|
|
self.int_br.remove_all_flows()
|
|
|
|
# Add a canary flow to int_br to track OVS restarts
|
|
self.int_br.add_flow(table=constants.CANARY_TABLE, priority=0,
|
|
actions="drop")
|
|
|
|
# Insert 'drop' action as the default for Table DVR_TO_SRC_MAC
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=1,
|
|
actions="drop")
|
|
|
|
# Insert 'normal' action as the default for Table LOCAL_SWITCHING
|
|
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
|
priority=1,
|
|
actions="normal")
|
|
|
|
dvr_macs = self.plugin_rpc.get_dvr_mac_address_list(self.context)
|
|
LOG.debug("L2 Agent DVR: Received these MACs: %r", dvr_macs)
|
|
for mac in dvr_macs:
|
|
if mac['mac_address'] == self.dvr_mac_address:
|
|
continue
|
|
# Table 0 (default) will now sort DVR traffic from other
|
|
# traffic depending on in_port
|
|
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
|
priority=2,
|
|
in_port=self.patch_tun_ofport,
|
|
dl_src=mac['mac_address'],
|
|
actions="resubmit(,%s)" %
|
|
constants.DVR_TO_SRC_MAC)
|
|
# Table DVR_NOT_LEARN ensures unique dvr macs in the cloud
|
|
# are not learnt, as they may
|
|
# result in flow explosions
|
|
self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
|
|
priority=1,
|
|
dl_src=mac['mac_address'],
|
|
actions="output:%s" % self.patch_int_ofport)
|
|
|
|
self.registered_dvr_macs.add(mac['mac_address'])
|
|
|
|
self.tun_br.add_flow(priority=1,
|
|
in_port=self.patch_int_ofport,
|
|
actions="resubmit(,%s)" %
|
|
constants.DVR_PROCESS)
|
|
# table-miss should be sent to learning table
|
|
self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
|
|
priority=0,
|
|
actions="resubmit(,%s)" %
|
|
constants.LEARN_FROM_TUN)
|
|
|
|
self.tun_br.add_flow(table=constants.DVR_PROCESS,
|
|
priority=0,
|
|
actions="resubmit(,%s)" %
|
|
constants.PATCH_LV_TO_TUN)
|
|
|
|
def dvr_mac_address_update(self, dvr_macs):
|
|
if not (self.enable_tunneling and self.enable_distributed_routing):
|
|
return
|
|
|
|
LOG.debug("DVR Mac address update with host-mac: %s", dvr_macs)
|
|
|
|
if not self.dvr_mac_address:
|
|
LOG.debug("Self mac unknown, ignoring this "
|
|
"dvr_mac_address_update() ")
|
|
return
|
|
|
|
dvr_host_macs = set()
|
|
for entry in dvr_macs:
|
|
if entry['mac_address'] == self.dvr_mac_address:
|
|
continue
|
|
dvr_host_macs.add(entry['mac_address'])
|
|
|
|
if dvr_host_macs == self.registered_dvr_macs:
|
|
LOG.debug("DVR Mac address already up to date")
|
|
return
|
|
|
|
dvr_macs_added = dvr_host_macs - self.registered_dvr_macs
|
|
dvr_macs_removed = self.registered_dvr_macs - dvr_host_macs
|
|
|
|
for oldmac in dvr_macs_removed:
|
|
self.int_br.delete_flows(table=constants.LOCAL_SWITCHING,
|
|
in_port=self.patch_tun_ofport,
|
|
dl_src=oldmac)
|
|
self.tun_br.delete_flows(table=constants.DVR_NOT_LEARN,
|
|
dl_src=oldmac)
|
|
LOG.debug("Removed DVR MAC flow for %s", oldmac)
|
|
self.registered_dvr_macs.remove(oldmac)
|
|
|
|
for newmac in dvr_macs_added:
|
|
self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
|
|
priority=2,
|
|
in_port=self.patch_tun_ofport,
|
|
dl_src=newmac,
|
|
actions="resubmit(,%s)" %
|
|
constants.DVR_TO_SRC_MAC)
|
|
self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
|
|
priority=1,
|
|
dl_src=newmac,
|
|
actions="output:%s" % self.patch_int_ofport)
|
|
LOG.debug("Added DVR MAC flow for %s", newmac)
|
|
self.registered_dvr_macs.add(newmac)
|
|
|
|
def is_dvr_router_interface(self, device_owner):
|
|
return device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE
|
|
|
|
def process_tunneled_network(self, network_type, lvid, segmentation_id):
|
|
if not (self.enable_tunneling and self.enable_distributed_routing):
|
|
return
|
|
self.tun_br.add_flow(table=constants.TUN_TABLE[network_type],
|
|
priority=1,
|
|
tun_id=segmentation_id,
|
|
actions="mod_vlan_vid:%s,"
|
|
"resubmit(,%s)" %
|
|
(lvid, constants.DVR_NOT_LEARN))
|
|
|
|
def _bind_distributed_router_interface_port(self, port, fixed_ips,
|
|
device_owner, local_vlan):
|
|
# since router port must have only one fixed IP, directly
|
|
# use fixed_ips[0]
|
|
subnet_uuid = fixed_ips[0]['subnet_id']
|
|
csnat_ofport = constants.OFPORT_INVALID
|
|
ldm = None
|
|
if subnet_uuid in self.local_dvr_map:
|
|
ldm = self.local_dvr_map[subnet_uuid]
|
|
csnat_ofport = ldm.get_csnat_ofport()
|
|
if csnat_ofport == constants.OFPORT_INVALID:
|
|
LOG.error(_("DVR: Duplicate DVR router interface detected "
|
|
"for subnet %s"), subnet_uuid)
|
|
return
|
|
else:
|
|
# set up LocalDVRSubnetMapping available for this subnet
|
|
subnet_info = self.plugin_rpc.get_subnet_for_dvr(self.context,
|
|
subnet_uuid)
|
|
if not subnet_info:
|
|
LOG.error(_("DVR: Unable to retrieve subnet information"
|
|
" for subnet_id %s"), subnet_uuid)
|
|
return
|
|
LOG.debug("get_subnet_for_dvr for subnet %s returned with %s" %
|
|
(subnet_uuid, subnet_info))
|
|
ldm = LocalDVRSubnetMapping(subnet_info)
|
|
self.local_dvr_map[subnet_uuid] = ldm
|
|
|
|
# DVR takes over
|
|
ldm.set_dvr_owned(True)
|
|
|
|
subnet_info = ldm.get_subnet_info()
|
|
ip_subnet = subnet_info['cidr']
|
|
local_compute_ports = (
|
|
self.plugin_rpc.get_ports_on_host_by_subnet(
|
|
self.context, self.host, subnet_uuid))
|
|
LOG.debug("DVR: List of ports received from "
|
|
"get_ports_on_host_by_subnet %s",
|
|
local_compute_ports)
|
|
for prt in local_compute_ports:
|
|
vif = self.int_br.get_vif_port_by_id(prt['id'])
|
|
if not vif:
|
|
continue
|
|
ldm.add_compute_ofport(vif.vif_id, vif.ofport)
|
|
if vif.vif_id in self.local_ports:
|
|
# ensure if a compute port is already on
|
|
# a different dvr routed subnet
|
|
# if yes, queue this subnet to that port
|
|
ovsport = self.local_ports[vif.vif_id]
|
|
ovsport.add_subnet(subnet_uuid)
|
|
else:
|
|
# the compute port is discovered first here that its on
|
|
# a dvr routed subnet queue this subnet to that port
|
|
ovsport = OVSPort(vif.vif_id, vif.ofport,
|
|
vif.vif_mac, prt['device_owner'])
|
|
|
|
ovsport.add_subnet(subnet_uuid)
|
|
self.local_ports[vif.vif_id] = ovsport
|
|
|
|
# create rule for just this vm port
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=4,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=ovsport.get_mac(),
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
"output:%s" %
|
|
(subnet_info['gateway_mac'],
|
|
ovsport.get_ofport()))
|
|
|
|
# create rule to forward broadcast/multicast frames from dvr
|
|
# router interface to appropriate local tenant ports
|
|
ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
|
|
if csnat_ofport != constants.OFPORT_INVALID:
|
|
ofports = str(csnat_ofport) + ',' + ofports
|
|
if ofports:
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=2,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet,
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
"output:%s" %
|
|
(subnet_info['gateway_mac'], ofports))
|
|
|
|
self.tun_br.add_flow(table=constants.DVR_PROCESS,
|
|
priority=3,
|
|
dl_vlan=local_vlan,
|
|
proto='arp',
|
|
nw_dst=subnet_info['gateway_ip'],
|
|
actions="drop")
|
|
|
|
self.tun_br.add_flow(table=constants.DVR_PROCESS,
|
|
priority=2,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=port.vif_mac,
|
|
actions="drop")
|
|
|
|
self.tun_br.add_flow(table=constants.DVR_PROCESS,
|
|
priority=1,
|
|
dl_vlan=local_vlan,
|
|
dl_src=port.vif_mac,
|
|
actions="mod_dl_src:%s,resubmit(,%s)" %
|
|
(self.dvr_mac_address,
|
|
constants.PATCH_LV_TO_TUN))
|
|
|
|
# the dvr router interface is itself a port, so capture it
|
|
# queue this subnet to that port. A subnet appears only once as
|
|
# a router interface on any given router
|
|
ovsport = OVSPort(port.vif_id, port.ofport,
|
|
port.vif_mac, device_owner)
|
|
ovsport.add_subnet(subnet_uuid)
|
|
self.local_ports[port.vif_id] = ovsport
|
|
|
|
def _bind_port_on_dvr_subnet(self, port, fixed_ips,
|
|
device_owner, local_vlan):
|
|
# Handle new compute port added use-case
|
|
subnet_uuid = None
|
|
for ips in fixed_ips:
|
|
if ips['subnet_id'] not in self.local_dvr_map:
|
|
continue
|
|
subnet_uuid = ips['subnet_id']
|
|
ldm = self.local_dvr_map[subnet_uuid]
|
|
if not ldm.is_dvr_owned():
|
|
# well this is CSNAT stuff, let dvr come in
|
|
# and do plumbing for this vm later
|
|
continue
|
|
|
|
# This confirms that this compute port belongs
|
|
# to a dvr hosted subnet.
|
|
# Accommodate this VM Port into the existing rule in
|
|
# the integration bridge
|
|
LOG.debug("DVR: Plumbing compute port %s", port.vif_id)
|
|
subnet_info = ldm.get_subnet_info()
|
|
ip_subnet = subnet_info['cidr']
|
|
csnat_ofport = ldm.get_csnat_ofport()
|
|
ldm.add_compute_ofport(port.vif_id, port.ofport)
|
|
if port.vif_id in self.local_ports:
|
|
# ensure if a compute port is already on a different
|
|
# dvr routed subnet
|
|
# if yes, queue this subnet to that port
|
|
ovsport = self.local_ports[port.vif_id]
|
|
ovsport.add_subnet(subnet_uuid)
|
|
else:
|
|
# the compute port is discovered first here that its
|
|
# on a dvr routed subnet, queue this subnet to that port
|
|
ovsport = OVSPort(port.vif_id, port.ofport,
|
|
port.vif_mac, device_owner)
|
|
|
|
ovsport.add_subnet(subnet_uuid)
|
|
self.local_ports[port.vif_id] = ovsport
|
|
# create a rule for this vm port
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=4,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=ovsport.get_mac(),
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
"output:%s" %
|
|
(subnet_info['gateway_mac'],
|
|
ovsport.get_ofport()))
|
|
ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
|
|
|
|
if csnat_ofport != constants.OFPORT_INVALID:
|
|
ofports = str(csnat_ofport) + ',' + ofports
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=2,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet,
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
" output:%s" %
|
|
(subnet_info['gateway_mac'], ofports))
|
|
|
|
def _bind_centralized_snat_port_on_dvr_subnet(self, port, fixed_ips,
|
|
device_owner, local_vlan):
|
|
if port.vif_id in self.local_ports:
|
|
# throw an error if CSNAT port is already on a different
|
|
# dvr routed subnet
|
|
ovsport = self.local_ports[port.vif_id]
|
|
subs = list(ovsport.get_subnets())
|
|
LOG.error(_("Centralized-SNAT port %s already seen on "),
|
|
port.vif_id)
|
|
LOG.error(_("a different subnet %s"), subs[0])
|
|
return
|
|
# since centralized-SNAT (CSNAT) port must have only one fixed
|
|
# IP, directly use fixed_ips[0]
|
|
subnet_uuid = fixed_ips[0]['subnet_id']
|
|
ldm = None
|
|
subnet_info = None
|
|
if subnet_uuid not in self.local_dvr_map:
|
|
# no csnat ports seen on this subnet - create csnat state
|
|
# for this subnet
|
|
subnet_info = self.plugin_rpc.get_subnet_for_dvr(self.context,
|
|
subnet_uuid)
|
|
ldm = LocalDVRSubnetMapping(subnet_info, port.ofport)
|
|
self.local_dvr_map[subnet_uuid] = ldm
|
|
else:
|
|
ldm = self.local_dvr_map[subnet_uuid]
|
|
subnet_info = ldm.get_subnet_info()
|
|
# Store csnat OF Port in the existing DVRSubnetMap
|
|
ldm.set_csnat_ofport(port.ofport)
|
|
|
|
# create ovsPort footprint for csnat port
|
|
ovsport = OVSPort(port.vif_id, port.ofport,
|
|
port.vif_mac, device_owner)
|
|
ovsport.add_subnet(subnet_uuid)
|
|
self.local_ports[port.vif_id] = ovsport
|
|
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=4,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=ovsport.get_mac(),
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
" output:%s" %
|
|
(subnet_info['gateway_mac'],
|
|
ovsport.get_ofport()))
|
|
ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
|
|
ofports = str(ldm.get_csnat_ofport()) + ',' + ofports
|
|
ip_subnet = subnet_info['cidr']
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=2,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet,
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
" output:%s" %
|
|
(subnet_info['gateway_mac'], ofports))
|
|
|
|
def bind_port_to_dvr(self, port, network_type, fixed_ips,
|
|
device_owner, local_vlan_id):
|
|
# a port coming up as distributed router interface
|
|
if not (self.enable_tunneling and self.enable_distributed_routing):
|
|
return
|
|
|
|
if network_type not in constants.TUNNEL_NETWORK_TYPES:
|
|
return
|
|
|
|
if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE:
|
|
self._bind_distributed_router_interface_port(port, fixed_ips,
|
|
device_owner,
|
|
local_vlan_id)
|
|
|
|
if device_owner and n_utils.is_dvr_serviced(device_owner):
|
|
self._bind_port_on_dvr_subnet(port, fixed_ips,
|
|
device_owner,
|
|
local_vlan_id)
|
|
|
|
if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT:
|
|
self._bind_centralized_snat_port_on_dvr_subnet(port, fixed_ips,
|
|
device_owner,
|
|
local_vlan_id)
|
|
|
|
def _unbind_distributed_router_interface_port(self, port, local_vlan):
|
|
|
|
ovsport = self.local_ports[port.vif_id]
|
|
|
|
# removal of distributed router interface
|
|
subnet_ids = ovsport.get_subnets()
|
|
subnet_set = set(subnet_ids)
|
|
# ensure we process for all the subnets laid on this removed port
|
|
for sub_uuid in subnet_set:
|
|
if sub_uuid not in self.local_dvr_map:
|
|
continue
|
|
|
|
ldm = self.local_dvr_map[sub_uuid]
|
|
subnet_info = ldm.get_subnet_info()
|
|
ip_subnet = subnet_info['cidr']
|
|
|
|
# DVR is no more owner
|
|
ldm.set_dvr_owned(False)
|
|
|
|
# remove all vm rules for this dvr subnet
|
|
# clear of compute_ports altogether
|
|
compute_ports = ldm.get_compute_ofports()
|
|
for vif_id in compute_ports:
|
|
ovsport = self.local_ports[vif_id]
|
|
self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=ovsport.get_mac())
|
|
ldm.remove_all_compute_ofports()
|
|
|
|
if ldm.get_csnat_ofport() != -1:
|
|
# If there is a csnat port on this agent, preserve
|
|
# the local_dvr_map state
|
|
ofports = str(ldm.get_csnat_ofport())
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=2,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet,
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
" output:%s" %
|
|
(subnet_info['gateway_mac'], ofports))
|
|
else:
|
|
# removed port is a distributed router interface
|
|
self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
|
|
proto='ip', dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet)
|
|
# remove subnet from local_dvr_map as no dvr (or) csnat
|
|
# ports available on this agent anymore
|
|
self.local_dvr_map.pop(sub_uuid, None)
|
|
|
|
self.tun_br.delete_flows(table=constants.DVR_PROCESS,
|
|
dl_vlan=local_vlan,
|
|
proto='arp',
|
|
nw_dst=subnet_info['gateway_ip'])
|
|
ovsport.remove_subnet(sub_uuid)
|
|
|
|
self.tun_br.delete_flows(table=constants.DVR_PROCESS,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=port.vif_mac)
|
|
|
|
self.tun_br.delete_flows(table=constants.DVR_PROCESS,
|
|
dl_vlan=local_vlan,
|
|
dl_src=port.vif_mac)
|
|
# release port state
|
|
self.local_ports.pop(port.vif_id, None)
|
|
|
|
def _unbind_port_on_dvr_subnet(self, port, local_vlan):
|
|
|
|
ovsport = self.local_ports[port.vif_id]
|
|
# This confirms that this compute port being removed belonged
|
|
# to a dvr hosted subnet.
|
|
# Accommodate this VM Port into the existing rule in
|
|
# the integration bridge
|
|
LOG.debug("DVR: Removing plumbing for compute port %s", port)
|
|
subnet_ids = ovsport.get_subnets()
|
|
# ensure we process for all the subnets laid on this port
|
|
for sub_uuid in subnet_ids:
|
|
if sub_uuid not in self.local_dvr_map:
|
|
continue
|
|
|
|
ldm = self.local_dvr_map[sub_uuid]
|
|
subnet_info = ldm.get_subnet_info()
|
|
ldm.remove_compute_ofport(port.vif_id)
|
|
ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
|
|
ip_subnet = subnet_info['cidr']
|
|
|
|
# first remove this vm port rule
|
|
self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=ovsport.get_mac())
|
|
if ldm.get_csnat_ofport() != -1:
|
|
# If there is a csnat port on this agent, preserve
|
|
# the local_dvr_map state
|
|
ofports = str(ldm.get_csnat_ofport()) + ',' + ofports
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=2,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet,
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
" output:%s" %
|
|
(subnet_info['gateway_mac'], ofports))
|
|
else:
|
|
if ofports:
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=2,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet,
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
" output:%s" %
|
|
(subnet_info['gateway_mac'],
|
|
ofports))
|
|
else:
|
|
# remove the flow altogether, as no ports (both csnat/
|
|
# compute) are available on this subnet in this
|
|
# agent
|
|
self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet)
|
|
# release port state
|
|
self.local_ports.pop(port.vif_id, None)
|
|
|
|
def _unbind_centralized_snat_port_on_dvr_subnet(self, port, local_vlan):
|
|
|
|
ovsport = self.local_ports[port.vif_id]
|
|
# This confirms that this compute port being removed belonged
|
|
# to a dvr hosted subnet.
|
|
# Accommodate this VM Port into the existing rule in
|
|
# the integration bridge
|
|
LOG.debug("DVR: Removing plumbing for csnat port %s", port)
|
|
sub_uuid = list(ovsport.get_subnets())[0]
|
|
# ensure we process for all the subnets laid on this port
|
|
if sub_uuid not in self.local_dvr_map:
|
|
return
|
|
ldm = self.local_dvr_map[sub_uuid]
|
|
subnet_info = ldm.get_subnet_info()
|
|
ip_subnet = subnet_info['cidr']
|
|
ldm.set_csnat_ofport(constants.OFPORT_INVALID)
|
|
# then remove csnat port rule
|
|
self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
|
|
dl_vlan=local_vlan,
|
|
dl_dst=ovsport.get_mac())
|
|
|
|
ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
|
|
if ofports:
|
|
self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
|
|
priority=2,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet,
|
|
actions="strip_vlan,mod_dl_src:%s,"
|
|
" output:%s" %
|
|
(subnet_info['gateway_mac'], ofports))
|
|
else:
|
|
self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
|
|
proto='ip',
|
|
dl_vlan=local_vlan,
|
|
nw_dst=ip_subnet)
|
|
if not ldm.is_dvr_owned():
|
|
# if not owned by DVR (only used for csnat), remove this
|
|
# subnet state altogether
|
|
self.local_dvr_map.pop(sub_uuid, None)
|
|
|
|
# release port state
|
|
self.local_ports.pop(port.vif_id, None)
|
|
|
|
def unbind_port_from_dvr(self, vif_port, local_vlan_id):
|
|
if not (self.enable_tunneling and self.enable_distributed_routing):
|
|
return
|
|
# Handle port removed use-case
|
|
if vif_port and vif_port.vif_id not in self.local_ports:
|
|
LOG.debug("DVR: Non distributed port, ignoring %s", vif_port)
|
|
return
|
|
|
|
ovsport = self.local_ports[vif_port.vif_id]
|
|
device_owner = ovsport.get_device_owner()
|
|
|
|
if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE:
|
|
self._unbind_distributed_router_interface_port(vif_port,
|
|
local_vlan_id)
|
|
|
|
if device_owner and n_utils.is_dvr_serviced(device_owner):
|
|
self._unbind_port_on_dvr_subnet(vif_port, local_vlan_id)
|
|
|
|
if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT:
|
|
self._unbind_centralized_snat_port_on_dvr_subnet(vif_port,
|
|
local_vlan_id)
|