bagpipe: skip network:* ports and external networks ports
With this change, bagpipe driver won't trigger agent RPCs for any network:* port or port of external networks. This was already done for DHCP ports, but extended to router interfaces, gateway interfaces, and external networks. The documentation and unit tests are updated accordingly. Change-Id: Iae071e1bc19f20b38cc380e37366184b15ae7088
This commit is contained in:
3 changed files with 124 additions and 17 deletions
@@ -26,6 +26,18 @@ nodes of BGP VPN instances and of their MPLS dataplane.
.. blockdiag:: overview.blockdiag
On DHCP ports, Router interface ports, external network ports, etc.
-------------------------------------------------------------------
No connectivity will be setup with BGP VPNs for DHCP ports or Router
interface ports, or other network specific ports. This improves the load on network nodes by
avoiding them to import/export a significant amount of routes, without limiting BGP VPN
deployment scenarios because no useful traffic would be exchanged between a router or DHCP
interface of a network associated to a BGP VPN.
Similarly, the driver will not bind a port on an external network. This behavior will be
revisited once a use case is well identified.
How to use ?
------------
@@ -20,7 +20,9 @@ from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import constants as const
from neutron.common import exceptions as n_exc
from neutron import context as n_context
from neutron.db import external_net_db
from neutron.db import l3_db
from neutron.db import models_v2
from neutron.extensions import portbindings
@@ -44,6 +46,11 @@ LOG = logging.getLogger(__name__)
BAGPIPE_DRIVER_NAME = "bagpipe"
class BGPVPNExternalNetAssociation(n_exc.NeutronException):
message = _("driver does not support associating an external"
"network to a BGPVPN")
def get_network_info_for_port(context, port_id):
"""Get MAC, IP and Gateway IP addresses informations for a specific port"""
@@ -133,6 +140,15 @@ def get_bgpvpns_of_router_assocs_by_network(context, net_id):
return
def network_is_external(context, net_id):
try:
context.session.query(external_net_db.ExternalNetwork).filter_by(
network_id=net_id).one()
return True
except exc.NoResultFound:
return False
class BaGPipeBGPVPNDriver(driver_api.BGPVPNDriver):
"""BGPVPN Service Driver class for BaGPipe"""
@@ -324,6 +340,10 @@ class BaGPipeBGPVPNDriver(driver_api.BGPVPNDriver):
self._format_bgpvpn(bgpvpn, net_id)
)
def create_net_assoc_precommit(self, context, net_assoc):
if network_is_external(context, net_assoc['network_id']):
raise BGPVPNExternalNetAssociation()
def create_net_assoc_postcommit(self, context, net_assoc):
if get_network_ports(context, net_assoc['network_id']):
bgpvpn = self.get_bgpvpn(context, net_assoc['bgpvpn_id'])
@@ -357,6 +377,19 @@ class BaGPipeBGPVPNDriver(driver_api.BGPVPNDriver):
return port.get(portbindings.HOST_ID)
def _ignore_port(self, context, port):
if port['device_owner'].startswith(const.DEVICE_OWNER_NETWORK_PREFIX):
LOG.info("Port %s owner is network:*, we'll do nothing",
port['id'])
return True
if network_is_external(context, port['network_id']):
LOG.info("Port %s is on an external network, we'll do nothing",
port['id'])
return True
return False
def notify_port_updated(self, context, port_id, original_port):
LOG.info("notify_port_updated on port %s", port_id)
@@ -365,8 +398,7 @@ class BaGPipeBGPVPNDriver(driver_api.BGPVPNDriver):
port_bgpvpn_info = {'id': port['id'],
'network_id': port['network_id']}
if port['device_owner'] == const.DEVICE_OWNER_DHCP:
LOG.info("Port %s is DHCP, ignoring", port['id'])
if self._ignore_port(context, port):
return
agent_host = self._get_port_host(context, port)
@@ -413,8 +445,7 @@ class BaGPipeBGPVPNDriver(driver_api.BGPVPNDriver):
port_bgpvpn_info = {'id': port['id'],
'network_id': port['network_id']}
if port['device_owner'] == const.DEVICE_OWNER_DHCP:
LOG.info("Port %s is DHCP, ignoring", port['id'])
if self._ignore_port(context, port):
return
agent_host = self._get_port_host(context, port)
@@ -19,7 +19,7 @@ import webob.exc
from neutron import context as n_context
from neutron.common.constants import DEVICE_OWNER_DHCP
from neutron.common.constants import DEVICE_OWNER_NETWORK_PREFIX
from neutron.common.constants import DEVICE_OWNER_ROUTER_INTF
from neutron.common.constants import PORT_STATUS_ACTIVE
from neutron.common.constants import PORT_STATUS_DOWN
@@ -46,6 +46,18 @@ class TestBagpipeCommon(test_plugin.BgpvpnTestCaseMixin):
super(TestBagpipeCommon, self).setUp(service_provider=provider,
core_plugin=plugin)
self.ctxt = n_context.Context('fake_user', 'fake_project')
n_dict = {"name": "netfoo",
"tenant_id": "fake_project",
"admin_state_up": True,
"router:external": True,
"shared": True}
self.external_net = {'network':
self.plugin.create_network(self.ctxt,
{'network': n_dict})}
class TestBagpipeServiceDriver(TestBagpipeCommon):
@@ -101,6 +113,21 @@ class TestBagpipeServiceDriver(TestBagpipeCommon):
mocked_update.assert_called_once_with(mock.ANY,
formatted_bgpvpn)
def test_bagpipe_associate_external_net_failed(self):
net_id = self.external_net['network']['id']
with self.bgpvpn(tenant_id='another_tenant') as bgpvpn:
id = bgpvpn['bgpvpn']['id']
data = {'network_association': {'network_id': net_id,
'tenant_id': self._tenant_id}}
bgpvpn_net_req = self.new_create_request(
'bgpvpn/bgpvpns',
data=data,
fmt=self.fmt,
id=id,
subresource='network_associations')
res = bgpvpn_net_req.get_response(self.ext_api)
self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code)
def test_bagpipe_associate_router(self):
mocked_update = self.mocked_bagpipeAPI.update_bgpvpn
with self.router(tenant_id=self._tenant_id) as router:
@@ -265,8 +292,6 @@ class TestBagpipeServiceDriverCallbacks(TestBagpipeCommon):
self.mock_update_bgpvpn_rpc = self.mocked_bagpipeAPI.update_bgpvpn
self.mock_delete_bgpvpn_rpc = self.mocked_bagpipeAPI.delete_bgpvpn
self.ctxt = n_context.Context('fake_user', 'fake_project')
def _build_expected_return_active(self, port):
bgpvpn_info_port = BGPVPN_INFO.copy()
bgpvpn_info_port.update({'id': port['id'],
@@ -368,8 +393,8 @@ class TestBagpipeServiceDriverCallbacks(TestBagpipeCommon):
TESTHOST)
self.assertFalse(self.mock_attach_rpc.called)
def test_bagpipe_callback_to_rpc_update_active_ignore_DHCP(self):
with self.port(device_owner=DEVICE_OWNER_DHCP,
def test_bagpipe_callback_to_rpc_update_active_ignore_net_ports(self):
with self.port(device_owner=DEVICE_OWNER_NETWORK_PREFIX,
arg_list=(portbindings.HOST_ID,),
**{portbindings.HOST_ID: TESTHOST}) as port:
self._update_port_status(port, PORT_STATUS_ACTIVE)
@@ -381,8 +406,8 @@ class TestBagpipeServiceDriverCallbacks(TestBagpipeCommon):
self.assertFalse(self.mock_attach_rpc.called)
self.assertFalse(self.mock_detach_rpc.called)
def test_bagpipe_callback_to_rpc_update_down_ignore_DHCP(self):
with self.port(device_owner=DEVICE_OWNER_DHCP,
def test_bagpipe_callback_to_rpc_update_down_ignore_net_ports(self):
with self.port(device_owner=DEVICE_OWNER_NETWORK_PREFIX,
arg_list=(portbindings.HOST_ID,),
**{portbindings.HOST_ID: TESTHOST}) as port:
self._update_port_status(port, PORT_STATUS_DOWN)
@@ -394,8 +419,8 @@ class TestBagpipeServiceDriverCallbacks(TestBagpipeCommon):
self.assertFalse(self.mock_attach_rpc.called)
self.assertFalse(self.mock_detach_rpc.called)
def test_bagpipe_callback_to_rpc_deleted_ignore_DHCP(self):
with self.port(device_owner=DEVICE_OWNER_DHCP,
def test_bagpipe_callback_to_rpc_deleted_ignore_net_ports(self):
with self.port(device_owner=DEVICE_OWNER_NETWORK_PREFIX,
arg_list=(portbindings.HOST_ID,),
**{portbindings.HOST_ID: TESTHOST}) as port:
self._update_port_status(port, PORT_STATUS_DOWN)
@@ -407,10 +432,49 @@ class TestBagpipeServiceDriverCallbacks(TestBagpipeCommon):
self.assertFalse(self.mock_attach_rpc.called)
self.assertFalse(self.mock_detach_rpc.called)
def test_bagpipe_callback_to_rpc_update_active_ignore_external_net(self):
with self.subnet(network=self.external_net) as subnet, \
self.port(subnet=subnet,
arg_list=(portbindings.HOST_ID,),
**{portbindings.HOST_ID: TESTHOST}) as port:
self._update_port_status(port, PORT_STATUS_ACTIVE)
self.bagpipe_driver.registry_port_updated(
None, None, None,
context=self.ctxt,
port=port['port']
)
self.assertFalse(self.mock_attach_rpc.called)
self.assertFalse(self.mock_detach_rpc.called)
def test_bagpipe_callback_to_rpc_update_down_ignore_external_net(self):
with self.subnet(network=self.external_net) as subnet, \
self.port(subnet=subnet,
arg_list=(portbindings.HOST_ID,),
**{portbindings.HOST_ID: TESTHOST}) as port:
self._update_port_status(port, PORT_STATUS_DOWN)
self.bagpipe_driver.registry_port_updated(
None, None, None,
context=self.ctxt,
port=port['port']
)
self.assertFalse(self.mock_attach_rpc.called)
self.assertFalse(self.mock_detach_rpc.called)
def test_bagpipe_callback_to_rpc_deleted_ignore_external_net(self):
with self.subnet(network=self.external_net) as subnet, \
self.port(subnet=subnet,
arg_list=(portbindings.HOST_ID,),
**{portbindings.HOST_ID: TESTHOST}) as port:
self._update_port_status(port, PORT_STATUS_DOWN)
self.bagpipe_driver.registry_port_deleted(
None, None, None,
context=self.ctxt,
port_id=port['port']['id']
)
self.assertFalse(self.mock_attach_rpc.called)
self.assertFalse(self.mock_detach_rpc.called)
def test_delete_port_to_bgpvpn_rpc(self):
ctxt = n_context.Context('fake_user', 'fake_project')
with self.network() as net, \
self.subnet(network=net) as subnet, \
self.port(subnet=subnet) as port, \
@@ -421,7 +485,7 @@ class TestBagpipeServiceDriverCallbacks(TestBagpipeCommon):
port['port'].update({'binding:host_id': TESTHOST})
self.plugin.delete_port(ctxt, port['port']['id'])
self.plugin.delete_port(self.ctxt, port['port']['id'])
self.mock_detach_rpc.assert_called_once_with(
mock.ANY,
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.