NSXV+NSXV3: Add support for dns-integration extension
One can enable DNS integration for the upstream neutron DNS integration extension by setting: nsx_extension_drivers = vmware_nsxv_dns (for NSXV) nsx_extension_drivers = vmware_nsxv3_dns (for NSXV3) Change-Id: Id100f8034e602d92310d22f900c48d9dfbe59a8d
This commit is contained in:
parent
e5c9fab873
commit
64dec92beb
@ -91,6 +91,7 @@ function neutron_plugin_configure_service {
|
||||
if [[ "$NSX_L2GW_DRIVER" != "" ]]; then
|
||||
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_l2gw_driver $NSX_L2GW_DRIVER
|
||||
fi
|
||||
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_extension_drivers vmware_nsxv_dns
|
||||
_nsxv_ini_set password "$NSXV_PASSWORD"
|
||||
_nsxv_ini_set user "$NSXV_USER"
|
||||
_nsxv_ini_set vdn_scope_id "$NSXV_VDN_SCOPE_ID"
|
||||
|
@ -165,6 +165,7 @@ function neutron_plugin_configure_service {
|
||||
if [[ "$NSX_L2GW_DRIVER" != "" ]]; then
|
||||
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_l2gw_driver $NSX_L2GW_DRIVER
|
||||
fi
|
||||
iniset /$Q_PLUGIN_CONF_FILE DEFAULT nsx_extension_drivers vmware_nsxv3_dns
|
||||
_nsxv3_ini_set nsx_api_user $NSX_USER
|
||||
_nsxv3_ini_set nsx_api_password $NSX_PASSWORD
|
||||
_nsxv3_ini_set retries $NSX_RETRIES
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
prelude: >
|
||||
The dns-integration extension is now supported in both NSXV and NSXV3
|
||||
plugins. It can be enabled by adding 'vmware_nsxv_dns' (for NSXV) or
|
||||
'vmware_nsxv3_dns' (for NSXV3) to the ``nsx_extension_drivers``
|
||||
configuration variable in neutron.conf file.
|
@ -38,6 +38,9 @@ neutron.qos.notification_drivers =
|
||||
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.extension_drivers =
|
||||
vmware_nsxv_dns = vmware_nsx.extension_drivers.dns_integration:DNSExtensionDriverNSXv
|
||||
vmware_nsxv3_dns = vmware_nsx.extension_drivers.dns_integration:DNSExtensionDriverNSXv3
|
||||
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
|
||||
|
324
vmware_nsx/extension_drivers/dns_integration.py
Normal file
324
vmware_nsx/extension_drivers/dns_integration.py
Normal file
@ -0,0 +1,324 @@
|
||||
# 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.plugins import directory
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
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._i18n import _LE, _LI
|
||||
from vmware_nsx.common import driver_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
DNS_DOMAIN_DEFAULT = 'openstacklocal.'
|
||||
|
||||
|
||||
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)
|
||||
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)
|
||||
if is_dns_domain_default:
|
||||
self._extend_port_dict(plugin_context.session, db_data,
|
||||
db_data, None)
|
||||
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(plugin_context.session, db_data, db_data,
|
||||
dns_data_db)
|
||||
|
||||
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):
|
||||
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):
|
||||
dns_domain = self._get_dns_domain()
|
||||
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):
|
||||
dns_domain = self._get_dns_domain()
|
||||
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):
|
||||
dns_assignment = []
|
||||
dns_name, dns_domain = self._get_request_dns_name_and_domain_name(
|
||||
dns_data_db)
|
||||
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):
|
||||
if port['fixed_ips']:
|
||||
return self._get_dns_names_for_port(port['fixed_ips'], dns_data_db)
|
||||
return []
|
||||
|
||||
def _extend_port_dict(self, session, db_data, response_data, dns_data_db):
|
||||
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)
|
||||
return response_data
|
||||
|
||||
def extend_port_dict(self, session, db_data, response_data):
|
||||
dns_data_db = db_data.dns
|
||||
return self._extend_port_dict(session, 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(_LI("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):
|
||||
LOG.info(_LI("DNSExtensionDriverNSXv3 initialization complete"))
|
||||
|
||||
def _get_dns_domain(self):
|
||||
if 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(_LE("ImportError exception occurred while loading "
|
||||
"the external DNS service driver"))
|
||||
raise dns.ExternalDNSDriverNotFound(
|
||||
driver=cfg.CONF.external_dns_driver)
|
0
vmware_nsx/tests/unit/extension_drivers/__init__.py
Normal file
0
vmware_nsx/tests/unit/extension_drivers/__init__.py
Normal file
103
vmware_nsx/tests/unit/extension_drivers/test_dns_integration.py
Normal file
103
vmware_nsx/tests/unit/extension_drivers/test_dns_integration.py
Normal file
@ -0,0 +1,103 @@
|
||||
# Copyright 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.plugins import directory
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron import context
|
||||
from neutron.extensions import dns
|
||||
|
||||
from vmware_nsx.extension_drivers import dns_integration
|
||||
from vmware_nsx.tests.unit.nsx_v import test_plugin as test_v_plugin
|
||||
from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_v3_plugin
|
||||
|
||||
|
||||
NETWORK_DOMAIN_NAME = 'net-domain.com.'
|
||||
NEW_NETWORK_DOMAIN_NAME = 'new-net-domain.com.'
|
||||
PORT_DNS_NAME = 'port-dns-name'
|
||||
NEW_PORT_DNS_NAME = 'new-port-dns-name'
|
||||
|
||||
|
||||
class NsxDNSIntegrationTestCase(object):
|
||||
_domain = 'domain.com.'
|
||||
dns_integration.DNS_DRIVER = None
|
||||
|
||||
def test_create_network_dns_domain(self):
|
||||
with self.network(dns_domain=NETWORK_DOMAIN_NAME,
|
||||
arg_list=(dns.DNSDOMAIN,)) as network:
|
||||
self.assertEqual(NETWORK_DOMAIN_NAME,
|
||||
network['network'][dns.DNSDOMAIN])
|
||||
|
||||
def test_update_network_dns_domain(self):
|
||||
with self.network(dns_domain=NETWORK_DOMAIN_NAME,
|
||||
arg_list=(dns.DNSDOMAIN,)) as network:
|
||||
update_data = {'network': {dns.DNSDOMAIN: NEW_NETWORK_DOMAIN_NAME}}
|
||||
updated_network = directory.get_plugin().update_network(
|
||||
context.get_admin_context(), network['network']['id'],
|
||||
update_data)
|
||||
self.assertEqual(NEW_NETWORK_DOMAIN_NAME,
|
||||
updated_network[dns.DNSDOMAIN])
|
||||
|
||||
def test_create_port_dns_name(self):
|
||||
with self.port(dns_name=PORT_DNS_NAME,
|
||||
arg_list=(dns.DNSNAME,)) as port:
|
||||
port_data = port['port']
|
||||
dns_assignment = port_data[dns.DNSASSIGNMENT][0]
|
||||
self.assertEqual(PORT_DNS_NAME, port_data[dns.DNSNAME])
|
||||
self.assertEqual(PORT_DNS_NAME, dns_assignment['hostname'])
|
||||
self.assertEqual(port_data['fixed_ips'][0]['ip_address'],
|
||||
dns_assignment['ip_address'])
|
||||
self.assertEqual(PORT_DNS_NAME + '.' + self._domain,
|
||||
dns_assignment['fqdn'])
|
||||
|
||||
def test_update_port_dns_name_ip(self):
|
||||
with self.subnet(cidr='10.0.0.0/24') as subnet:
|
||||
fixed_ips = [{'subnet_id': subnet['subnet']['id'],
|
||||
'ip_address': '10.0.0.3'}]
|
||||
with self.port(subnet=subnet, fixed_ips=fixed_ips,
|
||||
dns_name=PORT_DNS_NAME,
|
||||
arg_list=(dns.DNSNAME,)) as port:
|
||||
update_data = {'port': {
|
||||
dns.DNSNAME: NEW_PORT_DNS_NAME,
|
||||
'fixed_ips': [{'subnet_id': subnet['subnet']['id'],
|
||||
'ip_address': '10.0.0.4'}]}}
|
||||
updated_port = directory.get_plugin().update_port(
|
||||
context.get_admin_context(), port['port']['id'],
|
||||
update_data)
|
||||
dns_assignment = updated_port[dns.DNSASSIGNMENT][0]
|
||||
self.assertEqual(NEW_PORT_DNS_NAME, updated_port[dns.DNSNAME])
|
||||
self.assertEqual(NEW_PORT_DNS_NAME, dns_assignment['hostname'])
|
||||
self.assertEqual(updated_port['fixed_ips'][0]['ip_address'],
|
||||
dns_assignment['ip_address'])
|
||||
self.assertEqual(NEW_PORT_DNS_NAME + '.' + self._domain,
|
||||
dns_assignment['fqdn'])
|
||||
|
||||
|
||||
class NsxVDNSIntegrationTestCase(NsxDNSIntegrationTestCase,
|
||||
test_v_plugin.NsxVPluginV2TestCase):
|
||||
|
||||
def setUp(self):
|
||||
cfg.CONF.set_override('nsx_extension_drivers', ['vmware_nsxv_dns'])
|
||||
cfg.CONF.set_override('dns_domain', self._domain)
|
||||
super(NsxVDNSIntegrationTestCase, self).setUp()
|
||||
|
||||
|
||||
class NsxV3DNSIntegrationTestCase(NsxDNSIntegrationTestCase,
|
||||
test_v3_plugin.NsxV3PluginTestCaseMixin):
|
||||
|
||||
def setUp(self):
|
||||
cfg.CONF.set_override('nsx_extension_drivers', ['vmware_nsxv3_dns'])
|
||||
cfg.CONF.set_override('dns_domain', self._domain, 'nsx_v3')
|
||||
super(NsxV3DNSIntegrationTestCase, self).setUp()
|
Loading…
Reference in New Issue
Block a user