bf3bbb5c4c
Commit b8d98a5764
moved code
out of transactions to unblock the gate. This issues has been
resolved in the following:
1. Commit 61d0082f9ae9f9a935b5c3517c0b9036c40dbb00
2. Commit 72a2e9df96998f2198b2aba44e4daa2b466b1158
This also fixes dns transaction issues
Switch of context to admin context under db writer is causing
issues with table constraints.
Some usage of admin context for dns is still in place due to
extend_port_dict external API
Co-Authored-By: Anna Khmelnitsky <akhmelnitsky@vmware.com>
Change-Id: I21ec2df09c6b375973ce6f19e06e88318cf19452
343 lines
14 KiB
Python
343 lines
14 KiB
Python
# Copyright (c) 2016 IBM
|
|
# Copyright (c) 2017 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.
|
|
|
|
from neutron_lib.api import validators
|
|
from neutron_lib import context as n_context
|
|
from neutron_lib.plugins import directory
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
|
|
from neutron.extensions import availability_zone as az_ext
|
|
from neutron.extensions import dns
|
|
from neutron.objects import network as net_obj
|
|
from neutron.objects import ports as port_obj
|
|
from neutron.services.externaldns import driver
|
|
|
|
from vmware_nsx.common import driver_api
|
|
from vmware_nsx.plugins.nsx_v3 import availability_zones as nsx_az
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
DNS_DOMAIN_DEFAULT = 'openstacklocal.'
|
|
|
|
|
|
# TODO(asarfaty) use dns-domain/nameserver from network az instead of global
|
|
class DNSExtensionDriver(driver_api.ExtensionDriver):
|
|
_supported_extension_alias = 'dns-integration'
|
|
|
|
@property
|
|
def extension_alias(self):
|
|
return self._supported_extension_alias
|
|
|
|
def process_create_network(self, plugin_context, request_data, db_data):
|
|
dns_domain = request_data.get(dns.DNSDOMAIN)
|
|
if not validators.is_attr_set(dns_domain):
|
|
return
|
|
|
|
if dns_domain:
|
|
net_obj.NetworkDNSDomain(plugin_context,
|
|
network_id=db_data['id'],
|
|
dns_domain=dns_domain).create()
|
|
db_data[dns.DNSDOMAIN] = dns_domain
|
|
|
|
def process_update_network(self, plugin_context, request_data, db_data):
|
|
new_value = request_data.get(dns.DNSDOMAIN)
|
|
if not validators.is_attr_set(new_value):
|
|
return
|
|
|
|
current_dns_domain = db_data.get(dns.DNSDOMAIN)
|
|
if current_dns_domain == new_value:
|
|
return
|
|
|
|
net_id = db_data['id']
|
|
if current_dns_domain:
|
|
net_dns_domain = net_obj.NetworkDNSDomain.get_object(
|
|
plugin_context,
|
|
network_id=net_id)
|
|
if new_value:
|
|
net_dns_domain['dns_domain'] = new_value
|
|
db_data[dns.DNSDOMAIN] = new_value
|
|
net_dns_domain.update()
|
|
else:
|
|
net_dns_domain.delete()
|
|
db_data[dns.DNSDOMAIN] = ''
|
|
elif new_value:
|
|
net_obj.NetworkDNSDomain(plugin_context,
|
|
network_id=net_id,
|
|
dns_domain=new_value).create()
|
|
db_data[dns.DNSDOMAIN] = new_value
|
|
|
|
def process_create_port(self, plugin_context, request_data, db_data):
|
|
if not request_data.get(dns.DNSNAME):
|
|
return
|
|
dns_name, is_dns_domain_default = self._get_request_dns_name(
|
|
request_data, db_data['network_id'], plugin_context)
|
|
if is_dns_domain_default:
|
|
return
|
|
network = self._get_network(plugin_context, db_data['network_id'])
|
|
if self.external_dns_not_needed(
|
|
plugin_context, network) or not network[dns.DNSDOMAIN]:
|
|
current_dns_name = ''
|
|
current_dns_domain = ''
|
|
else:
|
|
current_dns_name = dns_name
|
|
current_dns_domain = network[dns.DNSDOMAIN]
|
|
port_obj.PortDNS(plugin_context,
|
|
port_id=db_data['id'],
|
|
current_dns_name=current_dns_name,
|
|
current_dns_domain=current_dns_domain,
|
|
previous_dns_name='',
|
|
previous_dns_domain='',
|
|
dns_name=dns_name).create()
|
|
|
|
def _update_dns_db(self, dns_name, dns_domain, db_data,
|
|
plugin_context, has_fixed_ips):
|
|
dns_data_db = port_obj.PortDNS.get_object(
|
|
plugin_context,
|
|
port_id=db_data['id'])
|
|
if dns_data_db:
|
|
is_dns_name_changed = (dns_name is not None and
|
|
dns_data_db['current_dns_name'] != dns_name)
|
|
|
|
if is_dns_name_changed or (has_fixed_ips and
|
|
dns_data_db['current_dns_name']):
|
|
dns_data_db['previous_dns_name'] = (
|
|
dns_data_db['current_dns_name'])
|
|
dns_data_db['previous_dns_domain'] = (
|
|
dns_data_db['current_dns_domain'])
|
|
if is_dns_name_changed:
|
|
dns_data_db[dns.DNSNAME] = dns_name
|
|
dns_data_db['current_dns_name'] = dns_name
|
|
if dns_name:
|
|
dns_data_db['current_dns_domain'] = dns_domain
|
|
else:
|
|
dns_data_db['current_dns_domain'] = ''
|
|
|
|
dns_data_db.update()
|
|
return dns_data_db
|
|
if dns_name:
|
|
dns_data_db = port_obj.PortDNS(plugin_context,
|
|
port_id=db_data['id'],
|
|
current_dns_name=dns_name,
|
|
current_dns_domain=dns_domain,
|
|
previous_dns_name='',
|
|
previous_dns_domain='',
|
|
dns_name=dns_name)
|
|
dns_data_db.create()
|
|
return dns_data_db
|
|
|
|
def process_update_port(self, plugin_context, request_data, db_data):
|
|
dns_name = request_data.get(dns.DNSNAME)
|
|
has_fixed_ips = 'fixed_ips' in request_data
|
|
if dns_name is None and not has_fixed_ips:
|
|
return
|
|
if dns_name is not None:
|
|
dns_name, is_dns_domain_default = self._get_request_dns_name(
|
|
request_data, db_data['network_id'], plugin_context)
|
|
if is_dns_domain_default:
|
|
self._extend_port_dict(db_data, db_data, None, plugin_context)
|
|
return
|
|
network = self._get_network(plugin_context, db_data['network_id'])
|
|
dns_domain = network[dns.DNSDOMAIN]
|
|
dns_data_db = None
|
|
if not dns_domain or self.external_dns_not_needed(plugin_context,
|
|
network):
|
|
# No need to update external DNS service. Only process the port's
|
|
# dns_name attribute if necessary
|
|
if dns_name is not None:
|
|
dns_data_db = self._process_only_dns_name_update(
|
|
plugin_context, db_data, dns_name)
|
|
else:
|
|
dns_data_db = self._update_dns_db(dns_name, dns_domain, db_data,
|
|
plugin_context, has_fixed_ips)
|
|
self._extend_port_dict(db_data, db_data, dns_data_db, plugin_context)
|
|
|
|
def _process_only_dns_name_update(self, plugin_context, db_data, dns_name):
|
|
dns_data_db = port_obj.PortDNS.get_object(
|
|
plugin_context,
|
|
port_id=db_data['id'])
|
|
if dns_data_db:
|
|
dns_data_db['dns_name'] = dns_name
|
|
dns_data_db.update()
|
|
return dns_data_db
|
|
if dns_name:
|
|
dns_data_db = port_obj.PortDNS(plugin_context,
|
|
port_id=db_data['id'],
|
|
current_dns_name='',
|
|
current_dns_domain='',
|
|
previous_dns_name='',
|
|
previous_dns_domain='',
|
|
dns_name=dns_name)
|
|
dns_data_db.create()
|
|
return dns_data_db
|
|
|
|
def external_dns_not_needed(self, context, network):
|
|
"""Decide if ports in network need to be sent to the DNS service.
|
|
|
|
:param context: plugin request context
|
|
:param network: network dictionary
|
|
:return: True or False
|
|
"""
|
|
pass
|
|
|
|
def extend_network_dict(self, session, db_data, response_data):
|
|
response_data[dns.DNSDOMAIN] = ''
|
|
if db_data.dns_domain:
|
|
response_data[dns.DNSDOMAIN] = db_data.dns_domain[dns.DNSDOMAIN]
|
|
return response_data
|
|
|
|
def _get_dns_domain(self, network_id, context=None):
|
|
if not cfg.CONF.dns_domain:
|
|
return ''
|
|
if cfg.CONF.dns_domain.endswith('.'):
|
|
return cfg.CONF.dns_domain
|
|
return '%s.' % cfg.CONF.dns_domain
|
|
|
|
def _get_request_dns_name(self, port, network_id, context):
|
|
dns_domain = self._get_dns_domain(network_id, context)
|
|
if ((dns_domain and dns_domain != DNS_DOMAIN_DEFAULT)):
|
|
return (port.get(dns.DNSNAME, ''), False)
|
|
return ('', True)
|
|
|
|
def _get_request_dns_name_and_domain_name(self, dns_data_db,
|
|
network_id, context):
|
|
dns_domain = self._get_dns_domain(network_id, context)
|
|
dns_name = ''
|
|
if ((dns_domain and dns_domain != DNS_DOMAIN_DEFAULT)):
|
|
if dns_data_db:
|
|
dns_name = dns_data_db.dns_name
|
|
return dns_name, dns_domain
|
|
|
|
def _get_dns_names_for_port(self, ips, dns_data_db, network_id, context):
|
|
dns_assignment = []
|
|
dns_name, dns_domain = self._get_request_dns_name_and_domain_name(
|
|
dns_data_db, network_id, context)
|
|
for ip in ips:
|
|
if dns_name:
|
|
hostname = dns_name
|
|
fqdn = dns_name
|
|
if not dns_name.endswith('.'):
|
|
fqdn = '%s.%s' % (dns_name, dns_domain)
|
|
else:
|
|
hostname = 'host-%s' % ip['ip_address'].replace(
|
|
'.', '-').replace(':', '-')
|
|
fqdn = hostname
|
|
if dns_domain:
|
|
fqdn = '%s.%s' % (hostname, dns_domain)
|
|
dns_assignment.append({'ip_address': ip['ip_address'],
|
|
'hostname': hostname,
|
|
'fqdn': fqdn})
|
|
return dns_assignment
|
|
|
|
def _get_dns_name_for_port_get(self, port, dns_data_db, context):
|
|
if port['fixed_ips']:
|
|
return self._get_dns_names_for_port(
|
|
port['fixed_ips'], dns_data_db,
|
|
port['network_id'], context)
|
|
return []
|
|
|
|
def _extend_port_dict(self, db_data, response_data,
|
|
dns_data_db, context=None):
|
|
if not dns_data_db:
|
|
response_data[dns.DNSNAME] = ''
|
|
else:
|
|
response_data[dns.DNSNAME] = dns_data_db[dns.DNSNAME]
|
|
response_data['dns_assignment'] = self._get_dns_name_for_port_get(
|
|
db_data, dns_data_db, context)
|
|
return response_data
|
|
|
|
def extend_port_dict(self, session, db_data, response_data):
|
|
dns_data_db = db_data.dns
|
|
return self._extend_port_dict(db_data, response_data, dns_data_db)
|
|
|
|
def _get_network(self, context, network_id):
|
|
plugin = directory.get_plugin()
|
|
return plugin.get_network(context, network_id)
|
|
|
|
|
|
class DNSExtensionDriverNSXv(DNSExtensionDriver):
|
|
|
|
def initialize(self):
|
|
LOG.info("DNSExtensionDriverNSXv initialization complete")
|
|
|
|
def external_dns_not_needed(self, context, network):
|
|
dns_driver = _get_dns_driver()
|
|
if not dns_driver:
|
|
return True
|
|
if network['router:external']:
|
|
return True
|
|
return False
|
|
|
|
|
|
class DNSExtensionDriverNSXv3(DNSExtensionDriver):
|
|
|
|
def initialize(self):
|
|
self._availability_zones = nsx_az.NsxV3AvailabilityZones()
|
|
LOG.info("DNSExtensionDriverNSXv3 initialization complete")
|
|
|
|
def _get_network_az(self, network_id, context):
|
|
if not context:
|
|
context = n_context.get_admin_context()
|
|
network = self._get_network(context, network_id)
|
|
if az_ext.AZ_HINTS in network and network[az_ext.AZ_HINTS]:
|
|
az_name = network[az_ext.AZ_HINTS][0]
|
|
return self._availability_zones.get_availability_zone(az_name)
|
|
return self._availability_zones.get_default_availability_zone()
|
|
|
|
def _get_dns_domain(self, network_id, context=None):
|
|
# try to get the dns-domain from the specific availability zone
|
|
# of this network
|
|
az = self._get_network_az(network_id, context)
|
|
if az.dns_domain:
|
|
dns_domain = az.dns_domain
|
|
elif cfg.CONF.nsx_v3.dns_domain:
|
|
dns_domain = cfg.CONF.nsx_v3.dns_domain
|
|
elif cfg.CONF.dns_domain:
|
|
dns_domain = cfg.CONF.dns_domain
|
|
else:
|
|
return ''
|
|
if dns_domain.endswith('.'):
|
|
return dns_domain
|
|
return '%s.' % dns_domain
|
|
|
|
def external_dns_not_needed(self, context, network):
|
|
dns_driver = _get_dns_driver()
|
|
if not dns_driver:
|
|
return True
|
|
if network['router:external']:
|
|
return True
|
|
return False
|
|
|
|
|
|
DNS_DRIVER = None
|
|
|
|
|
|
def _get_dns_driver():
|
|
global DNS_DRIVER
|
|
if DNS_DRIVER:
|
|
return DNS_DRIVER
|
|
if not cfg.CONF.external_dns_driver:
|
|
return
|
|
try:
|
|
DNS_DRIVER = driver.ExternalDNSService.get_instance()
|
|
LOG.debug("External DNS driver loaded: %s",
|
|
cfg.CONF.external_dns_driver)
|
|
return DNS_DRIVER
|
|
except ImportError:
|
|
LOG.exception("ImportError exception occurred while loading "
|
|
"the external DNS service driver")
|
|
raise dns.ExternalDNSDriverNotFound(
|
|
driver=cfg.CONF.external_dns_driver)
|