
When updating floating ips on shared routers, we should add the relevant nat rules on all the interfaces of the other routers sharing the same edge, to allow routing between them using the floating ip Change-Id: I9864bdf58c9cb7181549c91000b0c325bf5f0ff2
827 lines
40 KiB
Python
827 lines
40 KiB
Python
# Copyright 2014 VMware, Inc
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import netaddr
|
|
from oslo_config import cfg
|
|
|
|
from neutron.api.v2 import attributes as attr
|
|
from neutron.db import l3_db
|
|
from neutron.db import models_v2
|
|
from oslo_log import log as logging
|
|
|
|
from vmware_nsx._i18n import _
|
|
from vmware_nsx.common import exceptions as nsx_exc
|
|
from vmware_nsx.common import locking
|
|
from vmware_nsx.db import nsxv_db
|
|
from vmware_nsx.db import nsxv_models
|
|
from vmware_nsx.plugins.nsx_v.drivers import (
|
|
abstract_router_driver as router_driver)
|
|
from vmware_nsx.plugins.nsx_v import md_proxy as nsx_v_md_proxy
|
|
from vmware_nsx.plugins.nsx_v import plugin as nsx_v
|
|
from vmware_nsx.plugins.nsx_v.vshield.common import (
|
|
constants as vcns_const)
|
|
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
NSXV_ROUTER_RECONFIG = "nsxv_router_reconfig"
|
|
|
|
|
|
class RouterSharedDriver(router_driver.RouterBaseDriver):
|
|
|
|
def get_type(self):
|
|
return "shared"
|
|
|
|
def create_router(self, context, lrouter,
|
|
appliance_size=None, allow_metadata=True):
|
|
pass
|
|
|
|
def update_router(self, context, router_id, router):
|
|
r = router['router']
|
|
is_routes_update = True if 'routes' in r else False
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
if not edge_id:
|
|
return super(nsx_v.NsxVPluginV2, self.plugin).update_router(
|
|
context, router_id, router)
|
|
else:
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id), lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
gw_info = self.plugin._extract_external_gw(
|
|
context, router, is_extract=True)
|
|
super(nsx_v.NsxVPluginV2, self.plugin).update_router(
|
|
context, router_id, router)
|
|
self.update_routes(context, router_id, None)
|
|
# here is used to handle routes which tenant updates.
|
|
if gw_info != attr.ATTR_NOT_SPECIFIED:
|
|
self._update_router_gw_info(context, router_id, gw_info,
|
|
is_routes_update)
|
|
if 'admin_state_up' in r:
|
|
# If router was deployed on a different edge then
|
|
# admin-state-up is already updated on the new edge.
|
|
current_edge_id = (
|
|
edge_utils.get_router_edge_id(context, router_id))
|
|
if current_edge_id == edge_id:
|
|
self.plugin._update_router_admin_state(context, router_id,
|
|
self.get_type(),
|
|
r['admin_state_up'])
|
|
return self.plugin.get_router(context, router_id)
|
|
|
|
def delete_router(self, context, router_id):
|
|
pass
|
|
|
|
def _update_routes_on_routers(self, context, target_router_id, router_ids):
|
|
nexthop = None
|
|
all_routes = []
|
|
for router_id in router_ids:
|
|
routes = self.plugin._get_extra_routes_by_router_id(
|
|
context, router_id)
|
|
filters = {'device_id': [router_id]}
|
|
ports = self.plugin.get_ports(context.elevated(), filters)
|
|
self.plugin._add_network_info_for_routes(context, routes, ports)
|
|
all_routes.extend(routes)
|
|
if not nexthop:
|
|
router_qry = context.session.query(l3_db.Router)
|
|
router_db = router_qry.filter_by(id=router_id).one()
|
|
router_nexthop = self.plugin._get_external_attachment_info(
|
|
context, router_db)[2]
|
|
if router_nexthop:
|
|
nexthop = router_nexthop
|
|
# TODO(berlin) do rollback op.
|
|
edge_utils.update_routes(self.nsx_v, context, target_router_id,
|
|
all_routes, nexthop)
|
|
|
|
# return a dic of each router -> list of vnics from the other routers
|
|
def _get_all_routers_vnic_indices(self, context, router_ids):
|
|
|
|
all_vnic_indices = {}
|
|
if len(router_ids) < 1:
|
|
# there are no routers
|
|
return all_vnic_indices
|
|
|
|
intf_ports = self.plugin.get_ports(
|
|
context.elevated(),
|
|
filters={'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]})
|
|
|
|
edge_id = edge_utils.get_router_edge_id(context, router_ids[0])
|
|
edge_vnic_bindings = nsxv_db.get_edge_vnic_bindings_by_edge(
|
|
context.session, edge_id)
|
|
|
|
for this_router_id in router_ids:
|
|
# get networks IDs for this router
|
|
router_net_ids = list(
|
|
set([port['network_id']
|
|
for port in intf_ports
|
|
if port['device_id'] == this_router_id]))
|
|
|
|
# get vnic index for each network
|
|
vnic_indices = []
|
|
for net_id in router_net_ids:
|
|
vnic_indices.extend([edge_vnic_binding.vnic_index
|
|
for edge_vnic_binding
|
|
in edge_vnic_bindings
|
|
if edge_vnic_binding.network_id == net_id
|
|
])
|
|
|
|
# make sure the list is unique:
|
|
vnic_indices = list(set(vnic_indices))
|
|
# add to the result dict
|
|
all_vnic_indices[this_router_id] = list(vnic_indices)
|
|
|
|
return all_vnic_indices
|
|
|
|
def _update_nat_rules_on_routers(self, context,
|
|
target_router_id, router_ids):
|
|
snats = []
|
|
dnats = []
|
|
vnics_by_router = self._get_all_routers_vnic_indices(
|
|
context, router_ids)
|
|
for router_id in router_ids:
|
|
router_qry = context.session.query(l3_db.Router)
|
|
router = router_qry.filter_by(id=router_id).one()
|
|
if router.gw_port:
|
|
snat, dnat = self.plugin._get_nat_rules(context, router)
|
|
snats.extend(snat)
|
|
dnats.extend(dnat)
|
|
if len(dnat) > 0:
|
|
# Copy each DNAT rule to all vnics of the other routers,
|
|
# to allow NAT-ed traffic between routers
|
|
other_vnics = []
|
|
for other_router_id in router_ids:
|
|
if other_router_id != router_id:
|
|
other_vnics.extend(
|
|
vnics_by_router[other_router_id])
|
|
for rule in dnat:
|
|
for vnic_index in other_vnics:
|
|
new_rule = rule.copy()
|
|
# use explicit vnic_index
|
|
new_rule['vnic_index'] = vnic_index
|
|
dnats.extend([new_rule])
|
|
|
|
edge_utils.update_nat_rules(
|
|
self.nsx_v, context, target_router_id, snats, dnats)
|
|
|
|
def _update_external_interface_on_routers(self, context,
|
|
target_router_id, router_ids):
|
|
ext_net_ids = self._get_ext_net_ids(context, router_ids)
|
|
if len(ext_net_ids) > 1:
|
|
msg = _("Can't configure external interface on multiple external "
|
|
"networks")
|
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
|
gateway_primary_addr = None
|
|
gateway_mask = None
|
|
gateway_nexthop = None
|
|
secondary = []
|
|
if not ext_net_ids:
|
|
ext_net_id = None
|
|
else:
|
|
ext_net_id = ext_net_ids[0]
|
|
for router_id in router_ids:
|
|
router_qry = context.session.query(l3_db.Router)
|
|
router = router_qry.filter_by(id=router_id).one()
|
|
addr, mask, nexthop = self.plugin._get_external_attachment_info(
|
|
context, router)
|
|
if addr:
|
|
if not gateway_primary_addr:
|
|
gateway_primary_addr = addr
|
|
else:
|
|
secondary.append(addr)
|
|
if mask and not gateway_mask:
|
|
gateway_mask = mask
|
|
if nexthop and not gateway_nexthop:
|
|
gateway_nexthop = nexthop
|
|
secondary.extend(self.plugin._get_floatingips_by_router(
|
|
context, router_id))
|
|
LOG.debug('Configure ext interface as following, ext_net: %s, '
|
|
'primaryAddress: %s, netmask: %s, nexthop: %s, secondary: '
|
|
'%s.', ext_net_id, gateway_primary_addr, gateway_mask,
|
|
gateway_nexthop, secondary)
|
|
edge_utils.update_external_interface(
|
|
self.nsx_v, context, target_router_id, ext_net_id,
|
|
gateway_primary_addr, gateway_mask, secondary)
|
|
|
|
def _update_subnets_and_dnat_firewall_on_routers(self, context,
|
|
target_router_id,
|
|
router_ids,
|
|
allow_external=True):
|
|
fake_fw_rules = []
|
|
for router_id in router_ids:
|
|
router_qry = context.session.query(l3_db.Router)
|
|
router = router_qry.filter_by(id=router_id).one()
|
|
subnet_cidrs = self.plugin._find_router_subnets_cidrs(
|
|
context, router['id'])
|
|
routes = self.plugin._get_extra_routes_by_router_id(
|
|
context, router['id'])
|
|
subnet_cidrs.extend([route['destination'] for route in routes])
|
|
if subnet_cidrs:
|
|
# Fake fw rule to open subnets firewall flows and static routes
|
|
# relative flows
|
|
fake_subnet_fw_rule = {
|
|
'action': 'allow',
|
|
'enabled': True,
|
|
'source_ip_address': subnet_cidrs,
|
|
'destination_ip_address': subnet_cidrs}
|
|
fake_fw_rules.append(fake_subnet_fw_rule)
|
|
_, dnat_rules = self.plugin._get_nat_rules(context, router)
|
|
dnat_cidrs = [rule['dst'] for rule in dnat_rules]
|
|
if dnat_cidrs:
|
|
# Fake fw rule to open dnat firewall flows
|
|
fake_dnat_fw_rule = {
|
|
'action': 'allow',
|
|
'enabled': True,
|
|
'destination_ip_address': dnat_cidrs}
|
|
fake_fw_rules.append(fake_dnat_fw_rule)
|
|
nosnat_fw_rules = self.plugin._get_nosnat_subnets_fw_rules(
|
|
context, router)
|
|
fake_fw_rules.extend(nosnat_fw_rules)
|
|
|
|
# If metadata service is enabled, block access to inter-edge network
|
|
if self.plugin.metadata_proxy_handler:
|
|
fake_fw_rules += (
|
|
nsx_v_md_proxy.get_router_fw_rules())
|
|
|
|
# TODO(berlin): Add fw rules if fw service is supported
|
|
fake_fw = {'firewall_rule_list': fake_fw_rules}
|
|
edge_utils.update_firewall(self.nsx_v, context, target_router_id,
|
|
fake_fw, allow_external=allow_external)
|
|
|
|
def update_routes(self, context, router_id, nexthop):
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
if edge_id:
|
|
router_db = self.plugin._get_router(context, router_id)
|
|
available_router_ids, conflict_router_ids = (
|
|
self._get_available_and_conflicting_ids(context, router_id))
|
|
is_conflict = self.edge_manager.is_router_conflict_on_edge(
|
|
context, router_id, conflict_router_ids, [], 0)
|
|
if is_conflict:
|
|
self._remove_router_services_on_edge(context, router_id)
|
|
self._unbind_router_on_edge(context, router_id)
|
|
self._bind_router_on_available_edge(
|
|
context, router_id, router_db.admin_state_up)
|
|
new_edge_id = edge_utils.get_router_edge_id(context,
|
|
router_id)
|
|
with locking.LockManager.get_lock(
|
|
str(new_edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
self._add_router_services_on_available_edge(context,
|
|
router_id)
|
|
else:
|
|
router_ids = self.edge_manager.get_routers_on_same_edge(
|
|
context, router_id)
|
|
if router_ids:
|
|
self._update_routes_on_routers(
|
|
context, router_id, router_ids)
|
|
|
|
def _get_ext_net_ids(self, context, router_ids):
|
|
ext_net_ids = []
|
|
for router_id in router_ids:
|
|
router_qry = context.session.query(l3_db.Router)
|
|
router_db = router_qry.filter_by(id=router_id).one()
|
|
ext_net_id = router_db.gw_port_id and router_db.gw_port.network_id
|
|
if ext_net_id and ext_net_id not in ext_net_ids:
|
|
ext_net_ids.append(ext_net_id)
|
|
return ext_net_ids
|
|
|
|
def _get_shared_routers(self, context):
|
|
shared_routers = []
|
|
routers_qry = context.session.query(l3_db.Router).all()
|
|
for r in routers_qry:
|
|
nsx_attr = (context.session.query(
|
|
nsxv_models.NsxvRouterExtAttributes).filter_by(
|
|
router_id=r['id']).first())
|
|
if nsx_attr and nsx_attr['router_type'] == 'shared':
|
|
shared_routers.append(r)
|
|
return shared_routers
|
|
|
|
def _get_available_and_conflicting_ids(self, context, router_id):
|
|
"""Query all conflicting router ids with existing router id.
|
|
The router with static routes will be conflict with all other routers.
|
|
The routers with different gateway will be conflict.
|
|
The routers with overlapping interface will be conflict.
|
|
"""
|
|
# 1. Check gateway
|
|
# 2. Check subnet interface
|
|
# 3. Check static routes
|
|
router_list = []
|
|
src_router_dict = {}
|
|
ports_qry = context.session.query(models_v2.Port)
|
|
intf_ports = ports_qry.filter_by(
|
|
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF).all()
|
|
gw_ports = ports_qry.filter_by(
|
|
device_owner=l3_db.DEVICE_OWNER_ROUTER_GW).all()
|
|
shared_routers = self._get_shared_routers(context)
|
|
for r in shared_routers:
|
|
router_dict = {}
|
|
router_dict['id'] = r['id']
|
|
router_dict['gateway'] = None
|
|
for gwp in gw_ports:
|
|
if gwp['id'] == r['gw_port_id']:
|
|
router_dict['gateway'] = (
|
|
gwp['fixed_ips'][0]['subnet_id'])
|
|
subnet_ids = [p['fixed_ips'][0]['subnet_id'] for p in
|
|
intf_ports if p['device_id'] == r['id']]
|
|
router_dict['subnet_ids'] = subnet_ids
|
|
extra_routes = self.plugin._get_extra_routes_by_router_id(
|
|
context, r['id'])
|
|
destinations = [routes['destination'] for routes in extra_routes]
|
|
router_dict['destinations'] = destinations
|
|
|
|
LOG.debug('The router configuration is %s for router %s',
|
|
router_dict, router_dict['id'])
|
|
if router_id != r['id']:
|
|
router_list.append(router_dict)
|
|
else:
|
|
src_router_dict = router_dict
|
|
|
|
# Router with static routes is conflict with other routers
|
|
available_routers = []
|
|
conflict_routers = []
|
|
if src_router_dict['destinations'] != []:
|
|
conflict_routers = [r['id'] for r in router_list]
|
|
return (available_routers, conflict_routers)
|
|
|
|
subnets_qry = context.session.query(models_v2.Subnet).all()
|
|
conflict_cidr_set = []
|
|
for subnet in subnets_qry:
|
|
if subnet['id'] in src_router_dict['subnet_ids']:
|
|
conflict_cidr_set.append(subnet['cidr'])
|
|
if (src_router_dict['gateway'] is not None and
|
|
subnet['id'] == src_router_dict['gateway']):
|
|
conflict_cidr_set.append(subnet['cidr'])
|
|
conflict_ip_set = netaddr.IPSet(conflict_cidr_set)
|
|
# Check conflict router ids with gateway and interface
|
|
for r in router_list:
|
|
if r['destinations'] != []:
|
|
conflict_routers.append(r['id'])
|
|
else:
|
|
cidr_set = []
|
|
for subnet in subnets_qry:
|
|
if subnet['id'] in r['subnet_ids']:
|
|
cidr_set.append(subnet['cidr'])
|
|
ip_set = netaddr.IPSet(cidr_set)
|
|
if (src_router_dict['gateway'] is None or
|
|
r['gateway'] is None or
|
|
src_router_dict['gateway'] == r['gateway']):
|
|
if (conflict_ip_set & ip_set):
|
|
conflict_routers.append(r['id'])
|
|
else:
|
|
available_routers.append(r['id'])
|
|
else:
|
|
conflict_routers.append(r['id'])
|
|
|
|
return (available_routers, conflict_routers)
|
|
|
|
def _get_conflict_network_and_router_ids_by_intf(self, context, router_id):
|
|
"""Collect conflicting networks and routers based on interface ports.
|
|
Collect conflicting networks which has overlapping subnet attached
|
|
to another router.
|
|
Collect conflict routers which has overlap network attached to it.
|
|
Returns:
|
|
conflict_network_ids: networks which has overlapping ips
|
|
conflict_router_ids: routers which has overlapping interfaces
|
|
intf_num: interfaces number attached on the router
|
|
"""
|
|
conflict_network_ids = []
|
|
conflict_router_ids = []
|
|
ports_qry = context.session.query(models_v2.Port)
|
|
intf_ports = ports_qry.filter_by(
|
|
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF).all()
|
|
|
|
router_net_ids = list(
|
|
set([port['network_id'] for port in intf_ports
|
|
if port['device_id'] == router_id]))
|
|
if cfg.CONF.allow_overlapping_ips:
|
|
router_intf_ports = [port for port in intf_ports
|
|
if port['device_id'] == router_id]
|
|
subnet_ids = []
|
|
for port in router_intf_ports:
|
|
subnet_ids.append(port['fixed_ips'][0]['subnet_id'])
|
|
subnets_qry = context.session.query(models_v2.Subnet).all()
|
|
subnets = [subnet for subnet in subnets_qry
|
|
if subnet['id'] in subnet_ids]
|
|
conflict_network_ids.extend(
|
|
self.plugin._get_conflict_network_ids_by_overlapping(
|
|
context, subnets))
|
|
|
|
other_router_ports = [port for port in intf_ports
|
|
if port['device_id'] != router_id]
|
|
|
|
for port in other_router_ports:
|
|
if port['network_id'] in router_net_ids:
|
|
conflict_router_ids.append(port['device_id'])
|
|
|
|
conflict_router_ids = list(set(conflict_router_ids))
|
|
conflict_network_ids = list(set(conflict_network_ids))
|
|
intf_num = len(router_net_ids)
|
|
return (conflict_network_ids, conflict_router_ids, intf_num)
|
|
|
|
def _get_external_network_id_by_router(self, context, router_id):
|
|
"""Get router's external network id if it has."""
|
|
router = self.plugin.get_router(context, router_id)
|
|
ports_qry = context.session.query(models_v2.Port)
|
|
gw_ports = ports_qry.filter_by(
|
|
device_id=router_id,
|
|
device_owner=l3_db.DEVICE_OWNER_ROUTER_GW,
|
|
id=router['gw_port_id']).all()
|
|
|
|
if gw_ports:
|
|
return gw_ports[0]['network_id']
|
|
|
|
def _get_conflict_network_ids_by_ext_net(self, context, router_id):
|
|
"""Collect conflicting networks based on external network.
|
|
Collect conflicting networks which has overlapping subnet with the
|
|
router's external network
|
|
"""
|
|
conflict_network_ids = []
|
|
ext_net_id = self._get_external_network_id_by_router(context,
|
|
router_id)
|
|
if ext_net_id:
|
|
ext_net = self.plugin._get_network(context, ext_net_id)
|
|
if ext_net.subnets:
|
|
ext_subnet = ext_net.subnets[0]
|
|
if ext_subnet:
|
|
conflict_network_ids.extend(
|
|
self.plugin._get_conflict_network_ids_by_overlapping(
|
|
context, [ext_subnet]))
|
|
return conflict_network_ids
|
|
|
|
def _get_conflict_router_ids_by_ext_net(self, context,
|
|
conflict_network_ids):
|
|
"""Collect conflict routers based on its external network.
|
|
Collect conflict router if it has external network and the external
|
|
network is in conflict_network_ids
|
|
"""
|
|
ext_net_filters = {'router:external': [True]}
|
|
ext_nets = self.plugin.get_networks(
|
|
context.elevated(), filters=ext_net_filters)
|
|
ext_net_ids = [ext_net.get('id') for ext_net in ext_nets]
|
|
conflict_ext_net_ids = list(set(ext_net_ids) &
|
|
set(conflict_network_ids))
|
|
gw_ports_filter = {'network_id': conflict_ext_net_ids,
|
|
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_GW]}
|
|
ports_qry = context.session.query(models_v2.Port)
|
|
gw_ports = self.plugin._apply_filters_to_query(
|
|
ports_qry, models_v2.Port, gw_ports_filter).all()
|
|
return list(set([gw_port['device_id'] for gw_port in gw_ports]))
|
|
|
|
def _get_optional_and_conflict_router_ids_by_gw(self, context, router_id):
|
|
"""Collect conflict routers and optional routers based on GW port.
|
|
Collect conflict router if it has different external network,
|
|
else, collect optional router if it is not distributed and exclusive
|
|
Returns:
|
|
optional_router_ids: routers we can use its edge for the shared router.
|
|
conflict_router_ids: conflict routers which has different gateway
|
|
"""
|
|
ext_net_id = self._get_external_network_id_by_router(context,
|
|
router_id)
|
|
routers = context.session.query(l3_db.Router).all()
|
|
optional_router_ids = []
|
|
conflict_router_ids = []
|
|
|
|
if ext_net_id:
|
|
ports_qry = context.session.query(models_v2.Port)
|
|
all_gw_ports = ports_qry.filter_by(
|
|
device_owner=l3_db.DEVICE_OWNER_ROUTER_GW).all()
|
|
metadata_nets = nsxv_db.get_nsxv_internal_network(
|
|
context.session,
|
|
vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE)
|
|
metadata_net_ids = [metadata_net['network_id']
|
|
for metadata_net in metadata_nets]
|
|
# filter out metadata gw_ports
|
|
all_gw_ports = [gw_port for gw_port in all_gw_ports
|
|
if gw_port['network_id'] not in metadata_net_ids]
|
|
for gw_port in all_gw_ports:
|
|
if gw_port and gw_port['network_id'] != ext_net_id:
|
|
conflict_router_ids.append(gw_port['device_id'])
|
|
|
|
for router in routers:
|
|
router_res = {}
|
|
self.plugin._extend_nsx_router_dict(router_res, router)
|
|
if (router['id'] not in conflict_router_ids
|
|
and router_res.get('router_type') == 'shared'):
|
|
optional_router_ids.append(router['id'])
|
|
return optional_router_ids, conflict_router_ids
|
|
|
|
def _bind_router_on_available_edge(self, context, router_id, admin_state):
|
|
with locking.LockManager.get_lock("router", lock_file_prefix="bind-"):
|
|
conflict_network_ids, conflict_router_ids, intf_num = (
|
|
self._get_conflict_network_and_router_ids_by_intf(context,
|
|
router_id))
|
|
conflict_network_ids_by_ext_net = (
|
|
self._get_conflict_network_ids_by_ext_net(context, router_id))
|
|
conflict_network_ids.extend(conflict_network_ids_by_ext_net)
|
|
optional_router_ids, new_conflict_router_ids = (
|
|
self._get_available_and_conflicting_ids(context, router_id))
|
|
conflict_router_ids.extend(new_conflict_router_ids)
|
|
conflict_router_ids = list(set(conflict_router_ids))
|
|
|
|
new = self.edge_manager.bind_router_on_available_edge(
|
|
context, router_id, optional_router_ids,
|
|
conflict_router_ids, conflict_network_ids, intf_num)
|
|
# configure metadata service on the router.
|
|
metadata_proxy_handler = self.plugin.metadata_proxy_handler
|
|
if metadata_proxy_handler and new:
|
|
metadata_proxy_handler.configure_router_edge(router_id)
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
# add all internal interfaces of the router on edge
|
|
intf_net_ids = (
|
|
self.plugin._get_internal_network_ids_by_router(context,
|
|
router_id))
|
|
for network_id in intf_net_ids:
|
|
address_groups = self.plugin._get_address_groups(
|
|
context, router_id, network_id)
|
|
edge_utils.update_internal_interface(
|
|
self.nsx_v, context, router_id, network_id,
|
|
address_groups, admin_state)
|
|
|
|
def _unbind_router_on_edge(self, context, router_id):
|
|
self.edge_manager.unbind_router_on_edge(context, router_id)
|
|
metadata_proxy_handler = self.plugin.metadata_proxy_handler
|
|
if metadata_proxy_handler:
|
|
metadata_proxy_handler.cleanup_router_edge(router_id)
|
|
|
|
def _add_router_services_on_available_edge(self, context, router_id):
|
|
router_ids = self.edge_manager.get_routers_on_same_edge(
|
|
context, router_id)
|
|
self._update_external_interface_on_routers(
|
|
context, router_id, router_ids)
|
|
self._update_routes_on_routers(context, router_id, router_ids)
|
|
self._update_nat_rules_on_routers(context, router_id, router_ids)
|
|
self._update_subnets_and_dnat_firewall_on_routers(
|
|
context, router_id, router_ids, allow_external=True)
|
|
|
|
def _remove_router_services_on_edge(self, context, router_id,
|
|
intf_net_id=None):
|
|
router_ids = self.edge_manager.get_routers_on_same_edge(
|
|
context, router_id)
|
|
router_ids.remove(router_id)
|
|
# Refresh firewall, nats, ext_vnic as well as static routes
|
|
self._update_routes_on_routers(context, router_id, router_ids)
|
|
self._update_subnets_and_dnat_firewall_on_routers(
|
|
context, router_id, router_ids, allow_external=True)
|
|
self._update_nat_rules_on_routers(context, router_id, router_ids)
|
|
self._update_external_interface_on_routers(
|
|
context, router_id, router_ids)
|
|
intf_net_ids = (
|
|
self.plugin._get_internal_network_ids_by_router(context,
|
|
router_id))
|
|
if intf_net_id:
|
|
intf_net_ids.remove(intf_net_id)
|
|
for net_id in intf_net_ids:
|
|
edge_utils.delete_interface(self.nsx_v, context, router_id, net_id,
|
|
is_wait=False)
|
|
|
|
def _update_router_gw_info(self, context, router_id, info,
|
|
is_routes_update=False):
|
|
router = self.plugin._get_router(context, router_id)
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
if not edge_id:
|
|
super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info(
|
|
context, router_id, info, router=router)
|
|
# UPDATE gw info only if the router has been attached to an edge
|
|
else:
|
|
is_migrated = False
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
router_ids = self.edge_manager.get_routers_on_same_edge(
|
|
context, router_id)
|
|
org_ext_net_id = (router.gw_port_id and
|
|
router.gw_port.network_id)
|
|
org_enable_snat = router.enable_snat
|
|
orgaddr, orgmask, orgnexthop = (
|
|
self.plugin._get_external_attachment_info(
|
|
context, router))
|
|
super(nsx_v.NsxVPluginV2, self.plugin)._update_router_gw_info(
|
|
context, router_id, info, router=router)
|
|
new_ext_net_id = (router.gw_port_id and
|
|
router.gw_port.network_id)
|
|
new_enable_snat = router.enable_snat
|
|
newaddr, newmask, newnexthop = (
|
|
self.plugin._get_external_attachment_info(
|
|
context, router))
|
|
if new_ext_net_id and new_ext_net_id != org_ext_net_id:
|
|
# Check whether the gw address has overlapping
|
|
# with networks attached to the same edge
|
|
conflict_network_ids = (
|
|
self._get_conflict_network_ids_by_ext_net(
|
|
context, router_id))
|
|
is_migrated = self.edge_manager.is_router_conflict_on_edge(
|
|
context, router_id, [], conflict_network_ids)
|
|
if is_migrated:
|
|
self._remove_router_services_on_edge(context,
|
|
router_id)
|
|
self._unbind_router_on_edge(context, router_id)
|
|
|
|
if not is_migrated:
|
|
ext_net_ids = self._get_ext_net_ids(context, router_ids)
|
|
if len(ext_net_ids) > 1:
|
|
# move all routing service of the router from existing
|
|
# edge to a new available edge if new_ext_net_id is
|
|
# changed.
|
|
self._remove_router_services_on_edge(context,
|
|
router_id)
|
|
self._unbind_router_on_edge(context, router_id)
|
|
is_migrated = True
|
|
else:
|
|
# Clear gateway info if all routers has no gw conf
|
|
if (orgnexthop and
|
|
(org_ext_net_id != new_ext_net_id or
|
|
len(ext_net_ids) == 0)):
|
|
LOG.debug("Delete default gateway %s", orgnexthop)
|
|
edge_utils.clear_gateway(self.nsx_v, context,
|
|
router_id)
|
|
|
|
# Update external vnic if addr or mask is changed
|
|
if orgaddr != newaddr or orgmask != newmask:
|
|
self._update_external_interface_on_routers(
|
|
context, router_id, router_ids)
|
|
|
|
# Update SNAT rules if ext net changed
|
|
# or ext net not changed but snat is changed.
|
|
if ((new_ext_net_id != org_ext_net_id) or
|
|
(new_ext_net_id == org_ext_net_id and
|
|
new_enable_snat != org_enable_snat)):
|
|
self._update_nat_rules_on_routers(context,
|
|
router_id,
|
|
router_ids)
|
|
|
|
if (new_ext_net_id != org_ext_net_id or
|
|
new_enable_snat != org_enable_snat or
|
|
is_routes_update):
|
|
self._update_subnets_and_dnat_firewall_on_routers(
|
|
context, router_id, router_ids,
|
|
allow_external=True)
|
|
|
|
# Update static routes in all.
|
|
self._update_routes_on_routers(
|
|
context, router_id, router_ids)
|
|
if is_migrated:
|
|
self._bind_router_on_available_edge(
|
|
context, router_id, router.admin_state_up)
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
self._add_router_services_on_available_edge(context,
|
|
router_id)
|
|
|
|
def _base_add_router_interface(self, context, router_id, interface_info):
|
|
with locking.LockManager.get_lock("router", lock_file_prefix="bind-",
|
|
external=True):
|
|
return super(nsx_v.NsxVPluginV2, self.plugin).add_router_interface(
|
|
context, router_id, interface_info)
|
|
|
|
def add_router_interface(self, context, router_id, interface_info):
|
|
self.plugin._check_intf_number_of_router(context, router_id)
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
router_db = self.plugin._get_router(context, router_id)
|
|
if edge_id:
|
|
is_migrated = False
|
|
with locking.LockManager.get_lock("router",
|
|
lock_file_prefix="bind-",
|
|
external=True):
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG,
|
|
external=True):
|
|
router_ids = self.edge_manager.get_routers_on_same_edge(
|
|
context, router_id)
|
|
info = super(nsx_v.NsxVPluginV2,
|
|
self.plugin).add_router_interface(
|
|
context, router_id, interface_info)
|
|
subnet = self.plugin.get_subnet(context, info['subnet_id'])
|
|
network_id = subnet['network_id']
|
|
# Collect all conflict networks whose cidr are overlapped
|
|
# with networks attached to the router and conflict routers
|
|
# which has same network with the router's.
|
|
conflict_network_ids, conflict_router_ids, _ = (
|
|
self._get_conflict_network_and_router_ids_by_intf(
|
|
context, router_id))
|
|
|
|
_, new_conflict_router_ids = (
|
|
self._get_available_and_conflicting_ids(context,
|
|
router_id))
|
|
conflict_router_ids.extend(new_conflict_router_ids)
|
|
conflict_router_ids = list(set(conflict_router_ids))
|
|
|
|
interface_ports = (
|
|
self.plugin._get_router_interface_ports_by_network(
|
|
context, router_id, network_id))
|
|
# Consider whether another subnet of the same network
|
|
# has been attached to the router.
|
|
if len(interface_ports) > 1:
|
|
is_conflict = (
|
|
self.edge_manager.is_router_conflict_on_edge(
|
|
context, router_id, conflict_router_ids,
|
|
conflict_network_ids, 0))
|
|
else:
|
|
is_conflict = (
|
|
self.edge_manager.is_router_conflict_on_edge(
|
|
context, router_id, conflict_router_ids,
|
|
conflict_network_ids, 1))
|
|
if is_conflict:
|
|
if len(interface_ports) > 1:
|
|
self._remove_router_services_on_edge(
|
|
context, router_id)
|
|
else:
|
|
self._remove_router_services_on_edge(
|
|
context, router_id, network_id)
|
|
self._unbind_router_on_edge(context, router_id)
|
|
is_migrated = True
|
|
else:
|
|
address_groups = self.plugin._get_address_groups(
|
|
context, router_id, network_id)
|
|
edge_utils.update_internal_interface(
|
|
self.nsx_v, context, router_id,
|
|
network_id, address_groups,
|
|
router_db.admin_state_up)
|
|
if router_db.gw_port and router_db.enable_snat:
|
|
self._update_nat_rules_on_routers(
|
|
context, router_id, router_ids)
|
|
self._update_subnets_and_dnat_firewall_on_routers(
|
|
context, router_id, router_ids,
|
|
allow_external=True)
|
|
if is_migrated:
|
|
self._bind_router_on_available_edge(
|
|
context, router_id, router_db.admin_state_up)
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
self._add_router_services_on_available_edge(context,
|
|
router_id)
|
|
else:
|
|
info = self._base_add_router_interface(context, router_id,
|
|
interface_info)
|
|
# bind and configure routing service on an available edge
|
|
self._bind_router_on_available_edge(
|
|
context, router_id, router_db.admin_state_up)
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
self._add_router_services_on_available_edge(context,
|
|
router_id)
|
|
return info
|
|
|
|
def remove_router_interface(self, context, router_id, interface_info):
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
info = super(
|
|
nsx_v.NsxVPluginV2, self.plugin).remove_router_interface(
|
|
context, router_id, interface_info)
|
|
subnet = self.plugin.get_subnet(context, info['subnet_id'])
|
|
network_id = subnet['network_id']
|
|
router_ids = self.edge_manager.get_routers_on_same_edge(
|
|
context, router_id)
|
|
self._update_nat_rules_on_routers(context, router_id, router_ids)
|
|
self._update_subnets_and_dnat_firewall_on_routers(
|
|
context, router_id, router_ids, allow_external=True)
|
|
ports = self.plugin._get_router_interface_ports_by_network(
|
|
context, router_id, network_id)
|
|
if not ports:
|
|
edge_utils.delete_interface(self.nsx_v, context,
|
|
router_id, network_id)
|
|
# unbind all services if no interfaces attached to the router
|
|
if not self.plugin._get_internal_network_ids_by_router(
|
|
context, router_id):
|
|
self._remove_router_services_on_edge(context, router_id)
|
|
self._unbind_router_on_edge(context, router_id)
|
|
else:
|
|
address_groups = self.plugin._get_address_groups(
|
|
context, router_id, network_id)
|
|
edge_utils.update_internal_interface(
|
|
self.nsx_v, context, router_id, network_id, address_groups)
|
|
return info
|
|
|
|
def _update_edge_router(self, context, router_id):
|
|
edge_id = edge_utils.get_router_edge_id(context, router_id)
|
|
with locking.LockManager.get_lock(
|
|
str(edge_id),
|
|
lock_file_prefix=NSXV_ROUTER_RECONFIG):
|
|
router_ids = self.edge_manager.get_routers_on_same_edge(
|
|
context, router_id)
|
|
if router_ids:
|
|
self._update_external_interface_on_routers(
|
|
context, router_id, router_ids)
|
|
self._update_nat_rules_on_routers(
|
|
context, router_id, router_ids)
|
|
self._update_subnets_and_dnat_firewall_on_routers(
|
|
context, router_id, router_ids, allow_external=True)
|