Merge "Extraroute extension support for nuage plugin"
This commit is contained in:
commit
58f0b7baf5
@ -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')
|
@ -1 +1 @@
|
||||
1b837a7125a9
|
||||
10cd28e692e9
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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'])
|
||||
|
Loading…
Reference in New Issue
Block a user