Allow to parse keywords in dns labels
Co-Authored-By: Slawek Kaplonski <skaplons@redhat.com> Related-Bug: #1843218 Change-Id: Ie8b6eb88e046c172d99212f966bdee327f42ed37
This commit is contained in:
parent
90309cf6e2
commit
7727fc07e6
@ -1,5 +1,5 @@
|
||||
function configure_dns_extension {
|
||||
neutron_ml2_extension_driver_add "subnet_dns_publish_fixed_ip"
|
||||
neutron_ml2_extension_driver_add "dns_domain_keywords"
|
||||
}
|
||||
function configure_dns_integration {
|
||||
iniset $NEUTRON_CONF DEFAULT external_dns_driver designate
|
||||
|
@ -41,3 +41,166 @@ Specifically, floating ips, ports and networks are extended as follows:
|
||||
* Floating ips have a *dns_name* and a *dns_domain* attribute.
|
||||
* Ports have a *dns_name* attribute.
|
||||
* Networks have a *dns_domain* attributes.
|
||||
|
||||
|
||||
Pre-configured domains for projects and users
|
||||
---------------------------------------------
|
||||
|
||||
ML2 plugin extension ``dns_domain_keywords`` provides same dns integration as
|
||||
``dns_domain_ports`` and ``subnet_dns_publish_fixed_ip`` and it also allows to
|
||||
configure network's dns_domain with some specific keywords: ``<project_id>``,
|
||||
``<project_name>``, ``<user_id>``, ``<user_name>``. Please see example below for
|
||||
more details.
|
||||
|
||||
* Create DNS zone. ``0511951bd56e4a0aac27ac65e00bddd0`` is ID of the project
|
||||
used in the example
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack zone create 0511951bd56e4a0aac27ac65e00bddd0.example.com. --email admin@0511951bd56e4a0aac27ac65e00bddd0.example.com
|
||||
+----------------+----------------------------------------------------+
|
||||
| Field | Value |
|
||||
+----------------+----------------------------------------------------+
|
||||
| action | CREATE |
|
||||
| attributes | |
|
||||
| created_at | 2021-02-19T14:48:06.000000 |
|
||||
| description | None |
|
||||
| email | admin@0511951bd56e4a0aac27ac65e00bddd0.example.com |
|
||||
| id | c14a8edc-d0b9-4cdd-93f1-1ab5a5f5ff9d |
|
||||
| masters | |
|
||||
| name | 0511951bd56e4a0aac27ac65e00bddd0.example.com. |
|
||||
| pool_id | 794ccc2c-d751-44fe-b57f-8894c9f5c842 |
|
||||
| project_id | 0511951bd56e4a0aac27ac65e00bddd0 |
|
||||
| serial | 1613746085 |
|
||||
| status | PENDING |
|
||||
| transferred_at | None |
|
||||
| ttl | 3600 |
|
||||
| type | PRIMARY |
|
||||
| updated_at | None |
|
||||
| version | 1 |
|
||||
+----------------+----------------------------------------------------+
|
||||
|
||||
* Create network with dns_domain
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack network create dns-test-network --dns-domain "<project_id>.demo.net."
|
||||
+---------------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+---------------------------+--------------------------------------+
|
||||
| admin_state_up | UP |
|
||||
| availability_zone_hints | |
|
||||
| availability_zones | |
|
||||
| created_at | 2021-02-19T15:16:36Z |
|
||||
| description | |
|
||||
| dns_domain | <project_id>.demo.net. |
|
||||
| id | fb247287-43aa-4a83-b768-a3b34dc6735a |
|
||||
| ipv4_address_scope | None |
|
||||
| ipv6_address_scope | None |
|
||||
| is_default | False |
|
||||
| is_vlan_transparent | None |
|
||||
| mtu | 1450 |
|
||||
| name | dns-test-network |
|
||||
| port_security_enabled | True |
|
||||
| project_id | 0511951bd56e4a0aac27ac65e00bddd0 |
|
||||
| provider:network_type | vxlan |
|
||||
| provider:physical_network | None |
|
||||
| provider:segmentation_id | 1003 |
|
||||
| qos_policy_id | None |
|
||||
| revision_number | 1 |
|
||||
| router:external | Internal |
|
||||
| segments | None |
|
||||
| shared | False |
|
||||
| status | ACTIVE |
|
||||
| subnets | |
|
||||
| tags | |
|
||||
| updated_at | 2021-02-19T15:16:37Z |
|
||||
+---------------------------+--------------------------------------+
|
||||
|
||||
$ openstack subnet create --network dns-test-network --subnet-range 192.168.100.0/24 --dns-publish-fixed-ip dns-test-subnet
|
||||
+----------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+----------------------+--------------------------------------+
|
||||
| allocation_pools | 192.168.100.2-192.168.100.254 |
|
||||
| cidr | 192.168.100.0/24 |
|
||||
| created_at | 2021-02-19T15:21:50Z |
|
||||
| description | |
|
||||
| dns_nameservers | |
|
||||
| dns_publish_fixed_ip | True |
|
||||
| enable_dhcp | True |
|
||||
| gateway_ip | 192.168.100.1 |
|
||||
| host_routes | |
|
||||
| id | 2547a3f2-374f-4262-aed5-3a69af73e732 |
|
||||
| ip_version | 4 |
|
||||
| ipv6_address_mode | None |
|
||||
| ipv6_ra_mode | None |
|
||||
| name | dns-test-subnet |
|
||||
| network_id | fb247287-43aa-4a83-b768-a3b34dc6735a |
|
||||
| prefix_length | None |
|
||||
| project_id | 0511951bd56e4a0aac27ac65e00bddd0 |
|
||||
| revision_number | 0 |
|
||||
| segment_id | None |
|
||||
| service_types | |
|
||||
| subnetpool_id | None |
|
||||
| tags | |
|
||||
| updated_at | 2021-02-19T15:21:50Z |
|
||||
+----------------------+--------------------------------------+
|
||||
|
||||
* Create port in that network
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack port create --network dns-test-network --dns-name dns-test-port test-port
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| Field | Value |
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| admin_state_up | UP |
|
||||
| allowed_address_pairs | |
|
||||
| binding_host_id | |
|
||||
| binding_profile | |
|
||||
| binding_vif_details | |
|
||||
| binding_vif_type | unbound |
|
||||
| binding_vnic_type | normal |
|
||||
| created_at | 2021-02-19T15:22:51Z |
|
||||
| data_plane_status | None |
|
||||
| description | |
|
||||
| device_id | |
|
||||
| device_owner | |
|
||||
| device_profile | None |
|
||||
| dns_assignment | fqdn='dns-test-port.0511951bd56e4a0aac27ac65e00bddd0.example.com.', hostname='dns-test-port', ip_address='192.168.100.17' |
|
||||
| dns_domain | |
|
||||
| dns_name | dns-test-port |
|
||||
| extra_dhcp_opts | |
|
||||
| fixed_ips | ip_address='192.168.100.17', subnet_id='2547a3f2-374f-4262-aed5-3a69af73e732' |
|
||||
| id | f30908a1-6ef5-4137-bff4-c1205c6660ee |
|
||||
| ip_allocation | None |
|
||||
| mac_address | fa:16:3e:e8:33:b8 |
|
||||
| name | test-port |
|
||||
| network_id | fb247287-43aa-4a83-b768-a3b34dc6735a |
|
||||
| numa_affinity_policy | None |
|
||||
| port_security_enabled | True |
|
||||
| project_id | 0511951bd56e4a0aac27ac65e00bddd0 |
|
||||
| propagate_uplink_status | None |
|
||||
| qos_network_policy_id | None |
|
||||
| qos_policy_id | None |
|
||||
| resource_request | None |
|
||||
| revision_number | 1 |
|
||||
| security_group_ids | 4425c3fd-6705-4134-9878-07b333d81314 |
|
||||
| status | DOWN |
|
||||
| tags | |
|
||||
| trunk_details | None |
|
||||
| updated_at | 2021-02-19T15:22:51Z |
|
||||
+-------------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
* Test if recordset was created properly in the DNS zone
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack recordset list c14a8edc-d0b9-4cdd-93f1-1ab5a5f5ff9d
|
||||
+--------------------------------------+-------------------------------------------------------------+------+------------------------------------------------------------------------------------------------------+--------+--------+
|
||||
| id | name | type | records | status | action |
|
||||
+--------------------------------------+-------------------------------------------------------------+------+------------------------------------------------------------------------------------------------------+--------+--------+
|
||||
| 1c302468-4e30-466e-9330-e4cd9191ff99 | 0511951bd56e4a0aac27ac65e00bddd0.example.com. | SOA | ns1.devstack.org. admin.0511951bd56e4a0aac27ac65e00bddd0.example.com. 1613748171 3549 600 86400 3600 | ACTIVE | NONE |
|
||||
| 99ce92d1-8c7a-4193-aeb2-44835048a6fa | 0511951bd56e4a0aac27ac65e00bddd0.example.com. | NS | ns1.devstack.org. | ACTIVE | NONE |
|
||||
| 01f0569d-ce81-4424-915f-c6fe6229256e | dns-test-port.0511951bd56e4a0aac27ac65e00bddd0.example.com. | A | 192.168.100.17 | ACTIVE | NONE |
|
||||
+--------------------------------------+-------------------------------------------------------------+------+------------------------------------------------------------------------------------------------------+--------+--------+
|
||||
|
@ -18,6 +18,7 @@ from neutron_lib.api.definitions import auto_allocated_topology
|
||||
from neutron_lib.api.definitions import availability_zone as az_def
|
||||
from neutron_lib.api.definitions import default_subnetpools
|
||||
from neutron_lib.api.definitions import dns
|
||||
from neutron_lib.api.definitions import dns_domain_keywords
|
||||
from neutron_lib.api.definitions import expose_port_forwarding_in_fip
|
||||
from neutron_lib.api.definitions import external_net
|
||||
from neutron_lib.api.definitions import extra_dhcp_opt
|
||||
@ -70,6 +71,7 @@ ML2_SUPPORTED_API_EXTENSIONS_OVN_L3 = [
|
||||
sorting.ALIAS,
|
||||
project_id.ALIAS,
|
||||
dns.ALIAS,
|
||||
dns_domain_keywords.ALIAS,
|
||||
agent_def.ALIAS,
|
||||
az_def.ALIAS,
|
||||
raz_def.ALIAS,
|
||||
|
20
neutron/extensions/dns_integration_domain_keywords.py
Normal file
20
neutron/extensions/dns_integration_domain_keywords.py
Normal file
@ -0,0 +1,20 @@
|
||||
# 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.definitions import dns_domain_keywords as apidef
|
||||
from neutron_lib.api import extensions
|
||||
|
||||
|
||||
class Dns_integration_domain_keywords(extensions.APIExtensionDescriptor):
|
||||
"""Extension class supporting configuration of dns domain with keywords."""
|
||||
|
||||
api_definition = apidef
|
46
neutron/plugins/ml2/extensions/dns_domain_keywords.py
Normal file
46
neutron/plugins/ml2/extensions/dns_domain_keywords.py
Normal file
@ -0,0 +1,46 @@
|
||||
# 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.definitions import dns as dns_apidef
|
||||
from neutron_lib.api.definitions import dns_domain_keywords
|
||||
from neutron_lib.api.definitions import dns_domain_ports as ports_apidef
|
||||
from neutron_lib.api.definitions import subnet_dns_publish_fixed_ip as sn_dns
|
||||
from neutron_lib import constants as lib_const
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.plugins.ml2.extensions import subnet_dns_publish_fixed_ip
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DnsDomainKeywordsExtensionDriver(
|
||||
subnet_dns_publish_fixed_ip.SubnetDNSPublishFixedIPExtensionDriver):
|
||||
|
||||
_supported_extension_aliases = [dns_apidef.ALIAS,
|
||||
ports_apidef.ALIAS,
|
||||
sn_dns.ALIAS,
|
||||
dns_domain_keywords.ALIAS]
|
||||
|
||||
def initialize(self):
|
||||
LOG.info("DnsDomainKeywordsExtensionDriver initialization complete")
|
||||
|
||||
@staticmethod
|
||||
def _parse_dns_domain(plugin_context, domain):
|
||||
for keyword in lib_const.DNS_LABEL_KEYWORDS:
|
||||
keyword_value = getattr(plugin_context, keyword, None)
|
||||
if keyword_value is not None:
|
||||
domain = domain.replace('<' + keyword + '>', keyword_value)
|
||||
else:
|
||||
LOG.warning("Keyword <%s> does not have value in current "
|
||||
"context and it will not be replaced in the "
|
||||
"domain %s", keyword, domain)
|
||||
return domain
|
@ -43,6 +43,10 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
def extension_alias(self):
|
||||
return self._supported_extension_alias
|
||||
|
||||
@staticmethod
|
||||
def _parse_dns_domain(plugin_context, domain):
|
||||
return domain
|
||||
|
||||
def process_create_network(self, plugin_context, request_data, db_data):
|
||||
dns_domain = request_data.get(dns_apidef.DNSDOMAIN)
|
||||
if not validators.is_attr_set(dns_domain):
|
||||
@ -101,7 +105,7 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
flag = self.external_dns_not_needed(plugin_context, network, subnets)
|
||||
current_dns_name, current_dns_domain = (
|
||||
self._calculate_current_dns_name_and_domain(
|
||||
dns_name, external_dns_domain, flag))
|
||||
plugin_context, dns_name, external_dns_domain, flag))
|
||||
|
||||
dns_data_obj = port_obj.PortDNS(
|
||||
plugin_context,
|
||||
@ -115,7 +119,7 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
dns_data_obj.create()
|
||||
return dns_data_obj
|
||||
|
||||
def _calculate_current_dns_name_and_domain(self, dns_name,
|
||||
def _calculate_current_dns_name_and_domain(self, plugin_context, dns_name,
|
||||
external_dns_domain,
|
||||
no_external_dns_service):
|
||||
# When creating a new PortDNS object, the current_dns_name and
|
||||
@ -131,7 +135,8 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
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
|
||||
return dns_name, self._parse_dns_domain(
|
||||
plugin_context, external_dns_domain)
|
||||
|
||||
def _update_dns_db(self, plugin_context, request_data, db_data, network,
|
||||
subnets):
|
||||
@ -153,7 +158,8 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
dns_data_db = self._populate_previous_external_dns_data(
|
||||
dns_data_db)
|
||||
dns_data_db = self._populate_current_external_dns_data(
|
||||
request_data, network, dns_data_db, dns_name, dns_domain,
|
||||
plugin_context, request_data,
|
||||
network, dns_data_db, dns_name, dns_domain,
|
||||
is_dns_name_changed, is_dns_domain_changed)
|
||||
elif not dns_data_db['current_dns_name']:
|
||||
# If port was removed from external DNS service in previous
|
||||
@ -176,15 +182,17 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
dns_data_db['current_dns_domain'])
|
||||
return dns_data_db
|
||||
|
||||
def _populate_current_external_dns_data(self, request_data, network,
|
||||
dns_data_db, dns_name, dns_domain,
|
||||
is_dns_name_changed,
|
||||
def _populate_current_external_dns_data(self, plugin_context, request_data,
|
||||
network, dns_data_db, dns_name,
|
||||
dns_domain, is_dns_name_changed,
|
||||
is_dns_domain_changed):
|
||||
if is_dns_name_changed or is_dns_domain_changed:
|
||||
if is_dns_name_changed:
|
||||
dns_data_db[dns_apidef.DNSNAME] = dns_name
|
||||
external_dns_domain = (dns_data_db[dns_apidef.DNSDOMAIN] or
|
||||
network.get(dns_apidef.DNSDOMAIN))
|
||||
external_dns_domain = self._parse_dns_domain(
|
||||
plugin_context, external_dns_domain)
|
||||
if is_dns_domain_changed:
|
||||
dns_data_db[dns_apidef.DNSDOMAIN] = dns_domain
|
||||
external_dns_domain = request_data[dns_apidef.DNSDOMAIN]
|
||||
|
@ -13,6 +13,7 @@
|
||||
#
|
||||
|
||||
from neutron_lib.api.definitions import dns as dns_apidef
|
||||
from neutron_lib.api.definitions import dns_domain_keywords
|
||||
from neutron_lib.api.definitions import external_net
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib.api.definitions import provider_net as pnet
|
||||
@ -106,6 +107,7 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
||||
if not api_extensions.is_extension_supported(
|
||||
core_plugin, dns_apidef.ALIAS):
|
||||
aliases.remove(dns_apidef.ALIAS)
|
||||
aliases.remove(dns_domain_keywords.ALIAS)
|
||||
|
||||
@property
|
||||
def supported_extension_aliases(self):
|
||||
|
@ -0,0 +1,226 @@
|
||||
# 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 neutron_lib.api.definitions import dns as dns_apidef
|
||||
from neutron_lib.api.definitions import provider_net as pnet
|
||||
from neutron_lib import context
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.objects import ports as port_obj
|
||||
from neutron.plugins.ml2.extensions import dns_domain_keywords
|
||||
from neutron.tests.unit.plugins.ml2.extensions import test_dns_integration
|
||||
|
||||
|
||||
PROJECT_ID = uuidutils.generate_uuid()
|
||||
|
||||
|
||||
class DNSDomainKeyworkdsTestCase(
|
||||
test_dns_integration.DNSIntegrationTestCase):
|
||||
|
||||
_extension_drivers = ['dns_domain_keywords']
|
||||
_expected_dns_domain = "%s.%s" % (PROJECT_ID,
|
||||
test_dns_integration.DNSDOMAIN)
|
||||
|
||||
def _create_port_for_test(self, provider_net=True, dns_domain=True,
|
||||
dns_name=True, ipv4=True, ipv6=True,
|
||||
dns_domain_port=False):
|
||||
net_kwargs = {}
|
||||
if provider_net:
|
||||
net_kwargs = {
|
||||
'arg_list': (pnet.NETWORK_TYPE, pnet.SEGMENTATION_ID,),
|
||||
pnet.NETWORK_TYPE: 'vxlan',
|
||||
pnet.SEGMENTATION_ID: '2016',
|
||||
}
|
||||
if dns_domain:
|
||||
net_kwargs[dns_apidef.DNSDOMAIN] = (
|
||||
"<project_id>.%s" % test_dns_integration.DNSDOMAIN)
|
||||
net_kwargs['arg_list'] = \
|
||||
net_kwargs.get('arg_list', ()) + (dns_apidef.DNSDOMAIN,)
|
||||
net_kwargs['shared'] = True
|
||||
res = self._create_network(self.fmt, 'test_network', True,
|
||||
**net_kwargs)
|
||||
network = self.deserialize(self.fmt, res)
|
||||
if ipv4:
|
||||
cidr = '10.0.0.0/24'
|
||||
self._create_subnet_for_test(network['network']['id'], cidr)
|
||||
|
||||
if ipv6:
|
||||
cidr = 'fd3d:bdd4:da60::/64'
|
||||
self._create_subnet_for_test(network['network']['id'], cidr)
|
||||
|
||||
port_kwargs = {}
|
||||
if dns_name:
|
||||
port_kwargs = {
|
||||
'arg_list': (dns_apidef.DNSNAME,),
|
||||
dns_apidef.DNSNAME: test_dns_integration.DNSNAME
|
||||
}
|
||||
if dns_domain_port:
|
||||
port_kwargs[dns_apidef.DNSDOMAIN] = (
|
||||
test_dns_integration.PORTDNSDOMAIN)
|
||||
port_kwargs['arg_list'] = (port_kwargs.get('arg_list', ()) +
|
||||
(dns_apidef.DNSDOMAIN,))
|
||||
res = self._create_port('json', network['network']['id'],
|
||||
set_context=True, tenant_id=PROJECT_ID,
|
||||
**port_kwargs)
|
||||
self.assertEqual(201, res.status_int)
|
||||
port = self.deserialize(self.fmt, res)['port']
|
||||
ctx = context.get_admin_context()
|
||||
dns_data_db = port_obj.PortDNS.get_object(ctx, port_id=port['id'])
|
||||
return port, dns_data_db
|
||||
|
||||
def _update_port_for_test(self, port,
|
||||
new_dns_name=test_dns_integration.NEWDNSNAME,
|
||||
new_dns_domain=None, **kwargs):
|
||||
test_dns_integration.mock_client.reset_mock()
|
||||
ip_addresses = [netaddr.IPAddress(ip['ip_address'])
|
||||
for ip in port['fixed_ips']]
|
||||
records_v4 = [ip for ip in ip_addresses if ip.version == 4]
|
||||
records_v6 = [ip for ip in ip_addresses if ip.version == 6]
|
||||
recordsets = []
|
||||
if records_v4:
|
||||
recordsets.append({'id': test_dns_integration.V4UUID,
|
||||
'records': records_v4})
|
||||
if records_v6:
|
||||
recordsets.append({'id': test_dns_integration.V6UUID,
|
||||
'records': records_v6})
|
||||
test_dns_integration.mock_client.recordsets.list.return_value = (
|
||||
recordsets)
|
||||
test_dns_integration.mock_admin_client.reset_mock()
|
||||
body = {}
|
||||
if new_dns_name is not None:
|
||||
body['dns_name'] = new_dns_name
|
||||
if new_dns_domain is not None:
|
||||
body[dns_apidef.DNSDOMAIN] = new_dns_domain
|
||||
body.update(kwargs)
|
||||
data = {'port': body}
|
||||
# NOTE(slaweq): Admin context is required here to be able to update
|
||||
# fixed_ips of the port as by default it is not possible for non-admin
|
||||
# users
|
||||
ctx = context.Context(project_id=PROJECT_ID, is_admin=True)
|
||||
req = self.new_update_request('ports', data, port['id'], context=ctx)
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(200, res.status_int)
|
||||
port = self.deserialize(self.fmt, res)['port']
|
||||
admin_ctx = context.get_admin_context()
|
||||
dns_data_db = port_obj.PortDNS.get_object(admin_ctx,
|
||||
port_id=port['id'])
|
||||
return port, dns_data_db
|
||||
|
||||
def _verify_port_dns(self, port, dns_data_db, dns_name=True,
|
||||
dns_domain=True, ptr_zones=True, delete_records=False,
|
||||
provider_net=True, dns_driver=True, original_ips=None,
|
||||
current_dns_name=test_dns_integration.DNSNAME,
|
||||
previous_dns_name='', dns_domain_port=False,
|
||||
current_dns_domain=None, previous_dns_domain=None):
|
||||
current_dns_domain = current_dns_domain or self._expected_dns_domain
|
||||
previous_dns_domain = previous_dns_domain or self._expected_dns_domain
|
||||
super(DNSDomainKeyworkdsTestCase, self)._verify_port_dns(
|
||||
port=port, dns_data_db=dns_data_db, dns_name=dns_name,
|
||||
dns_domain=dns_domain, ptr_zones=ptr_zones,
|
||||
delete_records=delete_records, provider_net=provider_net,
|
||||
dns_driver=dns_driver, original_ips=original_ips,
|
||||
current_dns_name=current_dns_name,
|
||||
previous_dns_name=previous_dns_name,
|
||||
dns_domain_port=dns_domain_port,
|
||||
current_dns_domain=current_dns_domain,
|
||||
previous_dns_domain=previous_dns_domain)
|
||||
|
||||
def test__parse_dns_domain(self, *mocks):
|
||||
ctx = context.Context(
|
||||
project_id=uuidutils.generate_uuid(),
|
||||
project_name="project",
|
||||
user_id=uuidutils.generate_uuid(),
|
||||
user_name="user"
|
||||
)
|
||||
domains = [
|
||||
("<project_id>.<project_name>.<user_id>.<user_name>.domain",
|
||||
"%s.%s.%s.%s.domain" % (ctx.project_id, ctx.project_name,
|
||||
ctx.user_id, ctx.user_name)),
|
||||
("<project_id>.domain",
|
||||
"%s.domain" % ctx.project_id),
|
||||
("<project_name>.domain",
|
||||
"%s.domain" % ctx.project_name),
|
||||
("<user_id>.domain",
|
||||
"%s.domain" % ctx.user_id),
|
||||
("<user_name>.domain",
|
||||
"%s.domain" % ctx.user_name)]
|
||||
|
||||
for domain, expected_domain in domains:
|
||||
self.assertEqual(
|
||||
expected_domain,
|
||||
dns_domain_keywords.DnsDomainKeywordsExtensionDriver.
|
||||
_parse_dns_domain(ctx, domain))
|
||||
|
||||
def test__parse_dns_domain_missing_fields_in_context(self, *mocks):
|
||||
domain = "<project_id>.<project_name>.<user_id>.<user_name>.domain"
|
||||
ctx = context.Context(
|
||||
project_id=uuidutils.generate_uuid(),
|
||||
project_name=None,
|
||||
user_id=uuidutils.generate_uuid(),
|
||||
user_name="user"
|
||||
)
|
||||
expected_domain = "%s.<project_name>.%s.%s.domain" % (
|
||||
ctx.project_id, ctx.user_id, ctx.user_name)
|
||||
|
||||
self.assertEqual(
|
||||
expected_domain,
|
||||
dns_domain_keywords.DnsDomainKeywordsExtensionDriver.
|
||||
_parse_dns_domain(ctx, domain))
|
||||
|
||||
def test_update_port_with_current_dns_name(self, *mocks):
|
||||
port, dns_data_db = self._create_port_for_test()
|
||||
port, dns_data_db = self._update_port_for_test(
|
||||
port, new_dns_name=test_dns_integration.DNSNAME)
|
||||
self.assertEqual(test_dns_integration.DNSNAME,
|
||||
dns_data_db['current_dns_name'])
|
||||
self.assertEqual(self._expected_dns_domain,
|
||||
dns_data_db['current_dns_domain'])
|
||||
self.assertEqual('', dns_data_db['previous_dns_name'])
|
||||
self.assertEqual('', dns_data_db['previous_dns_domain'])
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_client.recordsets.create.call_args_list)
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_admin_client.recordsets.
|
||||
create.call_args_list)
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_client.recordsets.delete.call_args_list)
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_admin_client.recordsets.
|
||||
delete.call_args_list)
|
||||
|
||||
def test_update_port_non_dns_name_attribute(self, *mocks):
|
||||
port, dns_data_db = self._create_port_for_test()
|
||||
port_name = 'port_name'
|
||||
kwargs = {'name': port_name}
|
||||
port, dns_data_db = self._update_port_for_test(port,
|
||||
new_dns_name=None,
|
||||
**kwargs)
|
||||
self.assertEqual(test_dns_integration.DNSNAME,
|
||||
dns_data_db['current_dns_name'])
|
||||
self.assertEqual(self._expected_dns_domain,
|
||||
dns_data_db['current_dns_domain'])
|
||||
self.assertEqual('', dns_data_db['previous_dns_name'])
|
||||
self.assertEqual('', dns_data_db['previous_dns_domain'])
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_client.recordsets.create.call_args_list)
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_admin_client.recordsets.
|
||||
create.call_args_list)
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_client.recordsets.delete.call_args_list)
|
||||
self.assertFalse(
|
||||
test_dns_integration.mock_admin_client.recordsets.
|
||||
delete.call_args_list)
|
||||
self.assertEqual(port_name, port['name'])
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Special keywords ``<project_id>``, ``<project_name>``, ``<user_name>``
|
||||
and ``<user_id>`` can be used in the network's, port's and floating IP's
|
||||
``dns_domain`` attribute.
|
||||
Those special keywords will be replaced by the corresponding data from the
|
||||
request context.
|
||||
With that cloud admin can define dns_domain for shared network and ports
|
||||
which belongs to the other projects in the way that each project can use
|
||||
separate DNS zones which needs to be pre-created by users.
|
||||
To enable this feature ``dns_domain_keywords`` ML2 plugin extension has to
|
||||
be enabled in the Neutron config.
|
||||
Enabling multiple dns_integration extensions at the same time leads to an
|
||||
error.
|
@ -118,6 +118,7 @@ neutron.ml2.extension_drivers =
|
||||
uplink_status_propagation = neutron.plugins.ml2.extensions.uplink_status_propagation:UplinkStatusPropagationExtensionDriver
|
||||
tag_ports_during_bulk_creation = neutron.plugins.ml2.extensions.tag_ports_during_bulk_creation:TagPortsDuringBulkCreationExtensionDriver
|
||||
subnet_dns_publish_fixed_ip = neutron.plugins.ml2.extensions.subnet_dns_publish_fixed_ip:SubnetDNSPublishFixedIPExtensionDriver
|
||||
dns_domain_keywords = neutron.plugins.ml2.extensions.dns_domain_keywords:DnsDomainKeywordsExtensionDriver
|
||||
neutron.ipam_drivers =
|
||||
fake = neutron.tests.unit.ipam.fake_driver:FakeDriver
|
||||
internal = neutron.ipam.drivers.neutrondb_ipam.driver:NeutronDbPool
|
||||
|
Loading…
Reference in New Issue
Block a user