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 dd6f865818
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_vport_id = Column(String(36))
nuage_vif_id = Column(String(36)) nuage_vif_id = Column(String(36))
static_ip = Column(Boolean()) 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, nuage_models.NetPartition,
filters) filters)
return query 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.api.v2 import attributes
from neutron.common import constants as os_constants from neutron.common import constants as os_constants
from neutron.common import exceptions as n_exc from neutron.common import exceptions as n_exc
from neutron.common import utils
from neutron.db import api as db from neutron.db import api as db
from neutron.db import db_base_plugin_v2 from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db from neutron.db import external_net_db
from neutron.db import extraroute_db
from neutron.db import l3_db from neutron.db import l3_db
from neutron.db import models_v2 from neutron.db import models_v2
from neutron.db import quota_db # noqa from neutron.db import quota_db # noqa
@ -46,12 +48,13 @@ from neutron import policy
class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
external_net_db.External_net_db_mixin, external_net_db.External_net_db_mixin,
extraroute_db.ExtraRoute_db_mixin,
l3_db.L3_NAT_db_mixin, l3_db.L3_NAT_db_mixin,
netpartition.NetPartitionPluginBase): netpartition.NetPartitionPluginBase):
"""Class that implements Nuage Networks' plugin functionality.""" """Class that implements Nuage Networks' plugin functionality."""
supported_extension_aliases = ["router", "binding", "external-net", supported_extension_aliases = ["router", "binding", "external-net",
"net-partition", "nuage-router", "net-partition", "nuage-router",
"nuage-subnet", "quotas"] "nuage-subnet", "quotas", "extraroute"]
binding_view = "extension:port_binding:view" binding_view = "extension:port_binding:view"
@ -606,6 +609,65 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
nuage_group_id=group_id) nuage_group_id=group_id)
return neutron_router 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): def delete_router(self, context, id):
session = context.session session = context.session
ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid(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): def delete_vms(self, params):
pass 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 import mock
from oslo.config import cfg from oslo.config import cfg
from webob import exc
from neutron.extensions import portbindings from neutron.extensions import portbindings
from neutron.plugins.nuage import extensions 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 import _test_extension_portbindings as test_bindings
from neutron.tests.unit.nuage import fake_nuageclient from neutron.tests.unit.nuage import fake_nuageclient
from neutron.tests.unit import test_db_plugin 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 from neutron.tests.unit import test_l3_plugin
API_EXT_PATH = os.path.dirname(extensions.__file__) API_EXT_PATH = os.path.dirname(extensions.__file__)
@ -160,3 +162,32 @@ class TestNuagePortsV2(NuagePluginV2TestCase,
class TestNuageL3NatTestCase(NuagePluginV2TestCase, class TestNuageL3NatTestCase(NuagePluginV2TestCase,
test_l3_plugin.L3NatDBIntTestCase): test_l3_plugin.L3NatDBIntTestCase):
pass 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'])