Merge "Extraroute extension support for nuage plugin"

This commit is contained in:
Jenkins 2014-05-24 21:36:06 +00:00 committed by Gerrit Code Review
commit 58f0b7baf5
7 changed files with 200 additions and 2 deletions

View File

@ -0,0 +1,68 @@
# Copyright 2014 OpenStack Foundation
#
# 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.
#
"""nuage_extraroute
Revision ID: 10cd28e692e9
Revises: 1b837a7125a9
Create Date: 2014-05-14 14:47:53.148132
"""
# revision identifiers, used by Alembic.
revision = '10cd28e692e9'
down_revision = '1b837a7125a9'
# Change to ['*'] if this migration applies to all plugins
migration_for_plugins = [
'neutron.plugins.nuage.plugin.NuagePlugin'
]
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
def upgrade(active_plugins=None, options=None):
if not migration.should_run(active_plugins, migration_for_plugins):
return
op.create_table(
'routerroutes_mapping',
sa.Column('router_id', sa.String(length=36), nullable=False),
sa.Column('nuage_route_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
ondelete='CASCADE'),
)
op.create_table(
'routerroutes',
sa.Column('destination', sa.String(length=64), nullable=False),
sa.Column('nexthop', sa.String(length=64), nullable=False),
sa.Column('router_id', sa.String(length=36), nullable=False),
sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('destination', 'nexthop',
'router_id'),
)
def downgrade(active_plugins=None, options=None):
if not migration.should_run(active_plugins, migration_for_plugins):
return
op.drop_table('routerroutes')
op.drop_table('routerroutes_mapping')

View File

@ -1 +1 @@
1b837a7125a9
10cd28e692e9

View File

@ -72,3 +72,13 @@ class PortVPortMapping(model_base.BASEV2):
nuage_vport_id = Column(String(36))
nuage_vif_id = Column(String(36))
static_ip = Column(Boolean())
class RouterRoutesMapping(model_base.BASEV2, models_v2.Route):
__tablename__ = 'routerroutes_mapping'
router_id = Column(String(36),
ForeignKey('routers.id',
ondelete="CASCADE"),
primary_key=True,
nullable=False)
nuage_route_id = Column(String(36))

View File

@ -131,3 +131,24 @@ def get_net_partitions(session, filters=None, fields=None):
nuage_models.NetPartition,
filters)
return query
def delete_static_route(session, static_route):
session.delete(static_route)
def get_router_route_mapping(session, id, route):
qry = session.query(nuage_models.RouterRoutesMapping)
return qry.filter_by(router_id=id,
destination=route['destination'],
nexthop=route['nexthop']).one()
def add_static_route(session, router_id, nuage_rtr_id,
destination, nexthop):
staticrt = nuage_models.RouterRoutesMapping(router_id=router_id,
nuage_route_id=nuage_rtr_id,
destination=destination,
nexthop=nexthop)
session.add(staticrt)
return staticrt

View File

