
1.Upgrade pylint to 2.4.4, add exclusions to the tests, and fix some lint errors in the code 2. Fix user creation with GRANT in MySQL 8.0(Ubuntu Focal) In Ubuntu Bionic (18.04) mysql 5.7 version used to create the user implicitly when using using the GRANT. Ubuntu Focal (20.04) has mysql 8.0 and with mysql 8.0 there is no implicit user creation with GRANT. We need to create the user first before using GRANT command. See also commit I97b0dcbb88c6ef7c22e3c55970211bed792bbd0d 3. Remove fwaas from the zuul.yaml 4. Remove DB migration test which is failing ue to FWaaS migration with py38 5. Fix cover tests python version in .tox 6. fix requirememnts Change-Id: I22654a5d5ccaad3185ae3365a90afba1ce870695
244 lines
10 KiB
Python
244 lines
10 KiB
Python
# 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 _
|
|
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
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Nsxv3IpamDriver(common.NsxAbstractIpamDriver):
|
|
"""IPAM Driver For NSX-V3 networks."""
|
|
|
|
def __init__(self, subnetpool, context):
|
|
super(Nsxv3IpamDriver, self).__init__(subnetpool, context)
|
|
self.nsxlib_ipam = self._nsxlib.ip_pool
|
|
|
|
# Mark which updates to the pool are supported
|
|
self.support_update_gateway = True
|
|
self.support_update_pools = True
|
|
|
|
@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 _get_ranges_from_request(self, subnet_request):
|
|
if subnet_request.allocation_pools:
|
|
ranges = [
|
|
{'start': str(pool[0]), 'end': str(pool[-1])}
|
|
for pool in subnet_request.allocation_pools]
|
|
else:
|
|
ranges = []
|
|
return ranges
|
|
|
|
def _is_supported_net(self, subnet_request):
|
|
"""This driver doesn't support multicast cidrs"""
|
|
if not hasattr(subnet_request, "subnet_cidr"):
|
|
return True
|
|
net = netaddr.IPNetwork(subnet_request.subnet_cidr[0])
|
|
return not net.is_multicast()
|
|
|
|
def allocate_backend_pool(self, subnet_request):
|
|
"""Create a pool on the NSX backend and return its ID"""
|
|
# 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),
|
|
allocation_ranges=self._get_ranges_from_request(
|
|
subnet_request),
|
|
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("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("Failed to delete IPAM from backend: %s", e)
|
|
# Continue anyway, since this subnet was already removed
|
|
|
|
def update_backend_pool(self, nsx_pool_id, subnet_request):
|
|
update_args = {
|
|
'cidr': self._get_cidr_from_request(subnet_request),
|
|
'allocation_ranges': self._get_ranges_from_request(subnet_request),
|
|
'gateway_ip': subnet_request.gateway_ip}
|
|
try:
|
|
self.nsxlib_ipam.update(
|
|
nsx_pool_id, **update_args)
|
|
except nsx_lib_exc.ManagerError as e:
|
|
LOG.error("NSX IPAM failed to update pool %(id)s: "
|
|
" %(e)s; code %(code)s",
|
|
{'e': e,
|
|
'id': nsx_pool_id,
|
|
'code': e.error_code})
|
|
if (e.error_code == error.ERR_CODE_IPAM_RANGE_MODIFY or
|
|
e.error_code == error.ERR_CODE_IPAM_RANGE_DELETE or
|
|
e.error_code == error.ERR_CODE_IPAM_RANGE_SHRUNK):
|
|
# The change is not allowed: already allocated IPs out of
|
|
# the new range
|
|
raise ipam_exc.InvalidSubnetRequest(
|
|
reason=_("Already allocated IPs outside of the updated "
|
|
"pools"))
|
|
except Exception as e:
|
|
# unexpected error
|
|
msg = _('Failed to update subnet IPAM: %s') % e
|
|
raise ipam_exc.IpamValueInvalid(message=msg)
|
|
|
|
|
|
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 = self._nsxlib.ip_pool
|
|
|
|
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("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("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})
|
|
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)
|
|
raise ipam_exc.IpamValueInvalid(message=msg)
|
|
# another backend error
|
|
raise ipam_exc.IPAllocationFailed()
|
|
except Exception as e:
|
|
LOG.error("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, 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_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': 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)
|