Merge "Add support for tenant-provided NSX gateways devices"
This commit is contained in:
commit
1a239b8375
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user