]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Floatingip extension support for nuage plugin
authorronak <ronak.malav.shah@gmail.com>
Wed, 21 May 2014 17:05:48 +0000 (10:05 -0700)
committerronak <ronak.malav.shah@gmail.com>
Wed, 11 Jun 2014 06:13:47 +0000 (23:13 -0700)
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

neutron/db/migration/alembic_migrations/versions/2db5203cb7a9_nuage_floatingip.py [new file with mode: 0644]
neutron/db/migration/alembic_migrations/versions/HEAD
neutron/plugins/nuage/common/constants.py
neutron/plugins/nuage/nuage_models.py
neutron/plugins/nuage/nuagedb.py
neutron/plugins/nuage/plugin.py
neutron/tests/unit/nuage/fake_nuageclient.py
neutron/tests/unit/nuage/test_nuage_plugin.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 (file)
index 0000000..57876d0
--- /dev/null
@@ -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')
index 7bf9d4cdf0daf17bc8bb671d3420487cbc76887f..2431c7d6b6982079fce4727aaa53af86f3925af5 100644 (file)
@@ -1 +1 @@
-10cd28e692e9
\ No newline at end of file
+2db5203cb7a9
\ No newline at end of file
index f35481299f2f9f84905c420b87c3847b5a552dea..ff2680bf7bac3c7742a74f6a8f9409800188bf3a 100644 (file)
@@ -24,3 +24,5 @@ AUTO_CREATE_PORT_OWNERS = [
 ]
 
 NOVA_PORT_OWNER_PREF = 'compute:'
+
+SR_TYPE_FLOATING = "FLOATING"
index 2ac8e25eaea8b7f5fe182d6eea5835d14ab7de7a..f3ebcffa18ccefb01879b882bdab3c9a44c411c5 100644 (file)
@@ -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))
index a52b695c04b72b5191e9cc14ea2cc371ee4036fc..bd1b2f3d221e98a711aef8cba915275a7f99bce3 100644 (file)
@@ -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)
index b27821895d0c88453c1fa69fe2a9792292d448f8..d5d9d34d9d6bec2371e23ceab99f2561356d200c 100644 (file)
@@ -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)
index a9eabdb3e2f19d20889d165341ab77db832cb648..6465c403ee84d543f040f5f68c2b32fb3008bb2f 100644 (file)
@@ -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
index 1fd40e43ad080edad8963003080525752d016085..afbc91bb9a63f85d346423b70f069bf147fa2620 100644 (file)
 #
 # @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()