Add Queries For BGP Route Lookups
This patch contains code for looking in the Neutron DB and identifying next-hops for tenant networks and floating IP's. These queries use the centralized router as the next-hop and ignore host routes attached to a distributed router. Partially-Implements: blueprint bgp-dynamic-routing Co-Authored-By: vikram.choudhary <vikram.choudhary@huawei.com> Change-Id: If08d6595d9bc657a7ecfb487e8df40860837d200
This commit is contained in:
parent
ed95209657
commit
18a8e1bc04
|
@ -12,22 +12,33 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
|
||||
from oslo_db import exception as oslo_db_exc
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import aliased
|
||||
from sqlalchemy.orm import exc as sa_exc
|
||||
|
||||
from neutron_lib import constants as lib_consts
|
||||
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.db import address_scope_db
|
||||
from neutron.db import common_db_mixin as common_db
|
||||
from neutron.db import l3_attrs_db
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import model_base
|
||||
from neutron.db import models_v2
|
||||
from neutron.extensions import bgp as bgp_ext
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
DEVICE_OWNER_ROUTER_GW = lib_consts.DEVICE_OWNER_ROUTER_GW
|
||||
DEVICE_OWNER_ROUTER_INTF = lib_consts.DEVICE_OWNER_ROUTER_INTF
|
||||
|
||||
|
||||
class BgpSpeakerPeerBinding(model_base.BASEV2):
|
||||
|
@ -140,7 +151,9 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||
res['peers'] = self.get_bgp_peers_by_bgp_speaker(context,
|
||||
bgp_speaker['id'],
|
||||
fields=bgp_peer_attrs)
|
||||
res['advertised_routes'] = []
|
||||
res['advertised_routes'] = self.get_routes_by_bgp_speaker_id(
|
||||
context,
|
||||
bgp_speaker_id)
|
||||
return res
|
||||
|
||||
def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker):
|
||||
|
@ -267,8 +280,30 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||
except sa_exc.NoResultFound:
|
||||
raise bgp_ext.BgpSpeakerNotFound(id=bgp_speaker_id)
|
||||
|
||||
def _get_bgp_speaker_ids_by_router(self, context, router_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
network_binding = aliased(BgpSpeakerNetworkBinding)
|
||||
r_port = aliased(l3_db.RouterPort)
|
||||
query = context.session.query(network_binding.bgp_speaker_id)
|
||||
query = query.filter(
|
||||
r_port.router_id == router_id,
|
||||
r_port.port_type == lib_consts.DEVICE_OWNER_ROUTER_GW,
|
||||
r_port.port_id == models_v2.Port.id,
|
||||
models_v2.Port.network_id == network_binding.network_id)
|
||||
|
||||
return [binding.bgp_speaker_id for binding in query.all()]
|
||||
|
||||
def _get_bgp_speaker_ids_by_binding_network(self, context, network_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
query = context.session.query(
|
||||
BgpSpeakerNetworkBinding.bgp_speaker_id)
|
||||
query = query.filter(
|
||||
BgpSpeakerNetworkBinding.network_id == network_id)
|
||||
return query.all()
|
||||
|
||||
def get_advertised_routes(self, context, bgp_speaker_id):
|
||||
return self._make_advertised_routes_dict([])
|
||||
routes = self.get_routes_by_bgp_speaker_id(context, bgp_speaker_id)
|
||||
return self._make_advertised_routes_dict(routes)
|
||||
|
||||
def _get_id_for(self, resource, id_name):
|
||||
try:
|
||||
|
@ -406,3 +441,473 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||
'auth_type', 'password']
|
||||
res = dict((k, bgp_peer[k]) for k in attrs)
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _get_address_scope_ids_for_bgp_speaker(self, context, bgp_speaker_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
binding = aliased(BgpSpeakerNetworkBinding)
|
||||
address_scope = aliased(address_scope_db.AddressScope)
|
||||
query = context.session.query(address_scope)
|
||||
query = query.filter(
|
||||
binding.bgp_speaker_id == bgp_speaker_id,
|
||||
models_v2.Subnet.ip_version == binding.ip_version,
|
||||
models_v2.Subnet.network_id == binding.network_id,
|
||||
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id,
|
||||
models_v2.SubnetPool.address_scope_id == address_scope.id)
|
||||
return [scope.id for scope in query.all()]
|
||||
|
||||
def get_routes_by_bgp_speaker_id(self, context, bgp_speaker_id):
|
||||
"""Get all routes that should be advertised by a BgpSpeaker."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
net_routes = self._get_tenant_network_routes_by_bgp_speaker(
|
||||
context,
|
||||
bgp_speaker_id)
|
||||
fip_routes = self._get_central_fip_host_routes_by_bgp_speaker(
|
||||
context,
|
||||
bgp_speaker_id)
|
||||
return itertools.chain(fip_routes, net_routes)
|
||||
|
||||
def get_routes_by_bgp_speaker_binding(self, context,
|
||||
bgp_speaker_id, network_id):
|
||||
"""Get all routes for the given bgp_speaker binding."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fip_routes = self._get_central_fip_host_routes_by_binding(
|
||||
context,
|
||||
network_id,
|
||||
bgp_speaker_id)
|
||||
net_routes = self._get_tenant_network_routes_by_binding(
|
||||
context,
|
||||
network_id,
|
||||
bgp_speaker_id)
|
||||
return itertools.chain(fip_routes, net_routes)
|
||||
|
||||
def _get_routes_by_router(self, context, router_id):
|
||||
bgp_speaker_ids = self._get_bgp_speaker_ids_by_router(context,
|
||||
router_id)
|
||||
route_dict = {}
|
||||
for bgp_speaker_id in bgp_speaker_ids:
|
||||
fip_routes = self._get_central_fip_host_routes_by_router(
|
||||
context,
|
||||
router_id,
|
||||
bgp_speaker_id)
|
||||
net_routes = self._get_tenant_network_routes_by_router(
|
||||
context,
|
||||
router_id,
|
||||
bgp_speaker_id)
|
||||
routes = list(itertools.chain(fip_routes, net_routes))
|
||||
route_dict[bgp_speaker_id] = routes
|
||||
return route_dict
|
||||
|
||||
def _get_central_fip_host_routes_by_router(self, context, router_id,
|
||||
bgp_speaker_id):
|
||||
"""Get floating IP host routes with the given router as nexthop."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
dest_alias = aliased(l3_db.FloatingIP,
|
||||
name='destination')
|
||||
next_hop_alias = aliased(models_v2.IPAllocation,
|
||||
name='next_hop')
|
||||
binding_alias = aliased(BgpSpeakerNetworkBinding,
|
||||
name='binding')
|
||||
router_attrs = aliased(l3_attrs_db.RouterExtraAttributes,
|
||||
name='router_attrs')
|
||||
query = context.session.query(dest_alias.floating_ip_address,
|
||||
next_hop_alias.ip_address)
|
||||
query = query.join(
|
||||
next_hop_alias,
|
||||
next_hop_alias.network_id == dest_alias.floating_network_id)
|
||||
query = query.join(l3_db.Router,
|
||||
dest_alias.router_id == l3_db.Router.id)
|
||||
query = query.filter(
|
||||
l3_db.Router.id == router_id,
|
||||
dest_alias.router_id == l3_db.Router.id,
|
||||
l3_db.Router.id == router_attrs.router_id,
|
||||
router_attrs.distributed == sa.sql.false(),
|
||||
l3_db.Router.gw_port_id == next_hop_alias.port_id,
|
||||
next_hop_alias.subnet_id == models_v2.Subnet.id,
|
||||
models_v2.Subnet.ip_version == 4,
|
||||
binding_alias.network_id == models_v2.Subnet.network_id,
|
||||
binding_alias.bgp_speaker_id == bgp_speaker_id,
|
||||
binding_alias.ip_version == 4,
|
||||
BgpSpeaker.advertise_floating_ip_host_routes == sa.sql.true())
|
||||
query = query.outerjoin(router_attrs,
|
||||
l3_db.Router.id == router_attrs.router_id)
|
||||
query = query.filter(router_attrs.distributed != sa.sql.true())
|
||||
return self._host_route_list_from_tuples(query.all())
|
||||
|
||||
def _get_central_fip_host_routes_by_binding(self, context,
|
||||
network_id, bgp_speaker_id):
|
||||
"""Get all floating IP host routes for the given network binding."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
# Query the DB for floating IP's and the IP address of the
|
||||
# gateway port
|
||||
dest_alias = aliased(l3_db.FloatingIP,
|
||||
name='destination')
|
||||
next_hop_alias = aliased(models_v2.IPAllocation,
|
||||
name='next_hop')
|
||||
binding_alias = aliased(BgpSpeakerNetworkBinding,
|
||||
name='binding')
|
||||
router_attrs = aliased(l3_attrs_db.RouterExtraAttributes,
|
||||
name='router_attrs')
|
||||
query = context.session.query(dest_alias.floating_ip_address,
|
||||
next_hop_alias.ip_address)
|
||||
query = query.join(
|
||||
next_hop_alias,
|
||||
next_hop_alias.network_id == dest_alias.floating_network_id)
|
||||
query = query.join(
|
||||
binding_alias,
|
||||
binding_alias.network_id == dest_alias.floating_network_id)
|
||||
query = query.join(l3_db.Router,
|
||||
dest_alias.router_id == l3_db.Router.id)
|
||||
query = query.filter(
|
||||
dest_alias.floating_network_id == network_id,
|
||||
dest_alias.router_id == l3_db.Router.id,
|
||||
l3_db.Router.gw_port_id == next_hop_alias.port_id,
|
||||
next_hop_alias.subnet_id == models_v2.Subnet.id,
|
||||
models_v2.Subnet.ip_version == 4,
|
||||
binding_alias.network_id == models_v2.Subnet.network_id,
|
||||
binding_alias.bgp_speaker_id == BgpSpeaker.id,
|
||||
BgpSpeaker.id == bgp_speaker_id,
|
||||
BgpSpeaker.advertise_floating_ip_host_routes == sa.sql.true())
|
||||
query = query.outerjoin(router_attrs,
|
||||
l3_db.Router.id == router_attrs.router_id)
|
||||
query = query.filter(router_attrs.distributed != sa.sql.true())
|
||||
return self._host_route_list_from_tuples(query.all())
|
||||
|
||||
def _get_central_fip_host_routes_by_bgp_speaker(self, context,
|
||||
bgp_speaker_id):
|
||||
"""Get all the floating IP host routes advertised by a BgpSpeaker."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
dest_alias = aliased(l3_db.FloatingIP,
|
||||
name='destination')
|
||||
next_hop_alias = aliased(models_v2.IPAllocation,
|
||||
name='next_hop')
|
||||
speaker_binding = aliased(BgpSpeakerNetworkBinding,
|
||||
name="speaker_network_mapping")
|
||||
router_attrs = aliased(l3_attrs_db.RouterExtraAttributes,
|
||||
name='router_attrs')
|
||||
query = context.session.query(dest_alias.floating_ip_address,
|
||||
next_hop_alias.ip_address)
|
||||
query = query.select_from(dest_alias,
|
||||
BgpSpeaker,
|
||||
l3_db.Router,
|
||||
models_v2.Subnet)
|
||||
query = query.join(
|
||||
next_hop_alias,
|
||||
next_hop_alias.network_id == dest_alias.floating_network_id)
|
||||
query = query.join(
|
||||
speaker_binding,
|
||||
speaker_binding.network_id == dest_alias.floating_network_id)
|
||||
query = query.join(l3_db.Router,
|
||||
dest_alias.router_id == l3_db.Router.id)
|
||||
query = query.filter(
|
||||
BgpSpeaker.id == bgp_speaker_id,
|
||||
BgpSpeaker.advertise_floating_ip_host_routes,
|
||||
speaker_binding.bgp_speaker_id == BgpSpeaker.id,
|
||||
dest_alias.floating_network_id == speaker_binding.network_id,
|
||||
next_hop_alias.network_id == speaker_binding.network_id,
|
||||
dest_alias.router_id == l3_db.Router.id,
|
||||
l3_db.Router.gw_port_id == next_hop_alias.port_id,
|
||||
next_hop_alias.subnet_id == models_v2.Subnet.id,
|
||||
models_v2.Subnet.ip_version == 4)
|
||||
query = query.outerjoin(router_attrs,
|
||||
l3_db.Router.id == router_attrs.router_id)
|
||||
query = query.filter(router_attrs.distributed != sa.sql.true())
|
||||
return self._host_route_list_from_tuples(query.all())
|
||||
|
||||
def _get_tenant_network_routes_by_binding(self, context,
|
||||
network_id, bgp_speaker_id):
|
||||
"""Get all tenant network routes for the given network."""
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
tenant_networks_query = self._tenant_networks_by_network_query(
|
||||
context,
|
||||
network_id,
|
||||
bgp_speaker_id)
|
||||
nexthops_query = self._nexthop_ip_addresses_by_binding_query(
|
||||
context,
|
||||
network_id,
|
||||
bgp_speaker_id)
|
||||
join_q = self._join_tenant_networks_to_next_hops(
|
||||
context,
|
||||
tenant_networks_query.subquery(),
|
||||
nexthops_query.subquery())
|
||||
return self._make_advertised_routes_list(join_q.all())
|
||||
|
||||
def _get_tenant_network_routes_by_router(self, context, router_id,
|
||||
bgp_speaker_id):
|
||||
"""Get all tenant network routes with the given router as nexthop."""
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
scopes = self._get_address_scope_ids_for_bgp_speaker(
|
||||
context,
|
||||
bgp_speaker_id)
|
||||
address_scope = aliased(address_scope_db.AddressScope)
|
||||
inside_query = context.session.query(
|
||||
models_v2.Subnet.cidr,
|
||||
models_v2.IPAllocation.ip_address,
|
||||
address_scope.id)
|
||||
outside_query = context.session.query(
|
||||
address_scope.id,
|
||||
models_v2.IPAllocation.ip_address)
|
||||
speaker_binding = aliased(BgpSpeakerNetworkBinding,
|
||||
name="speaker_network_mapping")
|
||||
port_alias = aliased(l3_db.RouterPort, name='routerport')
|
||||
inside_query = inside_query.filter(
|
||||
port_alias.router_id == router_id,
|
||||
models_v2.IPAllocation.port_id == port_alias.port_id,
|
||||
models_v2.IPAllocation.subnet_id == models_v2.Subnet.id,
|
||||
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id,
|
||||
models_v2.SubnetPool.address_scope_id == address_scope.id,
|
||||
address_scope.id.in_(scopes),
|
||||
port_alias.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW,
|
||||
speaker_binding.bgp_speaker_id == bgp_speaker_id)
|
||||
outside_query = outside_query.filter(
|
||||
port_alias.router_id == router_id,
|
||||
port_alias.port_type == lib_consts.DEVICE_OWNER_ROUTER_GW,
|
||||
models_v2.IPAllocation.port_id == port_alias.port_id,
|
||||
models_v2.IPAllocation.subnet_id == models_v2.Subnet.id,
|
||||
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id,
|
||||
models_v2.SubnetPool.address_scope_id == address_scope.id,
|
||||
address_scope.id.in_(scopes),
|
||||
speaker_binding.bgp_speaker_id == bgp_speaker_id,
|
||||
speaker_binding.network_id == models_v2.Port.network_id,
|
||||
port_alias.port_id == models_v2.Port.id)
|
||||
inside_query = inside_query.subquery()
|
||||
outside_query = outside_query.subquery()
|
||||
join_query = context.session.query(inside_query.c.cidr,
|
||||
outside_query.c.ip_address)
|
||||
and_cond = and_(inside_query.c.id == outside_query.c.id)
|
||||
join_query = join_query.join(outside_query, and_cond)
|
||||
return self._make_advertised_routes_list(join_query.all())
|
||||
|
||||
def _get_tenant_network_routes_by_bgp_speaker(self, context,
|
||||
bgp_speaker_id):
|
||||
"""Get all tenant network routes to be advertised by a BgpSpeaker."""
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
tenant_nets_q = self._tenant_networks_by_bgp_speaker_query(
|
||||
context,
|
||||
bgp_speaker_id)
|
||||
nexthops_q = self._nexthop_ip_addresses_by_bgp_speaker_query(
|
||||
context,
|
||||
bgp_speaker_id)
|
||||
join_q = self._join_tenant_networks_to_next_hops(
|
||||
context,
|
||||
tenant_nets_q.subquery(),
|
||||
nexthops_q.subquery())
|
||||
|
||||
return self._make_advertised_routes_list(join_q.all())
|
||||
|
||||
def _join_tenant_networks_to_next_hops(self, context,
|
||||
tenant_networks_subquery,
|
||||
nexthops_subquery):
|
||||
"""Join subquery for tenant networks to subquery for nexthop IP's"""
|
||||
left_subq = tenant_networks_subquery
|
||||
right_subq = nexthops_subquery
|
||||
join_query = context.session.query(left_subq.c.cidr,
|
||||
right_subq.c.ip_address)
|
||||
and_cond = and_(left_subq.c.router_id == right_subq.c.router_id,
|
||||
left_subq.c.ip_version == right_subq.c.ip_version)
|
||||
join_query = join_query.join(right_subq, and_cond)
|
||||
return join_query
|
||||
|
||||
def _tenant_networks_by_network_query(self, context,
|
||||
network_id, bgp_speaker_id):
|
||||
"""Return subquery for tenant networks by binding network ID"""
|
||||
address_scope = aliased(address_scope_db.AddressScope,
|
||||
name='address_scope')
|
||||
router_attrs = aliased(l3_attrs_db.RouterExtraAttributes,
|
||||
name='router_attrs')
|
||||
tenant_networks_query = context.session.query(
|
||||
l3_db.RouterPort.router_id,
|
||||
models_v2.Subnet.cidr,
|
||||
models_v2.Subnet.ip_version,
|
||||
address_scope.id)
|
||||
tenant_networks_query = tenant_networks_query.filter(
|
||||
l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW,
|
||||
l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_SNAT,
|
||||
l3_db.RouterPort.router_id == router_attrs.router_id,
|
||||
models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id,
|
||||
models_v2.IPAllocation.subnet_id == models_v2.Subnet.id,
|
||||
models_v2.Subnet.network_id != network_id,
|
||||
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id,
|
||||
models_v2.SubnetPool.address_scope_id == address_scope.id,
|
||||
BgpSpeaker.id == bgp_speaker_id,
|
||||
BgpSpeaker.ip_version == address_scope.ip_version,
|
||||
models_v2.Subnet.ip_version == address_scope.ip_version)
|
||||
return tenant_networks_query
|
||||
|
||||
def _tenant_networks_by_bgp_speaker_query(self, context, bgp_speaker_id):
|
||||
"""Return subquery for tenant networks by binding bgp_speaker_id"""
|
||||
router_id = l3_db.RouterPort.router_id.distinct().label('router_id')
|
||||
tenant_nets_subq = context.session.query(router_id,
|
||||
models_v2.Subnet.cidr,
|
||||
models_v2.Subnet.ip_version)
|
||||
scopes = self._get_address_scope_ids_for_bgp_speaker(context,
|
||||
bgp_speaker_id)
|
||||
filters = self._tenant_networks_by_bgp_speaker_filters(scopes)
|
||||
tenant_nets_subq = tenant_nets_subq.filter(*filters)
|
||||
return tenant_nets_subq
|
||||
|
||||
def _tenant_networks_by_bgp_speaker_filters(self, address_scope_ids):
|
||||
"""Return the filters for querying tenant networks by BGP speaker"""
|
||||
router_attrs = aliased(l3_attrs_db.RouterExtraAttributes,
|
||||
name='router_attrs')
|
||||
return [models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id,
|
||||
l3_db.RouterPort.router_id == router_attrs.router_id,
|
||||
l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW,
|
||||
l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_SNAT,
|
||||
models_v2.IPAllocation.subnet_id == models_v2.Subnet.id,
|
||||
models_v2.Subnet.network_id != BgpSpeakerNetworkBinding.network_id,
|
||||
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id,
|
||||
models_v2.SubnetPool.address_scope_id.in_(address_scope_ids),
|
||||
models_v2.Subnet.ip_version == BgpSpeakerNetworkBinding.ip_version,
|
||||
BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id,
|
||||
BgpSpeaker.advertise_tenant_networks == sa.sql.true()]
|
||||
|
||||
def _nexthop_ip_addresses_by_binding_query(self, context,
|
||||
network_id, bgp_speaker_id):
|
||||
"""Return the subquery for locating nexthops by binding network"""
|
||||
nexthops_query = context.session.query(
|
||||
l3_db.RouterPort.router_id,
|
||||
models_v2.IPAllocation.ip_address,
|
||||
models_v2.Subnet.ip_version)
|
||||
filters = self._next_hop_ip_addresses_by_binding_filters(
|
||||
network_id,
|
||||
bgp_speaker_id)
|
||||
nexthops_query = nexthops_query.filter(*filters)
|
||||
return nexthops_query
|
||||
|
||||
def _next_hop_ip_addresses_by_binding_filters(self,
|
||||
network_id,
|
||||
bgp_speaker_id):
|
||||
"""Return the filters for querying nexthops by binding network"""
|
||||
address_scope = aliased(address_scope_db.AddressScope,
|
||||
name='address_scope')
|
||||
return [models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id,
|
||||
models_v2.IPAllocation.subnet_id == models_v2.Subnet.id,
|
||||
BgpSpeaker.id == bgp_speaker_id,
|
||||
BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id,
|
||||
BgpSpeakerNetworkBinding.network_id == network_id,
|
||||
models_v2.Subnet.network_id == BgpSpeakerNetworkBinding.network_id,
|
||||
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id,
|
||||
models_v2.SubnetPool.address_scope_id == address_scope.id,
|
||||
models_v2.Subnet.ip_version == address_scope.ip_version,
|
||||
l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_GW]
|
||||
|
||||
def _nexthop_ip_addresses_by_bgp_speaker_query(self, context,
|
||||
bgp_speaker_id):
|
||||
"""Return the subquery for locating nexthops by BGP speaker"""
|
||||
nexthops_query = context.session.query(
|
||||
l3_db.RouterPort.router_id,
|
||||
models_v2.IPAllocation.ip_address,
|
||||
models_v2.Subnet.ip_version)
|
||||
filters = self._next_hop_ip_addresses_by_bgp_speaker_filters(
|
||||
bgp_speaker_id)
|
||||
nexthops_query = nexthops_query.filter(*filters)
|
||||
return nexthops_query
|
||||
|
||||
def _next_hop_ip_addresses_by_bgp_speaker_filters(self, bgp_speaker_id):
|
||||
"""Return the filters for querying nexthops by BGP speaker"""
|
||||
router_attrs = aliased(l3_attrs_db.RouterExtraAttributes,
|
||||
name='router_attrs')
|
||||
|
||||
return [l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_GW,
|
||||
l3_db.RouterPort.router_id == router_attrs.router_id,
|
||||
BgpSpeakerNetworkBinding.network_id == models_v2.Subnet.network_id,
|
||||
BgpSpeakerNetworkBinding.ip_version == models_v2.Subnet.ip_version,
|
||||
BgpSpeakerNetworkBinding.bgp_speaker_id == bgp_speaker_id,
|
||||
models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id,
|
||||
models_v2.IPAllocation.subnet_id == models_v2.Subnet.id]
|
||||
|
||||
def _tenant_prefixes_by_router(self, context, router_id, bgp_speaker_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
query = context.session.query(models_v2.Subnet.cidr.distinct())
|
||||
filters = self._tenant_prefixes_by_router_filters(router_id,
|
||||
bgp_speaker_id)
|
||||
query = query.filter(*filters)
|
||||
return [x[0] for x in query.all()]
|
||||
|
||||
def _tenant_prefixes_by_router_filters(self, router_id, bgp_speaker_id):
|
||||
binding = aliased(BgpSpeakerNetworkBinding, name='network_binding')
|
||||
subnetpool = aliased(models_v2.SubnetPool,
|
||||
name='subnetpool')
|
||||
router_attrs = aliased(l3_attrs_db.RouterExtraAttributes,
|
||||
name='router_attrs')
|
||||
return [models_v2.Subnet.id == models_v2.IPAllocation.subnet_id,
|
||||
models_v2.Subnet.subnetpool_id == subnetpool.id,
|
||||
l3_db.RouterPort.router_id == router_id,
|
||||
l3_db.Router.id == l3_db.RouterPort.router_id,
|
||||
l3_db.Router.id == router_attrs.router_id,
|
||||
l3_db.Router.gw_port_id == models_v2.Port.id,
|
||||
models_v2.Port.network_id == binding.network_id,
|
||||
binding.bgp_speaker_id == BgpSpeaker.id,
|
||||
l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_INTF,
|
||||
models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id]
|
||||
|
||||
def _tenant_prefixes_by_router_interface(self,
|
||||
context,
|
||||
router_port_id,
|
||||
bgp_speaker_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
query = context.session.query(models_v2.Subnet.cidr.distinct())
|
||||
filters = self._tenant_prefixes_by_router_filters(router_port_id,
|
||||
bgp_speaker_id)
|
||||
query = query.filter(*filters)
|
||||
return [x[0] for x in query.all()]
|
||||
|
||||
def _tenant_prefixes_by_router_port_filters(self,
|
||||
router_port_id,
|
||||
bgp_speaker_id):
|
||||
binding = aliased(BgpSpeakerNetworkBinding, name='network_binding')
|
||||
return [models_v2.Subnet.id == models_v2.IPAllocation.subnet_id,
|
||||
l3_db.RouterPort.port_id == router_port_id,
|
||||
l3_db.Router.id == l3_db.RouterPort.router_id,
|
||||
l3_db.Router.gw_port_id == models_v2.Port.id,
|
||||
models_v2.Port.network_id == binding.network_id,
|
||||
binding.bgp_speaker_id == BgpSpeaker.id,
|
||||
models_v2.Subnet.ip_version == binding.ip_version,
|
||||
l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_INTF,
|
||||
models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id]
|
||||
|
||||
def _bgp_speakers_for_gateway_network(self, context, network_id):
|
||||
"""Return all BgpSpeakers for the given gateway network"""
|
||||
with context.session.begin(subtransactions=True):
|
||||
query = context.session.query(BgpSpeaker)
|
||||
query = query.filter(
|
||||
BgpSpeakerNetworkBinding.network_id == network_id,
|
||||
BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id)
|
||||
try:
|
||||
return query.all()
|
||||
except sa_exc.NoResultFound:
|
||||
raise bgp_ext.NetworkNotBound(network_id=network_id)
|
||||
|
||||
def _bgp_speaker_for_gateway_network(self, context,
|
||||
network_id, ip_version):
|
||||
"""Return the BgpSpeaker by given gateway network and ip_version"""
|
||||
with context.session.begin(subtransactions=True):
|
||||
query = context.session.query(BgpSpeaker)
|
||||
query = query.filter(
|
||||
BgpSpeakerNetworkBinding.network_id == network_id,
|
||||
BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id,
|
||||
BgpSpeakerNetworkBinding.ip_version == ip_version)
|
||||
try:
|
||||
return query.one()
|
||||
except sa_exc.NoResultFound:
|
||||
raise bgp_ext.NetworkNotBoundForIpVersion(
|
||||
network_id=network_id,
|
||||
ip_version=ip_version)
|
||||
|
||||
def _make_advertised_routes_list(self, routes):
|
||||
route_list = ({'destination': x,
|
||||
'next_hop': y} for x, y in routes)
|
||||
return route_list
|
||||
|
||||
def _route_list_from_prefixes_and_next_hop(self, routes, next_hop):
|
||||
route_list = [{'destination': x,
|
||||
'next_hop': next_hop} for x in routes]
|
||||
return route_list
|
||||
|
||||
def _host_route_list_from_tuples(self, ip_next_hop_tuples):
|
||||
"""Return the list of host routes given a list of (IP, nexthop)"""
|
||||
return ({'destination': x + '/32',
|
||||
'next_hop': y} for x, y in ip_next_hop_tuples)
|
||||
|
|
|
@ -154,6 +154,11 @@ class InvalidBgpPeerMd5Authentication(exceptions.BadRequest):
|
|||
message = _("A password must be supplied when using auth_type md5.")
|
||||
|
||||
|
||||
class NetworkNotBoundForIpVersion(NetworkNotBound):
|
||||
message = _("Network %(network_id)s is not bound to a IPv%(ip_version)s "
|
||||
"BgpSpeaker.")
|
||||
|
||||
|
||||
class Bgp(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import netaddr
|
||||
from tempest import config
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
from tempest import test
|
||||
|
@ -41,8 +42,25 @@ class BgpSpeakerTestJSONBase(base.BaseAdminNetworkTest):
|
|||
msg = "BGP Speaker extension is not enabled."
|
||||
raise cls.skipException(msg)
|
||||
|
||||
cls.admin_routerports = []
|
||||
cls.admin_floatingips = []
|
||||
cls.admin_routers = []
|
||||
cls.ext_net_id = CONF.network.public_network_id
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
for floatingip in cls.admin_floatingips:
|
||||
cls._try_delete_resource(cls.admin_client.delete_floatingip,
|
||||
floatingip['id'])
|
||||
for routerport in cls.admin_routerports:
|
||||
cls._try_delete_resource(
|
||||
cls.admin_client.remove_router_interface_with_subnet_id,
|
||||
routerport['router_id'], routerport['subnet_id'])
|
||||
for router in cls.admin_routers:
|
||||
cls._try_delete_resource(cls.admin_client.delete_router,
|
||||
router['id'])
|
||||
super(BgpSpeakerTestJSONBase, cls).resource_cleanup()
|
||||
|
||||
def create_bgp_speaker(self, auto_delete=True, **args):
|
||||
data = {'bgp_speaker': args}
|
||||
bgp_speaker = self.admin_client.create_bgp_speaker(data)
|
||||
|
@ -171,3 +189,94 @@ class BgpSpeakerTestJSON(BgpSpeakerTestJSONBase):
|
|||
bgp_speaker = self.admin_client.get_bgp_speaker(bgp_speaker_id)
|
||||
network_list = bgp_speaker['bgp-speaker']['networks']
|
||||
self.assertTrue(not network_list)
|
||||
|
||||
@test.idempotent_id('5bef22ad-5e70-4f7b-937a-dc1944642996')
|
||||
def test_get_advertised_routes_null_address_scope(self):
|
||||
self.useFixture(fixtures.LockFixture('gateway_network_binding'))
|
||||
bgp_speaker = self.create_bgp_speaker(**self.default_bgp_speaker_args)
|
||||
bgp_speaker_id = bgp_speaker['bgp-speaker']['id']
|
||||
self.admin_client.add_bgp_gateway_network(bgp_speaker_id,
|
||||
self.ext_net_id)
|
||||
routes = self.admin_client.get_bgp_advertised_routes(bgp_speaker_id)
|
||||
self.assertEqual(0, len(routes['advertised_routes']))
|
||||
|
||||
@test.idempotent_id('cae9cdb1-ad65-423c-9604-d4cd0073616e')
|
||||
def test_get_advertised_routes_floating_ips(self):
|
||||
self.useFixture(fixtures.LockFixture('gateway_network_binding'))
|
||||
bgp_speaker = self.create_bgp_speaker(**self.default_bgp_speaker_args)
|
||||
bgp_speaker_id = bgp_speaker['bgp-speaker']['id']
|
||||
self.admin_client.add_bgp_gateway_network(bgp_speaker_id,
|
||||
self.ext_net_id)
|
||||
tenant_net = self.create_network()
|
||||
tenant_subnet = self.create_subnet(tenant_net)
|
||||
ext_gw_info = {'network_id': self.ext_net_id}
|
||||
router = self.admin_client.create_router(
|
||||
'my-router',
|
||||
external_gateway_info=ext_gw_info,
|
||||
admin_state_up=True,
|
||||
distributed=False)
|
||||
self.admin_routers.append(router['router'])
|
||||
self.admin_client.add_router_interface_with_subnet_id(
|
||||
router['router']['id'],
|
||||
tenant_subnet['id'])
|
||||
self.admin_routerports.append({'router_id': router['router']['id'],
|
||||
'subnet_id': tenant_subnet['id']})
|
||||
tenant_port = self.create_port(tenant_net)
|
||||
floatingip = self.create_floatingip(self.ext_net_id)
|
||||
self.admin_floatingips.append(floatingip)
|
||||
self.client.update_floatingip(floatingip['id'],
|
||||
port_id=tenant_port['id'])
|
||||
routes = self.admin_client.get_bgp_advertised_routes(bgp_speaker_id)
|
||||
self.assertEqual(1, len(routes['advertised_routes']))
|
||||
self.assertEqual(floatingip['floating_ip_address'] + '/32',
|
||||
routes['advertised_routes'][0]['destination'])
|
||||
|
||||
@test.idempotent_id('c9ad566e-fe8f-4559-8303-bbad9062a30c')
|
||||
def test_get_advertised_routes_tenant_networks(self):
|
||||
self.useFixture(fixtures.LockFixture('gateway_network_binding'))
|
||||
addr_scope = self.create_address_scope('my-scope', ip_version=4)
|
||||
ext_net = self.create_shared_network(**{'router:external': True})
|
||||
tenant_net = self.create_network()
|
||||
ext_subnetpool = self.create_subnetpool(
|
||||
'test-pool-ext',
|
||||
is_admin=True,
|
||||
default_prefixlen=24,
|
||||
address_scope_id=addr_scope['id'],
|
||||
prefixes=['8.0.0.0/8'])
|
||||
tenant_subnetpool = self.create_subnetpool(
|
||||
'tenant-test-pool',
|
||||
default_prefixlen=25,
|
||||
address_scope_id=addr_scope['id'],
|
||||
prefixes=['10.10.0.0/16'])
|
||||
self.create_subnet({'id': ext_net['id']},
|
||||
cidr=netaddr.IPNetwork('8.0.0.0/24'),
|
||||
ip_version=4,
|
||||
client=self.admin_client,
|
||||
subnetpool_id=ext_subnetpool['id'])
|
||||
tenant_subnet = self.create_subnet(
|
||||
{'id': tenant_net['id']},
|
||||
cidr=netaddr.IPNetwork('10.10.0.0/24'),
|
||||
ip_version=4,
|
||||
subnetpool_id=tenant_subnetpool['id'])
|
||||
ext_gw_info = {'network_id': ext_net['id']}
|
||||
router = self.admin_client.create_router(
|
||||
'my-router',
|
||||
external_gateway_info=ext_gw_info,
|
||||
distributed=False)['router']
|
||||
self.admin_routers.append(router)
|
||||
self.admin_client.add_router_interface_with_subnet_id(
|
||||
router['id'],
|
||||
tenant_subnet['id'])
|
||||
self.admin_routerports.append({'router_id': router['id'],
|
||||
'subnet_id': tenant_subnet['id']})
|
||||
bgp_speaker = self.create_bgp_speaker(**self.default_bgp_speaker_args)
|
||||
bgp_speaker_id = bgp_speaker['bgp-speaker']['id']
|
||||
self.admin_client.add_bgp_gateway_network(bgp_speaker_id,
|
||||
ext_net['id'])
|
||||
routes = self.admin_client.get_bgp_advertised_routes(bgp_speaker_id)
|
||||
self.assertEqual(1, len(routes['advertised_routes']))
|
||||
self.assertEqual(tenant_subnet['cidr'],
|
||||
routes['advertised_routes'][0]['destination'])
|
||||
fixed_ip = router['external_gateway_info']['external_fixed_ips'][0]
|
||||
self.assertEqual(fixed_ip['ip_address'],
|
||||
routes['advertised_routes'][0]['next_hop'])
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import netaddr
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
from neutron.tests.api import test_bgp_speaker_extensions as test_base
|
||||
|
@ -51,3 +52,69 @@ class BgpSpeakerTestJSONNegative(test_base.BgpSpeakerTestJSONBase):
|
|||
|
||||
self.assertRaises(lib_exc.BadRequest, self.update_bgp_speaker,
|
||||
bgp_speaker_id, local_as='4321')
|
||||
|
||||
@test.idempotent_id('9cc33701-51e5-421f-a5d5-fd7b330e550f')
|
||||
def test_get_advertised_routes_tenant_networks(self):
|
||||
addr_scope1 = self.create_address_scope('my-scope1', ip_version=4)
|
||||
addr_scope2 = self.create_address_scope('my-scope2', ip_version=4)
|
||||
ext_net = self.create_shared_network(**{'router:external': True})
|
||||
tenant_net1 = self.create_network()
|
||||
tenant_net2 = self.create_network()
|
||||
ext_subnetpool = self.create_subnetpool(
|
||||
'test-pool-ext',
|
||||
is_admin=True,
|
||||
default_prefixlen=24,
|
||||
address_scope_id=addr_scope1['id'],
|
||||
prefixes=['8.0.0.0/8'])
|
||||
tenant_subnetpool1 = self.create_subnetpool(
|
||||
'tenant-test-pool',
|
||||
default_prefixlen=25,
|
||||
address_scope_id=addr_scope1['id'],
|
||||
prefixes=['10.10.0.0/16'])
|
||||
tenant_subnetpool2 = self.create_subnetpool(
|
||||
'tenant-test-pool',
|
||||
default_prefixlen=25,
|
||||
address_scope_id=addr_scope2['id'],
|
||||
prefixes=['11.10.0.0/16'])
|
||||
self.create_subnet({'id': ext_net['id']},
|
||||
cidr=netaddr.IPNetwork('8.0.0.0/24'),
|
||||
ip_version=4,
|
||||
client=self.admin_client,
|
||||
subnetpool_id=ext_subnetpool['id'])
|
||||
tenant_subnet1 = self.create_subnet(
|
||||
{'id': tenant_net1['id']},
|
||||
cidr=netaddr.IPNetwork('10.10.0.0/24'),
|
||||
ip_version=4,
|
||||
subnetpool_id=tenant_subnetpool1['id'])
|
||||
tenant_subnet2 = self.create_subnet(
|
||||
{'id': tenant_net2['id']},
|
||||
cidr=netaddr.IPNetwork('11.10.0.0/24'),
|
||||
ip_version=4,
|
||||
subnetpool_id=tenant_subnetpool2['id'])
|
||||
ext_gw_info = {'network_id': ext_net['id']}
|
||||
router = self.admin_client.create_router(
|
||||
'my-router',
|
||||
distributed=False,
|
||||
external_gateway_info=ext_gw_info)['router']
|
||||
self.admin_routers.append(router)
|
||||
self.admin_client.add_router_interface_with_subnet_id(
|
||||
router['id'],
|
||||
tenant_subnet1['id'])
|
||||
self.admin_routerports.append({'router_id': router['id'],
|
||||
'subnet_id': tenant_subnet1['id']})
|
||||
self.admin_client.add_router_interface_with_subnet_id(
|
||||
router['id'],
|
||||
tenant_subnet2['id'])
|
||||
self.admin_routerports.append({'router_id': router['id'],
|
||||
'subnet_id': tenant_subnet2['id']})
|
||||
bgp_speaker = self.create_bgp_speaker(**self.default_bgp_speaker_args)
|
||||
bgp_speaker_id = bgp_speaker['bgp-speaker']['id']
|
||||
self.admin_client.add_bgp_gateway_network(bgp_speaker_id,
|
||||
ext_net['id'])
|
||||
routes = self.admin_client.get_bgp_advertised_routes(bgp_speaker_id)
|
||||
self.assertEqual(1, len(routes['advertised_routes']))
|
||||
self.assertEqual(tenant_subnet1['cidr'],
|
||||
routes['advertised_routes'][0]['destination'])
|
||||
fixed_ip = router['external_gateway_info']['external_fixed_ips'][0]
|
||||
self.assertEqual(fixed_ip['ip_address'],
|
||||
routes['advertised_routes'][0]['next_hop'])
|
||||
|
|
|
@ -320,6 +320,22 @@ class NetworkClientJSON(service_client.ServiceClient):
|
|||
body = json.loads(body)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def get_bgp_advertised_routes(self, bgp_speaker_id):
|
||||
base_uri = '%s/bgp-speakers/%s/get_advertised_routes'
|
||||
uri = base_uri % (self.uri_prefix, bgp_speaker_id)
|
||||
resp, body = self.get(uri)
|
||||
body = {'advertised_routes': self.deserialize_list(body)}
|
||||
self.expected_success(200, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def get_bgp_router_routes(self, router_id):
|
||||
base_uri = '%s/router-routes/%s'
|
||||
uri = base_uri % (self.uri_prefix, router_id)
|
||||
resp, body = self.get(uri)
|
||||
body = self.deserialize_list(body)
|
||||
self.expected_success(200, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
# Common methods that are hard to automate
|
||||
def create_bulk_network(self, names, shared=False):
|
||||
network_list = [{'name': name, 'shared': shared} for name in names]
|
||||
|
|
|
@ -13,10 +13,13 @@
|
|||
# under the License.
|
||||
|
||||
import contextlib
|
||||
import netaddr
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.api.v2 import attributes as attrs
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.extensions import bgp
|
||||
from neutron.extensions import external_net
|
||||
from neutron import manager
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.services.bgp import bgp_plugin
|
||||
|
@ -67,10 +70,79 @@ class BgpEntityCreationMixin(object):
|
|||
yield bgp_peer
|
||||
self.bgp_plugin.delete_bgp_peer(self.context, bgp_peer['id'])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def bgp_speaker_with_gateway_network(self, address_scope_id, local_as,
|
||||
advertise_fip_host_routes=True,
|
||||
advertise_tenant_networks=True,
|
||||
network_external=True,
|
||||
fmt=None, set_context=False):
|
||||
pass
|
||||
|
||||
@contextlib.contextmanager
|
||||
def bgp_speaker_with_router(self, address_scope_id, local_as,
|
||||
gw_network_id=None, gw_subnet_ids=None,
|
||||
tenant_subnet_ids=None,
|
||||
advertise_fip_host_routes=True,
|
||||
advertise_tenant_networks=True,
|
||||
fmt=None, set_context=False,
|
||||
router_distributed=False):
|
||||
pass
|
||||
|
||||
@contextlib.contextmanager
|
||||
def router(self, name='bgp-test-router', tenant_id=_uuid(),
|
||||
admin_state_up=True, **kwargs):
|
||||
request = {'router': {'tenant_id': tenant_id,
|
||||
'name': name,
|
||||
'admin_state_up': admin_state_up}}
|
||||
for arg in kwargs:
|
||||
request['router'][arg] = kwargs[arg]
|
||||
router = self.l3plugin.create_router(self.context, request)
|
||||
yield router
|
||||
|
||||
@contextlib.contextmanager
|
||||
def router_with_external_and_tenant_networks(
|
||||
self,
|
||||
tenant_id=_uuid(),
|
||||
gw_prefix='8.8.8.0/24',
|
||||
tenant_prefix='192.168.0.0/16',
|
||||
address_scope=None,
|
||||
distributed=False):
|
||||
prefixes = [gw_prefix, tenant_prefix]
|
||||
gw_ip_net = netaddr.IPNetwork(gw_prefix)
|
||||
tenant_ip_net = netaddr.IPNetwork(tenant_prefix)
|
||||
subnetpool_args = {'tenant_id': tenant_id,
|
||||
'name': 'bgp-pool'}
|
||||
if address_scope:
|
||||
subnetpool_args['address_scope_id'] = address_scope['id']
|
||||
|
||||
with self.network() as ext_net, self.network() as int_net,\
|
||||
self.subnetpool(prefixes, **subnetpool_args) as pool:
|
||||
subnetpool_id = pool['subnetpool']['id']
|
||||
gw_net_id = ext_net['network']['id']
|
||||
with self.subnet(ext_net,
|
||||
cidr=gw_prefix,
|
||||
subnetpool_id=subnetpool_id,
|
||||
ip_version=gw_ip_net.version),\
|
||||
self.subnet(int_net,
|
||||
cidr=tenant_prefix,
|
||||
subnetpool_id=subnetpool_id,
|
||||
ip_version=tenant_ip_net.version) as int_subnet:
|
||||
self._update('networks', gw_net_id,
|
||||
{'network': {external_net.EXTERNAL: True}})
|
||||
ext_gw_info = {'network_id': gw_net_id}
|
||||
with self.router(external_gateway_info=ext_gw_info,
|
||||
distributed=distributed) as router:
|
||||
router_id = router['id']
|
||||
router_interface_info = {'subnet_id':
|
||||
int_subnet['subnet']['id']}
|
||||
self.l3plugin.add_router_interface(self.context,
|
||||
router_id,
|
||||
router_interface_info)
|
||||
yield router, ext_net, int_net
|
||||
|
||||
|
||||
class BgpTests(test_plugin.Ml2PluginV2TestCase,
|
||||
BgpEntityCreationMixin):
|
||||
#FIXME(tidwellr) Lots of duplicated setup code, try to streamline
|
||||
fmt = 'json'
|
||||
|
||||
def setUp(self):
|
||||
|
@ -79,6 +151,8 @@ class BgpTests(test_plugin.Ml2PluginV2TestCase,
|
|||
p_const.L3_ROUTER_NAT)
|
||||
self.bgp_plugin = bgp_plugin.BgpPlugin()
|
||||
self.plugin = manager.NeutronManager.get_plugin()
|
||||
self.l3plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
p_const.L3_ROUTER_NAT)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def subnetpool_with_address_scope(self, ip_version, prefixes=None,
|
||||
|
@ -335,3 +409,422 @@ class BgpTests(test_plugin.Ml2PluginV2TestCase,
|
|||
self.assertRaises(bgp.InvalidBgpPeerMd5Authentication,
|
||||
self.bgp_plugin.create_bgp_peer,
|
||||
self.context, bgp_peer)
|
||||
|
||||
def test__get_address_scope_ids_for_bgp_speaker(self):
|
||||
prefixes1 = ['8.0.0.0/8']
|
||||
prefixes2 = ['9.0.0.0/8']
|
||||
prefixes3 = ['10.0.0.0/8']
|
||||
tenant_id = _uuid()
|
||||
with self.bgp_speaker(4, 1234) as speaker,\
|
||||
self.subnetpool_with_address_scope(4,
|
||||
prefixes=prefixes1,
|
||||
tenant_id=tenant_id) as sp1,\
|
||||
self.subnetpool_with_address_scope(4,
|
||||
prefixes=prefixes2,
|
||||
tenant_id=tenant_id) as sp2,\
|
||||
self.subnetpool_with_address_scope(4,
|
||||
prefixes=prefixes3,
|
||||
tenant_id=tenant_id) as sp3,\
|
||||
self.network() as network1, self.network() as network2,\
|
||||
self.network() as network3:
|
||||
network1_id = network1['network']['id']
|
||||
network2_id = network2['network']['id']
|
||||
network3_id = network3['network']['id']
|
||||
base_subnet_data = {'allocation_pools': attrs.ATTR_NOT_SPECIFIED,
|
||||
'cidr': attrs.ATTR_NOT_SPECIFIED,
|
||||
'prefixlen': attrs.ATTR_NOT_SPECIFIED,
|
||||
'ip_version': 4,
|
||||
'enable_dhcp': True,
|
||||
'dns_nameservers': attrs.ATTR_NOT_SPECIFIED,
|
||||
'host_routes': attrs.ATTR_NOT_SPECIFIED}
|
||||
subnet1_data = {'network_id': network1_id,
|
||||
'subnetpool_id': sp1['id'],
|
||||
'name': 'subnet1',
|
||||
'tenant_id': tenant_id}
|
||||
subnet2_data = {'network_id': network2_id,
|
||||
'subnetpool_id': sp2['id'],
|
||||
'name': 'subnet2',
|
||||
'tenant_id': tenant_id}
|
||||
subnet3_data = {'network_id': network3_id,
|
||||
'subnetpool_id': sp3['id'],
|
||||
'name': 'subnet2',
|
||||
'tenant_id': tenant_id}
|
||||
for k in base_subnet_data:
|
||||
subnet1_data[k] = base_subnet_data[k]
|
||||
subnet2_data[k] = base_subnet_data[k]
|
||||
subnet3_data[k] = base_subnet_data[k]
|
||||
|
||||
self.plugin.create_subnet(self.context, {'subnet': subnet1_data})
|
||||
self.plugin.create_subnet(self.context, {'subnet': subnet2_data})
|
||||
self.plugin.create_subnet(self.context, {'subnet': subnet3_data})
|
||||
self.bgp_plugin.add_gateway_network(self.context, speaker['id'],
|
||||
{'network_id': network1_id})
|
||||
self.bgp_plugin.add_gateway_network(self.context, speaker['id'],
|
||||
{'network_id': network2_id})
|
||||
scopes = self.bgp_plugin._get_address_scope_ids_for_bgp_speaker(
|
||||
self.context,
|
||||
speaker['id'])
|
||||
self.assertEqual(2, len(scopes))
|
||||
self.assertTrue(sp1['address_scope_id'] in scopes)
|
||||
self.assertTrue(sp2['address_scope_id'] in scopes)
|
||||
|
||||
def test_get_routes_by_bgp_speaker_binding(self):
|
||||
gw_prefix = '172.16.10.0/24'
|
||||
tenant_prefix = '10.10.10.0/24'
|
||||
tenant_id = _uuid()
|
||||
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||
'shared': True, 'name': 'bgp-scope'}
|
||||
scope = self.plugin.create_address_scope(
|
||||
self.context,
|
||||
{'address_scope': scope_data})
|
||||
with self.router_with_external_and_tenant_networks(
|
||||
tenant_id=tenant_id,
|
||||
gw_prefix=gw_prefix,
|
||||
tenant_prefix=tenant_prefix,
|
||||
address_scope=scope) as res:
|
||||
router, ext_net, int_net = res
|
||||
ext_gw_info = router['external_gateway_info']
|
||||
gw_net_id = ext_net['network']['id']
|
||||
with self.bgp_speaker(4, 1234,
|
||||
networks=[gw_net_id]) as speaker:
|
||||
bgp_speaker_id = speaker['id']
|
||||
routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding(
|
||||
self.context,
|
||||
bgp_speaker_id,
|
||||
gw_net_id)
|
||||
routes = list(routes)
|
||||
next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address']
|
||||
self.assertEqual(1, len(routes))
|
||||
self.assertEqual(tenant_prefix, routes[0]['destination'])
|
||||
self.assertEqual(next_hop, routes[0]['next_hop'])
|
||||
|
||||
def test_get_routes_by_binding_network(self):
|
||||
gw_prefix = '172.16.10.0/24'
|
||||
tenant_prefix = '10.10.10.0/24'
|
||||
tenant_id = _uuid()
|
||||
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||
'shared': True, 'name': 'bgp-scope'}
|
||||
scope = self.plugin.create_address_scope(
|
||||
self.context,
|
||||
{'address_scope': scope_data})
|
||||
with self.router_with_external_and_tenant_networks(
|
||||
tenant_id=tenant_id,
|
||||
gw_prefix=gw_prefix,
|
||||
tenant_prefix=tenant_prefix,
|
||||
address_scope=scope) as res:
|
||||
router, ext_net, int_net = res
|
||||
ext_gw_info = router['external_gateway_info']
|
||||
gw_net_id = ext_net['network']['id']
|
||||
with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker:
|
||||
bgp_speaker_id = speaker['id']
|
||||
routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding(
|
||||
self.context,
|
||||
bgp_speaker_id,
|
||||
gw_net_id)
|
||||
routes = list(routes)
|
||||
next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address']
|
||||
self.assertEqual(1, len(routes))
|
||||
self.assertEqual(tenant_prefix, routes[0]['destination'])
|
||||
self.assertEqual(next_hop, routes[0]['next_hop'])
|
||||
|
||||
def _advertised_routes_by_bgp_speaker(self,
|
||||
bgp_speaker_ip_version,
|
||||
local_as,
|
||||
tenant_cidr,
|
||||
gateway_cidr,
|
||||
fip_routes=True,
|
||||
router_distributed=False):
|
||||
tenant_id = _uuid()
|
||||
scope_data = {'tenant_id': tenant_id,
|
||||
'ip_version': bgp_speaker_ip_version,
|
||||
'shared': True,
|
||||
'name': 'bgp-scope'}
|
||||
scope = self.plugin.create_address_scope(
|
||||
self.context,
|
||||
{'address_scope': scope_data})
|
||||
with self.router_with_external_and_tenant_networks(
|
||||
tenant_id=tenant_id,
|
||||
gw_prefix=gateway_cidr,
|
||||
tenant_prefix=tenant_cidr,
|
||||
address_scope=scope,
|
||||
distributed=router_distributed) as res:
|
||||
router, ext_net, int_net = res
|
||||
gw_net_id = ext_net['network']['id']
|
||||
with self.bgp_speaker(
|
||||
bgp_speaker_ip_version,
|
||||
local_as,
|
||||
networks=[gw_net_id],
|
||||
advertise_fip_host_routes=fip_routes) as speaker:
|
||||
routes = self.bgp_plugin.get_advertised_routes(
|
||||
self.context,
|
||||
speaker['id'])
|
||||
return routes['advertised_routes']
|
||||
|
||||
def test__tenant_prefixes_by_router_no_gateway_port(self):
|
||||
with self.network() as net1, self.network() as net2,\
|
||||
self.subnetpool_with_address_scope(6, tenant_id='test-tenant',
|
||||
prefixes=['2001:db8::/63']) as pool:
|
||||
subnetpool_id = pool['id']
|
||||
with self.subnet(network=net1,
|
||||
cidr=None,
|
||||
subnetpool_id=subnetpool_id,
|
||||
ip_version=6) as ext_subnet,\
|
||||
self.subnet(network=net2,
|
||||
cidr=None,
|
||||
subnetpool_id=subnetpool_id,
|
||||
ip_version=6) as int_subnet,\
|
||||
self.router() as router:
|
||||
|
||||
router_id = router['id']
|
||||
int_subnet_id = int_subnet['subnet']['id']
|
||||
ext_subnet_id = ext_subnet['subnet']['id']
|
||||
self.l3plugin.add_router_interface(self.context,
|
||||
router_id,
|
||||
{'subnet_id':
|
||||
int_subnet_id})
|
||||
self.l3plugin.add_router_interface(self.context,
|
||||
router_id,
|
||||
{'subnet_id':
|
||||
ext_subnet_id})
|
||||
with self.bgp_speaker(6, 1234) as speaker:
|
||||
bgp_speaker_id = speaker['id']
|
||||
cidrs = list(self.bgp_plugin._tenant_prefixes_by_router(
|
||||
self.context,
|
||||
router_id,
|
||||
bgp_speaker_id))
|
||||
self.assertFalse(cidrs)
|
||||
|
||||
def test_get_ipv6_tenant_subnet_routes_by_bgp_speaker_ipv6(self):
|
||||
tenant_cidr = '2001:db8::/64'
|
||||
binding_cidr = '2001:ab8::/64'
|
||||
routes = self._advertised_routes_by_bgp_speaker(6, 1234, tenant_cidr,
|
||||
binding_cidr)
|
||||
self.assertEqual(1, len(routes))
|
||||
dest_prefix = routes[0]['destination']
|
||||
next_hop = routes[0]['next_hop']
|
||||
self.assertEqual(tenant_cidr, dest_prefix)
|
||||
self.assertTrue(netaddr.IPSet([binding_cidr]).__contains__(next_hop))
|
||||
|
||||
def test_get_ipv4_tenant_subnet_routes_by_bgp_speaker_ipv4(self):
|
||||
tenant_cidr = '172.16.10.0/24'
|
||||
binding_cidr = '20.10.1.0/24'
|
||||
routes = self._advertised_routes_by_bgp_speaker(4, 1234, tenant_cidr,
|
||||
binding_cidr)
|
||||
routes = list(routes)
|
||||
self.assertEqual(1, len(routes))
|
||||
dest_prefix = routes[0]['destination']
|
||||
next_hop = routes[0]['next_hop']
|
||||
self.assertEqual(tenant_cidr, dest_prefix)
|
||||
self.assertTrue(netaddr.IPSet([binding_cidr]).__contains__(next_hop))
|
||||
|
||||
def test_get_ipv4_tenant_subnet_routes_by_bgp_speaker_dvr_router(self):
|
||||
tenant_cidr = '172.16.10.0/24'
|
||||
binding_cidr = '20.10.1.0/24'
|
||||
routes = self._advertised_routes_by_bgp_speaker(
|
||||
4,
|
||||
1234,
|
||||
tenant_cidr,
|
||||
binding_cidr,
|
||||
router_distributed=True)
|
||||
routes = list(routes)
|
||||
self.assertEqual(1, len(routes))
|
||||
|
||||
def test_all_routes_by_bgp_speaker_different_tenant_address_scope(self):
|
||||
binding_cidr = '2001:db8::/64'
|
||||
tenant_cidr = '2002:ab8::/64'
|
||||
with self.subnetpool_with_address_scope(6, tenant_id='test-tenant',
|
||||
prefixes=[binding_cidr]) as ext_pool,\
|
||||
self.subnetpool_with_address_scope(6, tenant_id='test-tenant',
|
||||
prefixes=[tenant_cidr]) as int_pool,\
|
||||
self.network() as ext_net, self.network() as int_net:
|
||||
gw_net_id = ext_net['network']['id']
|
||||
ext_pool_id = ext_pool['id']
|
||||
int_pool_id = int_pool['id']
|
||||
self._update('networks', gw_net_id,
|
||||
{'network': {external_net.EXTERNAL: True}})
|
||||
with self.subnet(cidr=None,
|
||||
subnetpool_id=ext_pool_id,
|
||||
network=ext_net,
|
||||
ip_version=6) as ext_subnet,\
|
||||
self.subnet(cidr=None,
|
||||
subnetpool_id=int_pool_id,
|
||||
network=int_net,
|
||||
ip_version=6) as int_subnet,\
|
||||
self.router() as router:
|
||||
router_id = router['id']
|
||||
int_subnet_id = int_subnet['subnet']['id']
|
||||
ext_subnet_id = ext_subnet['subnet']['id']
|
||||
self.l3plugin.add_router_interface(self.context,
|
||||
router_id,
|
||||
{'subnet_id':
|
||||
int_subnet_id})
|
||||
self.l3plugin.add_router_interface(self.context,
|
||||
router_id,
|
||||
{'subnet_id':
|
||||
ext_subnet_id})
|
||||
with self.bgp_speaker(6, 1234,
|
||||
networks=[gw_net_id]) as speaker:
|
||||
bgp_speaker_id = speaker['id']
|
||||
cidrs = self.bgp_plugin.get_routes_by_bgp_speaker_id(
|
||||
self.context,
|
||||
bgp_speaker_id)
|
||||
self.assertEqual(0, len(list(cidrs)))
|
||||
|
||||
def test__get_routes_by_router_with_fip(self):
|
||||
gw_prefix = '172.16.10.0/24'
|
||||
tenant_prefix = '10.10.10.0/24'
|
||||
tenant_id = _uuid()
|
||||
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||
'shared': True, 'name': 'bgp-scope'}
|
||||
scope = self.plugin.create_address_scope(
|
||||
self.context,
|
||||
{'address_scope': scope_data})
|
||||
with self.router_with_external_and_tenant_networks(
|
||||
tenant_id=tenant_id,
|
||||
gw_prefix=gw_prefix,
|
||||
tenant_prefix=tenant_prefix,
|
||||
address_scope=scope) as res:
|
||||
router, ext_net, int_net = res
|
||||
ext_gw_info = router['external_gateway_info']
|
||||
gw_net_id = ext_net['network']['id']
|
||||
tenant_net_id = int_net['network']['id']
|
||||
fixed_port_data = {'port':
|
||||
{'name': 'test',
|
||||
'network_id': tenant_net_id,
|
||||
'tenant_id': tenant_id,
|
||||
'admin_state_up': True,
|
||||
'device_id': _uuid(),
|
||||
'device_owner': 'compute:nova',
|
||||
'mac_address': attrs.ATTR_NOT_SPECIFIED,
|
||||
'fixed_ips': attrs.ATTR_NOT_SPECIFIED}}
|
||||
fixed_port = self.plugin.create_port(self.context,
|
||||
fixed_port_data)
|
||||
fip_data = {'floatingip': {'floating_network_id': gw_net_id,
|
||||
'tenant_id': tenant_id,
|
||||
'port_id': fixed_port['id']}}
|
||||
fip = self.l3plugin.create_floatingip(self.context, fip_data)
|
||||
fip_prefix = fip['floating_ip_address'] + '/32'
|
||||
with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker:
|
||||
bgp_speaker_id = speaker['id']
|
||||
routes = self.bgp_plugin._get_routes_by_router(self.context,
|
||||
router['id'])
|
||||
routes = routes[bgp_speaker_id]
|
||||
next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address']
|
||||
self.assertEqual(2, len(routes))
|
||||
tenant_prefix_found = False
|
||||
fip_prefix_found = False
|
||||
for route in routes:
|
||||
self.assertEqual(next_hop, route['next_hop'])
|
||||
if route['destination'] == tenant_prefix:
|
||||
tenant_prefix_found = True
|
||||
if route['destination'] == fip_prefix:
|
||||
fip_prefix_found = True
|
||||
self.assertTrue(tenant_prefix_found)
|
||||
self.assertTrue(fip_prefix_found)
|
||||
|
||||
def test_get_routes_by_bgp_speaker_id_with_fip(self):
|
||||
gw_prefix = '172.16.10.0/24'
|
||||
tenant_prefix = '10.10.10.0/24'
|
||||
tenant_id = _uuid()
|
||||
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||
'shared': True, 'name': 'bgp-scope'}
|
||||
scope = self.plugin.create_address_scope(
|
||||
self.context,
|
||||
{'address_scope': scope_data})
|
||||
with self.router_with_external_and_tenant_networks(
|
||||
tenant_id=tenant_id,
|
||||
gw_prefix=gw_prefix,
|
||||
tenant_prefix=tenant_prefix,
|
||||
address_scope=scope) as res:
|
||||
router, ext_net, int_net = res
|
||||
ext_gw_info = router['external_gateway_info']
|
||||
gw_net_id = ext_net['network']['id']
|
||||
tenant_net_id = int_net['network']['id']
|
||||
fixed_port_data = {'port':
|
||||
{'name': 'test',
|
||||
'network_id': tenant_net_id,
|
||||
'tenant_id': tenant_id,
|
||||
'admin_state_up': True,
|
||||
'device_id': _uuid(),
|
||||
'device_owner': 'compute:nova',
|
||||
'mac_address': attrs.ATTR_NOT_SPECIFIED,
|
||||
'fixed_ips': attrs.ATTR_NOT_SPECIFIED}}
|
||||
fixed_port = self.plugin.create_port(self.context,
|
||||
fixed_port_data)
|
||||
fip_data = {'floatingip': {'floating_network_id': gw_net_id,
|
||||
'tenant_id': tenant_id,
|
||||
'port_id': fixed_port['id']}}
|
||||
fip = self.l3plugin.create_floatingip(self.context, fip_data)
|
||||
fip_prefix = fip['floating_ip_address'] + '/32'
|
||||
with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker:
|
||||
bgp_speaker_id = speaker['id']
|
||||
routes = self.bgp_plugin.get_routes_by_bgp_speaker_id(
|
||||
self.context,
|
||||
bgp_speaker_id)
|
||||
routes = list(routes)
|
||||
next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address']
|
||||
self.assertEqual(2, len(routes))
|
||||
tenant_prefix_found = False
|
||||
fip_prefix_found = False
|
||||
for route in routes:
|
||||
self.assertEqual(next_hop, route['next_hop'])
|
||||
if route['destination'] == tenant_prefix:
|
||||
tenant_prefix_found = True
|
||||
if route['destination'] == fip_prefix:
|
||||
fip_prefix_found = True
|
||||
self.assertTrue(tenant_prefix_found)
|
||||
self.assertTrue(fip_prefix_found)
|
||||
|
||||
def test_get_routes_by_bgp_speaker_binding_with_fip(self):
|
||||
gw_prefix = '172.16.10.0/24'
|
||||
tenant_prefix = '10.10.10.0/24'
|
||||
tenant_id = _uuid()
|
||||
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||
'shared': True, 'name': 'bgp-scope'}
|
||||
scope = self.plugin.create_address_scope(
|
||||
self.context,
|
||||
{'address_scope': scope_data})
|
||||
with self.router_with_external_and_tenant_networks(
|
||||
tenant_id=tenant_id,
|
||||
gw_prefix=gw_prefix,
|
||||
tenant_prefix=tenant_prefix,
|
||||
address_scope=scope) as res:
|
||||
router, ext_net, int_net = res
|
||||
ext_gw_info = router['external_gateway_info']
|
||||
gw_net_id = ext_net['network']['id']
|
||||
tenant_net_id = int_net['network']['id']
|
||||
fixed_port_data = {'port':
|
||||
{'name': 'test',
|
||||
'network_id': tenant_net_id,
|
||||
'tenant_id': tenant_id,
|
||||
'admin_state_up': True,
|
||||
'device_id': _uuid(),
|
||||
'device_owner': 'compute:nova',
|
||||
'mac_address': attrs.ATTR_NOT_SPECIFIED,
|
||||
'fixed_ips': attrs.ATTR_NOT_SPECIFIED}}
|
||||
fixed_port = self.plugin.create_port(self.context,
|
||||
fixed_port_data)
|
||||
fip_data = {'floatingip': {'floating_network_id': gw_net_id,
|
||||
'tenant_id': tenant_id,
|
||||
'port_id': fixed_port['id']}}
|
||||
fip = self.l3plugin.create_floatingip(self.context, fip_data)
|
||||
fip_prefix = fip['floating_ip_address'] + '/32'
|
||||
with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker:
|
||||
bgp_speaker_id = speaker['id']
|
||||
routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding(
|
||||
self.context,
|
||||
bgp_speaker_id,
|
||||
gw_net_id)
|
||||
routes = list(routes)
|
||||
next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address']
|
||||
self.assertEqual(2, len(routes))
|
||||
tenant_prefix_found = False
|
||||
fip_prefix_found = False
|
||||
for route in routes:
|
||||
self.assertEqual(next_hop, route['next_hop'])
|
||||
if route['destination'] == tenant_prefix:
|
||||
tenant_prefix_found = True
|
||||
if route['destination'] == fip_prefix:
|
||||
fip_prefix_found = True
|
||||
self.assertTrue(tenant_prefix_found)
|
||||
self.assertTrue(fip_prefix_found)
|
||||
|
|
Loading…
Reference in New Issue