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