neutron-tempest-plugin/neutron_tempest_plugin/neutron_dynamic_routing/scenario/test_simple_bgp.py

228 lines
8.7 KiB
Python

# Copyright (c) 2017 Midokura SARL
# 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 tempest.common import utils
from tempest.common import waiters
from tempest.lib.common import ssh
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from neutron_tempest_plugin import config
from neutron_tempest_plugin.scenario import base
from neutron_tempest_plugin.scenario import constants
from neutron_tempest_plugin.services.bgp import bgp_client
CONF = config.CONF
class BgpClientMixin(object):
@classmethod
def resource_setup(cls):
super(BgpClientMixin, cls).resource_setup()
if bgp_client is None:
msg = "No BGP service client is available"
raise cls.skipException(msg)
manager = cls.os_admin
cls.bgp_client = bgp_client.BgpSpeakerClientJSON(
manager.auth_provider,
CONF.network.catalog_type,
CONF.network.region or CONF.identity.region,
endpoint_type=CONF.network.endpoint_type,
build_interval=CONF.network.build_interval,
build_timeout=CONF.network.build_timeout,
**manager.default_params)
def create_bgp_speaker(self, **kwargs):
bgp_speaker = self.bgp_client.create_bgp_speaker(post_data={
'bgp_speaker': kwargs,
})['bgp_speaker']
self.addCleanup(self.bgp_client.delete_bgp_speaker, bgp_speaker['id'])
return bgp_speaker
def create_bgp_peer(self, **kwargs):
bgp_peer = self.bgp_client.create_bgp_peer(post_data={
'bgp_peer': kwargs,
})['bgp_peer']
self.addCleanup(self.bgp_client.delete_bgp_peer, bgp_peer['id'])
return bgp_peer
def add_bgp_peer_with_id(self, bgp_speaker_id, bgp_peer_id):
self.bgp_client.add_bgp_peer_with_id(bgp_speaker_id, bgp_peer_id)
class Bgp(BgpClientMixin, base.BaseTempestTestCase):
"""Test the following topology
+-------------------+
| public |
| network |
| |
+-+---------------+-+
| |
| |
+-------+-+ +-+-------+
| LEFT | | RIGHT |
| router | <--BGP--> | router |
| | | |
+----+----+ +----+----+
| |
+----+----+ +----+----+
| LEFT | | RIGHT |
| network | | network |
| | | |
+---------+ +---------+
"""
credentials = ['primary', 'admin']
@classmethod
@utils.requires_ext(extension="bgp-speaker-router-insertion",
service="network")
def resource_setup(cls):
super(Bgp, cls).resource_setup()
# common
cls.keypair = cls.create_keypair()
cls.secgroup = cls.os_primary.network_client.create_security_group(
name=data_utils.rand_name('secgroup-'))['security_group']
cls.security_groups.append(cls.secgroup)
cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id'])
cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id'])
# LEFT
cls.router = cls.create_router(
data_utils.rand_name('left-router'),
admin_state_up=True,
external_network_id=CONF.network.public_network_id)
cls.network = cls.create_network(network_name='left-network')
cls.subnet = cls.create_subnet(cls.network,
name='left-subnet')
cls.create_router_interface(cls.router['id'], cls.subnet['id'])
# RIGHT
cls._right_network, cls._right_subnet, cls._right_router = \
cls._create_right_network()
@classmethod
def _create_right_network(cls):
# NOTE(yamamoto): Disable SNAT to workaround a bug
# https://midonet.atlassian.net/browse/MNA-1114
router = cls.create_admin_router(
data_utils.rand_name('right-router'),
admin_state_up=True,
external_network_id=CONF.network.public_network_id,
enable_snat=False,
project_id=cls.os_primary.network_client.tenant_id)
network = cls.create_network(network_name='right-network')
subnet = cls.create_subnet(
network,
cidr=netaddr.IPNetwork('10.10.0.0/24'),
name='right-subnet')
cls.create_router_interface(router['id'], subnet['id'])
return network, subnet, router
def _create_server(self, create_floating_ip=True, network=None):
if network is None:
network = self.network
port = self.create_port(network, security_groups=[self.secgroup['id']])
if create_floating_ip:
fip = self.create_and_associate_floatingip(port['id'])
else:
fip = None
server = self.create_server(
flavor_ref=CONF.compute.flavor_ref,
image_ref=CONF.compute.image_ref,
key_name=self.keypair['name'],
networks=[{'port': port['id']}])['server']
waiters.wait_for_server_status(self.os_primary.servers_client,
server['id'],
constants.SERVER_STATUS_ACTIVE)
return {'port': port, 'fip': fip, 'server': server}
def _find_ipv4_subnet(self, network_id):
subnets = self.os_admin.network_client.list_subnets(
network_id=network_id)['subnets']
for subnet in subnets:
if subnet['ip_version'] == 4:
return subnet['id']
msg = "No suitable subnets on public network"
raise self.skipException(msg)
def _get_external_ip(self, router, subnet_id):
for ip in router['external_gateway_info']['external_fixed_ips']:
if ip['subnet_id'] == subnet_id:
return ip['ip_address']
return None
def _setup_bgp(self):
network_id = CONF.network.public_network_id
subnet_id = self._find_ipv4_subnet(network_id)
sites = [
dict(name="left", network=self.network, subnet=self.subnet,
router=self.router, local_as=64512),
dict(name="right", network=self._right_network,
subnet=self._right_subnet, router=self._right_router,
local_as=64513),
]
psk = data_utils.rand_name('mysecret')
for i in range(0, 2):
site = sites[i]
router = site['router']
site['bgp_speaker'] = self.create_bgp_speaker(
name=data_utils.rand_name('%s-bgp-speaker' % site['name']),
local_as=site['local_as'],
ip_version=4,
logical_router=router['id'])
site['external_v4_ip'] = self._get_external_ip(router, subnet_id)
for i in range(0, 2):
site = sites[i]
bgp_speaker_id = site['bgp_speaker']['id']
peer = sites[1 - i]
peer_ip = peer['external_v4_ip']
peer_as = peer['local_as']
bgp_peer = self.create_bgp_peer(
name=data_utils.rand_name('%s-bgp-peer' % site['name']),
peer_ip=peer_ip,
remote_as=peer_as,
auth_type='md5',
password=psk)
self.add_bgp_peer_with_id(bgp_speaker_id, bgp_peer['id'])
@decorators.idempotent_id('c1208ce2-c55f-4424-9035-25de83161d6f')
def test_bgp(self):
# RIGHT
right_server = self._create_server(
network=self._right_network,
create_floating_ip=False)
# LEFT
left_server = self._create_server()
ssh_client = ssh.Client(left_server['fip']['floating_ip_address'],
CONF.validation.image_ssh_user,
pkey=self.keypair['private_key'])
# check LEFT -> RIGHT connectivity via BGP advertised routes
self.check_remote_connectivity(
ssh_client,
right_server['port']['fixed_ips'][0]['ip_address'],
should_succeed=False)
self._setup_bgp()
self.check_remote_connectivity(
ssh_client,
right_server['port']['fixed_ips'][0]['ip_address'])