Merge "NSXv3 DNS integration"
This commit is contained in:
commit
8a53f29e4d
vmware_nsx
@ -1,5 +1,4 @@
|
||||
# Copyright (c) 2016 IBM
|
||||
# Copyright (c) 2017 VMware, Inc.
|
||||
# Copyright (c) 2018 VMware, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -17,6 +16,9 @@
|
||||
from neutron_lib.api.definitions import availability_zone as az_def
|
||||
from neutron_lib.api.definitions import dns
|
||||
from neutron_lib.api import validators
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources
|
||||
from neutron_lib import context as n_context
|
||||
from neutron_lib.exceptions import dns as dns_exc
|
||||
from neutron_lib.plugins import directory
|
||||
@ -34,6 +36,12 @@ LOG = logging.getLogger(__name__)
|
||||
DNS_DOMAIN_DEFAULT = 'openstacklocal.'
|
||||
|
||||
|
||||
def _dotted_domain(dns_domain):
|
||||
if dns_domain.endswith('.'):
|
||||
return dns_domain
|
||||
return '%s.' % dns_domain
|
||||
|
||||
|
||||
# TODO(asarfaty) use dns-domain/nameserver from network az instead of global
|
||||
class DNSExtensionDriver(driver_api.ExtensionDriver):
|
||||
_supported_extension_alias = 'dns-integration'
|
||||
@ -81,27 +89,55 @@ class DNSExtensionDriver(driver_api.ExtensionDriver):
|
||||
db_data[dns.DNSDOMAIN] = new_value
|
||||
|
||||
def process_create_port(self, plugin_context, request_data, db_data):
|
||||
if not request_data.get(dns.DNSNAME):
|
||||
if not (request_data.get(dns.DNSNAME) or
|
||||
request_data.get(dns.DNSDOMAIN)):
|
||||
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()
|
||||
self._create_port_dns_record(plugin_context, request_data, db_data,
|
||||
network, dns_name)
|
||||
|
||||
def _create_port_dns_record(self, plugin_context, request_data, db_data,
|
||||
network, dns_name):
|
||||
external_dns_domain = (request_data.get(dns.DNSDOMAIN) or
|
||||
network.get(dns.DNSDOMAIN))
|
||||
current_dns_name, current_dns_domain = (
|
||||
self._calculate_current_dns_name_and_domain(
|
||||
dns_name, external_dns_domain,
|
||||
self.external_dns_not_needed(plugin_context, network)))
|
||||
|
||||
dns_data_obj = 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,
|
||||
dns_domain=request_data.get(dns.DNSDOMAIN, ''))
|
||||
dns_data_obj.create()
|
||||
return dns_data_obj
|
||||
|
||||
def _calculate_current_dns_name_and_domain(self, dns_name,
|
||||
external_dns_domain,
|
||||
no_external_dns_service):
|
||||
# When creating a new PortDNS object, the current_dns_name and
|
||||
# current_dns_domain fields hold the data that the integration driver
|
||||
# will send to the external DNS service. They are set to non-blank
|
||||
# values only if all the following conditions are met:
|
||||
# 1) There is an external DNS integration driver configured
|
||||
# 2) The user request contains a valid non-blank value for the port's
|
||||
# dns_name
|
||||
# 3) The user request contains a valid non-blank value for the port's
|
||||
# dns_domain or the port's network has a non-blank value in its
|
||||
# dns_domain attribute
|
||||
are_both_dns_attributes_set = dns_name and external_dns_domain
|
||||
if no_external_dns_service or not are_both_dns_attributes_set:
|
||||
return '', ''
|
||||
return dns_name, external_dns_domain
|
||||
|
||||
def _update_dns_db(self, dns_name, dns_domain, db_data,
|
||||
plugin_context, has_fixed_ips):
|
||||
@ -202,9 +238,7 @@ class DNSExtensionDriver(driver_api.ExtensionDriver):
|
||||
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
|
||||
return _dotted_domain(cfg.CONF.dns_domain)
|
||||
|
||||
def _get_request_dns_name(self, port, network_id, context):
|
||||
dns_domain = self._get_dns_domain(network_id, context)
|
||||
@ -301,22 +335,28 @@ class DNSExtensionDriverNSXv3(DNSExtensionDriver):
|
||||
# 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:
|
||||
if (az.dns_domain
|
||||
and _dotted_domain(az.dns_domain) !=
|
||||
_dotted_domain(DNS_DOMAIN_DEFAULT)):
|
||||
dns_domain = az.dns_domain
|
||||
elif cfg.CONF.nsx_v3.dns_domain:
|
||||
elif (cfg.CONF.nsx_v3.dns_domain
|
||||
and (_dotted_domain(cfg.CONF.nsx_v3.dns_domain) !=
|
||||
_dotted_domain(DNS_DOMAIN_DEFAULT))):
|
||||
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
|
||||
return _dotted_domain(dns_domain)
|
||||
|
||||
def external_dns_not_needed(self, context, network):
|
||||
dns_driver = _get_dns_driver()
|
||||
if not dns_driver:
|
||||
return True
|
||||
provider_type = network.get('provider:network_type')
|
||||
if not provider_type:
|
||||
return True
|
||||
|
||||
if network['router:external']:
|
||||
return True
|
||||
return False
|
||||
@ -355,3 +395,111 @@ def _get_dns_driver():
|
||||
"the external DNS service driver")
|
||||
raise dns_exc.ExternalDNSDriverNotFound(
|
||||
driver=cfg.CONF.external_dns_driver)
|
||||
|
||||
|
||||
def _send_data_to_external_dns_service(context, dns_driver, dns_domain,
|
||||
dns_name, records):
|
||||
try:
|
||||
dns_driver.create_record_set(context, dns_domain, dns_name, records)
|
||||
except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e:
|
||||
LOG.exception("Error publishing port data in external DNS "
|
||||
"service. Name: '%(name)s'. Domain: '%(domain)s'. "
|
||||
"DNS service driver message '%(message)s'",
|
||||
{"name": dns_name,
|
||||
"domain": dns_domain,
|
||||
"message": e.msg})
|
||||
|
||||
|
||||
def _remove_data_from_external_dns_service(context, dns_driver, dns_domain,
|
||||
dns_name, records):
|
||||
try:
|
||||
dns_driver.delete_record_set(context, dns_domain, dns_name, records)
|
||||
except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e:
|
||||
LOG.exception("Error deleting port data from external DNS "
|
||||
"service. Name: '%(name)s'. Domain: '%(domain)s'. "
|
||||
"IP addresses '%(ips)s'. DNS service driver message "
|
||||
"'%(message)s'",
|
||||
{"name": dns_name,
|
||||
"domain": dns_domain,
|
||||
"message": e.msg,
|
||||
"ips": ', '.join(records)})
|
||||
|
||||
|
||||
def _create_port_in_external_dns_service(resource, event, trigger, **kwargs):
|
||||
dns_driver = _get_dns_driver()
|
||||
if not dns_driver:
|
||||
return
|
||||
context = kwargs['context']
|
||||
port = kwargs['port']
|
||||
dns_data_db = port_obj.PortDNS.get_object(
|
||||
context, port_id=port['id'])
|
||||
if not (dns_data_db and dns_data_db['current_dns_name']):
|
||||
return
|
||||
records = [ip['ip_address'] for ip in port['fixed_ips']]
|
||||
_send_data_to_external_dns_service(context, dns_driver,
|
||||
dns_data_db['current_dns_domain'],
|
||||
dns_data_db['current_dns_name'],
|
||||
records)
|
||||
|
||||
|
||||
def _update_port_in_external_dns_service(resource, event, trigger, **kwargs):
|
||||
dns_driver = _get_dns_driver()
|
||||
if not dns_driver:
|
||||
return
|
||||
context = kwargs['context']
|
||||
updated_port = kwargs['port']
|
||||
original_port = kwargs.get('original_port')
|
||||
if not original_port:
|
||||
return
|
||||
original_ips = [ip['ip_address'] for ip in original_port['fixed_ips']]
|
||||
updated_ips = [ip['ip_address'] for ip in updated_port['fixed_ips']]
|
||||
is_dns_name_changed = (updated_port[dns.DNSNAME] !=
|
||||
original_port[dns.DNSNAME])
|
||||
is_dns_domain_changed = (dns.DNSDOMAIN in updated_port and
|
||||
updated_port[dns.DNSDOMAIN] !=
|
||||
original_port[dns.DNSDOMAIN])
|
||||
ips_changed = set(original_ips) != set(updated_ips)
|
||||
if not any((is_dns_name_changed, is_dns_domain_changed, ips_changed)):
|
||||
return
|
||||
dns_data_db = port_obj.PortDNS.get_object(
|
||||
context, port_id=updated_port['id'])
|
||||
if not (dns_data_db and
|
||||
(dns_data_db['previous_dns_name'] or
|
||||
dns_data_db['current_dns_name'])):
|
||||
return
|
||||
if dns_data_db['previous_dns_name']:
|
||||
_remove_data_from_external_dns_service(
|
||||
context, dns_driver, dns_data_db['previous_dns_domain'],
|
||||
dns_data_db['previous_dns_name'], original_ips)
|
||||
if dns_data_db['current_dns_name']:
|
||||
_send_data_to_external_dns_service(context, dns_driver,
|
||||
dns_data_db['current_dns_domain'],
|
||||
dns_data_db['current_dns_name'],
|
||||
updated_ips)
|
||||
|
||||
|
||||
def _delete_port_in_external_dns_service(resource, event, trigger, **kwargs):
|
||||
dns_driver = _get_dns_driver()
|
||||
if not dns_driver:
|
||||
return
|
||||
context = kwargs['context']
|
||||
port_id = kwargs['port_id']
|
||||
dns_data_db = port_obj.PortDNS.get_object(
|
||||
context, port_id=port_id)
|
||||
if not dns_data_db:
|
||||
return
|
||||
if dns_data_db['current_dns_name']:
|
||||
ip_allocations = port_obj.IPAllocation.get_objects(context,
|
||||
port_id=port_id)
|
||||
records = [str(alloc['ip_address']) for alloc in ip_allocations]
|
||||
_remove_data_from_external_dns_service(
|
||||
context, dns_driver, dns_data_db['current_dns_domain'],
|
||||
dns_data_db['current_dns_name'], records)
|
||||
|
||||
|
||||
registry.subscribe(
|
||||
_create_port_in_external_dns_service, resources.PORT, events.AFTER_CREATE)
|
||||
registry.subscribe(
|
||||
_update_port_in_external_dns_service, resources.PORT, events.AFTER_UPDATE)
|
||||
registry.subscribe(
|
||||
_delete_port_in_external_dns_service, resources.PORT, events.BEFORE_DELETE)
|
||||
|
@ -2615,6 +2615,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
|
||||
if not cfg.CONF.nsx_v3.native_dhcp_metadata:
|
||||
nsx_rpc.handle_port_metadata_access(self, context, neutron_db)
|
||||
kwargs = {'context': context, 'port': neutron_db}
|
||||
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
||||
return port_data
|
||||
|
||||
def _pre_delete_port_check(self, context, port_id, l2gw_port_check):
|
||||
@ -3061,6 +3063,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
if cfg.CONF.nsx_v3.native_dhcp_metadata:
|
||||
self._update_dhcp_binding(context, original_port, updated_port)
|
||||
|
||||
# Notifications must be sent after the above transaction is complete
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'port': updated_port,
|
||||
'mac_address_updated': False,
|
||||
'original_port': original_port,
|
||||
}
|
||||
|
||||
registry.notify(resources.PORT, events.AFTER_UPDATE, self, **kwargs)
|
||||
return updated_port
|
||||
|
||||
def _extend_get_port_dict_qos_and_binding(self, context, port):
|
||||
|
Loading…
x
Reference in New Issue
Block a user