Merge "Add support for tenant-provided NSX gateways devices"
This commit is contained in:
		@@ -46,11 +46,29 @@ def build_resource_info(plural_mappings, resource_map, which_service,
 | 
			
		||||
    API resource objects for advanced services extensions. Will optionally
 | 
			
		||||
    translate underscores to dashes in resource names, register the resource,
 | 
			
		||||
    and accept action information for resources.
 | 
			
		||||
 | 
			
		||||
    :param plural_mappings: mappings between singular and plural forms
 | 
			
		||||
    :param resource_map: attribute map for the WSGI resources to create
 | 
			
		||||
    :param which_service: The name of the service for which the WSGI resources
 | 
			
		||||
                          are being created. This name will be used to pass
 | 
			
		||||
                          the appropriate plugin to the WSGI resource.
 | 
			
		||||
                          It can be set to None or "CORE"to create WSGI
 | 
			
		||||
                          resources for the the core plugin
 | 
			
		||||
    :param action_map: custom resource actions
 | 
			
		||||
    :param register_quota: it can be set to True to register quotas for the
 | 
			
		||||
                           resource(s) being created
 | 
			
		||||
    :param translate_name: replaces underscores with dashes
 | 
			
		||||
    :param allow_bulk: True if bulk create are allowed
 | 
			
		||||
    """
 | 
			
		||||
    resources = []
 | 
			
		||||
    if not which_service:
 | 
			
		||||
        which_service = constants.CORE
 | 
			
		||||
    if action_map is None:
 | 
			
		||||
        action_map = {}
 | 
			
		||||
    plugin = manager.NeutronManager.get_service_plugins()[which_service]
 | 
			
		||||
    if which_service != constants.CORE:
 | 
			
		||||
        plugin = manager.NeutronManager.get_service_plugins()[which_service]
 | 
			
		||||
    else:
 | 
			
		||||
        plugin = manager.NeutronManager.get_plugin()
 | 
			
		||||
    for collection_name in resource_map:
 | 
			
		||||
        resource_name = plural_mappings[collection_name]
 | 
			
		||||
        params = resource_map.get(collection_name, {})
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,100 @@
 | 
			
		||||
# Copyright 2014 OpenStack Foundation
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
"""nsx_gw_devices
 | 
			
		||||
 | 
			
		||||
Revision ID: 19180cf98af6
 | 
			
		||||
Revises: 117643811bca
 | 
			
		||||
Create Date: 2014-02-26 02:46:26.151741
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
# revision identifiers, used by Alembic.
 | 
			
		||||
revision = '19180cf98af6'
 | 
			
		||||
down_revision = '117643811bca'
 | 
			
		||||
 | 
			
		||||
# Change to ['*'] if this migration applies to all plugins
 | 
			
		||||
 | 
			
		||||
migration_for_plugins = [
 | 
			
		||||
    'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2',
 | 
			
		||||
    'neutron.plugins.nicira.NeutronServicePlugin.NvpAdvancedPlugin',
 | 
			
		||||
    'neutron.plugins.vmware.plugin.NsxPlugin',
 | 
			
		||||
    'neutron.plugins.vmware.plugin.NsxServicePlugin'
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
        'networkgatewaydevicereferences',
 | 
			
		||||
        sa.Column('id', sa.String(length=36), nullable=False),
 | 
			
		||||
        sa.Column('network_gateway_id', sa.String(length=36), nullable=True),
 | 
			
		||||
        sa.Column('interface_name', sa.String(length=64), nullable=True),
 | 
			
		||||
        sa.ForeignKeyConstraint(['network_gateway_id'], ['networkgateways.id'],
 | 
			
		||||
                                ondelete='CASCADE'),
 | 
			
		||||
        sa.PrimaryKeyConstraint('id'),
 | 
			
		||||
        mysql_engine='InnoDB')
 | 
			
		||||
    # Copy data from networkgatewaydevices into networkgatewaydevicereference
 | 
			
		||||
    op.execute("INSERT INTO networkgatewaydevicereferences SELECT "
 | 
			
		||||
               "id, network_gateway_id, interface_name FROM "
 | 
			
		||||
               "networkgatewaydevices")
 | 
			
		||||
    # drop networkgatewaydevices
 | 
			
		||||
    op.drop_table('networkgatewaydevices')
 | 
			
		||||
    op.create_table(
 | 
			
		||||
        'networkgatewaydevices',
 | 
			
		||||
        sa.Column('tenant_id', sa.String(length=255), nullable=True),
 | 
			
		||||
        sa.Column('id', sa.String(length=36), nullable=False),
 | 
			
		||||
        sa.Column('nsx_id', sa.String(length=36), nullable=True),
 | 
			
		||||
        sa.Column('name', sa.String(length=255), nullable=True),
 | 
			
		||||
        sa.Column('connector_type', sa.String(length=10), nullable=True),
 | 
			
		||||
        sa.Column('connector_ip', sa.String(length=64), nullable=True),
 | 
			
		||||
        sa.Column('status', sa.String(length=16), nullable=True),
 | 
			
		||||
        sa.PrimaryKeyConstraint('id'),
 | 
			
		||||
        mysql_engine='InnoDB')
 | 
			
		||||
    # Create a networkgatewaydevice for each existing reference.
 | 
			
		||||
    # For existing references nsx_id == neutron_id
 | 
			
		||||
    # Do not fill conenctor info as they would be unknown
 | 
			
		||||
    op.execute("INSERT INTO networkgatewaydevices (id, nsx_id) SELECT "
 | 
			
		||||
               "id, id as nsx_id FROM networkgatewaydevicereferences")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def downgrade(active_plugins=None, options=None):
 | 
			
		||||
    if not migration.should_run(active_plugins, migration_for_plugins):
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    op.drop_table('networkgatewaydevices')
 | 
			
		||||
    # Re-create previous version of networkgatewaydevices table
 | 
			
		||||
    op.create_table(
 | 
			
		||||
        'networkgatewaydevices',
 | 
			
		||||
        sa.Column('id', sa.String(length=36), nullable=False),
 | 
			
		||||
        sa.Column('network_gateway_id', sa.String(length=36), nullable=True),
 | 
			
		||||
        sa.Column('interface_name', sa.String(length=64), nullable=True),
 | 
			
		||||
        sa.ForeignKeyConstraint(['network_gateway_id'], ['networkgateways.id'],
 | 
			
		||||
                                ondelete='CASCADE'),
 | 
			
		||||
        sa.PrimaryKeyConstraint('id'),
 | 
			
		||||
        mysql_engine='InnoDB')
 | 
			
		||||
    # Copy from networkgatewaydevicereferences to networkgatewaydevices
 | 
			
		||||
    op.execute("INSERT INTO networkgatewaydevices SELECT "
 | 
			
		||||
               "id, network_gateway_id, interface_name FROM "
 | 
			
		||||
               "networkgatewaydevicereferences")
 | 
			
		||||
    # Dropt networkgatewaydevicereferences
 | 
			
		||||
    op.drop_table('networkgatewaydevicereferences')
 | 
			
		||||
@@ -15,10 +15,14 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from neutron.common import exceptions as n_exc
 | 
			
		||||
from neutron.openstack.common import log
 | 
			
		||||
from neutron.plugins.vmware.api_client import client
 | 
			
		||||
from neutron.plugins.vmware.api_client import exception as api_exc
 | 
			
		||||
from neutron.plugins.vmware.dbexts import db as nsx_db
 | 
			
		||||
from neutron.plugins.vmware.dbexts import networkgw_db
 | 
			
		||||
from neutron.plugins.vmware import nsx_cluster
 | 
			
		||||
from neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
 | 
			
		||||
from neutron.plugins.vmware.nsxlib import router as routerlib
 | 
			
		||||
from neutron.plugins.vmware.nsxlib import secgroup as secgrouplib
 | 
			
		||||
from neutron.plugins.vmware.nsxlib import switch as switchlib
 | 
			
		||||
@@ -211,3 +215,35 @@ def create_nsx_cluster(cluster_opts, concurrent_connections, gen_timeout):
 | 
			
		||||
        concurrent_connections=concurrent_connections,
 | 
			
		||||
        gen_timeout=gen_timeout)
 | 
			
		||||
    return cluster
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_nsx_device_status(cluster, nsx_uuid):
 | 
			
		||||
    try:
 | 
			
		||||
        status_up = l2gwlib.get_gateway_device_status(
 | 
			
		||||
            cluster, nsx_uuid)
 | 
			
		||||
        if status_up:
 | 
			
		||||
            return networkgw_db.STATUS_ACTIVE
 | 
			
		||||
        else:
 | 
			
		||||
            return networkgw_db.STATUS_DOWN
 | 
			
		||||
    except api_exc.NsxApiException:
 | 
			
		||||
        return networkgw_db.STATUS_UNKNOWN
 | 
			
		||||
    except n_exc.NotFound:
 | 
			
		||||
        return networkgw_db.ERROR
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_nsx_device_statuses(cluster, tenant_id):
 | 
			
		||||
    try:
 | 
			
		||||
        status_dict = l2gwlib.get_gateway_devices_status(
 | 
			
		||||
            cluster, tenant_id)
 | 
			
		||||
        return dict((nsx_device_id,
 | 
			
		||||
                     networkgw_db.STATUS_ACTIVE if connected
 | 
			
		||||
                     else networkgw_db.STATUS_DOWN) for
 | 
			
		||||
                    (nsx_device_id, connected) in status_dict.iteritems())
 | 
			
		||||
    except api_exc.NsxApiException:
 | 
			
		||||
        # Do not make a NSX API exception fatal
 | 
			
		||||
        if tenant_id:
 | 
			
		||||
            LOG.warn(_("Unable to retrieve operational status for gateway "
 | 
			
		||||
                       "devices belonging to tenant: %s"), tenant_id)
 | 
			
		||||
        else:
 | 
			
		||||
            LOG.warn(_("Unable to retrieve operational status for "
 | 
			
		||||
                       "gateway devices"))
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,17 @@ MAX_DISPLAY_NAME_LEN = 40
 | 
			
		||||
NEUTRON_VERSION = version_info.release_string()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Allowed network types for the NSX Plugin
 | 
			
		||||
class NetworkTypes:
 | 
			
		||||
    """Allowed provider network types for the NSX Plugin."""
 | 
			
		||||
    L3_EXT = 'l3_ext'
 | 
			
		||||
    STT = 'stt'
 | 
			
		||||
    GRE = 'gre'
 | 
			
		||||
    FLAT = 'flat'
 | 
			
		||||
    VLAN = 'vlan'
 | 
			
		||||
    BRIDGE = 'bridge'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_tags(**kwargs):
 | 
			
		||||
    tags = ([dict(tag=value, scope=key)
 | 
			
		||||
            for key, value in kwargs.iteritems()])
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@
 | 
			
		||||
#    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
 | 
			
		||||
 | 
			
		||||
@@ -37,6 +36,11 @@ SEGMENTATION_ID = 'segmentation_id'
 | 
			
		||||
ALLOWED_CONNECTION_ATTRIBUTES = set((NETWORK_ID,
 | 
			
		||||
                                     SEGMENTATION_TYPE,
 | 
			
		||||
                                     SEGMENTATION_ID))
 | 
			
		||||
# Constants for gateway device operational status
 | 
			
		||||
STATUS_UNKNOWN = "UNKNOWN"
 | 
			
		||||
STATUS_ERROR = "ERROR"
 | 
			
		||||
STATUS_ACTIVE = "ACTIVE"
 | 
			
		||||
STATUS_DOWN = "DOWN"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GatewayInUse(exceptions.InUse):
 | 
			
		||||
