Merge "NSXv BGP support"

This commit is contained in:
Jenkins 2017-04-15 12:28:48 +00:00 committed by Gerrit Code Review
commit 232fb88221
17 changed files with 1454 additions and 2 deletions

View File

@ -107,6 +107,19 @@ Add neutron-fwaas repo as an external repository and configure following flags i
driver = vmware_nsx.services.fwaas.nsx_v.edge_fwaas_driver.EdgeFwaasDriver
Neutron dynamic routing plugin (bgp)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Add neutron-dynamic-routing repo as an external repository and configure following flags in ``local.conf``::
[[local|localrc]]
enable_plugin neutron-dynamic-routing https://git.openstack.org/openstack/neutron-dynamic-routing
DR_MODE=dr_plugin
[[post-config|$NEUTRON_CONF]]
[DEFAULT]
api_extensions_path = $DEST/neutron-dynamic-routing/neutron_dynamic_routing/extensions
NSXv3
-----

View File

@ -9,6 +9,7 @@ ${DIR}/tox_install_project.sh networking-sfc networking_sfc $*
${DIR}/tox_install_project.sh neutron-lbaas neutron_lbaas $*
${DIR}/tox_install_project.sh vmware-nsxlib vmware_nsxlib $*
${DIR}/tox_install_project.sh neutron-fwaas neutron_fwaas $*
${DIR}/tox_install_project.sh neutron-dynamic-routing neutron-dynamic-routing $*
CONSTRAINTS_FILE=$1
shift

View File

@ -1 +1 @@
7c4704ad37df
8699700cd95c

View File