@ -25,9 +25,11 @@ from neutron.api import extensions as neutron_extensions
from neutron.api.v2 import attributes
from neutron.common import constants as os_constants
from neutron.common import exceptions as n_exc
from neutron.common import utils
from neutron.db import api as db
from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db
from neutron.db import extraroute_db
from neutron.db import l3_db
from neutron.db import models_v2
from neutron.db import quota_db # noqa
@ -46,12 +48,13 @@ from neutron import policy
class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
external_net_db.External_net_db_mixin,
extraroute_db.ExtraRoute_db_mixin,
l3_db.L3_NAT_db_mixin,
netpartition.NetPartitionPluginBase):
"""Class that implements Nuage Networks' plugin functionality."""
supported_extension_aliases = ["router", "binding", "external-net",
"net-partition", "nuage-router",
"nuage-subnet", "quotas"]
"nuage-subnet", "quotas", "extraroute"]
binding_view = "extension:port_binding:view"
@ -606,6 +609,65 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
nuage_group_id=group_id)
return neutron_router
def _validate_nuage_staticroutes(self, old_routes, added, removed):
cidrs = []
for old in old_routes:
if old not in removed:
ip = netaddr.IPNetwork(old['destination'])
cidrs.append(ip)
for route in added:
ip = netaddr.IPNetwork(route['destination'])
matching = netaddr.all_matching_cidrs(ip.ip, cidrs)
if matching:
msg = _('for same subnet, multiple static routes not allowed')
raise n_exc.BadRequest(resource='router', msg=msg)
cidrs.append(ip)
def update_router(self, context, id, router):
r = router['router']
with context.session.begin(subtransactions=True):
if 'routes' in r:
old_routes = self._get_extra_routes_by_router_id(context,
id)
added, removed = utils.diff_list_of_dict(old_routes,
r['routes'])
self._validate_nuage_staticroutes(old_routes, added, removed)
ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid(
context.session, id)
if not ent_rtr_mapping:
msg = (_("Router %s does not hold net-partition "
"assoc on VSD. extra-route failed") % id)
raise n_exc.BadRequest(resource='router', msg=msg)
# Let it do internal checks first and verify it.
router_updated = super(NuagePlugin,
self).update_router(context,
id,
router)
for route in removed:
rtr_rt_mapping = nuagedb.get_router_route_mapping(
context.session, id, route)
if rtr_rt_mapping:
self.nuageclient.delete_nuage_staticroute(
rtr_rt_mapping['nuage_route_id'])
nuagedb.delete_static_route(context.session,
rtr_rt_mapping)
for route in added:
params = {
'parent_id': ent_rtr_mapping['nuage_router_id'],
'net': netaddr.IPNetwork(route['destination']),
'nexthop': route['nexthop']
}
nuage_rt_id = self.nuageclient.create_nuage_staticroute(
params)
nuagedb.add_static_route(context.session,
id, nuage_rt_id,
route['destination'],
route['nexthop'])
else:
router_updated = super(NuagePlugin, self).update_router(
context, id, router)
return router_updated
def delete_router(self, context, id):
session = context.session
ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid(session,

View File

@ -83,3 +83,9 @@ class FakeNuageClient(object):
def delete_vms(self, params):
pass
def create_nuage_staticroute(self, params):
return str(uuid.uuid4())
def delete_nuage_staticroute(self, id):
pass

View File

@ -18,6 +18,7 @@ import os
import mock
from oslo.config import cfg
from webob import exc
from neutron.extensions import portbindings
from neutron.plugins.nuage import extensions
@ -25,6 +26,7 @@ from neutron.plugins.nuage import plugin as nuage_plugin
from neutron.tests.unit import _test_extension_portbindings as test_bindings
from neutron.tests.unit.nuage import fake_nuageclient
from neutron.tests.unit import test_db_plugin
from neutron.tests.unit import test_extension_extraroute as extraroute_test
from neutron.tests.unit import test_l3_plugin
API_EXT_PATH = os.path.dirname(extensions.__file__)
@ -160,3 +162,32 @@ class TestNuagePortsV2(NuagePluginV2TestCase,
class TestNuageL3NatTestCase(NuagePluginV2TestCase,
test_l3_plugin.L3NatDBIntTestCase):
pass
class TestNuageExtrarouteTestCase(NuagePluginV2TestCase,
extraroute_test.ExtraRouteDBIntTestCase):
def test_router_update_with_dup_destination_address(self):
with self.router() as r:
with self.subnet(cidr='10.0.1.0/24') as s:
with self.port(subnet=s, no_delete=True) as p:
self._router_interface_action('add',
r['router']['id'],
None,
p['port']['id'])
routes = [{'destination': '135.207.0.0/16',
'nexthop': '10.0.1.3'},
{'destination': '135.207.0.0/16',
'nexthop': '10.0.1.5'}]
self._update('routers', r['router']['id'],
{'router': {'routes':
routes}},
expected_code=exc.HTTPBadRequest.code)
# clean-up
self._router_interface_action('remove',
r['router']['id'],
None,
p['port']['id'])