From 27ab7111df57fc78fcfc7b740a69342f89dfd3d8 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Wed, 3 May 2017 11:16:20 +0300 Subject: [PATCH] NSX|V3: Support specific IP allocations in IPAM The new NSX-V3 version supports allocation of specific IPs. This patch adds the relevant error handling, and all the relevant tests. Depends-on: Ic717023a41657e2de9200e38a3354ca76ee134f5 Change-Id: I822127a342f0ee63cf72ceededd1efa6e2753b94 --- vmware_nsx/services/ipam/nsx_v3/driver.py | 21 +++- .../unit/services/ipam/test_nsxv3_driver.py | 114 +++--------------- 2 files changed, 35 insertions(+), 100 deletions(-) diff --git a/vmware_nsx/services/ipam/nsx_v3/driver.py b/vmware_nsx/services/ipam/nsx_v3/driver.py index 1433ee2c66..d5acf5d1fa 100644 --- a/vmware_nsx/services/ipam/nsx_v3/driver.py +++ b/vmware_nsx/services/ipam/nsx_v3/driver.py @@ -163,16 +163,20 @@ class Nsxv3IpamSubnet(common.NsxAbstractIpamSubnet): '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: + # The NSX backend does not support allocation of specific IPs + # prior to version 2.0. 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_IPAM_IP_ALLOCATED: + # This IP is already in use + raise ipam_exc.IpAddressAlreadyAllocated( + ip=ip_address, subnet_id=self._subnet_id) 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) @@ -190,15 +194,21 @@ class Nsxv3IpamSubnet(common.NsxAbstractIpamSubnet): raise ipam_exc.IPAllocationFailed() return ip_address - def backend_deallocate(self, address): + def backend_deallocate(self, ip_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("Skip deallocation of gateway-ip for pool %s", + self._nsx_pool_id) + return try: - self.nsxlib_ipam.release(self._nsx_pool_id, ip_addr=address) + self.nsxlib_ipam.release(self._nsx_pool_id, ip_address) except nsx_lib_exc.ManagerError as e: # fail silently LOG.error("NSX IPAM failed to free ip %(ip)s of subnet " "%(id)s: %(e)s; code %(code)s", {'e': e, - 'ip': address, + 'ip': ip_address, 'id': self._subnet_id, 'code': e.error_code}) @@ -225,7 +235,6 @@ class Nsxv3IpamSubnet(common.NsxAbstractIpamSubnet): 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) diff --git a/vmware_nsx/tests/unit/services/ipam/test_nsxv3_driver.py b/vmware_nsx/tests/unit/services/ipam/test_nsxv3_driver.py index 3c4f54dcd6..5175a702c5 100644 --- a/vmware_nsx/tests/unit/services/ipam/test_nsxv3_driver.py +++ b/vmware_nsx/tests/unit/services/ipam/test_nsxv3_driver.py @@ -63,14 +63,18 @@ class MockIPPools(object): 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]] + if kwargs.get('ip_addr'): + ip_addr = netaddr.IPAddress(kwargs['ip_addr']) + # verify that this ip was not yet allocated + if ip_addr in nsx_pool['allocated']: + raise nsx_lib_exc.ManagerError( + manager='dummy', operation='allocate', + details='IP already allocated', + error_code=error.ERR_CODE_IPAM_IP_ALLOCATED) + # skip ip validation for this mock. + nsx_pool['allocated'].append(ip_addr) + return {'allocation_id': str(ip_addr)} # get an unused ip from the pool ranges = nsx_pool['pool']['subnets'][0]['allocation_ranges'] for ip_range in ranges: @@ -85,6 +89,11 @@ class MockIPPools(object): details='All IPs in the pool are allocated', error_code=error.ERR_CODE_IPAM_POOL_EXHAUSTED) + def _release_ip(*args, **kwargs): + nsx_pool = self.nsx_pools[args[0]] + ip_addr = netaddr.IPAddress(args[1]) + nsx_pool['allocated'].remove(ip_addr) + mock.patch( "vmware_nsxlib.v3.resources.IpPool.get", side_effect=_get_pool).start() @@ -101,7 +110,8 @@ class MockIPPools(object): "vmware_nsxlib.v3.resources.IpPool.allocate", side_effect=_allocate_ip).start() mock.patch( - "vmware_nsxlib.v3.resources.IpPool.release").start() + "vmware_nsxlib.v3.resources.IpPool.release", + side_effect=_release_ip).start() class TestNsxv3IpamSubnets(test_plugin.TestSubnetsV2, MockIPPools): @@ -113,35 +123,11 @@ class TestNsxv3IpamSubnets(test_plugin.TestSubnetsV2, MockIPPools): super(TestNsxv3IpamSubnets, self).setUp() self.patch_nsxlib_ipam() - def test_update_subnet_gw_ip_in_use_by_router_returns_409(self): - self.skipTest('Allocating a specific IP 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') - def test_subnet_update_ipv4_and_ipv6_pd_slaac_subnets(self): - self.skipTest('Allocating a specific IP is not supported') + self.skipTest('Update ipam subnet is not supported') def test_subnet_update_ipv4_and_ipv6_pd_v6stateless_subnets(self): - self.skipTest('Allocating a specific IP is not supported') + self.skipTest('Update ipam subnet is not supported') class TestNsxv3IpamPorts(test_plugin.TestPortsV2, MockIPPools): @@ -153,66 +139,6 @@ class TestNsxv3IpamPorts(test_plugin.TestPortsV2, MockIPPools): 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_update_port_with_new_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')