NSXv BGP support
This change implement's a new BGP plugin which allows BGP support in Openstack, using NSXv service edges (ESG). When a BGP speaker is associated with an external network, service edges which accommodates tenant routers that have their GW port on this network would be configured to enable BGP/Dynamic-routing. The specific BGP configuration (e.g - localAS, neighbours) for the edge is retrieved from the BGP speaker object and its peers. This change also adds an extension to the BGP peer object, this extension allows the cloud operator to associate a BGP peer with a specific service edge that will serve as GW edge for the network, multiple GW edges are supported by enabling ECMP on tenant service edges. Co-Authored: yuyangbj <yangyu@vmware.com> Change-Id: Ife69b97f3232bee378a48d91dc53bdc8837de7f5
This commit is contained in:
parent
a2e05bc255
commit
81f9380765
@ -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
|
||||
-----
|
||||
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
7c4704ad37df
|
||||
8699700cd95c
|
||||
|
@ -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'))
|
@ -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
|
||||
|
@ -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)
|
||||
|
98
vmware_nsx/extensions/edge_service_gateway_bgp_peer.py
Normal file
98
vmware_nsx/extensions/edge_service_gateway_bgp_peer.py
Normal 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 {}
|
200
vmware_nsx/plugins/nsx_v/vshield/edge_dynamic_routing_driver.py
Normal file
200
vmware_nsx/plugins/nsx_v/vshield/edge_dynamic_routing_driver.py
Normal 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)
|
@ -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)
|
||||
|
@ -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__()
|
||||
|
0
vmware_nsx/services/dynamic_routing/__init__.py
Normal file
0
vmware_nsx/services/dynamic_routing/__init__.py
Normal file
256
vmware_nsx/services/dynamic_routing/bgp_plugin.py
Normal file
256
vmware_nsx/services/dynamic_routing/bgp_plugin.py
Normal 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'])
|
541
vmware_nsx/services/dynamic_routing/nsx_v/driver.py
Normal file
541
vmware_nsx/services/dynamic_routing/nsx_v/driver.py
Normal 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])
|
@ -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
|
||||
|
@ -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)
|
Loading…
Reference in New Issue
Block a user