Merge "Allow to parse keywords in dns labels"
This commit is contained in:
commit
e431c09438
@ -1,5 +1,5 @@
|
|||||||
function configure_dns_extension {
|
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 {
|
function configure_dns_integration {
|
||||||
iniset $NEUTRON_CONF DEFAULT external_dns_driver designate
|
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.
|
* Floating ips have a *dns_name* and a *dns_domain* attribute.
|
||||||
* Ports have a *dns_name* attribute.
|
* Ports have a *dns_name* attribute.
|
||||||
* Networks have a *dns_domain* attributes.
|
* 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 availability_zone as az_def
|
||||||
from neutron_lib.api.definitions import default_subnetpools
|
from neutron_lib.api.definitions import default_subnetpools
|
||||||
from neutron_lib.api.definitions import dns
|
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 expose_port_forwarding_in_fip
|
||||||
from neutron_lib.api.definitions import external_net
|
from neutron_lib.api.definitions import external_net
|
||||||
from neutron_lib.api.definitions import extra_dhcp_opt
|
from neutron_lib.api.definitions import extra_dhcp_opt
|
||||||
@ -74,6 +75,7 @@ ML2_SUPPORTED_API_EXTENSIONS_OVN_L3 = [
|
|||||||
sorting.ALIAS,
|
sorting.ALIAS,
|
||||||
project_id.ALIAS,
|
project_id.ALIAS,
|
||||||
dns.ALIAS,
|
dns.ALIAS,
|
||||||
|
dns_domain_keywords.ALIAS,
|
||||||
agent_def.ALIAS,
|
agent_def.ALIAS,
|
||||||
az_def.ALIAS,
|
az_def.ALIAS,
|
||||||
raz_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):
|
def extension_alias(self):
|
||||||
return self._supported_extension_alias
|
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):
|
def process_create_network(self, plugin_context, request_data, db_data):
|
||||||
dns_domain = request_data.get(dns_apidef.DNSDOMAIN)
|
dns_domain = request_data.get(dns_apidef.DNSDOMAIN)
|
||||||
if not validators.is_attr_set(dns_domain):
|
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)
|
flag = self.external_dns_not_needed(plugin_context, network, subnets)
|
||||||
current_dns_name, current_dns_domain = (
|
current_dns_name, current_dns_domain = (
|
||||||
self._calculate_current_dns_name_and_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(
|
dns_data_obj = port_obj.PortDNS(
|
||||||
plugin_context,
|
plugin_context,
|
||||||
@ -115,7 +119,7 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
dns_data_obj.create()
|
dns_data_obj.create()
|
||||||
return dns_data_obj
|
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,
|
external_dns_domain,
|
||||||
no_external_dns_service):
|
no_external_dns_service):
|
||||||
# When creating a new PortDNS object, the current_dns_name and
|
# 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
|
are_both_dns_attributes_set = dns_name and external_dns_domain
|
||||||
if no_external_dns_service or not are_both_dns_attributes_set:
|
if no_external_dns_service or not are_both_dns_attributes_set:
|
||||||
return '', ''
|
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,
|
def _update_dns_db(self, plugin_context, request_data, db_data, network,
|
||||||
subnets):
|
subnets):
|
||||||
@ -153,7 +158,8 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
dns_data_db = self._populate_previous_external_dns_data(
|
dns_data_db = self._populate_previous_external_dns_data(
|
||||||
dns_data_db)
|
dns_data_db)
|
||||||
dns_data_db = self._populate_current_external_dns_data(
|
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)
|
is_dns_name_changed, is_dns_domain_changed)
|
||||||
elif not dns_data_db['current_dns_name']:
|
elif not dns_data_db['current_dns_name']:
|
||||||
# If port was removed from external DNS service in previous
|
# If port was removed from external DNS service in previous
|
||||||
@ -176,15 +182,17 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
dns_data_db['current_dns_domain'])
|
dns_data_db['current_dns_domain'])
|
||||||
return dns_data_db
|
return dns_data_db
|
||||||
|
|
||||||
def _populate_current_external_dns_data(self, request_data, network,
|
def _populate_current_external_dns_data(self, plugin_context, request_data,
|
||||||
dns_data_db, dns_name, dns_domain,
|
network, dns_data_db, dns_name,
|
||||||
is_dns_name_changed,
|
dns_domain, is_dns_name_changed,
|
||||||
is_dns_domain_changed):
|
is_dns_domain_changed):
|
||||||
if is_dns_name_changed or is_dns_domain_changed:
|
if is_dns_name_changed or is_dns_domain_changed:
|
||||||
if is_dns_name_changed:
|
if is_dns_name_changed:
|
||||||
dns_data_db[dns_apidef.DNSNAME] = dns_name
|
dns_data_db[dns_apidef.DNSNAME] = dns_name
|
||||||
external_dns_domain = (dns_data_db[dns_apidef.DNSDOMAIN] or
|
external_dns_domain = (dns_data_db[dns_apidef.DNSDOMAIN] or
|
||||||
network.get(dns_apidef.DNSDOMAIN))
|
network.get(dns_apidef.DNSDOMAIN))
|
||||||
|
external_dns_domain = self._parse_dns_domain(
|
||||||
|
plugin_context, external_dns_domain)
|
||||||
if is_dns_domain_changed:
|
if is_dns_domain_changed:
|
||||||
dns_data_db[dns_apidef.DNSDOMAIN] = dns_domain
|
dns_data_db[dns_apidef.DNSDOMAIN] = dns_domain
|
||||||
external_dns_domain = request_data[dns_apidef.DNSDOMAIN]
|
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 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 external_net
|
||||||
from neutron_lib.api.definitions import portbindings
|
from neutron_lib.api.definitions import portbindings
|
||||||
from neutron_lib.api.definitions import provider_net as pnet
|
from neutron_lib.api.definitions import provider_net as pnet
|
||||||
@ -107,6 +108,7 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
|
|||||||
if not api_extensions.is_extension_supported(
|
if not api_extensions.is_extension_supported(
|
||||||
core_plugin, dns_apidef.ALIAS):
|
core_plugin, dns_apidef.ALIAS):
|
||||||
aliases.remove(dns_apidef.ALIAS)
|
aliases.remove(dns_apidef.ALIAS)
|
||||||
|
aliases.remove(dns_domain_keywords.ALIAS)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_extension_aliases(self):
|
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
|
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
|
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
|
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 =
|
neutron.ipam_drivers =
|
||||||
fake = neutron.tests.unit.ipam.fake_driver:FakeDriver
|
fake = neutron.tests.unit.ipam.fake_driver:FakeDriver
|
||||||
internal = neutron.ipam.drivers.neutrondb_ipam.driver:NeutronDbPool
|
internal = neutron.ipam.drivers.neutrondb_ipam.driver:NeutronDbPool
|
||||||
|
Loading…
Reference in New Issue
Block a user