From 17b5ba264673fcf3cdabae4e48ab8c8c5cb60d12 Mon Sep 17 00:00:00 2001 From: ronak Date: Wed, 21 May 2014 10:05:48 -0700 Subject: [PATCH] Floatingip extension support for nuage plugin Nuage's VSP supports adding floating-ip which fits nicely with this floatingip extension supported by openstack's neutron. This set of change enables that. Implements: blueprint floatingip-ext-for-nuage-plugin Change-Id: I7178ef0dd170567d0e50035eeed0db2ca33f6c1a --- .../versions/2db5203cb7a9_nuage_floatingip.py | 83 ++++++ .../alembic_migrations/versions/HEAD | 2 +- neutron/plugins/nuage/common/constants.py | 2 + neutron/plugins/nuage/nuage_models.py | 34 ++- neutron/plugins/nuage/nuagedb.py | 48 ++++ neutron/plugins/nuage/plugin.py | 256 +++++++++++++++++- neutron/tests/unit/nuage/fake_nuageclient.py | 41 ++- neutron/tests/unit/nuage/test_nuage_plugin.py | 92 ++++++- 8 files changed, 521 insertions(+), 37 deletions(-) create mode 100644 neutron/db/migration/alembic_migrations/versions/2db5203cb7a9_nuage_floatingip.py diff --git a/neutron/db/migration/alembic_migrations/versions/2db5203cb7a9_nuage_floatingip.py b/neutron/db/migration/alembic_migrations/versions/2db5203cb7a9_nuage_floatingip.py new file mode 100644 index 000000000..57876d096 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/2db5203cb7a9_nuage_floatingip.py @@ -0,0 +1,83 @@ +# 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_floatingip + +Revision ID: 2db5203cb7a9 +Revises: 10cd28e692e9 +Create Date: 2014-05-19 16:39:42.048125 + +""" + +# revision identifiers, used by Alembic. +revision = '2db5203cb7a9' +down_revision = '10cd28e692e9' + +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( + 'nuage_floatingip_pool_mapping', + sa.Column('fip_pool_id', sa.String(length=36), nullable=False), + sa.Column('net_id', sa.String(length=36), nullable=True), + sa.Column('router_id', sa.String(length=36), nullable=True), + sa.ForeignKeyConstraint(['net_id'], ['networks.id'], + ondelete='CASCADE'), + sa.ForeignKeyConstraint(['router_id'], ['routers.id'], + ondelete='CASCADE'), + sa.PrimaryKeyConstraint('fip_pool_id'), + ) + op.create_table( + 'nuage_floatingip_mapping', + sa.Column('fip_id', sa.String(length=36), nullable=False), + sa.Column('router_id', sa.String(length=36), nullable=True), + sa.Column('nuage_fip_id', sa.String(length=36), nullable=True), + sa.ForeignKeyConstraint(['fip_id'], ['floatingips.id'], + ondelete='CASCADE'), + sa.PrimaryKeyConstraint('fip_id'), + ) + op.rename_table('net_partitions', 'nuage_net_partitions') + op.rename_table('net_partition_router_mapping', + 'nuage_net_partition_router_mapping') + op.rename_table('router_zone_mapping', 'nuage_router_zone_mapping') + op.rename_table('subnet_l2dom_mapping', 'nuage_subnet_l2dom_mapping') + op.rename_table('port_mapping', 'nuage_port_mapping') + op.rename_table('routerroutes_mapping', 'nuage_routerroutes_mapping') + + +def downgrade(active_plugins=None, options=None): + if not migration.should_run(active_plugins, migration_for_plugins): + return + + op.drop_table('nuage_floatingip_mapping') + op.drop_table('nuage_floatingip_pool_mapping') + op.rename_table('nuage_net_partitions', 'net_partitions') + op.rename_table('nuage_net_partition_router_mapping', + 'net_partition_router_mapping') + op.rename_table('nuage_router_zone_mapping', 'router_zone_mapping') + op.rename_table('nuage_subnet_l2dom_mapping', 'subnet_l2dom_mapping') + op.rename_table('nuage_port_mapping', 'port_mapping') + op.rename_table('nuage_routerroutes_mapping', 'routerroutes_mapping') diff --git a/neutron/db/migration/alembic_migrations/versions/HEAD b/neutron/db/migration/alembic_migrations/versions/HEAD index 7bf9d4cdf..2431c7d6b 100644 --- a/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -10cd28e692e9 \ No newline at end of file +2db5203cb7a9 \ No newline at end of file diff --git a/neutron/plugins/nuage/common/constants.py b/neutron/plugins/nuage/common/constants.py index f35481299..ff2680bf7 100644 --- a/neutron/plugins/nuage/common/constants.py +++ b/neutron/plugins/nuage/common/constants.py @@ -24,3 +24,5 @@ AUTO_CREATE_PORT_OWNERS = [ ] NOVA_PORT_OWNER_PREF = 'compute:' + +SR_TYPE_FLOATING = "FLOATING" diff --git a/neutron/plugins/nuage/nuage_models.py b/neutron/plugins/nuage/nuage_models.py index 2ac8e25ea..f3ebcffa1 100644 --- a/neutron/plugins/nuage/nuage_models.py +++ b/neutron/plugins/nuage/nuage_models.py @@ -22,16 +22,16 @@ from neutron.db import models_v2 class NetPartition(model_base.BASEV2, models_v2.HasId): - __tablename__ = 'net_partitions' + __tablename__ = 'nuage_net_partitions' name = Column(String(64)) l3dom_tmplt_id = Column(String(36)) l2dom_tmplt_id = Column(String(36)) class NetPartitionRouter(model_base.BASEV2): - __tablename__ = "net_partition_router_mapping" + __tablename__ = "nuage_net_partition_router_mapping" net_partition_id = Column(String(36), - ForeignKey('net_partitions.id', + ForeignKey('nuage_net_partitions.id', ondelete="CASCADE"), primary_key=True) router_id = Column(String(36), @@ -41,7 +41,7 @@ class NetPartitionRouter(model_base.BASEV2): class RouterZone(model_base.BASEV2): - __tablename__ = "router_zone_mapping" + __tablename__ = "nuage_router_zone_mapping" router_id = Column(String(36), ForeignKey('routers.id', ondelete="CASCADE"), primary_key=True) @@ -51,12 +51,12 @@ class RouterZone(model_base.BASEV2): class SubnetL2Domain(model_base.BASEV2): - __tablename__ = 'subnet_l2dom_mapping' + __tablename__ = 'nuage_subnet_l2dom_mapping' subnet_id = Column(String(36), ForeignKey('subnets.id', ondelete="CASCADE"), primary_key=True) net_partition_id = Column(String(36), - ForeignKey('net_partitions.id', + ForeignKey('nuage_net_partitions.id', ondelete="CASCADE")) nuage_subnet_id = Column(String(36)) nuage_l2dom_tmplt_id = Column(String(36)) @@ -65,7 +65,7 @@ class SubnetL2Domain(model_base.BASEV2): class PortVPortMapping(model_base.BASEV2): - __tablename__ = 'port_mapping' + __tablename__ = 'nuage_port_mapping' port_id = Column(String(36), ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) @@ -75,10 +75,28 @@ class PortVPortMapping(model_base.BASEV2): class RouterRoutesMapping(model_base.BASEV2, models_v2.Route): - __tablename__ = 'routerroutes_mapping' + __tablename__ = 'nuage_routerroutes_mapping' router_id = Column(String(36), ForeignKey('routers.id', ondelete="CASCADE"), primary_key=True, nullable=False) nuage_route_id = Column(String(36)) + + +class FloatingIPPoolMapping(model_base.BASEV2): + __tablename__ = "nuage_floatingip_pool_mapping" + fip_pool_id = Column(String(36), primary_key=True) + net_id = Column(String(36), + ForeignKey('networks.id', ondelete="CASCADE")) + router_id = Column(String(36)) + + +class FloatingIPMapping(model_base.BASEV2): + __tablename__ = 'nuage_floatingip_mapping' + fip_id = Column(String(36), + ForeignKey('floatingips.id', + ondelete="CASCADE"), + primary_key=True) + router_id = Column(String(36)) + nuage_fip_id = Column(String(36)) diff --git a/neutron/plugins/nuage/nuagedb.py b/neutron/plugins/nuage/nuagedb.py index a52b695c0..bd1b2f3d2 100644 --- a/neutron/plugins/nuage/nuagedb.py +++ b/neutron/plugins/nuage/nuagedb.py @@ -58,6 +58,10 @@ def update_subnetl2dom_mapping(subnet_l2dom, subnet_l2dom.update(new_dict) +def delete_subnetl2dom_mapping(session, subnet_l2dom): + session.delete(subnet_l2dom) + + def add_port_vport_mapping(session, port_id, nuage_vport_id, nuage_vif_id, static_ip): port_mapping = nuage_models.PortVPortMapping(port_id=port_id, @@ -152,3 +156,47 @@ def add_static_route(session, router_id, nuage_rtr_id, nexthop=nexthop) session.add(staticrt) return staticrt + + +def add_fip_mapping(session, neutron_fip_id, router_id, nuage_fip_id): + fip = nuage_models.FloatingIPMapping(fip_id=neutron_fip_id, + router_id=router_id, + nuage_fip_id=nuage_fip_id) + session.add(fip) + return fip + + +def delete_fip_mapping(session, fip_mapping): + session.delete(fip_mapping) + + +def add_fip_pool_mapping(session, fip_pool_id, net_id, router_id=None): + fip_pool_mapping = nuage_models.FloatingIPPoolMapping( + fip_pool_id=fip_pool_id, + net_id=net_id, + router_id=router_id) + session.add(fip_pool_mapping) + return fip_pool_mapping + + +def delete_fip_pool_mapping(session, fip_pool_mapping): + session.delete(fip_pool_mapping) + + +def get_fip_pool_by_id(session, id): + query = session.query(nuage_models.FloatingIPPoolMapping) + return query.filter_by(fip_pool_id=id).first() + + +def get_fip_pool_from_netid(session, net_id): + query = session.query(nuage_models.FloatingIPPoolMapping) + return query.filter_by(net_id=net_id).first() + + +def get_fip_mapping_by_id(session, id): + qry = session.query(nuage_models.FloatingIPMapping) + return qry.filter_by(fip_id=id).first() + + +def update_fip_pool_mapping(fip_pool_mapping, new_dict): + fip_pool_mapping.update(new_dict) diff --git a/neutron/plugins/nuage/plugin.py b/neutron/plugins/nuage/plugin.py index b27821895..d5d9d34d9 100644 --- a/neutron/plugins/nuage/plugin.py +++ b/neutron/plugins/nuage/plugin.py @@ -33,6 +33,7 @@ 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 +from neutron.extensions import external_net from neutron.extensions import l3 from neutron.extensions import portbindings from neutron.openstack.common import excutils @@ -300,11 +301,48 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, self._process_l3_create(context, net, network['network']) return net + def _validate_update_network(self, context, id, network): + req_data = network['network'] + is_external_set = req_data.get(external_net.EXTERNAL) + if not attributes.is_attr_set(is_external_set): + return (None, None) + neutron_net = self.get_network(context, id) + if neutron_net.get(external_net.EXTERNAL) == is_external_set: + return (None, None) + subnet = self._validate_nuage_sharedresource(context, 'network', id) + if subnet and not is_external_set: + msg = _('External network with subnets can not be ' + 'changed to non-external network') + raise nuage_exc.OperationNotSupported(msg=msg) + return (is_external_set, subnet) + def update_network(self, context, id, network): with context.session.begin(subtransactions=True): + is_external_set, subnet = self._validate_update_network(context, + id, + network) net = super(NuagePlugin, self).update_network(context, id, network) self._process_l3_update(context, net, network['network']) + if subnet and is_external_set: + subn = subnet[0] + subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(context.session, + subn['id']) + if subnet_l2dom: + nuage_subnet_id = subnet_l2dom['nuage_subnet_id'] + nuage_l2dom_tid = subnet_l2dom['nuage_l2dom_tmplt_id'] + user_id = subnet_l2dom['nuage_user_id'] + group_id = subnet_l2dom['nuage_group_id'] + self.nuageclient.delete_subnet(nuage_subnet_id, + nuage_l2dom_tid) + self.nuageclient.delete_user(user_id) + self.nuageclient.delete_group(group_id) + nuagedb.delete_subnetl2dom_mapping(context.session, + subnet_l2dom) + self._add_nuage_sharedresource(context, + subnet[0], + id, + constants.SR_TYPE_FLOATING) return net def delete_network(self, context, id): @@ -341,18 +379,46 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, msg = "no-gateway option not supported with subnets" raise nuage_exc.OperationNotSupported(msg=msg) - def create_subnet(self, context, subnet): - subn = subnet['subnet'] - net_id = subn['network_id'] + def _delete_nuage_sharedresource(self, context, net_id): + sharedresource_id = self.nuageclient.delete_nuage_sharedresource( + net_id) + if sharedresource_id: + fip_pool_mapping = nuagedb.get_fip_pool_by_id(context.session, + sharedresource_id) + if fip_pool_mapping: + with context.session.begin(subtransactions=True): + nuagedb.delete_fip_pool_mapping(context.session, + fip_pool_mapping) + + def _validate_nuage_sharedresource(self, context, resource, net_id): + filter = {'network_id': [net_id]} + existing_subn = self.get_subnets(context, filters=filter) + if len(existing_subn) > 1: + msg = _('Only one subnet is allowed per ' + 'external network %s') % net_id + raise nuage_exc.OperationNotSupported(msg=msg) + return existing_subn - if self._network_is_external(context, net_id): - return super(NuagePlugin, self).create_subnet(context, subnet) + def _add_nuage_sharedresource(self, context, subnet, net_id, type): + net = netaddr.IPNetwork(subnet['cidr']) + params = { + 'neutron_subnet': subnet, + 'net': net, + 'type': type + } + fip_pool_id = self.nuageclient.create_nuage_sharedresource(params) + nuagedb.add_fip_pool_mapping(context.session, fip_pool_id, net_id) - self._validate_create_subnet(subn) + def _create_nuage_sharedresource(self, context, subnet, type): + subn = subnet['subnet'] + net_id = subn['network_id'] + self._validate_nuage_sharedresource(context, 'subnet', net_id) + with context.session.begin(subtransactions=True): + subn = super(NuagePlugin, self).create_subnet(context, subnet) + self._add_nuage_sharedresource(context, subn, net_id, type) + return subn - net_partition = self._get_net_partition_for_subnet(context, subnet) - neutron_subnet = super(NuagePlugin, self).create_subnet(context, - subnet) + def _create_nuage_subnet(self, context, neutron_subnet, net_partition): net = netaddr.IPNetwork(neutron_subnet['cidr']) params = { 'net_partition': net_partition, @@ -372,21 +438,36 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, user_id = nuage_subnet['nuage_userid'] group_id = nuage_subnet['nuage_groupid'] id = nuage_subnet['nuage_l2domain_id'] - session = context.session - with session.begin(subtransactions=True): - nuagedb.add_subnetl2dom_mapping(session, + with context.session.begin(subtransactions=True): + nuagedb.add_subnetl2dom_mapping(context.session, neutron_subnet['id'], id, net_partition['id'], l2dom_id=l2dom_id, nuage_user_id=user_id, nuage_group_id=group_id) + + def create_subnet(self, context, subnet): + subn = subnet['subnet'] + net_id = subn['network_id'] + + if self._network_is_external(context, net_id): + return self._create_nuage_sharedresource( + context, subnet, constants.SR_TYPE_FLOATING) + + self._validate_create_subnet(subn) + + net_partition = self._get_net_partition_for_subnet(context, subnet) + neutron_subnet = super(NuagePlugin, self).create_subnet(context, + subnet) + self._create_nuage_subnet(context, neutron_subnet, net_partition) return neutron_subnet def delete_subnet(self, context, id): subnet = self.get_subnet(context, id) if self._network_is_external(context, subnet['network_id']): - return super(NuagePlugin, self).delete_subnet(context, id) + super(NuagePlugin, self).delete_subnet(context, id) + return self._delete_nuage_sharedresource(context, id) subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(context.session, id) if subnet_l2dom: @@ -523,6 +604,11 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, with session.begin(subtransactions=True): subnet_l2dom = nuagedb.get_subnet_l2dom_by_id(session, subnet_id) + if not subnet_l2dom: + return super(NuagePlugin, + self).remove_router_interface(context, + router_id, + interface_info) nuage_subn_id = subnet_l2dom['nuage_subnet_id'] if self.nuageclient.vms_on_l2domain(nuage_subn_id): msg = (_("Subnet %s has one or more active VMs " @@ -727,7 +813,6 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, if net_partition: with session.begin(subtransactions=True): nuagedb.delete_net_partition(session, net_partition) - self._create_net_partition(session, default_net_part) def create_net_partition(self, context, net_partition): @@ -767,3 +852,146 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, fields=fields) return [self._make_net_partition_dict(net_partition, fields) for net_partition in net_partitions] + + def _check_floatingip_update(self, context, port): + filter = {'fixed_port_id': [port['id']]} + local_fip = self.get_floatingips(context, + filters=filter) + if local_fip: + fip = local_fip[0] + self._create_update_floatingip(context, + fip, port['id']) + + def _create_update_floatingip(self, context, + neutron_fip, port_id): + rtr_id = neutron_fip['router_id'] + net_id = neutron_fip['floating_network_id'] + + fip_pool_mapping = nuagedb.get_fip_pool_from_netid(context.session, + net_id) + fip_mapping = nuagedb.get_fip_mapping_by_id(context.session, + neutron_fip['id']) + + if not fip_mapping: + ent_rtr_mapping = nuagedb.get_ent_rtr_mapping_by_rtrid( + context.session, rtr_id) + if not ent_rtr_mapping: + msg = _('router %s is not associated with ' + 'any net-partition') % rtr_id + raise n_exc.BadRequest(resource='floatingip', + msg=msg) + params = { + 'nuage_rtr_id': ent_rtr_mapping['nuage_router_id'], + 'nuage_fippool_id': fip_pool_mapping['fip_pool_id'], + 'neutron_fip_ip': neutron_fip['floating_ip_address'] + } + nuage_fip_id = self.nuageclient.create_nuage_floatingip(params) + nuagedb.add_fip_mapping(context.session, + neutron_fip['id'], + rtr_id, nuage_fip_id) + else: + if rtr_id != fip_mapping['router_id']: + msg = _('Floating IP can not be associated to VM in ' + 'different router context') + raise nuage_exc.OperationNotSupported(msg=msg) + nuage_fip_id = fip_mapping['nuage_fip_id'] + + fip_pool_dict = {'router_id': neutron_fip['router_id']} + nuagedb.update_fip_pool_mapping(fip_pool_mapping, + fip_pool_dict) + + # Update VM if required + port_mapping = nuagedb.get_port_mapping_by_id(context.session, + port_id) + if port_mapping: + params = { + 'nuage_vport_id': port_mapping['nuage_vport_id'], + 'nuage_fip_id': nuage_fip_id + } + self.nuageclient.update_nuage_vm_vport(params) + + def create_floatingip(self, context, floatingip): + fip = floatingip['floatingip'] + with context.session.begin(subtransactions=True): + neutron_fip = super(NuagePlugin, self).create_floatingip( + context, floatingip) + if not neutron_fip['router_id']: + return neutron_fip + try: + self._create_update_floatingip(context, neutron_fip, + fip['port_id']) + except (nuage_exc.OperationNotSupported, n_exc.BadRequest): + with excutils.save_and_reraise_exception(): + super(NuagePlugin, self).delete_floatingip( + context, neutron_fip['id']) + return neutron_fip + + def disassociate_floatingips(self, context, port_id): + super(NuagePlugin, self).disassociate_floatingips(context, port_id) + port_mapping = nuagedb.get_port_mapping_by_id(context.session, + port_id) + if port_mapping: + params = { + 'nuage_vport_id': port_mapping['nuage_vport_id'], + 'nuage_fip_id': None + } + self.nuageclient.update_nuage_vm_vport(params) + + def update_floatingip(self, context, id, floatingip): + fip = floatingip['floatingip'] + orig_fip = self._get_floatingip(context, id) + port_id = orig_fip['fixed_port_id'] + with context.session.begin(subtransactions=True): + neutron_fip = super(NuagePlugin, self).update_floatingip( + context, id, floatingip) + if fip['port_id'] is not None: + if not neutron_fip['router_id']: + ret_msg = 'floating-ip is not associated yet' + raise n_exc.BadRequest(resource='floatingip', + msg=ret_msg) + + try: + self._create_update_floatingip(context, + neutron_fip, + fip['port_id']) + except nuage_exc.OperationNotSupported: + with excutils.save_and_reraise_exception(): + super(NuagePlugin, + self).disassociate_floatingips(context, + fip['port_id']) + except n_exc.BadRequest: + with excutils.save_and_reraise_exception(): + super(NuagePlugin, self).delete_floatingip(context, + id) + else: + port_mapping = nuagedb.get_port_mapping_by_id(context.session, + port_id) + if port_mapping: + params = { + 'nuage_vport_id': port_mapping['nuage_vport_id'], + 'nuage_fip_id': None + } + self.nuageclient.update_nuage_vm_vport(params) + return neutron_fip + + def delete_floatingip(self, context, id): + fip = self._get_floatingip(context, id) + port_id = fip['fixed_port_id'] + with context.session.begin(subtransactions=True): + if port_id: + port_mapping = nuagedb.get_port_mapping_by_id(context.session, + port_id) + if (port_mapping and + port_mapping['nuage_vport_id'] is not None): + params = { + 'nuage_vport_id': port_mapping['nuage_vport_id'], + 'nuage_fip_id': None + } + self.nuageclient.update_nuage_vm_vport(params) + fip_mapping = nuagedb.get_fip_mapping_by_id(context.session, + id) + if fip_mapping: + self.nuageclient.delete_nuage_floatingip( + fip_mapping['nuage_fip_id']) + nuagedb.delete_fip_mapping(context.session, fip_mapping) + super(NuagePlugin, self).delete_floatingip(context, id) diff --git a/neutron/tests/unit/nuage/fake_nuageclient.py b/neutron/tests/unit/nuage/fake_nuageclient.py index a9eabdb3e..6465c403e 100644 --- a/neutron/tests/unit/nuage/fake_nuageclient.py +++ b/neutron/tests/unit/nuage/fake_nuageclient.py @@ -14,7 +14,7 @@ # # @author: Ronak Shah, Aniket Dandekar, Nuage Networks, Alcatel-Lucent USA Inc. -import uuid +from neutron.openstack.common import uuidutils class FakeNuageClient(object): @@ -30,10 +30,10 @@ class FakeNuageClient(object): def create_subnet(self, neutron_subnet, params): nuage_subnet = { - 'nuage_l2template_id': str(uuid.uuid4()), - 'nuage_userid': str(uuid.uuid4()), - 'nuage_groupid': str(uuid.uuid4()), - 'nuage_l2domain_id': str(uuid.uuid4()) + 'nuage_l2template_id': uuidutils.generate_uuid(), + 'nuage_userid': uuidutils.generate_uuid(), + 'nuage_groupid': uuidutils.generate_uuid(), + 'nuage_l2domain_id': uuidutils.generate_uuid() } return nuage_subnet @@ -42,10 +42,10 @@ class FakeNuageClient(object): def create_router(self, neutron_router, router, params): nuage_router = { - 'nuage_userid': str(uuid.uuid4()), - 'nuage_groupid': str(uuid.uuid4()), - 'nuage_domain_id': str(uuid.uuid4()), - 'nuage_def_zone_id': str(uuid.uuid4()), + 'nuage_userid': uuidutils.generate_uuid(), + 'nuage_groupid': uuidutils.generate_uuid(), + 'nuage_domain_id': uuidutils.generate_uuid(), + 'nuage_def_zone_id': uuidutils.generate_uuid(), } return nuage_router @@ -66,9 +66,9 @@ class FakeNuageClient(object): def create_net_partition(self, params): fake_net_partition = { - 'nuage_entid': str(uuid.uuid4()), - 'l3dom_id': str(uuid.uuid4()), - 'l2dom_id': str(uuid.uuid4()), + 'nuage_entid': uuidutils.generate_uuid(), + 'l3dom_id': uuidutils.generate_uuid(), + 'l2dom_id': uuidutils.generate_uuid(), } return fake_net_partition @@ -85,7 +85,22 @@ class FakeNuageClient(object): pass def create_nuage_staticroute(self, params): - return str(uuid.uuid4()) + return uuidutils.generate_uuid() def delete_nuage_staticroute(self, id): pass + + def create_nuage_sharedresource(self, params): + return uuidutils.generate_uuid() + + def delete_nuage_sharedresource(self, id): + pass + + def create_nuage_floatingip(self, params): + return uuidutils.generate_uuid() + + def delete_nuage_floatingip(self, id): + pass + + def update_nuage_vm_vport(self, params): + pass diff --git a/neutron/tests/unit/nuage/test_nuage_plugin.py b/neutron/tests/unit/nuage/test_nuage_plugin.py index 1fd40e43a..afbc91bb9 100644 --- a/neutron/tests/unit/nuage/test_nuage_plugin.py +++ b/neutron/tests/unit/nuage/test_nuage_plugin.py @@ -14,12 +14,14 @@ # # @author: Ronak Shah, Aniket Dandekar, Nuage Networks, Alcatel-Lucent USA Inc. +import contextlib import os import mock from oslo.config import cfg from webob import exc +from neutron.extensions import external_net from neutron.extensions import portbindings from neutron.plugins.nuage import extensions from neutron.plugins.nuage import plugin as nuage_plugin @@ -67,6 +69,83 @@ class NuagePluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase): super(NuagePluginV2TestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) + def _assert_no_assoc_fip(self, fip): + body = self._show('floatingips', + fip['floatingip']['id']) + self.assertIsNone(body['floatingip']['port_id']) + self.assertIsNone( + body['floatingip']['fixed_ip_address']) + + def _associate_and_assert_fip(self, fip, port, allow=True): + port_id = port['port']['id'] + ip_address = (port['port']['fixed_ips'] + [0]['ip_address']) + if allow: + body = self._update( + 'floatingips', fip['floatingip']['id'], + {'floatingip': {'port_id': port_id}}) + self.assertEqual( + body['floatingip']['port_id'], port_id) + self.assertEqual( + body['floatingip']['fixed_ip_address'], + ip_address) + return body['floatingip']['router_id'] + else: + code = exc.HTTPInternalServerError.code + self._update( + 'floatingips', fip['floatingip']['id'], + {'floatingip': {'port_id': port_id}}, + expected_code=code) + + def _test_floatingip_update_different_router(self): + with contextlib.nested(self.subnet(cidr='10.0.0.0/24'), + self.subnet(cidr='10.0.1.0/24')) as ( + s1, s2): + with contextlib.nested(self.port(subnet=s1), + self.port(subnet=s2)) as (p1, p2): + private_sub1 = {'subnet': + {'id': + p1['port']['fixed_ips'][0]['subnet_id']}} + private_sub2 = {'subnet': + {'id': + p2['port']['fixed_ips'][0]['subnet_id']}} + with self.subnet(cidr='12.0.0.0/24') as public_sub: + with contextlib.nested( + self.floatingip_no_assoc_with_public_sub( + private_sub1, public_sub=public_sub), + self.floatingip_no_assoc_with_public_sub( + private_sub2, public_sub=public_sub)) as ( + (fip1, r1), (fip2, r2)): + + self._assert_no_assoc_fip(fip1) + self._assert_no_assoc_fip(fip2) + + fip1_r1_res = self._associate_and_assert_fip(fip1, p1) + self.assertEqual(fip1_r1_res, r1['router']['id']) + # The following operation will associate the floating + # ip to a different router and should fail + self._associate_and_assert_fip(fip1, p2, allow=False) + # disassociate fip1 + self._update( + 'floatingips', fip1['floatingip']['id'], + {'floatingip': {'port_id': None}}) + fip2_r2_res = self._associate_and_assert_fip(fip2, p2) + self.assertEqual(fip2_r2_res, r2['router']['id']) + + def _test_network_update_external_failure(self): + with self.router() as r: + with self.subnet() as s1: + self._set_net_external(s1['subnet']['network_id']) + self._add_external_gateway_to_router( + r['router']['id'], + s1['subnet']['network_id']) + self._update('networks', s1['subnet']['network_id'], + {'network': {external_net.EXTERNAL: False}}, + expected_code=exc.HTTPInternalServerError.code) + self._remove_external_gateway_from_router( + r['router']['id'], + s1['subnet']['network_id']) + class TestNuageBasicGet(NuagePluginV2TestCase, test_db_plugin.TestBasicGet): @@ -161,7 +240,12 @@ class TestNuagePortsV2(NuagePluginV2TestCase, class TestNuageL3NatTestCase(NuagePluginV2TestCase, test_l3_plugin.L3NatDBIntTestCase): - pass + + def test_floatingip_update_different_router(self): + self._test_floatingip_update_different_router() + + def test_network_update_external_failure(self): + self._test_network_update_external_failure() class TestNuageExtrarouteTestCase(NuagePluginV2TestCase, @@ -191,3 +275,9 @@ class TestNuageExtrarouteTestCase(NuagePluginV2TestCase, r['router']['id'], None, p['port']['id']) + + def test_floatingip_update_different_router(self): + self._test_floatingip_update_different_router() + + def test_network_update_external_failure(self): + self._test_network_update_external_failure() -- 2.45.2