Merge "NSX|v3: provider networks updates"
This commit is contained in:
commit
bd6de55f7c
@ -0,0 +1,8 @@
|
||||
---
|
||||
prelude: >
|
||||
Adding support for Geneve and nSX-network provider networks.
|
||||
features:
|
||||
- |
|
||||
Deprecating the VXLAN provider network type.
|
||||
Adding Geneve provider networks (with overlay transport zone).
|
||||
Adding nsx-net provider networks attached to an existing nsx
|
@ -67,7 +67,8 @@ class NsxV3NetworkTypes(object):
|
||||
"""Allowed provider network types for the NSXv3 Plugin."""
|
||||
FLAT = 'flat'
|
||||
VLAN = 'vlan'
|
||||
VXLAN = 'vxlan'
|
||||
GENEVE = 'geneve'
|
||||
NSX_NETWORK = 'nsx-net'
|
||||
|
||||
|
||||
def is_nsx_version_1_1_0(nsx_version):
|
||||
|
@ -1 +1 @@
|
||||
84ceffa27115
|
||||
a1be06050b41
|
@ -0,0 +1,60 @@
|
||||
# Copyright 2017 VMware, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""update nsx binding types
|
||||
|
||||
Revision ID: a1be06050b41
|
||||
Revises: 84ceffa27115
|
||||
Create Date: 2017-09-04 23:58:22.003350
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a1be06050b41'
|
||||
down_revision = '84ceffa27115'
|
||||
depends_on = ('aede17d51d0f')
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import migration as neutron_op
|
||||
|
||||
|
||||
all_tz_binding_type_enum = sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
|
||||
'vxlan', 'geneve', 'portgroup', 'nsx-net',
|
||||
name='tz_network_bindings_binding_type')
|
||||
|
||||
new_tz_binding_type_enum = sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
|
||||
'geneve', 'portgroup', 'nsx-net',
|
||||
name='tz_network_bindings_binding_type')
|
||||
|
||||
|
||||
def upgrade():
|
||||
# add the new network types to the enum
|
||||
neutron_op.alter_enum_add_value(
|
||||
'tz_network_bindings',
|
||||
'binding_type',
|
||||
all_tz_binding_type_enum,
|
||||
False)
|
||||
|
||||
# change existing entries with type 'vxlan' to 'geneve'
|
||||
op.execute("UPDATE tz_network_bindings SET binding_type='geneve' "
|
||||
"where binding_type='vxlan'")
|
||||
|
||||
# remove 'vxlan' from the enum
|
||||
op.alter_column(
|
||||
'tz_network_bindings',
|
||||
'binding_type',
|
||||
type_=new_tz_binding_type_enum,
|
||||
existing_type=all_tz_binding_type_enum,
|
||||
existing_nullable=False)
|
@ -43,9 +43,9 @@ class TzNetworkBinding(model_base.BASEV2, models.TimestampMixin):
|
||||
network_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('networks.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
# 'flat', 'vlan', 'stt', 'gre', 'l3_ext', 'vxlan', 'portgroup'
|
||||
# 'flat', 'vlan', 'stt', 'gre', 'l3_ext', 'geneve', 'portgroup', 'nsx-net'
|
||||
binding_type = sa.Column(sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
|
||||
'vxlan', 'portgroup',
|
||||
'geneve', 'portgroup', 'nsx-net',
|
||||
name='tz_network_bindings_binding_type'),
|
||||
nullable=False, primary_key=True)
|
||||
phy_uuid = sa.Column(sa.String(36), primary_key=True, default='')
|
||||
|
@ -693,12 +693,30 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
if bindings:
|
||||
raise n_exc.VlanIdInUse(
|
||||
vlan_id=vlan_id, physical_network=physical_net)
|
||||
elif net_type == utils.NsxV3NetworkTypes.VXLAN:
|
||||
elif net_type == utils.NsxV3NetworkTypes.GENEVE:
|
||||
if vlan_id:
|
||||
err_msg = (_("Segmentation ID cannot be specified with "
|
||||
"%s network type") %
|
||||
utils.NsxV3NetworkTypes.VXLAN)
|
||||
utils.NsxV3NetworkTypes.GENEVE)
|
||||
tz_type = self.nsxlib.transport_zone.TRANSPORT_TYPE_OVERLAY
|
||||
elif net_type == utils.NsxV3NetworkTypes.NSX_NETWORK:
|
||||
# Linking neutron networks to an existing NSX logical switch
|
||||
if physical_net is None:
|
||||
err_msg = (_("Physical network must be specified with "
|
||||
"%s network type") % net_type)
|
||||
# Validate the logical switch existence
|
||||
try:
|
||||
self.nsxlib.logical_switch.get(physical_net)
|
||||
except nsx_lib_exc.ResourceNotFound:
|
||||
err_msg = (_('Logical switch %s does not exist') %
|
||||
physical_net)
|
||||
# make sure no other neutron network is using it
|
||||
bindings = (
|
||||
nsx_db.get_network_bindings_by_vlanid_and_physical_net(
|
||||
context.elevated().session, 0, physical_net))
|
||||
if bindings:
|
||||
err_msg = (_('Logical switch %s is already used by '
|
||||
'another network') % physical_net)
|
||||
else:
|
||||
err_msg = (_('%(net_type_param)s %(net_type_value)s not '
|
||||
'supported') %
|
||||
@ -718,7 +736,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
physical_net = az._default_overlay_tz_uuid
|
||||
|
||||
# validate the transport zone existence and type
|
||||
if not err_msg and is_provider_net and physical_net:
|
||||
if (not err_msg and is_provider_net and physical_net and
|
||||
net_type != utils.NsxV3NetworkTypes.NSX_NETWORK):
|
||||
try:
|
||||
backend_type = self.nsxlib.transport_zone.get_transport_type(
|
||||
physical_net)
|
||||
@ -752,43 +771,68 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
def _create_network_at_the_backend(self, context, net_data, az):
|
||||
is_provider_net, net_type, physical_net, vlan_id = (
|
||||
self._validate_provider_create(context, net_data, az))
|
||||
neutron_net_id = net_data.get('id') or uuidutils.generate_uuid()
|
||||
# To ensure that the correct tag will be set
|
||||
net_data['id'] = neutron_net_id
|
||||
# update the network name to indicate the neutron id too.
|
||||
net_name = utils.get_name_and_uuid(net_data['name'] or 'network',
|
||||
neutron_net_id)
|
||||
tags = self.nsxlib.build_v3_tags_payload(
|
||||
net_data, resource_type='os-neutron-net-id',
|
||||
project_name=context.tenant_name)
|
||||
|
||||
admin_state = net_data.get('admin_state_up', True)
|
||||
if is_provider_net and net_type == utils.NsxV3NetworkTypes.NSX_NETWORK:
|
||||
# Network already exists on the NSX backend
|
||||
nsx_id = physical_net
|
||||
else:
|
||||
# Create network on the backend
|
||||
neutron_net_id = net_data.get('id') or uuidutils.generate_uuid()
|
||||
# To ensure that the correct tag will be set
|
||||
net_data['id'] = neutron_net_id
|
||||
# update the network name to indicate the neutron id too.
|
||||
net_name = utils.get_name_and_uuid(net_data['name'] or 'network',
|
||||
neutron_net_id)
|
||||
tags = self.nsxlib.build_v3_tags_payload(
|
||||
net_data, resource_type='os-neutron-net-id',
|
||||
project_name=context.tenant_name)
|
||||
|
||||
# Create network on the backend
|
||||
LOG.debug('create_network: %(net_name)s, %(physical_net)s, '
|
||||
'%(tags)s, %(admin_state)s, %(vlan_id)s',
|
||||
{'net_name': net_name,
|
||||
'physical_net': physical_net,
|
||||
'tags': tags,
|
||||
'admin_state': admin_state,
|
||||
'vlan_id': vlan_id})
|
||||
nsx_result = self.nsxlib.logical_switch.create(
|
||||
net_name, physical_net, tags,
|
||||
admin_state=admin_state,
|
||||
vlan_id=vlan_id,
|
||||
description=net_data.get('description'))
|
||||
admin_state = net_data.get('admin_state_up', True)
|
||||
LOG.debug('create_network: %(net_name)s, %(physical_net)s, '
|
||||
'%(tags)s, %(admin_state)s, %(vlan_id)s',
|
||||
{'net_name': net_name,
|
||||
'physical_net': physical_net,
|
||||
'tags': tags,
|
||||
'admin_state': admin_state,
|
||||
'vlan_id': vlan_id})
|
||||
nsx_result = self.nsxlib.logical_switch.create(
|
||||
net_name, physical_net, tags,
|
||||
admin_state=admin_state,
|
||||
vlan_id=vlan_id,
|
||||
description=net_data.get('description'))
|
||||
nsx_id = nsx_result['id']
|
||||
|
||||
return (is_provider_net,
|
||||
net_type,
|
||||
physical_net,
|
||||
vlan_id,
|
||||
nsx_result['id'])
|
||||
nsx_id)
|
||||
|
||||
def _is_overlay_network(self, context, network_id):
|
||||
"""Return True if this is an overlay network
|
||||
|
||||
1. No binding ("normal" overlay networks will have no binding)
|
||||
2. Geneve network
|
||||
3. nsx network where the backend network is connected to an overlay TZ
|
||||
"""
|
||||
bindings = nsx_db.get_network_bindings(context.session, network_id)
|
||||
# With NSX plugin, "normal" overlay networks will have no binding
|
||||
return (not bindings or
|
||||
bindings[0].binding_type == utils.NsxV3NetworkTypes.VXLAN)
|
||||
if not bindings:
|
||||
return True
|
||||
binding = bindings[0]
|
||||
if binding.binding_type == utils.NsxV3NetworkTypes.GENEVE:
|
||||
return True
|
||||
if binding.binding_type == utils.NsxV3NetworkTypes.NSX_NETWORK:
|
||||
# check the backend network
|
||||
# TODO(asarfaty): Keep TZ type in DB to avoid going to the backend
|
||||
ls = self.nsxlib.logical_switch.get(binding.phy_uuid)
|
||||
tz = ls.get('transport_zone_id')
|
||||
if tz:
|
||||
backend_type = self.nsxlib.transport_zone.get_transport_type(
|
||||
tz)
|
||||
return (backend_type ==
|
||||
self.nsxlib.transport_zone.TRANSPORT_TYPE_OVERLAY)
|
||||
return False
|
||||
|
||||
def _extend_network_dict_provider(self, context, network, bindings=None):
|
||||
if 'id' not in network:
|
||||
@ -824,6 +868,13 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
return super(NsxV3Plugin, self).get_subnets(
|
||||
context, filters, fields, sorts, limit, marker, page_reverse)
|
||||
|
||||
def _network_is_nsx_net(self, context, network_id):
|
||||
bindings = nsx_db.get_network_bindings(context.session, network_id)
|
||||
if not bindings:
|
||||
return False
|
||||
return (bindings[0].binding_type ==
|
||||
utils.NsxV3NetworkTypes.NSX_NETWORK)
|
||||
|
||||
def create_network(self, context, network):
|
||||
net_data = network['network']
|
||||
external = net_data.get(ext_net_extn.EXTERNAL)
|
||||
@ -979,17 +1030,27 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
self._disable_native_dhcp(context, network_id)
|
||||
|
||||
nsx_net_id = self._get_network_nsx_id(context, network_id)
|
||||
is_nsx_net = self._network_is_nsx_net(context, network_id)
|
||||
is_overlay_network = self._is_overlay_network(context, network_id)
|
||||
# First call DB operation for delete network as it will perform
|
||||
# checks on active ports
|
||||
self._retry_delete_network(context, network_id)
|
||||
if not self._network_is_external(context, network_id):
|
||||
if (not self._network_is_external(context, network_id) and
|
||||
not is_nsx_net):
|
||||
# TODO(salv-orlando): Handle backend failure, possibly without
|
||||
# requiring us to un-delete the DB object. For instance, ignore
|
||||
# failures occurring if logical switch is not found
|
||||
self.nsxlib.logical_switch.delete(nsx_net_id)
|
||||
else:
|
||||
if (cfg.CONF.nsx_v3.native_dhcp_metadata and is_nsx_net and
|
||||
is_overlay_network):
|
||||
# Delete the mdproxy port manually
|
||||
port_id = self.nsxlib.get_id_by_resource_and_tag(
|
||||
self.nsxlib.logical_port.resource_type,
|
||||
'os-neutron-net-id', network_id)
|
||||
if port_id:
|
||||
self.nsxlib.logical_port.delete(port_id)
|
||||
# TODO(berlin): delete subnets public announce on the network
|
||||
pass
|
||||
|
||||
def _get_network_nsx_id(self, context, neutron_id):
|
||||
# get the nsx switch id from the DB mapping
|
||||
@ -1010,6 +1071,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
# Neutron does not support changing provider network values
|
||||
providernet._raise_if_updates_provider_attributes(net_data)
|
||||
extern_net = self._network_is_external(context, id)
|
||||
is_nsx_net = self._network_is_nsx_net(context, id)
|
||||
if extern_net:
|
||||
self._assert_on_external_net_with_qos(net_data)
|
||||
updated_net = super(NsxV3Plugin, self).update_network(context, id,
|
||||
@ -1022,7 +1084,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
self._process_l3_update(context, updated_net, network['network'])
|
||||
self._extend_network_dict_provider(context, updated_net)
|
||||
|
||||
if (not extern_net and
|
||||
if (not extern_net and not is_nsx_net and
|
||||
('name' in net_data or 'admin_state_up' in net_data or
|
||||
'description' in net_data)):
|
||||
try:
|
||||
@ -3186,7 +3248,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
def _validate_multiple_subnets_routers(self, context, router_id, net_id):
|
||||
network = self.get_network(context, net_id)
|
||||
net_type = network.get(pnet.NETWORK_TYPE)
|
||||
if (net_type and net_type != utils.NsxV3NetworkTypes.VXLAN):
|
||||
if (net_type and net_type != utils.NsxV3NetworkTypes.GENEVE):
|
||||
err_msg = (_("Only overlay networks can be attached to a logical "
|
||||
"router. Network %(net_id)s is a %(net_type)s based "
|
||||
"network") % {'net_id': net_id, 'net_type': net_type})
|
||||
|
@ -78,6 +78,10 @@ def _mock_create_firewall_rules(*args):
|
||||
]}
|
||||
|
||||
|
||||
def _return_id_key(*args, **kwargs):
|
||||
return {'id': uuidutils.generate_uuid()}
|
||||
|
||||
|
||||
def _mock_nsx_backend_calls():
|
||||
mock.patch("vmware_nsxlib.v3.client.NSX3Client").start()
|
||||
|
||||
@ -85,9 +89,6 @@ def _mock_nsx_backend_calls():
|
||||
'resource_type': 'FakeResource',
|
||||
'id': uuidutils.generate_uuid()}
|
||||
|
||||
def _return_id_key(*args, **kwargs):
|
||||
return {'id': uuidutils.generate_uuid()}
|
||||
|
||||
def _return_id(*args, **kwargs):
|
||||
return uuidutils.generate_uuid()
|
||||
|
||||
@ -113,11 +114,6 @@ def _mock_nsx_backend_calls():
|
||||
"get_id_by_name_or_id",
|
||||
return_value=uuidutils.generate_uuid()).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.core_resources.NsxLibTransportZone."
|
||||
"get_id_by_name_or_id",
|
||||
return_value=uuidutils.generate_uuid()).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.core_resources.NsxLibBridgeEndpoint.create",
|
||||
side_effect=_return_id_key).start()
|
||||
@ -284,6 +280,167 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
|
||||
networks = self.plugin.get_networks(ctx)
|
||||
self.assertListEqual([], networks)
|
||||
|
||||
def test_create_provider_flat_network(self):
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'flat'}
|
||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.'
|
||||
'create', side_effect=_return_id_key) as nsx_create, \
|
||||
mock.patch('vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.'
|
||||
'delete') as nsx_delete, \
|
||||
mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='VLAN'),\
|
||||
self.network(name='flat_net',
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE, )) as net:
|
||||
self.assertEqual('flat', net['network'].get(pnet.NETWORK_TYPE))
|
||||
# make sure the network is created at the backend
|
||||
nsx_create.assert_called_once()
|
||||
|
||||
# Delete the network and make sure it is deleted from the backend
|
||||
req = self.new_delete_request('networks', net['network']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
|
||||
nsx_delete.assert_called_once()
|
||||
|
||||
def test_create_provider_flat_network_with_physical_net(self):
|
||||
physical_network = nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'flat',
|
||||
pnet.PHYSICAL_NETWORK: physical_network}
|
||||
with mock.patch(
|
||||
'vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='VLAN'),\
|
||||
self.network(name='flat_net',
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.PHYSICAL_NETWORK)) as net:
|
||||
self.assertEqual('flat', net['network'].get(pnet.NETWORK_TYPE))
|
||||
|
||||
def test_create_provider_flat_network_with_vlan(self):
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'flat',
|
||||
pnet.SEGMENTATION_ID: 11}
|
||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='VLAN'):
|
||||
result = self._create_network(fmt='json', name='bad_flat_net',
|
||||
admin_state_up=True,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.SEGMENTATION_ID))
|
||||
data = self.deserialize('json', result)
|
||||
# should fail
|
||||
self.assertEqual('InvalidInput', data['NeutronError']['type'])
|
||||
|
||||
def test_create_provider_geneve_network(self):
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'geneve'}
|
||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.'
|
||||
'create', side_effect=_return_id_key) as nsx_create, \
|
||||
mock.patch('vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.'
|
||||
'delete') as nsx_delete, \
|
||||
mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='OVERLAY'),\
|
||||
self.network(name='geneve_net',
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE, )) as net:
|
||||
self.assertEqual('geneve', net['network'].get(pnet.NETWORK_TYPE))
|
||||
# make sure the network is created at the backend
|
||||
nsx_create.assert_called_once()
|
||||
|
||||
# Delete the network and make sure it is deleted from the backend
|
||||
req = self.new_delete_request('networks', net['network']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
|
||||
nsx_delete.assert_called_once()
|
||||
|
||||
def test_create_provider_geneve_network_with_physical_net(self):
|
||||
physical_network = nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'geneve',
|
||||
pnet.PHYSICAL_NETWORK: physical_network}
|
||||
with mock.patch(
|
||||
'vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='OVERLAY'),\
|
||||
self.network(name='geneve_net',
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE, )) as net:
|
||||
self.assertEqual('geneve', net['network'].get(pnet.NETWORK_TYPE))
|
||||
|
||||
def test_create_provider_geneve_network_with_vlan(self):
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'geneve',
|
||||
pnet.SEGMENTATION_ID: 11}
|
||||
with mock.patch(
|
||||
'vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='OVERLAY'):
|
||||
result = self._create_network(fmt='json', name='bad_geneve_net',
|
||||
admin_state_up=True,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.SEGMENTATION_ID))
|
||||
data = self.deserialize('json', result)
|
||||
# should fail
|
||||
self.assertEqual('InvalidInput', data['NeutronError']['type'])
|
||||
|
||||
def test_create_provider_vlan_network(self):
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'vlan',
|
||||
pnet.SEGMENTATION_ID: 11}
|
||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.'
|
||||
'create', side_effect=_return_id_key) as nsx_create, \
|
||||
mock.patch('vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.'
|
||||
'delete') as nsx_delete, \
|
||||
mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='VLAN'),\
|
||||
self.network(name='vlan_net',
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.SEGMENTATION_ID)) as net:
|
||||
self.assertEqual('vlan', net['network'].get(pnet.NETWORK_TYPE))
|
||||
# make sure the network is created at the backend
|
||||
nsx_create.assert_called_once()
|
||||
|
||||
# Delete the network and make sure it is deleted from the backend
|
||||
req = self.new_delete_request('networks', net['network']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
|
||||
nsx_delete.assert_called_once()
|
||||
|
||||
def test_create_provider_nsx_network(self):
|
||||
physical_network = 'Fake logical switch'
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'nsx-net',
|
||||
pnet.PHYSICAL_NETWORK: physical_network}
|
||||
|
||||
with mock.patch(
|
||||
'vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.create',
|
||||
side_effect=nsxlib_exc.ResourceNotFound) as nsx_create, \
|
||||
mock.patch('vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.'
|
||||
'delete') as nsx_delete, \
|
||||
self.network(name='nsx_net',
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.PHYSICAL_NETWORK)) as net:
|
||||
self.assertEqual('nsx-net', net['network'].get(pnet.NETWORK_TYPE))
|
||||
self.assertEqual(physical_network,
|
||||
net['network'].get(pnet.PHYSICAL_NETWORK))
|
||||
# make sure the network is NOT created at the backend
|
||||
nsx_create.assert_not_called()
|
||||
|
||||
# Delete the network. It should NOT deleted from the backend
|
||||
req = self.new_delete_request('networks', net['network']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
|
||||
nsx_delete.assert_not_called()
|
||||
|
||||
def test_create_provider_bad_nsx_network(self):
|
||||
physical_network = 'Bad logical switch'
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'nsx-net',
|
||||
pnet.PHYSICAL_NETWORK: physical_network}
|
||||
with mock.patch(
|
||||
"vmware_nsxlib.v3.core_resources.NsxLibLogicalSwitch.get",
|
||||
side_effect=nsxlib_exc.ResourceNotFound):
|
||||
result = self._create_network(fmt='json', name='bad_nsx_net',
|
||||
admin_state_up=True,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(pnet.NETWORK_TYPE,
|
||||
pnet.PHYSICAL_NETWORK))
|
||||
data = self.deserialize('json', result)
|
||||
# should fail
|
||||
self.assertEqual('InvalidInput', data['NeutronError']['type'])
|
||||
|
||||
|
||||
class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user