Support for NVP advanced service router
When creating an LR:
    - deploy an Edge asynchronously
    - create a L2 switch for connecting LR and Edge
    - attach a router port to the L2 switch.
    - assign ip address 169.254.2.1/28 and nexthop 169.254.2.3 to LR
When set external gateway:
    - configure Edge interface and default gateway
    - Add static routes to Edge for all logic networks attached to LR via nexthop 169.254.2.1
    - configure SNAT rules for all logic networks attached to LR
When add router interface:
    - Add static route/SNAT rule for the network attached to LR
When associate floating IP address:
    - configure DNAT rule for the floating ip and the port
Tests being done:
    - Verified Edge is deployed asynchronously and LR is attached to the internal created L2 switch
    - Manually attach Edge's vNic to the L2 switch and Edge is able to ping 169.254.2.1
    - Verified router-delete deletes Edge asynchronously and remove the internal L2 switch
    - Verified SNAT/DNAT/static-routes rules are configured on Edge in correct order
    - Verified external vnic ip address/netmask and default gateway is configured
Implements: blueprint nvp-service-router
Change-Id: If9eff53df4d65cf4e318dedbfaafc742f6c6ab7f
			
			
This commit is contained in:
		@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright 2013 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.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""service router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Revision ID: 4a666eb208c2
 | 
				
			||||||
 | 
					Revises: 38fc1f6789f8
 | 
				
			||||||
 | 
					Create Date: 2013-09-03 01:55:57.799217
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# revision identifiers, used by Alembic.
 | 
				
			||||||
 | 
					revision = '4a666eb208c2'
 | 
				
			||||||
 | 
					down_revision = '38fc1f6789f8'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Change to ['*'] if this migration applies to all plugins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					migration_for_plugins = [
 | 
				
			||||||
 | 
					    'neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin'
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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(
 | 
				
			||||||
 | 
					        'vcns_router_bindings',
 | 
				
			||||||
 | 
					        sa.Column('status', sa.String(length=16), nullable=False),
 | 
				
			||||||
 | 
					        sa.Column('status_description', sa.String(length=255), nullable=True),
 | 
				
			||||||
 | 
					        sa.Column('router_id', sa.String(length=36), nullable=False),
 | 
				
			||||||
 | 
					        sa.Column('edge_id', sa.String(length=16), nullable=True),
 | 
				
			||||||
 | 
					        sa.Column('lswitch_id', sa.String(length=36), nullable=False),
 | 
				
			||||||
 | 
					        sa.PrimaryKeyConstraint('router_id'),
 | 
				
			||||||
 | 
					        mysql_engine='InnoDB'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    op.add_column(
 | 
				
			||||||
 | 
					        u'nsxrouterextattributess',
 | 
				
			||||||
 | 
					        sa.Column('service_router',
 | 
				
			||||||
 | 
					        sa.Boolean(),
 | 
				
			||||||
 | 
					        nullable=False))
 | 
				
			||||||
 | 
					    op.execute("UPDATE nsxrouterextattributess set service_router=False")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def downgrade(active_plugins=None, options=None):
 | 
				
			||||||
 | 
					    if not migration.should_run(active_plugins, migration_for_plugins):
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    op.drop_column(u'nsxrouterextattributess', 'service_router')
 | 
				
			||||||
 | 
					    op.drop_table('vcns_router_bindings')
 | 
				
			||||||
@@ -54,6 +54,7 @@ from neutron.extensions import portsecurity as psec
 | 
				
			|||||||
from neutron.extensions import providernet as pnet
 | 
					from neutron.extensions import providernet as pnet
 | 
				
			||||||
from neutron.extensions import securitygroup as ext_sg
 | 
					from neutron.extensions import securitygroup as ext_sg
 | 
				
			||||||
from neutron.openstack.common import excutils
 | 
					from neutron.openstack.common import excutils
 | 
				
			||||||
 | 
					from neutron.plugins.common import constants as plugin_const
 | 
				
			||||||
from neutron.plugins.nicira.common import config
 | 
					from neutron.plugins.nicira.common import config
 | 
				
			||||||
from neutron.plugins.nicira.common import exceptions as nvp_exc
 | 
					from neutron.plugins.nicira.common import exceptions as nvp_exc
 | 
				
			||||||
from neutron.plugins.nicira.common import securitygroups as nvp_sec
 | 
					from neutron.plugins.nicira.common import securitygroups as nvp_sec
 | 
				
			||||||
@@ -78,6 +79,7 @@ NVP_NOSNAT_RULES_ORDER = 10
 | 
				
			|||||||
NVP_FLOATINGIP_NAT_RULES_ORDER = 224
 | 
					NVP_FLOATINGIP_NAT_RULES_ORDER = 224
 | 
				
			||||||
NVP_EXTGW_NAT_RULES_ORDER = 255
 | 
					NVP_EXTGW_NAT_RULES_ORDER = 255
 | 
				
			||||||
NVP_EXT_PATH = os.path.join(os.path.dirname(__file__), 'extensions')
 | 
					NVP_EXT_PATH = os.path.join(os.path.dirname(__file__), 'extensions')
 | 
				
			||||||
 | 
					NVP_DEFAULT_NEXTHOP = '1.1.1.1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Provider network extension - allowed network types for the NVP Plugin
 | 
					# Provider network extension - allowed network types for the NVP Plugin
 | 
				
			||||||
@@ -1381,6 +1383,45 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return super(NvpPluginV2, self).get_router(context, id, fields)
 | 
					            return super(NvpPluginV2, self).get_router(context, id, fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _create_lrouter(self, context, router, nexthop):
 | 
				
			||||||
 | 
					        tenant_id = self._get_tenant_id_for_create(context, router)
 | 
				
			||||||
 | 
					        name = router['name']
 | 
				
			||||||
 | 
					        distributed = router.get('distributed')
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            lrouter = nvplib.create_lrouter(
 | 
				
			||||||
 | 
					                self.cluster, tenant_id, name, nexthop,
 | 
				
			||||||
 | 
					                distributed=attr.is_attr_set(distributed) and distributed)
 | 
				
			||||||
 | 
					        except nvp_exc.NvpInvalidVersion:
 | 
				
			||||||
 | 
					            msg = _("Cannot create a distributed router with the NVP "
 | 
				
			||||||
 | 
					                    "platform currently in execution. Please, try "
 | 
				
			||||||
 | 
					                    "without specifying the 'distributed' attribute.")
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            raise q_exc.BadRequest(resource='router', msg=msg)
 | 
				
			||||||
 | 
					        except NvpApiClient.NvpApiException:
 | 
				
			||||||
 | 
					            raise nvp_exc.NvpPluginException(
 | 
				
			||||||
 | 
					                err_msg=_("Unable to create logical router on NVP Platform"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Create the port here - and update it later if we have gw_info
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self._create_and_attach_router_port(
 | 
				
			||||||
 | 
					                self.cluster, context, lrouter['uuid'], {'fake_ext_gw': True},
 | 
				
			||||||
 | 
					                "L3GatewayAttachment",
 | 
				
			||||||
 | 
					                self.cluster.default_l3_gw_service_uuid)
 | 
				
			||||||
 | 
					        except nvp_exc.NvpPluginException:
 | 
				
			||||||
 | 
					            LOG.exception(_("Unable to create L3GW port on logical router "
 | 
				
			||||||
 | 
					                            "%(router_uuid)s. Verify Default Layer-3 Gateway "
 | 
				
			||||||
 | 
					                            "service %(def_l3_gw_svc)s id is correct"),
 | 
				
			||||||
 | 
					                          {'router_uuid': lrouter['uuid'],
 | 
				
			||||||
 | 
					                           'def_l3_gw_svc':
 | 
				
			||||||
 | 
					                           self.cluster.default_l3_gw_service_uuid})
 | 
				
			||||||
 | 
					            # Try and remove logical router from NVP
 | 
				
			||||||
 | 
					            nvplib.delete_lrouter(self.cluster, lrouter['uuid'])
 | 
				
			||||||
 | 
					            # Return user a 500 with an apter message
 | 
				
			||||||
 | 
					            raise nvp_exc.NvpPluginException(
 | 
				
			||||||
 | 
					                err_msg=_("Unable to create router %s") % router['name'])
 | 
				
			||||||
 | 
					        lrouter['status'] = plugin_const.ACTIVE
 | 
				
			||||||
 | 
					        return lrouter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_router(self, context, router):
 | 
					    def create_router(self, context, router):
 | 
				
			||||||
        # NOTE(salvatore-orlando): We completely override this method in
 | 
					        # NOTE(salvatore-orlando): We completely override this method in
 | 
				
			||||||
        # order to be able to use the NVP ID as Neutron ID
 | 
					        # order to be able to use the NVP ID as Neutron ID
 | 
				
			||||||
@@ -1390,8 +1431,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
        has_gw_info = False
 | 
					        has_gw_info = False
 | 
				
			||||||
        tenant_id = self._get_tenant_id_for_create(context, r)
 | 
					        tenant_id = self._get_tenant_id_for_create(context, r)
 | 
				
			||||||
        # default value to set - nvp wants it (even if we don't have it)
 | 
					        # default value to set - nvp wants it (even if we don't have it)
 | 
				
			||||||
        nexthop = '1.1.1.1'
 | 
					        nexthop = NVP_DEFAULT_NEXTHOP
 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
        # if external gateway info are set, then configure nexthop to
 | 
					        # if external gateway info are set, then configure nexthop to
 | 
				
			||||||
        # default external gateway
 | 
					        # default external gateway
 | 
				
			||||||
        if 'external_gateway_info' in r and r.get('external_gateway_info'):
 | 
					        if 'external_gateway_info' in r and r.get('external_gateway_info'):
 | 
				
			||||||
@@ -1412,44 +1452,14 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                if ext_net.subnets:
 | 
					                if ext_net.subnets:
 | 
				
			||||||
                    ext_subnet = ext_net.subnets[0]
 | 
					                    ext_subnet = ext_net.subnets[0]
 | 
				
			||||||
                    nexthop = ext_subnet.gateway_ip
 | 
					                    nexthop = ext_subnet.gateway_ip
 | 
				
			||||||
            distributed = r.get('distributed')
 | 
					        lrouter = self._create_lrouter(context, r, nexthop)
 | 
				
			||||||
            lrouter = nvplib.create_lrouter(
 | 
					 | 
				
			||||||
                self.cluster, tenant_id, router['router']['name'], nexthop,
 | 
					 | 
				
			||||||
                distributed=attr.is_attr_set(distributed) and distributed)
 | 
					 | 
				
			||||||
        # Use NVP identfier for Neutron resource
 | 
					        # Use NVP identfier for Neutron resource
 | 
				
			||||||
        r['id'] = lrouter['uuid']
 | 
					        r['id'] = lrouter['uuid']
 | 
				
			||||||
        # Update 'distributed' with value returned from NVP
 | 
					        # Update 'distributed' with value returned from NVP
 | 
				
			||||||
        # This will be useful for setting the value if the API request
 | 
					        # This will be useful for setting the value if the API request
 | 
				
			||||||
            # did not specify any value for the 'distributed' attribute.
 | 
					        # did not specify any value for the 'distributed' attribute
 | 
				
			||||||
        # Platforms older than 3.x do not support the attribute
 | 
					        # Platforms older than 3.x do not support the attribute
 | 
				
			||||||
        r['distributed'] = lrouter.get('distributed', False)
 | 
					        r['distributed'] = lrouter.get('distributed', False)
 | 
				
			||||||
        except nvp_exc.NvpInvalidVersion:
 | 
					 | 
				
			||||||
            msg = _("Cannot create a distributed router with the NVP "
 | 
					 | 
				
			||||||
                    "platform currently in execution. Please, try "
 | 
					 | 
				
			||||||
                    "without specifying the 'distributed' attribute.")
 | 
					 | 
				
			||||||
            LOG.exception(msg)
 | 
					 | 
				
			||||||
            raise q_exc.BadRequest(resource='router', msg=msg)
 | 
					 | 
				
			||||||
        except NvpApiClient.NvpApiException:
 | 
					 | 
				
			||||||
            raise nvp_exc.NvpPluginException(
 | 
					 | 
				
			||||||
                err_msg=_("Unable to create logical router on NVP Platform"))
 | 
					 | 
				
			||||||
        # Create the port here - and update it later if we have gw_info
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            self._create_and_attach_router_port(
 | 
					 | 
				
			||||||
                self.cluster, context, lrouter['uuid'], {'fake_ext_gw': True},
 | 
					 | 
				
			||||||
                "L3GatewayAttachment",
 | 
					 | 
				
			||||||
                self.cluster.default_l3_gw_service_uuid)
 | 
					 | 
				
			||||||
        except nvp_exc.NvpPluginException:
 | 
					 | 
				
			||||||
            LOG.exception(_("Unable to create L3GW port on logical router "
 | 
					 | 
				
			||||||
                            "%(router_uuid)s. Verify Default Layer-3 Gateway "
 | 
					 | 
				
			||||||
                            "service %(def_l3_gw_svc)s id is correct"),
 | 
					 | 
				
			||||||
                          {'router_uuid': lrouter['uuid'],
 | 
					 | 
				
			||||||
                           'def_l3_gw_svc':
 | 
					 | 
				
			||||||
                           self.cluster.default_l3_gw_service_uuid})
 | 
					 | 
				
			||||||
            # Try and remove logical router from NVP
 | 
					 | 
				
			||||||
            nvplib.delete_lrouter(self.cluster, lrouter['uuid'])
 | 
					 | 
				
			||||||
            # Return user a 500 with an apter message
 | 
					 | 
				
			||||||
            raise nvp_exc.NvpPluginException(
 | 
					 | 
				
			||||||
                err_msg=_("Unable to create router %s") % r['name'])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with context.session.begin(subtransactions=True):
 | 
					        with context.session.begin(subtransactions=True):
 | 
				
			||||||
            # Transaction nesting is needed to avoid foreign key violations
 | 
					            # Transaction nesting is needed to avoid foreign key violations
 | 
				
			||||||
@@ -1459,14 +1469,23 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                                         tenant_id=tenant_id,
 | 
					                                         tenant_id=tenant_id,
 | 
				
			||||||
                                         name=r['name'],
 | 
					                                         name=r['name'],
 | 
				
			||||||
                                         admin_state_up=r['admin_state_up'],
 | 
					                                         admin_state_up=r['admin_state_up'],
 | 
				
			||||||
                                         status="ACTIVE")
 | 
					                                         status=lrouter['status'])
 | 
				
			||||||
                self._process_distributed_router_create(context, router_db, r)
 | 
					                self._process_nsx_router_create(context, router_db, r)
 | 
				
			||||||
                context.session.add(router_db)
 | 
					                context.session.add(router_db)
 | 
				
			||||||
            if has_gw_info:
 | 
					            if has_gw_info:
 | 
				
			||||||
                self._update_router_gw_info(context, router_db['id'], gw_info)
 | 
					                self._update_router_gw_info(context, router_db['id'], gw_info)
 | 
				
			||||||
        router = self._make_router_dict(router_db)
 | 
					        router = self._make_router_dict(router_db)
 | 
				
			||||||
        return router
 | 
					        return router
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_lrouter(self, context, router_id, name, nexthop, routes=None):
 | 
				
			||||||
 | 
					        return nvplib.update_lrouter(
 | 
				
			||||||
 | 
					            self.cluster, router_id, name,
 | 
				
			||||||
 | 
					            nexthop, routes=routes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_lrouter_routes(self, router_id, routes):
 | 
				
			||||||
 | 
					        nvplib.update_explicit_routes_lrouter(
 | 
				
			||||||
 | 
					            self.cluster, router_id, routes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update_router(self, context, router_id, router):
 | 
					    def update_router(self, context, router_id, router):
 | 
				
			||||||
        # Either nexthop is updated or should be kept as it was before
 | 
					        # Either nexthop is updated or should be kept as it was before
 | 
				
			||||||
        r = router['router']
 | 
					        r = router['router']
 | 
				
			||||||
@@ -1494,8 +1513,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                            "this must be updated through the default "
 | 
					                            "this must be updated through the default "
 | 
				
			||||||
                            "gateway attribute")
 | 
					                            "gateway attribute")
 | 
				
			||||||
                    raise q_exc.BadRequest(resource='router', msg=msg)
 | 
					                    raise q_exc.BadRequest(resource='router', msg=msg)
 | 
				
			||||||
            previous_routes = nvplib.update_lrouter(
 | 
					            previous_routes = self._update_lrouter(
 | 
				
			||||||
                self.cluster, router_id, r.get('name'),
 | 
					                context, router_id, r.get('name'),
 | 
				
			||||||
                nexthop, routes=r.get('routes'))
 | 
					                nexthop, routes=r.get('routes'))
 | 
				
			||||||
        # NOTE(salv-orlando): The exception handling below is not correct, but
 | 
					        # NOTE(salv-orlando): The exception handling below is not correct, but
 | 
				
			||||||
        # unfortunately nvplib raises a neutron notfound exception when an
 | 
					        # unfortunately nvplib raises a neutron notfound exception when an
 | 
				
			||||||
@@ -1525,8 +1544,11 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                extraroute.RoutesExhausted):
 | 
					                extraroute.RoutesExhausted):
 | 
				
			||||||
            with excutils.save_and_reraise_exception():
 | 
					            with excutils.save_and_reraise_exception():
 | 
				
			||||||
                # revert changes made to NVP
 | 
					                # revert changes made to NVP
 | 
				
			||||||
                nvplib.update_explicit_routes_lrouter(
 | 
					                self._update_lrouter_routes(
 | 
				
			||||||
                    self.cluster, router_id, previous_routes)
 | 
					                    router_id, previous_routes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _delete_lrouter(self, context, id):
 | 
				
			||||||
 | 
					        nvplib.delete_lrouter(self.cluster, id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def delete_router(self, context, router_id):
 | 
					    def delete_router(self, context, router_id):
 | 
				
			||||||
        with context.session.begin(subtransactions=True):
 | 
					        with context.session.begin(subtransactions=True):
 | 
				
			||||||
@@ -1544,7 +1566,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
            # allow an extra field for storing the cluster information
 | 
					            # allow an extra field for storing the cluster information
 | 
				
			||||||
            # together with the resource
 | 
					            # together with the resource
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                nvplib.delete_lrouter(self.cluster, router_id)
 | 
					                self._delete_lrouter(context, router_id)
 | 
				
			||||||
            except q_exc.NotFound:
 | 
					            except q_exc.NotFound:
 | 
				
			||||||
                LOG.warning(_("Logical router '%s' not found "
 | 
					                LOG.warning(_("Logical router '%s' not found "
 | 
				
			||||||
                              "on NVP Platform"), router_id)
 | 
					                              "on NVP Platform"), router_id)
 | 
				
			||||||
@@ -1553,6 +1575,27 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                    err_msg=(_("Unable to delete logical router '%s' "
 | 
					                    err_msg=(_("Unable to delete logical router '%s' "
 | 
				
			||||||
                               "on NVP Platform") % router_id))
 | 
					                               "on NVP Platform") % router_id))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _add_subnet_snat_rule(self, router, subnet):
 | 
				
			||||||
 | 
					        gw_port = router.gw_port
 | 
				
			||||||
 | 
					        if gw_port and router.enable_snat:
 | 
				
			||||||
 | 
					            # There is a change gw_port might have multiple IPs
 | 
				
			||||||
 | 
					            # In that case we will consider only the first one
 | 
				
			||||||
 | 
					            if gw_port.get('fixed_ips'):
 | 
				
			||||||
 | 
					                snat_ip = gw_port['fixed_ips'][0]['ip_address']
 | 
				
			||||||
 | 
					                cidr_prefix = int(subnet['cidr'].split('/')[1])
 | 
				
			||||||
 | 
					                nvplib.create_lrouter_snat_rule(
 | 
				
			||||||
 | 
					                    self.cluster, router['id'], snat_ip, snat_ip,
 | 
				
			||||||
 | 
					                    order=NVP_EXTGW_NAT_RULES_ORDER - cidr_prefix,
 | 
				
			||||||
 | 
					                    match_criteria={'source_ip_addresses': subnet['cidr']})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _delete_subnet_snat_rule(self, router, subnet):
 | 
				
			||||||
 | 
					        # Remove SNAT rule if external gateway is configured
 | 
				
			||||||
 | 
					        if router.gw_port:
 | 
				
			||||||
 | 
					            nvplib.delete_nat_rules_by_match(
 | 
				
			||||||
 | 
					                self.cluster, router['id'], "SourceNatRule",
 | 
				
			||||||
 | 
					                max_num_expected=1, min_num_expected=1,
 | 
				
			||||||
 | 
					                source_ip_addresses=subnet['cidr'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add_router_interface(self, context, router_id, interface_info):
 | 
					    def add_router_interface(self, context, router_id, interface_info):
 | 
				
			||||||
        # When adding interface by port_id we need to create the
 | 
					        # When adding interface by port_id we need to create the
 | 
				
			||||||
        # peer port on the nvp logical router in this routine
 | 
					        # peer port on the nvp logical router in this routine
 | 
				
			||||||
@@ -1587,17 +1630,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
        # If there is an external gateway we need to configure the SNAT rule.
 | 
					        # If there is an external gateway we need to configure the SNAT rule.
 | 
				
			||||||
        # Fetch router from DB
 | 
					        # Fetch router from DB
 | 
				
			||||||
        router = self._get_router(context, router_id)
 | 
					        router = self._get_router(context, router_id)
 | 
				
			||||||
        gw_port = router.gw_port
 | 
					        self._add_subnet_snat_rule(router, subnet)
 | 
				
			||||||
        if gw_port and router.enable_snat:
 | 
					 | 
				
			||||||
            # There is a change gw_port might have multiple IPs
 | 
					 | 
				
			||||||
            # In that case we will consider only the first one
 | 
					 | 
				
			||||||
            if gw_port.get('fixed_ips'):
 | 
					 | 
				
			||||||
                snat_ip = gw_port['fixed_ips'][0]['ip_address']
 | 
					 | 
				
			||||||
                cidr_prefix = int(subnet['cidr'].split('/')[1])
 | 
					 | 
				
			||||||
                nvplib.create_lrouter_snat_rule(
 | 
					 | 
				
			||||||
                    self.cluster, router_id, snat_ip, snat_ip,
 | 
					 | 
				
			||||||
                    order=NVP_EXTGW_NAT_RULES_ORDER - cidr_prefix,
 | 
					 | 
				
			||||||
                    match_criteria={'source_ip_addresses': subnet['cidr']})
 | 
					 | 
				
			||||||
        nvplib.create_lrouter_nosnat_rule(
 | 
					        nvplib.create_lrouter_nosnat_rule(
 | 
				
			||||||
            self.cluster, router_id,
 | 
					            self.cluster, router_id,
 | 
				
			||||||
            order=NVP_NOSNAT_RULES_ORDER,
 | 
					            order=NVP_NOSNAT_RULES_ORDER,
 | 
				
			||||||
@@ -1655,12 +1688,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
            if not subnet:
 | 
					            if not subnet:
 | 
				
			||||||
                subnet = self._get_subnet(context, subnet_id)
 | 
					                subnet = self._get_subnet(context, subnet_id)
 | 
				
			||||||
            router = self._get_router(context, router_id)
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
            # Remove SNAT rule if external gateway is configured
 | 
					            self._delete_subnet_snat_rule(router, subnet)
 | 
				
			||||||
            if router.gw_port:
 | 
					 | 
				
			||||||
                nvplib.delete_nat_rules_by_match(
 | 
					 | 
				
			||||||
                    self.cluster, router_id, "SourceNatRule",
 | 
					 | 
				
			||||||
                    max_num_expected=1, min_num_expected=1,
 | 
					 | 
				
			||||||
                    source_ip_addresses=subnet['cidr'])
 | 
					 | 
				
			||||||
            # Relax the minimum expected number as the nosnat rules
 | 
					            # Relax the minimum expected number as the nosnat rules
 | 
				
			||||||
            # do not exist in 2.x deployments
 | 
					            # do not exist in 2.x deployments
 | 
				
			||||||
            nvplib.delete_nat_rules_by_match(
 | 
					            nvplib.delete_nat_rules_by_match(
 | 
				
			||||||
@@ -1677,7 +1705,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                           "on NVP Platform")))
 | 
					                           "on NVP Platform")))
 | 
				
			||||||
        return info
 | 
					        return info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _retrieve_and_delete_nat_rules(self, floating_ip_address,
 | 
					    def _retrieve_and_delete_nat_rules(self, context, floating_ip_address,
 | 
				
			||||||
                                       internal_ip, router_id,
 | 
					                                       internal_ip, router_id,
 | 
				
			||||||
                                       min_num_rules_expected=0):
 | 
					                                       min_num_rules_expected=0):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -1720,12 +1748,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                                       ips_to_add=[],
 | 
					                                       ips_to_add=[],
 | 
				
			||||||
                                       ips_to_remove=nvp_floating_ips)
 | 
					                                       ips_to_remove=nvp_floating_ips)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
 | 
					    def _get_fip_assoc_data(self, context, fip, floatingip_db):
 | 
				
			||||||
        """Update floating IP association data.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Overrides method from base class.
 | 
					 | 
				
			||||||
        The method is augmented for creating NAT rules in the process.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if (('fixed_ip_address' in fip and fip['fixed_ip_address']) and
 | 
					        if (('fixed_ip_address' in fip and fip['fixed_ip_address']) and
 | 
				
			||||||
            not ('port_id' in fip and fip['port_id'])):
 | 
					            not ('port_id' in fip and fip['port_id'])):
 | 
				
			||||||
            msg = _("fixed_ip_address cannot be specified without a port_id")
 | 
					            msg = _("fixed_ip_address cannot be specified without a port_id")
 | 
				
			||||||
@@ -1748,7 +1771,6 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                fip,
 | 
					                fip,
 | 
				
			||||||
                floatingip_db['floating_network_id'])
 | 
					                floatingip_db['floating_network_id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        floating_ip = floatingip_db['floating_ip_address']
 | 
					 | 
				
			||||||
        # Retrieve and delete existing NAT rules, if any
 | 
					        # Retrieve and delete existing NAT rules, if any
 | 
				
			||||||
        if not router_id and floatingip_db.get('fixed_port_id'):
 | 
					        if not router_id and floatingip_db.get('fixed_port_id'):
 | 
				
			||||||
            # This happens if we're disassociating. Need to explicitly
 | 
					            # This happens if we're disassociating. Need to explicitly
 | 
				
			||||||
@@ -1757,9 +1779,22 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
            tmp_fip['port_id'] = floatingip_db['fixed_port_id']
 | 
					            tmp_fip['port_id'] = floatingip_db['fixed_port_id']
 | 
				
			||||||
            _pid, internal_ip, router_id = self.get_assoc_data(
 | 
					            _pid, internal_ip, router_id = self.get_assoc_data(
 | 
				
			||||||
                context, tmp_fip, floatingip_db['floating_network_id'])
 | 
					                context, tmp_fip, floatingip_db['floating_network_id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (port_id, internal_ip, router_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
 | 
				
			||||||
 | 
					        """Update floating IP association data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Overrides method from base class.
 | 
				
			||||||
 | 
					        The method is augmented for creating NAT rules in the process.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        port_id, internal_ip, router_id = self._get_fip_assoc_data(
 | 
				
			||||||
 | 
					            context, fip, floatingip_db)
 | 
				
			||||||
 | 
					        floating_ip = floatingip_db['floating_ip_address']
 | 
				
			||||||
        # If there's no association router_id will be None
 | 
					        # If there's no association router_id will be None
 | 
				
			||||||
        if router_id:
 | 
					        if router_id:
 | 
				
			||||||
            self._retrieve_and_delete_nat_rules(floating_ip,
 | 
					            self._retrieve_and_delete_nat_rules(context,
 | 
				
			||||||
 | 
					                                                floating_ip,
 | 
				
			||||||
                                                internal_ip,
 | 
					                                                internal_ip,
 | 
				
			||||||
                                                router_id)
 | 
					                                                router_id)
 | 
				
			||||||
            # Fetch logical port of router's external gateway
 | 
					            # Fetch logical port of router's external gateway
 | 
				
			||||||
