Skip to content

Commit

Permalink
[config] Add the BGPVPN resource
Browse files Browse the repository at this point in the history
This patch adds new bgpvpn resource to the contrail schema to support
the OpenStack Neutron API extension networking-bgpvpn [1].

[1] http://docs.openstack.org/developer/networking-bgpvpn/

Implements: blueprint bgpvpn
Closes-Bug: #1640946
Depends-On: I0cb574c5c01f4f05a09b10d9fdfbfec65fc863dd
Change-Id: I5cb551a299734798e19173a0fba9afe1c1f5f057
  • Loading branch information
Édouard Thuleau authored and Édouard Thuleau committed Feb 1, 2017
1 parent e3c5253 commit 0a8fd59
Show file tree
Hide file tree
Showing 12 changed files with 1,191 additions and 50 deletions.
103 changes: 103 additions & 0 deletions specs/neutron_bgpvpn.md
@@ -0,0 +1,103 @@
# 1. Introduction
Add the support to the new OpenStack Neutron API extension named **Networking BGP VPN** to Contrail which was added to supporting inter-connections between L3VPNs or E-VPNs and Neutron resources, (i.e. Networks, Routers and Ports).
This extension uses the [plugin Neutron framework](https://wiki.openstack.org/wiki/Neutron/ServiceTypeFramework) and defines a new resource named *bgpvpn* that contains all [attributes](http://docs.openstack.org/developer/networking-bgpvpn/api.html#bgpvpn-resource) needed to declare a BGP VPN connection. Then, that connection can be associated/disassociated to networks or/and routers by users.

# 2. Problem statement
A typical use-case is the following:
* a tenant already having a BGP IP VPN (a set of external sites) setup outside the datacenter, and they want to be able to trigger the establishment of connectivity between VMs and these VPN sites.
* Another similar need is when E-VPN is used to provide an Ethernet interconnect between multiple sites.

# 3. Proposed solution
Extend the Contrail schema with that new BGP VPN resource and use the schema transformer to apply BGP VPN's route targets to the associated network resources (ie. routing instance).

## 3.1 Alternatives considered
N/A

## 3.2 API schema changes
Add new BGP VPN resource to Contrail data model named `bgpvpn`:

Resource attributes | Operations | Type
-|-|-
Project Owner | CR | Parent reference
Type | CR | Property `VpnType` string enum `['l2', 'l3']`
Route targets | CRU | ListProperty `RouteTargetList`
Import targets | CRU | ListProperty `RouteTargetList`
Export targets | CRU | ListProperty `RouteTargetList`
Networks | CRU | `virtual-network` back reference
Routers | CRU | `logical-router` back reference

For the ` bgpvpn` `type` attribute, only the type `l3` can be used for `bgpvpn` associated to `logical routers`. About the `virtual network` association authorizations depend on the `forwarding mode` set on the `virtual network` as describe below:

Forwarding mode | Authorized VPN types
-|-
`l2_l3` | `l2` and/or `l3`
`l2` | `l2` only
`l3` | `l3` only

`forwarding mode` update on a `virtual network` can fail depends of the `bgpvpn` `type` associated to it.

## 3.3 User workflow impact
That feature was designed for the OpenStack Neutron API. Only the cloud administrator can create, allocate to a tenant, update and delete the `bgpvpn` resource. User can only list them (eventually update the name) and define association with network resource (ie. `virtual networks` and `logical router`).

On the contrail side, we will add that resource without any constraints on resource manipulation.

## 3.4 UI changes
N/A

## 3.5 Notification impact
N/A

# 4. Implementation

## 4.1 Assignee(s)
### Developers
* Édouard Thuleau

### Reviewers
* Sachin Bansal

## 4.2 Work items
### Contrail schema
Add the new `bgpvpn` resource to the Contrail data model as describe in 3.2

### VNC API server
Add some checks on the association of a BGP VPN resource to the other resource to avoid any ambiguity like it's describe in the [BGP VPN documentation](http://docs.openstack.org/developer/networking-bgpvpn/api.html#association-constraints).

### Schema transformer
Add a new ST class to support that new `bgpvpn` resource and add code in classes `VirtualNetworkST` and `LogicalRouterST` to merge BGP VPN's route targets.

### Neutron BGP VPN driver
Write a driver for the Neutron BGP VPN extension based on that new `bgpvpn` resource.

# 5. Performance and scaling impact
## 5.1 API and control plane
Insignificant impact on the config and control.

## 5.2 Forwarding performance
Add some easy routing stuff on vrouter to leak routes between VRFs accordingly to defined route targets.

# 6. Upgrade
First implementation was done by Orange/Cloudwatt based on the Contrail key/value exposed through the VNC API which was a proof of concept first implementation.

We could easily add a mechanism to detect if used Contrail API support the new `bgpvpn` resource, if not fallback to the first driver and propose a migration script (or an automatic migration which detects `bgpvpn` resources in kv store when driver is initializing).

# 7. Deprecation
* No deprecation on the Contrail API.
* Deprecates the first Neutron BGP VPN Contrail driver.

# 8. Dependencies
N/A

# 9. Testing
## 9.1 Unit tests
Add unit tests to the added code on the config side.

## 9.2 Dev tests

## 9.3 System tests

# 10. Documentation Impact

# 11. References
* Blueprint: https://blueprints.launchpad.net/opencontrail/+spec/bgpvpn
* Neutron BGP VPN extension documentation: http://docs.openstack.org/developer/networking-bgpvpn/index.html
1 change: 1 addition & 0 deletions src/config/api-server/db_manage.py
Expand Up @@ -8,6 +8,7 @@
from netaddr import IPAddress, IPNetwork
import argparse
from cStringIO import StringIO
import time

import kazoo.client
import kazoo.exceptions
Expand Down
180 changes: 178 additions & 2 deletions src/config/api-server/tests/test_crud_basic.py
Expand Up @@ -824,7 +824,8 @@ def test_put_on_wrong_type(self):
self.assertThat(rb_obj.display_name, Equals('foobar'))

def test_floatingip_as_instanceip(self):
ipam_fixt = self.useFixture(NetworkIpamTestFixtureGen(self._vnc_lib))
ipam_fixt = self.useFixture(NetworkIpamTestFixtureGen(
self._vnc_lib, network_ipam_name='ipam-%s' % self.id()))

project_fixt = self.useFixture(ProjectTestFixtureGen(self._vnc_lib, 'default-project'))

Expand Down Expand Up @@ -860,7 +861,8 @@ def test_floatingip_as_instanceip(self):
# end test_floatingip_as_instanceip

def test_aliasip_as_instanceip(self):
ipam_fixt = self.useFixture(NetworkIpamTestFixtureGen(self._vnc_lib))
ipam_fixt = self.useFixture(NetworkIpamTestFixtureGen(
self._vnc_lib, network_ipam_name='ipam-%s' % self.id()))

project_fixt = self.useFixture(ProjectTestFixtureGen(self._vnc_lib, 'default-project'))

Expand Down Expand Up @@ -1949,6 +1951,180 @@ def test_name_attribute_in_detail_list_resource(self):
self.assertIn('name', vn_dict)
self.assertEqual(vn_dict['name'], vn_obj.fq_name[-1])

def test_bgpvpn_type_assoc_with_network_l2_l3_forwarding_mode(self):
# Create virtual network with forwarding mode set to 'l2' and 'l3'
vn_l2_l3 = self.create_virtual_network('vn-l2-l3-%s' % self.id())
# Create l2 bgpvpn
bgpvpn_l2 = Bgpvpn('bgpvpn-l2-%s' % self.id(), bgpvpn_type='l2')
self._vnc_lib.bgpvpn_create(bgpvpn_l2)
# Create l3 bgpvpn
bgpvpn_l3 = Bgpvpn('bgpvpn-l3-%s' % self.id())
self._vnc_lib.bgpvpn_create(bgpvpn_l3)

# Trying to associate a 'l2' bgpvpn on the virtual network
vn_l2_l3.add_bgpvpn(bgpvpn_l2)
self._vnc_lib.virtual_network_update(vn_l2_l3)
vn_l2_l3 = self._vnc_lib.virtual_network_read(id=vn_l2_l3.uuid)

# Trying to associate a 'l3' bgpvpn on the virtual network
vn_l2_l3.add_bgpvpn(bgpvpn_l3)
self._vnc_lib.virtual_network_update(vn_l2_l3)
vn_l2_l3 = self._vnc_lib.virtual_network_read(id=vn_l2_l3.uuid)

# Try to change the virtual network forwarding mode to 'l2' only
with ExpectedException(BadRequest):
vn_l2_l3.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l2'))
self._vnc_lib.virtual_network_update(vn_l2_l3)
vn_l2_l3 = self._vnc_lib.virtual_network_read(id=vn_l2_l3.uuid)

# Try to change the virtual network forwarding mode to 'l3' only
with ExpectedException(BadRequest):
vn_l2_l3.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l3'))
self._vnc_lib.virtual_network_update(vn_l2_l3)

def test_bgpvpn_type_assoc_with_network_l2_forwarding_mode(self):
# Create virtual network with forwarding mode set to 'l2' only
vn_l2 = self.create_virtual_network('vn-l2-%s' % self.id())
vn_l2.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l2'))
self._vnc_lib.virtual_network_update(vn_l2)
vn_l2 = self._vnc_lib.virtual_network_read(id=vn_l2.uuid)
# Create l2 bgpvpn
bgpvpn_l2 = Bgpvpn('bgpvpn-l2-%s' % self.id(), bgpvpn_type='l2')
self._vnc_lib.bgpvpn_create(bgpvpn_l2)
# Create l3 bgpvpn
bgpvpn_l3 = Bgpvpn('bgpvpn-l3-%s' % self.id())
self._vnc_lib.bgpvpn_create(bgpvpn_l3)

# Trying to associate a 'l2' bgpvpn on the virtual network
vn_l2.add_bgpvpn(bgpvpn_l2)
self._vnc_lib.virtual_network_update(vn_l2)
vn_l2 = self._vnc_lib.virtual_network_read(id=vn_l2.uuid)

# Trying to associate a 'l3' bgpvpn on the virtual network
with ExpectedException(BadRequest):
vn_l2.add_bgpvpn(bgpvpn_l3)
self._vnc_lib.virtual_network_update(vn_l2)
vn_l2 = self._vnc_lib.virtual_network_read(id=vn_l2.uuid)

# Try to change the virtual network forwarding mode to 'l3' only
with ExpectedException(BadRequest):
vn_l2.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l3'))
self._vnc_lib.virtual_network_update(vn_l2)
vn_l2 = self._vnc_lib.virtual_network_read(id=vn_l2.uuid)

# Try to change the virtual network forwarding mode to 'l2' and l3'
vn_l2.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l2_l3'))
self._vnc_lib.virtual_network_update(vn_l2)

def test_bgpvpn_type_assoc_with_network_l3_forwarding_mode(self):
# Create virtual network with forwarding mode set to 'l3' only
vn_l3 = self.create_virtual_network('vn-l3-%s' % self.id())
vn_l3.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l3'))
self._vnc_lib.virtual_network_update(vn_l3)
vn_l3 = self._vnc_lib.virtual_network_read(id=vn_l3.uuid)
# Create l2 bgpvpn
bgpvpn_l2 = Bgpvpn('bgpvpn-l2-%s' % self.id(), bgpvpn_type='l2')
self._vnc_lib.bgpvpn_create(bgpvpn_l2)
# Create l3 bgpvpn
bgpvpn_l3 = Bgpvpn('bgpvpn-l3-%s' % self.id())
self._vnc_lib.bgpvpn_create(bgpvpn_l3)

# Trying to associate a 'l3' bgpvpn on the virtual network
vn_l3.add_bgpvpn(bgpvpn_l3)
self._vnc_lib.virtual_network_update(vn_l3)
vn_l3 = self._vnc_lib.virtual_network_read(id=vn_l3.uuid)

# Trying to associate a 'l2' bgpvpn on the virtual network
with ExpectedException(BadRequest):
vn_l3.add_bgpvpn(bgpvpn_l2)
self._vnc_lib.virtual_network_update(vn_l3)
vn_l3 = self._vnc_lib.virtual_network_read(id=vn_l3.uuid)

# Try to change the virtual network forwarding mode to 'l2' only
with ExpectedException(BadRequest):
vn_l3.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l2'))
self._vnc_lib.virtual_network_update(vn_l3)
vn_l3 = self._vnc_lib.virtual_network_read(id=vn_l3.uuid)

# Try to change the virtual network forwarding mode to 'l2' and l3'
vn_l3.set_virtual_network_properties(
VirtualNetworkType(forwarding_mode='l2_l3'))
self._vnc_lib.virtual_network_update(vn_l3)

def test_bgpvpn_type_limited_to_l3_for_router_assoc(self):
# Create logical router
lr, _, _, _ = self.create_logical_router(
'lr-%s' % self.id(), nb_of_attached_networks=0)
# Create l2 bgpvpn
bgpvpn_l2 = Bgpvpn('bgpvpn-l2-%s' % self.id(), bgpvpn_type='l2')
self._vnc_lib.bgpvpn_create(bgpvpn_l2)

# Trying to associate a 'l2' bgpvpn on the logical router
with ExpectedException(BadRequest):
lr.add_bgpvpn(bgpvpn_l2)
self._vnc_lib.logical_router_update(lr)

def test_bgpvpn_fail_assoc_network_with_gw_router_assoc_to_bgpvpn(self):
# Create one bgpvpn
bgpvpn = Bgpvpn('bgpvpn-%s' % self.id())
self._vnc_lib.bgpvpn_create(bgpvpn)
# Create one virtual network with one logical router as gateway
lr, vns, _, _ = self.create_logical_router('lr-%s' % self.id())
# We attached only one virtual network to the logical router
vn = vns[0]

# Associate the bgppvpn to the logical router
lr.add_bgpvpn(bgpvpn)
self._vnc_lib.logical_router_update(lr)
lr = self._vnc_lib.logical_router_read(id=lr.uuid)

# The try to set that same bgpvpn to the virtual network
with ExpectedException(BadRequest):
vn.add_bgpvpn(bgpvpn)
self._vnc_lib.virtual_network_update(vn)

def test_bgpvpn_fail_assoc_router_with_network_assoc_to_bgpvpn(self):
# Create one bgpvpn
bgpvpn = Bgpvpn('bgpvpn-%s' % self.id())
self._vnc_lib.bgpvpn_create(bgpvpn)
# Create one virtual network with one logical router as gateway
lr, vns, vmis, _ = self.create_logical_router('lr-%s' % self.id())
# We attached only one virtual network to the logical router
vn = vns[0]
vmi = vmis[0]

# Associate the bgpvpn to the virtual network
vn.add_bgpvpn(bgpvpn)
self._vnc_lib.virtual_network_update(vn)
lr = self._vnc_lib.logical_router_read(id=lr.uuid)

# The try to set that same bgpvpn to the logical router
with ExpectedException(BadRequest):
lr.add_bgpvpn(bgpvpn)
self._vnc_lib.logical_router_update(lr)
lr = self._vnc_lib.logical_router_read(id=lr.uuid)

# Detatch the logical router from the virtual network
lr.del_virtual_machine_interface(vmi)
self._vnc_lib.logical_router_update(lr)
lr = self._vnc_lib.logical_router_read(id=lr.uuid)

# Associate the bgpvpn to the logical router
lr.add_bgpvpn(bgpvpn)
self._vnc_lib.logical_router_update(lr)
lr = self._vnc_lib.logical_router_read(id=lr.uuid)

# Try to reattach the virtual network to the logical router
with ExpectedException(BadRequest):
lr.add_virtual_machine_interface(vmi)
self._vnc_lib.logical_router_update(lr)
# end class TestVncCfgApiServer


Expand Down
2 changes: 2 additions & 0 deletions src/config/api-server/vnc_cfg_api_server.py
Expand Up @@ -831,6 +831,8 @@ def http_resource_update(self, obj_type, id):
# State modification starts from here. Ensure that cleanup is done for all state changes
cleanup_on_failure = []
obj_ids = {'uuid': id}
if 'uuid' not in obj_dict:
obj_dict['uuid'] = id

def stateful_update():
get_context().set_state('PRE_DBE_UPDATE')
Expand Down

0 comments on commit 0a8fd59

Please sign in to comment.