@@ -48,6 +52,15 @@ class GatewayNotFound(exceptions.NotFound):
 | 
			
		||||
    message = _("Network Gateway %(gateway_id)s could not be found")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GatewayDeviceInUse(exceptions.InUse):
 | 
			
		||||
    message = _("Network Gateway Device '%(device_id)s' is still used by "
 | 
			
		||||
                "one or more network gateways.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GatewayDeviceNotFound(exceptions.NotFound):
 | 
			
		||||
    message = _("Network Gateway Device %(device_id)s could not be found.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NetworkGatewayPortInUse(exceptions.InUse):
 | 
			
		||||
    message = _("Port '%(port_id)s' is owned by '%(device_owner)s' and "
 | 
			
		||||
                "therefore cannot be deleted directly via the port API.")
 | 
			
		||||
@@ -104,7 +117,7 @@ class NetworkConnection(model_base.BASEV2, models_v2.HasTenant):
 | 
			
		||||
                        primary_key=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NetworkGatewayDevice(model_base.BASEV2):
 | 
			
		||||
class NetworkGatewayDeviceReference(model_base.BASEV2):
 | 
			
		||||
    id = sa.Column(sa.String(36), primary_key=True)
 | 
			
		||||
    network_gateway_id = sa.Column(sa.String(36),
 | 
			
		||||
                                   sa.ForeignKey('networkgateways.id',
 | 
			
		||||
@@ -112,6 +125,20 @@ class NetworkGatewayDevice(model_base.BASEV2):
 | 
			
		||||
    interface_name = sa.Column(sa.String(64))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NetworkGatewayDevice(model_base.BASEV2, models_v2.HasId,
 | 
			
		||||
                           models_v2.HasTenant):
 | 
			
		||||
    nsx_id = sa.Column(sa.String(36))
 | 
			
		||||
    # Optional name for the gateway device
 | 
			
		||||
    name = sa.Column(sa.String(255))
 | 
			
		||||
    # Transport connector type. Not using enum as range of
 | 
			
		||||
    # connector types might vary with backend version
 | 
			
		||||
    connector_type = sa.Column(sa.String(10))
 | 
			
		||||
    # Transport connector IP Address
 | 
			
		||||
    connector_ip = sa.Column(sa.String(64))
 | 
			
		||||
    # operational status
 | 
			
		||||
    status = sa.Column(sa.String(16))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NetworkGateway(model_base.BASEV2, models_v2.HasId,
 | 
			
		||||
                     models_v2.HasTenant):
 | 
			
		||||
    """Defines the data model for a network gateway."""
 | 
			
		||||
@@ -119,7 +146,7 @@ class NetworkGateway(model_base.BASEV2, models_v2.HasId,
 | 
			
		||||
    # Tenant id is nullable for this resource
 | 
			
		||||
    tenant_id = sa.Column(sa.String(36))
 | 
			
		||||
    default = sa.Column(sa.Boolean())
 | 
			
		||||
    devices = orm.relationship(NetworkGatewayDevice,
 | 
			
		||||
    devices = orm.relationship(NetworkGatewayDeviceReference,
 | 
			
		||||
                               backref='networkgateways',
 | 
			
		||||
                               cascade='all,delete')
 | 
			
		||||
    network_connections = orm.relationship(NetworkConnection, lazy='joined')
 | 
			
		||||
@@ -127,7 +154,8 @@ class NetworkGateway(model_base.BASEV2, models_v2.HasId,
 | 
			
		||||
 | 
			
		||||
class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
 | 
			
		||||
 | 
			
		||||
    resource = networkgw.RESOURCE_NAME.replace('-', '_')
 | 
			
		||||
    gateway_resource = networkgw.GATEWAY_RESOURCE_NAME
 | 
			
		||||
    device_resource = networkgw.DEVICE_RESOURCE_NAME
 | 
			
		||||
 | 
			
		||||
    def _get_network_gateway(self, context, gw_id):
 | 
			
		||||
        try:
 | 
			
		||||
@@ -222,7 +250,7 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
 | 
			
		||||
                                          device_owner=port['device_owner'])
 | 
			
		||||
 | 
			
		||||
    def create_network_gateway(self, context, network_gateway):
 | 
			
		||||
        gw_data = network_gateway[self.resource]
 | 
			
		||||
        gw_data = network_gateway[self.gateway_resource]
 | 
			
		||||
        tenant_id = self._get_tenant_id_for_create(context, gw_data)
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            gw_db = NetworkGateway(
 | 
			
		||||
@@ -230,14 +258,17 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
 | 
			
		||||
                tenant_id=tenant_id,
 | 
			
		||||
                name=gw_data.get('name'))
 | 
			
		||||
            # Device list is guaranteed to be a valid list
 | 
			
		||||
            gw_db.devices.extend([NetworkGatewayDevice(**device)
 | 
			
		||||
            # TODO(salv-orlando): Enforce that gateway device identifiers
 | 
			
		||||
            # in this list are among the tenant's NSX network gateway devices
 | 
			
		||||
            # to avoid risk a tenant 'guessing' other tenant's network devices
 | 
			
		||||
            gw_db.devices.extend([NetworkGatewayDeviceReference(**device)
 | 
			
		||||
                                  for device in gw_data['devices']])
 | 
			
		||||
            context.session.add(gw_db)
 | 
			
		||||
        LOG.debug(_("Created network gateway with id:%s"), gw_db['id'])
 | 
			
		||||
        return self._make_network_gateway_dict(gw_db)
 | 
			
		||||
 | 
			
		||||
    def update_network_gateway(self, context, id, network_gateway):
 | 
			
		||||
        gw_data = network_gateway[self.resource]
 | 
			
		||||
        gw_data = network_gateway[self.gateway_resource]
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            gw_db = self._get_network_gateway(context, id)
 | 
			
		||||
            if gw_db.default:
 | 
			
		||||
@@ -363,9 +394,90 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
 | 
			
		||||
                raise MultipleGatewayConnections(
 | 
			
		||||
                    gateway_id=network_gateway_id)
 | 
			
		||||
            # Remove gateway port from network
 | 
			
		||||
            # FIXME(salvatore-orlando): Ensure state of port in NSX is
 | 
			
		||||
            # FIXME(salvatore-orlando): Ensure state of port in NVP is
 | 
			
		||||
            # consistent with outcome of transaction
 | 
			
		||||
            self.delete_port(context, net_connection['port_id'],
 | 
			
		||||
                             nw_gw_port_check=False)
 | 
			
		||||
            # Remove NetworkConnection record
 | 
			
		||||
            context.session.delete(net_connection)
 | 
			
		||||
 | 
			
		||||
    def _make_gateway_device_dict(self, gateway_device, fields=None,
 | 
			
		||||
                                  include_nsx_id=False):
 | 
			
		||||
        res = {'id': gateway_device['id'],
 | 
			
		||||
               'name': gateway_device['name'],
 | 
			
		||||
               'status': gateway_device['status'],
 | 
			
		||||
               'connector_type': gateway_device['connector_type'],
 | 
			
		||||
               'connector_ip': gateway_device['connector_ip'],
 | 
			
		||||
               'tenant_id': gateway_device['tenant_id']}
 | 
			
		||||
        if include_nsx_id:
 | 
			
		||||
            # Return the NSX mapping as well. This attribute will not be
 | 
			
		||||
            # returned in the API response anyway. Ensure it will not be
 | 
			
		||||
            # filtered out in field selection.
 | 
			
		||||
            if fields:
 | 
			
		||||
                fields.append('nsx_id')
 | 
			
		||||
            res['nsx_id'] = gateway_device['nsx_id']
 | 
			
		||||
        return self._fields(res, fields)
 | 
			
		||||
 | 
			
		||||
    def _get_gateway_device(self, context, device_id):
 | 
			
		||||
        try:
 | 
			
		||||
            return self._get_by_id(context, NetworkGatewayDevice, device_id)
 | 
			
		||||
        except sa_orm_exc.NoResultFound:
 | 
			
		||||
            raise GatewayDeviceNotFound(device_id=device_id)
 | 
			
		||||
 | 
			
		||||
    def _is_device_in_use(self, context, device_id):
 | 
			
		||||
        query = self._get_collection_query(
 | 
			
		||||
            context, NetworkGatewayDeviceReference, {'id': [device_id]})
 | 
			
		||||
        return query.first()
 | 
			
		||||
 | 
			
		||||
    def get_gateway_device(self, context, device_id, fields=None,
 | 
			
		||||
                           include_nsx_id=False):
 | 
			
		||||
        return self._make_gateway_device_dict(
 | 
			
		||||
            self._get_gateway_device(context, device_id),
 | 
			
		||||
            fields, include_nsx_id)
 | 
			
		||||
 | 
			
		||||
    def get_gateway_devices(self, context, filters=None, fields=None,
 | 
			
		||||
                            include_nsx_id=False):
 | 
			
		||||
        query = self._get_collection_query(context,
 | 
			
		||||
                                           NetworkGatewayDevice,
 | 
			
		||||
                                           filters=filters)
 | 
			
		||||
        return [self._make_gateway_device_dict(row, fields, include_nsx_id)
 | 
			
		||||
                for row in query]
 | 
			
		||||
 | 
			
		||||
    def create_gateway_device(self, context, gateway_device,
 | 
			
		||||
                              initial_status=STATUS_UNKNOWN):
 | 
			
		||||
        device_data = gateway_device[self.device_resource]
 | 
			
		||||
        tenant_id = self._get_tenant_id_for_create(context, device_data)
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            device_db = NetworkGatewayDevice(
 | 
			
		||||
                id=device_data.get('id', uuidutils.generate_uuid()),
 | 
			
		||||
                tenant_id=tenant_id,
 | 
			
		||||
                name=device_data.get('name'),
 | 
			
		||||
                connector_type=device_data['connector_type'],
 | 
			
		||||
                connector_ip=device_data['connector_ip'],
 | 
			
		||||
                status=initial_status)
 | 
			
		||||
            context.session.add(device_db)
 | 
			
		||||
        LOG.debug(_("Created network gateway device: %s"), device_db['id'])
 | 
			
		||||
        return self._make_gateway_device_dict(device_db)
 | 
			
		||||
 | 
			
		||||
    def update_gateway_device(self, context, gateway_device_id,
 | 
			
		||||
                              gateway_device, include_nsx_id=False):
 | 
			
		||||
        device_data = gateway_device[self.device_resource]
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            device_db = self._get_gateway_device(context, gateway_device_id)
 | 
			
		||||
            # Ensure there is something to update before doing it
 | 
			
		||||
            if any([device_db[k] != device_data[k] for k in device_data]):
 | 
			
		||||
                device_db.update(device_data)
 | 
			
		||||
        LOG.debug(_("Updated network gateway device: %s"),
 | 
			
		||||
                  gateway_device_id)
 | 
			
		||||
        return self._make_gateway_device_dict(
 | 
			
		||||
            device_db, include_nsx_id=include_nsx_id)
 | 
			
		||||
 | 
			
		||||
    def delete_gateway_device(self, context, device_id):
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            # A gateway device should not be deleted
 | 
			
		||||
            # if it is used in any network gateway service
 | 
			
		||||
            if self._is_device_in_use(context, device_id):
 | 
			
		||||
                raise GatewayDeviceInUse(device_id=device_id)
 | 
			
		||||
            device_db = self._get_gateway_device(context, device_id)
 | 
			
		||||
            context.session.delete(device_db)
 | 
			
		||||
        LOG.debug(_("Deleted network gateway device: %s."), device_id)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,24 +19,23 @@ from abc import abstractmethod
 | 
			
		||||
 | 
			
		||||
from oslo.config import cfg
 | 
			
		||||
 | 
			
		||||
from neutron.api import extensions
 | 
			
		||||
from neutron.api.v2 import attributes
 | 
			
		||||
from neutron.api.v2 import base
 | 
			
		||||
from neutron import manager
 | 
			
		||||
from neutron import quota
 | 
			
		||||
from neutron.api.v2 import resource_helper
 | 
			
		||||
from neutron.plugins.vmware.common.utils import NetworkTypes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RESOURCE_NAME = "network_gateway"
 | 
			
		||||
GATEWAY_RESOURCE_NAME = "network_gateway"
 | 
			
		||||
DEVICE_RESOURCE_NAME = "gateway_device"
 | 
			
		||||
# Use dash for alias and collection name
 | 
			
		||||
EXT_ALIAS = RESOURCE_NAME.replace('_', '-')
 | 
			
		||||
COLLECTION_NAME = "%ss" % EXT_ALIAS
 | 
			
		||||
EXT_ALIAS = GATEWAY_RESOURCE_NAME.replace('_', '-')
 | 
			
		||||
NETWORK_GATEWAYS = "%ss" % EXT_ALIAS
 | 
			
		||||
GATEWAY_DEVICES = "%ss" % DEVICE_RESOURCE_NAME.replace('_', '-')
 | 
			
		||||
DEVICE_ID_ATTR = 'id'
 | 
			
		||||
IFACE_NAME_ATTR = 'interface_name'
 | 
			
		||||
 | 
			
		||||
# Attribute Map for Network Gateway Resource
 | 
			
		||||
# TODO(salvatore-orlando): add admin state as other neutron resources
 | 
			
		||||
RESOURCE_ATTRIBUTE_MAP = {
 | 
			
		||||
    COLLECTION_NAME: {
 | 
			
		||||
    NETWORK_GATEWAYS: {
 | 
			
		||||
        'id': {'allow_post': False, 'allow_put': False,
 | 
			
		||||
               'is_visible': True},
 | 
			
		||||
        'name': {'allow_post': True, 'allow_put': True,
 | 
			
		||||
@@ -54,6 +53,28 @@ RESOURCE_ATTRIBUTE_MAP = {
 | 
			
		||||
                      'validate': {'type:string': None},
 | 
			
		||||
                      'required_by_policy': True,
 | 
			
		||||
                      'is_visible': True}
 | 
			
		||||
    },
 | 
			
		||||
    GATEWAY_DEVICES: {
 | 
			
		||||
        'id': {'allow_post': False, 'allow_put': False,
 | 
			
		||||
               'is_visible': True},
 | 
			
		||||
        'name': {'allow_post': True, 'allow_put': True,
 | 
			
		||||
                 'validate': {'type:string': None},
 | 
			
		||||
                 'is_visible': True, 'default': ''},
 | 
			
		||||
        'client_certificate': {'allow_post': True, 'allow_put': True,
 | 
			
		||||
                               'validate': {'type:string': None},
 | 
			
		||||
                               'is_visible': True},
 | 
			
		||||
        'connector_type': {'allow_post': True, 'allow_put': True,
 | 
			
		||||
                           'validate': {'type:connector_type': None},
 | 
			
		||||
                           'is_visible': True},
 | 
			
		||||
        'connector_ip': {'allow_post': True, 'allow_put': True,
 | 
			
		||||
                         'validate': {'type:ip_address': None},
 | 
			
		||||
                         'is_visible': True},
 | 
			
		||||
        'tenant_id': {'allow_post': True, 'allow_put': False,
 | 
			
		||||
                      'validate': {'type:string': None},
 | 
			
		||||
                      'required_by_policy': True,
 | 
			
		||||
                      'is_visible': True},
 | 
			
		||||
        'status': {'allow_post': False, 'allow_put': False,
 | 
			
		||||
                   'is_visible': True},
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -85,6 +106,23 @@ def _validate_device_list(data, valid_values=None):
 | 
			
		||||
        return (_("%s: provided data are not iterable") %
 | 
			
		||||
                _validate_device_list.__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate_connector_type(data, valid_values=None):
 | 
			
		||||
    if not data:
 | 
			
		||||
        # A connector type is compulsory
 | 
			
		||||
        msg = _("A connector type is required to create a gateway device")
 | 
			
		||||
        return msg
 | 
			
		||||
    connector_types = (valid_values if valid_values else
 | 
			
		||||
                       [NetworkTypes.GRE,
 | 
			
		||||
                        NetworkTypes.STT,
 | 
			
		||||
                        NetworkTypes.BRIDGE,
 | 
			
		||||
                        'ipsec%s' % NetworkTypes.GRE,
 | 
			
		||||
                        'ipsec%s' % NetworkTypes.STT])
 | 
			
		||||
    if data not in connector_types:
 | 
			
		||||
        msg = _("Unknown connector type: %s") % data
 | 
			
		||||
        return msg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
nw_gw_quota_opts = [
 | 
			
		||||
    cfg.IntOpt('quota_network_gateway',
 | 
			
		||||
               default=5,
 | 
			
		||||
@@ -95,6 +133,7 @@ nw_gw_quota_opts = [
 | 
			
		||||
cfg.CONF.register_opts(nw_gw_quota_opts, 'QUOTAS')
 | 
			
		||||
 | 
			
		||||
attributes.validators['type:device_list'] = _validate_device_list
 | 
			
		||||
attributes.validators['type:connector_type'] = _validate_connector_type
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Networkgw(object):
 | 
			
		||||
@@ -132,22 +171,21 @@ class Networkgw(object):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_resources(cls):
 | 
			
		||||
        """Returns Ext Resources."""
 | 
			
		||||
        plugin = manager.NeutronManager.get_plugin()
 | 
			
		||||
        params = RESOURCE_ATTRIBUTE_MAP.get(COLLECTION_NAME, dict())
 | 
			
		||||
 | 
			
		||||
        member_actions = {'connect_network': 'PUT',
 | 
			
		||||
                          'disconnect_network': 'PUT'}
 | 
			
		||||
        member_actions = {
 | 
			
		||||
            GATEWAY_RESOURCE_NAME.replace('_', '-'): {
 | 
			
		||||
                'connect_network': 'PUT',
 | 
			
		||||
                'disconnect_network': 'PUT'}}
 | 
			
		||||
 | 
			
		||||
        # register quotas for network gateways
 | 
			
		||||
        quota.QUOTAS.register_resource_by_name(RESOURCE_NAME)
 | 
			
		||||
        collection_name = COLLECTION_NAME.replace('_', '-')
 | 
			
		||||
        controller = base.create_resource(collection_name,
 | 
			
		||||
                                          RESOURCE_NAME,
 | 
			
		||||
                                          plugin, params,
 | 
			
		||||
                                          member_actions=member_actions)
 | 
			
		||||
        return [extensions.ResourceExtension(COLLECTION_NAME,
 | 
			
		||||
                                             controller,
 | 
			
		||||
                                             member_actions=member_actions)]
 | 
			
		||||
        plural_mappings = resource_helper.build_plural_mappings(
 | 
			
		||||
            {}, RESOURCE_ATTRIBUTE_MAP)
 | 
			
		||||
 | 
			
		||||
        return resource_helper.build_resource_info(plural_mappings,
 | 
			
		||||
                                                   RESOURCE_ATTRIBUTE_MAP,
 | 
			
		||||
                                                   None,
 | 
			
		||||
                                                   action_map=member_actions,
 | 
			
		||||
                                                   register_quota=True,
 | 
			
		||||
                                                   translate_name=True)
 | 
			
		||||
 | 
			
		||||
    def get_extended_resources(self, version):
 | 
			
		||||
        if version == "2.0":
 | 
			
		||||
@@ -187,3 +225,23 @@ class NetworkGatewayPluginBase(object):
 | 
			
		||||
    def disconnect_network(self, context, network_gateway_id,
 | 
			
		||||
                           network_mapping_info):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def create_gateway_device(self, context, gateway_device):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def update_gateway_device(self, context, id, gateway_device):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def delete_gateway_device(self, context, id):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def get_gateway_device(self, context, id, fields=None):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def get_gateway_devices(self, context, filters=None, fields=None):
 | 
			
		||||
        pass
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ HTTP_DELETE = "DELETE"
 | 
			
		||||
HTTP_PUT = "PUT"
 | 
			
		||||
 | 
			
		||||
GWSERVICE_RESOURCE = "gateway-service"
 | 
			
		||||
TRANSPORTNODE_RESOURCE = "transport-node"
 | 
			
		||||
 | 
			
		||||
LOG = log.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@@ -58,7 +59,7 @@ def create_l2_gw_service(cluster, tenant_id, display_name, devices):
 | 
			
		||||
        "type": "L2GatewayServiceConfig"
 | 
			
		||||
    }
 | 
			
		||||
    return do_request(
 | 
			
		||||
        "POST", _build_uri_path(GWSERVICE_RESOURCE),
 | 
			
		||||
        HTTP_POST, _build_uri_path(GWSERVICE_RESOURCE),
 | 
			
		||||
        json.dumps(gwservice_obj), cluster=cluster)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -74,8 +75,8 @@ def plug_l2_gw_service(cluster, lswitch_id, lport_id,
 | 
			
		||||
 | 
			
		||||
def get_l2_gw_service(cluster, gateway_id):
 | 
			
		||||
    return do_request(
 | 
			
		||||
        "GET", _build_uri_path(GWSERVICE_RESOURCE,
 | 
			
		||||
                               resource_id=gateway_id),
 | 
			
		||||
        HTTP_GET, _build_uri_path(GWSERVICE_RESOURCE,
 | 
			
		||||
                                  resource_id=gateway_id),
 | 
			
		||||
        cluster=cluster)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -98,12 +99,101 @@ def update_l2_gw_service(cluster, gateway_id, display_name):
 | 
			
		||||
        # Nothing to update
 | 
			
		||||
        return gwservice_obj
 | 
			
		||||
    gwservice_obj["display_name"] = utils.check_and_truncate(display_name)
 | 
			
		||||
    return do_request("PUT", _build_uri_path(GWSERVICE_RESOURCE,
 | 
			
		||||
                                             resource_id=gateway_id),
 | 
			
		||||
    return do_request(HTTP_PUT, _build_uri_path(GWSERVICE_RESOURCE,
 | 
			
		||||
                                                resource_id=gateway_id),
 | 
			
		||||
                      json.dumps(gwservice_obj), cluster=cluster)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_l2_gw_service(cluster, gateway_id):
 | 
			
		||||
    do_request("DELETE", _build_uri_path(GWSERVICE_RESOURCE,
 | 
			
		||||
                                         resource_id=gateway_id),
 | 
			
		||||
    do_request(HTTP_DELETE, _build_uri_path(GWSERVICE_RESOURCE,
 | 
			
		||||
                                            resource_id=gateway_id),
 | 
			
		||||
               cluster=cluster)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _build_gateway_device_body(tenant_id, display_name, neutron_id,
 | 
			
		||||
                               connector_type, connector_ip,
 | 
			
		||||
                               client_certificate, tz_uuid):
 | 
			
		||||
 | 
			
		||||
    connector_type_mappings = {
 | 
			
		||||
        utils.NetworkTypes.STT: "STTConnector",
 | 
			
		||||
        utils.NetworkTypes.GRE: "GREConnector",
 | 
			
		||||
        utils.NetworkTypes.BRIDGE: "BridgeConnector",
 | 
			
		||||
        'ipsec%s' % utils.NetworkTypes.STT: "IPsecSTT",
 | 
			
		||||
        'ipsec%s' % utils.NetworkTypes.GRE: "IPsecGRE"}
 | 
			
		||||
    nsx_connector_type = connector_type_mappings[connector_type]
 | 
			
		||||
    body = {"display_name": utils.check_and_truncate(display_name),
 | 
			
		||||
            "tags": utils.get_tags(os_tid=tenant_id,
 | 
			
		||||
                                   q_gw_dev_id=neutron_id),
 | 
			
		||||
            "transport_connectors": [
 | 
			
		||||
                {"transport_zone_uuid": tz_uuid,
 | 
			
		||||
                 "ip_address": connector_ip,
 | 
			
		||||
                 "type": nsx_connector_type}],
 | 
			
		||||
            "admin_status_enabled": True}
 | 
			
		||||
    if client_certificate:
 | 
			
		||||
        body["credential"] = {"client_certificate":
 | 
			
		||||
                              {"pem_encoded": client_certificate},
 | 
			
		||||
                              "type": "SecurityCertificateCredential"}
 | 
			
		||||
    return body
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_gateway_device(cluster, tenant_id, display_name, neutron_id,
 | 
			
		||||
                          tz_uuid, connector_type, connector_ip,
 | 
			
		||||
                          client_certificate):
 | 
			
		||||
    body = _build_gateway_device_body(tenant_id, display_name, neutron_id,
 | 
			
		||||
                                      connector_type, connector_ip,
 | 
			
		||||
                                      client_certificate, tz_uuid)
 | 
			
		||||
    return do_request(
 | 
			
		||||
        HTTP_POST, _build_uri_path(TRANSPORTNODE_RESOURCE),
 | 
			
		||||
        json.dumps(body), cluster=cluster)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_gateway_device(cluster, gateway_id, tenant_id,
 | 
			
		||||
                          display_name, neutron_id,
 | 
			
		||||
                          tz_uuid, connector_type, connector_ip,
 | 
			
		||||
                          client_certificate):
 | 
			
		||||
    body = _build_gateway_device_body(tenant_id, display_name, neutron_id,
 | 
			
		||||
                                      connector_type, connector_ip,
 | 
			
		||||
                                      client_certificate, tz_uuid)
 | 
			
		||||
    return do_request(
 | 
			
		||||
        HTTP_PUT,
 | 
			
		||||
        _build_uri_path(TRANSPORTNODE_RESOURCE, resource_id=gateway_id),
 | 
			
		||||
        json.dumps(body), cluster=cluster)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_gateway_device(cluster, device_uuid):
 | 
			
		||||
    return do_request(HTTP_DELETE,
 | 
			
		||||
                      _build_uri_path(TRANSPORTNODE_RESOURCE,
 | 
			
		||||
                                      device_uuid),
 | 
			
		||||
                      cluster=cluster)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_gateway_device_status(cluster, device_uuid):
 | 
			
		||||
    status_res = do_request(HTTP_GET,
 | 
			
		||||
                            _build_uri_path(TRANSPORTNODE_RESOURCE,
 | 
			
		||||
                                            device_uuid,
 | 
			
		||||
                                            extra_action='status'),
 | 
			
		||||
                            cluster=cluster)
 | 
			
		||||
    # Returns the connection status
 | 
			
		||||
    return status_res['connection']['connected']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_gateway_devices_status(cluster, tenant_id=None):
 | 
			
		||||
    if tenant_id:
 | 
			
		||||
        gw_device_query_path = _build_uri_path(
 | 
			
		||||
            TRANSPORTNODE_RESOURCE,
 | 
			
		||||
            fields="uuid,tags",
 | 
			
		||||
            relations="TransportNodeStatus",
 | 
			
		||||
            filters={'tag': tenant_id,
 | 
			
		||||
                     'tag_scope': 'os_tid'})
 | 
			
		||||
    else:
 | 
			
		||||
        gw_device_query_path = _build_uri_path(
 | 
			
		||||
            TRANSPORTNODE_RESOURCE,
 | 
			
		||||
            fields="uuid,tags",
 | 
			
		||||
            relations="TransportNodeStatus")
 | 
			
		||||
 | 
			
		||||
    response = get_all_query_pages(gw_device_query_path, cluster)
 | 
			
		||||
    results = {}
 | 
			
		||||
    for item in response:
 | 
			
		||||
        results[item['uuid']] = (item['_relations']['TransportNodeStatus']
 | 
			
		||||
                                 ['connection']['connected'])
 | 
			
		||||
    return results
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,7 @@ from neutron.plugins.vmware.common import exceptions as nsx_exc
 | 
			
		||||
from neutron.plugins.vmware.common import nsx_utils
 | 
			
		||||
from neutron.plugins.vmware.common import securitygroups as sg_utils
 | 
			
		||||
from neutron.plugins.vmware.common import sync
 | 
			
		||||
from neutron.plugins.vmware.common.utils import NetworkTypes
 | 
			
		||||
from neutron.plugins.vmware.dbexts import db as nsx_db
 | 
			
		||||
from neutron.plugins.vmware.dbexts import distributedrouter as dist_rtr
 | 
			
		||||
from neutron.plugins.vmware.dbexts import maclearning as mac_db
 | 
			
		||||
@@ -83,17 +84,6 @@ NSX_EXTGW_NAT_RULES_ORDER = 255
 | 
			
		||||
NSX_DEFAULT_NEXTHOP = '1.1.1.1'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Provider network extension - allowed network types for the NSX Plugin
 | 
			
		||||
class NetworkTypes:
 | 
			
		||||
    """Allowed provider network types for the NSX Plugin."""
 | 
			
		||||
    L3_EXT = 'l3_ext'
 | 
			
		||||
    STT = 'stt'
 | 
			
		||||
    GRE = 'gre'
 | 
			
		||||
    FLAT = 'flat'
 | 
			
		||||
    VLAN = 'vlan'
 | 
			
		||||
    BRIDGE = 'bridge'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
			
		||||
                  agentschedulers_db.DhcpAgentSchedulerDbMixin,
 | 
			
		||||
                  db_base_plugin_v2.NeutronDbPluginV2,
 | 
			
		||||
@@ -205,7 +195,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
			
		||||
                def_gw_data = {'id': def_l2_gw_uuid,
 | 
			
		||||
                               'name': 'default L2 gateway service',
 | 
			
		||||
                               'devices': []}
 | 
			
		||||
                gw_res_name = networkgw.RESOURCE_NAME.replace('-', '_')
 | 
			
		||||
                gw_res_name = networkgw.GATEWAY_RESOURCE_NAME.replace('-', '_')
 | 
			
		||||
                def_network_gw = super(
 | 
			
		||||
                    NsxPluginV2, self).create_network_gateway(
 | 
			
		||||
                        ctx, {gw_res_name: def_gw_data})
 | 
			
		||||
@@ -2009,7 +1999,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
			
		||||
        # Ensure the default gateway in the config file is in sync with the db
 | 
			
		||||
        self._ensure_default_network_gateway()
 | 
			
		||||
        # Need to re-do authZ checks here in order to avoid creation on NSX
 | 
			
		||||
        gw_data = network_gateway[networkgw.RESOURCE_NAME.replace('-', '_')]
 | 
			
		||||
        gw_data = network_gateway[networkgw.GATEWAY_RESOURCE_NAME]
 | 
			
		||||
        tenant_id = self._get_tenant_id_for_create(context, gw_data)
 | 
			
		||||
        devices = gw_data['devices']
 | 
			
		||||
        # Populate default physical network where not specified
 | 
			
		||||
@@ -2017,8 +2007,15 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
			
		||||
            if not device.get('interface_name'):
 | 
			
		||||
                device['interface_name'] = self.cluster.default_interface_name
 | 
			
		||||
        try:
 | 
			
		||||
            # Replace Neutron device identifiers with NSX identifiers
 | 
			
		||||
            # TODO(salv-orlando): Make this operation more efficient doing a
 | 
			
		||||
            # single DB query for all devices
 | 
			
		||||
            nsx_devices = [{'id': self._get_nsx_device_id(context,
 | 
			
		||||
                                                          device['id']),
 | 
			
		||||
                            'interface_name': device['interface_name']} for
 | 
			
		||||
                           device in devices]
 | 
			
		||||
            nsx_res = l2gwlib.create_l2_gw_service(
 | 
			
		||||
                self.cluster, tenant_id, gw_data['name'], devices)
 | 
			
		||||
                self.cluster, tenant_id, gw_data['name'], nsx_devices)
 | 
			
		||||
            nsx_uuid = nsx_res.get('uuid')
 | 
			
		||||
        except api_exc.Conflict:
 | 
			
		||||
            raise nsx_exc.L2GatewayAlreadyInUse(gateway=gw_data['name'])
 | 
			
		||||
@@ -2027,8 +2024,8 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
			
		||||
            LOG.exception(err_msg)
 | 
			
		||||
            raise nsx_exc.NsxPluginException(err_msg=err_msg)
 | 
			
		||||
        gw_data['id'] = nsx_uuid
 | 
			
		||||
        return super(NsxPluginV2, self).create_network_gateway(context,
 | 
			
		||||
                                                               network_gateway)
 | 
			
		||||
        return super(NsxPluginV2, self).create_network_gateway(
 | 
			
		||||
            context, network_gateway)
 | 
			
		||||
 | 
			
		||||
    def delete_network_gateway(self, context, gateway_id):
 | 
			
		||||
        """Remove a layer-2 network gateway.
 | 
			
		||||
@@ -2069,7 +2066,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
			
		||||
        # Ensure the default gateway in the config file is in sync with the db
 | 
			
		||||
        self._ensure_default_network_gateway()
 | 
			
		||||
        # Update gateway on backend when there's a name change
 | 
			
		||||
        name = network_gateway[networkgw.RESOURCE_NAME].get('name')
 | 
			
		||||
        name = network_gateway[networkgw.GATEWAY_RESOURCE_NAME].get('name')
 | 
			
		||||
        if name:
 | 
			
		||||
            try:
 | 
			
		||||
                l2gwlib.update_l2_gw_service(self.cluster, id, name)
 | 
			
		||||
@@ -2098,6 +2095,205 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
 | 
			
		||||
        return super(NsxPluginV2, self).disconnect_network(
 | 
			
		||||
            context, network_gateway_id, network_mapping_info)
 | 
			
		||||
 | 
			
		||||
    def _get_nsx_device_id(self, context, device_id):
 | 
			
		||||
        return self._get_gateway_device(context, device_id)['nsx_id']
 | 
			
		||||
 | 
			
		||||
    # TODO(salv-orlando): Handlers for Gateway device operations should be
 | 
			
		||||
    # moved into the appropriate nsx_handlers package once the code for the
 | 
			
		||||
    # blueprint nsx-async-backend-communication merges
 | 
			
		||||
    def create_gateway_device_handler(self, context, gateway_device,
 | 
			
		||||
                                      client_certificate):
 | 
			
		||||
        neutron_id = gateway_device['id']
 | 
			
		||||
        try:
 | 
			
		||||
            nsx_res = l2gwlib.create_gateway_device(
 | 
			
		||||
                self.cluster,
 | 
			
		||||
                gateway_device['tenant_id'],
 | 
			
		||||
                gateway_device['name'],
 | 
			
		||||
                neutron_id,
 | 
			
		||||
                self.cluster.default_tz_uuid,
 | 
			
		||||
                gateway_device['connector_type'],
 | 
			
		||||
                gateway_device['connector_ip'],
 | 
			
		||||
                client_certificate)
 | 
			
		||||
 | 
			
		||||
            # Fetch status (it needs another NSX API call)
 | 
			
		||||
            device_status = nsx_utils.get_nsx_device_status(self.cluster,
 | 
			
		||||
                                                            nsx_res['uuid'])
 | 
			
		||||
 | 
			
		||||
            # set NSX GW device in neutron database and update status
 | 
			
		||||
            with context.session.begin(subtransactions=True):
 | 
			
		||||
                query = self._model_query(
 | 
			
		||||
                    context, networkgw_db.NetworkGatewayDevice).filter(
 | 
			
		||||
                        networkgw_db.NetworkGatewayDevice.id == neutron_id)
 | 
			
		||||
                query.update({'status': device_status,
 | 
			
		||||
                              'nsx_id': nsx_res['uuid']},
 | 
			
		||||
                             synchronize_session=False)
 | 
			
		||||
            LOG.debug(_("Neutron gateway device: %(neutron_id)s; "
 | 
			
		||||
                        "NSX transport node identifier: %(nsx_id)s; "
 | 
			
		||||
                        "Operational status: %(status)s."),
 | 
			
		||||
                      {'neutron_id': neutron_id,
 | 
			
		||||
                       'nsx_id': nsx_res['uuid'],
 | 
			
		||||
                       'status': device_status})
 | 
			
		||||
            return device_status
 | 
			
		||||
        except api_exc.NsxApiException:
 | 
			
		||||
            # Remove gateway device from neutron database
 | 
			
		||||
            with excutils.save_and_reraise_exception():
 | 
			
		||||
                LOG.exception(_("Unable to create gateway device: %s on NSX "
 | 
			
		||||
                                "backend."), neutron_id)
 | 
			
		||||
                with context.session.begin(subtransactions=True):
 | 
			
		||||
                    query = self._model_query(
 | 
			
		||||
                        context, networkgw_db.NetworkGatewayDevice).filter(
 | 
			
		||||
                            networkgw_db.NetworkGatewayDevice.id == neutron_id)
 | 
			
		||||
                    query.delete(synchronize_session=False)
 | 
			
		||||
 | 
			
		||||
    def update_gateway_device_handler(self, context, gateway_device,
 | 
			
		||||
                                      old_gateway_device_data,
 | 
			
		||||
                                      client_certificate):
 | 
			
		||||
        nsx_id = gateway_device['nsx_id']
 | 
			
		||||
        neutron_id = gateway_device['id']
 | 
			
		||||
        try:
 | 
			
		||||
            l2gwlib.update_gateway_device(
 | 
			
		||||
                self.cluster,
 | 
			
		||||
                nsx_id,
 | 
			
		||||
                gateway_device['tenant_id'],
 | 
			
		||||
                gateway_device['name'],
 | 
			
		||||
                neutron_id,
 | 
			
		||||
                self.cluster.default_tz_uuid,
 | 
			
		||||
                gateway_device['connector_type'],
 | 
			
		||||
                gateway_device['connector_ip'],
 | 
			
		||||
                client_certificate)
 | 
			
		||||
 | 
			
		||||
            # Fetch status (it needs another NSX API call)
 | 
			
		||||
            device_status = nsx_utils.get_nsx_device_status(self.cluster,
 | 
			
		||||
                                                            nsx_id)
 | 
			
		||||
 | 
			
		||||
            # update status
 | 
			
		||||
            with context.session.begin(subtransactions=True):
 | 
			
		||||
                query = self._model_query(
 | 
			
		||||
                    context, networkgw_db.NetworkGatewayDevice).filter(
 | 
			
		||||
                        networkgw_db.NetworkGatewayDevice.id == neutron_id)
 | 
			
		||||
                query.update({'status': device_status},
 | 
			
		||||
                             synchronize_session=False)
 | 
			
		||||
            LOG.debug(_("Neutron gateway device: %(neutron_id)s; "
 | 
			
		||||
                        "NSX transport node identifier: %(nsx_id)s; "
 | 
			
		||||
                        "Operational status: %(status)s."),
 | 
			
		||||
                      {'neutron_id': neutron_id,
 | 
			
		||||
                       'nsx_id': nsx_id,
 | 
			
		||||
                       'status': device_status})
 | 
			
		||||
            return device_status
 | 
			
		||||
        except api_exc.NsxApiException:
 | 
			
		||||
            # Rollback gateway device on neutron database
 | 
			
		||||
            # As the NSX failure could be transient, we don't put the
 | 
			
		||||
            # gateway device in error status here.
 | 
			
		||||
            with excutils.save_and_reraise_exception():
 | 
			
		||||
                LOG.exception(_("Unable to update gateway device: %s on NSX "
 | 
			
		||||
                                "backend."), neutron_id)
 | 
			
		||||
                super(NsxPluginV2, self).update_gateway_device(
 | 
			
		||||
                    context, neutron_id, old_gateway_device_data)
 | 
			
		||||
        except n_exc.NotFound:
 | 
			
		||||
            # The gateway device was probably deleted in the backend.
 | 
			
		||||
            # The DB change should be rolled back and the status must
 | 
			
		||||
            # be put in error
 | 
			
		||||
            with excutils.save_and_reraise_exception():
 | 
			
		||||
                LOG.exception(_("Unable to update gateway device: %s on NSX "
 | 
			
		||||
                                "backend, as the gateway was not found on "
 | 
			
		||||
                                "the NSX backend."), neutron_id)
 | 
			
		||||
            with context.session.begin(subtransactions=True):
 | 
			
		||||
                super(NsxPluginV2, self).update_gateway_device(
 | 
			
		||||
                    context, neutron_id, old_gateway_device_data)
 | 
			
		||||
                query = self._model_query(
 | 
			
		||||
                    context, networkgw_db.NetworkGatewayDevice).filter(
 | 
			
		||||
                        networkgw_db.NetworkGatewayDevice.id == neutron_id)
 | 
			
		||||
                query.update({'status': networkgw_db.ERROR},
 | 
			
		||||
                             synchronize_session=False)
 | 
			
		||||
 | 
			
		||||
    def get_gateway_device(self, context, device_id, fields=None):
 | 
			
		||||
        # Get device from database
 | 
			
		||||
        gw_device = super(NsxPluginV2, self).get_gateway_device(
 | 
			
		||||
            context, device_id, fields, include_nsx_id=True)
 | 
			
		||||
        # Fetch status from NSX
 | 
			
		||||
        nsx_id = gw_device['nsx_id']
 | 
			
		||||
        device_status = nsx_utils.get_nsx_device_status(self.cluster, nsx_id)
 | 
			
		||||
        # TODO(salv-orlando): Asynchronous sync for gateway device status
 | 
			
		||||
        # Update status in database
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            query = self._model_query(
 | 
			
		||||
                context, networkgw_db.NetworkGatewayDevice).filter(
 | 
			
		||||
                    networkgw_db.NetworkGatewayDevice.id == device_id)
 | 
			
		||||
            query.update({'status': device_status},
 | 
			
		||||
                         synchronize_session=False)
 | 
			
		||||
        gw_device['status'] = device_status
 | 
			
		||||
        return gw_device
 | 
			
		||||
 | 
			
		||||
    def get_gateway_devices(self, context, filters=None, fields=None):
 | 
			
		||||
        # Get devices from database
 | 
			
		||||
        devices = super(NsxPluginV2, self).get_gateway_devices(
 | 
			
		||||
            context, filters, fields, include_nsx_id=True)
 | 
			
		||||
        # Fetch operational status from NVP, filter by tenant tag
 | 
			
		||||
        # TODO(salv-orlando): Asynchronous sync for gateway device status
 | 
			
		||||
        tenant_id = context.tenant_id if not context.is_admin else None
 | 
			
		||||
        nsx_statuses = nsx_utils.get_nsx_device_statuses(self.cluster,
 | 
			
		||||
                                                         tenant_id)
 | 
			
		||||
        # Update statuses in database
 | 
			
		||||
        with context.session.begin(subtransactions=True):
 | 
			
		||||
            for device in devices:
 | 
			
		||||
                new_status = nsx_statuses.get(device['nsx_id'])
 | 
			
		||||
                if new_status:
 | 
			
		||||
                    device['status'] = new_status
 | 
			
		||||
        return devices
 | 
			
		||||
 | 
			
		||||
    def create_gateway_device(self, context, gateway_device):
 | 
			
		||||
        # NOTE(salv-orlando): client-certificate will not be stored
 | 
			
		||||
        # in the database
 | 
			
		||||
        device_data = gateway_device[networkgw.DEVICE_RESOURCE_NAME]
 | 
			
		||||
        client_certificate = device_data.pop('client_certificate')
 | 
			
		||||
        gw_device = super(NsxPluginV2, self).create_gateway_device(
 | 
			
		||||
            context, gateway_device)
 | 
			
		||||
        # DB operation was successful, perform NSX operation
 | 
			
		||||
        gw_device['status'] = self.create_gateway_device_handler(
 | 
			
		||||
            context, gw_device, client_certificate)
 | 
			
		||||
        return gw_device
 | 
			
		||||
 | 
			
		||||
    def update_gateway_device(self, context, device_id,
 | 
			
		||||
                              gateway_device):
 | 
			
		||||
        # NOTE(salv-orlando): client-certificate will not be stored
 | 
			
		||||
        # in the database
 | 
			
		||||
        client_certificate = (
 | 
			
		||||
            gateway_device[networkgw.DEVICE_RESOURCE_NAME].pop(
 | 
			
		||||
                'client_certificate', None))
 | 
			
		||||
        # Retrive current state from DB in case a rollback should be needed
 | 
			
		||||
        old_gw_device_data = super(NsxPluginV2, self).get_gateway_device(
 | 
			
		||||
            context, device_id, include_nsx_id=True)
 | 
			
		||||
        gw_device = super(NsxPluginV2, self).update_gateway_device(
 | 
			
		||||
            context, device_id, gateway_device, include_nsx_id=True)
 | 
			
		||||
        # DB operation was successful, perform NSX operation
 | 
			
		||||
        gw_device['status'] = self.update_gateway_device_handler(
 | 
			
		||||
            context, gw_device, old_gw_device_data, client_certificate)
 | 
			
		||||
        gw_device.pop('nsx_id')
 | 
			
		||||
        return gw_device
 | 
			
		||||
 | 
			
		||||
    def delete_gateway_device(self, context, device_id):
 | 
			
		||||
        nsx_device_id = self._get_nsx_device_id(context, device_id)
 | 
			
		||||
        super(NsxPluginV2, self).delete_gateway_device(
 | 
			
		||||
            context, device_id)
 | 
			
		||||
        # DB operation was successful, peform NSX operation
 | 
			
		||||
        # TODO(salv-orlando): State consistency with neutron DB
 | 
			
		||||
        # should be ensured even in case of backend failures
 | 
			
		||||
        try:
 | 
			
		||||
            l2gwlib.delete_gateway_device(self.cluster, nsx_device_id)
 | 
			
		||||
        except n_exc.NotFound:
 | 
			
		||||
            LOG.warn(_("Removal of gateway device: %(neutron_id)s failed on "
 | 
			
		||||
                       "NSX backend (NSX id:%(nsx_id)s) because the NSX "
 | 
			
		||||
                       "resource was not found"),
 | 
			
		||||
                     {'neutron_id': device_id, 'nsx_id': nsx_device_id})
 | 
			
		||||
        except api_exc.NsxApiException:
 | 
			
		||||
            LOG.exception(_("Removal of gateway device: %(neutron_id)s "
 | 
			
		||||
                            "failed on NSX backend (NSX id:%(nsx_id)s). "
 | 
			
		||||
                            "Neutron and NSX states have diverged."),
 | 
			
		||||
                          {'neutron_id': device_id,
 | 
			
		||||
                           'nsx_id': nsx_device_id})
 | 
			
		||||
            # In this case a 500 should be returned
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def create_security_group(self, context, security_group, default_sg=False):
 | 
			
		||||
        """Create security group.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,11 +27,11 @@ from neutron import context
 | 
			
		||||
from neutron.db import api as db_api
 | 
			
		||||
from neutron.db import db_base_plugin_v2
 | 
			
		||||
from neutron import manager
 | 
			
		||||
from neutron.openstack.common import uuidutils
 | 
			
		||||
from neutron.plugins.vmware.api_client import exception as api_exc
 | 
			
		||||
from neutron.plugins.vmware.dbexts import networkgw_db
 | 
			
		||||
from neutron.plugins.vmware.extensions import networkgw
 | 
			
		||||
from neutron.plugins.vmware import nsxlib
 | 
			
		||||
from neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
 | 
			
		||||
from neutron import quota
 | 
			
		||||
from neutron.tests import base
 | 
			
		||||
from neutron.tests.unit import test_api_v2
 | 
			
		||||
@@ -69,7 +69,8 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
 | 
			
		||||
        super(NetworkGatewayExtensionTestCase, self).setUp()
 | 
			
		||||
        plugin = '%s.%s' % (networkgw.__name__,
 | 
			
		||||
                            networkgw.NetworkGatewayPluginBase.__name__)
 | 
			
		||||
        self._resource = networkgw.RESOURCE_NAME.replace('-', '_')
 | 
			
		||||
        self._gw_resource = networkgw.GATEWAY_RESOURCE_NAME
 | 
			
		||||
        self._dev_resource = networkgw.DEVICE_RESOURCE_NAME
 | 
			
		||||
 | 
			
		||||
        # Ensure existing ExtensionManager is not used
 | 
			
		||||
        extensions.PluginAwareExtensionManager._instance = None
 | 
			
		||||
@@ -100,67 +101,67 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_network_gateway_create(self):
 | 
			
		||||
        nw_gw_id = _uuid()
 | 
			
		||||
        data = {self._resource: {'name': 'nw-gw',
 | 
			
		||||
                                 'tenant_id': _uuid(),
 | 
			
		||||
                                 'devices': [{'id': _uuid(),
 | 
			
		||||
                                              'interface_name': 'xxx'}]}}
 | 
			
		||||
        return_value = data[self._resource].copy()
 | 
			
		||||
        data = {self._gw_resource: {'name': 'nw-gw',
 | 
			
		||||
                                    'tenant_id': _uuid(),
 | 
			
		||||
                                    'devices': [{'id': _uuid(),
 | 
			
		||||
                                                 'interface_name': 'xxx'}]}}
 | 
			
		||||
        return_value = data[self._gw_resource].copy()
 | 
			
		||||
        return_value.update({'id': nw_gw_id})
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.create_network_gateway.return_value = return_value
 | 
			
		||||
        res = self.api.post_json(_get_path(networkgw.COLLECTION_NAME), data)
 | 
			
		||||
        res = self.api.post_json(_get_path(networkgw.NETWORK_GATEWAYS), data)
 | 
			
		||||
        instance.create_network_gateway.assert_called_with(
 | 
			
		||||
            mock.ANY, network_gateway=data)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPCreated.code)
 | 
			
		||||
        self.assertIn(self._resource, res.json)
 | 
			
		||||
        nw_gw = res.json[self._resource]
 | 
			
		||||
        self.assertIn(self._gw_resource, res.json)
 | 
			
		||||
        nw_gw = res.json[self._gw_resource]
 | 
			
		||||
        self.assertEqual(nw_gw['id'], nw_gw_id)
 | 
			
		||||
 | 
			
		||||
    def _test_network_gateway_create_with_error(
 | 
			
		||||
        self, data, error_code=exc.HTTPBadRequest.code):
 | 
			
		||||
        res = self.api.post_json(_get_path(networkgw.COLLECTION_NAME), data,
 | 
			
		||||
        res = self.api.post_json(_get_path(networkgw.NETWORK_GATEWAYS), data,
 | 
			
		||||
                                 expect_errors=True)
 | 
			
		||||
        self.assertEqual(res.status_int, error_code)
 | 
			
		||||
 | 
			
		||||
    def test_network_gateway_create_invalid_device_spec(self):
 | 
			
		||||
        data = {self._resource: {'name': 'nw-gw',
 | 
			
		||||
                                 'tenant_id': _uuid(),
 | 
			
		||||
                                 'devices': [{'id': _uuid(),
 | 
			
		||||
                                              'invalid': 'xxx'}]}}
 | 
			
		||||
        data = {self._gw_resource: {'name': 'nw-gw',
 | 
			
		||||
                                    'tenant_id': _uuid(),
 | 
			
		||||
                                    'devices': [{'id': _uuid(),
 | 
			
		||||
                                                 'invalid': 'xxx'}]}}
 | 
			
		||||
        self._test_network_gateway_create_with_error(data)
 | 
			
		||||
 | 
			
		||||
    def test_network_gateway_create_extra_attr_in_device_spec(self):
 | 
			
		||||
        data = {self._resource: {'name': 'nw-gw',
 | 
			
		||||
                                 'tenant_id': _uuid(),
 | 
			
		||||
                                 'devices': [{'id': _uuid(),
 | 
			
		||||
                                              'interface_name': 'xxx',
 | 
			
		||||
                                              'extra_attr': 'onetoomany'}]}}
 | 
			
		||||
        data = {self._gw_resource: {'name': 'nw-gw',
 | 
			
		||||
                                    'tenant_id': _uuid(),
 | 
			
		||||
                                    'devices':
 | 
			
		||||
                                    [{'id': _uuid(),
 | 
			
		||||
                                      'interface_name': 'xxx',
 | 
			
		||||
                                      'extra_attr': 'onetoomany'}]}}
 | 
			
		||||
        self._test_network_gateway_create_with_error(data)
 | 
			
		||||
 | 
			
		||||
    def test_network_gateway_update(self):
 | 
			
		||||
        nw_gw_name = 'updated'
 | 
			
		||||
        data = {self._resource: {'name': nw_gw_name}}
 | 
			
		||||
        data = {self._gw_resource: {'name': nw_gw_name}}
 | 
			
		||||
        nw_gw_id = _uuid()
 | 
			
		||||
        return_value = {'id': nw_gw_id,
 | 
			
		||||
                        'name': nw_gw_name}
 | 
			
		||||
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.update_network_gateway.return_value = return_value
 | 
			
		||||
        res = self.api.put_json(_get_path('%s/%s' % (networkgw.COLLECTION_NAME,
 | 
			
		||||
                                                     nw_gw_id)),
 | 
			
		||||
                                data)
 | 
			
		||||
        res = self.api.put_json(
 | 
			
		||||
            _get_path('%s/%s' % (networkgw.NETWORK_GATEWAYS, nw_gw_id)), data)
 | 
			
		||||
        instance.update_network_gateway.assert_called_with(
 | 
			
		||||
            mock.ANY, nw_gw_id, network_gateway=data)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPOk.code)
 | 
			
		||||
        self.assertIn(self._resource, res.json)
 | 
			
		||||
        nw_gw = res.json[self._resource]
 | 
			
		||||
        self.assertIn(self._gw_resource, res.json)
 | 
			
		||||
        nw_gw = res.json[self._gw_resource]
 | 
			
		||||
        self.assertEqual(nw_gw['id'], nw_gw_id)
 | 
			
		||||
        self.assertEqual(nw_gw['name'], nw_gw_name)
 | 
			
		||||
 | 
			
		||||
    def test_network_gateway_delete(self):
 | 
			
		||||
        nw_gw_id = _uuid()
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        res = self.api.delete(_get_path('%s/%s' % (networkgw.COLLECTION_NAME,
 | 
			
		||||
        res = self.api.delete(_get_path('%s/%s' % (networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                                   nw_gw_id)))
 | 
			
		||||
 | 
			
		||||
        instance.delete_network_gateway.assert_called_with(mock.ANY,
 | 
			
		||||
@@ -169,15 +170,15 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_network_gateway_get(self):
 | 
			
		||||
        nw_gw_id = _uuid()
 | 
			
		||||
        return_value = {self._resource: {'name': 'test',
 | 
			
		||||
                                         'devices':
 | 
			
		||||
                                         [{'id': _uuid(),
 | 
			
		||||
                                           'interface_name': 'xxx'}],
 | 
			
		||||
                                         'id': nw_gw_id}}
 | 
			
		||||
        return_value = {self._gw_resource: {'name': 'test',
 | 
			
		||||
                                            'devices':
 | 
			
		||||
                                            [{'id': _uuid(),
 | 
			
		||||
                                              'interface_name': 'xxx'}],
 | 
			
		||||
                                            'id': nw_gw_id}}
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.get_network_gateway.return_value = return_value
 | 
			
		||||
 | 
			
		||||
        res = self.api.get(_get_path('%s/%s' % (networkgw.COLLECTION_NAME,
 | 
			
		||||
        res = self.api.get(_get_path('%s/%s' % (networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                                nw_gw_id)))
 | 
			
		||||
 | 
			
		||||
        instance.get_network_gateway.assert_called_with(mock.ANY,
 | 
			
		||||
@@ -187,15 +188,15 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_network_gateway_list(self):
 | 
			
		||||
        nw_gw_id = _uuid()
 | 
			
		||||
        return_value = [{self._resource: {'name': 'test',
 | 
			
		||||
                                          'devices':
 | 
			
		||||
                                          [{'id': _uuid(),
 | 
			
		||||
                                            'interface_name': 'xxx'}],
 | 
			
		||||
                                          'id': nw_gw_id}}]
 | 
			
		||||
        return_value = [{self._gw_resource: {'name': 'test',
 | 
			
		||||
                                             'devices':
 | 
			
		||||
                                             [{'id': _uuid(),
 | 
			
		||||
                                               'interface_name': 'xxx'}],
 | 
			
		||||
                                             'id': nw_gw_id}}]
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.get_network_gateways.return_value = return_value
 | 
			
		||||
 | 
			
		||||
        res = self.api.get(_get_path(networkgw.COLLECTION_NAME))
 | 
			
		||||
        res = self.api.get(_get_path(networkgw.NETWORK_GATEWAYS))
 | 
			
		||||
 | 
			
		||||
        instance.get_network_gateways.assert_called_with(mock.ANY,
 | 
			
		||||
                                                         fields=mock.ANY,
 | 
			
		||||
@@ -216,7 +217,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.connect_network.return_value = return_value
 | 
			
		||||
        res = self.api.put_json(_get_path('%s/%s/connect_network' %
 | 
			
		||||
                                          (networkgw.COLLECTION_NAME,
 | 
			
		||||
                                          (networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                           nw_gw_id)),
 | 
			
		||||
                                mapping_data)
 | 
			
		||||
        instance.connect_network.assert_called_with(mock.ANY,
 | 
			
		||||
@@ -233,7 +234,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
 | 
			
		||||
        mapping_data = {'network_id': nw_id}
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        res = self.api.put_json(_get_path('%s/%s/disconnect_network' %
 | 
			
		||||
                                          (networkgw.COLLECTION_NAME,
 | 
			
		||||
                                          (networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                           nw_gw_id)),
 | 
			
		||||
                                mapping_data)
 | 
			
		||||
        instance.disconnect_network.assert_called_with(mock.ANY,
 | 
			
		||||
@@ -241,6 +242,116 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
 | 
			
		||||
                                                       mapping_data)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPOk.code)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_get(self):
 | 
			
		||||
        gw_dev_id = _uuid()
 | 
			
		||||
        return_value = {self._dev_resource: {'name': 'test',
 | 
			
		||||
                                             'connector_type': 'stt',
 | 
			
		||||
                                             'connector_ip': '1.1.1.1',
 | 
			
		||||
                                             'id': gw_dev_id}}
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.get_gateway_device.return_value = return_value
 | 
			
		||||
 | 
			
		||||
        res = self.api.get(_get_path('%s/%s' % (networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                                                gw_dev_id)))
 | 
			
		||||
 | 
			
		||||
        instance.get_gateway_device.assert_called_with(mock.ANY,
 | 
			
		||||
                                                       gw_dev_id,
 | 
			
		||||
                                                       fields=mock.ANY)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPOk.code)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_list(self):
 | 
			
		||||
        gw_dev_id = _uuid()
 | 
			
		||||
        return_value = [{self._dev_resource: {'name': 'test',
 | 
			
		||||
                                              'connector_type': 'stt',
 | 
			
		||||
                                              'connector_ip': '1.1.1.1',
 | 
			
		||||
                                              'id': gw_dev_id}}]
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.get_gateway_devices.return_value = return_value
 | 
			
		||||
 | 
			
		||||
        res = self.api.get(_get_path(networkgw.GATEWAY_DEVICES))
 | 
			
		||||
 | 
			
		||||
        instance.get_gateway_devices.assert_called_with(mock.ANY,
 | 
			
		||||
                                                        fields=mock.ANY,
 | 
			
		||||
                                                        filters=mock.ANY)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPOk.code)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_create(self):
 | 
			
		||||
        gw_dev_id = _uuid()
 | 
			
		||||
        data = {self._dev_resource: {'name': 'test-dev',
 | 
			
		||||
                                     'tenant_id': _uuid(),
 | 
			
		||||
                                     'client_certificate': 'xyz',
 | 
			
		||||
                                     'connector_type': 'stt',
 | 
			
		||||
                                     'connector_ip': '1.1.1.1'}}
 | 
			
		||||
        return_value = data[self._dev_resource].copy()
 | 
			
		||||
        return_value.update({'id': gw_dev_id})
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.create_gateway_device.return_value = return_value
 | 
			
		||||
        res = self.api.post_json(_get_path(networkgw.GATEWAY_DEVICES), data)
 | 
			
		||||
        instance.create_gateway_device.assert_called_with(
 | 
			
		||||
            mock.ANY, gateway_device=data)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPCreated.code)
 | 
			
		||||
        self.assertIn(self._dev_resource, res.json)
 | 
			
		||||
        gw_dev = res.json[self._dev_resource]
 | 
			
		||||
        self.assertEqual(gw_dev['id'], gw_dev_id)
 | 
			
		||||
 | 
			
		||||
    def _test_gateway_device_create_with_error(
 | 
			
		||||
        self, data, error_code=exc.HTTPBadRequest.code):
 | 
			
		||||
        res = self.api.post_json(_get_path(networkgw.GATEWAY_DEVICES), data,
 | 
			
		||||
                                 expect_errors=True)
 | 
			
		||||
        self.assertEqual(res.status_int, error_code)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_create_invalid_connector_type(self):
 | 
			
		||||
        data = {self._gw_resource: {'name': 'test-dev',
 | 
			
		||||
                                    'client_certificate': 'xyz',
 | 
			
		||||
                                    'tenant_id': _uuid(),
 | 
			
		||||
                                    'connector_type': 'invalid',
 | 
			
		||||
                                    'connector_ip': '1.1.1.1'}}
 | 
			
		||||
        self._test_gateway_device_create_with_error(data)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_create_invalid_connector_ip(self):
 | 
			
		||||
        data = {self._gw_resource: {'name': 'test-dev',
 | 
			
		||||
                                    'client_certificate': 'xyz',
 | 
			
		||||
                                    'tenant_id': _uuid(),
 | 
			
		||||
                                    'connector_type': 'stt',
 | 
			
		||||
                                    'connector_ip': 'invalid'}}
 | 
			
		||||
        self._test_gateway_device_create_with_error(data)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_create_extra_attr_in_device_spec(self):
 | 
			
		||||
        data = {self._gw_resource: {'name': 'test-dev',
 | 
			
		||||
                                    'client_certificate': 'xyz',
 | 
			
		||||
                                    'tenant_id': _uuid(),
 | 
			
		||||
                                    'alien_attribute': 'E.T.',
 | 
			
		||||
                                    'connector_type': 'stt',
 | 
			
		||||
                                    'connector_ip': '1.1.1.1'}}
 | 
			
		||||
        self._test_gateway_device_create_with_error(data)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_update(self):
 | 
			
		||||
        gw_dev_name = 'updated'
 | 
			
		||||
        data = {self._dev_resource: {'name': gw_dev_name}}
 | 
			
		||||
        gw_dev_id = _uuid()
 | 
			
		||||
        return_value = {'id': gw_dev_id,
 | 
			
		||||
                        'name': gw_dev_name}
 | 
			
		||||
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        instance.update_gateway_device.return_value = return_value
 | 
			
		||||
        res = self.api.put_json(
 | 
			
		||||
            _get_path('%s/%s' % (networkgw.GATEWAY_DEVICES, gw_dev_id)), data)
 | 
			
		||||
        instance.update_gateway_device.assert_called_with(
 | 
			
		||||
            mock.ANY, gw_dev_id, gateway_device=data)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPOk.code)
 | 
			
		||||
        self.assertIn(self._dev_resource, res.json)
 | 
			
		||||
        gw_dev = res.json[self._dev_resource]
 | 
			
		||||
        self.assertEqual(gw_dev['id'], gw_dev_id)
 | 
			
		||||
        self.assertEqual(gw_dev['name'], gw_dev_name)
 | 
			
		||||
 | 
			
		||||
    def test_gateway_device_delete(self):
 | 
			
		||||
        gw_dev_id = _uuid()
 | 
			
		||||
        instance = self.plugin.return_value
 | 
			
		||||
        res = self.api.delete(_get_path('%s/%s' % (networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                                                   gw_dev_id)))
 | 
			
		||||
        instance.delete_gateway_device.assert_called_with(mock.ANY, gw_dev_id)
 | 
			
		||||
        self.assertEqual(res.status_int, exc.HTTPNoContent.code)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
    """Unit tests for Network Gateway DB support."""
 | 
			
		||||
@@ -250,21 +361,23 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
            plugin = '%s.%s' % (__name__, TestNetworkGatewayPlugin.__name__)
 | 
			
		||||
        if not ext_mgr:
 | 
			
		||||
            ext_mgr = TestExtensionManager()
 | 
			
		||||
        self.resource = networkgw.RESOURCE_NAME.replace('-', '_')
 | 
			
		||||
        self.gw_resource = networkgw.GATEWAY_RESOURCE_NAME
 | 
			
		||||
        self.dev_resource = networkgw.DEVICE_RESOURCE_NAME
 | 
			
		||||
 | 
			
		||||
        super(NetworkGatewayDbTestCase, self).setUp(plugin=plugin,
 | 
			
		||||
                                                    ext_mgr=ext_mgr)
 | 
			
		||||
 | 
			
		||||
    def _create_network_gateway(self, fmt, tenant_id, name=None,
 | 
			
		||||
                                devices=None, arg_list=None, **kwargs):
 | 
			
		||||
        data = {self.resource: {'tenant_id': tenant_id,
 | 
			
		||||
                                'devices': devices}}
 | 
			
		||||
        data = {self.gw_resource: {'tenant_id': tenant_id,
 | 
			
		||||
                                   'devices': devices}}
 | 
			
		||||
        if name:
 | 
			
		||||
            data[self.resource]['name'] = name
 | 
			
		||||
            data[self.gw_resource]['name'] = name
 | 
			
		||||
        for arg in arg_list or ():
 | 
			
		||||
            # Arg must be present and not empty
 | 
			
		||||
            if arg in kwargs and kwargs[arg]:
 | 
			
		||||
                data[self.resource][arg] = kwargs[arg]
 | 
			
		||||
        nw_gw_req = self.new_create_request(networkgw.COLLECTION_NAME,
 | 
			
		||||
                data[self.gw_resource][arg] = kwargs[arg]
 | 
			
		||||
        nw_gw_req = self.new_create_request(networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                            data, fmt)
 | 
			
		||||
        if (kwargs.get('set_context') and tenant_id):
 | 
			
		||||
            # create a specific auth context for this request
 | 
			
		||||
@@ -275,16 +388,89 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
    @contextlib.contextmanager
 | 
			
		||||
    def _network_gateway(self, name='gw1', devices=None,
 | 
			
		||||
                         fmt='json', tenant_id=_uuid()):
 | 
			
		||||
        device = None
 | 
			
		||||
        if not devices:
 | 
			
		||||
            devices = [{'id': _uuid(), 'interface_name': 'xyz'}]
 | 
			
		||||
            device_res = self._create_gateway_device(
 | 
			
		||||
                fmt, tenant_id, 'stt', '1.1.1.1', 'xxxxxx',
 | 
			
		||||
                name='whatever')
 | 
			
		||||
            if device_res.status_int >= 400:
 | 
			
		||||
                raise exc.HTTPClientError(code=device_res.status_int)
 | 
			
		||||
            device = self.deserialize(fmt, device_res)
 | 
			
		||||
            devices = [{'id': device[self.dev_resource]['id'],
 | 
			
		||||
                        'interface_name': 'xyz'}]
 | 
			
		||||
 | 
			
		||||
        res = self._create_network_gateway(fmt, tenant_id, name=name,
 | 
			
		||||
                                           devices=devices)
 | 
			
		||||
        network_gateway = self.deserialize(fmt, res)
 | 
			
		||||
        if res.status_int >= 400:
 | 
			
		||||
            raise exc.HTTPClientError(code=res.status_int)
 | 
			
		||||
        network_gateway = self.deserialize(fmt, res)
 | 
			
		||||
        yield network_gateway
 | 
			
		||||
        self._delete(networkgw.COLLECTION_NAME,
 | 
			
		||||
                     network_gateway[self.resource]['id'])
 | 
			
		||||
 | 
			
		||||
        self._delete(networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                     network_gateway[self.gw_resource]['id'])
 | 
			
		||||
        if device:
 | 
			
		||||
            self._delete(networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                         device[self.dev_resource]['id'])
 | 
			
		||||
 | 
			
		||||
    def _create_gateway_device(self, fmt, tenant_id,
 | 
			
		||||
                               connector_type, connector_ip,
 | 
			
		||||
                               client_certificate, name=None,
 | 
			
		||||
                               set_context=False):
 | 
			
		||||
        data = {self.dev_resource: {'tenant_id': tenant_id,
 | 
			
		||||
                                    'connector_type': connector_type,
 | 
			
		||||
                                    'connector_ip': connector_ip,
 | 
			
		||||
                                    'client_certificate': client_certificate}}
 | 
			
		||||
        if name:
 | 
			
		||||
            data[self.dev_resource]['name'] = name
 | 
			
		||||
        gw_dev_req = self.new_create_request(networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                                             data, fmt)
 | 
			
		||||
        if (set_context and tenant_id):
 | 
			
		||||
            # create a specific auth context for this request
 | 
			
		||||
            gw_dev_req.environ['neutron.context'] = context.Context(
 | 
			
		||||
                '', tenant_id)
 | 
			
		||||
        return gw_dev_req.get_response(self.ext_api)
 | 
			
		||||
 | 
			
		||||
    def _update_gateway_device(self, fmt, gateway_device_id,
 | 
			
		||||
                               connector_type=None, connector_ip=None,
 | 
			
		||||
                               client_certificate=None, name=None,
 | 
			
		||||
                               set_context=False, tenant_id=None):
 | 
			
		||||
        data = {self.dev_resource: {}}
 | 
			
		||||
        if connector_type:
 | 
			
		||||
            data[self.dev_resource]['connector_type'] = connector_type
 | 
			
		||||
        if connector_ip:
 | 
			
		||||
            data[self.dev_resource]['connector_ip'] = connector_ip
 | 
			
		||||
        if client_certificate:
 | 
			
		||||
            data[self.dev_resource]['client_certificate'] = client_certificate
 | 
			
		||||
        if name:
 | 
			
		||||
            data[self.dev_resource]['name'] = name
 | 
			
		||||
        gw_dev_req = self.new_update_request(networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                                             data, gateway_device_id, fmt)
 | 
			
		||||
        if (set_context and tenant_id):
 | 
			
		||||
            # create a specific auth context for this request
 | 
			
		||||
            gw_dev_req.environ['neutron.context'] = context.Context(
 | 
			
		||||
                '', tenant_id)
 | 
			
		||||
        return gw_dev_req.get_response(self.ext_api)
 | 
			
		||||
 | 
			
		||||
    @contextlib.contextmanager
 | 
			
		||||
    def _gateway_device(self, name='gw_dev',
 | 
			
		||||
                        connector_type='stt',
 | 
			
		||||
                        connector_ip='1.1.1.1',
 | 
			
		||||
                        client_certificate='xxxxxxxxxxxxxxx',
 | 
			
		||||
                        fmt='json', tenant_id=_uuid()):
 | 
			
		||||
        res = self._create_gateway_device(
 | 
			
		||||
            fmt,
 | 
			
		||||
            tenant_id,
 | 
			
		||||
            connector_type=connector_type,
 | 
			
		||||
            connector_ip=connector_ip,
 | 
			
		||||
            client_certificate=client_certificate,
 | 
			
		||||
            name=name)
 | 
			
		||||
        if res.status_int >= 400:
 | 
			
		||||
            raise exc.HTTPClientError(code=res.status_int)
 | 
			
		||||
        gateway_device = self.deserialize(fmt, res)
 | 
			
		||||
        yield gateway_device
 | 
			
		||||
 | 
			
		||||
        self._delete(networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                     gateway_device[self.dev_resource]['id'])
 | 
			
		||||
 | 
			
		||||
    def _gateway_action(self, action, network_gateway_id, network_id,
 | 
			
		||||
                        segmentation_type, segmentation_id=None,
 | 
			
		||||
@@ -294,7 +480,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        if segmentation_id:
 | 
			
		||||
            connection_data['segmentation_id'] = segmentation_id
 | 
			
		||||
 | 
			
		||||
        req = self.new_action_request(networkgw.COLLECTION_NAME,
 | 
			
		||||
        req = self.new_action_request(networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                      connection_data,
 | 
			
		||||
                                      network_gateway_id,
 | 
			
		||||
                                      "%s_network" % action)
 | 
			
		||||
@@ -307,7 +493,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net:
 | 
			
		||||
                body = self._gateway_action('connect',
 | 
			
		||||
                                            gw[self.resource]['id'],
 | 
			
		||||
                                            gw[self.gw_resource]['id'],
 | 
			
		||||
                                            net['network']['id'],
 | 
			
		||||
                                            segmentation_type,
 | 
			
		||||
                                            segmentation_id)
 | 
			
		||||
@@ -320,10 +506,10 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
                gw_port_id = connection_info['port_id']
 | 
			
		||||
                port_body = self._show('ports', gw_port_id)
 | 
			
		||||
                self.assertEqual(port_body['port']['device_id'],
 | 
			
		||||
                                 gw[self.resource]['id'])
 | 
			
		||||
                                 gw[self.gw_resource]['id'])
 | 
			
		||||
                # Clean up - otherwise delete will fail
 | 
			
		||||
                body = self._gateway_action('disconnect',
 | 
			
		||||
                                            gw[self.resource]['id'],
 | 
			
		||||
                                            gw[self.gw_resource]['id'],
 | 
			
		||||
                                            net['network']['id'],
 | 
			
		||||
                                            segmentation_type,
 | 
			
		||||
                                            segmentation_id)
 | 
			
		||||
@@ -332,90 +518,98 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
                                  expected_code=exc.HTTPNotFound.code)
 | 
			
		||||
 | 
			
		||||
    def test_create_network_gateway(self):
 | 
			
		||||
        name = 'test-gw'
 | 
			
		||||
        devices = [{'id': _uuid(), 'interface_name': 'xxx'},
 | 
			
		||||
                   {'id': _uuid(), 'interface_name': 'yyy'}]
 | 
			
		||||
        keys = [('devices', devices), ('name', name)]
 | 
			
		||||
        with self._network_gateway(name=name, devices=devices) as gw:
 | 
			
		||||
            for k, v in keys:
 | 
			
		||||
                self.assertEqual(gw[self.resource][k], v)
 | 
			
		||||
        with contextlib.nested(
 | 
			
		||||
            self._gateway_device(name='dev_1'),
 | 
			
		||||
            self._gateway_device(name='dev_2')) as (dev_1, dev_2):
 | 
			
		||||
            name = 'test-gw'
 | 
			
		||||
            dev_1_id = dev_1[self.dev_resource]['id']
 | 
			
		||||
            dev_2_id = dev_2[self.dev_resource]['id']
 | 
			
		||||
            devices = [{'id': dev_1_id, 'interface_name': 'xxx'},
 | 
			
		||||
                       {'id': dev_2_id, 'interface_name': 'yyy'}]
 | 
			
		||||
            keys = [('devices', devices), ('name', name)]
 | 
			
		||||
            with self._network_gateway(name=name, devices=devices) as gw:
 | 
			
		||||
                for k, v in keys:
 | 
			
		||||
                    self.assertEqual(gw[self.gw_resource][k], v)
 | 
			
		||||
 | 
			
		||||
    def test_create_network_gateway_no_interface_name(self):
 | 
			
		||||
        name = 'test-gw'
 | 
			
		||||
        devices = [{'id': _uuid()}]
 | 
			
		||||
        exp_devices = devices
 | 
			
		||||
        exp_devices[0]['interface_name'] = 'breth0'
 | 
			
		||||
        keys = [('devices', exp_devices), ('name', name)]
 | 
			
		||||
        with self._network_gateway(name=name, devices=devices) as gw:
 | 
			
		||||
            for k, v in keys:
 | 
			
		||||
                self.assertEqual(gw[self.resource][k], v)
 | 
			
		||||
 | 
			
		||||
    def _test_delete_network_gateway(self, exp_gw_count=0):
 | 
			
		||||
        name = 'test-gw'
 | 
			
		||||
        devices = [{'id': _uuid(), 'interface_name': 'xxx'},
 | 
			
		||||
                   {'id': _uuid(), 'interface_name': 'yyy'}]
 | 
			
		||||
        with self._network_gateway(name=name, devices=devices):
 | 
			
		||||
            # Nothing to do here - just let the gateway go
 | 
			
		||||
            pass
 | 
			
		||||
        # Verify nothing left on db
 | 
			
		||||
        session = db_api.get_session()
 | 
			
		||||
        gw_query = session.query(networkgw_db.NetworkGateway)
 | 
			
		||||
        dev_query = session.query(networkgw_db.NetworkGatewayDevice)
 | 
			
		||||
        self.assertEqual(exp_gw_count, gw_query.count())
 | 
			
		||||
        self.assertEqual(0, dev_query.count())
 | 
			
		||||
        with self._gateway_device() as dev:
 | 
			
		||||
            name = 'test-gw'
 | 
			
		||||
            devices = [{'id': dev[self.dev_resource]['id']}]
 | 
			
		||||
            exp_devices = devices
 | 
			
		||||
            exp_devices[0]['interface_name'] = 'breth0'
 | 
			
		||||
            keys = [('devices', exp_devices), ('name', name)]
 | 
			
		||||
            with self._network_gateway(name=name, devices=devices) as gw:
 | 
			
		||||
                for k, v in keys:
 | 
			
		||||
                    self.assertEqual(gw[self.gw_resource][k], v)
 | 
			
		||||
 | 
			
		||||
    def test_delete_network_gateway(self):
 | 
			
		||||
        self._test_delete_network_gateway()
 | 
			
		||||
        with self._gateway_device() as dev:
 | 
			
		||||
            name = 'test-gw'
 | 
			
		||||
            device_id = dev[self.dev_resource]['id']
 | 
			
		||||
            devices = [{'id': device_id,
 | 
			
		||||
                        'interface_name': 'xxx'}]
 | 
			
		||||
            with self._network_gateway(name=name, devices=devices) as gw:
 | 
			
		||||
                # Nothing to do here - just let the gateway go
 | 
			
		||||
                gw_id = gw[self.gw_resource]['id']
 | 
			
		||||
        # Verify nothing left on db
 | 
			
		||||
        session = db_api.get_session()
 | 
			
		||||
        dev_query = session.query(
 | 
			
		||||
            networkgw_db.NetworkGatewayDevice).filter(
 | 
			
		||||
                networkgw_db.NetworkGatewayDevice.id == device_id)
 | 
			
		||||
        self.assertIsNone(dev_query.first())
 | 
			
		||||
        gw_query = session.query(networkgw_db.NetworkGateway).filter(
 | 
			
		||||
            networkgw_db.NetworkGateway.id == gw_id)
 | 
			
		||||
        self.assertIsNone(gw_query.first())
 | 
			
		||||
 | 
			
		||||
    def test_update_network_gateway(self):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            data = {self.resource: {'name': 'new_name'}}
 | 
			
		||||
            req = self.new_update_request(networkgw.COLLECTION_NAME,
 | 
			
		||||
            data = {self.gw_resource: {'name': 'new_name'}}
 | 
			
		||||
            req = self.new_update_request(networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                          data,
 | 
			
		||||
                                          gw[self.resource]['id'])
 | 
			
		||||
                                          gw[self.gw_resource]['id'])
 | 
			
		||||
            res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
            self.assertEqual(res[self.resource]['name'],
 | 
			
		||||
                             data[self.resource]['name'])
 | 
			
		||||
            self.assertEqual(res[self.gw_resource]['name'],
 | 
			
		||||
                             data[self.gw_resource]['name'])
 | 
			
		||||
 | 
			
		||||
    def test_get_network_gateway(self):
 | 
			
		||||
        with self._network_gateway(name='test-gw') as gw:
 | 
			
		||||
            req = self.new_show_request(networkgw.COLLECTION_NAME,
 | 
			
		||||
                                        gw[self.resource]['id'])
 | 
			
		||||
            req = self.new_show_request(networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                        gw[self.gw_resource]['id'])
 | 
			
		||||
            res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
            self.assertEqual(res[self.resource]['name'],
 | 
			
		||||
                             gw[self.resource]['name'])
 | 
			
		||||
            self.assertEqual(res[self.gw_resource]['name'],
 | 
			
		||||
                             gw[self.gw_resource]['name'])
 | 
			
		||||
 | 
			
		||||
    def test_list_network_gateways(self):
 | 
			
		||||
        with self._network_gateway(name='test-gw-1') as gw1:
 | 
			
		||||
            with self._network_gateway(name='test_gw_2') as gw2:
 | 
			
		||||
                req = self.new_list_request(networkgw.COLLECTION_NAME)
 | 
			
		||||
                req = self.new_list_request(networkgw.NETWORK_GATEWAYS)
 | 
			
		||||
                res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
                key = self.resource + 's'
 | 
			
		||||
                key = self.gw_resource + 's'
 | 
			
		||||
                self.assertEqual(len(res[key]), 2)
 | 
			
		||||
                self.assertEqual(res[key][0]['name'],
 | 
			
		||||
                                 gw1[self.resource]['name'])
 | 
			
		||||
                                 gw1[self.gw_resource]['name'])
 | 
			
		||||
                self.assertEqual(res[key][1]['name'],
 | 
			
		||||
                                 gw2[self.resource]['name'])
 | 
			
		||||
                                 gw2[self.gw_resource]['name'])
 | 
			
		||||
 | 
			
		||||
    def _test_list_network_gateway_with_multiple_connections(
 | 
			
		||||
        self, expected_gateways=1):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net_1:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 777)
 | 
			
		||||
                req = self.new_list_request(networkgw.COLLECTION_NAME)
 | 
			
		||||
                req = self.new_list_request(networkgw.NETWORK_GATEWAYS)
 | 
			
		||||
                res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
                key = self.resource + 's'
 | 
			
		||||
                key = self.gw_resource + 's'
 | 
			
		||||
                self.assertEqual(len(res[key]), expected_gateways)
 | 
			
		||||
                for item in res[key]:
 | 
			
		||||
                    self.assertIn('ports', item)
 | 
			
		||||
                    if item['id'] == gw[self.resource]['id']:
 | 
			
		||||
                    if item['id'] == gw[self.gw_resource]['id']:
 | 
			
		||||
                        gw_ports = item['ports']
 | 
			
		||||
                self.assertEqual(len(gw_ports), 2)
 | 
			
		||||
                segmentation_ids = [555, 777]
 | 
			
		||||
@@ -425,11 +619,11 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
                    segmentation_ids.remove(gw_port['segmentation_id'])
 | 
			
		||||
                # Required cleanup
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 777)
 | 
			
		||||
 | 
			
		||||
@@ -449,19 +643,19 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net_1:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 777)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 777)
 | 
			
		||||
 | 
			
		||||
@@ -470,19 +664,19 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
            with self._network_gateway() as gw_2:
 | 
			
		||||
                with self.network() as net_1:
 | 
			
		||||
                    self._gateway_action('connect',
 | 
			
		||||
                                         gw_1[self.resource]['id'],
 | 
			
		||||
                                         gw_1[self.gw_resource]['id'],
 | 
			
		||||
                                         net_1['network']['id'],
 | 
			
		||||
                                         'vlan', 555)
 | 
			
		||||
                    self._gateway_action('connect',
 | 
			
		||||
                                         gw_2[self.resource]['id'],
 | 
			
		||||
                                         gw_2[self.gw_resource]['id'],
 | 
			
		||||
                                         net_1['network']['id'],
 | 
			
		||||
                                         'vlan', 555)
 | 
			
		||||
                    self._gateway_action('disconnect',
 | 
			
		||||
                                         gw_1[self.resource]['id'],
 | 
			
		||||
                                         gw_1[self.gw_resource]['id'],
 | 
			
		||||
                                         net_1['network']['id'],
 | 
			
		||||
                                         'vlan', 555)
 | 
			
		||||
                    self._gateway_action('disconnect',
 | 
			
		||||
                                         gw_2[self.resource]['id'],
 | 
			
		||||
                                         gw_2[self.gw_resource]['id'],
 | 
			
		||||
                                         net_1['network']['id'],
 | 
			
		||||
                                         'vlan', 555)
 | 
			
		||||
 | 
			
		||||
@@ -490,25 +684,25 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net_1:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                with self.network() as net_2:
 | 
			
		||||
                    self._gateway_action('connect',
 | 
			
		||||
                                         gw[self.resource]['id'],
 | 
			
		||||
                                         gw[self.gw_resource]['id'],
 | 
			
		||||
                                         net_2['network']['id'],
 | 
			
		||||
                                         'vlan', 555,
 | 
			
		||||
                                         expected_status=exc.HTTPConflict.code)
 | 
			
		||||
                # Clean up - otherwise delete will fail
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
 | 
			
		||||
    def test_connect_invalid_network_returns_400(self):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     'hohoho',
 | 
			
		||||
                                     'vlan', 555,
 | 
			
		||||
                                     expected_status=exc.HTTPBadRequest.code)
 | 
			
		||||
@@ -516,7 +710,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
    def test_connect_unspecified_network_returns_400(self):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     None,
 | 
			
		||||
                                     'vlan', 555,
 | 
			
		||||
                                     expected_status=exc.HTTPBadRequest.code)
 | 
			
		||||
@@ -525,25 +719,25 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net_1:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 777)
 | 
			
		||||
                # This should raise
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan',
 | 
			
		||||
                                     expected_status=exc.HTTPConflict.code)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 777)
 | 
			
		||||
 | 
			
		||||
@@ -551,7 +745,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net_1:
 | 
			
		||||
                body = self._gateway_action('connect',
 | 
			
		||||
                                            gw[self.resource]['id'],
 | 
			
		||||
                                            gw[self.gw_resource]['id'],
 | 
			
		||||
                                            net_1['network']['id'],
 | 
			
		||||
                                            'vlan', 555)
 | 
			
		||||
                # fetch port id and try to delete it
 | 
			
		||||
@@ -559,7 +753,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
                self._delete('ports', gw_port_id,
 | 
			
		||||
                             expected_code=exc.HTTPConflict.code)
 | 
			
		||||
                body = self._gateway_action('disconnect',
 | 
			
		||||
                                            gw[self.resource]['id'],
 | 
			
		||||
                                            gw[self.gw_resource]['id'],
 | 
			
		||||
                                            net_1['network']['id'],
 | 
			
		||||
                                            'vlan', 555)
 | 
			
		||||
 | 
			
		||||
@@ -567,14 +761,14 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net_1:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'flat')
 | 
			
		||||
                self._delete(networkgw.COLLECTION_NAME,
 | 
			
		||||
                             gw[self.resource]['id'],
 | 
			
		||||
                self._delete(networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                             gw[self.gw_resource]['id'],
 | 
			
		||||
                             expected_code=exc.HTTPConflict.code)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'flat')
 | 
			
		||||
 | 
			
		||||
@@ -582,25 +776,99 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
 | 
			
		||||
        with self._network_gateway() as gw:
 | 
			
		||||
            with self.network() as net_1:
 | 
			
		||||
                self._gateway_action('connect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 999,
 | 
			
		||||
                                     expected_status=exc.HTTPNotFound.code)
 | 
			
		||||
                self._gateway_action('disconnect',
 | 
			
		||||
                                     gw[self.resource]['id'],
 | 
			
		||||
                                     gw[self.gw_resource]['id'],
 | 
			
		||||
                                     net_1['network']['id'],
 | 
			
		||||
                                     'vlan', 555)
 | 
			
		||||
 | 
			
		||||
    def test_create_gateway_device(
 | 
			
		||||
        self, expected_status=networkgw_db.STATUS_UNKNOWN):
 | 
			
		||||
        with self._gateway_device(name='test-dev',
 | 
			
		||||
                                  connector_type='stt',
 | 
			
		||||
                                  connector_ip='1.1.1.1',
 | 
			
		||||
                                  client_certificate='xyz') as dev:
 | 
			
		||||
            self.assertEqual(dev[self.dev_resource]['name'], 'test-dev')
 | 
			
		||||
            self.assertEqual(dev[self.dev_resource]['connector_type'], 'stt')
 | 
			
		||||
            self.assertEqual(dev[self.dev_resource]['connector_ip'], '1.1.1.1')
 | 
			
		||||
            self.assertEqual(dev[self.dev_resource]['status'], expected_status)
 | 
			
		||||
 | 
			
		||||
    def test_get_gateway_device(
 | 
			
		||||
        self, expected_status=networkgw_db.STATUS_UNKNOWN):
 | 
			
		||||
        with self._gateway_device(name='test-dev',
 | 
			
		||||
                                  connector_type='stt',
 | 
			
		||||
                                  connector_ip='1.1.1.1',
 | 
			
		||||
                                  client_certificate='xyz') as dev:
 | 
			
		||||
            req = self.new_show_request(networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                                        dev[self.dev_resource]['id'])
 | 
			
		||||
            res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['name'], 'test-dev')
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['connector_type'], 'stt')
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['connector_ip'], '1.1.1.1')
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['status'], expected_status)
 | 
			
		||||
 | 
			
		||||
    def test_update_gateway_device(
 | 
			
		||||
        self, expected_status=networkgw_db.STATUS_UNKNOWN):
 | 
			
		||||
        with self._gateway_device(name='test-dev',
 | 
			
		||||
                                  connector_type='stt',
 | 
			
		||||
                                  connector_ip='1.1.1.1',
 | 
			
		||||
                                  client_certificate='xyz') as dev:
 | 
			
		||||
            self._update_gateway_device('json', dev[self.dev_resource]['id'],
 | 
			
		||||
                                        connector_type='stt',
 | 
			
		||||
                                        connector_ip='2.2.2.2',
 | 
			
		||||
                                        name='test-dev-upd')
 | 
			
		||||
            req = self.new_show_request(networkgw.GATEWAY_DEVICES,
 | 
			
		||||
                                        dev[self.dev_resource]['id'])
 | 
			
		||||
            res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['name'], 'test-dev-upd')
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['connector_type'], 'stt')
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['connector_ip'], '2.2.2.2')
 | 
			
		||||
        self.assertEqual(res[self.dev_resource]['status'], expected_status)
 | 
			
		||||
 | 
			
		||||
    def test_delete_gateway_device(self):
 | 
			
		||||
        with self._gateway_device(name='test-dev',
 | 
			
		||||
                                  connector_type='stt',
 | 
			
		||||
                                  connector_ip='1.1.1.1',
 | 
			
		||||
                                  client_certificate='xyz') as dev:
 | 
			
		||||
            # Nothing to do here - just note the device id
 | 
			
		||||
            dev_id = dev[self.dev_resource]['id']
 | 
			
		||||
        # Verify nothing left on db
 | 
			
		||||
        session = db_api.get_session()
 | 
			
		||||
        dev_query = session.query(networkgw_db.NetworkGatewayDevice)
 | 
			
		||||
        dev_query.filter(networkgw_db.NetworkGatewayDevice.id == dev_id)
 | 
			
		||||
        self.assertIsNone(dev_query.first())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestNetworkGateway(NsxPluginV2TestCase,
 | 
			
		||||
                         NetworkGatewayDbTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None):
 | 
			
		||||
        cfg.CONF.set_override('api_extensions_path', NSXEXT_PATH)
 | 
			
		||||
        # Mock l2gwlib calls for gateway devices since this resource is not
 | 
			
		||||
        # mocked through the fake NVP API client
 | 
			
		||||
        create_gw_dev_patcher = mock.patch.object(
 | 
			
		||||
            l2gwlib, 'create_gateway_device')
 | 
			
		||||
        update_gw_dev_patcher = mock.patch.object(
 | 
			
		||||
            l2gwlib, 'update_gateway_device')
 | 
			
		||||
        delete_gw_dev_patcher = mock.patch.object(
 | 
			
		||||
            l2gwlib, 'delete_gateway_device')
 | 
			
		||||
        get_gw_dev_status_patcher = mock.patch.object(
 | 
			
		||||
            l2gwlib, 'get_gateway_device_status')
 | 
			
		||||
        mock_create_gw_dev = create_gw_dev_patcher.start()
 | 
			
		||||
        mock_create_gw_dev.return_value = {'uuid': 'callejon'}
 | 
			
		||||
        update_gw_dev_patcher.start()
 | 
			
		||||
        delete_gw_dev_patcher.start()
 | 
			
		||||
        self.mock_get_gw_dev_status = get_gw_dev_status_patcher.start()
 | 
			
		||||
 | 
			
		||||
        self.addCleanup(mock.patch.stopall)
 | 
			
		||||
        super(TestNetworkGateway,
 | 
			
		||||
              self).setUp(plugin=plugin, ext_mgr=ext_mgr)
 | 
			
		||||
 | 
			
		||||
@@ -608,15 +876,15 @@ class TestNetworkGateway(NsxPluginV2TestCase,
 | 
			
		||||
        name = 'this_is_a_gateway_whose_name_is_longer_than_40_chars'
 | 
			
		||||
        with self._network_gateway(name=name) as nw_gw:
 | 
			
		||||
            # Assert Neutron name is not truncated
 | 
			
		||||
            self.assertEqual(nw_gw[self.resource]['name'], name)
 | 
			
		||||
            self.assertEqual(nw_gw[self.gw_resource]['name'], name)
 | 
			
		||||
 | 
			
		||||
    def test_update_network_gateway_with_name_calls_backend(self):
 | 
			
		||||
        with mock.patch.object(
 | 
			
		||||
            nsxlib.l2gateway, 'update_l2_gw_service') as mock_update_gw:
 | 
			
		||||
            with self._network_gateway(name='cavani') as nw_gw:
 | 
			
		||||
                nw_gw_id = nw_gw[self.resource]['id']
 | 
			
		||||
                self._update(networkgw.COLLECTION_NAME, nw_gw_id,
 | 
			
		||||
                             {self.resource: {'name': 'higuain'}})
 | 
			
		||||
                nw_gw_id = nw_gw[self.gw_resource]['id']
 | 
			
		||||
                self._update(networkgw.NETWORK_GATEWAYS, nw_gw_id,
 | 
			
		||||
                             {self.gw_resource: {'name': 'higuain'}})
 | 
			
		||||
                mock_update_gw.assert_called_once_with(
 | 
			
		||||
                    mock.ANY, nw_gw_id, 'higuain')
 | 
			
		||||
 | 
			
		||||
@@ -624,22 +892,22 @@ class TestNetworkGateway(NsxPluginV2TestCase,
 | 
			
		||||
        with mock.patch.object(
 | 
			
		||||
            nsxlib.l2gateway, 'update_l2_gw_service') as mock_update_gw:
 | 
			
		||||
            with self._network_gateway(name='something') as nw_gw:
 | 
			
		||||
                nw_gw_id = nw_gw[self.resource]['id']
 | 
			
		||||
                self._update(networkgw.COLLECTION_NAME, nw_gw_id,
 | 
			
		||||
                             {self.resource: {}})
 | 
			
		||||
                nw_gw_id = nw_gw[self.gw_resource]['id']
 | 
			
		||||
                self._update(networkgw.NETWORK_GATEWAYS, nw_gw_id,
 | 
			
		||||
                             {self.gw_resource: {}})
 | 
			
		||||
                self.assertEqual(mock_update_gw.call_count, 0)
 | 
			
		||||
 | 
			
		||||
    def test_update_network_gateway_name_exceeds_40_chars(self):
 | 
			
		||||
        new_name = 'this_is_a_gateway_whose_name_is_longer_than_40_chars'
 | 
			
		||||
        with self._network_gateway(name='something') as nw_gw:
 | 
			
		||||
            nw_gw_id = nw_gw[self.resource]['id']
 | 
			
		||||
            self._update(networkgw.COLLECTION_NAME, nw_gw_id,
 | 
			
		||||
                         {self.resource: {'name': new_name}})
 | 
			
		||||
            req = self.new_show_request(networkgw.COLLECTION_NAME,
 | 
			
		||||
            nw_gw_id = nw_gw[self.gw_resource]['id']
 | 
			
		||||
            self._update(networkgw.NETWORK_GATEWAYS, nw_gw_id,
 | 
			
		||||
                         {self.gw_resource: {'name': new_name}})
 | 
			
		||||
            req = self.new_show_request(networkgw.NETWORK_GATEWAYS,
 | 
			
		||||
                                        nw_gw_id)
 | 
			
		||||
            res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
            # Assert Neutron name is not truncated
 | 
			
		||||
            self.assertEqual(new_name, res[self.resource]['name'])
 | 
			
		||||
            self.assertEqual(new_name, res[self.gw_resource]['name'])
 | 
			
		||||
            # Assert NSX name is truncated
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                new_name[:40],
 | 
			
		||||
@@ -652,49 +920,77 @@ class TestNetworkGateway(NsxPluginV2TestCase,
 | 
			
		||||
        with mock.patch.object(nsxlib.l2gateway,
 | 
			
		||||
                               'create_l2_gw_service',
 | 
			
		||||
                               new=raise_nsx_api_exc):
 | 
			
		||||
            res = self._create_network_gateway(
 | 
			
		||||
                self.fmt, 'xxx', name='yyy',
 | 
			
		||||
                devices=[{'id': uuidutils.generate_uuid()}])
 | 
			
		||||
            with self._gateway_device() as dev:
 | 
			
		||||
                res = self._create_network_gateway(
 | 
			
		||||
                    self.fmt, 'xxx', name='yyy',
 | 
			
		||||
                    devices=[{'id': dev[self.dev_resource]['id']}])
 | 
			
		||||
            self.assertEqual(500, res.status_int)
 | 
			
		||||
 | 
			
		||||
    def test_create_network_gateway_nsx_error_returns_409(self):
 | 
			
		||||
        with mock.patch.object(nsxlib.l2gateway,
 | 
			
		||||
                               'create_l2_gw_service',
 | 
			
		||||
                               side_effect=api_exc.Conflict):
 | 
			
		||||
            res = self._create_network_gateway(
 | 
			
		||||
                self.fmt, 'xxx', name='yyy',
 | 
			
		||||
                devices=[{'id': uuidutils.generate_uuid()}])
 | 
			
		||||
            with self._gateway_device() as dev:
 | 
			
		||||
                res = self._create_network_gateway(
 | 
			
		||||
                    self.fmt, 'xxx', name='yyy',
 | 
			
		||||
                    devices=[{'id': dev[self.dev_resource]['id']}])
 | 
			
		||||
            self.assertEqual(409, res.status_int)
 | 
			
		||||
 | 
			
		||||
    def test_list_network_gateways(self):
 | 
			
		||||
        with self._network_gateway(name='test-gw-1') as gw1:
 | 
			
		||||
            with self._network_gateway(name='test_gw_2') as gw2:
 | 
			
		||||
                req = self.new_list_request(networkgw.COLLECTION_NAME)
 | 
			
		||||
                req = self.new_list_request(networkgw.NETWORK_GATEWAYS)
 | 
			
		||||
                res = self.deserialize('json', req.get_response(self.ext_api))
 | 
			
		||||
                # We expect the default gateway too
 | 
			
		||||
                key = self.resource + 's'
 | 
			
		||||
                key = self.gw_resource + 's'
 | 
			
		||||
                self.assertEqual(len(res[key]), 3)
 | 
			
		||||
                self.assertEqual(res[key][0]['default'],
 | 
			
		||||
                                 True)
 | 
			
		||||
                self.assertEqual(res[key][1]['name'],
 | 
			
		||||
                                 gw1[self.resource]['name'])
 | 
			
		||||
                                 gw1[self.gw_resource]['name'])
 | 
			
		||||
                self.assertEqual(res[key][2]['name'],
 | 
			
		||||
                                 gw2[self.resource]['name'])
 | 
			
		||||
                                 gw2[self.gw_resource]['name'])
 | 
			
		||||
 | 
			
		||||
    def test_list_network_gateway_with_multiple_connections(self):
 | 
			
		||||
        self._test_list_network_gateway_with_multiple_connections(
 | 
			
		||||
            expected_gateways=2)
 | 
			
		||||
 | 
			
		||||
    def test_delete_network_gateway(self):
 | 
			
		||||
        # The default gateway must still be there
 | 
			
		||||
        self._test_delete_network_gateway(1)
 | 
			
		||||
 | 
			
		||||
    def test_show_network_gateway_nsx_error_returns_404(self):
 | 
			
		||||
        invalid_id = 'b5afd4a9-eb71-4af7-a082-8fc625a35b61'
 | 
			
		||||
        req = self.new_show_request(networkgw.COLLECTION_NAME, invalid_id)
 | 
			
		||||
        req = self.new_show_request(networkgw.NETWORK_GATEWAYS, invalid_id)
 | 
			
		||||
        res = req.get_response(self.ext_api)
 | 
			
		||||
        self.assertEqual(exc.HTTPNotFound.code, res.status_int)
 | 
			
		||||
 | 
			
		||||
    def test_create_gateway_device(self):
 | 
			
		||||
        self.mock_get_gw_dev_status.return_value = True
 | 
			
		||||
        super(TestNetworkGateway, self).test_create_gateway_device(
 | 
			
		||||
            expected_status=networkgw_db.STATUS_ACTIVE)
 | 
			
		||||
 | 
			
		||||
    def test_create_gateway_device_status_down(self):
 | 
			
		||||
        self.mock_get_gw_dev_status.return_value = False
 | 
			
		||||
        super(TestNetworkGateway, self).test_create_gateway_device(
 | 
			
		||||
            expected_status=networkgw_db.STATUS_DOWN)
 | 
			
		||||
 | 
			
		||||
    def test_get_gateway_device(self):
 | 
			
		||||
        self.mock_get_gw_dev_status.return_value = True
 | 
			
		||||
        super(TestNetworkGateway, self).test_get_gateway_device(
 | 
			
		||||
            expected_status=networkgw_db.STATUS_ACTIVE)
 | 
			
		||||
 | 
			
		||||
    def test_get_gateway_device_status_down(self):
 | 
			
		||||
        self.mock_get_gw_dev_status.return_value = False
 | 
			
		||||
        super(TestNetworkGateway, self).test_get_gateway_device(
 | 
			
		||||
            expected_status=networkgw_db.STATUS_DOWN)
 | 
			
		||||
 | 
			
		||||
    def test_update_gateway_device(self):
 | 
			
		||||
        self.mock_get_gw_dev_status.return_value = True
 | 
			
		||||
        super(TestNetworkGateway, self).test_update_gateway_device(
 | 
			
		||||
            expected_status=networkgw_db.STATUS_ACTIVE)
 | 
			
		||||
 | 
			
		||||
    def test_update_gateway_device_status_down(self):
 | 
			
		||||
        self.mock_get_gw_dev_status.return_value = False
 | 
			
		||||
        super(TestNetworkGateway, self).test_update_gateway_device(
 | 
			
		||||
            expected_status=networkgw_db.STATUS_DOWN)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestNetworkGatewayPlugin(db_base_plugin_v2.NeutronDbPluginV2,
 | 
			
		||||
                               networkgw_db.NetworkGatewayMixin):
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,11 @@
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import mock
 | 
			
		||||
 | 
			
		||||
from neutron.openstack.common import jsonutils
 | 
			
		||||
from neutron.plugins.vmware.api_client import exception
 | 
			
		||||
from neutron.plugins.vmware.common import utils as nsx_utils
 | 
			
		||||
from neutron.plugins.vmware import nsxlib
 | 
			
		||||
from neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
 | 
			
		||||
from neutron.plugins.vmware.nsxlib import switch as switchlib
 | 
			
		||||
@@ -145,3 +149,148 @@ class L2GatewayTestCase(base.NsxlibTestCase):
 | 
			
		||||
        self.assertIn('LogicalPortAttachment', resp_obj)
 | 
			
		||||
        self.assertEqual(resp_obj['LogicalPortAttachment']['type'],
 | 
			
		||||
                         'L2GatewayAttachment')
 | 
			
		||||
 | 
			
		||||
    def _create_expected_req_body(self, display_name, neutron_id,
 | 
			
		||||
                                  connector_type, connector_ip,
 | 
			
		||||
                                  client_certificate):
 | 
			
		||||
        body = {
 | 
			
		||||
            "display_name": display_name,
 | 
			
		||||
            "tags": [{"tag": neutron_id, "scope": "q_gw_dev_id"},
 | 
			
		||||
                     {"tag": 'fake_tenant', "scope": "os_tid"},
 | 
			
		||||
                     {"tag": nsx_utils.NEUTRON_VERSION,
 | 
			
		||||
                      "scope": "quantum"}],
 | 
			
		||||
            "transport_connectors": [
 | 
			
		||||
                {"transport_zone_uuid": 'fake_tz_uuid',
 | 
			
		||||
                    "ip_address": connector_ip,
 | 
			
		||||
                    "type": '%sConnector' % connector_type}],
 | 
			
		||||
            "admin_status_enabled": True
 | 
			
		||||
        }
 | 
			
		||||
        if client_certificate:
 | 
			
		||||
            body["credential"] = {
 | 
			
		||||
                "client_certificate": {
 | 
			
		||||
                    "pem_encoded": client_certificate},
 | 
			
		||||
                "type": "SecurityCertificateCredential"}
 | 
			
		||||
        return body
 | 
			
		||||
 | 
			
		||||
    def test_create_gw_device(self):
 | 
			
		||||
        # NOTE(salv-orlando): This unit test mocks backend calls rather than
 | 
			
		||||
        # leveraging the fake NVP API client
 | 
			
		||||
        display_name = 'fake-device'
 | 
			
		||||
        neutron_id = 'whatever'
 | 
			
		||||
        connector_type = 'stt'
 | 
			
		||||
        connector_ip = '1.1.1.1'
 | 
			
		||||
        client_certificate = 'this_should_be_a_certificate'
 | 
			
		||||
        with mock.patch.object(l2gwlib, 'do_request') as request_mock:
 | 
			
		||||
            expected_req_body = self._create_expected_req_body(
 | 
			
		||||
                display_name, neutron_id, connector_type.upper(),
 | 
			
		||||
                connector_ip, client_certificate)
 | 
			
		||||
            l2gwlib.create_gateway_device(
 | 
			
		||||
                self.fake_cluster, 'fake_tenant', display_name, neutron_id,
 | 
			
		||||
                'fake_tz_uuid', connector_type, connector_ip,
 | 
			
		||||
                client_certificate)
 | 
			
		||||
            request_mock.assert_called_once_with(
 | 
			
		||||
                "POST",
 | 
			
		||||
                "/ws.v1/transport-node",
 | 
			
		||||
                jsonutils.dumps(expected_req_body),
 | 
			
		||||
                cluster=self.fake_cluster)
 | 
			
		||||
 | 
			
		||||
    def test_update_gw_device(self):
 | 
			
		||||
        # NOTE(salv-orlando): This unit test mocks backend calls rather than
 | 
			
		||||
        # leveraging the fake NVP API client
 | 
			
		||||
        display_name = 'fake-device'
 | 
			
		||||
        neutron_id = 'whatever'
 | 
			
		||||
        connector_type = 'stt'
 | 
			
		||||
        connector_ip = '1.1.1.1'
 | 
			
		||||
        client_certificate = 'this_should_be_a_certificate'
 | 
			
		||||
        with mock.patch.object(l2gwlib, 'do_request') as request_mock:
 | 
			
		||||
            expected_req_body = self._create_expected_req_body(
 | 
			
		||||
                display_name, neutron_id, connector_type.upper(),
 | 
			
		||||
                connector_ip, client_certificate)
 | 
			
		||||
            l2gwlib.update_gateway_device(
 | 
			
		||||
                self.fake_cluster, 'whatever', 'fake_tenant',
 | 
			
		||||
                display_name, neutron_id,
 | 
			
		||||
                'fake_tz_uuid', connector_type, connector_ip,
 | 
			
		||||
                client_certificate)
 | 
			
		||||
 | 
			
		||||
            request_mock.assert_called_once_with(
 | 
			
		||||
                "PUT",
 | 
			
		||||
                "/ws.v1/transport-node/whatever",
 | 
			
		||||
                jsonutils.dumps(expected_req_body),
 | 
			
		||||
                cluster=self.fake_cluster)
 | 
			
		||||
 | 
			
		||||
    def test_update_gw_device_without_certificate(self):
 | 
			
		||||
        # NOTE(salv-orlando): This unit test mocks backend calls rather than
 | 
			
		||||
        # leveraging the fake NVP API client
 | 
			
		||||
        display_name = 'fake-device'
 | 
			
		||||
        neutron_id = 'whatever'
 | 
			
		||||
        connector_type = 'stt'
 | 
			
		||||
        connector_ip = '1.1.1.1'
 | 
			
		||||
        with mock.patch.object(l2gwlib, 'do_request') as request_mock:
 | 
			
		||||
            expected_req_body = self._create_expected_req_body(
 | 
			
		||||
                display_name, neutron_id, connector_type.upper(),
 | 
			
		||||
                connector_ip, None)
 | 
			
		||||
            l2gwlib.update_gateway_device(
 | 
			
		||||
                self.fake_cluster, 'whatever', 'fake_tenant',
 | 
			
		||||
                display_name, neutron_id,
 | 
			
		||||
                'fake_tz_uuid', connector_type, connector_ip,
 | 
			
		||||
                client_certificate=None)
 | 
			
		||||
 | 
			
		||||
            request_mock.assert_called_once_with(
 | 
			
		||||
                "PUT",
 | 
			
		||||
                "/ws.v1/transport-node/whatever",
 | 
			
		||||
                jsonutils.dumps(expected_req_body),
 | 
			
		||||
                cluster=self.fake_cluster)
 | 
			
		||||
 | 
			
		||||
    def test_get_gw_device_status(self):
 | 
			
		||||
        # NOTE(salv-orlando): This unit test mocks backend calls rather than
 | 
			
		||||
        # leveraging the fake NVP API client
 | 
			
		||||
        with mock.patch.object(l2gwlib, 'do_request') as request_mock:
 | 
			
		||||
            l2gwlib.get_gateway_device_status(self.fake_cluster, 'whatever')
 | 
			
		||||
            request_mock.assert_called_once_with(
 | 
			
		||||
                "GET",
 | 
			
		||||
                "/ws.v1/transport-node/whatever/status",
 | 
			
		||||
                cluster=self.fake_cluster)
 | 
			
		||||
 | 
			
		||||
    def test_get_gw_devices_status(self):
 | 
			
		||||
        # NOTE(salv-orlando): This unit test mocks backend calls rather than
 | 
			
		||||
        # leveraging the fake NVP API client
 | 
			
		||||
        with mock.patch.object(nsxlib, 'do_request') as request_mock:
 | 
			
		||||
            request_mock.return_value = {
 | 
			
		||||
                'results': [],
 | 
			
		||||
                'page_cursor': None,
 | 
			
		||||
                'result_count': 0}
 | 
			
		||||
            l2gwlib.get_gateway_devices_status(self.fake_cluster)
 | 
			
		||||
            request_mock.assert_called_once_with(
 | 
			
		||||
                "GET",
 | 
			
		||||
                ("/ws.v1/transport-node?fields=uuid,tags&"
 | 
			
		||||
                 "relations=TransportNodeStatus&"
 | 
			
		||||
                 "_page_length=1000&tag_scope=quantum"),
 | 
			
		||||
                cluster=self.fake_cluster)
 | 
			
		||||
 | 
			
		||||
    def test_get_gw_devices_status_filter_by_tenant(self):
 | 
			
		||||
        # NOTE(salv-orlando): This unit test mocks backend calls rather than
 | 
			
		||||
        # leveraging the fake NVP API client
 | 
			
		||||
        with mock.patch.object(nsxlib, 'do_request') as request_mock:
 | 
			
		||||
            request_mock.return_value = {
 | 
			
		||||
                'results': [],
 | 
			
		||||
                'page_cursor': None,
 | 
			
		||||
                'result_count': 0}
 | 
			
		||||
            l2gwlib.get_gateway_devices_status(self.fake_cluster,
 | 
			
		||||
                                               tenant_id='ssc_napoli')
 | 
			
		||||
            request_mock.assert_called_once_with(
 | 
			
		||||
                "GET",
 | 
			
		||||
                ("/ws.v1/transport-node?fields=uuid,tags&"
 | 
			
		||||
                 "relations=TransportNodeStatus&"
 | 
			
		||||
                 "tag_scope=os_tid&tag=ssc_napoli&"
 | 
			
		||||
                 "_page_length=1000&tag_scope=quantum"),
 | 
			
		||||
                cluster=self.fake_cluster)
 | 
			
		||||
 | 
			
		||||
    def test_delete_gw_device(self):
 | 
			
		||||
        # NOTE(salv-orlando): This unit test mocks backend calls rather than
 | 
			
		||||
        # leveraging the fake NVP API client
 | 
			
		||||
        with mock.patch.object(l2gwlib, 'do_request') as request_mock:
 | 
			
		||||
            l2gwlib.delete_gateway_device(self.fake_cluster, 'whatever')
 | 
			
		||||
            request_mock.assert_called_once_with(
 | 
			
		||||
                "DELETE",
 | 
			
		||||
                "/ws.v1/transport-node/whatever",
 | 
			
		||||
                cluster=self.fake_cluster)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user