NSX|V3 IPAM support
The NSX-V3 plugin will use the NSX-V3 backend IPAM. An IP pool will be created for each subnet, and port IPs will be allocated from this pool. The current backend limitation is that we cannot allocate a specific IP, so port create/update with fixed_ips will fail, unless the requested ip is the subnet gateway ip. To enable this option set 'ipam_driver = vmware_nsxv3_ipam' in the neutron.conf Change-Id: I5263555cbb776018a5d01f19d0997fd2adf6483d
This commit is contained in:
parent
95231630f2
commit
1266099049
11
releasenotes/notes/nsxv3-ipam-support-137174152c65459d.yaml
Normal file
11
releasenotes/notes/nsxv3-ipam-support-137174152c65459d.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
prelude: >
|
||||
The NSX-v3 plugin can use the platform IPAM for ip allocations for all
|
||||
network types.
|
||||
features:
|
||||
- The NSX-v3 plugin can use the platform IPAM for ip allocations for all
|
||||
network types.
|
||||
In order to use this feature, the ipam_driver in the neutron.conf file
|
||||
should be set to vmware_nsxv3_ipam.
|
||||
Currently the plugin does not support allocating a specific address
|
||||
from the pool depending on the NSX version.
|
@ -37,6 +37,7 @@ neutron.qos.notification_drivers =
|
||||
vmware_nsxv3_message_queue = vmware_nsx.services.qos.nsx_v3.message_queue:NsxV3QosNotificationDriver
|
||||
neutron.ipam_drivers =
|
||||
vmware_nsxv_ipam = vmware_nsx.services.ipam.nsx_v.driver:NsxvIpamDriver
|
||||
vmware_nsxv3_ipam = vmware_nsx.services.ipam.nsx_v3.driver:Nsxv3IpamDriver
|
||||
vmware_nsx.neutron.nsxv.router_type_drivers =
|
||||
shared = vmware_nsx.plugins.nsx_v.drivers.shared_router_driver:RouterSharedDriver
|
||||
distributed = vmware_nsx.plugins.nsx_v.drivers.distributed_router_driver:RouterDistributedDriver
|
||||
|
@ -423,3 +423,28 @@ def save_sg_rule_mappings(session, rules):
|
||||
mapping = nsx_models.NeutronNsxRuleMapping(
|
||||
neutron_id=neutron_id, nsx_id=nsx_id)
|
||||
session.add(mapping)
|
||||
|
||||
|
||||
def add_nsx_ipam_subnet_pool(session, subnet_id, nsx_pool_id):
|
||||
with session.begin(subtransactions=True):
|
||||
binding = nsx_models.NsxSubnetIpam(
|
||||
subnet_id=subnet_id,
|
||||
nsx_pool_id=nsx_pool_id)
|
||||
session.add(binding)
|
||||
return binding
|
||||
|
||||
|
||||
def get_nsx_ipam_pool_for_subnet(session, subnet_id):
|
||||
try:
|
||||
entry = session.query(
|
||||
nsx_models.NsxSubnetIpam).filter_by(
|
||||
subnet_id=subnet_id).one()
|
||||
return entry.nsx_pool_id
|
||||
except exc.NoResultFound:
|
||||
return
|
||||
|
||||
|
||||
def del_nsx_ipam_subnet_pool(session, subnet_id, nsx_pool_id):
|
||||
return (session.query(nsx_models.NsxSubnetIpam).
|
||||
filter_by(subnet_id=subnet_id,
|
||||
nsx_pool_id=nsx_pool_id).delete())
|
||||
|
@ -1 +1 @@
|
||||
d49ac91b560e
|
||||
5c8f451290b7
|
@ -0,0 +1,33 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
"""nsxv_subnet_ipam rename to nsx_subnet_ipam
|
||||
|
||||
Revision ID: 5c8f451290b7
|
||||
Revises: d49ac91b560e
|
||||
Create Date: 2016-12-25 11:08:30.300482
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5c8f451290b7'
|
||||
down_revision = 'd49ac91b560e'
|
||||
depends_on = ('6e6da8296c0e',)
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.rename_table('nsxv_subnet_ipam',
|
||||
'nsx_subnet_ipam')
|
@ -368,3 +368,12 @@ class NsxPortMirrorSessionMapping(model_base.BASEV2):
|
||||
nullable=False,
|
||||
primary_key=True)
|
||||
port_mirror_session_id = sa.Column(sa.String(36), nullable=False)
|
||||
|
||||
|
||||
class NsxSubnetIpam(model_base.BASEV2, models.TimestampMixin):
|
||||
"""Map Subnets with their backend pool id."""
|
||||
__tablename__ = 'nsx_subnet_ipam'
|
||||
# the Subnet id is not a foreign key because the subnet is deleted
|
||||
# before the pool does
|
||||
subnet_id = sa.Column(sa.String(36), primary_key=True)
|
||||
nsx_pool_id = sa.Column(sa.String(36), primary_key=True)
|
||||
|
@ -803,28 +803,3 @@ def update_nsxv_subnet_ext_attributes(session, subnet_id,
|
||||
binding[ext_dns_search_domain.DNS_SEARCH_DOMAIN] = dns_search_domain
|
||||
binding[ext_dhcp_mtu.DHCP_MTU] = dhcp_mtu
|
||||
return binding
|
||||
|
||||
|
||||
def add_nsxv_ipam_subnet_pool(session, subnet_id, nsx_pool_id):
|
||||
with session.begin(subtransactions=True):
|
||||
binding = nsxv_models.NsxvSubnetIpam(
|
||||
subnet_id=subnet_id,
|
||||
nsx_pool_id=nsx_pool_id)
|
||||
session.add(binding)
|
||||
return binding
|
||||
|
||||
|
||||
def get_nsxv_ipam_pool_for_subnet(session, subnet_id):
|
||||
try:
|
||||
entry = session.query(
|
||||
nsxv_models.NsxvSubnetIpam).filter_by(
|
||||
subnet_id=subnet_id).one()
|
||||
return entry.nsx_pool_id
|
||||
except exc.NoResultFound:
|
||||
return
|
||||
|
||||
|
||||
def del_nsxv_ipam_subnet_pool(session, subnet_id, nsx_pool_id):
|
||||
return (session.query(nsxv_models.NsxvSubnetIpam).
|
||||
filter_by(subnet_id=subnet_id,
|
||||
nsx_pool_id=nsx_pool_id).delete())
|
||||
|
@ -347,12 +347,3 @@ class NsxvSubnetExtAttributes(model_base.BASEV2, models.TimestampMixin):
|
||||
models_v2.Subnet,
|
||||
backref=orm.backref("nsxv_subnet_attributes", lazy='joined',
|
||||
uselist=False, cascade='delete'))
|
||||
|
||||
|
||||
class NsxvSubnetIpam(model_base.BASEV2, models.TimestampMixin):
|
||||
"""Map Subnets with their backend pool id."""
|
||||
__tablename__ = 'nsxv_subnet_ipam'
|
||||
# the Subnet id is not a foreign key because the subnet is deleted
|
||||
# before the pool does
|
||||
subnet_id = sa.Column(sa.String(36), primary_key=True)
|
||||
nsx_pool_id = sa.Column(sa.String(36), primary_key=True)
|
||||
|
0
vmware_nsx/services/ipam/common/__init__.py
Normal file
0
vmware_nsx/services/ipam/common/__init__.py
Normal file
220
vmware_nsx/services/ipam/common/driver.py
Normal file
220
vmware_nsx/services/ipam/common/driver.py
Normal file
@ -0,0 +1,220 @@
|
||||
# Copyright 2016 VMware, Inc.
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from neutron.ipam import driver as ipam_base
|
||||
from neutron.ipam.drivers.neutrondb_ipam import driver as neutron_driver
|
||||
from neutron.ipam import exceptions as ipam_exc
|
||||
from neutron.ipam import requests as ipam_req
|
||||
from neutron.ipam import subnet_alloc
|
||||
from neutron_lib.plugins import directory
|
||||
|
||||
from vmware_nsx.db import db as nsx_db
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class NsxIpamBase(object):
|
||||
@classmethod
|
||||
def get_core_plugin(cls):
|
||||
return directory.get_plugin()
|
||||
|
||||
@classmethod
|
||||
def _fetch_subnet(cls, context, id):
|
||||
p = cls.get_core_plugin()
|
||||
return p._get_subnet(context, id)
|
||||
|
||||
@classmethod
|
||||
def _fetch_network(cls, context, id):
|
||||
p = cls.get_core_plugin()
|
||||
return p.get_network(context, id)
|
||||
|
||||
|
||||
class NsxSubnetRequestFactory(ipam_req.SubnetRequestFactory, NsxIpamBase):
|
||||
"""Builds request using subnet info, including the network id"""
|
||||
|
||||
@classmethod
|
||||
def get_request(cls, context, subnet, subnetpool):
|
||||
req = super(NsxSubnetRequestFactory, cls).get_request(
|
||||
context, subnet, subnetpool)
|
||||
# Add the network id into the request
|
||||
if 'network_id' in subnet:
|
||||
req.network_id = subnet['network_id']
|
||||
|
||||
return req
|
||||
|
||||
|
||||
class NsxAbstractIpamDriver(subnet_alloc.SubnetAllocator, NsxIpamBase):
|
||||
"""Abstract IPAM Driver For NSX."""
|
||||
|
||||
def __init__(self, subnetpool, context):
|
||||
super(NsxAbstractIpamDriver, self).__init__(subnetpool, context)
|
||||
# in case of unsupported networks (or pre-upgrade networks)
|
||||
# the neutron internal driver will be used
|
||||
self.default_ipam = neutron_driver.NeutronDbPool(subnetpool, context)
|
||||
|
||||
def _is_supported_net(self, subnet_request):
|
||||
"""By default - all networks are supported"""
|
||||
return True
|
||||
|
||||
def get_subnet_request_factory(self):
|
||||
# override the OOB factory to add the network ID
|
||||
return NsxSubnetRequestFactory
|
||||
|
||||
@abc.abstractproperty
|
||||
def _subnet_class(self):
|
||||
"""Return the class of the subnet that should be used."""
|
||||
pass
|
||||
|
||||
def get_subnet(self, subnet_id):
|
||||
"""Retrieve an IPAM subnet."""
|
||||
nsx_pool_id = nsx_db.get_nsx_ipam_pool_for_subnet(
|
||||
self._context.session, subnet_id)
|
||||
if not nsx_pool_id:
|
||||
# Unsupported (or pre-upgrade) network
|
||||
return self.default_ipam.get_subnet(subnet_id)
|
||||
|
||||
return self._subnet_class.load(subnet_id, nsx_pool_id, self._context)
|
||||
|
||||
@abc.abstractmethod
|
||||
def allocate_backend_pool(self, subnet_request):
|
||||
"""Create a pool on the NSX backend and return its ID"""
|
||||
pass
|
||||
|
||||
def allocate_subnet(self, subnet_request):
|
||||
"""Create an IPAMSubnet object for the provided request."""
|
||||
if not self._is_supported_net(subnet_request=subnet_request):
|
||||
# fallback to the neutron internal driver implementation
|
||||
return self.default_ipam.allocate_subnet(subnet_request)
|
||||
|
||||
if self._subnetpool:
|
||||
subnet = super(NsxAbstractIpamDriver, self).allocate_subnet(
|
||||
subnet_request)
|
||||
subnet_request = subnet.get_details()
|
||||
|
||||
# SubnetRequest must be an instance of SpecificSubnet
|
||||
if not isinstance(subnet_request, ipam_req.SpecificSubnetRequest):
|
||||
raise ipam_exc.InvalidSubnetRequestType(
|
||||
subnet_type=type(subnet_request))
|
||||
|
||||
# Add the pool to the NSX backend
|
||||
nsx_pool_id = self.allocate_backend_pool(subnet_request)
|
||||
|
||||
# Add the pool to the DB
|
||||
nsx_db.add_nsx_ipam_subnet_pool(self._context.session,
|
||||
subnet_request.subnet_id,
|
||||
nsx_pool_id)
|
||||
# return the subnet object
|
||||
return self._subnet_class.load(subnet_request.subnet_id, nsx_pool_id,
|
||||
self._context,
|
||||
tenant_id=subnet_request.tenant_id)
|
||||
|
||||
def _raise_update_not_supported(self):
|
||||
msg = _('Changing the subnet range or gateway is not supported')
|
||||
raise ipam_exc.IpamValueInvalid(message=msg)
|
||||
|
||||
def update_subnet(self, subnet_request):
|
||||
"""Update subnet info in the IPAM driver.
|
||||
|
||||
The NSX backend does not support changing the ip pool cidr or gateway
|
||||
"""
|
||||
#TODO(asarfaty): the nsx-v3 backend does support update
|
||||
nsx_pool_id = nsx_db.get_nsx_ipam_pool_for_subnet(
|
||||
self._context.session, subnet_request.subnet_id)
|
||||
if not nsx_pool_id:
|
||||
# Unsupported (or pre-upgrade) network
|
||||
return self.default_ipam.update_subnet(
|
||||
subnet_request)
|
||||
|
||||
# get the current pool data
|
||||
curr_subnet = self._subnet_class.load(
|
||||
subnet_request.subnet_id, nsx_pool_id,
|
||||
self._context, tenant_id=subnet_request.tenant_id).get_details()
|
||||
|
||||
# check that the gateway / cidr / pools did not change
|
||||
if (subnet_request.gateway_ip and
|
||||
str(subnet_request.gateway_ip) != str(curr_subnet.gateway_ip)):
|
||||
self._raise_update_not_supported()
|
||||
|
||||
if subnet_request.prefixlen != curr_subnet.prefixlen:
|
||||
self._raise_update_not_supported()
|
||||
|
||||
if (len(subnet_request.allocation_pools) !=
|
||||
len(curr_subnet.allocation_pools)):
|
||||
self._raise_update_not_supported()
|
||||
|
||||
for pool_ind in range(len(subnet_request.allocation_pools)):
|
||||
pool_req = subnet_request.allocation_pools[pool_ind]
|
||||
curr_pool = curr_subnet.allocation_pools[pool_ind]
|
||||
if (pool_req.first != curr_pool.first or
|
||||
pool_req.last != curr_pool.last):
|
||||
self._raise_update_not_supported()
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_backend_pool(self, nsx_pool_id):
|
||||
pass
|
||||
|
||||
def remove_subnet(self, subnet_id):
|
||||
"""Delete an IPAM subnet pool from backend & DB."""
|
||||
nsx_pool_id = nsx_db.get_nsx_ipam_pool_for_subnet(
|
||||
self._context.session, subnet_id)
|
||||
if not nsx_pool_id:
|
||||
# Unsupported (or pre-upgrade) network
|
||||
self.default_ipam.remove_subnet(subnet_id)
|
||||
return
|
||||
|
||||
# Delete from backend
|
||||
self.delete_backend_pool(nsx_pool_id)
|
||||
|
||||
# delete pool from DB
|
||||
nsx_db.del_nsx_ipam_subnet_pool(self._context.session,
|
||||
subnet_id, nsx_pool_id)
|
||||
|
||||
|
||||
class NsxAbstractIpamSubnet(ipam_base.Subnet, NsxIpamBase):
|
||||
"""Manage IP addresses for the NSX IPAM driver."""
|
||||
|
||||
def __init__(self, subnet_id, nsx_pool_id, ctx, tenant_id):
|
||||
self._subnet_id = subnet_id
|
||||
self._nsx_pool_id = nsx_pool_id
|
||||
self._context = ctx
|
||||
self._tenant_id = tenant_id
|
||||
|
||||
@classmethod
|
||||
def load(cls, neutron_subnet_id, nsx_pool_id, ctx, tenant_id=None):
|
||||
"""Load an IPAM subnet object given its neutron ID."""
|
||||
return cls(neutron_subnet_id, nsx_pool_id, ctx, tenant_id)
|
||||
|
||||
def allocate(self, address_request):
|
||||
"""Allocate an IP from the pool"""
|
||||
return self.backend_allocate(address_request)
|
||||
|
||||
@abc.abstractmethod
|
||||
def backend_allocate(self, address_request):
|
||||
pass
|
||||
|
||||
def deallocate(self, address):
|
||||
"""Return an IP to the pool"""
|
||||
self.backend_deallocate(address)
|
||||
|
||||
@abc.abstractmethod
|
||||
def backend_deallocate(self, address):
|
||||
pass
|
||||
|
||||
def update_allocation_pools(self, pools, cidr):
|
||||
# Not supported
|
||||
pass
|
@ -17,66 +17,34 @@
|
||||
import netaddr
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.extensions import external_net as ext_net_extn
|
||||
from neutron.extensions import multiprovidernet as mpnet
|
||||
from neutron.extensions import providernet as pnet
|
||||
from neutron.ipam import driver as ipam_base
|
||||
from neutron.ipam.drivers.neutrondb_ipam import driver as neutron_driver
|
||||
from neutron.ipam import exceptions as ipam_exc
|
||||
from neutron.ipam import requests as ipam_req
|
||||
from neutron.ipam import subnet_alloc
|
||||
from neutron_lib.api import validators
|
||||
from neutron_lib.plugins import directory
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vmware_nsx._i18n import _, _LE
|
||||
from vmware_nsx.common import locking
|
||||
from vmware_nsx.db import nsxv_db
|
||||
from vmware_nsx.plugins.nsx_v.vshield.common import constants
|
||||
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions as vc_exc
|
||||
from vmware_nsx.services.ipam.common import driver as common
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NsxvIpamBase(object):
|
||||
@classmethod
|
||||
def get_core_plugin(cls):
|
||||
return directory.get_plugin()
|
||||
|
||||
@classmethod
|
||||
def _fetch_subnet(cls, context, id):
|
||||
p = cls.get_core_plugin()
|
||||
return p._get_subnet(context, id)
|
||||
|
||||
@classmethod
|
||||
def _fetch_network(cls, context, id):
|
||||
p = cls.get_core_plugin()
|
||||
return p.get_network(context, id)
|
||||
class NsxVIpamBase(common.NsxIpamBase):
|
||||
|
||||
@property
|
||||
def _vcns(self):
|
||||
p = self.get_core_plugin()
|
||||
return p.nsx_v.vcns
|
||||
|
||||
def _get_vcns_error_code(self, e):
|
||||
"""Get the error code out of VcnsApiException"""
|
||||
try:
|
||||
desc = et.fromstring(e.response)
|
||||
return int(desc.find('errorCode').text)
|
||||
except Exception:
|
||||
LOG.error(_LE('IPAM pool: Error code not present. %s'),
|
||||
e.response)
|
||||
|
||||
|
||||
class NsxvIpamDriver(subnet_alloc.SubnetAllocator, NsxvIpamBase):
|
||||
class NsxvIpamDriver(common.NsxAbstractIpamDriver, NsxVIpamBase):
|
||||
"""IPAM Driver For NSX-V external & provider networks."""
|
||||
|
||||
def __init__(self, subnetpool, context):
|
||||
super(NsxvIpamDriver, self).__init__(subnetpool, context)
|
||||
# in case of regular networks (not external, not provider net)
|
||||
# or ipv6 networks, the neutron internal driver will be used
|
||||
self.default_ipam = neutron_driver.NeutronDbPool(subnetpool, context)
|
||||
|
||||
def _is_ext_or_provider_net(self, subnet_request):
|
||||
"""Return True if the network of the request is external or
|
||||
provider network
|
||||
@ -110,19 +78,9 @@ class NsxvIpamDriver(subnet_alloc.SubnetAllocator, NsxvIpamBase):
|
||||
return (self._is_ext_or_provider_net(subnet_request) and
|
||||
not self._is_ipv6_subnet(subnet_request))
|
||||
|
||||
def get_subnet_request_factory(self):
|
||||
# override the OOB factory to add the network ID
|
||||
return NsxvSubnetRequestFactory
|
||||
|
||||
def get_subnet(self, subnet_id):
|
||||
"""Retrieve an IPAM subnet."""
|
||||
nsx_pool_id = nsxv_db.get_nsxv_ipam_pool_for_subnet(
|
||||
self._context.session, subnet_id)
|
||||
if not nsx_pool_id:
|
||||
# Unsupported (or pre-upgrade) network
|
||||
return self.default_ipam.get_subnet(subnet_id)
|
||||
|
||||
return NsxvIpamSubnet.load(subnet_id, nsx_pool_id, self._context)
|
||||
@property
|
||||
def _subnet_class(self):
|
||||
return NsxvIpamSubnet
|
||||
|
||||
def allocate_backend_pool(self, subnet_request):
|
||||
"""Create a pool on the NSX backend and return its ID"""
|
||||
@ -151,114 +109,27 @@ class NsxvIpamDriver(subnet_alloc.SubnetAllocator, NsxvIpamBase):
|
||||
|
||||
return nsx_pool_id
|
||||
|
||||
def allocate_subnet(self, subnet_request):
|
||||
"""Create an IPAMSubnet object for the provided request."""
|
||||
if not self._is_supported_net(subnet_request=subnet_request):
|
||||
# fallback to the neutron internal driver implementation
|
||||
return self.default_ipam.allocate_subnet(subnet_request)
|
||||
|
||||
if self._subnetpool:
|
||||
subnet = super(NsxvIpamDriver, self).allocate_subnet(
|
||||
subnet_request)
|
||||
subnet_request = subnet.get_details()
|
||||
|
||||
# SubnetRequest must be an instance of SpecificSubnet
|
||||
if not isinstance(subnet_request, ipam_req.SpecificSubnetRequest):
|
||||
raise ipam_exc.InvalidSubnetRequestType(
|
||||
subnet_type=type(subnet_request))
|
||||
|
||||
# Add the pool to the NSX backend
|
||||
nsx_pool_id = self.allocate_backend_pool(subnet_request)
|
||||
|
||||
# Add the pool to the DB
|
||||
nsxv_db.add_nsxv_ipam_subnet_pool(self._context.session,
|
||||
subnet_request.subnet_id,
|
||||
nsx_pool_id)
|
||||
# return the subnet object
|
||||
return NsxvIpamSubnet(subnet_request.subnet_id, nsx_pool_id,
|
||||
self._context, subnet_request.tenant_id)
|
||||
|
||||
def _raise_update_not_supported(self):
|
||||
msg = _('Changing the subnet range or gateway is not supported')
|
||||
raise ipam_exc.IpamValueInvalid(message=msg)
|
||||
|
||||
def update_subnet(self, subnet_request):
|
||||
"""Update subnet info in the IPAM driver.
|
||||
|
||||
The NSX backend does not support changing the ip pool cidr or gateway
|
||||
"""
|
||||
nsx_pool_id = nsxv_db.get_nsxv_ipam_pool_for_subnet(
|
||||
self._context.session, subnet_request.subnet_id)
|
||||
if not nsx_pool_id:
|
||||
# Unsupported (or pre-upgrade) network
|
||||
return self.default_ipam.update_subnet(
|
||||
subnet_request)
|
||||
|
||||
# get the current pool data
|
||||
curr_subnet = NsxvIpamSubnet(
|
||||
subnet_request.subnet_id, nsx_pool_id,
|
||||
self._context, subnet_request.tenant_id).get_details()
|
||||
|
||||
# check that the gateway / cidr / pools did not change
|
||||
if str(subnet_request.gateway_ip) != str(curr_subnet.gateway_ip):
|
||||
self._raise_update_not_supported()
|
||||
|
||||
if subnet_request.prefixlen != curr_subnet.prefixlen:
|
||||
self._raise_update_not_supported()
|
||||
|
||||
if (len(subnet_request.allocation_pools) !=
|
||||
len(curr_subnet.allocation_pools)):
|
||||
self._raise_update_not_supported()
|
||||
|
||||
for pool_ind in range(len(subnet_request.allocation_pools)):
|
||||
pool_req = subnet_request.allocation_pools[pool_ind]
|
||||
curr_pool = curr_subnet.allocation_pools[pool_ind]
|
||||
if (pool_req.first != curr_pool.first or
|
||||
pool_req.last != curr_pool.last):
|
||||
self._raise_update_not_supported()
|
||||
|
||||
def remove_subnet(self, subnet_id):
|
||||
"""Delete an IPAM subnet pool from backend & DB."""
|
||||
nsx_pool_id = nsxv_db.get_nsxv_ipam_pool_for_subnet(
|
||||
self._context.session, subnet_id)
|
||||
if not nsx_pool_id:
|
||||
# Unsupported (or pre-upgrade) network
|
||||
self.default_ipam.remove_subnet(subnet_id)
|
||||
return
|
||||
|
||||
with locking.LockManager.get_lock('nsx-ipam-' + nsx_pool_id):
|
||||
# Delete from backend
|
||||
try:
|
||||
self._vcns.delete_ipam_ip_pool(nsx_pool_id)
|
||||
except vc_exc.VcnsApiException as e:
|
||||
LOG.error(_LE("Failed to delete IPAM from backend: %s"), e)
|
||||
# Continue anyway, since this subnet was already removed
|
||||
|
||||
# delete pool from DB
|
||||
nsxv_db.del_nsxv_ipam_subnet_pool(self._context.session,
|
||||
subnet_id, nsx_pool_id)
|
||||
def delete_backend_pool(self, nsx_pool_id):
|
||||
try:
|
||||
self._vcns.delete_ipam_ip_pool(nsx_pool_id)
|
||||
except vc_exc.VcnsApiException as e:
|
||||
LOG.error(_LE("Failed to delete IPAM from backend: %s"), e)
|
||||
# Continue anyway, since this subnet was already removed
|
||||
|
||||
|
||||
class NsxvIpamSubnet(ipam_base.Subnet, NsxvIpamBase):
|
||||
"""Manage IP addresses for the NSX IPAM driver."""
|
||||
class NsxvIpamSubnet(common.NsxAbstractIpamSubnet, NsxVIpamBase):
|
||||
"""Manage IP addresses for the NSX-V IPAM driver."""
|
||||
|
||||
def __init__(self, subnet_id, nsx_pool_id, ctx, tenant_id):
|
||||
self._subnet_id = subnet_id
|
||||
self._nsx_pool_id = nsx_pool_id
|
||||
self._context = ctx
|
||||
self._tenant_id = tenant_id
|
||||
def _get_vcns_error_code(self, e):
|
||||
"""Get the error code out of VcnsApiException"""
|
||||
try:
|
||||
desc = et.fromstring(e.response)
|
||||
return int(desc.find('errorCode').text)
|
||||
except Exception:
|
||||
LOG.error(_LE('IPAM pool: Error code not present. %s'),
|
||||
e.response)
|
||||
|
||||
@classmethod
|
||||
def load(cls, neutron_subnet_id, nsx_pool_id, ctx, tenant_id=None):
|
||||
"""Load an IPAM subnet object given its neutron ID."""
|
||||
return cls(neutron_subnet_id, nsx_pool_id, ctx, tenant_id)
|
||||
|
||||
def allocate(self, address_request):
|
||||
"""Allocate an IP from the pool"""
|
||||
with locking.LockManager.get_lock('nsx-ipam-' + self._nsx_pool_id):
|
||||
return self._allocate(address_request)
|
||||
|
||||
def _allocate(self, address_request):
|
||||
def backend_allocate(self, address_request):
|
||||
try:
|
||||
# allocate a specific IP
|
||||
if isinstance(address_request, ipam_req.SpecificAddressRequest):
|
||||
@ -288,12 +159,7 @@ class NsxvIpamSubnet(ipam_base.Subnet, NsxvIpamBase):
|
||||
raise ipam_exc.IPAllocationFailed()
|
||||
return ip_address
|
||||
|
||||
def deallocate(self, address):
|
||||
"""Return an IP to the pool"""
|
||||
with locking.LockManager.get_lock('nsx-ipam-' + self._nsx_pool_id):
|
||||
self._deallocate(address)
|
||||
|
||||
def _deallocate(self, address):
|
||||
def backend_deallocate(self, address):
|
||||
try:
|
||||
self._vcns.release_ipam_ip_to_pool(self._nsx_pool_id, address)
|
||||
except vc_exc.VcnsApiException as e:
|
||||
@ -306,10 +172,6 @@ class NsxvIpamSubnet(ipam_base.Subnet, NsxvIpamBase):
|
||||
subnet_id=self._subnet_id,
|
||||
ip_address=address)
|
||||
|
||||
def update_allocation_pools(self, pools, cidr):
|
||||
# Not supported
|
||||
pass
|
||||
|
||||
def _get_pool_cidr(self, pool):
|
||||
# rebuild the cidr from the pool range & prefix using the first
|
||||
# range in the pool, because they all should belong to the same cidr
|
||||
@ -334,17 +196,3 @@ class NsxvIpamSubnet(ipam_base.Subnet, NsxvIpamBase):
|
||||
return ipam_req.SpecificSubnetRequest(
|
||||
self._tenant_id, self._subnet_id,
|
||||
cidr, gateway_ip=gateway_ip, allocation_pools=pools)
|
||||
|
||||
|
||||
class NsxvSubnetRequestFactory(ipam_req.SubnetRequestFactory, NsxvIpamBase):
|
||||
"""Builds request using subnet info, including the network id"""
|
||||
|
||||
@classmethod
|
||||
def get_request(cls, context, subnet, subnetpool):
|
||||
req = super(NsxvSubnetRequestFactory, cls).get_request(
|
||||
context, subnet, subnetpool)
|
||||
# Add the network id into the request
|
||||
if 'network_id' in subnet:
|
||||
req.network_id = subnet['network_id']
|
||||
|
||||
return req
|
||||
|
13
vmware_nsx/services/ipam/nsx_v3/README.rst
Normal file
13
vmware_nsx/services/ipam/nsx_v3/README.rst
Normal file
@ -0,0 +1,13 @@
|
||||
=================================================================
|
||||
Enabling NSXv3 IPAM for external & provider networks in Devstack
|
||||
=================================================================
|
||||
|
||||
1. Download DevStack
|
||||
|
||||
2. Update the ``local.conf`` file::
|
||||
|
||||
[[post-config|$NEUTRON_CONF]]
|
||||
[DEFAULT]
|
||||
ipam_driver = vmware_nsxv3_ipam
|
||||
|
||||
3. run ``stack.sh``
|
0
vmware_nsx/services/ipam/nsx_v3/__init__.py
Normal file
0
vmware_nsx/services/ipam/nsx_v3/__init__.py
Normal file
200
vmware_nsx/services/ipam/nsx_v3/driver.py
Normal file
200
vmware_nsx/services/ipam/nsx_v3/driver.py
Normal file
@ -0,0 +1,200 @@
|
||||
# Copyright 2016 VMware, Inc.
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
|
||||
import netaddr
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.ipam import exceptions as ipam_exc
|
||||
from neutron.ipam import requests as ipam_req
|
||||
|
||||
from vmware_nsx._i18n import _, _LE, _LI, _LW
|
||||
from vmware_nsx.services.ipam.common import driver as common
|
||||
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
|
||||
from vmware_nsxlib.v3 import nsx_constants as error
|
||||
from vmware_nsxlib.v3 import resources
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Nsxv3IpamDriver(common.NsxAbstractIpamDriver):
|
||||
"""IPAM Driver For NSX-V3 external & provider networks."""
|
||||
|
||||
def __init__(self, subnetpool, context):
|
||||
super(Nsxv3IpamDriver, self).__init__(subnetpool, context)
|
||||
self.nsxlib_ipam = resources.IpPool(
|
||||
self.get_core_plugin().nsxlib.client)
|
||||
|
||||
@property
|
||||
def _subnet_class(self):
|
||||
return Nsxv3IpamSubnet
|
||||
|
||||
def _get_cidr_from_request(self, subnet_request):
|
||||
return "%s/%s" % (subnet_request.subnet_cidr[0],
|
||||
subnet_request.prefixlen)
|
||||
|
||||
def allocate_backend_pool(self, subnet_request):
|
||||
"""Create a pool on the NSX backend and return its ID"""
|
||||
if subnet_request.allocation_pools:
|
||||
ranges = [
|
||||
{'start': str(pool[0]), 'end': str(pool[-1])}
|
||||
for pool in subnet_request.allocation_pools]
|
||||
else:
|
||||
ranges = []
|
||||
|
||||
# name/description length on backend is long, so there is no problem
|
||||
name = 'subnet_' + subnet_request.subnet_id
|
||||
description = 'OS IP pool for subnet ' + subnet_request.subnet_id
|
||||
try:
|
||||
response = self.nsxlib_ipam.create(
|
||||
self._get_cidr_from_request(subnet_request),
|
||||
ranges=ranges,
|
||||
display_name=name,
|
||||
description=description,
|
||||
gateway_ip=subnet_request.gateway_ip)
|
||||
nsx_pool_id = response['id']
|
||||
except Exception as e:
|
||||
#TODO(asarfaty): handle specific errors
|
||||
msg = _('Failed to create subnet IPAM: %s') % e
|
||||
raise ipam_exc.IpamValueInvalid(message=msg)
|
||||
return nsx_pool_id
|
||||
|
||||
def delete_backend_pool(self, nsx_pool_id):
|
||||
# Because of the delete_subnet flow in the neutron plugin,
|
||||
# some ports still hold IPs from this pool.
|
||||
# Those ports be deleted shortly after this function.
|
||||
# We need to release those IPs before deleting the backed pool,
|
||||
# or else it will fail.
|
||||
pool_allocations = self.nsxlib_ipam.get_allocations(nsx_pool_id)
|
||||
if pool_allocations and pool_allocations.get('result_count'):
|
||||
for allocation in pool_allocations.get('results', []):
|
||||
ip_addr = allocation.get('allocation_id')
|
||||
try:
|
||||
self.nsxlib_ipam.release(nsx_pool_id, ip_addr)
|
||||
except Exception as e:
|
||||
LOG.warning(_LW("Failed to release ip %(ip)s from pool "
|
||||
"%(pool)s: %(e)s"),
|
||||
{'ip': ip_addr, 'pool': nsx_pool_id, 'e': e})
|
||||
try:
|
||||
self.nsxlib_ipam.delete(nsx_pool_id)
|
||||
except Exception as e:
|
||||
LOG.error(_LE("Failed to delete IPAM from backend: %s"), e)
|
||||
# Continue anyway, since this subnet was already removed
|
||||
|
||||
|
||||
class Nsxv3IpamSubnet(common.NsxAbstractIpamSubnet):
|
||||
"""Manage IP addresses for the NSX V3 IPAM driver."""
|
||||
|
||||
def __init__(self, subnet_id, nsx_pool_id, ctx, tenant_id):
|
||||
super(Nsxv3IpamSubnet, self).__init__(
|
||||
subnet_id, nsx_pool_id, ctx, tenant_id)
|
||||
self.nsxlib_ipam = resources.IpPool(
|
||||
self.get_core_plugin().nsxlib.client)
|
||||
|
||||
def backend_allocate(self, address_request):
|
||||
try:
|
||||
# allocate a specific IP
|
||||
if isinstance(address_request, ipam_req.SpecificAddressRequest):
|
||||
# This handles both specific and automatic address requests
|
||||
ip_address = str(address_request.address)
|
||||
# If this is the subnet gateway IP - no need to allocate it
|
||||
subnet = self.get_details()
|
||||
if str(subnet.gateway_ip) == ip_address:
|
||||
LOG.info(_LI("Skip allocation of gateway-ip for pool %s"),
|
||||
self._nsx_pool_id)
|
||||
return ip_address
|
||||
else:
|
||||
# Allocate any free IP
|
||||
ip_address = None
|
||||
response = self.nsxlib_ipam.allocate(self._nsx_pool_id,
|
||||
ip_addr=ip_address)
|
||||
ip_address = response['allocation_id']
|
||||
except nsx_lib_exc.ManagerError as e:
|
||||
LOG.error(_LE("NSX IPAM failed to allocate ip %(ip)s of subnet "
|
||||
"%(id)s:"
|
||||
" %(e)s; code %(code)s"),
|
||||
{'e': e,
|
||||
'ip': ip_address,
|
||||
'id': self._subnet_id,
|
||||
'code': e.error_code})
|
||||
# Currently the backend does not support allocation of specific IPs
|
||||
# When this support is added we should handle allocation errors.
|
||||
if e.error_code == error.ERR_CODE_IPAM_POOL_EXHAUSTED:
|
||||
# No more IP addresses available on the pool
|
||||
raise ipam_exc.IpAddressGenerationFailure(
|
||||
subnet_id=self._subnet_id)
|
||||
if e.error_code == error.ERR_CODE_IPAM_SPECIFIC_IP:
|
||||
msg = (_("NSX-V3 IPAM driver does not support allocation of a "
|
||||
"specific ip %s for port") % ip_address)
|
||||
raise NotImplementedError(msg)
|
||||
if e.error_code == error.ERR_CODE_OBJECT_NOT_FOUND:
|
||||
msg = (_("NSX-V3 IPAM failed to allocate: pool %s was not "
|
||||
"found") % self._nsx_pool_id)
|
||||
raise ipam_exc.IpamValueInvalid(message=msg)
|
||||
else:
|
||||
# another backend error
|
||||
raise ipam_exc.IPAllocationFailed()
|
||||
except Exception as e:
|
||||
LOG.error(_LE("NSX IPAM failed to allocate ip %(ip)s of subnet "
|
||||
"%(id)s:"
|
||||
" %(e)s"),
|
||||
{'e': e,
|
||||
'ip': ip_address,
|
||||
'id': self._subnet_id})
|
||||
# handle unexpected failures
|
||||
raise ipam_exc.IPAllocationFailed()
|
||||
return ip_address
|
||||
|
||||
def backend_deallocate(self, address):
|
||||
try:
|
||||
self.nsxlib_ipam.release(self._nsx_pool_id, ip_addr=address)
|
||||
except nsx_lib_exc.ManagerError as e:
|
||||
# fail silently
|
||||
LOG.error(_LE("NSX IPAM failed to free ip %(ip)s of subnet "
|
||||
"%(id)s:"
|
||||
" %(e)s; code %(code)s"),
|
||||
{'e': e,
|
||||
'ip': address,
|
||||
'id': self._subnet_id,
|
||||
'code': e.error_code})
|
||||
|
||||
def get_details(self):
|
||||
"""Return subnet data as a SpecificSubnetRequest"""
|
||||
# get the pool from the backend
|
||||
try:
|
||||
pool_details = self.nsxlib_ipam.get(self._nsx_pool_id)
|
||||
except Exception as e:
|
||||
msg = _('Failed to get details for nsx pool: %(id)s: '
|
||||
'%(e)s') % {'id': self._nsx_pool_id, 'e': e}
|
||||
raise ipam_exc.IpamValueInvalid(message=msg)
|
||||
|
||||
first_range = pool_details.get('subnets', [None])[0]
|
||||
if not first_range:
|
||||
msg = _('Failed to get details for nsx pool: %(id)s') % {
|
||||
'id': self._nsx_pool_id}
|
||||
raise ipam_exc.IpamValueInvalid(message=msg)
|
||||
|
||||
cidr = first_range.get('cidr')
|
||||
gateway_ip = first_range.get('gateway_ip')
|
||||
pools = []
|
||||
for subnet in pool_details.get('subnets', []):
|
||||
for ip_range in subnet.get('allocation_ranges', []):
|
||||
pools.append(netaddr.IPRange(ip_range.get('start'),
|
||||
ip_range.get('end')))
|
||||
|
||||
return ipam_req.SpecificSubnetRequest(
|
||||
self._tenant_id, self._subnet_id,
|
||||
cidr, gateway_ip=gateway_ip, allocation_pools=pools)
|
220
vmware_nsx/tests/unit/services/ipam/test_nsxv3_driver.py
Normal file
220
vmware_nsx/tests/unit/services/ipam/test_nsxv3_driver.py
Normal file
@ -0,0 +1,220 @@
|
||||
# Copyright 2016 VMware, Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
import mock
|
||||
import netaddr
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from vmware_nsx.tests.unit.nsx_v3 import test_plugin
|
||||
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
|
||||
from vmware_nsxlib.v3 import nsx_constants as error
|
||||
|
||||
|
||||
class MockIPPools(object):
|
||||
|
||||
def patch_nsxlib_ipam(self):
|
||||
self.nsx_pools = {}
|
||||
|
||||
def _create_pool(*args, **kwargs):
|
||||
pool_id = uuidutils.generate_uuid()
|
||||
gateway_ip = None
|
||||
if kwargs.get('gateway_ip'):
|
||||
gateway_ip = str(kwargs['gateway_ip'])
|
||||
subnet = {"allocation_ranges": kwargs.get('ranges'),
|
||||
"gateway_ip": gateway_ip,
|
||||
"cidr": args[0]}
|
||||
pool = {'id': pool_id,
|
||||
'subnets': [subnet]}
|
||||
self.nsx_pools[pool_id] = {'pool': pool, 'allocated': []}
|
||||
return {'id': pool_id}
|
||||
|
||||
def _delete_pool(pool_id):
|
||||
del self.nsx_pools[pool_id]
|
||||
|
||||
def _get_pool(pool_id):
|
||||
return self.nsx_pools[pool_id]['pool']
|
||||
|
||||
def _allocate_ip(*args, **kwargs):
|
||||
#TODO(asarfaty): add support for specific ip allocation
|
||||
if kwargs.get('ip_addr'):
|
||||
raise nsx_lib_exc.ManagerError(
|
||||
manager='dummy', operation='allocate',
|
||||
details='allocating specific IP is not supported',
|
||||
error_code=error.ERR_CODE_IPAM_SPECIFIC_IP)
|
||||
|
||||
nsx_pool = self.nsx_pools[args[0]]
|
||||
# get an unused ip from the pool
|
||||
ranges = nsx_pool['pool']['subnets'][0]['allocation_ranges']
|
||||
for ip_range in ranges:
|
||||
r = netaddr.IPRange(ip_range['start'], ip_range['end'])
|
||||
for ip_addr in r:
|
||||
if ip_addr not in nsx_pool['allocated']:
|
||||
nsx_pool['allocated'].append(ip_addr)
|
||||
return {'allocation_id': str(ip_addr)}
|
||||
# no IP was found
|
||||
raise nsx_lib_exc.ManagerError(
|
||||
manager='dummy', operation='allocate',
|
||||
details='All IPs in the pool are allocated',
|
||||
error_code=error.ERR_CODE_IPAM_POOL_EXHAUSTED)
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.IpPool.get",
|
||||
side_effect=_get_pool).start()
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.IpPool.create",
|
||||
side_effect=_create_pool).start()
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.IpPool.delete",
|
||||
side_effect=_delete_pool).start()
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.IpPool.allocate",
|
||||
side_effect=_allocate_ip).start()
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.IpPool.release").start()
|
||||
|
||||
|
||||
class TestNsxv3IpamSubnets(test_plugin.TestSubnetsV2, MockIPPools):
|
||||
"""Run the nsxv3 plugin subnets tests with the ipam driver."""
|
||||
def setUp(self):
|
||||
cfg.CONF.set_override(
|
||||
"ipam_driver",
|
||||
"vmware_nsx.services.ipam.nsx_v3.driver.Nsxv3IpamDriver")
|
||||
super(TestNsxv3IpamSubnets, self).setUp()
|
||||
self.patch_nsxlib_ipam()
|
||||
|
||||
def test_update_subnet_from_gw_to_new_gw(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_subnet_gw_outside_cidr_returns_200(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_subnet_from_gw_to_no_gw(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_subnet_allocation_pools(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_subnet_allocation_pools_and_gateway_ip(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_subnet_gw_ip_in_use_by_router_returns_409(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_subnet_from_no_gw_to_no_gw(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def _test_subnet_update_ipv4_and_ipv6_pd_subnets(self, ra_addr_mode):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_subnet_with_allocation_range(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_delete_subnet_ipv6_slaac_port_exists(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_subnet_ipv6_slaac_with_port_on_network(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_subnet_ipv6_slaac_with_dhcp_port_on_network(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_subnet_dhcpv6_stateless_with_port_on_network(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_subnet_ipv6_slaac_with_port_not_found(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_subnet_ipv6_slaac_with_db_reference_error(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
|
||||
class TestNsxv3IpamPorts(test_plugin.TestPortsV2, MockIPPools):
|
||||
"""Run the nsxv3 plugin ports tests with the ipam driver."""
|
||||
def setUp(self):
|
||||
cfg.CONF.set_override(
|
||||
"ipam_driver",
|
||||
"vmware_nsx.services.ipam.nsx_v3.driver.Nsxv3IpamDriver")
|
||||
super(TestNsxv3IpamPorts, self).setUp()
|
||||
self.patch_nsxlib_ipam()
|
||||
|
||||
def test_update_port_mac_v6_slaac(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_port_with_multiple_ipv4_and_ipv6_subnets(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_port_with_ipv6_slaac_subnet_in_fixed_ips(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_port_with_ipv6_pd_subnet_in_fixed_ips(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_port_anticipating_allocation(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_update_port_with_ipv6_slaac_subnet_in_fixed_ips(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_update_port_mac_ip(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_update_port_update_ips(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_update_port_excluding_ipv6_slaac_subnet_from_fixed_ips(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_update_port_update_ip(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_requested_subnet_id_v6_slaac(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_update_port_invalid_fixed_ip_address_v6_slaac(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_update_dhcp_port_with_exceeding_fixed_ips(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_requested_subnet_id_v4_and_v6_slaac(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_requested_ips_only(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_ip_allocation_for_ipv6_subnet_slaac_address_mode(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_ip_allocation_for_ipv6_2_subnet_slaac_mode(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_delete_port_with_ipv6_slaac_address(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_requested_duplicate_ip(self):
|
||||
self.skipTest('Allocating a specific IP is not supported')
|
||||
|
||||
def test_create_port_invalid_fixed_ip_address_v6_pd_slaac(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_port_invalid_subnet_v6_pd_slaac(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_port_update_ip_address_only(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
||||
|
||||
def test_update_port_invalid_fixed_ip_address_v6_pd_slaac(self):
|
||||
self.skipTest('Update ipam subnet is not supported')
|
Loading…
Reference in New Issue
Block a user