@@ -1797,6 +1832,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
                                    "internal ip:%(internal_ip)s"),
 | 
					                                    "internal ip:%(internal_ip)s"),
 | 
				
			||||||
                                  {'floating_ip': floating_ip,
 | 
					                                  {'floating_ip': floating_ip,
 | 
				
			||||||
                                   'internal_ip': internal_ip})
 | 
					                                   'internal_ip': internal_ip})
 | 
				
			||||||
 | 
					                    msg = _("Failed to update NAT rules for floatingip update")
 | 
				
			||||||
                    raise nvp_exc.NvpPluginException(err_msg=msg)
 | 
					                    raise nvp_exc.NvpPluginException(err_msg=msg)
 | 
				
			||||||
            elif floatingip_db['fixed_port_id']:
 | 
					            elif floatingip_db['fixed_port_id']:
 | 
				
			||||||
                # This is a disassociation.
 | 
					                # This is a disassociation.
 | 
				
			||||||
@@ -1816,7 +1852,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
        fip_db = self._get_floatingip(context, id)
 | 
					        fip_db = self._get_floatingip(context, id)
 | 
				
			||||||
        # Check whether the floating ip is associated or not
 | 
					        # Check whether the floating ip is associated or not
 | 
				
			||||||
        if fip_db.fixed_port_id:
 | 
					        if fip_db.fixed_port_id:
 | 
				
			||||||
            self._retrieve_and_delete_nat_rules(fip_db.floating_ip_address,
 | 
					            self._retrieve_and_delete_nat_rules(context,
 | 
				
			||||||
 | 
					                                                fip_db.floating_ip_address,
 | 
				
			||||||
                                                fip_db.fixed_ip_address,
 | 
					                                                fip_db.fixed_ip_address,
 | 
				
			||||||
                                                fip_db.router_id,
 | 
					                                                fip_db.router_id,
 | 
				
			||||||
                                                min_num_rules_expected=1)
 | 
					                                                min_num_rules_expected=1)
 | 
				
			||||||
