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
|
API resource objects for advanced services extensions. Will optionally
|
||||||
translate underscores to dashes in resource names, register the resource,
|
translate underscores to dashes in resource names, register the resource,
|
||||||
and accept action information for resources.
|
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 = []
|
resources = []
|
||||||
|
if not which_service:
|
||||||
|
which_service = constants.CORE
|
||||||
if action_map is None:
|
if action_map is None:
|
||||||
action_map = {}
|
action_map = {}
|
||||||
|
if which_service != constants.CORE:
|
||||||
plugin = manager.NeutronManager.get_service_plugins()[which_service]
|
plugin = manager.NeutronManager.get_service_plugins()[which_service]
|
||||||
|
else:
|
||||||
|
plugin = manager.NeutronManager.get_plugin()
|
||||||
for collection_name in resource_map:
|
for collection_name in resource_map:
|
||||||
resource_name = plural_mappings[collection_name]
|
resource_name = plural_mappings[collection_name]
|
||||||
params = resource_map.get(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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron.common import exceptions as n_exc
|
||||||
from neutron.openstack.common import log
|
from neutron.openstack.common import log
|
||||||
from neutron.plugins.vmware.api_client import client
|
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 db as nsx_db
|
||||||
|
from neutron.plugins.vmware.dbexts import networkgw_db
|
||||||
from neutron.plugins.vmware import nsx_cluster
|
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 router as routerlib
|
||||||
from neutron.plugins.vmware.nsxlib import secgroup as secgrouplib
|
from neutron.plugins.vmware.nsxlib import secgroup as secgrouplib
|
||||||
from neutron.plugins.vmware.nsxlib import switch as switchlib
|
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,
|
concurrent_connections=concurrent_connections,
|
||||||
gen_timeout=gen_timeout)
|
gen_timeout=gen_timeout)
|
||||||
return cluster
|
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()
|
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):
|
def get_tags(**kwargs):
|
||||||
tags = ([dict(tag=value, scope=key)
|
tags = ([dict(tag=value, scope=key)
|
||||||
for key, value in kwargs.iteritems()])
|
for key, value in kwargs.iteritems()])
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@ -37,6 +36,11 @@ SEGMENTATION_ID = 'segmentation_id'
|
|||||||
ALLOWED_CONNECTION_ATTRIBUTES = set((NETWORK_ID,
|
ALLOWED_CONNECTION_ATTRIBUTES = set((NETWORK_ID,
|
||||||
SEGMENTATION_TYPE,
|
SEGMENTATION_TYPE,
|
||||||
SEGMENTATION_ID))
|
SEGMENTATION_ID))
|
||||||
|
# Constants for gateway device operational status
|
||||||
|
STATUS_UNKNOWN = "UNKNOWN"
|
||||||
|
STATUS_ERROR = "ERROR"
|
||||||
|
STATUS_ACTIVE = "ACTIVE"
|
||||||
|
STATUS_DOWN = "DOWN"
|
||||||
|
|
||||||
|
|
||||||
class GatewayInUse(exceptions.InUse):
|
class GatewayInUse(exceptions.InUse):
|
||||||
@ -48,6 +52,15 @@ class GatewayNotFound(exceptions.NotFound):
|
|||||||
message = _("Network Gateway %(gateway_id)s could not be found")
|
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):
|
class NetworkGatewayPortInUse(exceptions.InUse):
|
||||||
message = _("Port '%(port_id)s' is owned by '%(device_owner)s' and "
|
message = _("Port '%(port_id)s' is owned by '%(device_owner)s' and "
|
||||||
"therefore cannot be deleted directly via the port API.")
|
"therefore cannot be deleted directly via the port API.")
|
||||||
@ -104,7 +117,7 @@ class NetworkConnection(model_base.BASEV2, models_v2.HasTenant):
|
|||||||
primary_key=True)
|
primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
class NetworkGatewayDevice(model_base.BASEV2):
|
class NetworkGatewayDeviceReference(model_base.BASEV2):
|
||||||
id = sa.Column(sa.String(36), primary_key=True)
|
id = sa.Column(sa.String(36), primary_key=True)
|
||||||
network_gateway_id = sa.Column(sa.String(36),
|
network_gateway_id = sa.Column(sa.String(36),
|
||||||
sa.ForeignKey('networkgateways.id',
|
sa.ForeignKey('networkgateways.id',
|
||||||
@ -112,6 +125,20 @@ class NetworkGatewayDevice(model_base.BASEV2):
|
|||||||
interface_name = sa.Column(sa.String(64))
|
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,
|
class NetworkGateway(model_base.BASEV2, models_v2.HasId,
|
||||||
models_v2.HasTenant):
|
models_v2.HasTenant):
|
||||||
"""Defines the data model for a network gateway."""
|
"""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 is nullable for this resource
|
||||||
tenant_id = sa.Column(sa.String(36))
|
tenant_id = sa.Column(sa.String(36))
|
||||||
default = sa.Column(sa.Boolean())
|
default = sa.Column(sa.Boolean())
|
||||||
devices = orm.relationship(NetworkGatewayDevice,
|
devices = orm.relationship(NetworkGatewayDeviceReference,
|
||||||
backref='networkgateways',
|
backref='networkgateways',
|
||||||
cascade='all,delete')
|
cascade='all,delete')
|
||||||
network_connections = orm.relationship(NetworkConnection, lazy='joined')
|
network_connections = orm.relationship(NetworkConnection, lazy='joined')
|
||||||
@ -127,7 +154,8 @@ class NetworkGateway(model_base.BASEV2, models_v2.HasId,
|
|||||||
|
|
||||||
class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
|
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):
|
def _get_network_gateway(self, context, gw_id):
|
||||||
try:
|
try:
|
||||||
@ -222,7 +250,7 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
|
|||||||
device_owner=port['device_owner'])
|
device_owner=port['device_owner'])
|
||||||
|
|
||||||
def create_network_gateway(self, context, network_gateway):
|
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)
|
tenant_id = self._get_tenant_id_for_create(context, gw_data)
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
gw_db = NetworkGateway(
|
gw_db = NetworkGateway(
|
||||||
@ -230,14 +258,17 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
|
|||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
name=gw_data.get('name'))
|
name=gw_data.get('name'))
|
||||||
# Device list is guaranteed to be a valid list
|
# 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']])
|
for device in gw_data['devices']])
|
||||||
context.session.add(gw_db)
|
context.session.add(gw_db)
|
||||||
LOG.debug(_("Created network gateway with id:%s"), gw_db['id'])
|
LOG.debug(_("Created network gateway with id:%s"), gw_db['id'])
|
||||||
return self._make_network_gateway_dict(gw_db)
|
return self._make_network_gateway_dict(gw_db)
|
||||||
|
|
||||||
def update_network_gateway(self, context, id, network_gateway):
|
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):
|
with context.session.begin(subtransactions=True):
|
||||||
gw_db = self._get_network_gateway(context, id)
|
gw_db = self._get_network_gateway(context, id)
|
||||||
if gw_db.default:
|
if gw_db.default:
|
||||||
@ -363,9 +394,90 @@ class NetworkGatewayMixin(networkgw.NetworkGatewayPluginBase):
|
|||||||
raise MultipleGatewayConnections(
|
raise MultipleGatewayConnections(
|
||||||
gateway_id=network_gateway_id)
|
gateway_id=network_gateway_id)
|
||||||
# Remove gateway port from network
|
# 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
|
# consistent with outcome of transaction
|
||||||
self.delete_port(context, net_connection['port_id'],
|
self.delete_port(context, net_connection['port_id'],
|
||||||
nw_gw_port_check=False)
|
nw_gw_port_check=False)
|
||||||
# Remove NetworkConnection record
|
# Remove NetworkConnection record
|
||||||
context.session.delete(net_connection)
|
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 oslo.config import cfg
|
||||||
|
|
||||||
from neutron.api import extensions
|
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
from neutron.api.v2 import base
|
from neutron.api.v2 import resource_helper
|
||||||
from neutron import manager
|
from neutron.plugins.vmware.common.utils import NetworkTypes
|
||||||
from neutron import quota
|
|
||||||
|
|
||||||
|
GATEWAY_RESOURCE_NAME = "network_gateway"
|
||||||
RESOURCE_NAME = "network_gateway"
|
DEVICE_RESOURCE_NAME = "gateway_device"
|
||||||
# Use dash for alias and collection name
|
# Use dash for alias and collection name
|
||||||
EXT_ALIAS = RESOURCE_NAME.replace('_', '-')
|
EXT_ALIAS = GATEWAY_RESOURCE_NAME.replace('_', '-')
|
||||||
COLLECTION_NAME = "%ss" % EXT_ALIAS
|
NETWORK_GATEWAYS = "%ss" % EXT_ALIAS
|
||||||
|
GATEWAY_DEVICES = "%ss" % DEVICE_RESOURCE_NAME.replace('_', '-')
|
||||||
DEVICE_ID_ATTR = 'id'
|
DEVICE_ID_ATTR = 'id'
|
||||||
IFACE_NAME_ATTR = 'interface_name'
|
IFACE_NAME_ATTR = 'interface_name'
|
||||||
|
|
||||||
# Attribute Map for Network Gateway Resource
|
# Attribute Map for Network Gateway Resource
|
||||||
# TODO(salvatore-orlando): add admin state as other neutron resources
|
# TODO(salvatore-orlando): add admin state as other neutron resources
|
||||||
RESOURCE_ATTRIBUTE_MAP = {
|
RESOURCE_ATTRIBUTE_MAP = {
|
||||||
COLLECTION_NAME: {
|
NETWORK_GATEWAYS: {
|
||||||
'id': {'allow_post': False, 'allow_put': False,
|
'id': {'allow_post': False, 'allow_put': False,
|
||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
'name': {'allow_post': True, 'allow_put': True,
|
'name': {'allow_post': True, 'allow_put': True,
|
||||||
@ -54,6 +53,28 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'validate': {'type:string': None},
|
'validate': {'type:string': None},
|
||||||
'required_by_policy': True,
|
'required_by_policy': True,
|
||||||
'is_visible': 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") %
|
return (_("%s: provided data are not iterable") %
|
||||||
_validate_device_list.__name__)
|
_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 = [
|
nw_gw_quota_opts = [
|
||||||
cfg.IntOpt('quota_network_gateway',
|
cfg.IntOpt('quota_network_gateway',
|
||||||
default=5,
|
default=5,
|
||||||
@ -95,6 +133,7 @@ nw_gw_quota_opts = [
|
|||||||
cfg.CONF.register_opts(nw_gw_quota_opts, 'QUOTAS')
|
cfg.CONF.register_opts(nw_gw_quota_opts, 'QUOTAS')
|
||||||
|
|
||||||
attributes.validators['type:device_list'] = _validate_device_list
|
attributes.validators['type:device_list'] = _validate_device_list
|
||||||
|
attributes.validators['type:connector_type'] = _validate_connector_type
|
||||||
|
|
||||||
|
|
||||||
class Networkgw(object):
|
class Networkgw(object):
|
||||||
@ -132,22 +171,21 @@ class Networkgw(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_resources(cls):
|
def get_resources(cls):
|
||||||
"""Returns Ext Resources."""
|
"""Returns Ext Resources."""
|
||||||
plugin = manager.NeutronManager.get_plugin()
|
|
||||||
params = RESOURCE_ATTRIBUTE_MAP.get(COLLECTION_NAME, dict())
|
|
||||||
|
|
||||||
member_actions = {'connect_network': 'PUT',
|
member_actions = {
|
||||||
'disconnect_network': 'PUT'}
|
GATEWAY_RESOURCE_NAME.replace('_', '-'): {
|
||||||
|
'connect_network': 'PUT',
|
||||||
|
'disconnect_network': 'PUT'}}
|
||||||
|
|
||||||
# register quotas for network gateways
|
plural_mappings = resource_helper.build_plural_mappings(
|
||||||
quota.QUOTAS.register_resource_by_name(RESOURCE_NAME)
|
{}, RESOURCE_ATTRIBUTE_MAP)
|
||||||
collection_name = COLLECTION_NAME.replace('_', '-')
|
|
||||||
controller = base.create_resource(collection_name,
|
return resource_helper.build_resource_info(plural_mappings,
|
||||||
RESOURCE_NAME,
|
RESOURCE_ATTRIBUTE_MAP,
|
||||||
plugin, params,
|
None,
|
||||||
member_actions=member_actions)
|
action_map=member_actions,
|
||||||
return [extensions.ResourceExtension(COLLECTION_NAME,
|
register_quota=True,
|
||||||
controller,
|
translate_name=True)
|
||||||
member_actions=member_actions)]
|
|
||||||
|
|
||||||
def get_extended_resources(self, version):
|
def get_extended_resources(self, version):
|
||||||
if version == "2.0":
|
if version == "2.0":
|
||||||
@ -187,3 +225,23 @@ class NetworkGatewayPluginBase(object):
|
|||||||
def disconnect_network(self, context, network_gateway_id,
|
def disconnect_network(self, context, network_gateway_id,
|
||||||
network_mapping_info):
|
network_mapping_info):
|
||||||
pass
|
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"
|
HTTP_PUT = "PUT"
|
||||||
|
|
||||||
GWSERVICE_RESOURCE = "gateway-service"
|
GWSERVICE_RESOURCE = "gateway-service"
|
||||||
|
TRANSPORTNODE_RESOURCE = "transport-node"
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ def create_l2_gw_service(cluster, tenant_id, display_name, devices):
|
|||||||
"type": "L2GatewayServiceConfig"
|
"type": "L2GatewayServiceConfig"
|
||||||
}
|
}
|
||||||
return do_request(
|
return do_request(
|
||||||
"POST", _build_uri_path(GWSERVICE_RESOURCE),
|
HTTP_POST, _build_uri_path(GWSERVICE_RESOURCE),
|
||||||
json.dumps(gwservice_obj), cluster=cluster)
|
json.dumps(gwservice_obj), cluster=cluster)
|
||||||
|
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ def plug_l2_gw_service(cluster, lswitch_id, lport_id,
|
|||||||
|
|
||||||
def get_l2_gw_service(cluster, gateway_id):
|
def get_l2_gw_service(cluster, gateway_id):
|
||||||
return do_request(
|
return do_request(
|
||||||
"GET", _build_uri_path(GWSERVICE_RESOURCE,
|
HTTP_GET, _build_uri_path(GWSERVICE_RESOURCE,
|
||||||
resource_id=gateway_id),
|
resource_id=gateway_id),
|
||||||
cluster=cluster)
|
cluster=cluster)
|
||||||
|
|
||||||
@ -98,12 +99,101 @@ def update_l2_gw_service(cluster, gateway_id, display_name):
|
|||||||
# Nothing to update
|
# Nothing to update
|
||||||
return gwservice_obj
|
return gwservice_obj
|
||||||
gwservice_obj["display_name"] = utils.check_and_truncate(display_name)
|
gwservice_obj["display_name"] = utils.check_and_truncate(display_name)
|
||||||
return do_request("PUT", _build_uri_path(GWSERVICE_RESOURCE,
|
return do_request(HTTP_PUT, _build_uri_path(GWSERVICE_RESOURCE,
|
||||||
resource_id=gateway_id),
|
resource_id=gateway_id),
|
||||||
json.dumps(gwservice_obj), cluster=cluster)
|
json.dumps(gwservice_obj), cluster=cluster)
|
||||||
|
|
||||||
|
|
||||||
def delete_l2_gw_service(cluster, gateway_id):
|
def delete_l2_gw_service(cluster, gateway_id):
|
||||||
do_request("DELETE", _build_uri_path(GWSERVICE_RESOURCE,
|
do_request(HTTP_DELETE, _build_uri_path(GWSERVICE_RESOURCE,
|
||||||
resource_id=gateway_id),
|
resource_id=gateway_id),
|
||||||
cluster=cluster)
|
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 nsx_utils
|
||||||
from neutron.plugins.vmware.common import securitygroups as sg_utils
|
from neutron.plugins.vmware.common import securitygroups as sg_utils
|
||||||
from neutron.plugins.vmware.common import sync
|
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 db as nsx_db
|
||||||
from neutron.plugins.vmware.dbexts import distributedrouter as dist_rtr
|
from neutron.plugins.vmware.dbexts import distributedrouter as dist_rtr
|
||||||
from neutron.plugins.vmware.dbexts import maclearning as mac_db
|
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'
|
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,
|
class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||||
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
||||||
db_base_plugin_v2.NeutronDbPluginV2,
|
db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
@ -205,7 +195,7 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
def_gw_data = {'id': def_l2_gw_uuid,
|
def_gw_data = {'id': def_l2_gw_uuid,
|
||||||
'name': 'default L2 gateway service',
|
'name': 'default L2 gateway service',
|
||||||
'devices': []}
|
'devices': []}
|
||||||
gw_res_name = networkgw.RESOURCE_NAME.replace('-', '_')
|
gw_res_name = networkgw.GATEWAY_RESOURCE_NAME.replace('-', '_')
|
||||||
def_network_gw = super(
|
def_network_gw = super(
|
||||||
NsxPluginV2, self).create_network_gateway(
|
NsxPluginV2, self).create_network_gateway(
|
||||||
ctx, {gw_res_name: def_gw_data})
|
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
|
# Ensure the default gateway in the config file is in sync with the db
|
||||||
self._ensure_default_network_gateway()
|
self._ensure_default_network_gateway()
|
||||||
# Need to re-do authZ checks here in order to avoid creation on NSX
|
# 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)
|
tenant_id = self._get_tenant_id_for_create(context, gw_data)
|
||||||
devices = gw_data['devices']
|
devices = gw_data['devices']
|
||||||
# Populate default physical network where not specified
|
# Populate default physical network where not specified
|
||||||
@ -2017,8 +2007,15 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
if not device.get('interface_name'):
|
if not device.get('interface_name'):
|
||||||
device['interface_name'] = self.cluster.default_interface_name
|
device['interface_name'] = self.cluster.default_interface_name
|
||||||
try:
|
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(
|
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')
|
nsx_uuid = nsx_res.get('uuid')
|
||||||
except api_exc.Conflict:
|
except api_exc.Conflict:
|
||||||
raise nsx_exc.L2GatewayAlreadyInUse(gateway=gw_data['name'])
|
raise nsx_exc.L2GatewayAlreadyInUse(gateway=gw_data['name'])
|
||||||
@ -2027,8 +2024,8 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
|||||||
LOG.exception(err_msg)
|
LOG.exception(err_msg)
|
||||||
raise nsx_exc.NsxPluginException(err_msg=err_msg)
|
raise nsx_exc.NsxPluginException(err_msg=err_msg)
|
||||||
gw_data['id'] = nsx_uuid
|
gw_data['id'] = nsx_uuid
|
||||||
return super(NsxPluginV2, self).create_network_gateway(context,
|
return super(NsxPluginV2, self).create_network_gateway(
|
||||||
network_gateway)
|
context, network_gateway)
|
||||||
|
|
||||||
def delete_network_gateway(self, context, gateway_id):
|
def delete_network_gateway(self, context, gateway_id):
|
||||||
"""Remove a layer-2 network gateway.
|
"""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
|
# Ensure the default gateway in the config file is in sync with the db
|
||||||
self._ensure_default_network_gateway()
|
self._ensure_default_network_gateway()
|
||||||
# Update gateway on backend when there's a name change
|
# 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:
|
if name:
|
||||||
try:
|
try:
|
||||||
l2gwlib.update_l2_gw_service(self.cluster, id, name)
|
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(
|
return super(NsxPluginV2, self).disconnect_network(
|
||||||
context, network_gateway_id, network_mapping_info)
|
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):
|
def create_security_group(self, context, security_group, default_sg=False):
|
||||||
"""Create security group.
|
"""Create security group.
|
||||||
|
|
||||||
|
@ -27,11 +27,11 @@ from neutron import context
|
|||||||
from neutron.db import api as db_api
|
from neutron.db import api as db_api
|
||||||
from neutron.db import db_base_plugin_v2
|
from neutron.db import db_base_plugin_v2
|
||||||
from neutron import manager
|
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.api_client import exception as api_exc
|
||||||
from neutron.plugins.vmware.dbexts import networkgw_db
|
from neutron.plugins.vmware.dbexts import networkgw_db
|
||||||
from neutron.plugins.vmware.extensions import networkgw
|
from neutron.plugins.vmware.extensions import networkgw
|
||||||
from neutron.plugins.vmware import nsxlib
|
from neutron.plugins.vmware import nsxlib
|
||||||
|
from neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
|
||||||
from neutron import quota
|
from neutron import quota
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
from neutron.tests.unit import test_api_v2
|
from neutron.tests.unit import test_api_v2
|
||||||
@ -69,7 +69,8 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
super(NetworkGatewayExtensionTestCase, self).setUp()
|
super(NetworkGatewayExtensionTestCase, self).setUp()
|
||||||
plugin = '%s.%s' % (networkgw.__name__,
|
plugin = '%s.%s' % (networkgw.__name__,
|
||||||
networkgw.NetworkGatewayPluginBase.__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
|
# Ensure existing ExtensionManager is not used
|
||||||
extensions.PluginAwareExtensionManager._instance = None
|
extensions.PluginAwareExtensionManager._instance = None
|
||||||
@ -100,67 +101,67 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_network_gateway_create(self):
|
def test_network_gateway_create(self):
|
||||||
nw_gw_id = _uuid()
|
nw_gw_id = _uuid()
|
||||||
data = {self._resource: {'name': 'nw-gw',
|
data = {self._gw_resource: {'name': 'nw-gw',
|
||||||
'tenant_id': _uuid(),
|
'tenant_id': _uuid(),
|
||||||
'devices': [{'id': _uuid(),
|
'devices': [{'id': _uuid(),
|
||||||
'interface_name': 'xxx'}]}}
|
'interface_name': 'xxx'}]}}
|
||||||
return_value = data[self._resource].copy()
|
return_value = data[self._gw_resource].copy()
|
||||||
return_value.update({'id': nw_gw_id})
|
return_value.update({'id': nw_gw_id})
|
||||||
instance = self.plugin.return_value
|
instance = self.plugin.return_value
|
||||||
instance.create_network_gateway.return_value = 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(
|
instance.create_network_gateway.assert_called_with(
|
||||||
mock.ANY, network_gateway=data)
|
mock.ANY, network_gateway=data)
|
||||||
self.assertEqual(res.status_int, exc.HTTPCreated.code)
|
self.assertEqual(res.status_int, exc.HTTPCreated.code)
|
||||||
self.assertIn(self._resource, res.json)
|
self.assertIn(self._gw_resource, res.json)
|
||||||
nw_gw = res.json[self._resource]
|
nw_gw = res.json[self._gw_resource]
|
||||||
self.assertEqual(nw_gw['id'], nw_gw_id)
|
self.assertEqual(nw_gw['id'], nw_gw_id)
|
||||||
|
|
||||||
def _test_network_gateway_create_with_error(
|
def _test_network_gateway_create_with_error(
|
||||||
self, data, error_code=exc.HTTPBadRequest.code):
|
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)
|
expect_errors=True)
|
||||||
self.assertEqual(res.status_int, error_code)
|
self.assertEqual(res.status_int, error_code)
|
||||||
|
|
||||||
def test_network_gateway_create_invalid_device_spec(self):
|
def test_network_gateway_create_invalid_device_spec(self):
|
||||||
data = {self._resource: {'name': 'nw-gw',
|
data = {self._gw_resource: {'name': 'nw-gw',
|
||||||
'tenant_id': _uuid(),
|
'tenant_id': _uuid(),
|
||||||
'devices': [{'id': _uuid(),
|
'devices': [{'id': _uuid(),
|
||||||
'invalid': 'xxx'}]}}
|
'invalid': 'xxx'}]}}
|
||||||
self._test_network_gateway_create_with_error(data)
|
self._test_network_gateway_create_with_error(data)
|
||||||
|
|
||||||
def test_network_gateway_create_extra_attr_in_device_spec(self):
|
def test_network_gateway_create_extra_attr_in_device_spec(self):
|
||||||
data = {self._resource: {'name': 'nw-gw',
|
data = {self._gw_resource: {'name': 'nw-gw',
|
||||||
'tenant_id': _uuid(),
|
'tenant_id': _uuid(),
|
||||||
'devices': [{'id': _uuid(),
|
'devices':
|
||||||
|
[{'id': _uuid(),
|
||||||
'interface_name': 'xxx',
|
'interface_name': 'xxx',
|
||||||
'extra_attr': 'onetoomany'}]}}
|
'extra_attr': 'onetoomany'}]}}
|
||||||
self._test_network_gateway_create_with_error(data)
|
self._test_network_gateway_create_with_error(data)
|
||||||
|
|
||||||
def test_network_gateway_update(self):
|
def test_network_gateway_update(self):
|
||||||
nw_gw_name = 'updated'
|
nw_gw_name = 'updated'
|
||||||
data = {self._resource: {'name': nw_gw_name}}
|
data = {self._gw_resource: {'name': nw_gw_name}}
|
||||||
nw_gw_id = _uuid()
|
nw_gw_id = _uuid()
|
||||||
return_value = {'id': nw_gw_id,
|
return_value = {'id': nw_gw_id,
|
||||||
'name': nw_gw_name}
|
'name': nw_gw_name}
|
||||||
|
|
||||||
instance = self.plugin.return_value
|
instance = self.plugin.return_value
|
||||||
instance.update_network_gateway.return_value = return_value
|
instance.update_network_gateway.return_value = return_value
|
||||||
res = self.api.put_json(_get_path('%s/%s' % (networkgw.COLLECTION_NAME,
|
res = self.api.put_json(
|
||||||
nw_gw_id)),
|
_get_path('%s/%s' % (networkgw.NETWORK_GATEWAYS, nw_gw_id)), data)
|
||||||
data)
|
|
||||||
instance.update_network_gateway.assert_called_with(
|
instance.update_network_gateway.assert_called_with(
|
||||||
mock.ANY, nw_gw_id, network_gateway=data)
|
mock.ANY, nw_gw_id, network_gateway=data)
|
||||||
self.assertEqual(res.status_int, exc.HTTPOk.code)
|
self.assertEqual(res.status_int, exc.HTTPOk.code)
|
||||||
self.assertIn(self._resource, res.json)
|
self.assertIn(self._gw_resource, res.json)
|
||||||
nw_gw = res.json[self._resource]
|
nw_gw = res.json[self._gw_resource]
|
||||||
self.assertEqual(nw_gw['id'], nw_gw_id)
|
self.assertEqual(nw_gw['id'], nw_gw_id)
|
||||||
self.assertEqual(nw_gw['name'], nw_gw_name)
|
self.assertEqual(nw_gw['name'], nw_gw_name)
|
||||||
|
|
||||||
def test_network_gateway_delete(self):
|
def test_network_gateway_delete(self):
|
||||||
nw_gw_id = _uuid()
|
nw_gw_id = _uuid()
|
||||||
instance = self.plugin.return_value
|
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)))
|
nw_gw_id)))
|
||||||
|
|
||||||
instance.delete_network_gateway.assert_called_with(mock.ANY,
|
instance.delete_network_gateway.assert_called_with(mock.ANY,
|
||||||
@ -169,7 +170,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_network_gateway_get(self):
|
def test_network_gateway_get(self):
|
||||||
nw_gw_id = _uuid()
|
nw_gw_id = _uuid()
|
||||||
return_value = {self._resource: {'name': 'test',
|
return_value = {self._gw_resource: {'name': 'test',
|
||||||
'devices':
|
'devices':
|
||||||
[{'id': _uuid(),
|
[{'id': _uuid(),
|
||||||
'interface_name': 'xxx'}],
|
'interface_name': 'xxx'}],
|
||||||
@ -177,7 +178,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
instance = self.plugin.return_value
|
instance = self.plugin.return_value
|
||||||
instance.get_network_gateway.return_value = 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)))
|
nw_gw_id)))
|
||||||
|
|
||||||
instance.get_network_gateway.assert_called_with(mock.ANY,
|
instance.get_network_gateway.assert_called_with(mock.ANY,
|
||||||
@ -187,7 +188,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_network_gateway_list(self):
|
def test_network_gateway_list(self):
|
||||||
nw_gw_id = _uuid()
|
nw_gw_id = _uuid()
|
||||||
return_value = [{self._resource: {'name': 'test',
|
return_value = [{self._gw_resource: {'name': 'test',
|
||||||
'devices':
|
'devices':
|
||||||
[{'id': _uuid(),
|
[{'id': _uuid(),
|
||||||
'interface_name': 'xxx'}],
|
'interface_name': 'xxx'}],
|
||||||
@ -195,7 +196,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
instance = self.plugin.return_value
|
instance = self.plugin.return_value
|
||||||
instance.get_network_gateways.return_value = 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,
|
instance.get_network_gateways.assert_called_with(mock.ANY,
|
||||||
fields=mock.ANY,
|
fields=mock.ANY,
|
||||||
@ -216,7 +217,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
instance = self.plugin.return_value
|
instance = self.plugin.return_value
|
||||||
instance.connect_network.return_value = return_value
|
instance.connect_network.return_value = return_value
|
||||||
res = self.api.put_json(_get_path('%s/%s/connect_network' %
|
res = self.api.put_json(_get_path('%s/%s/connect_network' %
|
||||||
(networkgw.COLLECTION_NAME,
|
(networkgw.NETWORK_GATEWAYS,
|
||||||
nw_gw_id)),
|
nw_gw_id)),
|
||||||
mapping_data)
|
mapping_data)
|
||||||
instance.connect_network.assert_called_with(mock.ANY,
|
instance.connect_network.assert_called_with(mock.ANY,
|
||||||
@ -233,7 +234,7 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
mapping_data = {'network_id': nw_id}
|
mapping_data = {'network_id': nw_id}
|
||||||
instance = self.plugin.return_value
|
instance = self.plugin.return_value
|
||||||
res = self.api.put_json(_get_path('%s/%s/disconnect_network' %
|
res = self.api.put_json(_get_path('%s/%s/disconnect_network' %
|
||||||
(networkgw.COLLECTION_NAME,
|
(networkgw.NETWORK_GATEWAYS,
|
||||||
nw_gw_id)),
|
nw_gw_id)),
|
||||||
mapping_data)
|
mapping_data)
|
||||||
instance.disconnect_network.assert_called_with(mock.ANY,
|
instance.disconnect_network.assert_called_with(mock.ANY,
|
||||||
@ -241,6 +242,116 @@ class NetworkGatewayExtensionTestCase(base.BaseTestCase):
|
|||||||
mapping_data)
|
mapping_data)
|
||||||
self.assertEqual(res.status_int, exc.HTTPOk.code)
|
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):
|
class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
||||||
"""Unit tests for Network Gateway DB support."""
|
"""Unit tests for Network Gateway DB support."""
|
||||||
@ -250,21 +361,23 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
plugin = '%s.%s' % (__name__, TestNetworkGatewayPlugin.__name__)
|
plugin = '%s.%s' % (__name__, TestNetworkGatewayPlugin.__name__)
|
||||||
if not ext_mgr:
|
if not ext_mgr:
|
||||||
ext_mgr = TestExtensionManager()
|
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,
|
super(NetworkGatewayDbTestCase, self).setUp(plugin=plugin,
|
||||||
ext_mgr=ext_mgr)
|
ext_mgr=ext_mgr)
|
||||||
|
|
||||||
def _create_network_gateway(self, fmt, tenant_id, name=None,
|
def _create_network_gateway(self, fmt, tenant_id, name=None,
|
||||||
devices=None, arg_list=None, **kwargs):
|
devices=None, arg_list=None, **kwargs):
|
||||||
data = {self.resource: {'tenant_id': tenant_id,
|
data = {self.gw_resource: {'tenant_id': tenant_id,
|
||||||
'devices': devices}}
|
'devices': devices}}
|
||||||
if name:
|
if name:
|
||||||
data[self.resource]['name'] = name
|
data[self.gw_resource]['name'] = name
|
||||||
for arg in arg_list or ():
|
for arg in arg_list or ():
|
||||||
# Arg must be present and not empty
|
# Arg must be present and not empty
|
||||||
if arg in kwargs and kwargs[arg]:
|
if arg in kwargs and kwargs[arg]:
|
||||||
data[self.resource][arg] = kwargs[arg]
|
data[self.gw_resource][arg] = kwargs[arg]
|
||||||
nw_gw_req = self.new_create_request(networkgw.COLLECTION_NAME,
|
nw_gw_req = self.new_create_request(networkgw.NETWORK_GATEWAYS,
|
||||||
data, fmt)
|
data, fmt)
|
||||||
if (kwargs.get('set_context') and tenant_id):
|
if (kwargs.get('set_context') and tenant_id):
|
||||||
# create a specific auth context for this request
|
# create a specific auth context for this request
|
||||||
@ -275,16 +388,89 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _network_gateway(self, name='gw1', devices=None,
|
def _network_gateway(self, name='gw1', devices=None,
|
||||||
fmt='json', tenant_id=_uuid()):
|
fmt='json', tenant_id=_uuid()):
|
||||||
|
device = None
|
||||||
if not devices:
|
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,
|
res = self._create_network_gateway(fmt, tenant_id, name=name,
|
||||||
devices=devices)
|
devices=devices)
|
||||||
network_gateway = self.deserialize(fmt, res)
|
|
||||||
if res.status_int >= 400:
|
if res.status_int >= 400:
|
||||||
raise exc.HTTPClientError(code=res.status_int)
|
raise exc.HTTPClientError(code=res.status_int)
|
||||||
|
network_gateway = self.deserialize(fmt, res)
|
||||||
yield network_gateway
|
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,
|
def _gateway_action(self, action, network_gateway_id, network_id,
|
||||||
segmentation_type, segmentation_id=None,
|
segmentation_type, segmentation_id=None,
|
||||||
@ -294,7 +480,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
if segmentation_id:
|
if segmentation_id:
|
||||||
connection_data['segmentation_id'] = 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,
|
connection_data,
|
||||||
network_gateway_id,
|
network_gateway_id,
|
||||||
"%s_network" % action)
|
"%s_network" % action)
|
||||||
@ -307,7 +493,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net:
|
with self.network() as net:
|
||||||
body = self._gateway_action('connect',
|
body = self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net['network']['id'],
|
net['network']['id'],
|
||||||
segmentation_type,
|
segmentation_type,
|
||||||
segmentation_id)
|
segmentation_id)
|
||||||
@ -320,10 +506,10 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
gw_port_id = connection_info['port_id']
|
gw_port_id = connection_info['port_id']
|
||||||
port_body = self._show('ports', gw_port_id)
|
port_body = self._show('ports', gw_port_id)
|
||||||
self.assertEqual(port_body['port']['device_id'],
|
self.assertEqual(port_body['port']['device_id'],
|
||||||
gw[self.resource]['id'])
|
gw[self.gw_resource]['id'])
|
||||||
# Clean up - otherwise delete will fail
|
# Clean up - otherwise delete will fail
|
||||||
body = self._gateway_action('disconnect',
|
body = self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net['network']['id'],
|
net['network']['id'],
|
||||||
segmentation_type,
|
segmentation_type,
|
||||||
segmentation_id)
|
segmentation_id)
|
||||||
@ -332,90 +518,98 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
expected_code=exc.HTTPNotFound.code)
|
expected_code=exc.HTTPNotFound.code)
|
||||||
|
|
||||||
def test_create_network_gateway(self):
|
def test_create_network_gateway(self):
|
||||||
|
with contextlib.nested(
|
||||||
|
self._gateway_device(name='dev_1'),
|
||||||
|
self._gateway_device(name='dev_2')) as (dev_1, dev_2):
|
||||||
name = 'test-gw'
|
name = 'test-gw'
|
||||||
devices = [{'id': _uuid(), 'interface_name': 'xxx'},
|
dev_1_id = dev_1[self.dev_resource]['id']
|
||||||
{'id': _uuid(), 'interface_name': 'yyy'}]
|
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)]
|
keys = [('devices', devices), ('name', name)]
|
||||||
with self._network_gateway(name=name, devices=devices) as gw:
|
with self._network_gateway(name=name, devices=devices) as gw:
|
||||||
for k, v in keys:
|
for k, v in keys:
|
||||||
self.assertEqual(gw[self.resource][k], v)
|
self.assertEqual(gw[self.gw_resource][k], v)
|
||||||
|
|
||||||
def test_create_network_gateway_no_interface_name(self):
|
def test_create_network_gateway_no_interface_name(self):
|
||||||
|
with self._gateway_device() as dev:
|
||||||
name = 'test-gw'
|
name = 'test-gw'
|
||||||
devices = [{'id': _uuid()}]
|
devices = [{'id': dev[self.dev_resource]['id']}]
|
||||||
exp_devices = devices
|
exp_devices = devices
|
||||||
exp_devices[0]['interface_name'] = 'breth0'
|
exp_devices[0]['interface_name'] = 'breth0'
|
||||||
keys = [('devices', exp_devices), ('name', name)]
|
keys = [('devices', exp_devices), ('name', name)]
|
||||||
with self._network_gateway(name=name, devices=devices) as gw:
|
with self._network_gateway(name=name, devices=devices) as gw:
|
||||||
for k, v in keys:
|
for k, v in keys:
|
||||||
self.assertEqual(gw[self.resource][k], v)
|
self.assertEqual(gw[self.gw_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())
|
|
||||||
|
|
||||||
def test_delete_network_gateway(self):
|
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):
|
def test_update_network_gateway(self):
|
||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
data = {self.resource: {'name': 'new_name'}}
|
data = {self.gw_resource: {'name': 'new_name'}}
|
||||||
req = self.new_update_request(networkgw.COLLECTION_NAME,
|
req = self.new_update_request(networkgw.NETWORK_GATEWAYS,
|
||||||
data,
|
data,
|
||||||
gw[self.resource]['id'])
|
gw[self.gw_resource]['id'])
|
||||||
res = self.deserialize('json', req.get_response(self.ext_api))
|
res = self.deserialize('json', req.get_response(self.ext_api))
|
||||||
self.assertEqual(res[self.resource]['name'],
|
self.assertEqual(res[self.gw_resource]['name'],
|
||||||
data[self.resource]['name'])
|
data[self.gw_resource]['name'])
|
||||||
|
|
||||||
def test_get_network_gateway(self):
|
def test_get_network_gateway(self):
|
||||||
with self._network_gateway(name='test-gw') as gw:
|
with self._network_gateway(name='test-gw') as gw:
|
||||||
req = self.new_show_request(networkgw.COLLECTION_NAME,
|
req = self.new_show_request(networkgw.NETWORK_GATEWAYS,
|
||||||
gw[self.resource]['id'])
|
gw[self.gw_resource]['id'])
|
||||||
res = self.deserialize('json', req.get_response(self.ext_api))
|
res = self.deserialize('json', req.get_response(self.ext_api))
|
||||||
self.assertEqual(res[self.resource]['name'],
|
self.assertEqual(res[self.gw_resource]['name'],
|
||||||
gw[self.resource]['name'])
|
gw[self.gw_resource]['name'])
|
||||||
|
|
||||||
def test_list_network_gateways(self):
|
def test_list_network_gateways(self):
|
||||||
with self._network_gateway(name='test-gw-1') as gw1:
|
with self._network_gateway(name='test-gw-1') as gw1:
|
||||||
with self._network_gateway(name='test_gw_2') as gw2:
|
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))
|
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(len(res[key]), 2)
|
||||||
self.assertEqual(res[key][0]['name'],
|
self.assertEqual(res[key][0]['name'],
|
||||||
gw1[self.resource]['name'])
|
gw1[self.gw_resource]['name'])
|
||||||
self.assertEqual(res[key][1]['name'],
|
self.assertEqual(res[key][1]['name'],
|
||||||
gw2[self.resource]['name'])
|
gw2[self.gw_resource]['name'])
|
||||||
|
|
||||||
def _test_list_network_gateway_with_multiple_connections(
|
def _test_list_network_gateway_with_multiple_connections(
|
||||||
self, expected_gateways=1):
|
self, expected_gateways=1):
|
||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 777)
|
'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))
|
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)
|
self.assertEqual(len(res[key]), expected_gateways)
|
||||||
for item in res[key]:
|
for item in res[key]:
|
||||||
self.assertIn('ports', item)
|
self.assertIn('ports', item)
|
||||||
if item['id'] == gw[self.resource]['id']:
|
if item['id'] == gw[self.gw_resource]['id']:
|
||||||
gw_ports = item['ports']
|
gw_ports = item['ports']
|
||||||
self.assertEqual(len(gw_ports), 2)
|
self.assertEqual(len(gw_ports), 2)
|
||||||
segmentation_ids = [555, 777]
|
segmentation_ids = [555, 777]
|
||||||
@ -425,11 +619,11 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
segmentation_ids.remove(gw_port['segmentation_id'])
|
segmentation_ids.remove(gw_port['segmentation_id'])
|
||||||
# Required cleanup
|
# Required cleanup
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 777)
|
'vlan', 777)
|
||||||
|
|
||||||
@ -449,19 +643,19 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 777)
|
'vlan', 777)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 777)
|
'vlan', 777)
|
||||||
|
|
||||||
@ -470,19 +664,19 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw_2:
|
with self._network_gateway() as gw_2:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw_1[self.resource]['id'],
|
gw_1[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw_2[self.resource]['id'],
|
gw_2[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw_1[self.resource]['id'],
|
gw_1[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw_2[self.resource]['id'],
|
gw_2[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
|
|
||||||
@ -490,25 +684,25 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
with self.network() as net_2:
|
with self.network() as net_2:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_2['network']['id'],
|
net_2['network']['id'],
|
||||||
'vlan', 555,
|
'vlan', 555,
|
||||||
expected_status=exc.HTTPConflict.code)
|
expected_status=exc.HTTPConflict.code)
|
||||||
# Clean up - otherwise delete will fail
|
# Clean up - otherwise delete will fail
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
|
|
||||||
def test_connect_invalid_network_returns_400(self):
|
def test_connect_invalid_network_returns_400(self):
|
||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
'hohoho',
|
'hohoho',
|
||||||
'vlan', 555,
|
'vlan', 555,
|
||||||
expected_status=exc.HTTPBadRequest.code)
|
expected_status=exc.HTTPBadRequest.code)
|
||||||
@ -516,7 +710,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
def test_connect_unspecified_network_returns_400(self):
|
def test_connect_unspecified_network_returns_400(self):
|
||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
None,
|
None,
|
||||||
'vlan', 555,
|
'vlan', 555,
|
||||||
expected_status=exc.HTTPBadRequest.code)
|
expected_status=exc.HTTPBadRequest.code)
|
||||||
@ -525,25 +719,25 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 777)
|
'vlan', 777)
|
||||||
# This should raise
|
# This should raise
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan',
|
'vlan',
|
||||||
expected_status=exc.HTTPConflict.code)
|
expected_status=exc.HTTPConflict.code)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 777)
|
'vlan', 777)
|
||||||
|
|
||||||
@ -551,7 +745,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
body = self._gateway_action('connect',
|
body = self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
# fetch port id and try to delete it
|
# fetch port id and try to delete it
|
||||||
@ -559,7 +753,7 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
self._delete('ports', gw_port_id,
|
self._delete('ports', gw_port_id,
|
||||||
expected_code=exc.HTTPConflict.code)
|
expected_code=exc.HTTPConflict.code)
|
||||||
body = self._gateway_action('disconnect',
|
body = self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
|
|
||||||
@ -567,14 +761,14 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'flat')
|
'flat')
|
||||||
self._delete(networkgw.COLLECTION_NAME,
|
self._delete(networkgw.NETWORK_GATEWAYS,
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
expected_code=exc.HTTPConflict.code)
|
expected_code=exc.HTTPConflict.code)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'flat')
|
'flat')
|
||||||
|
|
||||||
@ -582,25 +776,99 @@ class NetworkGatewayDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
with self._network_gateway() as gw:
|
with self._network_gateway() as gw:
|
||||||
with self.network() as net_1:
|
with self.network() as net_1:
|
||||||
self._gateway_action('connect',
|
self._gateway_action('connect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'vlan', 555)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 999,
|
'vlan', 999,
|
||||||
expected_status=exc.HTTPNotFound.code)
|
expected_status=exc.HTTPNotFound.code)
|
||||||
self._gateway_action('disconnect',
|
self._gateway_action('disconnect',
|
||||||
gw[self.resource]['id'],
|
gw[self.gw_resource]['id'],
|
||||||
net_1['network']['id'],
|
net_1['network']['id'],
|
||||||
'vlan', 555)
|
'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,
|
class TestNetworkGateway(NsxPluginV2TestCase,
|
||||||
NetworkGatewayDbTestCase):
|
NetworkGatewayDbTestCase):
|
||||||
|
|
||||||
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None):
|
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None):
|
||||||
cfg.CONF.set_override('api_extensions_path', NSXEXT_PATH)
|
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,
|
super(TestNetworkGateway,
|
||||||
self).setUp(plugin=plugin, ext_mgr=ext_mgr)
|
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'
|
name = 'this_is_a_gateway_whose_name_is_longer_than_40_chars'
|
||||||
with self._network_gateway(name=name) as nw_gw:
|
with self._network_gateway(name=name) as nw_gw:
|
||||||
# Assert Neutron name is not truncated
|
# 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):
|
def test_update_network_gateway_with_name_calls_backend(self):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
nsxlib.l2gateway, 'update_l2_gw_service') as mock_update_gw:
|
nsxlib.l2gateway, 'update_l2_gw_service') as mock_update_gw:
|
||||||
with self._network_gateway(name='cavani') as nw_gw:
|
with self._network_gateway(name='cavani') as nw_gw:
|
||||||
nw_gw_id = nw_gw[self.resource]['id']
|
nw_gw_id = nw_gw[self.gw_resource]['id']
|
||||||
self._update(networkgw.COLLECTION_NAME, nw_gw_id,
|
self._update(networkgw.NETWORK_GATEWAYS, nw_gw_id,
|
||||||
{self.resource: {'name': 'higuain'}})
|
{self.gw_resource: {'name': 'higuain'}})
|
||||||
mock_update_gw.assert_called_once_with(
|
mock_update_gw.assert_called_once_with(
|
||||||
mock.ANY, nw_gw_id, 'higuain')
|
mock.ANY, nw_gw_id, 'higuain')
|
||||||
|
|
||||||
@ -624,22 +892,22 @@ class TestNetworkGateway(NsxPluginV2TestCase,
|
|||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
nsxlib.l2gateway, 'update_l2_gw_service') as mock_update_gw:
|
nsxlib.l2gateway, 'update_l2_gw_service') as mock_update_gw:
|
||||||
with self._network_gateway(name='something') as nw_gw:
|
with self._network_gateway(name='something') as nw_gw:
|
||||||
nw_gw_id = nw_gw[self.resource]['id']
|
nw_gw_id = nw_gw[self.gw_resource]['id']
|
||||||
self._update(networkgw.COLLECTION_NAME, nw_gw_id,
|
self._update(networkgw.NETWORK_GATEWAYS, nw_gw_id,
|
||||||
{self.resource: {}})
|
{self.gw_resource: {}})
|
||||||
self.assertEqual(mock_update_gw.call_count, 0)
|
self.assertEqual(mock_update_gw.call_count, 0)
|
||||||
|
|
||||||
def test_update_network_gateway_name_exceeds_40_chars(self):
|
def test_update_network_gateway_name_exceeds_40_chars(self):
|
||||||
new_name = 'this_is_a_gateway_whose_name_is_longer_than_40_chars'
|
new_name = 'this_is_a_gateway_whose_name_is_longer_than_40_chars'
|
||||||
with self._network_gateway(name='something') as nw_gw:
|
with self._network_gateway(name='something') as nw_gw:
|
||||||
nw_gw_id = nw_gw[self.resource]['id']
|
nw_gw_id = nw_gw[self.gw_resource]['id']
|
||||||
self._update(networkgw.COLLECTION_NAME, nw_gw_id,
|
self._update(networkgw.NETWORK_GATEWAYS, nw_gw_id,
|
||||||
{self.resource: {'name': new_name}})
|
{self.gw_resource: {'name': new_name}})
|
||||||
req = self.new_show_request(networkgw.COLLECTION_NAME,
|
req = self.new_show_request(networkgw.NETWORK_GATEWAYS,
|
||||||
nw_gw_id)
|
nw_gw_id)
|
||||||
res = self.deserialize('json', req.get_response(self.ext_api))
|
res = self.deserialize('json', req.get_response(self.ext_api))
|
||||||
# Assert Neutron name is not truncated
|
# 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
|
# Assert NSX name is truncated
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
new_name[:40],
|
new_name[:40],
|
||||||
@ -652,49 +920,77 @@ class TestNetworkGateway(NsxPluginV2TestCase,
|
|||||||
with mock.patch.object(nsxlib.l2gateway,
|
with mock.patch.object(nsxlib.l2gateway,
|
||||||
'create_l2_gw_service',
|
'create_l2_gw_service',
|
||||||
new=raise_nsx_api_exc):
|
new=raise_nsx_api_exc):
|
||||||
|
with self._gateway_device() as dev:
|
||||||
res = self._create_network_gateway(
|
res = self._create_network_gateway(
|
||||||
self.fmt, 'xxx', name='yyy',
|
self.fmt, 'xxx', name='yyy',
|
||||||
devices=[{'id': uuidutils.generate_uuid()}])
|
devices=[{'id': dev[self.dev_resource]['id']}])
|
||||||
self.assertEqual(500, res.status_int)
|
self.assertEqual(500, res.status_int)
|
||||||
|
|
||||||
def test_create_network_gateway_nsx_error_returns_409(self):
|
def test_create_network_gateway_nsx_error_returns_409(self):
|
||||||
with mock.patch.object(nsxlib.l2gateway,
|
with mock.patch.object(nsxlib.l2gateway,
|
||||||
'create_l2_gw_service',
|
'create_l2_gw_service',
|
||||||
side_effect=api_exc.Conflict):
|
side_effect=api_exc.Conflict):
|
||||||
|
with self._gateway_device() as dev:
|
||||||
res = self._create_network_gateway(
|
res = self._create_network_gateway(
|
||||||
self.fmt, 'xxx', name='yyy',
|
self.fmt, 'xxx', name='yyy',
|
||||||
devices=[{'id': uuidutils.generate_uuid()}])
|
devices=[{'id': dev[self.dev_resource]['id']}])
|
||||||
self.assertEqual(409, res.status_int)
|
self.assertEqual(409, res.status_int)
|
||||||
|
|
||||||
def test_list_network_gateways(self):
|
def test_list_network_gateways(self):
|
||||||
with self._network_gateway(name='test-gw-1') as gw1:
|
with self._network_gateway(name='test-gw-1') as gw1:
|
||||||
with self._network_gateway(name='test_gw_2') as gw2:
|
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))
|
res = self.deserialize('json', req.get_response(self.ext_api))
|
||||||
# We expect the default gateway too
|
# We expect the default gateway too
|
||||||
key = self.resource + 's'
|
key = self.gw_resource + 's'
|
||||||
self.assertEqual(len(res[key]), 3)
|
self.assertEqual(len(res[key]), 3)
|
||||||
self.assertEqual(res[key][0]['default'],
|
self.assertEqual(res[key][0]['default'],
|
||||||
True)
|
True)
|
||||||
self.assertEqual(res[key][1]['name'],
|
self.assertEqual(res[key][1]['name'],
|
||||||
gw1[self.resource]['name'])
|
gw1[self.gw_resource]['name'])
|
||||||
self.assertEqual(res[key][2]['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):
|
def test_list_network_gateway_with_multiple_connections(self):
|
||||||
self._test_list_network_gateway_with_multiple_connections(
|
self._test_list_network_gateway_with_multiple_connections(
|
||||||
expected_gateways=2)
|
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):
|
def test_show_network_gateway_nsx_error_returns_404(self):
|
||||||
invalid_id = 'b5afd4a9-eb71-4af7-a082-8fc625a35b61'
|
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)
|
res = req.get_response(self.ext_api)
|
||||||
self.assertEqual(exc.HTTPNotFound.code, res.status_int)
|
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,
|
class TestNetworkGatewayPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
networkgw_db.NetworkGatewayMixin):
|
networkgw_db.NetworkGatewayMixin):
|
||||||
|
@ -14,7 +14,11 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from neutron.openstack.common import jsonutils
|
||||||
from neutron.plugins.vmware.api_client import exception
|
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 import nsxlib
|
||||||
from neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
|
from neutron.plugins.vmware.nsxlib import l2gateway as l2gwlib
|
||||||
from neutron.plugins.vmware.nsxlib import switch as switchlib
|
from neutron.plugins.vmware.nsxlib import switch as switchlib
|
||||||
@ -145,3 +149,148 @@ class L2GatewayTestCase(base.NsxlibTestCase):
|
|||||||
self.assertIn('LogicalPortAttachment', resp_obj)
|
self.assertIn('LogicalPortAttachment', resp_obj)
|
||||||
self.assertEqual(resp_obj['LogicalPortAttachment']['type'],
|
self.assertEqual(resp_obj['LogicalPortAttachment']['type'],
|
||||||
'L2GatewayAttachment')
|
'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