@ -0,0 +1,51 @@
# Copyright 2017 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.
"""nsxv_bgp_speaker_mapping
Revision ID: 8699700cd95c
Revises: 7c4704ad37df
Create Date: 2017-02-16 03:13:39.775670
"""
# revision identifiers, used by Alembic.
revision = '8699700cd95c'
down_revision = '7c4704ad37df'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table(
'nsxv_bgp_speaker_bindings',
sa.Column('edge_id', sa.String(36), nullable=False),
sa.Column('bgp_speaker_id', sa.String(36), nullable=False),
sa.Column('bgp_identifier', sa.String(64), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['bgp_speaker_id'], ['bgp_speakers.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('edge_id'))
op.create_table(
'nsxv_bgp_peer_edge_bindings',
sa.Column('peer_id', sa.String(36), nullable=False),
sa.Column('edge_id', sa.String(36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['peer_id'], ['bgp_peers.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('peer_id'))

View File

@ -894,3 +894,56 @@ def update_nsxv_port_ext_attributes(session, port_id,
except exc.NoResultFound:
return add_nsxv_port_ext_attributes(
session, port_id, vnic_type=vnic_type)
def add_nsxv_bgp_speaker_binding(session, edge_id, speaker_id,
bgp_identifier):
with session.begin(subtransactions=True):
binding = nsxv_models.NsxvBgpSpeakerBinding(
edge_id=edge_id,
bgp_speaker_id=speaker_id,
bgp_identifier=bgp_identifier)
session.add(binding)
return binding
def get_nsxv_bgp_speaker_binding(session, edge_id):
try:
binding = (session.query(nsxv_models.NsxvBgpSpeakerBinding).
filter_by(edge_id=edge_id).
one())
return binding
except exc.NoResultFound:
LOG.debug("No dynamic routing enabled on edge %s.", edge_id)
def get_nsxv_bgp_speaker_bindings(session, speaker_id):
try:
return (session.query(nsxv_models.NsxvBgpSpeakerBinding).
filter_by(bgp_speaker_id=speaker_id).all())
except exc.NoResultFound:
return []
def delete_nsxv_bgp_speaker_binding(session, edge_id):
binding = session.query(
nsxv_models.NsxvBgpSpeakerBinding).filter_by(edge_id=edge_id)
if binding:
binding.delete()
def add_nsxv_bgp_peer_edge_binding(session, peer_id, edge_id):
with session.begin(subtransactions=True):
binding = nsxv_models.NsxvBgpPeerEdgeBinding(edge_id=edge_id,
peer_id=peer_id)
session.add(binding)
return binding
def get_nsxv_bgp_peer_edge_binding(session, peer_id):
try:
binding = (session.query(nsxv_models.NsxvBgpPeerEdgeBinding).
filter_by(peer_id=peer_id).one())
return binding
except exc.NoResultFound:
pass

View File

@ -381,3 +381,28 @@ class NsxvPortExtAttributes(model_base.BASEV2, models.TimestampMixin):
models_v2.Port,
backref=orm.backref("nsx_port_attributes", lazy='joined',
uselist=False, cascade='delete'))
class NsxvBgpSpeakerBinding(model_base.BASEV2, models.TimestampMixin):
# Maps bgp_speaker_id to NSXv edge id
__tablename__ = 'nsxv_bgp_speaker_bindings'
edge_id = sa.Column(sa.String(36), primary_key=True)
bgp_speaker_id = sa.Column(sa.String(36),
sa.ForeignKey('bgp_speakers.id',
ondelete='CASCADE'),
nullable=False)
# A given BGP speaker sets the value of its BGP Identifier to an IP address
# that is assigned to that BGP speaker.
bgp_identifier = sa.Column(sa.String(64), nullable=False)
class NsxvBgpPeerEdgeBinding(model_base.BASEV2, models.TimestampMixin):
# Maps between bgp-peer and edges service gateway.
__tablename__ = 'nsxv_bgp_peer_edge_bindings'
peer_id = sa.Column(sa.String(36),
sa.ForeignKey('bgp_peers.id',
ondelete='CASCADE'),
primary_key=True,
nullable=False)
edge_id = sa.Column(sa.String(36), nullable=False)

View File

@ -0,0 +1,98 @@
# Copyright 2017 VMware, Inc. 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.
import re
from neutron_lib.api import extensions
from neutron_lib.api import validators
from neutron_lib import exceptions as nexception
from vmware_nsx._i18n import _
EDGE_SERVICE_GW = 'esg_id'
ESG_BGP_PEER_EXT_ALIAS = 'edge-service-gateway-bgp-peer'
def _validate_edge_service_gw_id(esg_id, valid_values=None):
if re.match(r'^edge-[1-9]+[0-9]*$', esg_id) is None:
msg = _("'%s' is not a valid edge service gateway id.") % esg_id
return msg
validators.add_validator('validate_edge_service_gw_id',
_validate_edge_service_gw_id)
RESOURCE_ATTRIBUTE_MAP = {
'bgp-peers': {
EDGE_SERVICE_GW: {
'allow_post': True,
'allow_put': False,
'default': None,
'validate': {'type:validate_edge_service_gw_id': None},
'enforce_policy': True,
'is_visible': True,
'required_by_policy': False
}
}
}
class BgpDisabledOnEsgPeer(nexception.InvalidInput):
message = _("To add this peer to BGP speaker you must first enable BGP on "
"the associated ESG - '%(esg_id)s'.")
class EsgRemoteASDoNotMatch(nexception.InvalidInput):
message = _("Specified remote AS is '%(remote_as)s', but ESG '%(esg_id)s' "
"is configured on AS %(esg_as)s.")
class ExternalSubnetHasGW(nexception.InvalidInput):
message = _("Subnet '%(subnet_id)s' on external network '%(network_id)s' "
"is configured with gateway IP, set to None before enabling "
"BGP on the network.")
class Edge_service_gateway_bgp_peer(extensions.ExtensionDescriptor):
"""Extension class to allow identifying of-peer with specificN SXv edge
service gateway.
"""
@classmethod
def get_name(cls):
return "Edge service gateway bgp peer"
@classmethod
def get_alias(cls):
return ESG_BGP_PEER_EXT_ALIAS
@classmethod
def get_description(cls):
return ("Adding a new (optional) attribute 'esg_id' to bgp-peer "
"resource, where esg_id is a valid NSXv Edge service gateway "
"id.")
@classmethod
def get_updated(cls):
return "2017-04-01T10:00:00-00:00"
def get_required_extensions(self):
return ["bgp"]
def get_extended_resources(self, version):
if version == "2.0":
return RESOURCE_ATTRIBUTE_MAP
else:
return {}

View File

@ -0,0 +1,200 @@
# Copyright 2017 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.
from oslo_log import log as logging
from vmware_nsx.common import locking
LOG = logging.getLogger(__name__)
class EdgeDynamicRoutingDriver(object):
"""Edge driver API to implement the dynamic routing"""
def __init__(self):
# it will be initialized at subclass
self.vcns = None
def _get_routing_global_config(self, edge_id):
h, config = self.vcns.get_dynamic_routing_service(edge_id)
global_config = config if config else {}
global_config.setdefault('ipPrefixes', {'ipPrefixes': []})
curr_prefixes = [{'ipPrefix': prx}
for prx in global_config['ipPrefixes']['ipPrefixes']]
global_config['ipPrefixes'] = curr_prefixes
return {'routingGlobalConfig': global_config}
def _update_global_routing_config(self, edge_id, **kwargs):
global_config = self._get_routing_global_config(edge_id)
current_prefixes = global_config['routingGlobalConfig']['ipPrefixes']
global_config['routingGlobalConfig']['ecmp'] = True
if 'router_id' in kwargs:
global_config['routingGlobalConfig']['routerId'] = (
kwargs['router_id'])
current_prefixes[:] = [p for p in current_prefixes
if p['ipPrefix']['name'] not in
kwargs.get('prefixes_to_remove', [])]
# Avoid adding duplicate rules when shared router relocation
current_prefixes.extend([p for p in kwargs.get('prefixes_to_add', [])
if p not in current_prefixes])
self.vcns.update_dynamic_routing_service(edge_id, global_config)
def _reset_routing_global_config(self, edge_id):
global_config = self._get_routing_global_config(edge_id)
global_config['routingGlobalConfig']['ecmp'] = False
global_config['routingGlobalConfig'].pop('routerId', None)
global_config['routingGlobalConfig'].pop('ipPrefixes', None)
self.vcns.update_dynamic_routing_service(edge_id, global_config)
def get_routing_bgp_config(self, edge_id):
h, config = self.vcns.get_bgp_routing_config(edge_id)
bgp_config = config if config else {}
bgp_config.setdefault('enabled', False)
bgp_config.setdefault('bgpNeighbours', {'bgpNeighbours': []})
bgp_config.setdefault('redistribution', {'rules': {'rules': []}})
curr_neighbours = [{'bgpNeighbour': nbr} for nbr in
bgp_config['bgpNeighbours']['bgpNeighbours']]
bgp_config['bgpNeighbours'] = curr_neighbours
for nbr in curr_neighbours:
bgp_filters = [{'bgpFilter': bf} for bf
in nbr['bgpNeighbour']['bgpFilters']['bgpFilters']]
nbr['bgpNeighbour']['bgpFilters'] = bgp_filters
redistribution_rules = [{'rule': rule} for rule in
bgp_config['redistribution']['rules']['rules']]
bgp_config['redistribution']['rules'] = redistribution_rules
return {'bgp': bgp_config}
def _update_bgp_routing_config(self, edge_id, **kwargs):
bgp_config = self.get_routing_bgp_config(edge_id)
curr_neighbours = bgp_config['bgp']['bgpNeighbours']
curr_rules = bgp_config['bgp']['redistribution']['rules']
bgp_config['bgp']['enabled'] = True
if 'local_as' in kwargs:
bgp_config['bgp']['localAS'] = kwargs['local_as']
if 'enabled' in kwargs:
bgp_config['bgp']['redistribution']['enabled'] = kwargs['enabled']
curr_rules[:] = [rule for rule in curr_rules
if rule['rule'].get('prefixName') not in
kwargs.get('rules_to_remove', [])]
# Avoid adding duplicate rules when shared router relocation
curr_rules_prefixes = [r['rule'].get('prefixName') for r in curr_rules]
curr_rules.extend([r for r in kwargs.get('rules_to_add', [])
if r['rule'].get('prefixName') not in
curr_rules_prefixes])
neighbours_to_remove = [nbr['bgpNeighbour']['ipAddress'] for nbr in
kwargs.get('neighbours_to_remove', [])]
curr_neighbours[:] = [nbr for nbr in curr_neighbours
if nbr['bgpNeighbour']['ipAddress']
not in neighbours_to_remove]
curr_neighbours.extend(kwargs.get('neighbours_to_add', []))
self.vcns.update_bgp_dynamic_routing(edge_id, bgp_config)
def _get_edge_static_routes(self, edge_id):
h, routes = self.vcns.get_routes(edge_id)
static_routes = routes if routes else {}
static_routes.setdefault('staticRoutes', {'staticRoutes': []})
return static_routes
def _remove_default_static_routes(self, edge_id, droutes=None):
routes = self._get_edge_static_routes(edge_id)
# if droutes is empty or None, then remove all default static routes
droutes = droutes or routes['staticRoutes']['staticRoutes']
droutes = [r['nextHop'] for r in droutes]
routes['staticRoutes']['staticRoutes'] = [
r for r in routes['staticRoutes']['staticRoutes']
if r['network'] != '0.0.0.0/0' or r['nextHop'] not in droutes]
self.vcns.update_routes(edge_id, routes)
def _add_default_static_routes(self, edge_id, default_routes):
routes = self._get_edge_static_routes(edge_id)
routes['staticRoutes']['staticRoutes'].extend(default_routes)
self.vcns.update_routes(edge_id, routes)
def add_bgp_speaker_config(self, edge_id, prot_router_id, local_as,
enabled, default_routes, bgp_neighbours,
prefixes, redistribution_rules):
with locking.LockManager.get_lock(str(edge_id)):
self._update_global_routing_config(edge_id,
router_id=prot_router_id,
prefixes_to_add=prefixes)
self._update_bgp_routing_config(edge_id, enabled=enabled,
local_as=local_as,
neighbours_to_add=bgp_neighbours,
prefixes_to_add=prefixes,
rules_to_add=redistribution_rules)
self._add_default_static_routes(edge_id, default_routes)
def delete_bgp_speaker_config(self, edge_id):
with locking.LockManager.get_lock(str(edge_id)):
self.vcns.delete_bgp_routing_config(edge_id)
self._remove_default_static_routes(edge_id)
self._reset_routing_global_config(edge_id)
def add_bgp_neighbours(self, edge_id, bgp_neighbours,
default_routes=None):
# Query the bgp config first and update the bgpNeighbour
with locking.LockManager.get_lock(str(edge_id)):
self._update_bgp_routing_config(edge_id,
neighbours_to_add=bgp_neighbours)
if default_routes:
self._add_default_static_routes(edge_id, default_routes)
def remove_bgp_neighbours(self, edge_id, bgp_neighbours,
default_routes=None):
with locking.LockManager.get_lock(str(edge_id)):
self._update_bgp_routing_config(
edge_id, neighbours_to_remove=bgp_neighbours)
if default_routes:
self._remove_default_static_routes(edge_id, default_routes)
def update_bgp_neighbour(self, edge_id, bgp_neighbour):
with locking.LockManager.get_lock(str(edge_id)):
self._update_bgp_routing_config(
edge_id,
neighbours_to_remove=[bgp_neighbour],
neighbours_to_add=[bgp_neighbour])
def update_routing_redistribution(self, edge_id, enabled):
with locking.LockManager.get_lock(str(edge_id)):
self._update_bgp_routing_config(edge_id, enabled=enabled)
def add_bgp_redistribution_rules(self, edge_id, prefixes, rules):
with locking.LockManager.get_lock(str(edge_id)):
self._update_global_routing_config(edge_id,
prefixes_to_add=prefixes)
self._update_bgp_routing_config(edge_id, rules_to_add=rules)
LOG.debug("Added redistribution rules %s on edge %s", rules, edge_id)
def remove_bgp_redistribution_rules(self, edge_id, prefixes):
with locking.LockManager.get_lock(str(edge_id)):
self._update_bgp_routing_config(edge_id, rules_to_remove=prefixes)
self._update_global_routing_config(edge_id,
prefixes_to_remove=prefixes)
LOG.debug("Removed redistribution rules for prefixes %s on edge %s",
prefixes, edge_id)
def update_router_id(self, edge_id, router_id):
with locking.LockManager.get_lock(str(edge_id)):
self._update_global_routing_config(edge_id, router_id=router_id)

View File

@ -83,6 +83,10 @@ CERTIFICATE = "certificate"
NETWORK_TYPES = ['Network', 'VirtualWire', 'DistributedVirtualPortgroup']
# Dynamic routing constants
GLOBAL_ROUTING_CONFIG = "routing/config/global"
BGP_ROUTING_CONFIG = "routing/config/bgp"
def retry_upon_exception_exclude_error_codes(
exc, excluded_errors, delay=0.5, max_delay=4, max_attempts=0):
@ -1065,3 +1069,27 @@ class Vcns(object):
uri = '%s/scope/globalroot-0' % APPLICATION_PREFIX
h, apps = self.do_request(HTTP_GET, uri, decode=True)
return apps
def update_dynamic_routing_service(self, edge_id, request_config):
uri = self._build_uri_path(edge_id, GLOBAL_ROUTING_CONFIG)
return self.do_request(HTTP_PUT, uri,
VcnsApiClient.xmldumps(request_config),
format='xml')
def get_dynamic_routing_service(self, edge_id):
uri = self._build_uri_path(edge_id, GLOBAL_ROUTING_CONFIG)
return self.do_request(HTTP_GET, uri)
def update_bgp_dynamic_routing(self, edge_id, bgp_request):
uri = self._build_uri_path(edge_id, BGP_ROUTING_CONFIG)
return self.do_request(HTTP_PUT, uri,
VcnsApiClient.xmldumps(bgp_request),
format='xml')
def get_bgp_routing_config(self, edge_id):
uri = self._build_uri_path(edge_id, BGP_ROUTING_CONFIG)
return self.do_request(HTTP_GET, uri)
def delete_bgp_routing_config(self, edge_id):
uri = self._build_uri_path(edge_id, BGP_ROUTING_CONFIG)
return self.do_request(HTTP_DELETE, uri)

View File

@ -18,6 +18,7 @@ from oslo_config import cfg
from oslo_log import log as logging
from vmware_nsx.plugins.nsx_v.vshield import edge_appliance_driver
from vmware_nsx.plugins.nsx_v.vshield import edge_dynamic_routing_driver
from vmware_nsx.plugins.nsx_v.vshield import edge_firewall_driver
from vmware_nsx.plugins.nsx_v.vshield.tasks import tasks
from vmware_nsx.plugins.nsx_v.vshield import vcns
@ -29,7 +30,8 @@ LOG = logging.getLogger(__name__)
class VcnsDriver(edge_appliance_driver.EdgeApplianceDriver,
lbaas_v2.EdgeLoadbalancerDriverV2,
edge_firewall_driver.EdgeFirewallDriver):
edge_firewall_driver.EdgeFirewallDriver,
edge_dynamic_routing_driver.EdgeDynamicRoutingDriver):
def __init__(self, callbacks):
super(VcnsDriver, self).__init__()

View File

@ -0,0 +1,256 @@
# Copyright 2017 VMware, Inc.
#
# 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 oslo_log import log as logging
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron_dynamic_routing.db import bgp_db
from neutron_dynamic_routing.extensions import bgp as bgp_ext
from neutron_lib import context as n_context
from neutron_lib.services import base as service_base
from vmware_nsx.common import locking
from vmware_nsx.common import nsxv_constants
from vmware_nsx.db import nsxv_db
from vmware_nsx.extensions import edge_service_gateway_bgp_peer as ext_esg
from vmware_nsx.services.dynamic_routing.nsx_v import driver as nsxv_driver
PLUGIN_NAME = bgp_ext.BGP_EXT_ALIAS + '_nsx_svc_plugin'
LOG = logging.getLogger(__name__)
class NSXvBgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin):
supported_extension_aliases = [bgp_ext.BGP_EXT_ALIAS,
ext_esg.ESG_BGP_PEER_EXT_ALIAS]
def __init__(self):
super(NSXvBgpPlugin, self).__init__()
self.nsxv_driver = nsxv_driver.NSXvBgpDriver(self)
self._register_callbacks()
def get_plugin_name(self):
return PLUGIN_NAME
def get_plugin_type(self):
return bgp_ext.BGP_EXT_ALIAS
def get_plugin_description(self):
"""returns string description of the plugin."""
return ("BGP dynamic routing service for announcement of next-hops "
"for project networks, floating IP's, and DVR host routes.")
def _register_callbacks(self):
registry.subscribe(self.router_interface_callback,
resources.ROUTER_INTERFACE,
events.AFTER_CREATE)
registry.subscribe(self.router_interface_callback,
resources.ROUTER_INTERFACE,
events.AFTER_DELETE)
registry.subscribe(self.router_gateway_callback,
resources.ROUTER_GATEWAY,
events.AFTER_CREATE)
registry.subscribe(self.router_gateway_callback,
resources.ROUTER_GATEWAY,
events.AFTER_DELETE)
registry.subscribe(self._after_service_edge_create_callback,
nsxv_constants.SERVICE_EDGE,
events.AFTER_CREATE)
registry.subscribe(self._before_service_edge_delete_callback,
nsxv_constants.SERVICE_EDGE,
events.BEFORE_DELETE)
def create_bgp_speaker(self, context, bgp_speaker):
self.nsxv_driver.create_bgp_speaker(context, bgp_speaker)
return super(NSXvBgpPlugin, self).create_bgp_speaker(context,
bgp_speaker)
def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
self.nsxv_driver.update_bgp_speaker(context, bgp_speaker_id,
bgp_speaker)
# TBD(roeyc): rolling back changes on edges base class call failed.
return super(NSXvBgpPlugin, self).update_bgp_speaker(
context, bgp_speaker_id, bgp_speaker)
def delete_bgp_speaker(self, context, bgp_speaker_id):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
self.nsxv_driver.delete_bgp_speaker(context, bgp_speaker_id)
super(NSXvBgpPlugin, self).delete_bgp_speaker(context,
bgp_speaker_id)
def _get_esg_peer_info(self, context, bgp_peer_id):
binding = nsxv_db.get_nsxv_bgp_peer_edge_binding(context.session,
bgp_peer_id)
if binding:
return binding['edge_id']
def get_bgp_peer(self, context, bgp_peer_id, fields=None):
peer = super(NSXvBgpPlugin, self).get_bgp_peer(context,
bgp_peer_id, fields)
if fields is None or 'esg_id' in fields:
peer['esg_id'] = self._get_esg_peer_info(context, bgp_peer_id)
return peer
def get_bgp_peers_by_bgp_speaker(self, context,
bgp_speaker_id, fields=None):
ret = super(NSXvBgpPlugin, self).get_bgp_peers_by_bgp_speaker(
context, bgp_speaker_id, fields=fields)
if fields is None or 'esg_id' in fields:
for peer in ret:
peer['esg_id'] = self._get_esg_peer_info(context, peer['id'])
return ret
def create_bgp_peer(self, context, bgp_peer):
self.nsxv_driver.create_bgp_peer(context, bgp_peer)
peer = super(NSXvBgpPlugin, self).create_bgp_peer(context, bgp_peer)
esg_id = bgp_peer['bgp_peer'].get('esg_id')
if esg_id:
nsxv_db.add_nsxv_bgp_peer_edge_binding(context.session, peer['id'],
esg_id)
peer['esg_id'] = esg_id
return peer
def update_bgp_peer(self, context, bgp_peer_id, bgp_peer):
super(NSXvBgpPlugin, self).update_bgp_peer(context,
bgp_peer_id, bgp_peer)
self.nsxv_driver.update_bgp_peer(context, bgp_peer_id, bgp_peer)
return self.get_bgp_peer(context, bgp_peer_id)
def delete_bgp_peer(self, context, bgp_peer_id):
bgp_peer_info = {'bgp_peer_id': bgp_peer_id}
bgp_speaker_ids = self.nsxv_driver._get_bgp_speakers_by_bgp_peer(
context, bgp_peer_id)
for speaker_id in bgp_speaker_ids:
self.remove_bgp_peer(context, speaker_id, bgp_peer_info)
super(NSXvBgpPlugin, self).delete_bgp_peer(context, bgp_peer_id)
def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
self.nsxv_driver.add_bgp_peer(context,
bgp_speaker_id, bgp_peer_info)
return super(NSXvBgpPlugin, self).add_bgp_peer(context,
bgp_speaker_id,
bgp_peer_info)
def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
speaker = self._get_bgp_speaker(context, bgp_speaker_id)
if bgp_peer_info['bgp_peer_id'] not in speaker['peers']:
return
self.nsxv_driver.remove_bgp_peer(context,
bgp_speaker_id, bgp_peer_info)
return super(NSXvBgpPlugin, self).remove_bgp_peer(
context, bgp_speaker_id, bgp_peer_info)
def add_gateway_network(self, context, bgp_speaker_id, network_info):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
self.nsxv_driver.add_gateway_network(context,
bgp_speaker_id,
network_info)
return super(NSXvBgpPlugin, self).add_gateway_network(
context, bgp_speaker_id, network_info)
def remove_gateway_network(self, context, bgp_speaker_id, network_info):
with locking.LockManager.get_lock(str(bgp_speaker_id)):
super(NSXvBgpPlugin, self).remove_gateway_network(
context, bgp_speaker_id, network_info)
self.nsxv_driver.remove_gateway_network(context,
bgp_speaker_id,
network_info)
def get_advertised_routes(self, context, bgp_speaker_id):
return super(NSXvBgpPlugin, self).get_advertised_routes(
context, bgp_speaker_id)
def router_interface_callback(self, resource, event, trigger, **kwargs):
if not kwargs['network_id']:
# No GW network, hence no BGP speaker associated
return
context = kwargs['context']
router_id = kwargs['router_id']
subnets = kwargs.get('subnets')
network_id = kwargs['network_id']
port = kwargs['port']
speakers = self._bgp_speakers_for_gateway_network(context,
network_id)
for speaker in speakers:
speaker_id = speaker.id
with locking.LockManager.get_lock(str(speaker_id)):
speaker = self.get_bgp_speaker(context, speaker_id)
if network_id not in speaker['networks']:
continue
if event == events.AFTER_CREATE:
self.nsxv_driver.advertise_subnet(context, speaker_id,
router_id, subnets[0])
if event == events.AFTER_DELETE:
subnet_id = port['fixed_ips'][0]['subnet_id']
self.nsxv_driver.withdraw_subnet(context, speaker_id,
router_id, subnet_id)
def router_gateway_callback(self, resource, event, trigger, **kwargs):
context = kwargs.get('context') or n_context.get_admin_context()
router_id = kwargs['router_id']
network_id = kwargs['network_id']
speakers = self._bgp_speakers_for_gateway_network(context, network_id)
for speaker in speakers:
speaker_id = speaker.id
with locking.LockManager.get_lock(str(speaker_id)):
speaker = self.get_bgp_speaker(context, speaker_id)
if network_id not in speaker['networks']:
continue
if event == events.AFTER_DELETE:
gw_ips = kwargs['gateway_ips']
self.nsxv_driver.disable_bgp_on_router(context,
speaker,
router_id,
gw_ips[0])
def _before_service_edge_delete_callback(self, resource, event,
trigger, **kwargs):
context = kwargs['context']
router = kwargs['router']
ext_net_id = router.gw_port and router.gw_port['network_id']
gw_ip = router.gw_port and router.gw_port['fixed_ips'][0]['ip_address']
edge_id = kwargs.get('edge_id')
speakers = self._bgp_speakers_for_gateway_network(context, ext_net_id)
for speaker in speakers:
with locking.LockManager.get_lock(speaker.id):
speaker = self.get_bgp_speaker(context, speaker.id)
if ext_net_id not in speaker['networks']:
continue
self.nsxv_driver.disable_bgp_on_router(context, speaker,
router['id'],
gw_ip, edge_id)
def _after_service_edge_create_callback(self, resource, event,
trigger, **kwargs):
context = kwargs['context']
router = kwargs['router']
ext_net_id = router.gw_port and router.gw_port['network_id']
speakers = self._bgp_speakers_for_gateway_network(context, ext_net_id)
for speaker in speakers:
with locking.LockManager.get_lock(speaker.id):
speaker = self.get_bgp_speaker(context, speaker.id)
if ext_net_id not in speaker['networks']:
continue
self.nsxv_driver.enable_bgp_on_router(context, speaker,
router['id'])

View File

@ -0,0 +1,541 @@
# Copyright 2017 VMware, Inc.
#
# 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.
import netaddr
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from neutron_lib import constants as n_const
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from vmware_nsx._i18n import _
from vmware_nsx.common import locking
from vmware_nsx.common import nsxv_constants
from vmware_nsx.db import nsxv_db
from vmware_nsx.extensions import edge_service_gateway_bgp_peer as ext_esg_peer
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions as vcns_exc
LOG = logging.getLogger(__name__)
def ip_prefix(name, ip_address):
return {'ipPrefix': {'name': name, 'ipAddress': ip_address}}
def redistribution_rule(advertise_static_routes, prefix_name, action='permit'):
rule = {
'prefixName': prefix_name,
'action': action,
'from': {
'ospf': False,
'bgp': False,
'connected': not advertise_static_routes,
'static': advertise_static_routes
}
}
return {'rule': rule}
def bgp_neighbour(bgp_peer):
bgp_filter = {'bgpFilter': [{'direction': 'out', 'action': 'permit'}]}
nbr = {
'ipAddress': bgp_peer['peer_ip'],
'remoteAS': bgp_peer['remote_as'],
'bgpFilters': bgp_filter,
'password': bgp_peer['password']
}
return {'bgpNeighbour': nbr}
def gw_bgp_neighbour(ip_address, remote_as, password):
bgp_filter = {'bgpFilter': [{'direction': 'in', 'action': 'permit'}]}
nbr = {
'ipAddress': ip_address,
'remoteAS': remote_as,
'bgpFilters': bgp_filter,
'password': password
}
return {'bgpNeighbour': nbr}
def default_route(nexthop):
return {'network': '0.0.0.0/0',
'nextHop': nexthop}
class NSXvBgpDriver(object):
"""Class driver to address the neutron_dynamic_routing API"""
def __init__(self, plugin):
super(NSXvBgpDriver, self).__init__()
self._edge_password = cfg.CONF.nsxv.edge_appliance_password
self._plugin = plugin
self._core_plugin = directory.get_plugin()
self._nsxv = self._core_plugin.nsx_v
self._edge_manager = self._core_plugin.edge_manager
def prefix_name(self, subnet_id):
return 'subnet-%s' % subnet_id
def _get_router_edge_info(self, context, router_id):
edge_binding = nsxv_db.get_nsxv_router_binding(context.session,
router_id)
if not edge_binding:
return None, None
# Idicates which routes should be advertised - connected or static.
advertise_static_routes = False
if edge_binding['edge_type'] != nsxv_constants.SERVICE_EDGE:
# Distributed router
plr_id = self._edge_manager.get_plr_by_tlr_id(context, router_id)
edge_binding = nsxv_db.get_nsxv_router_binding(context.session,
plr_id)
if not edge_binding:
# Distributed router isn't bound to plr
return None, None
# PLR for distributed router, advertise static routes.
advertise_static_routes = True
return edge_binding['edge_id'], advertise_static_routes
def _get_dynamic_routing_edge_list(self, context, gateway_network_id):
# Filter the routers attached this network as gateway interface
filters = {'network_id': [gateway_network_id],
'device_owner': [n_const.DEVICE_OWNER_ROUTER_GW]}
fields = ['device_id', 'fixed_ips']
gateway_ports = self._core_plugin.get_ports(context, filters=filters,
fields=fields)
edge_router_dict = {}
for port in gateway_ports:
router_id = port['device_id']
router = self._core_plugin._get_router(context, router_id)
bgp_identifier = port['fixed_ips'][0]['ip_address']
edge_id, advertise_static_routes = (
self._get_router_edge_info(context, router_id))
if not edge_id:
# Shared router is not attached on any edge
continue
if edge_id not in edge_router_dict:
edge_router_dict[edge_id] = {'no_snat_routers': [],
'bgp_identifier':
bgp_identifier,
'advertise_static_routes':
advertise_static_routes}
if not router.enable_snat:
edge_router_dict[edge_id]['no_snat_routers'].append(router_id)
return edge_router_dict
def _query_tenant_subnets(self, context, router_ids):
# Query subnets attached to all of routers attached to same edge
subnets = []
for router_id in router_ids:
filters = {'device_id': [router_id],
'device_owner': [n_const.DEVICE_OWNER_ROUTER_INTF]}
int_ports = self._core_plugin.get_ports(context,
filters=filters,
fields=['fixed_ips'])
for p in int_ports:
subnet_id = p['fixed_ips'][0]['subnet_id']
subnet = self._core_plugin.get_subnet(context, subnet_id)
subnets.append({'id': subnet_id,
'cidr': subnet['cidr']})
LOG.debug("Got related subnets %s", subnets)
return subnets
def _get_bgp_speakers_by_bgp_peer(self, context, bgp_peer_id):
fields = ['id', 'peers']
bgp_speakers = self._plugin.get_bgp_speakers(context, fields=fields)
bgp_speaker_ids = [bgp_speaker['id'] for bgp_speaker in bgp_speakers
if bgp_peer_id in bgp_speaker['peers']]
return bgp_speaker_ids
def _get_prefixes_and_redistribution_rules(self, subnets,
advertise_static_routes):
prefixes = []
redis_rules = []
for subnet in subnets:
prefix_name = self.prefix_name(subnet['id'])
prefix = ip_prefix(prefix_name, subnet['cidr'])
prefixes.append(prefix)
rule = redistribution_rule(advertise_static_routes, prefix_name)
redis_rules.append(rule)
return prefixes, redis_rules
def create_bgp_speaker(self, context, bgp_speaker):
bgp_speaker_data = bgp_speaker['bgp_speaker']
ip_version = bgp_speaker_data.get('ip_version')
if ip_version and ip_version == 6:
err_msg = _("NSXv BGP does not support for IPv6")
raise n_exc.InvalidInput(error_message=err_msg)
def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker):
bgp_obj = bgp_speaker['bgp_speaker']
old_speaker_info = self._plugin.get_bgp_speaker(context,
bgp_speaker_id)
enabled_state = old_speaker_info['advertise_tenant_networks']
new_enabled_state = bgp_obj.get('advertise_tenant_networks',
enabled_state)
if new_enabled_state == enabled_state:
return
bgp_bindings = nsxv_db.get_nsxv_bgp_speaker_bindings(
context.session, bgp_speaker_id)
edge_ids = [bgp_binding['edge_id'] for bgp_binding in bgp_bindings]
action = 'Enabling' if new_enabled_state else 'Disabling'
LOG.info("%s BGP route redistribution on edges: %s.", action, edge_ids)
for edge_id in edge_ids:
try:
self._nsxv.update_routing_redistribution(edge_id,
new_enabled_state)
except vcns_exc.VcnsApiException:
LOG.warning("Failed to update BGP on edge %s.", edge_id)
def delete_bgp_speaker(self, context, bgp_speaker_id):
bgp_bindings = nsxv_db.get_nsxv_bgp_speaker_bindings(
context.session, bgp_speaker_id)
self._stop_bgp_on_edges(context, bgp_bindings, bgp_speaker_id)
def _validate_bgp_configuration_on_peer_esg(self, bgp_peer):
if not bgp_peer.get('esg_id'):
return
# TBD(roeyc): Validate peer_ip is on subnet
bgp_config = self._nsxv.get_routing_bgp_config(bgp_peer['esg_id'])
remote_as = bgp_peer['remote_as']
esg_id = bgp_peer['esg_id']
esg_as = bgp_config['bgp'].get('localAS')
if not bgp_config['bgp']['enabled']:
raise ext_esg_peer.BgpDisabledOnEsgPeer(esg_id=esg_id)
if esg_as != int(remote_as):
raise ext_esg_peer.EsgRemoteASDoNotMatch(remote_as=remote_as,
esg_id=esg_id,
esg_as=esg_as)
def create_bgp_peer(self, context, bgp_peer):
bgp_peer = bgp_peer['bgp_peer']
remote_ip = bgp_peer['peer_ip']
if not netaddr.valid_ipv4(remote_ip):
err_msg = _("NSXv BGP does not support for IPv6")
raise n_exc.InvalidInput(error_message=err_msg)
self._validate_bgp_configuration_on_peer_esg(bgp_peer)
def update_bgp_peer(self, context, bgp_peer_id, bgp_peer):
password = bgp_peer['bgp_peer'].get('password')
old_bgp_peer = self._plugin.get_bgp_peer(context, bgp_peer_id)
# Only password update is relevant for backend.
if old_bgp_peer['password'] == password:
return
bgp_speaker_ids = self._get_bgp_speakers_by_bgp_peer(context,
bgp_peer_id)
# Update the password for the old bgp peer and update NSX
old_bgp_peer['password'] = password
neighbour = bgp_neighbour(old_bgp_peer)
for bgp_speaker_id in bgp_speaker_ids:
with locking.LockManager.get_lock(bgp_speaker_id):
speaker = self._plugin.get_bgp_speaker(context, bgp_speaker_id)
if bgp_peer_id not in speaker['peers']:
continue
bgp_bindings = nsxv_db.get_nsxv_bgp_speaker_bindings(
context.session, bgp_speaker_id)
for binding in bgp_bindings:
try:
self._nsxv.update_bgp_neighbour(binding['edge_id'],
neighbour)
except vcns_exc.VcnsApiException:
LOG.error("Failed to update BGP neighbor '%s' on "
"edge '%s'", old_bgp_peer['peer_ip'],
binding['edge_id'])
def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
bgp_peer_obj = self._plugin.get_bgp_peer(context,
bgp_peer_info['bgp_peer_id'])
nbr = bgp_neighbour(bgp_peer_obj)
bgp_bindings = nsxv_db.get_nsxv_bgp_speaker_bindings(context.session,
bgp_speaker_id)
speaker = self._plugin.get_bgp_speaker(context, bgp_speaker_id)
# list of tenant edge routers to be removed as bgp-neighbours to this
# peer if it's associated with specific ESG.
neighbours = []
droute = default_route(nbr['bgpNeighbour']['ipAddress'])
for binding in bgp_bindings:
try:
self._nsxv.add_bgp_neighbours(binding['edge_id'], [nbr],
default_routes=[droute])
except vcns_exc.VcnsApiException:
LOG.error("Failed to add BGP neighbour on '%s'",
binding['edge_id'])
else:
nbr = gw_bgp_neighbour(binding['bgp_identifier'],
speaker['local_as'],
self._edge_password)
neighbours.append(nbr)
LOG.debug("Succesfully added BGP neighbor '%s' on '%s'",
bgp_peer_obj['peer_ip'], binding['edge_id'])
if bgp_peer_obj.get('esg_id'):
edge_gw = bgp_peer_obj['esg_id']
try:
self._nsxv.add_bgp_neighbours(edge_gw, neighbours)
except vcns_exc.VcnsApiException:
with excutils.save_and_reraise_exception():
LOG.error("Failed to add BGP neighbour on GW Edge '%s'",
edge_gw)
def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info):
bgp_peer_id = bgp_peer_info['bgp_peer_id']
bgp_peer_obj = self._plugin.get_bgp_peer(context, bgp_peer_id)
nbr = bgp_neighbour(bgp_peer_obj)
bgp_bindings = nsxv_db.get_nsxv_bgp_speaker_bindings(
context.session, bgp_speaker_id)
speaker = self._plugin.get_bgp_speaker(context, bgp_speaker_id)
# list of tenant edge routers to be removed as bgp-neighbours to this
# peer if it's associated with specific ESG.
neighbours = []
droute = default_route(nbr['bgpNeighbour']['ipAddress'])
for binding in bgp_bindings:
try:
self._nsxv.remove_bgp_neighbours(binding['edge_id'], [nbr],
default_routes=[droute])
except vcns_exc.VcnsApiException:
LOG.error("Failed to remove BGP neighbour on '%s'",
binding['edge_id'])
else:
nbr = gw_bgp_neighbour(binding['bgp_identifier'],
speaker['local_as'],
self._edge_password)
neighbours.append(nbr)
LOG.debug("Succesfully removed BGP neighbor '%s' on '%s'",
bgp_peer_obj['peer_ip'], binding['edge_id'])
if bgp_peer_obj.get('esg_id'):
edge_gw = bgp_peer_obj['esg_id']
try:
self._nsxv.remove_bgp_neighbours(edge_gw, neighbours)
except vcns_exc.VcnsApiException:
LOG.error("Failed to remove BGP neighbour on GW Edge '%s'",
edge_gw)
def add_gateway_network(self, context, bgp_speaker_id, network_info):
gateway_network_id = network_info['network_id']
ext_net = self._core_plugin.get_network(context, gateway_network_id)
if not ext_net['subnets']:
return
subnet_id = ext_net['subnets'][0]
ext_subnet = self._core_plugin.get_subnet(context, subnet_id)
if ext_subnet.get('gateway_ip'):
raise ext_esg_peer.ExternalSubnetHasGW(
network_id=gateway_network_id, subnet_id=subnet_id)
edge_router_dict = self._get_dynamic_routing_edge_list(
context, gateway_network_id)
speaker = self._plugin.get_bgp_speaker(context, bgp_speaker_id)
bgp_peers = self._plugin.get_bgp_peers_by_bgp_speaker(
context, bgp_speaker_id)
neighbours = []
for edge_id, edge_router_config in edge_router_dict.items():
router_ids = edge_router_config['no_snat_routers']
advertise_static_routes = (
edge_router_config['advertise_static_routes'])
subnets = self._query_tenant_subnets(context, router_ids)
# router_id here is in IP address format and is required for
# the BGP configuration.
bgp_identifier = edge_router_config['bgp_identifier']
try:
self._start_bgp_on_edge(context, edge_id, speaker,
bgp_peers, bgp_identifier, subnets,
advertise_static_routes)
except vcns_exc.VcnsApiException:
LOG.error("Failed to configure BGP speaker %s on edge '%s'.",
bgp_speaker_id)
else:
nbr = gw_bgp_neighbour(bgp_identifier, speaker['local_as'],
self._edge_password)
neighbours.append(nbr)
for edge_gw in [peer['esg_id'] for peer in bgp_peers
if peer.get('esg_id')]:
try:
self._nsxv.add_bgp_neighbours(edge_gw, neighbours)
except vcns_exc.VcnsApiException:
LOG.error("Failed to add BGP neighbour on GW Edge '%s'",
edge_gw)
def _start_bgp_on_edge(self, context, edge_id, speaker, bgp_peers,
bgp_identifier, subnets, advertise_static_routes):
enabled_state = speaker['advertise_tenant_networks']
local_as = speaker['local_as']
prefixes, redis_rules = self._get_prefixes_and_redistribution_rules(
subnets, advertise_static_routes)
bgp_neighbours = [bgp_neighbour(bgp_peer) for bgp_peer in bgp_peers]
default_routes = [default_route(peer['peer_ip']) for peer in bgp_peers]
try:
self._nsxv.add_bgp_speaker_config(edge_id, bgp_identifier,
local_as, enabled_state,
default_routes,
bgp_neighbours, prefixes,
redis_rules)
except vcns_exc.VcnsApiException:
with excutils.save_and_reraise_exception():
LOG.error("Failed to configure BGP speaker %s on edge '%s'.",
speaker['id'], edge_id)
else:
nsxv_db.add_nsxv_bgp_speaker_binding(context.session, edge_id,
speaker['id'], bgp_identifier)
def _stop_bgp_on_edges(self, context, bgp_bindings, speaker_id):
neighbours_to_remove = []
speaker = self._plugin.get_bgp_speaker(context, speaker_id)
for bgp_binding in bgp_bindings:
edge_id = bgp_binding['edge_id']
try:
self._nsxv.delete_bgp_speaker_config(edge_id)
except vcns_exc.VcnsApiException:
LOG.error("Failed to delete BGP speaker %s config on edge "
"%s.", speaker_id, edge_id)
else:
nsxv_db.delete_nsxv_bgp_speaker_binding(context.session,
edge_id)
nbr = gw_bgp_neighbour(bgp_binding['bgp_identifier'],
speaker['local_as'],
self._edge_password)
neighbours_to_remove.append(nbr)
# We should also remove all bgp neighbours on gw-edges which
# corresponds with tenant routers that are associated with this bgp
# speaker.
bgp_peers = self._plugin.get_bgp_peers_by_bgp_speaker(context,
speaker_id)
gw_edges = [peer['esg_id'] for peer in bgp_peers if peer.get('esg_id')]
for gw_edge in gw_edges:
try:
self._nsxv.remove_bgp_neighbours(gw_edge, neighbours_to_remove)
except vcns_exc.VcnsApiException:
LOG.error("Failed to remove BGP neighbour on GW edge '%s'.",
gw_edge)
def remove_gateway_network(self, context, bgp_speaker_id, network_info):
bgp_bindings = nsxv_db.get_nsxv_bgp_speaker_bindings(
context.session, bgp_speaker_id)
self._stop_bgp_on_edges(context, bgp_bindings, bgp_speaker_id)
def enable_bgp_on_router(self, context, speaker, router_id):
edge_id, advertise_static_routes = (
self._get_router_edge_info(context, router_id))
if not edge_id:
# shared router is not attached on any edge
return
router = self._core_plugin._get_router(context, router_id)
if router.enable_snat:
subnets = []
else:
subnets = self._query_tenant_subnets(context, [router_id])
bgp_peers = self._plugin.get_bgp_peers_by_bgp_speaker(
context, speaker['id'])
bgp_binding = nsxv_db.get_nsxv_bgp_speaker_binding(
context.session, edge_id)
if bgp_binding and subnets:
# Edge already configured with BGP (e.g - shared router edge),
# Add the router attached subnets.
prefixes, redis_rules = (
self._get_prefixes_and_redistribution_rules(
subnets, advertise_static_routes))
self._nsxv.add_bgp_redistribution_rules(edge_id, prefixes,
redis_rules)
elif not bgp_binding:
gw_port = router.gw_port['fixed_ips'][0]
bgp_identifier = gw_port['ip_address']
self._start_bgp_on_edge(context, edge_id, speaker, bgp_peers,
bgp_identifier, subnets,
advertise_static_routes)
nbr = gw_bgp_neighbour(bgp_identifier, speaker['local_as'],
self._edge_password)
for gw_edge_id in [peer['esg_id'] for peer in bgp_peers
if peer['esg_id']]:
self._nsxv.add_bgp_neighbours(gw_edge_id, [nbr])
def disable_bgp_on_router(self, context, speaker, router_id, gw_ip,
edge_id=None):
speaker = self._plugin.get_bgp_speaker(context, speaker['id'])
current_edge_id, advertise_static_routes = (
self._get_router_edge_info(context, router_id))
edge_id = edge_id or current_edge_id
if not edge_id:
return
routers_ids = (
self._core_plugin.edge_manager.get_routers_on_same_edge(
context, router_id))
bgp_binding = nsxv_db.get_nsxv_bgp_speaker_binding(context.session,
edge_id)
if not bgp_binding:
return
if len(routers_ids) > 1:
routers_ids.remove(router_id)
# Shared router, only remove prefixes and redistribution
# rules.
subnets = self._query_tenant_subnets(context, [router_id])
prefixes = [self.prefix_name(subnet['id'])
for subnet in subnets]
self._nsxv.remove_bgp_redistribution_rules(edge_id, prefixes)
if bgp_binding['bgp_identifier'] == gw_ip:
router = self._core_plugin._get_router(context, routers_ids[0])
new_bgp_identifier = (
router.gw_port['fixed_ips'][0]['ip_address'])
with context.session.begin(subtransactions=True):
bgp_binding['bgp_identifier'] = new_bgp_identifier
self._nsxv.update_router_id(edge_id, new_bgp_identifier)
else:
self._stop_bgp_on_edges(context, [bgp_binding], speaker['id'])
def advertise_subnet(self, context, speaker_id, router_id, subnet):
router = self._core_plugin._get_router(context, router_id)
if router.enable_snat:
# Do nothing, by default, only when advertisement is needed we add
# a new redistribution rule
return
edge_id, advertise_static_routes = (
self._get_router_edge_info(context, router_id))
if not edge_id:
# shared router is not attached on any edge
return
prefixes, redis_rules = self._get_prefixes_and_redistribution_rules(
[subnet], advertise_static_routes)
self._nsxv.add_bgp_redistribution_rules(edge_id, prefixes, redis_rules)
def withdraw_subnet(self, context, speaker_id, router_id, subnet_id):
router = self._core_plugin._get_router(context, router_id)
if router.enable_snat:
# Do nothing, by default, only when advertisement is needed we add
# a new redistribution rule
return
edge_id, advertise_static_routes = (
self._get_router_edge_info(context, router_id))
prefix_name = self.prefix_name(subnet_id)
self._nsxv.remove_bgp_redistribution_rules(edge_id, [prefix_name])

View File

@ -1474,3 +1474,128 @@ class FakeVcns(object):
'objectID': 'application-1001'}]
return applications
def update_dynamic_routing_service(self, edge_id, request_config):
header = {'status': 201}
response = {
'routerId': '172.24.4.12',
'ipPrefixes': {
'ipPrefixes': [
{'ipAddress': '10.0.0.0/24',
'name': 'prefix-name'}
]
}
}
return self.return_helper(header, response)
def get_dynamic_routing_service(self, edge_id):
header = {'status': 200}
response = {
'routerId': '172.24.4.12',
'ipPrefixes': {
'ipPrefixes': [
{'ipAddress': '10.0.0.0/24',
'name': 'prefix-name'}
]
},
'logging': {
'logLevel': 'info',
'enable': False
},
'ecmp': False
}
return self.return_helper(header, response)
def update_bgp_dynamic_routing(self, edge_id, bgp_request):
header = {"status": 201}
response = {
"localAS": 65000,
"enabled": True,
"bgpNeighbours": {
"bgpNeighbours": [
{
"bgpFilters": {
"bgpFilters": [
{
"action": "deny",
"direction": "in"
}
]
},
"password": None,
"ipAddress": "172.24.4.253",
"remoteAS": 65000
}
]
},
"redistribution": {
"rules": {
"rules": [
{
"action": "deny",
"from": {
"bgp": False,
"connected": False,
"static": False,
"ospf": False
},
"id": 0
},
{
"action": "permit",
"from": {
"bgp": False,
"connected": True,
"static": True,
"ospf": False
},
"id": 1,
"prefixName": "eee4eb79-359e-4416"
}
]
},
"enabled": True
}
}
return self.return_helper(header, response)
def get_bgp_routing_config(self, edge_id):
header = {'status': 200}
response = {
"localAS": 65000,
"enabled": True,
"redistribution": {
"rules": {
"rules": [
{
"action": "deny",
"from": {
"bgp": False,
"connected": False,
"static": False,
"ospf": False
},
"id": 0
},
{
"action": "permit",
"from": {
"bgp": False,
"connected": True,
"static": True,
"ospf": False
},
"id": 1,
"prefixName": "eee4eb79-359e-4416"
}
]
},
"enabled": True
}
}
return self.return_helper(header, response)
def delete_bgp_routing_config(self, edge_id):
header = {'status': 200}
response = ''
return header, response

View File

@ -0,0 +1,59 @@
# Copyright 2017 VMware, Inc.
# 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.
import mock
from neutron.tests import base
from neutron_dynamic_routing.db import bgp_db # noqa
from neutron_lib import context
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from vmware_nsx.services.dynamic_routing.nsx_v import driver as nsxv_driver
class TestNSXvBgpDriver(base.BaseTestCase):
def setUp(self):
super(TestNSXvBgpDriver, self).setUp()
self.context = context.get_admin_context()
with mock.patch.object(directory, 'get_plugin',
new=mock.Mock()):
self.provider = nsxv_driver.NSXvBgpDriver(mock.MagicMock())
def test_create_v6_bgp_speaker(self):
fake_bgp_speaker = {
"bgp_speaker": {
"ip_version": 6,
"local_as": "1000",
"name": "bgp-speaker"
}
}
self.assertRaises(n_exc.InvalidInput,
self.provider.create_bgp_speaker,
self.context, fake_bgp_speaker)
def test_create_v6_bgp_peer(self):
fake_bgp_peer = {
"bgp_peer": {
"auth_type": "none",
"remote_as": "1000",
"name": "bgp-peer",
"peer_ip": "fc00::/7"
}
}
self.assertRaises(n_exc.InvalidInput,
self.provider.create_bgp_peer,
self.context, fake_bgp_peer)