@@ -1828,7 +1865,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            fip_qry = context.session.query(l3_db.FloatingIP)
 | 
					            fip_qry = context.session.query(l3_db.FloatingIP)
 | 
				
			||||||
            fip_db = fip_qry.filter_by(fixed_port_id=port_id).one()
 | 
					            fip_db = fip_qry.filter_by(fixed_port_id=port_id).one()
 | 
				
			||||||
            self._retrieve_and_delete_nat_rules(fip_db.floating_ip_address,
 | 
					            self._retrieve_and_delete_nat_rules(context,
 | 
				
			||||||
 | 
					                                                fip_db.floating_ip_address,
 | 
				
			||||||
                                                fip_db.fixed_ip_address,
 | 
					                                                fip_db.fixed_ip_address,
 | 
				
			||||||
                                                fip_db.router_id,
 | 
					                                                fip_db.router_id,
 | 
				
			||||||
                                                min_num_rules_expected=1)
 | 
					                                                min_num_rules_expected=1)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										808
									
								
								neutron/plugins/nicira/NeutronServicePlugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										808
									
								
								neutron/plugins/nicira/NeutronServicePlugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,808 @@
 | 
				
			|||||||
 | 
					# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright 2013 VMware, Inc.
 | 
				
			||||||
 | 
					# All Rights Reserved
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    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.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import netaddr
 | 
				
			||||||
 | 
					from oslo.config import cfg
 | 
				
			||||||
 | 
					from sqlalchemy.orm import exc as sa_exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron.common import exceptions as q_exc
 | 
				
			||||||
 | 
					from neutron.db import l3_db
 | 
				
			||||||
 | 
					from neutron.openstack.common import log as logging
 | 
				
			||||||
 | 
					from neutron.plugins.common import constants as service_constants
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.common import config  # noqa
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.dbexts import servicerouter as sr_db
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.dbexts import vcns_db
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.dbexts import vcns_models
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.extensions import servicerouter as sr
 | 
				
			||||||
 | 
					from neutron.plugins.nicira import NeutronPlugin
 | 
				
			||||||
 | 
					from neutron.plugins.nicira import NvpApiClient
 | 
				
			||||||
 | 
					from neutron.plugins.nicira import nvplib
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.vshield.common import (
 | 
				
			||||||
 | 
					    constants as vcns_const)
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.vshield.common.constants import RouterStatus
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.vshield.common import exceptions
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.vshield.tasks.constants import TaskStatus
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.vshield import vcns_driver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ROUTER_TYPE_BASIC = 1
 | 
				
			||||||
 | 
					ROUTER_TYPE_ADVANCED = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ROUTER_STATUS = [
 | 
				
			||||||
 | 
					    service_constants.ACTIVE,
 | 
				
			||||||
 | 
					    service_constants.DOWN,
 | 
				
			||||||
 | 
					    service_constants.PENDING_CREATE,
 | 
				
			||||||
 | 
					    service_constants.PENDING_DELETE,
 | 
				
			||||||
 | 
					    service_constants.ERROR
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ROUTER_STATUS_LEVEL = {
 | 
				
			||||||
 | 
					    service_constants.ACTIVE: RouterStatus.ROUTER_STATUS_ACTIVE,
 | 
				
			||||||
 | 
					    service_constants.DOWN: RouterStatus.ROUTER_STATUS_DOWN,
 | 
				
			||||||
 | 
					    service_constants.PENDING_CREATE: (
 | 
				
			||||||
 | 
					        RouterStatus.ROUTER_STATUS_PENDING_CREATE
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    service_constants.PENDING_DELETE: (
 | 
				
			||||||
 | 
					        RouterStatus.ROUTER_STATUS_PENDING_DELETE
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    service_constants.ERROR: RouterStatus.ROUTER_STATUS_ERROR
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin,
 | 
				
			||||||
 | 
					                        NeutronPlugin.NvpPluginV2):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    supported_extension_aliases = (
 | 
				
			||||||
 | 
					        NeutronPlugin.NvpPluginV2.supported_extension_aliases + [
 | 
				
			||||||
 | 
					            'service-router'
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        super(NvpAdvancedPlugin, self).__init__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._super_create_ext_gw_port = (
 | 
				
			||||||
 | 
					            self._port_drivers['create'][l3_db.DEVICE_OWNER_ROUTER_GW])
 | 
				
			||||||
 | 
					        self._super_delete_ext_gw_port = (
 | 
				
			||||||
 | 
					            self._port_drivers['delete'][l3_db.DEVICE_OWNER_ROUTER_GW])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._port_drivers['create'][l3_db.DEVICE_OWNER_ROUTER_GW] = (
 | 
				
			||||||
 | 
					            self._vcns_create_ext_gw_port)
 | 
				
			||||||
 | 
					        self._port_drivers['delete'][l3_db.DEVICE_OWNER_ROUTER_GW] = (
 | 
				
			||||||
 | 
					            self._vcns_delete_ext_gw_port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # cache router type based on router id
 | 
				
			||||||
 | 
					        self._router_type = {}
 | 
				
			||||||
 | 
					        self.callbacks = VcnsCallbacks(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # load the vCNS driver
 | 
				
			||||||
 | 
					        self._load_vcns_drivers()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _load_vcns_drivers(self):
 | 
				
			||||||
 | 
					        self.vcns_driver = vcns_driver.VcnsDriver(self.callbacks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _set_router_type(self, router_id, router_type):
 | 
				
			||||||
 | 
					        self._router_type[router_id] = router_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_router_type(self, context=None, router_id=None, router=None):
 | 
				
			||||||
 | 
					        if not router:
 | 
				
			||||||
 | 
					            if router_id in self._router_type:
 | 
				
			||||||
 | 
					                return self._router_type[router_id]
 | 
				
			||||||
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug(_("EDGE: router = %s"), router)
 | 
				
			||||||
 | 
					        if router['nsx_attributes']['service_router']:
 | 
				
			||||||
 | 
					            router_type = ROUTER_TYPE_ADVANCED
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            router_type = ROUTER_TYPE_BASIC
 | 
				
			||||||
 | 
					        self._set_router_type(router['id'], router_type)
 | 
				
			||||||
 | 
					        return router_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _find_router_type(self, router):
 | 
				
			||||||
 | 
					        is_service_router = router.get(sr.SERVICE_ROUTER, False)
 | 
				
			||||||
 | 
					        if is_service_router:
 | 
				
			||||||
 | 
					            return ROUTER_TYPE_ADVANCED
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return ROUTER_TYPE_BASIC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _is_advanced_service_router(self, context=None, router_id=None,
 | 
				
			||||||
 | 
					                                    router=None):
 | 
				
			||||||
 | 
					        if router:
 | 
				
			||||||
 | 
					            router_type = self._get_router_type(router=router)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            router_type = self._get_router_type(context, router_id)
 | 
				
			||||||
 | 
					        return (router_type == ROUTER_TYPE_ADVANCED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _vcns_create_ext_gw_port(self, context, port_data):
 | 
				
			||||||
 | 
					        router_id = port_data['device_id']
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            self._super_create_ext_gw_port(context, port_data)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # NOP for Edge because currently the port will be create internally
 | 
				
			||||||
 | 
					        # by VSM
 | 
				
			||||||
 | 
					        LOG.debug(_("EDGE: _vcns_create_ext_gw_port"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _vcns_delete_ext_gw_port(self, context, port_data):
 | 
				
			||||||
 | 
					        router_id = port_data['device_id']
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            self._super_delete_ext_gw_port(context, port_data)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # NOP for Edge
 | 
				
			||||||
 | 
					        LOG.debug(_("EDGE: _vcns_delete_ext_gw_port"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_external_attachment_info(self, context, router):
 | 
				
			||||||
 | 
					        gw_port = router.gw_port
 | 
				
			||||||
 | 
					        ipaddress = None
 | 
				
			||||||
 | 
					        netmask = None
 | 
				
			||||||
 | 
					        nexthop = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if gw_port:
 | 
				
			||||||
 | 
					            # gw_port may have multiple IPs, only configure the first one
 | 
				
			||||||
 | 
					            if gw_port.get('fixed_ips'):
 | 
				
			||||||
 | 
					                ipaddress = gw_port['fixed_ips'][0]['ip_address']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_id = gw_port.get('network_id')
 | 
				
			||||||
 | 
					            if network_id:
 | 
				
			||||||
 | 
					                ext_net = self._get_network(context, network_id)
 | 
				
			||||||
 | 
					                if not ext_net.external:
 | 
				
			||||||
 | 
					                    msg = (_("Network '%s' is not a valid external "
 | 
				
			||||||
 | 
					                             "network") % network_id)
 | 
				
			||||||
 | 
					                    raise q_exc.BadRequest(resource='router', msg=msg)
 | 
				
			||||||
 | 
					                if ext_net.subnets:
 | 
				
			||||||
 | 
					                    ext_subnet = ext_net.subnets[0]
 | 
				
			||||||
 | 
					                    netmask = str(netaddr.IPNetwork(ext_subnet.cidr).netmask)
 | 
				
			||||||
 | 
					                    nexthop = ext_subnet.gateway_ip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (ipaddress, netmask, nexthop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_external_gateway_address(self, context, router):
 | 
				
			||||||
 | 
					        ipaddress, netmask, nexthop = self._get_external_attachment_info(
 | 
				
			||||||
 | 
					            context, router)
 | 
				
			||||||
 | 
					        return nexthop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _vcns_update_static_routes(self, context, **kwargs):
 | 
				
			||||||
 | 
					        router = kwargs.get('router')
 | 
				
			||||||
 | 
					        if router is None:
 | 
				
			||||||
 | 
					            router = self._get_router(context, kwargs['router_id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        edge_id = kwargs.get('edge_id')
 | 
				
			||||||
 | 
					        if edge_id is None:
 | 
				
			||||||
 | 
					            binding = vcns_db.get_vcns_router_binding(context.session,
 | 
				
			||||||
 | 
					                                                      router['id'])
 | 
				
			||||||
 | 
					            edge_id = binding['edge_id']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        skippable = True
 | 
				
			||||||
 | 
					        if 'nexthop' in kwargs:
 | 
				
			||||||
 | 
					            nexthop = kwargs['nexthop']
 | 
				
			||||||
 | 
					            # The default gateway and vnic config has dependencies, if we
 | 
				
			||||||
 | 
					            # explicitly specify nexthop to change, tell the driver not to
 | 
				
			||||||
 | 
					            # skip this route update
 | 
				
			||||||
 | 
					            skippable = False
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            nexthop = self._get_external_gateway_address(context,
 | 
				
			||||||
 | 
					                                                         router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'subnets' in kwargs:
 | 
				
			||||||
 | 
					            subnets = kwargs['subnets']
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            subnets = self._find_router_subnets_cidrs(context.elevated(),
 | 
				
			||||||
 | 
					                                                      router['id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        routes = []
 | 
				
			||||||
 | 
					        for subnet in subnets:
 | 
				
			||||||
 | 
					            routes.append({
 | 
				
			||||||
 | 
					                'cidr': subnet,
 | 
				
			||||||
 | 
					                'nexthop': vcns_const.INTEGRATION_LR_IPADDRESS.split('/')[0]
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        self.vcns_driver.update_routes(router['id'], edge_id, nexthop, routes,
 | 
				
			||||||
 | 
					                                       skippable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_nat_rules(self, context, router):
 | 
				
			||||||
 | 
					        fip_qry = context.session.query(l3_db.FloatingIP)
 | 
				
			||||||
 | 
					        fip_db = fip_qry.filter_by(router_id=router['id']).all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dnat = []
 | 
				
			||||||
 | 
					        snat = []
 | 
				
			||||||
 | 
					        for fip in fip_db:
 | 
				
			||||||
 | 
					            if fip.fixed_port_id:
 | 
				
			||||||
 | 
					                dnat.append({
 | 
				
			||||||
 | 
					                    'dst': fip.floating_ip_address,
 | 
				
			||||||
 | 
					                    'translated': fip.fixed_ip_address
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gw_port = router.gw_port
 | 
				
			||||||
 | 
					        if gw_port and router.enable_snat:
 | 
				
			||||||
 | 
					            if gw_port.get('fixed_ips'):
 | 
				
			||||||
 | 
					                snat_ip = gw_port['fixed_ips'][0]['ip_address']
 | 
				
			||||||
 | 
					                subnets = self._find_router_subnets_cidrs(context.elevated(),
 | 
				
			||||||
 | 
					                                                          router['id'])
 | 
				
			||||||
 | 
					                for subnet in subnets:
 | 
				
			||||||
 | 
					                    snat.append({
 | 
				
			||||||
 | 
					                        'src': subnet,
 | 
				
			||||||
 | 
					                        'translated': snat_ip
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (snat, dnat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_nat_rules(self, context, router):
 | 
				
			||||||
 | 
					        snat, dnat = self._get_nat_rules(context, router)
 | 
				
			||||||
 | 
					        binding = vcns_db.get_vcns_router_binding(context.session,
 | 
				
			||||||
 | 
					                                                  router['id'])
 | 
				
			||||||
 | 
					        self.vcns_driver.update_nat_rules(router['id'],
 | 
				
			||||||
 | 
					                                          binding['edge_id'],
 | 
				
			||||||
 | 
					                                          snat, dnat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_interface(self, context, router):
 | 
				
			||||||
 | 
					        addr, mask, nexthop = self._get_external_attachment_info(
 | 
				
			||||||
 | 
					            context, router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        secondary = []
 | 
				
			||||||
 | 
					        fip_qry = context.session.query(l3_db.FloatingIP)
 | 
				
			||||||
 | 
					        fip_db = fip_qry.filter_by(router_id=router['id']).all()
 | 
				
			||||||
 | 
					        for fip in fip_db:
 | 
				
			||||||
 | 
					            if fip.fixed_port_id:
 | 
				
			||||||
 | 
					                secondary.append(fip.floating_ip_address)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding = vcns_db.get_vcns_router_binding(context.session,
 | 
				
			||||||
 | 
					                                                  router['id'])
 | 
				
			||||||
 | 
					        self.vcns_driver.update_interface(
 | 
				
			||||||
 | 
					            router['id'], binding['edge_id'],
 | 
				
			||||||
 | 
					            vcns_const.EXTERNAL_VNIC_INDEX,
 | 
				
			||||||
 | 
					            self.vcns_driver.external_network,
 | 
				
			||||||
 | 
					            addr, mask, secondary=secondary)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_router_gw_info(self, context, router_id, info):
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            super(NvpAdvancedPlugin, self)._update_router_gw_info(
 | 
				
			||||||
 | 
					                context, router_id, info)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # get original gw_port config
 | 
				
			||||||
 | 
					        router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					        org_ext_net_id = router.gw_port_id and router.gw_port.network_id
 | 
				
			||||||
 | 
					        org_enable_snat = router.enable_snat
 | 
				
			||||||
 | 
					        orgaddr, orgmask, orgnexthop = self._get_external_attachment_info(
 | 
				
			||||||
 | 
					            context, router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(NeutronPlugin.NvpPluginV2, self)._update_router_gw_info(
 | 
				
			||||||
 | 
					            context, router_id, info, router=router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new_ext_net_id = router.gw_port_id and router.gw_port.network_id
 | 
				
			||||||
 | 
					        new_enable_snat = router.enable_snat
 | 
				
			||||||
 | 
					        newaddr, newmask, newnexthop = self._get_external_attachment_info(
 | 
				
			||||||
 | 
					            context, router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding = vcns_db.get_vcns_router_binding(context.session, router_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if new_ext_net_id != org_ext_net_id and orgnexthop:
 | 
				
			||||||
 | 
					            # network changed, need to remove default gateway before vnic
 | 
				
			||||||
 | 
					            # can be configured
 | 
				
			||||||
 | 
					            LOG.debug(_("VCNS: delete default gateway %s"), orgnexthop)
 | 
				
			||||||
 | 
					            self._vcns_update_static_routes(context,
 | 
				
			||||||
 | 
					                                            router=router,
 | 
				
			||||||
 | 
					                                            edge_id=binding['edge_id'],
 | 
				
			||||||
 | 
					                                            nexthop=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if orgaddr != newaddr or orgmask != newmask:
 | 
				
			||||||
 | 
					            self.vcns_driver.update_interface(
 | 
				
			||||||
 | 
					                router_id, binding['edge_id'],
 | 
				
			||||||
 | 
					                vcns_const.EXTERNAL_VNIC_INDEX,
 | 
				
			||||||
 | 
					                self.vcns_driver.external_network,
 | 
				
			||||||
 | 
					                newaddr, newmask)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if orgnexthop != newnexthop:
 | 
				
			||||||
 | 
					            self._vcns_update_static_routes(context,
 | 
				
			||||||
 | 
					                                            router=router,
 | 
				
			||||||
 | 
					                                            edge_id=binding['edge_id'],
 | 
				
			||||||
 | 
					                                            nexthop=newnexthop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (new_ext_net_id == org_ext_net_id and
 | 
				
			||||||
 | 
					            org_enable_snat == new_enable_snat):
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._update_nat_rules(context, router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _add_subnet_snat_rule(self, router, subnet):
 | 
				
			||||||
 | 
					        # NOP for service router
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(router=router):
 | 
				
			||||||
 | 
					            super(NvpAdvancedPlugin, self)._add_subnet_snat_rule(
 | 
				
			||||||
 | 
					                router, subnet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _delete_subnet_snat_rule(self, router, subnet):
 | 
				
			||||||
 | 
					        # NOP for service router
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(router=router):
 | 
				
			||||||
 | 
					            super(NvpAdvancedPlugin, self)._delete_subnet_snat_rule(
 | 
				
			||||||
 | 
					                router, subnet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _remove_floatingip_address(self, context, fip_db):
 | 
				
			||||||
 | 
					        # NOP for service router
 | 
				
			||||||
 | 
					        router_id = fip_db.router_id
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            super(NvpAdvancedPlugin, self)._remove_floatingip_address(
 | 
				
			||||||
 | 
					                context, fip_db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _create_advanced_service_router(self, context, name, lrouter, lswitch):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # store binding
 | 
				
			||||||
 | 
					        binding = vcns_db.add_vcns_router_binding(
 | 
				
			||||||
 | 
					            context.session, lrouter['uuid'], None, lswitch['uuid'],
 | 
				
			||||||
 | 
					            service_constants.PENDING_CREATE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # deploy edge
 | 
				
			||||||
 | 
					        jobdata = {
 | 
				
			||||||
 | 
					            'lrouter': lrouter,
 | 
				
			||||||
 | 
					            'lswitch': lswitch,
 | 
				
			||||||
 | 
					            'context': context
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # deploy and wait until the deploy requeste has been requested
 | 
				
			||||||
 | 
					        # so we will have edge_id ready. The wait here should be fine
 | 
				
			||||||
 | 
					        # as we're not in a database transaction now
 | 
				
			||||||
 | 
					        self.vcns_driver.deploy_edge(
 | 
				
			||||||
 | 
					            lrouter['uuid'], name, lswitch['uuid'], jobdata=jobdata,
 | 
				
			||||||
 | 
					            wait_for_exec=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return binding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _create_integration_lswitch(self, tenant_id, name):
 | 
				
			||||||
 | 
					        # use defautl transport zone
 | 
				
			||||||
 | 
					        transport_zone_config = [{
 | 
				
			||||||
 | 
					            "zone_uuid": self.cluster.default_tz_uuid,
 | 
				
			||||||
 | 
					            "transport_type": cfg.CONF.NVP.default_transport_type
 | 
				
			||||||
 | 
					        }]
 | 
				
			||||||
 | 
					        return self.vcns_driver.create_lswitch(name, transport_zone_config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _add_router_integration_interface(self, tenant_id, name,
 | 
				
			||||||
 | 
					                                          lrouter, lswitch):
 | 
				
			||||||
 | 
					        # create logic switch port
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            ls_port = nvplib.create_lport(
 | 
				
			||||||
 | 
					                self.cluster, lswitch['uuid'], tenant_id,
 | 
				
			||||||
 | 
					                '', '', lrouter['uuid'], True)
 | 
				
			||||||
 | 
					        except NvpApiClient.NvpApiException:
 | 
				
			||||||
 | 
					            msg = (_("An exception occured while creating a port "
 | 
				
			||||||
 | 
					                     "on lswitch %s") % lswitch['uuid'])
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            raise q_exc.NeutronException(message=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # create logic router port
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            neutron_port_id = ''
 | 
				
			||||||
 | 
					            pname = name[:36] + '-lp'
 | 
				
			||||||
 | 
					            admin_status_enabled = True
 | 
				
			||||||
 | 
					            lr_port = nvplib.create_router_lport(
 | 
				
			||||||
 | 
					                self.cluster, lrouter['uuid'], tenant_id,
 | 
				
			||||||
 | 
					                neutron_port_id, pname, admin_status_enabled,
 | 
				
			||||||
 | 
					                [vcns_const.INTEGRATION_LR_IPADDRESS])
 | 
				
			||||||
 | 
					        except NvpApiClient.NvpApiException:
 | 
				
			||||||
 | 
					            msg = (_("Unable to create port on NVP logical router %s") % name)
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            nvplib.delete_port(self.cluster, lswitch['uuid'], ls_port['uuid'])
 | 
				
			||||||
 | 
					            raise q_exc.NeutronException(message=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # attach logic router port to switch port
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self._update_router_port_attachment(
 | 
				
			||||||
 | 
					                self.cluster, None, lrouter['uuid'], {}, lr_port['uuid'],
 | 
				
			||||||
 | 
					                'PatchAttachment', ls_port['uuid'], None)
 | 
				
			||||||
 | 
					        except NvpApiClient.NvpApiException as e:
 | 
				
			||||||
 | 
					            # lr_port should have been deleted
 | 
				
			||||||
 | 
					            nvplib.delete_port(self.cluster, lswitch['uuid'], ls_port['uuid'])
 | 
				
			||||||
 | 
					            raise e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _create_lrouter(self, context, router, nexthop):
 | 
				
			||||||
 | 
					        lrouter = super(NvpAdvancedPlugin, self)._create_lrouter(
 | 
				
			||||||
 | 
					            context, router, vcns_const.INTEGRATION_EDGE_IPADDRESS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        router_type = self._find_router_type(router)
 | 
				
			||||||
 | 
					        self._set_router_type(lrouter['uuid'], router_type)
 | 
				
			||||||
 | 
					        if router_type == ROUTER_TYPE_BASIC:
 | 
				
			||||||
 | 
					            return lrouter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tenant_id = self._get_tenant_id_for_create(context, router)
 | 
				
			||||||
 | 
					        name = router['name']
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            lsname = name[:36] + '-ls'
 | 
				
			||||||
 | 
					            lswitch = self._create_integration_lswitch(
 | 
				
			||||||
 | 
					                tenant_id, lsname)
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            msg = _("Unable to create integration logic switch "
 | 
				
			||||||
 | 
					                    "for router %s") % name
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            nvplib.delete_lrouter(self.cluster, lrouter['uuid'])
 | 
				
			||||||
 | 
					            raise q_exc.NeutronException(message=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self._add_router_integration_interface(tenant_id, name,
 | 
				
			||||||
 | 
					                                                   lrouter, lswitch)
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            msg = _("Unable to add router interface to integration lswitch "
 | 
				
			||||||
 | 
					                    "for router %s") % name
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            nvplib.delete_lrouter(self.cluster, lrouter['uuid'])
 | 
				
			||||||
 | 
					            raise q_exc.NeutronException(message=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self._create_advanced_service_router(
 | 
				
			||||||
 | 
					                context, name, lrouter, lswitch)
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            msg = (_("Unable to create advance service router for %s") % name)
 | 
				
			||||||
 | 
					            LOG.exception(msg)
 | 
				
			||||||
 | 
					            self.vcns_driver.delete_lswitch(lswitch('uuid'))
 | 
				
			||||||
 | 
					            nvplib.delete_lrouter(self.cluster, lrouter['uuid'])
 | 
				
			||||||
 | 
					            raise q_exc.NeutronException(message=msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lrouter['status'] = service_constants.PENDING_CREATE
 | 
				
			||||||
 | 
					        return lrouter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _delete_lrouter(self, context, id):
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(context, id):
 | 
				
			||||||
 | 
					            super(NvpAdvancedPlugin, self)._delete_lrouter(context, id)
 | 
				
			||||||
 | 
					            if id in self._router_type:
 | 
				
			||||||
 | 
					                del self._router_type[id]
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding = vcns_db.get_vcns_router_binding(context.session, id)
 | 
				
			||||||
 | 
					        if binding:
 | 
				
			||||||
 | 
					            vcns_db.update_vcns_router_binding(
 | 
				
			||||||
 | 
					                context.session, id, status=service_constants.PENDING_DELETE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            lswitch_id = binding['lswitch_id']
 | 
				
			||||||
 | 
					            edge_id = binding['edge_id']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # delete lswitch
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                self.vcns_driver.delete_lswitch(lswitch_id)
 | 
				
			||||||
 | 
					            except exceptions.ResourceNotFound:
 | 
				
			||||||
 | 
					                LOG.warning(_("Did not found lswitch %s in NVP"), lswitch_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # delete edge
 | 
				
			||||||
 | 
					            jobdata = {
 | 
				
			||||||
 | 
					                'context': context
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            self.vcns_driver.delete_edge(id, edge_id, jobdata=jobdata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # delete LR
 | 
				
			||||||
 | 
					        nvplib.delete_lrouter(self.cluster, id)
 | 
				
			||||||
 | 
					        if id in self._router_type:
 | 
				
			||||||
 | 
					            del self._router_type[id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_lrouter(self, context, router_id, name, nexthop, routes=None):
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            return super(NvpAdvancedPlugin, self)._update_lrouter(
 | 
				
			||||||
 | 
					                context, router_id, name, nexthop, routes=routes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        previous_routes = super(NvpAdvancedPlugin, self)._update_lrouter(
 | 
				
			||||||
 | 
					            context, router_id, name,
 | 
				
			||||||
 | 
					            vcns_const.INTEGRATION_EDGE_IPADDRESS, routes=routes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(fank): Theoretically users can specify extra routes for
 | 
				
			||||||
 | 
					        # physical network, and routes for phyiscal network needs to be
 | 
				
			||||||
 | 
					        # configured on Edge. This can be done by checking if nexthop is in
 | 
				
			||||||
 | 
					        # external network. But for now we only handle routes for logic
 | 
				
			||||||
 | 
					        # space and leave it for future enhancement.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Let _update_router_gw_info handle nexthop change
 | 
				
			||||||
 | 
					        #self._vcns_update_static_routes(context, router_id=router_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return previous_routes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _retrieve_and_delete_nat_rules(self, context, floating_ip_address,
 | 
				
			||||||
 | 
					                                       internal_ip, router_id,
 | 
				
			||||||
 | 
					                                       min_num_rules_expected=0):
 | 
				
			||||||
 | 
					        # NOP for advanced service router
 | 
				
			||||||
 | 
					        if not self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            super(NvpAdvancedPlugin, self)._retrieve_and_delete_nat_rules(
 | 
				
			||||||
 | 
					                context, floating_ip_address, internal_ip, router_id,
 | 
				
			||||||
 | 
					                min_num_rules_expected=min_num_rules_expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
 | 
				
			||||||
 | 
					        # Update DB model only  for advanced service router
 | 
				
			||||||
 | 
					        router_id = self._get_fip_assoc_data(context, fip, floatingip_db)[2]
 | 
				
			||||||
 | 
					        if (router_id and
 | 
				
			||||||
 | 
					            not self._is_advanced_service_router(context, router_id)):
 | 
				
			||||||
 | 
					            super(NvpAdvancedPlugin, self)._update_fip_assoc(
 | 
				
			||||||
 | 
					                context, fip, floatingip_db, external_port)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            super(NeutronPlugin.NvpPluginV2, self)._update_fip_assoc(
 | 
				
			||||||
 | 
					                context, fip, floatingip_db, external_port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_nvp_lrouter_status(self, id):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            lrouter = nvplib.get_lrouter(self.cluster, id)
 | 
				
			||||||
 | 
					            lr_status = lrouter["_relations"]["LogicalRouterStatus"]
 | 
				
			||||||
 | 
					            if lr_status["fabric_status"]:
 | 
				
			||||||
 | 
					                nvp_status = RouterStatus.ROUTER_STATUS_ACTIVE
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                nvp_status = RouterStatus.ROUTER_STATUS_DOWN
 | 
				
			||||||
 | 
					        except q_exc.NotFound:
 | 
				
			||||||
 | 
					            nvp_status = RouterStatus.ROUTER_STATUS_ERROR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return nvp_status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_vse_status(self, context, id):
 | 
				
			||||||
 | 
					        binding = vcns_db.get_vcns_router_binding(context.session, id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        edge_status_level = self.vcns_driver.get_edge_status(
 | 
				
			||||||
 | 
					            binding['edge_id'])
 | 
				
			||||||
 | 
					        edge_db_status_level = ROUTER_STATUS_LEVEL[binding.status]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if edge_status_level > edge_db_status_level:
 | 
				
			||||||
 | 
					            return edge_status_level
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return edge_db_status_level
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_all_nvp_lrouters_statuses(self, tenant_id, fields):
 | 
				
			||||||
 | 
					        # get nvp lrouters status
 | 
				
			||||||
 | 
					        nvp_lrouters = nvplib.get_lrouters(self.cluster,
 | 
				
			||||||
 | 
					                                           tenant_id,
 | 
				
			||||||
 | 
					                                           fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nvp_status = {}
 | 
				
			||||||
 | 
					        for nvp_lrouter in nvp_lrouters:
 | 
				
			||||||
 | 
					            if (nvp_lrouter["_relations"]["LogicalRouterStatus"]
 | 
				
			||||||
 | 
					                ["fabric_status"]):
 | 
				
			||||||
 | 
					                nvp_status[nvp_lrouter['uuid']] = (
 | 
				
			||||||
 | 
					                    RouterStatus.ROUTER_STATUS_ACTIVE
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                nvp_status[nvp_lrouter['uuid']] = (
 | 
				
			||||||
 | 
					                    RouterStatus.ROUTER_STATUS_DOWN
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return nvp_status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_all_vse_statuses(self, context):
 | 
				
			||||||
 | 
					        bindings = self._model_query(
 | 
				
			||||||
 | 
					            context, vcns_models.VcnsRouterBinding)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        vse_db_status_level = {}
 | 
				
			||||||
 | 
					        edge_id_to_router_id = {}
 | 
				
			||||||
 | 
					        router_ids = []
 | 
				
			||||||
 | 
					        for binding in bindings:
 | 
				
			||||||
 | 
					            if not binding['edge_id']:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            router_id = binding['router_id']
 | 
				
			||||||
 | 
					            router_ids.append(router_id)
 | 
				
			||||||
 | 
					            edge_id_to_router_id[binding['edge_id']] = router_id
 | 
				
			||||||
 | 
					            vse_db_status_level[router_id] = (
 | 
				
			||||||
 | 
					                ROUTER_STATUS_LEVEL[binding['status']])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not vse_db_status_level:
 | 
				
			||||||
 | 
					            # no advanced service router, no need to query
 | 
				
			||||||
 | 
					            return {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        vse_status_level = {}
 | 
				
			||||||
 | 
					        edges_status_level = self.vcns_driver.get_edges_statuses()
 | 
				
			||||||
 | 
					        for edge_id, status_level in edges_status_level.iteritems():
 | 
				
			||||||
 | 
					            if edge_id in edge_id_to_router_id:
 | 
				
			||||||
 | 
					                router_id = edge_id_to_router_id[edge_id]
 | 
				
			||||||
 | 
					                db_status_level = vse_db_status_level[router_id]
 | 
				
			||||||
 | 
					                if status_level > db_status_level:
 | 
				
			||||||
 | 
					                    vse_status_level[router_id] = status_level
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    vse_status_level[router_id] = db_status_level
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return vse_status_level
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_router(self, context, id, fields=None):
 | 
				
			||||||
 | 
					        if fields and 'status' not in fields:
 | 
				
			||||||
 | 
					            return super(NvpAdvancedPlugin, self).get_router(
 | 
				
			||||||
 | 
					                context, id, fields=fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        router = super(NvpAdvancedPlugin, self).get_router(context, id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        router_type = self._find_router_type(router)
 | 
				
			||||||
 | 
					        if router_type == ROUTER_TYPE_ADVANCED:
 | 
				
			||||||
 | 
					            vse_status_level = self._get_vse_status(context, id)
 | 
				
			||||||
 | 
					            if vse_status_level > ROUTER_STATUS_LEVEL[router['status']]:
 | 
				
			||||||
 | 
					                router['status'] = ROUTER_STATUS[vse_status_level]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self._fields(router, fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_routers(self, context, filters=None, fields=None, **kwargs):
 | 
				
			||||||
 | 
					        routers = super(NvpAdvancedPlugin, self).get_routers(
 | 
				
			||||||
 | 
					            context, filters=filters, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if fields and 'status' not in fields:
 | 
				
			||||||
 | 
					            # no status checking, just return regular get_routers
 | 
				
			||||||
 | 
					            return [self._fields(router, fields) for router in routers]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for router in routers:
 | 
				
			||||||
 | 
					            router_type = self._find_router_type(router)
 | 
				
			||||||
 | 
					            if router_type == ROUTER_TYPE_ADVANCED:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # no advanced service router, return here
 | 
				
			||||||
 | 
					            return [self._fields(router, fields) for router in routers]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        vse_status_all = self._get_all_vse_statuses(context)
 | 
				
			||||||
 | 
					        for router in routers:
 | 
				
			||||||
 | 
					            router_type = self._find_router_type(router)
 | 
				
			||||||
 | 
					            if router_type == ROUTER_TYPE_ADVANCED:
 | 
				
			||||||
 | 
					                vse_status_level = vse_status_all.get(router['id'])
 | 
				
			||||||
 | 
					                if vse_status_level is None:
 | 
				
			||||||
 | 
					                    vse_status_level = RouterStatus.ROUTER_STATUS_ERROR
 | 
				
			||||||
 | 
					                if vse_status_level > ROUTER_STATUS_LEVEL[router['status']]:
 | 
				
			||||||
 | 
					                    router['status'] = ROUTER_STATUS[vse_status_level]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [self._fields(router, fields) for router in routers]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_router_interface(self, context, router_id, interface_info):
 | 
				
			||||||
 | 
					        info = super(NvpAdvancedPlugin, self).add_router_interface(
 | 
				
			||||||
 | 
					            context, router_id, interface_info)
 | 
				
			||||||
 | 
					        if self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					            if router.enable_snat:
 | 
				
			||||||
 | 
					                self._update_nat_rules(context, router)
 | 
				
			||||||
 | 
					            # TODO(fank): do rollback if error, or have a dedicated thread
 | 
				
			||||||
 | 
					            # do sync work (rollback, re-configure, or make router down)
 | 
				
			||||||
 | 
					            self._vcns_update_static_routes(context, router=router)
 | 
				
			||||||
 | 
					        return info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def remove_router_interface(self, context, router_id, interface_info):
 | 
				
			||||||
 | 
					        info = super(NvpAdvancedPlugin, self).remove_router_interface(
 | 
				
			||||||
 | 
					            context, router_id, interface_info)
 | 
				
			||||||
 | 
					        if self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					            if router.enable_snat:
 | 
				
			||||||
 | 
					                self._update_nat_rules(context, router)
 | 
				
			||||||
 | 
					            # TODO(fank): do rollback if error, or have a dedicated thread
 | 
				
			||||||
 | 
					            # do sync work (rollback, re-configure, or make router down)
 | 
				
			||||||
 | 
					            self._vcns_update_static_routes(context, router=router)
 | 
				
			||||||
 | 
					        return info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_floatingip(self, context, floatingip):
 | 
				
			||||||
 | 
					        fip = super(NvpAdvancedPlugin, self).create_floatingip(
 | 
				
			||||||
 | 
					            context, floatingip)
 | 
				
			||||||
 | 
					        router_id = fip.get('router_id')
 | 
				
			||||||
 | 
					        if router_id and self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					            # TODO(fank): do rollback if error, or have a dedicated thread
 | 
				
			||||||
 | 
					            # do sync work (rollback, re-configure, or make router down)
 | 
				
			||||||
 | 
					            self._update_interface(context, router)
 | 
				
			||||||
 | 
					            self._update_nat_rules(context, router)
 | 
				
			||||||
 | 
					        return fip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_floatingip(self, context, id, floatingip):
 | 
				
			||||||
 | 
					        fip = super(NvpAdvancedPlugin, self).update_floatingip(
 | 
				
			||||||
 | 
					            context, id, floatingip)
 | 
				
			||||||
 | 
					        router_id = fip.get('router_id')
 | 
				
			||||||
 | 
					        if router_id and self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					            # TODO(fank): do rollback if error, or have a dedicated thread
 | 
				
			||||||
 | 
					            # do sync work (rollback, re-configure, or make router down)
 | 
				
			||||||
 | 
					            self._update_interface(context, router)
 | 
				
			||||||
 | 
					            self._update_nat_rules(context, router)
 | 
				
			||||||
 | 
					        return fip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete_floatingip(self, context, id):
 | 
				
			||||||
 | 
					        fip_db = self._get_floatingip(context, id)
 | 
				
			||||||
 | 
					        router_id = None
 | 
				
			||||||
 | 
					        if fip_db.fixed_port_id:
 | 
				
			||||||
 | 
					            router_id = fip_db.router_id
 | 
				
			||||||
 | 
					        super(NvpAdvancedPlugin, self).delete_floatingip(context, id)
 | 
				
			||||||
 | 
					        if router_id and self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					            # TODO(fank): do rollback if error, or have a dedicated thread
 | 
				
			||||||
 | 
					            # do sync work (rollback, re-configure, or make router down)
 | 
				
			||||||
 | 
					            self._update_interface(context, router)
 | 
				
			||||||
 | 
					            self._update_nat_rules(context, router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def disassociate_floatingips(self, context, port_id):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            fip_qry = context.session.query(l3_db.FloatingIP)
 | 
				
			||||||
 | 
					            fip_db = fip_qry.filter_by(fixed_port_id=port_id).one()
 | 
				
			||||||
 | 
					            router_id = fip_db.router_id
 | 
				
			||||||
 | 
					        except sa_exc.NoResultFound:
 | 
				
			||||||
 | 
					            router_id = None
 | 
				
			||||||
 | 
					        super(NvpAdvancedPlugin, self).disassociate_floatingips(context,
 | 
				
			||||||
 | 
					                                                                port_id)
 | 
				
			||||||
 | 
					        if router_id and self._is_advanced_service_router(context, router_id):
 | 
				
			||||||
 | 
					            router = self._get_router(context, router_id)
 | 
				
			||||||
 | 
					            # TODO(fank): do rollback if error, or have a dedicated thread
 | 
				
			||||||
 | 
					            # do sync work (rollback, re-configure, or make router down)
 | 
				
			||||||
 | 
					            self._update_interface(context, router)
 | 
				
			||||||
 | 
					            self._update_nat_rules(context, router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VcnsCallbacks(object):
 | 
				
			||||||
 | 
					    """Edge callback implementation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Callback functions for asynchronous tasks
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self, plugin):
 | 
				
			||||||
 | 
					        self.plugin = plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def edge_deploy_started(self, task):
 | 
				
			||||||
 | 
					        """callback when deployment task started."""
 | 
				
			||||||
 | 
					        jobdata = task.userdata['jobdata']
 | 
				
			||||||
 | 
					        lrouter = jobdata['lrouter']
 | 
				
			||||||
 | 
					        context = jobdata['context']
 | 
				
			||||||
 | 
					        edge_id = task.userdata.get('edge_id')
 | 
				
			||||||
 | 
					        name = task.userdata['router_name']
 | 
				
			||||||
 | 
					        if edge_id:
 | 
				
			||||||
 | 
					            LOG.debug(_("Start deploying %(edge_id)s for router %(name)s"), {
 | 
				
			||||||
 | 
					                'edge_id': edge_id,
 | 
				
			||||||
 | 
					                'name': name})
 | 
				
			||||||
 | 
					            vcns_db.update_vcns_router_binding(
 | 
				
			||||||
 | 
					                context.session, lrouter['uuid'], edge_id=edge_id)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					                LOG.debug(_("Failed to deploy Edge for router %s"), name)
 | 
				
			||||||
 | 
					                vcns_db.update_vcns_router_binding(
 | 
				
			||||||
 | 
					                    context.session, lrouter['uuid'],
 | 
				
			||||||
 | 
					                    status=service_constants.ERROR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def edge_deploy_result(self, task):
 | 
				
			||||||
 | 
					        """callback when deployment task finished."""
 | 
				
			||||||
 | 
					        jobdata = task.userdata['jobdata']
 | 
				
			||||||
 | 
					        lrouter = jobdata['lrouter']
 | 
				
			||||||
 | 
					        context = jobdata['context']
 | 
				
			||||||
 | 
					        name = task.userdata['router_name']
 | 
				
			||||||
 | 
					        router_db = self.plugin._get_router(context, lrouter['uuid'])
 | 
				
			||||||
 | 
					        if task.status == TaskStatus.COMPLETED:
 | 
				
			||||||
 | 
					            LOG.debug(_("Successfully deployed %(edge_id)s for "
 | 
				
			||||||
 | 
					                        "router %(name)s"), {
 | 
				
			||||||
 | 
					                            'edge_id': task.userdata['edge_id'],
 | 
				
			||||||
 | 
					                            'name': name})
 | 
				
			||||||
 | 
					            if router_db['status'] == service_constants.PENDING_CREATE:
 | 
				
			||||||
 | 
					                router_db['status'] = service_constants.ACTIVE
 | 
				
			||||||
 | 
					                binding = vcns_db.get_vcns_router_binding(
 | 
				
			||||||
 | 
					                    context.session, lrouter['uuid'])
 | 
				
			||||||
 | 
					                # only update status to active if its status is pending create
 | 
				
			||||||
 | 
					                if binding['status'] == service_constants.PENDING_CREATE:
 | 
				
			||||||
 | 
					                    vcns_db.update_vcns_router_binding(
 | 
				
			||||||
 | 
					                        context.session, lrouter['uuid'],
 | 
				
			||||||
 | 
					                        status=service_constants.ACTIVE)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            LOG.debug(_("Failed to deploy Edge for router %s"), name)
 | 
				
			||||||
 | 
					            router_db['status'] = service_constants.ERROR
 | 
				
			||||||
 | 
					            vcns_db.update_vcns_router_binding(
 | 
				
			||||||
 | 
					                context.session, lrouter['uuid'],
 | 
				
			||||||
 | 
					                status=service_constants.ERROR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def edge_delete_result(self, task):
 | 
				
			||||||
 | 
					        jobdata = task.userdata['jobdata']
 | 
				
			||||||
 | 
					        router_id = task.userdata['router_id']
 | 
				
			||||||
 | 
					        context = jobdata['context']
 | 
				
			||||||
 | 
					        if task.status == TaskStatus.COMPLETED:
 | 
				
			||||||
 | 
					            vcns_db.delete_vcns_router_binding(context.session,
 | 
				
			||||||
 | 
					                                               router_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def interface_update_result(self, task):
 | 
				
			||||||
 | 
					        LOG.debug(_("interface_update_result %d"), task.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def snat_create_result(self, task):
 | 
				
			||||||
 | 
					        LOG.debug(_("snat_create_result %d"), task.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def snat_delete_result(self, task):
 | 
				
			||||||
 | 
					        LOG.debug(_("snat_delete_result %d"), task.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dnat_create_result(self, task):
 | 
				
			||||||
 | 
					        LOG.debug(_("dnat_create_result %d"), task.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dnat_delete_result(self, task):
 | 
				
			||||||
 | 
					        LOG.debug(_("dnat_delete_result %d"), task.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def routes_update_result(self, task):
 | 
				
			||||||
 | 
					        LOG.debug(_("routes_update_result %d"), task.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def nat_update_result(self, task):
 | 
				
			||||||
 | 
					        LOG.debug(_("nat_update_result %d"), task.status)
 | 
				
			||||||
@@ -17,53 +17,15 @@
 | 
				
			|||||||
# @author: Salvatore Orlando, Nicira, Inc
 | 
					# @author: Salvatore Orlando, Nicira, Inc
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from neutron.db import db_base_plugin_v2
 | 
					from neutron.plugins.nicira.dbexts import nsxrouter
 | 
				
			||||||
from neutron.extensions import l3
 | 
					 | 
				
			||||||
from neutron.openstack.common import log as logging
 | 
					 | 
				
			||||||
from neutron.plugins.nicira.dbexts import nicira_models
 | 
					 | 
				
			||||||
from neutron.plugins.nicira.extensions import distributedrouter as dist_rtr
 | 
					from neutron.plugins.nicira.extensions import distributedrouter as dist_rtr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOG = logging.getLogger(__name__)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DistributedRouter_mixin(nsxrouter.NsxRouterMixin):
 | 
				
			||||||
class DistributedRouter_mixin(object):
 | 
					 | 
				
			||||||
    """Mixin class to enable distributed router support."""
 | 
					    """Mixin class to enable distributed router support."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _extend_router_dict_distributed(self, router_res, router_db):
 | 
					    nsx_attributes = (
 | 
				
			||||||
        # Avoid setting attribute to None for routers already existing before
 | 
					        nsxrouter.NsxRouterMixin.nsx_attributes + [{
 | 
				
			||||||
        # the data model was extended with the distributed attribute
 | 
					            'name': dist_rtr.DISTRIBUTED,
 | 
				
			||||||
        nsx_attrs = router_db['nsx_attributes']
 | 
					            'default': False
 | 
				
			||||||
        # Return False if nsx attributes are not definied for this
 | 
					        }])
 | 
				
			||||||
        # neutron router
 | 
					 | 
				
			||||||
        router_res[dist_rtr.DISTRIBUTED] = (
 | 
					 | 
				
			||||||
            nsx_attrs and nsx_attrs['distributed'] or False)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _process_distributed_router_create(
 | 
					 | 
				
			||||||
        self, context, router_db, router_req):
 | 
					 | 
				
			||||||
        """Ensures persistency for the 'distributed' attribute.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Either creates or fetches the nicira extended attributes
 | 
					 | 
				
			||||||
        record for this router and stores the 'distributed'
 | 
					 | 
				
			||||||
        attribute value.
 | 
					 | 
				
			||||||
        This method should be called from within a transaction, as
 | 
					 | 
				
			||||||
        it does not start a new one.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if not router_db['nsx_attributes']:
 | 
					 | 
				
			||||||
            nsx_attributes = nicira_models.NSXRouterExtAttributes(
 | 
					 | 
				
			||||||
                router_id=router_db['id'],
 | 
					 | 
				
			||||||
                distributed=router_req['distributed'])
 | 
					 | 
				
			||||||
            context.session.add(nsx_attributes)
 | 
					 | 
				
			||||||
            router_db['nsx_attributes'] = nsx_attributes
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # The situation where the record already exists will
 | 
					 | 
				
			||||||
            # be likely once the NSXRouterExtAttributes model
 | 
					 | 
				
			||||||
            # will allow for defining several attributes pertaining
 | 
					 | 
				
			||||||
            # to different extensions
 | 
					 | 
				
			||||||
            router_db['nsx_attributes']['distributed'] = (
 | 
					 | 
				
			||||||
                router_req['distributed'])
 | 
					 | 
				
			||||||
        LOG.debug(_("Distributed router extension successfully processed "
 | 
					 | 
				
			||||||
                    "for router:%s"), router_db['id'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Register dict extend functions for ports
 | 
					 | 
				
			||||||
    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
 | 
					 | 
				
			||||||
        l3.ROUTERS, ['_extend_router_dict_distributed'])
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -89,6 +89,7 @@ class NSXRouterExtAttributes(model_base.BASEV2):
 | 
				
			|||||||
                       ForeignKey('routers.id', ondelete="CASCADE"),
 | 
					                       ForeignKey('routers.id', ondelete="CASCADE"),
 | 
				
			||||||
                       primary_key=True)
 | 
					                       primary_key=True)
 | 
				
			||||||
    distributed = Column(Boolean, default=False, nullable=False)
 | 
					    distributed = Column(Boolean, default=False, nullable=False)
 | 
				
			||||||
 | 
					    service_router = Column(Boolean, default=False, nullable=False)
 | 
				
			||||||
    # Add a relationship to the Router model in order to instruct
 | 
					    # Add a relationship to the Router model in order to instruct
 | 
				
			||||||
    # SQLAlchemy to eagerly load this association
 | 
					    # SQLAlchemy to eagerly load this association
 | 
				
			||||||
    router = orm.relationship(
 | 
					    router = orm.relationship(
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										70
									
								
								neutron/plugins/nicira/dbexts/nsxrouter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								neutron/plugins/nicira/dbexts/nsxrouter.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright 2013 Nicira Networks, Inc.  All rights reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    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.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @author: Salvatore Orlando, Nicira, Inc
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron.db import db_base_plugin_v2
 | 
				
			||||||
 | 
					from neutron.extensions import l3
 | 
				
			||||||
 | 
					from neutron.openstack.common import log as logging
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.dbexts import nicira_models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NsxRouterMixin(object):
 | 
				
			||||||
 | 
					    """Mixin class to enable nsx router support."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nsx_attributes = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _extend_nsx_router_dict(self, router_res, router_db):
 | 
				
			||||||
 | 
					        nsx_attrs = router_db['nsx_attributes']
 | 
				
			||||||
 | 
					        # Return False if nsx attributes are not definied for this
 | 
				
			||||||
 | 
					        # neutron router
 | 
				
			||||||
 | 
					        for attr in self.nsx_attributes:
 | 
				
			||||||
 | 
					            name = attr['name']
 | 
				
			||||||
 | 
					            default = attr['default']
 | 
				
			||||||
 | 
					            router_res[name] = (
 | 
				
			||||||
 | 
					                nsx_attrs and nsx_attrs[name] or default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _process_nsx_router_create(
 | 
				
			||||||
 | 
					        self, context, router_db, router_req):
 | 
				
			||||||
 | 
					        if not router_db['nsx_attributes']:
 | 
				
			||||||
 | 
					            kwargs = {}
 | 
				
			||||||
 | 
					            for attr in self.nsx_attributes:
 | 
				
			||||||
 | 
					                name = attr['name']
 | 
				
			||||||
 | 
					                default = attr['default']
 | 
				
			||||||
 | 
					                kwargs[name] = router_req.get(name, default)
 | 
				
			||||||
 | 
					            nsx_attributes = nicira_models.NSXRouterExtAttributes(
 | 
				
			||||||
 | 
					                router_id=router_db['id'], **kwargs)
 | 
				
			||||||
 | 
					            context.session.add(nsx_attributes)
 | 
				
			||||||
 | 
					            router_db['nsx_attributes'] = nsx_attributes
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # The situation where the record already exists will
 | 
				
			||||||
 | 
					            # be likely once the NSXRouterExtAttributes model
 | 
				
			||||||
 | 
					            # will allow for defining several attributes pertaining
 | 
				
			||||||
 | 
					            # to different extensions
 | 
				
			||||||
 | 
					            for attr in self.nsx_attributes:
 | 
				
			||||||
 | 
					                name = attr['name']
 | 
				
			||||||
 | 
					                default = attr['default']
 | 
				
			||||||
 | 
					                router_db['nsx_attributes'][name] = router_req.get(
 | 
				
			||||||
 | 
					                    name, default)
 | 
				
			||||||
 | 
					        LOG.debug(_("Nsx router extension successfully processed "
 | 
				
			||||||
 | 
					                    "for router:%s"), router_db['id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Register dict extend functions for ports
 | 
				
			||||||
 | 
					    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
 | 
				
			||||||
 | 
					        l3.ROUTERS, ['_extend_nsx_router_dict'])
 | 
				
			||||||
							
								
								
									
										29
									
								
								neutron/plugins/nicira/dbexts/servicerouter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								neutron/plugins/nicira/dbexts/servicerouter.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright 2013 VMware, Inc.  All rights reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    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.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.dbexts import distributedrouter as dist_rtr
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.extensions import servicerouter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceRouter_mixin(dist_rtr.DistributedRouter_mixin):
 | 
				
			||||||
 | 
					    """Mixin class to enable service router support."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nsx_attributes = (
 | 
				
			||||||
 | 
					        dist_rtr.DistributedRouter_mixin.nsx_attributes + [{
 | 
				
			||||||
 | 
					            'name': servicerouter.SERVICE_ROUTER,
 | 
				
			||||||
 | 
					            'default': False
 | 
				
			||||||
 | 
					        }])
 | 
				
			||||||
							
								
								
									
										50
									
								
								neutron/plugins/nicira/dbexts/vcns_db.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								neutron/plugins/nicira/dbexts/vcns_db.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright 2013 Nicira, Inc.
 | 
				
			||||||
 | 
					# All Rights Reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron.plugins.nicira.dbexts import vcns_models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def add_vcns_router_binding(session, router_id, vse_id, lswitch_id, status):
 | 
				
			||||||
 | 
					    with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					        binding = vcns_models.VcnsRouterBinding(
 | 
				
			||||||
 | 
					            router_id=router_id,
 | 
				
			||||||
 | 
					            edge_id=vse_id,
 | 
				
			||||||
 | 
					            lswitch_id=lswitch_id,
 | 
				
			||||||
 | 
					            status=status)
 | 
				
			||||||
 | 
					        session.add(binding)
 | 
				
			||||||
 | 
					        return binding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_vcns_router_binding(session, router_id):
 | 
				
			||||||
 | 
					    with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					        return (session.query(vcns_models.VcnsRouterBinding).
 | 
				
			||||||
 | 
					                filter_by(router_id=router_id).first())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def update_vcns_router_binding(session, router_id, **kwargs):
 | 
				
			||||||
 | 
					    with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					        binding = (session.query(vcns_models.VcnsRouterBinding).
 | 
				
			||||||
 | 
					                   filter_by(router_id=router_id).one())
 | 
				
			||||||
 | 
					        for key, value in kwargs.iteritems():
 | 
				
			||||||
 | 
					            binding[key] = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def delete_vcns_router_binding(session, router_id):
 | 
				
			||||||
 | 
					    with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					        binding = (session.query(vcns_models.VcnsRouterBinding).
 | 
				
			||||||
 | 
					                   filter_by(router_id=router_id).one())
 | 
				
			||||||
 | 
					        session.delete(binding)
 | 
				
			||||||
							
								
								
									
										38
									
								
								neutron/plugins/nicira/dbexts/vcns_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								neutron/plugins/nicira/dbexts/vcns_models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright 2013 Nicira, Inc.
 | 
				
			||||||
 | 
					# All Rights Reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sqlalchemy as sa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron.db import model_base
 | 
				
			||||||
 | 
					from neutron.db import models_v2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VcnsRouterBinding(model_base.BASEV2, models_v2.HasStatusDescription):
 | 
				
			||||||
 | 
					    """Represents the mapping between neutron router and vShield Edge."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __tablename__ = 'vcns_router_bindings'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # no ForeignKey to routers.id because for now, a router can be removed
 | 
				
			||||||
 | 
					    # from routers when delete_router is executed, but the binding is only
 | 
				
			||||||
 | 
					    # removed after the Edge is deleted
 | 
				
			||||||
 | 
					    router_id = sa.Column(sa.String(36),
 | 
				
			||||||
 | 
					                          primary_key=True)
 | 
				
			||||||
 | 
					    edge_id = sa.Column(sa.String(16),
 | 
				
			||||||
 | 
					                        nullable=True)
 | 
				
			||||||
 | 
					    lswitch_id = sa.Column(sa.String(36),
 | 
				
			||||||
 | 
					                           nullable=False)
 | 
				
			||||||
							
								
								
									
										61
									
								
								neutron/plugins/nicira/extensions/servicerouter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								neutron/plugins/nicira/extensions/servicerouter.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright 2013 VMware, Inc.  All rights reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    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.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @author: Kaiwei Fan, VMware, Inc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron.api import extensions
 | 
				
			||||||
 | 
					from neutron.api.v2 import attributes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SERVICE_ROUTER = 'service_router'
 | 
				
			||||||
 | 
					EXTENDED_ATTRIBUTES_2_0 = {
 | 
				
			||||||
 | 
					    'routers': {
 | 
				
			||||||
 | 
					        SERVICE_ROUTER: {'allow_post': True, 'allow_put': False,
 | 
				
			||||||
 | 
					                         'convert_to': attributes.convert_to_boolean,
 | 
				
			||||||
 | 
					                         'default': False, 'is_visible': True},
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Servicerouter(extensions.ExtensionDescriptor):
 | 
				
			||||||
 | 
					    """Extension class supporting advanced service router."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_name(cls):
 | 
				
			||||||
 | 
					        return "Service Router"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_alias(cls):
 | 
				
			||||||
 | 
					        return "service-router"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_description(cls):
 | 
				
			||||||
 | 
					        return "Provides service router"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_namespace(cls):
 | 
				
			||||||
 | 
					        return ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_updated(cls):
 | 
				
			||||||
 | 
					        return "2013-08-08T00:00:00-00:00"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_extended_resources(self, version):
 | 
				
			||||||
 | 
					        if version == "2.0":
 | 
				
			||||||
 | 
					            return EXTENDED_ATTRIBUTES_2_0
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return {}
 | 
				
			||||||
@@ -506,6 +506,7 @@ class EdgeApplianceDriver(object):
 | 
				
			|||||||
        LOG.debug(_("VCNS: start updating nat rules: %s"), rules)
 | 
					        LOG.debug(_("VCNS: start updating nat rules: %s"), rules)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        nat = {
 | 
					        nat = {
 | 
				
			||||||
 | 
					            'featureType': 'nat',
 | 
				
			||||||
            'rules': {
 | 
					            'rules': {
 | 
				
			||||||
                'natRulesDtos': rules
 | 
					                'natRulesDtos': rules
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -565,20 +566,18 @@ class EdgeApplianceDriver(object):
 | 
				
			|||||||
        static_routes = []
 | 
					        static_routes = []
 | 
				
			||||||
        for route in routes:
 | 
					        for route in routes:
 | 
				
			||||||
            static_routes.append({
 | 
					            static_routes.append({
 | 
				
			||||||
                "route": {
 | 
					 | 
				
			||||||
                "description": "",
 | 
					                "description": "",
 | 
				
			||||||
                "vnic": vcns_const.INTERNAL_VNIC_INDEX,
 | 
					                "vnic": vcns_const.INTERNAL_VNIC_INDEX,
 | 
				
			||||||
                "network": route['cidr'],
 | 
					                "network": route['cidr'],
 | 
				
			||||||
                "nextHop": route['nexthop']
 | 
					                "nextHop": route['nexthop']
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        request = {
 | 
					        request = {
 | 
				
			||||||
            "staticRouting": {
 | 
					            "staticRoutes": {
 | 
				
			||||||
                "staticRoutes": static_routes,
 | 
					                "staticRoutes": static_routes
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if gateway:
 | 
					        if gateway:
 | 
				
			||||||
            request["staticRouting"]["defaultRoute"] = {
 | 
					            request["defaultRoute"] = {
 | 
				
			||||||
                "description": "default-gateway",
 | 
					                "description": "default-gateway",
 | 
				
			||||||
                "gatewayAddress": gateway,
 | 
					                "gatewayAddress": gateway,
 | 
				
			||||||
                "vnic": vcns_const.EXTERNAL_VNIC_INDEX
 | 
					                "vnic": vcns_const.EXTERNAL_VNIC_INDEX
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,9 +37,6 @@ class Vcns(object):
 | 
				
			|||||||
        self.password = password
 | 
					        self.password = password
 | 
				
			||||||
        self.jsonapi_client = VcnsApiClient.VcnsApiHelper(address, user,
 | 
					        self.jsonapi_client = VcnsApiClient.VcnsApiHelper(address, user,
 | 
				
			||||||
                                                          password, 'json')
 | 
					                                                          password, 'json')
 | 
				
			||||||
        # TODO(fank): remove this after json syntax is fixed on VSM
 | 
					 | 
				
			||||||
        self.xmlapi_client = VcnsApiClient.VcnsApiHelper(address, user,
 | 
					 | 
				
			||||||
                                                         password, 'xml')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def do_request(self, method, uri, params=None, format='json', **kwargs):
 | 
					    def do_request(self, method, uri, params=None, format='json', **kwargs):
 | 
				
			||||||
        LOG.debug(_("VcnsApiHelper('%(method)s', '%(uri)s', '%(body)s')"), {
 | 
					        LOG.debug(_("VcnsApiHelper('%(method)s', '%(uri)s', '%(body)s')"), {
 | 
				
			||||||
@@ -100,7 +97,7 @@ class Vcns(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def update_routes(self, edge_id, routes):
 | 
					    def update_routes(self, edge_id, routes):
 | 
				
			||||||
        uri = "%s/%s/routing/config/static" % (URI_PREFIX, edge_id)
 | 
					        uri = "%s/%s/routing/config/static" % (URI_PREFIX, edge_id)
 | 
				
			||||||
        return self.do_request(HTTP_PUT, uri, routes, format='xml')
 | 
					        return self.do_request(HTTP_PUT, uri, routes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_lswitch(self, lsconfig):
 | 
					    def create_lswitch(self, lsconfig):
 | 
				
			||||||
        uri = "/api/ws.v1/lswitch"
 | 
					        uri = "/api/ws.v1/lswitch"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,10 +20,12 @@ import os
 | 
				
			|||||||
import neutron.plugins.nicira.api_client.client_eventlet as client
 | 
					import neutron.plugins.nicira.api_client.client_eventlet as client
 | 
				
			||||||
from neutron.plugins.nicira import extensions
 | 
					from neutron.plugins.nicira import extensions
 | 
				
			||||||
import neutron.plugins.nicira.NeutronPlugin as plugin
 | 
					import neutron.plugins.nicira.NeutronPlugin as plugin
 | 
				
			||||||
 | 
					import neutron.plugins.nicira.NeutronServicePlugin as service_plugin
 | 
				
			||||||
import neutron.plugins.nicira.NvpApiClient as nvpapi
 | 
					import neutron.plugins.nicira.NvpApiClient as nvpapi
 | 
				
			||||||
from neutron.plugins.nicira.vshield import vcns
 | 
					from neutron.plugins.nicira.vshield import vcns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
nvp_plugin = plugin.NvpPluginV2
 | 
					nvp_plugin = plugin.NvpPluginV2
 | 
				
			||||||
 | 
					nvp_service_plugin = service_plugin.NvpAdvancedPlugin
 | 
				
			||||||
api_helper = nvpapi.NVPApiHelper
 | 
					api_helper = nvpapi.NVPApiHelper
 | 
				
			||||||
nvp_client = client.NvpApiClientEventlet
 | 
					nvp_client = client.NvpApiClientEventlet
 | 
				
			||||||
vcns_class = vcns.Vcns
 | 
					vcns_class = vcns.Vcns
 | 
				
			||||||
@@ -32,6 +34,8 @@ STUBS_PATH = os.path.join(os.path.dirname(__file__), 'etc')
 | 
				
			|||||||
NVPEXT_PATH = os.path.dirname(extensions.__file__)
 | 
					NVPEXT_PATH = os.path.dirname(extensions.__file__)
 | 
				
			||||||
NVPAPI_NAME = '%s.%s' % (api_helper.__module__, api_helper.__name__)
 | 
					NVPAPI_NAME = '%s.%s' % (api_helper.__module__, api_helper.__name__)
 | 
				
			||||||
PLUGIN_NAME = '%s.%s' % (nvp_plugin.__module__, nvp_plugin.__name__)
 | 
					PLUGIN_NAME = '%s.%s' % (nvp_plugin.__module__, nvp_plugin.__name__)
 | 
				
			||||||
 | 
					SERVICE_PLUGIN_NAME = '%s.%s' % (nvp_service_plugin.__module__,
 | 
				
			||||||
 | 
					                                 nvp_service_plugin.__name__)
 | 
				
			||||||
CLIENT_NAME = '%s.%s' % (nvp_client.__module__, nvp_client.__name__)
 | 
					CLIENT_NAME = '%s.%s' % (nvp_client.__module__, nvp_client.__name__)
 | 
				
			||||||
VCNS_NAME = '%s.%s' % (vcns_class.__module__, vcns_class.__name__)
 | 
					VCNS_NAME = '%s.%s' % (vcns_class.__module__, vcns_class.__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										205
									
								
								neutron/tests/unit/nicira/test_edge_router.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								neutron/tests/unit/nicira/test_edge_router.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,205 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2013 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import copy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from eventlet import greenthread
 | 
				
			||||||
 | 
					import mock
 | 
				
			||||||
 | 
					from oslo.config import cfg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron.api.v2 import attributes
 | 
				
			||||||
 | 
					from neutron import context
 | 
				
			||||||
 | 
					from neutron.extensions import l3
 | 
				
			||||||
 | 
					from neutron.manager import NeutronManager
 | 
				
			||||||
 | 
					from neutron.openstack.common import uuidutils
 | 
				
			||||||
 | 
					from neutron.tests.unit.nicira import NVPEXT_PATH
 | 
				
			||||||
 | 
					from neutron.tests.unit.nicira import SERVICE_PLUGIN_NAME
 | 
				
			||||||
 | 
					from neutron.tests.unit.nicira import test_nicira_plugin
 | 
				
			||||||
 | 
					from neutron.tests.unit.nicira import VCNS_NAME
 | 
				
			||||||
 | 
					from neutron.tests.unit.nicira.vshield import fake_vcns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_uuid = uuidutils.generate_uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceRouterTestExtensionManager(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_resources(self):
 | 
				
			||||||
 | 
					        # If l3 resources have been loaded and updated by main API
 | 
				
			||||||
 | 
					        # router, update the map in the l3 extension so it will load
 | 
				
			||||||
 | 
					        # the same attributes as the API router
 | 
				
			||||||
 | 
					        l3_attr_map = copy.deepcopy(l3.RESOURCE_ATTRIBUTE_MAP)
 | 
				
			||||||
 | 
					        for res in l3.RESOURCE_ATTRIBUTE_MAP.keys():
 | 
				
			||||||
 | 
					            attr_info = attributes.RESOURCE_ATTRIBUTE_MAP.get(res)
 | 
				
			||||||
 | 
					            if attr_info:
 | 
				
			||||||
 | 
					                l3.RESOURCE_ATTRIBUTE_MAP[res] = attr_info
 | 
				
			||||||
 | 
					        resources = l3.L3.get_resources()
 | 
				
			||||||
 | 
					        # restore the original resources once the controllers are created
 | 
				
			||||||
 | 
					        l3.RESOURCE_ATTRIBUTE_MAP = l3_attr_map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return resources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_actions(self):
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_request_extensions(self):
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NvpRouterTestCase(test_nicira_plugin.TestNiciraL3NatTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self, plugin=None, ext_mgr=None):
 | 
				
			||||||
 | 
					        plugin = plugin or SERVICE_PLUGIN_NAME
 | 
				
			||||||
 | 
					        super(NvpRouterTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceRouterTestCase(NvpRouterTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def vcns_patch(self):
 | 
				
			||||||
 | 
					        instance = self.mock_vcns.start()
 | 
				
			||||||
 | 
					        instance.return_value.deploy_edge.side_effect = self.fc2.deploy_edge
 | 
				
			||||||
 | 
					        instance.return_value.get_edge_id.side_effect = self.fc2.get_edge_id
 | 
				
			||||||
 | 
					        instance.return_value.get_edge_deploy_status.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.get_edge_deploy_status)
 | 
				
			||||||
 | 
					        instance.return_value.delete_edge.side_effect = self.fc2.delete_edge
 | 
				
			||||||
 | 
					        instance.return_value.update_interface.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.update_interface)
 | 
				
			||||||
 | 
					        instance.return_value.get_nat_config.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.get_nat_config)
 | 
				
			||||||
 | 
					        instance.return_value.update_nat_config.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.update_nat_config)
 | 
				
			||||||
 | 
					        instance.return_value.delete_nat_rule.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.delete_nat_rule)
 | 
				
			||||||
 | 
					        instance.return_value.get_edge_status.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.get_edge_status)
 | 
				
			||||||
 | 
					        instance.return_value.get_edges.side_effect = self.fc2.get_edges
 | 
				
			||||||
 | 
					        instance.return_value.update_routes.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.update_routes)
 | 
				
			||||||
 | 
					        instance.return_value.create_lswitch.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.create_lswitch)
 | 
				
			||||||
 | 
					        instance.return_value.delete_lswitch.side_effect = (
 | 
				
			||||||
 | 
					            self.fc2.delete_lswitch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        cfg.CONF.set_override('api_extensions_path', NVPEXT_PATH)
 | 
				
			||||||
 | 
					        cfg.CONF.set_override('task_status_check_interval', 100, group="vcns")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # vcns does not support duplicated router name, ignore router name
 | 
				
			||||||
 | 
					        # validation for unit-test cases
 | 
				
			||||||
 | 
					        self.fc2 = fake_vcns.FakeVcns(unique_router_name=False)
 | 
				
			||||||
 | 
					        self.mock_vcns = mock.patch(VCNS_NAME, autospec=True)
 | 
				
			||||||
 | 
					        self.vcns_patch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(ServiceRouterTestCase, self).setUp(
 | 
				
			||||||
 | 
					            ext_mgr=ServiceRouterTestExtensionManager())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.fc2.set_fake_nvpapi(self.fc)
 | 
				
			||||||
 | 
					        self.addCleanup(self.fc2.reset_all)
 | 
				
			||||||
 | 
					        self.addCleanup(self.mock_vcns.stop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tearDown(self):
 | 
				
			||||||
 | 
					        plugin = NeutronManager.get_plugin()
 | 
				
			||||||
 | 
					        manager = plugin.vcns_driver.task_manager
 | 
				
			||||||
 | 
					        for i in range(20):
 | 
				
			||||||
 | 
					            if not manager.has_pending_task():
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            greenthread.sleep(0.1)
 | 
				
			||||||
 | 
					        if manager.has_pending_task():
 | 
				
			||||||
 | 
					            manager.show_pending_tasks()
 | 
				
			||||||
 | 
					            raise Exception(_("Tasks not completed"))
 | 
				
			||||||
 | 
					        manager.stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(ServiceRouterTestCase, self).tearDown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _create_router(self, fmt, tenant_id, name=None,
 | 
				
			||||||
 | 
					                       admin_state_up=None, set_context=False,
 | 
				
			||||||
 | 
					                       arg_list=None, **kwargs):
 | 
				
			||||||
 | 
					        data = {'router': {'tenant_id': tenant_id}}
 | 
				
			||||||
 | 
					        if name:
 | 
				
			||||||
 | 
					            data['router']['name'] = name
 | 
				
			||||||
 | 
					        if admin_state_up:
 | 
				
			||||||
 | 
					            data['router']['admin_state_up'] = admin_state_up
 | 
				
			||||||
 | 
					        for arg in (('admin_state_up', 'tenant_id') + (arg_list or ())):
 | 
				
			||||||
 | 
					            # Arg must be present and not empty
 | 
				
			||||||
 | 
					            if arg in kwargs and kwargs[arg]:
 | 
				
			||||||
 | 
					                data['router'][arg] = kwargs[arg]
 | 
				
			||||||
 | 
					        data['router']['service_router'] = True
 | 
				
			||||||
 | 
					        router_req = self.new_create_request('routers', data, fmt)
 | 
				
			||||||
 | 
					        if set_context and tenant_id:
 | 
				
			||||||
 | 
					            # create a specific auth context for this request
 | 
				
			||||||
 | 
					            router_req.environ['neutron.context'] = context.Context(
 | 
				
			||||||
 | 
					                '', tenant_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return router_req.get_response(self.ext_api)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_router_create(self):
 | 
				
			||||||
 | 
					        name = 'router1'
 | 
				
			||||||
 | 
					        tenant_id = _uuid()
 | 
				
			||||||
 | 
					        expected_value = [('name', name), ('tenant_id', tenant_id),
 | 
				
			||||||
 | 
					                          ('admin_state_up', True),
 | 
				
			||||||
 | 
					                          ('external_gateway_info', None),
 | 
				
			||||||
 | 
					                          ('service_router', True)]
 | 
				
			||||||
 | 
					        with self.router(name='router1', admin_state_up=True,
 | 
				
			||||||
 | 
					                         tenant_id=tenant_id) as router:
 | 
				
			||||||
 | 
					            expected_value_1 = expected_value + [('status', 'PENDING_CREATE')]
 | 
				
			||||||
 | 
					            for k, v in expected_value_1:
 | 
				
			||||||
 | 
					                self.assertEqual(router['router'][k], v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # wait ~1 seconds for router status update
 | 
				
			||||||
 | 
					            for i in range(2):
 | 
				
			||||||
 | 
					                greenthread.sleep(0.5)
 | 
				
			||||||
 | 
					                res = self._show('routers', router['router']['id'])
 | 
				
			||||||
 | 
					                if res['router']['status'] == 'ACTIVE':
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					            expected_value_2 = expected_value + [('status', 'ACTIVE')]
 | 
				
			||||||
 | 
					            for k, v in expected_value_2:
 | 
				
			||||||
 | 
					                self.assertEqual(res['router'][k], v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # check an integration lswitch is created
 | 
				
			||||||
 | 
					            lswitch_name = "%s-ls" % name
 | 
				
			||||||
 | 
					            for lswitch_id, lswitch in self.fc2._lswitches.iteritems():
 | 
				
			||||||
 | 
					                if lswitch['display_name'] == lswitch_name:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.fail("Integration lswitch not found")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check an integration lswitch is deleted
 | 
				
			||||||
 | 
					        lswitch_name = "%s-ls" % name
 | 
				
			||||||
 | 
					        for lswitch_id, lswitch in self.fc2._lswitches.iteritems():
 | 
				
			||||||
 | 
					            if lswitch['display_name'] == lswitch_name:
 | 
				
			||||||
 | 
					                self.fail("Integration switch is not deleted")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_router_show(self):
 | 
				
			||||||
 | 
					        name = 'router1'
 | 
				
			||||||
 | 
					        tenant_id = _uuid()
 | 
				
			||||||
 | 
					        expected_value = [('name', name), ('tenant_id', tenant_id),
 | 
				
			||||||
 | 
					                          ('admin_state_up', True),
 | 
				
			||||||
 | 
					                          ('status', 'PENDING_CREATE'),
 | 
				
			||||||
 | 
					                          ('external_gateway_info', None),
 | 
				
			||||||
 | 
					                          ('service_router', True)]
 | 
				
			||||||
 | 
					        with self.router(name='router1', admin_state_up=True,
 | 
				
			||||||
 | 
					                         tenant_id=tenant_id) as router:
 | 
				
			||||||
 | 
					            res = self._show('routers', router['router']['id'])
 | 
				
			||||||
 | 
					            for k, v in expected_value:
 | 
				
			||||||
 | 
					                self.assertEqual(res['router'][k], v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _test_router_create_with_gwinfo_and_l3_ext_net(self, vlan_id=None):
 | 
				
			||||||
 | 
					        super(ServiceRouterTestCase,
 | 
				
			||||||
 | 
					              self)._test_router_create_with_gwinfo_and_l3_ext_net(
 | 
				
			||||||
 | 
					                  vlan_id, validate_ext_gw=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _test_router_update_gateway_on_l3_ext_net(self, vlan_id=None):
 | 
				
			||||||
 | 
					        super(ServiceRouterTestCase,
 | 
				
			||||||
 | 
					              self)._test_router_update_gateway_on_l3_ext_net(
 | 
				
			||||||
 | 
					                  vlan_id, validate_ext_gw=False)
 | 
				
			||||||
@@ -32,6 +32,7 @@ from neutron.extensions import portbindings
 | 
				
			|||||||
from neutron.extensions import providernet as pnet
 | 
					from neutron.extensions import providernet as pnet
 | 
				
			||||||
from neutron.extensions import securitygroup as secgrp
 | 
					from neutron.extensions import securitygroup as secgrp
 | 
				
			||||||
from neutron import manager
 | 
					from neutron import manager
 | 
				
			||||||
 | 
					from neutron.manager import NeutronManager
 | 
				
			||||||
from neutron.openstack.common import uuidutils
 | 
					from neutron.openstack.common import uuidutils
 | 
				
			||||||
from neutron.plugins.nicira.common import exceptions as nvp_exc
 | 
					from neutron.plugins.nicira.common import exceptions as nvp_exc
 | 
				
			||||||
from neutron.plugins.nicira.common import sync
 | 
					from neutron.plugins.nicira.common import sync
 | 
				
			||||||
@@ -433,15 +434,21 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
    def _restore_l3_attribute_map(self):
 | 
					    def _restore_l3_attribute_map(self):
 | 
				
			||||||
        l3.RESOURCE_ATTRIBUTE_MAP = self._l3_attribute_map_bk
 | 
					        l3.RESOURCE_ATTRIBUTE_MAP = self._l3_attribute_map_bk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self, plugin=None, ext_mgr=None):
 | 
				
			||||||
        self._l3_attribute_map_bk = {}
 | 
					        self._l3_attribute_map_bk = {}
 | 
				
			||||||
        for item in l3.RESOURCE_ATTRIBUTE_MAP:
 | 
					        for item in l3.RESOURCE_ATTRIBUTE_MAP:
 | 
				
			||||||
            self._l3_attribute_map_bk[item] = (
 | 
					            self._l3_attribute_map_bk[item] = (
 | 
				
			||||||
                l3.RESOURCE_ATTRIBUTE_MAP[item].copy())
 | 
					                l3.RESOURCE_ATTRIBUTE_MAP[item].copy())
 | 
				
			||||||
        cfg.CONF.set_override('api_extensions_path', NVPEXT_PATH)
 | 
					        cfg.CONF.set_override('api_extensions_path', NVPEXT_PATH)
 | 
				
			||||||
        self.addCleanup(self._restore_l3_attribute_map)
 | 
					        self.addCleanup(self._restore_l3_attribute_map)
 | 
				
			||||||
 | 
					        ext_mgr = ext_mgr or TestNiciraL3ExtensionManager()
 | 
				
			||||||
        super(TestNiciraL3NatTestCase, self).setUp(
 | 
					        super(TestNiciraL3NatTestCase, self).setUp(
 | 
				
			||||||
            ext_mgr=TestNiciraL3ExtensionManager())
 | 
					            plugin=plugin, ext_mgr=ext_mgr)
 | 
				
			||||||
 | 
					        plugin_instance = NeutronManager.get_plugin()
 | 
				
			||||||
 | 
					        self._plugin_name = "%s.%s" % (
 | 
				
			||||||
 | 
					            plugin_instance.__module__,
 | 
				
			||||||
 | 
					            plugin_instance.__class__.__name__)
 | 
				
			||||||
 | 
					        self._plugin_class = plugin_instance.__class__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tearDown(self):
 | 
					    def tearDown(self):
 | 
				
			||||||
        super(TestNiciraL3NatTestCase, self).tearDown()
 | 
					        super(TestNiciraL3NatTestCase, self).tearDown()
 | 
				
			||||||
@@ -487,7 +494,8 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
    def test_create_l3_ext_network_without_vlan(self):
 | 
					    def test_create_l3_ext_network_without_vlan(self):
 | 
				
			||||||
        self._test_create_l3_ext_network()
 | 
					        self._test_create_l3_ext_network()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _test_router_create_with_gwinfo_and_l3_ext_net(self, vlan_id=None):
 | 
					    def _test_router_create_with_gwinfo_and_l3_ext_net(self, vlan_id=None,
 | 
				
			||||||
 | 
					                                                       validate_ext_gw=True):
 | 
				
			||||||
        with self._create_l3_ext_network(vlan_id) as net:
 | 
					        with self._create_l3_ext_network(vlan_id) as net:
 | 
				
			||||||
            with self.subnet(network=net) as s:
 | 
					            with self.subnet(network=net) as s:
 | 
				
			||||||
                data = {'router': {'tenant_id': 'whatever'}}
 | 
					                data = {'router': {'tenant_id': 'whatever'}}
 | 
				
			||||||
@@ -503,6 +511,7 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
                        s['subnet']['network_id'],
 | 
					                        s['subnet']['network_id'],
 | 
				
			||||||
                        (router['router']['external_gateway_info']
 | 
					                        (router['router']['external_gateway_info']
 | 
				
			||||||
                         ['network_id']))
 | 
					                         ['network_id']))
 | 
				
			||||||
 | 
					                    if validate_ext_gw:
 | 
				
			||||||
                        self._nvp_validate_ext_gw(router['router']['id'],
 | 
					                        self._nvp_validate_ext_gw(router['router']['id'],
 | 
				
			||||||
                                                  'l3_gw_uuid', vlan_id)
 | 
					                                                  'l3_gw_uuid', vlan_id)
 | 
				
			||||||
                finally:
 | 
					                finally:
 | 
				
			||||||
@@ -584,7 +593,8 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
                uuidutils.generate_uuid(),
 | 
					                uuidutils.generate_uuid(),
 | 
				
			||||||
                expected_code=webob.exc.HTTPNotFound.code)
 | 
					                expected_code=webob.exc.HTTPNotFound.code)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _test_router_update_gateway_on_l3_ext_net(self, vlan_id=None):
 | 
					    def _test_router_update_gateway_on_l3_ext_net(self, vlan_id=None,
 | 
				
			||||||
 | 
					                                                  validate_ext_gw=True):
 | 
				
			||||||
        with self.router() as r:
 | 
					        with self.router() as r:
 | 
				
			||||||
            with self.subnet() as s1:
 | 
					            with self.subnet() as s1:
 | 
				
			||||||
                with self._create_l3_ext_network(vlan_id) as net:
 | 
					                with self._create_l3_ext_network(vlan_id) as net:
 | 
				
			||||||
@@ -609,7 +619,9 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
                                      ['external_gateway_info']['network_id'])
 | 
					                                      ['external_gateway_info']['network_id'])
 | 
				
			||||||
                            self.assertEqual(net_id,
 | 
					                            self.assertEqual(net_id,
 | 
				
			||||||
                                             s2['subnet']['network_id'])
 | 
					                                             s2['subnet']['network_id'])
 | 
				
			||||||
                            self._nvp_validate_ext_gw(body['router']['id'],
 | 
					                            if validate_ext_gw:
 | 
				
			||||||
 | 
					                                self._nvp_validate_ext_gw(
 | 
				
			||||||
 | 
					                                    body['router']['id'],
 | 
				
			||||||
                                    'l3_gw_uuid', vlan_id)
 | 
					                                    'l3_gw_uuid', vlan_id)
 | 
				
			||||||
                        finally:
 | 
					                        finally:
 | 
				
			||||||
                            # Cleanup
 | 
					                            # Cleanup
 | 
				
			||||||
@@ -635,14 +647,10 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
        self._test_create_l3_ext_network(666)
 | 
					        self._test_create_l3_ext_network(666)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_floatingip_with_assoc_fails(self):
 | 
					    def test_floatingip_with_assoc_fails(self):
 | 
				
			||||||
        self._test_floatingip_with_assoc_fails(
 | 
					        self._test_floatingip_with_assoc_fails(self._plugin_name)
 | 
				
			||||||
            'neutron.plugins.nicira.'
 | 
					 | 
				
			||||||
            'NeutronPlugin.NvpPluginV2')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_floatingip_with_invalid_create_port(self):
 | 
					    def test_floatingip_with_invalid_create_port(self):
 | 
				
			||||||
        self._test_floatingip_with_invalid_create_port(
 | 
					        self._test_floatingip_with_invalid_create_port(self._plugin_name)
 | 
				
			||||||
            'neutron.plugins.nicira.'
 | 
					 | 
				
			||||||
            'NeutronPlugin.NvpPluginV2')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _nvp_metadata_setup(self):
 | 
					    def _nvp_metadata_setup(self):
 | 
				
			||||||
        cfg.CONF.set_override('metadata_mode', 'access_network', 'NVP')
 | 
					        cfg.CONF.set_override('metadata_mode', 'access_network', 'NVP')
 | 
				
			||||||
@@ -724,7 +732,7 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
        with self.router() as r:
 | 
					        with self.router() as r:
 | 
				
			||||||
            with self.subnet() as s:
 | 
					            with self.subnet() as s:
 | 
				
			||||||
                # Raise a NeutronException (eg: NotFound)
 | 
					                # Raise a NeutronException (eg: NotFound)
 | 
				
			||||||
                with mock.patch.object(NeutronPlugin.NvpPluginV2,
 | 
					                with mock.patch.object(self._plugin_class,
 | 
				
			||||||
                                       'create_subnet',
 | 
					                                       'create_subnet',
 | 
				
			||||||
                                       side_effect=ntn_exc.NotFound):
 | 
					                                       side_effect=ntn_exc.NotFound):
 | 
				
			||||||
                    self._router_interface_action(
 | 
					                    self._router_interface_action(
 | 
				
			||||||
@@ -746,7 +754,7 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
                # Raise a NeutronException when adding metadata subnet
 | 
					                # Raise a NeutronException when adding metadata subnet
 | 
				
			||||||
                # to router
 | 
					                # to router
 | 
				
			||||||
                # save function being mocked
 | 
					                # save function being mocked
 | 
				
			||||||
                real_func = NeutronPlugin.NvpPluginV2.add_router_interface
 | 
					                real_func = self._plugin_class.add_router_interface
 | 
				
			||||||
                plugin_instance = manager.NeutronManager.get_plugin()
 | 
					                plugin_instance = manager.NeutronManager.get_plugin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                def side_effect(*args):
 | 
					                def side_effect(*args):
 | 
				
			||||||
@@ -756,7 +764,7 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
                    # otherwise raise
 | 
					                    # otherwise raise
 | 
				
			||||||
                    raise NvpApiClient.NvpApiException()
 | 
					                    raise NvpApiClient.NvpApiException()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                with mock.patch.object(NeutronPlugin.NvpPluginV2,
 | 
					                with mock.patch.object(self._plugin_class,
 | 
				
			||||||
                                       'add_router_interface',
 | 
					                                       'add_router_interface',
 | 
				
			||||||
                                       side_effect=side_effect):
 | 
					                                       side_effect=side_effect):
 | 
				
			||||||
                    self._router_interface_action(
 | 
					                    self._router_interface_action(
 | 
				
			||||||
@@ -817,7 +825,7 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
                # Raise a NeutronException when removing
 | 
					                # Raise a NeutronException when removing
 | 
				
			||||||
                # metadata subnet from router
 | 
					                # metadata subnet from router
 | 
				
			||||||
                # save function being mocked
 | 
					                # save function being mocked
 | 
				
			||||||
                real_func = NeutronPlugin.NvpPluginV2.remove_router_interface
 | 
					                real_func = self._plugin_class.remove_router_interface
 | 
				
			||||||
                plugin_instance = manager.NeutronManager.get_plugin()
 | 
					                plugin_instance = manager.NeutronManager.get_plugin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                def side_effect(*args):
 | 
					                def side_effect(*args):
 | 
				
			||||||
@@ -827,7 +835,7 @@ class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
 | 
				
			|||||||
                    # otherwise raise
 | 
					                    # otherwise raise
 | 
				
			||||||
                    raise NvpApiClient.NvpApiException()
 | 
					                    raise NvpApiClient.NvpApiException()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                with mock.patch.object(NeutronPlugin.NvpPluginV2,
 | 
					                with mock.patch.object(self._plugin_class,
 | 
				
			||||||
                                       'remove_router_interface',
 | 
					                                       'remove_router_interface',
 | 
				
			||||||
                                       side_effect=side_effect):
 | 
					                                       side_effect=side_effect):
 | 
				
			||||||
                    self._router_interface_action('remove', r['router']['id'],
 | 
					                    self._router_interface_action('remove', r['router']['id'],
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user