Merge "Allow to select subnets to publish DNS records"
This commit is contained in:
commit
250d8a61c8
@ -1,5 +1,5 @@
|
|||||||
function configure_dns_extension {
|
function configure_dns_extension {
|
||||||
neutron_ml2_extension_driver_add "dns_domain_ports"
|
neutron_ml2_extension_driver_add "subnet_dns_publish_fixed_ip"
|
||||||
}
|
}
|
||||||
function configure_dns_integration {
|
function configure_dns_integration {
|
||||||
iniset $NEUTRON_CONF DEFAULT external_dns_driver designate
|
iniset $NEUTRON_CONF DEFAULT external_dns_driver designate
|
||||||
|
@ -94,7 +94,7 @@ In each of the use cases described below:
|
|||||||
* The examples assume the OpenStack DNS service as the external DNS.
|
* The examples assume the OpenStack DNS service as the external DNS.
|
||||||
* A, AAAA and PTR records will be created in the DNS service.
|
* A, AAAA and PTR records will be created in the DNS service.
|
||||||
* Before executing any of the use cases, the user must create in the DNS
|
* Before executing any of the use cases, the user must create in the DNS
|
||||||
service under his project a DNS zone where the A and AAAA records will be
|
service under their project a DNS zone where the A and AAAA records will be
|
||||||
created. For the description of the use cases below, it is assumed the zone
|
created. For the description of the use cases below, it is assumed the zone
|
||||||
``example.org.`` was created previously.
|
``example.org.`` was created previously.
|
||||||
* The PTR records will be created in zones owned by the project specified
|
* The PTR records will be created in zones owned by the project specified
|
||||||
@ -495,7 +495,8 @@ Note that in this use case:
|
|||||||
|
|
||||||
Following are the PTR records created for this example. Note that for
|
Following are the PTR records created for this example. Note that for
|
||||||
IPv4, the value of ``ipv4_ptr_zone_prefix_size`` is 24. Also, since the zone
|
IPv4, the value of ``ipv4_ptr_zone_prefix_size`` is 24. Also, since the zone
|
||||||
for the PTR records is created in the ``service`` project, you need to use
|
for the PTR records is created in the project specified in the ``[designate]``
|
||||||
|
section in the config above, usually the ``service`` project, you need to use
|
||||||
admin credentials in order to be able to view it.
|
admin credentials in order to be able to view it.
|
||||||
|
|
||||||
|
|
||||||
@ -516,7 +517,195 @@ Use case 3: Ports are published directly in the external DNS service
|
|||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
In this case, the user is creating ports or booting instances on a network
|
In this case, the user is creating ports or booting instances on a network
|
||||||
that is accessible externally. If the user wants to publish a port in the
|
that is accessible externally. There are multiple possible scenarios here
|
||||||
|
depending on which of the DNS extensions is enabled in the Neutron
|
||||||
|
configuration. These extensions are described in the following in
|
||||||
|
descending order of priority.
|
||||||
|
|
||||||
|
Use case 3a: The ``subnet_dns_publish_fixed_ips`` extension
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When the ``subnet_dns_publish_fixed_ips`` extension is enabled, it is possible
|
||||||
|
to make a selection per subnet whether DNS records should be published for
|
||||||
|
fixed IPs that are assigned to ports from that subnet. This happens via the
|
||||||
|
``dns_publish_fixed_ips`` attribute that this extension adds to the
|
||||||
|
definition of the subnet resource.
|
||||||
|
It is a boolean flag with a default value of ``False`` but it can be set
|
||||||
|
to ``True`` when creating or updating subnets. When the flag is ``True``,
|
||||||
|
all fixed IPs from this subnet are published in the external DNS service,
|
||||||
|
while at the same time IPs from other subnets having the flag set to
|
||||||
|
``False`` are not published, even if they otherwise would meet the
|
||||||
|
criteria from the other use cases below.
|
||||||
|
|
||||||
|
A typical scenario for this use case is a dual stack deployment, where a
|
||||||
|
tenant network would be configured with both an IPv4 and an IPv6 subnet.
|
||||||
|
The IPv4 subnet will usually be using some RFC1918 address space and being
|
||||||
|
NATted towards the outside on the attached router, therefore the fixed IPs
|
||||||
|
from this subnet will not be globally routed and they also should not be
|
||||||
|
published in the DNS service. (One can still bind floating IPs to these
|
||||||
|
fixed IPs and DNS records for those floating IPs can still be published
|
||||||
|
as described above in use cases 1 and 2).
|
||||||
|
|
||||||
|
But for the IPv6 subnet, no NAT will happen, instead the subnet will be
|
||||||
|
configured with some globally routable prefix and thus the user will want
|
||||||
|
to publish DNS records for fixed IPs from this subnet. This can be
|
||||||
|
achieved by setting the ``dns_publish_fixed_ips`` attribute for the
|
||||||
|
IPv6 subnet to ``True`` while leaving the flag set to ``False`` for
|
||||||
|
the IPv4 subnet. Example:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack network create dualstack
|
||||||
|
... output omitted ...
|
||||||
|
$ openstack subnet create --network dualstack dualstackv4 --subnet-range 192.0.2.0/24
|
||||||
|
... output omitted ...
|
||||||
|
$ openstack subnet create --network dualstack dualstackv6 --protocol ipv6 --subnet-range 2001:db8:42:42::/64 --dns-publish-fixed-ip
|
||||||
|
... output omitted ...
|
||||||
|
$ openstack zone create example.org. --email mail@example.org
|
||||||
|
... output omitted ...
|
||||||
|
$ openstack recordset list example.org.
|
||||||
|
+--------------------------------------+--------------+------+--------------------------------------------------------------------+--------+--------+
|
||||||
|
| id | name | type | records | status | action |
|
||||||
|
+--------------------------------------+--------------+------+--------------------------------------------------------------------+--------+--------+
|
||||||
|
| 404e9846-1482-433b-8bbc-67677e587d28 | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE |
|
||||||
|
| de73576a-f9c7-4892-934c-259b77ff02c0 | example.org. | SOA | ns1.devstack.org. mail.example.org. 1575897792 3559 600 86400 3600 | ACTIVE | NONE |
|
||||||
|
+--------------------------------------+--------------+------+--------------------------------------------------------------------+--------+--------+
|
||||||
|
$ openstack port create port1 --dns-domain example.org. --dns-name port1 --network dualstack
|
||||||
|
+-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| admin_state_up | UP |
|
||||||
|
| allowed_address_pairs | |
|
||||||
|
| binding_host_id | None |
|
||||||
|
| binding_profile | None |
|
||||||
|
| binding_vif_details | None |
|
||||||
|
| binding_vif_type | None |
|
||||||
|
| binding_vnic_type | normal |
|
||||||
|
| created_at | 2019-12-09T13:23:52Z |
|
||||||
|
| data_plane_status | None |
|
||||||
|
| description | |
|
||||||
|
| device_id | |
|
||||||
|
| device_owner | |
|
||||||
|
| dns_assignment | fqdn='port1.openstackgate.local.', hostname='port1', ip_address='192.0.2.100' |
|
||||||
|
| | fqdn='port1.openstackgate.local.', hostname='port1', ip_address='2001:db8:42:42::2a2' |
|
||||||
|
| dns_domain | example.org. |
|
||||||
|
| dns_name | port1 |
|
||||||
|
| extra_dhcp_opts | |
|
||||||
|
| fixed_ips | ip_address='192.0.2.100', subnet_id='47cc9a39-c88b-4082-a52c-1237c2a1d479' |
|
||||||
|
| | ip_address='2001:db8:42:42::2a2', subnet_id='f9c04195-1000-4575-a203-3c174772617f' |
|
||||||
|
| id | f8bc991b-1f84-435a-a5f8-814bd8b9ae9f |
|
||||||
|
| location | cloud='devstack', project.domain_id='default', project.domain_name=, project.id='86de4dab952d48f79e625b106f7a75f7', project.name='demo', region_name='RegionOne', zone= |
|
||||||
|
| mac_address | fa:16:3e:13:7a:56 |
|
||||||
|
| name | port1 |
|
||||||
|
| network_id | fa8118ed-b7c2-41b8-89bc-97e46f0491ac |
|
||||||
|
| port_security_enabled | True |
|
||||||
|
| project_id | 86de4dab952d48f79e625b106f7a75f7 |
|
||||||
|
| propagate_uplink_status | None |
|
||||||
|
| qos_policy_id | None |
|
||||||
|
| resource_request | None |
|
||||||
|
| revision_number | 1 |
|
||||||
|
| security_group_ids | f0b02df0-a0b9-4ce8-b067-8b61a8679e9d |
|
||||||
|
| status | DOWN |
|
||||||
|
| tags | |
|
||||||
|
| trunk_details | None |
|
||||||
|
| updated_at | 2019-12-09T13:23:53Z |
|
||||||
|
+-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
$ openstack recordset list example.org.
|
||||||
|
+--------------------------------------+--------------------+------+--------------------------------------------------------------------+--------+--------+
|
||||||
|
| id | name | type | records | status | action |
|
||||||
|
+--------------------------------------+--------------------+------+--------------------------------------------------------------------+--------+--------+
|
||||||
|
| 404e9846-1482-433b-8bbc-67677e587d28 | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE |
|
||||||
|
| de73576a-f9c7-4892-934c-259b77ff02c0 | example.org. | SOA | ns1.devstack.org. mail.example.org. 1575897833 3559 600 86400 3600 | ACTIVE | NONE |
|
||||||
|
| 85ce74a5-7dd6-42d3-932c-c9a029dea05e | port1.example.org. | AAAA | 2001:db8:42:42::2a2 | ACTIVE | NONE |
|
||||||
|
+--------------------------------------+--------------------+------+--------------------------------------------------------------------+--------+--------+
|
||||||
|
|
||||||
|
|
||||||
|
Use case 3b: The ``dns_domain_ports`` extension
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If the ``dns_domain for ports`` extension has been configured,
|
||||||
|
the user can create a port specifying a non-blank value in its
|
||||||
|
``dns_domain`` attribute. If the port is created in an externally
|
||||||
|
accessible network, DNS records will be published for this port:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack port create --network 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 --dns-name my-vm --dns-domain port-domain.org. test
|
||||||
|
+-------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+-------------------------+-------------------------------------------------------------------------------+
|
||||||
|
| admin_state_up | UP |
|
||||||
|
| allowed_address_pairs | |
|
||||||
|
| binding_host_id | None |
|
||||||
|
| binding_profile | None |
|
||||||
|
| binding_vif_details | None |
|
||||||
|
| binding_vif_type | None |
|
||||||
|
| binding_vnic_type | normal |
|
||||||
|
| created_at | 2019-06-12T15:43:29Z |
|
||||||
|
| data_plane_status | None |
|
||||||
|
| description | |
|
||||||
|
| device_id | |
|
||||||
|
| device_owner | |
|
||||||
|
| dns_assignment | fqdn='my-vm.example.org.', hostname='my-vm', ip_address='203.0.113.9' |
|
||||||
|
| | fqdn='my-vm.example.org.', hostname='my-vm', ip_address='2001:db8:10::9' |
|
||||||
|
| dns_domain | port-domain.org. |
|
||||||
|
| dns_name | my-vm |
|
||||||
|
| extra_dhcp_opts | |
|
||||||
|
| fixed_ips | ip_address='203.0.113.9', subnet_id='277eca5d-9869-474b-960e-6da5951d09f7' |
|
||||||
|
| | ip_address='2001:db8:10::9', subnet_id='eab47748-3f0a-4775-a09f-b0c24bb64bc4' |
|
||||||
|
| id | 57541c27-f8a9-41f1-8dde-eb10155496e6 |
|
||||||
|
| mac_address | fa:16:3e:55:d6:c7 |
|
||||||
|
| name | test |
|
||||||
|
| network_id | 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 |
|
||||||
|
| port_security_enabled | True |
|
||||||
|
| project_id | 07b21ad4-edb6-420b-bd76-9bb4aab0d135 |
|
||||||
|
| propagate_uplink_status | None |
|
||||||
|
| qos_policy_id | None |
|
||||||
|
| resource_request | None |
|
||||||
|
| revision_number | 1 |
|
||||||
|
| security_group_ids | 82227b10-d135-4bca-b41f-63c1f2286b3e |
|
||||||
|
| status | DOWN |
|
||||||
|
| tags | |
|
||||||
|
| trunk_details | None |
|
||||||
|
| updated_at | 2019-06-12T15:43:29Z |
|
||||||
|
+-------------------------+-------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
In this case, the port's ``dns_name`` (``my-vm``) will be published in the
|
||||||
|
``port-domain.org.`` zone, as shown here:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openstack recordset list port-domain.org.
|
||||||
|
+--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+
|
||||||
|
| id | name | type | records | status | action |
|
||||||
|
+--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+
|
||||||
|
| 03e5a35b-d984-4d10-942a-2de8ccb9b941 | port-domain.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1503272259 3549 600 86400 3600 | ACTIVE | NONE |
|
||||||
|
| d2dd1dfe-531d-4fea-8c0e-f5b559942ac5 | port-domain.org. | NS | ns1.devstack.org. | ACTIVE | NONE |
|
||||||
|
| 67a8e83d-7e3c-4fb1-9261-0481318bb7b5 | my-vm.port-domain.org. | A | 203.0.113.9 | ACTIVE | NONE |
|
||||||
|
| 5a4f671c-9969-47aa-82e1-e05754021852 | my-vm.port-domain.org. | AAAA | 2001:db8:10::9 | ACTIVE | NONE |
|
||||||
|
+--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If both the port and its network have a valid non-blank string assigned to
|
||||||
|
their ``dns_domain`` attributes, the port's ``dns_domain`` takes precedence
|
||||||
|
over the network's.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The name assigned to the port's ``dns_domain`` attribute must end with a
|
||||||
|
period (``.``).
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
In the above example, the ``port-domain.org.`` zone must be created before
|
||||||
|
Neutron can publish any port data to it.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
See :ref:`config-dns-int-ext-serv-net` for detailed instructions on how
|
||||||
|
to create the externally accessible network.
|
||||||
|
|
||||||
|
Use case 3c: The ``dns`` extension
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If the user wants to publish a port in the
|
||||||
external DNS service in a zone specified by the ``dns_domain`` attribute of the
|
external DNS service in a zone specified by the ``dns_domain`` attribute of the
|
||||||
network, these are the steps to be taken:
|
network, these are the steps to be taken:
|
||||||
|
|
||||||
@ -684,8 +873,8 @@ an instance. Notice that:
|
|||||||
the potential performance impact associated with this use case.
|
the potential performance impact associated with this use case.
|
||||||
|
|
||||||
Following are the PTR records created for this example. Note that for
|
Following are the PTR records created for this example. Note that for
|
||||||
IPv4, the value of ipv4_ptr_zone_prefix_size is 24. In the case of IPv6, the
|
IPv4, the value of ``ipv4_ptr_zone_prefix_size`` is 24. In the case of IPv6, the
|
||||||
value of ipv6_ptr_zone_prefix_size is 116.
|
value of ``ipv6_ptr_zone_prefix_size`` is 116.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
@ -710,80 +899,6 @@ value of ipv6_ptr_zone_prefix_size is 116.
|
|||||||
See :ref:`config-dns-int-ext-serv-net` for detailed instructions on how
|
See :ref:`config-dns-int-ext-serv-net` for detailed instructions on how
|
||||||
to create the externally accessible network.
|
to create the externally accessible network.
|
||||||
|
|
||||||
Alternatively, if the ``dns_domain for ports`` extension has been configured,
|
|
||||||
the user can create a port specifying a non-blank value in its
|
|
||||||
``dns_domain`` attribute, as shown here:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack port create --network 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 --dns-name my-vm --dns-domain port-domain.org. test
|
|
||||||
+-------------------------+-------------------------------------------------------------------------------+
|
|
||||||
| Field | Value |
|
|
||||||
+-------------------------+-------------------------------------------------------------------------------+
|
|
||||||
| admin_state_up | UP |
|
|
||||||
| allowed_address_pairs | |
|
|
||||||
| binding_host_id | None |
|
|
||||||
| binding_profile | None |
|
|
||||||
| binding_vif_details | None |
|
|
||||||
| binding_vif_type | None |
|
|
||||||
| binding_vnic_type | normal |
|
|
||||||
| created_at | 2019-06-12T15:43:29Z |
|
|
||||||
| data_plane_status | None |
|
|
||||||
| description | |
|
|
||||||
| device_id | |
|
|
||||||
| device_owner | |
|
|
||||||
| dns_assignment | fqdn='my-vm.example.org.', hostname='my-vm', ip_address='203.0.113.9' |
|
|
||||||
| | fqdn='my-vm.example.org.', hostname='my-vm', ip_address='2001:db8:10::9' |
|
|
||||||
| dns_domain | port-domain.org. |
|
|
||||||
| dns_name | my-vm |
|
|
||||||
| extra_dhcp_opts | |
|
|
||||||
| fixed_ips | ip_address='203.0.113.9', subnet_id='277eca5d-9869-474b-960e-6da5951d09f7' |
|
|
||||||
| | ip_address='2001:db8:10::9', subnet_id='eab47748-3f0a-4775-a09f-b0c24bb64bc4' |
|
|
||||||
| id | 57541c27-f8a9-41f1-8dde-eb10155496e6 |
|
|
||||||
| mac_address | fa:16:3e:55:d6:c7 |
|
|
||||||
| name | test |
|
|
||||||
| network_id | 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 |
|
|
||||||
| port_security_enabled | True |
|
|
||||||
| project_id | 07b21ad4-edb6-420b-bd76-9bb4aab0d135 |
|
|
||||||
| propagate_uplink_status | None |
|
|
||||||
| qos_policy_id | None |
|
|
||||||
| resource_request | None |
|
|
||||||
| revision_number | 1 |
|
|
||||||
| security_group_ids | 82227b10-d135-4bca-b41f-63c1f2286b3e |
|
|
||||||
| status | DOWN |
|
|
||||||
| tags | |
|
|
||||||
| trunk_details | None |
|
|
||||||
| updated_at | 2019-06-12T15:43:29Z |
|
|
||||||
+-------------------------+-------------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
In this case, the port's ``dns_name`` (``my-vm``) will be published in the
|
|
||||||
``port-domain.org.`` zone, as shown here:
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
|
|
||||||
$ openstack recordset list port-domain.org.
|
|
||||||
+--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+
|
|
||||||
| id | name | type | records | status | action |
|
|
||||||
+--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+
|
|
||||||
| 03e5a35b-d984-4d10-942a-2de8ccb9b941 | port-domain.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1503272259 3549 600 86400 3600 | ACTIVE | NONE |
|
|
||||||
| d2dd1dfe-531d-4fea-8c0e-f5b559942ac5 | port-domain.org. | NS | ns1.devstack.org. | ACTIVE | NONE |
|
|
||||||
| 67a8e83d-7e3c-4fb1-9261-0481318bb7b5 | my-vm.port-domain.org. | A | 203.0.113.9 | ACTIVE | NONE |
|
|
||||||
| 5a4f671c-9969-47aa-82e1-e05754021852 | my-vm.port-domain.org. | AAAA | 2001:db8:10::9 | ACTIVE | NONE |
|
|
||||||
+--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
If both the port and its network have a valid non-blank string assigned to
|
|
||||||
their ``dns_domain`` attributes, the port's ``dns_domain`` takes precedence
|
|
||||||
over the network's.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
The name assigned to the port's ``dns_domain`` attribute must end with a
|
|
||||||
period (``.``).
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
In the above example, the ``port-domain.org.`` zone must be created before
|
|
||||||
Neutron can publish any port data to it.
|
|
||||||
|
|
||||||
.. _config-dns-performance-considerations:
|
.. _config-dns-performance-considerations:
|
||||||
|
|
||||||
Performance considerations
|
Performance considerations
|
||||||
@ -798,10 +913,10 @@ use case.
|
|||||||
|
|
||||||
.. _config-dns-int-ext-serv-net:
|
.. _config-dns-int-ext-serv-net:
|
||||||
|
|
||||||
Configuration of the externally accessible network for use case 3
|
Configuration of the externally accessible network for use cases 3b and 3c
|
||||||
-----------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
In :ref:`config-dns-use-case-3`, the externally accessible network must
|
For use cases 3b and 3c, the externally accessible network must
|
||||||
meet the following requirements:
|
meet the following requirements:
|
||||||
|
|
||||||
* The network may not have attribute ``router:external`` set to ``True``.
|
* The network may not have attribute ``router:external`` set to ``True``.
|
||||||
@ -809,6 +924,6 @@ meet the following requirements:
|
|||||||
* For network types VLAN, GRE, VXLAN or GENEVE, the segmentation ID must be
|
* For network types VLAN, GRE, VXLAN or GENEVE, the segmentation ID must be
|
||||||
outside the ranges assigned to project networks.
|
outside the ranges assigned to project networks.
|
||||||
|
|
||||||
This usually implies that this use case only works for networks specifically
|
This usually implies that these use cases only work for networks specifically
|
||||||
created for this purpose by an admin, it does not work for networks
|
created for this purpose by an admin, they do not work for networks
|
||||||
which tenants can create.
|
which tenants can create on their own.
|
||||||
|
@ -5,13 +5,10 @@ DNS integration
|
|||||||
===============
|
===============
|
||||||
|
|
||||||
This page serves as a guide for how to use the DNS integration functionality of
|
This page serves as a guide for how to use the DNS integration functionality of
|
||||||
the Networking service. The functionality described covers DNS from two points
|
the Networking service and its interaction with the Compute service.
|
||||||
of view:
|
|
||||||
|
|
||||||
* The internal DNS functionality offered by the Networking service and its
|
The integration of the Networking service with an external DNSaaS
|
||||||
interaction with the Compute service.
|
(DNS-as-a-Service) is described in :ref:`config-dns-int-ext-serv`.
|
||||||
* Integration of the Compute service and the Networking service with an
|
|
||||||
external DNSaaS (DNS-as-a-Service).
|
|
||||||
|
|
||||||
Users can control the behavior of the Networking service in regards to DNS
|
Users can control the behavior of the Networking service in regards to DNS
|
||||||
using two attributes associated with ports, networks, and floating IPs. The
|
using two attributes associated with ports, networks, and floating IPs. The
|
||||||
@ -71,7 +68,7 @@ the internal DNS. To enable this functionality, do the following:
|
|||||||
``[ml2]`` section of ``/etc/neutron/plugins/ml2/ml2_conf.ini``. The
|
``[ml2]`` section of ``/etc/neutron/plugins/ml2/ml2_conf.ini``. The
|
||||||
following is an example:
|
following is an example:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: ini
|
||||||
|
|
||||||
[ml2]
|
[ml2]
|
||||||
extension_drivers = port_security,dns_domain_ports
|
extension_drivers = port_security,dns_domain_ports
|
||||||
|
@ -1 +1 @@
|
|||||||
a010322604bc
|
263d454a9655
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
# Copyright 2019 x-ion GmbH
|
||||||
|
#
|
||||||
|
# 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 alembic import op
|
||||||
|
from neutron_lib.db import constants as db_const
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
"""Add table and relations for subnet dns_publish_fixed_ip attribute
|
||||||
|
|
||||||
|
Revision ID: 263d454a9655
|
||||||
|
Revises: a010322604bc
|
||||||
|
Create Date: 2019-05-24 10:00:00.000000
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '263d454a9655'
|
||||||
|
down_revision = 'a010322604bc'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table('subnet_dns_publish_fixed_ips',
|
||||||
|
sa.Column('subnet_id',
|
||||||
|
sa.String(length=db_const.UUID_FIELD_SIZE),
|
||||||
|
nullable=False,
|
||||||
|
index=True),
|
||||||
|
sa.Column('dns_publish_fixed_ip',
|
||||||
|
sa.Boolean(),
|
||||||
|
nullable=False,
|
||||||
|
server_default=sa.sql.false()),
|
||||||
|
sa.ForeignKeyConstraint(['subnet_id'],
|
||||||
|
['subnets.id'],
|
||||||
|
ondelete='CASCADE'),
|
||||||
|
sa.PrimaryKeyConstraint('subnet_id'))
|
@ -14,6 +14,7 @@ from neutron_lib.db import constants
|
|||||||
from neutron_lib.db import model_base
|
from neutron_lib.db import model_base
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
from sqlalchemy import sql
|
||||||
|
|
||||||
from neutron.db.models import l3 as l3_models
|
from neutron.db.models import l3 as l3_models
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
@ -97,3 +98,25 @@ class PortDNS(model_base.BASEV2):
|
|||||||
uselist=False,
|
uselist=False,
|
||||||
cascade='delete'))
|
cascade='delete'))
|
||||||
revises_on_change = ('port', )
|
revises_on_change = ('port', )
|
||||||
|
|
||||||
|
|
||||||
|
class SubnetDNSPublishFixedIP(model_base.BASEV2):
|
||||||
|
__tablename__ = "subnet_dns_publish_fixed_ips"
|
||||||
|
|
||||||
|
subnet_id = sa.Column(sa.String(constants.UUID_FIELD_SIZE),
|
||||||
|
sa.ForeignKey('subnets.id', ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
index=True)
|
||||||
|
dns_publish_fixed_ip = sa.Column(sa.Boolean(),
|
||||||
|
nullable=False,
|
||||||
|
server_default=sql.false())
|
||||||
|
|
||||||
|
# Add a relationship to the Subnet model in order to instruct
|
||||||
|
# SQLAlchemy to eagerly load this association
|
||||||
|
subnet = orm.relationship(models_v2.Subnet,
|
||||||
|
load_on_pending=True,
|
||||||
|
backref=orm.backref("dns_publish_fixed_ip",
|
||||||
|
lazy='joined',
|
||||||
|
uselist=False,
|
||||||
|
cascade='delete'))
|
||||||
|
revises_on_change = ('subnet', )
|
||||||
|
20
neutron/extensions/subnet_dns_publish_fixed_ip.py
Normal file
20
neutron/extensions/subnet_dns_publish_fixed_ip.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 subnet_dns_publish_fixed_ip as apidef
|
||||||
|
from neutron_lib.api import extensions
|
||||||
|
|
||||||
|
|
||||||
|
class Subnet_dns_publish_fixed_ip(extensions.APIExtensionDescriptor):
|
||||||
|
"""Extension class supporting dns_publish_fixed_ip attribute for subnet."""
|
||||||
|
|
||||||
|
api_definition = apidef
|
@ -17,9 +17,11 @@ from neutron_lib.db import model_query
|
|||||||
from neutron_lib.objects import common_types
|
from neutron_lib.objects import common_types
|
||||||
from neutron_lib.utils import net as net_utils
|
from neutron_lib.utils import net as net_utils
|
||||||
|
|
||||||
|
from oslo_utils import versionutils
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
from sqlalchemy import and_, or_
|
from sqlalchemy import and_, or_
|
||||||
|
|
||||||
|
from neutron.db.models import dns as dns_models
|
||||||
from neutron.db.models import segment as segment_model
|
from neutron.db.models import segment as segment_model
|
||||||
from neutron.db.models import subnet_service_type
|
from neutron.db.models import subnet_service_type
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
@ -190,7 +192,8 @@ class SubnetServiceType(base.NeutronDbObject):
|
|||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class Subnet(base.NeutronDbObject):
|
class Subnet(base.NeutronDbObject):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
# Version 1.1: Add dns_publish_fixed_ip field
|
||||||
|
VERSION = '1.1'
|
||||||
|
|
||||||
db_model = models_v2.Subnet
|
db_model = models_v2.Subnet
|
||||||
new_facade = True
|
new_facade = True
|
||||||
@ -213,13 +216,15 @@ class Subnet(base.NeutronDbObject):
|
|||||||
'shared': obj_fields.BooleanField(nullable=True),
|
'shared': obj_fields.BooleanField(nullable=True),
|
||||||
'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer',
|
'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer',
|
||||||
nullable=True),
|
nullable=True),
|
||||||
|
'dns_publish_fixed_ip': obj_fields.BooleanField(nullable=True),
|
||||||
'host_routes': obj_fields.ListOfObjectsField('Route', nullable=True),
|
'host_routes': obj_fields.ListOfObjectsField('Route', nullable=True),
|
||||||
'ipv6_ra_mode': common_types.IPV6ModeEnumField(nullable=True),
|
'ipv6_ra_mode': common_types.IPV6ModeEnumField(nullable=True),
|
||||||
'ipv6_address_mode': common_types.IPV6ModeEnumField(nullable=True),
|
'ipv6_address_mode': common_types.IPV6ModeEnumField(nullable=True),
|
||||||
'service_types': obj_fields.ListOfStringsField(nullable=True)
|
'service_types': obj_fields.ListOfStringsField(nullable=True)
|
||||||
}
|
}
|
||||||
|
|
||||||
synthetic_fields = ['allocation_pools', 'dns_nameservers', 'host_routes',
|
synthetic_fields = ['allocation_pools', 'dns_nameservers',
|
||||||
|
'dns_publish_fixed_ip', 'host_routes',
|
||||||
'service_types', 'shared']
|
'service_types', 'shared']
|
||||||
|
|
||||||
foreign_keys = {'Network': {'network_id': 'id'}}
|
foreign_keys = {'Network': {'network_id': 'id'}}
|
||||||
@ -235,12 +240,29 @@ class Subnet(base.NeutronDbObject):
|
|||||||
self.add_extra_filter_name('shared')
|
self.add_extra_filter_name('shared')
|
||||||
|
|
||||||
def obj_load_attr(self, attrname):
|
def obj_load_attr(self, attrname):
|
||||||
|
if attrname == 'dns_publish_fixed_ip':
|
||||||
|
return self._load_dns_publish_fixed_ip()
|
||||||
if attrname == 'shared':
|
if attrname == 'shared':
|
||||||
return self._load_shared()
|
return self._load_shared()
|
||||||
if attrname == 'service_types':
|
if attrname == 'service_types':
|
||||||
return self._load_service_types()
|
return self._load_service_types()
|
||||||
super(Subnet, self).obj_load_attr(attrname)
|
super(Subnet, self).obj_load_attr(attrname)
|
||||||
|
|
||||||
|
def _load_dns_publish_fixed_ip(self, db_obj=None):
|
||||||
|
if db_obj:
|
||||||
|
object_data = db_obj.get('dns_publish_fixed_ip', None)
|
||||||
|
else:
|
||||||
|
object_data = SubnetDNSPublishFixedIP.get_objects(
|
||||||
|
self.obj_context,
|
||||||
|
subnet_id=self.id)
|
||||||
|
|
||||||
|
dns_publish_fixed_ip = False
|
||||||
|
if object_data:
|
||||||
|
dns_publish_fixed_ip = object_data.get(
|
||||||
|
'dns_publish_fixed_ip')
|
||||||
|
setattr(self, 'dns_publish_fixed_ip', dns_publish_fixed_ip)
|
||||||
|
self.obj_reset_changes(['dns_publish_fixed_ip'])
|
||||||
|
|
||||||
def _load_shared(self, db_obj=None):
|
def _load_shared(self, db_obj=None):
|
||||||
if db_obj:
|
if db_obj:
|
||||||
# NOTE(korzen) db_obj is passed when Subnet object is loaded
|
# NOTE(korzen) db_obj is passed when Subnet object is loaded
|
||||||
@ -273,6 +295,7 @@ class Subnet(base.NeutronDbObject):
|
|||||||
|
|
||||||
def from_db_object(self, db_obj):
|
def from_db_object(self, db_obj):
|
||||||
super(Subnet, self).from_db_object(db_obj)
|
super(Subnet, self).from_db_object(db_obj)
|
||||||
|
self._load_dns_publish_fixed_ip(db_obj)
|
||||||
self._load_shared(db_obj)
|
self._load_shared(db_obj)
|
||||||
self._load_service_types(db_obj)
|
self._load_service_types(db_obj)
|
||||||
|
|
||||||
@ -412,6 +435,11 @@ class Subnet(base.NeutronDbObject):
|
|||||||
raise ipam_exceptions.DeferIpam()
|
raise ipam_exceptions.DeferIpam()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def obj_make_compatible(self, primitive, target_version):
|
||||||
|
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||||
|
if _target_version < (1, 1): # version 1.1 adds "dns_publish_fixed_ip"
|
||||||
|
primitive.pop('dns_publish_fixed_ip', None)
|
||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class NetworkSubnetLock(base.NeutronDbObject):
|
class NetworkSubnetLock(base.NeutronDbObject):
|
||||||
@ -438,3 +466,18 @@ class NetworkSubnetLock(base.NeutronDbObject):
|
|||||||
subnet_lock = NetworkSubnetLock(context, network_id=network_id,
|
subnet_lock = NetworkSubnetLock(context, network_id=network_id,
|
||||||
subnet_id=subnet_id)
|
subnet_id=subnet_id)
|
||||||
subnet_lock.create()
|
subnet_lock.create()
|
||||||
|
|
||||||
|
|
||||||
|
@base.NeutronObjectRegistry.register
|
||||||
|
class SubnetDNSPublishFixedIP(base.NeutronDbObject):
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
db_model = dns_models.SubnetDNSPublishFixedIP
|
||||||
|
|
||||||
|
primary_keys = ['subnet_id']
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'subnet_id': common_types.UUIDField(),
|
||||||
|
'dns_publish_fixed_ip': obj_fields.BooleanField()
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ from oslo_log import log as logging
|
|||||||
from neutron.db import segments_db
|
from neutron.db import segments_db
|
||||||
from neutron.objects import network as net_obj
|
from neutron.objects import network as net_obj
|
||||||
from neutron.objects import ports as port_obj
|
from neutron.objects import ports as port_obj
|
||||||
|
from neutron.objects import subnet as subnet_obj
|
||||||
from neutron.services.externaldns import driver
|
from neutron.services.externaldns import driver
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -88,18 +89,19 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
request_data)
|
request_data)
|
||||||
if is_dns_domain_default:
|
if is_dns_domain_default:
|
||||||
return
|
return
|
||||||
network = self._get_network(plugin_context, db_data['network_id'])
|
network, subnets = self._get_details(plugin_context,
|
||||||
|
db_data['network_id'])
|
||||||
self._create_port_dns_record(plugin_context, request_data, db_data,
|
self._create_port_dns_record(plugin_context, request_data, db_data,
|
||||||
network, dns_name)
|
network, subnets, dns_name)
|
||||||
|
|
||||||
def _create_port_dns_record(self, plugin_context, request_data, db_data,
|
def _create_port_dns_record(self, plugin_context, request_data, db_data,
|
||||||
network, dns_name):
|
network, subnets, dns_name):
|
||||||
external_dns_domain = (request_data.get(dns_apidef.DNSDOMAIN) or
|
external_dns_domain = (request_data.get(dns_apidef.DNSDOMAIN) or
|
||||||
network.get(dns_apidef.DNSDOMAIN))
|
network.get(dns_apidef.DNSDOMAIN))
|
||||||
|
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,
|
dns_name, external_dns_domain, flag))
|
||||||
self.external_dns_not_needed(plugin_context, network)))
|
|
||||||
|
|
||||||
dns_data_obj = port_obj.PortDNS(
|
dns_data_obj = port_obj.PortDNS(
|
||||||
plugin_context,
|
plugin_context,
|
||||||
@ -131,7 +133,8 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
return '', ''
|
return '', ''
|
||||||
return dns_name, external_dns_domain
|
return dns_name, 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):
|
||||||
dns_name = request_data.get(dns_apidef.DNSNAME)
|
dns_name = request_data.get(dns_apidef.DNSNAME)
|
||||||
dns_domain = request_data.get(dns_apidef.DNSDOMAIN)
|
dns_domain = request_data.get(dns_apidef.DNSDOMAIN)
|
||||||
has_fixed_ips = 'fixed_ips' in request_data
|
has_fixed_ips = 'fixed_ips' in request_data
|
||||||
@ -162,7 +165,8 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
return dns_data_db
|
return dns_data_db
|
||||||
if dns_name or dns_domain:
|
if dns_name or dns_domain:
|
||||||
dns_data_db = self._create_port_dns_record(
|
dns_data_db = self._create_port_dns_record(
|
||||||
plugin_context, request_data, db_data, network, dns_name or '')
|
plugin_context, request_data, db_data, network, subnets,
|
||||||
|
dns_name or '')
|
||||||
return dns_data_db
|
return dns_data_db
|
||||||
|
|
||||||
def _populate_previous_external_dns_data(self, dns_data_db):
|
def _populate_previous_external_dns_data(self, dns_data_db):
|
||||||
@ -206,9 +210,10 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
self._extend_port_dict(plugin_context.session, db_data,
|
self._extend_port_dict(plugin_context.session, db_data,
|
||||||
db_data, None)
|
db_data, None)
|
||||||
return
|
return
|
||||||
network = self._get_network(plugin_context, db_data['network_id'])
|
network, subnets = self._get_details(plugin_context,
|
||||||
|
db_data['network_id'])
|
||||||
dns_data_db = None
|
dns_data_db = None
|
||||||
if self.external_dns_not_needed(plugin_context, network):
|
if self.external_dns_not_needed(plugin_context, network, subnets):
|
||||||
# No need to update external DNS service. Only process the port's
|
# No need to update external DNS service. Only process the port's
|
||||||
# dns_name or dns_domain attributes if necessary
|
# dns_name or dns_domain attributes if necessary
|
||||||
if has_dns_name or has_dns_domain:
|
if has_dns_name or has_dns_domain:
|
||||||
@ -216,7 +221,7 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
plugin_context, request_data, db_data)
|
plugin_context, request_data, db_data)
|
||||||
else:
|
else:
|
||||||
dns_data_db = self._update_dns_db(plugin_context, request_data,
|
dns_data_db = self._update_dns_db(plugin_context, request_data,
|
||||||
db_data, network)
|
db_data, network, subnets)
|
||||||
self._extend_port_dict(plugin_context.session, db_data, db_data,
|
self._extend_port_dict(plugin_context.session, db_data, db_data,
|
||||||
dns_data_db)
|
dns_data_db)
|
||||||
|
|
||||||
@ -247,14 +252,15 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
dns_data_db.create()
|
dns_data_db.create()
|
||||||
return dns_data_db
|
return dns_data_db
|
||||||
|
|
||||||
def external_dns_not_needed(self, context, network):
|
def external_dns_not_needed(self, context, network, subnets):
|
||||||
"""Decide if ports in network need to be sent to the DNS service.
|
"""Decide if ports in network need to be sent to the DNS service.
|
||||||
|
|
||||||
:param context: plugin request context
|
:param context: plugin request context
|
||||||
:param network: network dictionary
|
:param network: network dictionary
|
||||||
|
:param subnets: list of subnets in network
|
||||||
:return: True or False
|
:return: True or False
|
||||||
"""
|
"""
|
||||||
pass
|
return False
|
||||||
|
|
||||||
def extend_network_dict(self, session, db_data, response_data):
|
def extend_network_dict(self, session, db_data, response_data):
|
||||||
response_data[dns_apidef.DNSDOMAIN] = ''
|
response_data[dns_apidef.DNSDOMAIN] = ''
|
||||||
@ -324,9 +330,11 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
|||||||
return self._extend_port_dict(session, db_data, response_data,
|
return self._extend_port_dict(session, db_data, response_data,
|
||||||
dns_data_db)
|
dns_data_db)
|
||||||
|
|
||||||
def _get_network(self, context, network_id):
|
def _get_details(self, context, network_id):
|
||||||
plugin = directory.get_plugin()
|
plugin = directory.get_plugin()
|
||||||
return plugin.get_network(context, network_id)
|
network = plugin.get_network(context, network_id)
|
||||||
|
subnets = plugin.get_subnets_by_network(context, network_id)
|
||||||
|
return network, subnets
|
||||||
|
|
||||||
|
|
||||||
class DNSExtensionDriverML2(DNSExtensionDriver):
|
class DNSExtensionDriverML2(DNSExtensionDriver):
|
||||||
@ -361,10 +369,13 @@ class DNSExtensionDriverML2(DNSExtensionDriver):
|
|||||||
if vlan_range[0] <= segmentation_id <= vlan_range[1]:
|
if vlan_range[0] <= segmentation_id <= vlan_range[1]:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def external_dns_not_needed(self, context, network):
|
def external_dns_not_needed(self, context, network, subnets):
|
||||||
dns_driver = _get_dns_driver()
|
dns_driver = _get_dns_driver()
|
||||||
if not dns_driver:
|
if not dns_driver:
|
||||||
return True
|
return True
|
||||||
|
for subnet in subnets:
|
||||||
|
if subnet.get('dns_publish_fixed_ip'):
|
||||||
|
return False
|
||||||
if network['router:external']:
|
if network['router:external']:
|
||||||
return True
|
return True
|
||||||
segments = segments_db.get_network_segments(context, network['id'])
|
segments = segments_db.get_network_segments(context, network['id'])
|
||||||
@ -424,6 +435,24 @@ def _get_dns_driver():
|
|||||||
driver=cfg.CONF.external_dns_driver)
|
driver=cfg.CONF.external_dns_driver)
|
||||||
|
|
||||||
|
|
||||||
|
def _filter_by_subnet(context, fixed_ips):
|
||||||
|
subnet_filtered = []
|
||||||
|
filter_fixed_ips = False
|
||||||
|
for ip in fixed_ips:
|
||||||
|
# TODO(slaweq): This might be a performance issue if ports have lots
|
||||||
|
# of fixed_ips attached, possibly collect subnets first and do a
|
||||||
|
# single get_objects call instead
|
||||||
|
subnet = subnet_obj.Subnet.get_object(
|
||||||
|
context, id=ip['subnet_id'])
|
||||||
|
if subnet.get('dns_publish_fixed_ip'):
|
||||||
|
filter_fixed_ips = True
|
||||||
|
subnet_filtered.append(str(ip['ip_address']))
|
||||||
|
if filter_fixed_ips:
|
||||||
|
return subnet_filtered
|
||||||
|
else:
|
||||||
|
return [str(ip['ip_address']) for ip in fixed_ips]
|
||||||
|
|
||||||
|
|
||||||
def _create_port_in_external_dns_service(resource, event, trigger, **kwargs):
|
def _create_port_in_external_dns_service(resource, event, trigger, **kwargs):
|
||||||
dns_driver = _get_dns_driver()
|
dns_driver = _get_dns_driver()
|
||||||
if not dns_driver:
|
if not dns_driver:
|
||||||
@ -434,7 +463,7 @@ def _create_port_in_external_dns_service(resource, event, trigger, **kwargs):
|
|||||||
context, port_id=port['id'])
|
context, port_id=port['id'])
|
||||||
if not (dns_data_db and dns_data_db['current_dns_name']):
|
if not (dns_data_db and dns_data_db['current_dns_name']):
|
||||||
return
|
return
|
||||||
records = [ip['ip_address'] for ip in port['fixed_ips']]
|
records = _filter_by_subnet(context, port['fixed_ips'])
|
||||||
_send_data_to_external_dns_service(context, dns_driver,
|
_send_data_to_external_dns_service(context, dns_driver,
|
||||||
dns_data_db['current_dns_domain'],
|
dns_data_db['current_dns_domain'],
|
||||||
dns_data_db['current_dns_name'],
|
dns_data_db['current_dns_name'],
|
||||||
@ -478,8 +507,8 @@ def _update_port_in_external_dns_service(resource, event, trigger, **kwargs):
|
|||||||
original_port = kwargs.get('original_port')
|
original_port = kwargs.get('original_port')
|
||||||
if not original_port:
|
if not original_port:
|
||||||
return
|
return
|
||||||
original_ips = [ip['ip_address'] for ip in original_port['fixed_ips']]
|
original_ips = _filter_by_subnet(context, original_port['fixed_ips'])
|
||||||
updated_ips = [ip['ip_address'] for ip in updated_port['fixed_ips']]
|
updated_ips = _filter_by_subnet(context, updated_port['fixed_ips'])
|
||||||
is_dns_name_changed = (updated_port[dns_apidef.DNSNAME] !=
|
is_dns_name_changed = (updated_port[dns_apidef.DNSNAME] !=
|
||||||
original_port[dns_apidef.DNSNAME])
|
original_port[dns_apidef.DNSNAME])
|
||||||
is_dns_domain_changed = (dns_apidef.DNSDOMAIN in updated_port and
|
is_dns_domain_changed = (dns_apidef.DNSDOMAIN in updated_port and
|
||||||
@ -518,7 +547,7 @@ def _delete_port_in_external_dns_service(resource, event,
|
|||||||
if dns_data_db['current_dns_name']:
|
if dns_data_db['current_dns_name']:
|
||||||
ip_allocations = port_obj.IPAllocation.get_objects(context,
|
ip_allocations = port_obj.IPAllocation.get_objects(context,
|
||||||
port_id=port_id)
|
port_id=port_id)
|
||||||
records = [str(alloc['ip_address']) for alloc in ip_allocations]
|
records = _filter_by_subnet(context, ip_allocations)
|
||||||
_remove_data_from_external_dns_service(
|
_remove_data_from_external_dns_service(
|
||||||
context, dns_driver, dns_data_db['current_dns_domain'],
|
context, dns_driver, dns_data_db['current_dns_domain'],
|
||||||
dns_data_db['current_dns_name'], records)
|
dns_data_db['current_dns_name'], records)
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
# 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_ports as ports_apidef
|
||||||
|
from neutron_lib.api.definitions import subnet_dns_publish_fixed_ip as sn_dns
|
||||||
|
from neutron_lib.api import validators
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from neutron.objects import subnet as subnet_obj
|
||||||
|
from neutron.plugins.ml2.extensions import dns_integration as dns_int
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SubnetDNSPublishFixedIPExtensionDriver(
|
||||||
|
dns_int.DNSDomainPortsExtensionDriver):
|
||||||
|
|
||||||
|
_supported_extension_aliases = [dns_apidef.ALIAS,
|
||||||
|
ports_apidef.ALIAS,
|
||||||
|
sn_dns.ALIAS]
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
LOG.info("SubnetDNSPublishFixedIPExtensionDriver initialization "
|
||||||
|
"complete")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extension_aliases(self):
|
||||||
|
return self._supported_extension_aliases
|
||||||
|
|
||||||
|
def extend_subnet_dict(self, session, db_data, response_data):
|
||||||
|
# TODO(jh): This returns None instead of the proper response_data
|
||||||
|
# response_data = (
|
||||||
|
# super(SubnetDNSPublishFixedIPExtensionDriver,
|
||||||
|
# self).extend_subnet_dict(
|
||||||
|
# session, db_data, response_data))
|
||||||
|
response_data['dns_publish_fixed_ip'] = False
|
||||||
|
if db_data.dns_publish_fixed_ip:
|
||||||
|
response_data['dns_publish_fixed_ip'] = True
|
||||||
|
return response_data
|
||||||
|
|
||||||
|
def process_create_subnet(self, plugin_context, request_data, db_data):
|
||||||
|
flag = request_data.get(sn_dns.DNS_PUBLISH_FIXED_IP)
|
||||||
|
if not validators.is_attr_set(flag):
|
||||||
|
return
|
||||||
|
|
||||||
|
if flag:
|
||||||
|
subnet_obj.SubnetDNSPublishFixedIP(
|
||||||
|
plugin_context,
|
||||||
|
subnet_id=db_data['id'],
|
||||||
|
dns_publish_fixed_ip=flag).create()
|
||||||
|
db_data[sn_dns.DNS_PUBLISH_FIXED_IP] = flag
|
||||||
|
|
||||||
|
def process_update_subnet(self, plugin_context, request_data, db_data):
|
||||||
|
new_value = request_data.get(sn_dns.DNS_PUBLISH_FIXED_IP)
|
||||||
|
if not validators.is_attr_set(new_value):
|
||||||
|
return
|
||||||
|
|
||||||
|
current_value = db_data.get(sn_dns.DNS_PUBLISH_FIXED_IP)
|
||||||
|
if current_value == new_value:
|
||||||
|
return
|
||||||
|
|
||||||
|
subnet_id = db_data['id']
|
||||||
|
if new_value:
|
||||||
|
subnet_obj.SubnetDNSPublishFixedIP(
|
||||||
|
plugin_context,
|
||||||
|
subnet_id=subnet_id,
|
||||||
|
dns_publish_fixed_ip=new_value).create()
|
||||||
|
else:
|
||||||
|
sn_obj = subnet_obj.SubnetDNSPublishFixedIP.get_object(
|
||||||
|
plugin_context,
|
||||||
|
subnet_id=subnet_id)
|
||||||
|
sn_obj.delete()
|
||||||
|
db_data[sn_dns.DNS_PUBLISH_FIXED_IP] = new_value
|
@ -62,6 +62,7 @@ NETWORK_API_EXTENSIONS+=",standard-attr-segment"
|
|||||||
NETWORK_API_EXTENSIONS+=",standard-attr-timestamp"
|
NETWORK_API_EXTENSIONS+=",standard-attr-timestamp"
|
||||||
NETWORK_API_EXTENSIONS+=",standard-attr-tag"
|
NETWORK_API_EXTENSIONS+=",standard-attr-tag"
|
||||||
NETWORK_API_EXTENSIONS+=",subnet_allocation"
|
NETWORK_API_EXTENSIONS+=",subnet_allocation"
|
||||||
|
NETWORK_API_EXTENSIONS+=",subnet-dns-publish-fixed-ip"
|
||||||
NETWORK_API_EXTENSIONS+=",trunk"
|
NETWORK_API_EXTENSIONS+=",trunk"
|
||||||
NETWORK_API_EXTENSIONS+=",trunk-details"
|
NETWORK_API_EXTENSIONS+=",trunk-details"
|
||||||
NETWORK_API_EXTENSIONS+=",uplink-status-propagation"
|
NETWORK_API_EXTENSIONS+=",uplink-status-propagation"
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
# 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 l3 as l3_apidef
|
||||||
|
from neutron_lib.api.definitions import subnet_dns_publish_fixed_ip as api_def
|
||||||
|
from neutron_lib import constants
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from neutron.db import db_base_plugin_v2
|
||||||
|
from neutron.extensions import subnet_dns_publish_fixed_ip
|
||||||
|
from neutron.tests.unit.plugins.ml2 import test_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class SubnetDNSPublishFixedIPExtensionManager(object):
|
||||||
|
|
||||||
|
def get_resources(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_actions(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_request_extensions(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_extended_resources(self, version):
|
||||||
|
extension = subnet_dns_publish_fixed_ip.Subnet_dns_publish_fixed_ip()
|
||||||
|
return extension.get_extended_resources(version)
|
||||||
|
|
||||||
|
|
||||||
|
class SubnetDNSPublishFixedIPExtensionTestPlugin(
|
||||||
|
db_base_plugin_v2.NeutronDbPluginV2):
|
||||||
|
"""Test plugin to mixin the subnet_dns_publish_fixed_ip extension.
|
||||||
|
"""
|
||||||
|
|
||||||
|
supported_extension_aliases = [api_def.ALIAS,
|
||||||
|
dns_apidef.ALIAS,
|
||||||
|
l3_apidef.ALIAS]
|
||||||
|
|
||||||
|
|
||||||
|
class SubnetDNSPublishFixedIPExtensionTestCase(
|
||||||
|
test_plugin.Ml2PluginV2TestCase):
|
||||||
|
"""Test API extension subnet_dns_publish_fixed_ip attributes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_extension_drivers = ['subnet_dns_publish_fixed_ip']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
cfg.CONF.set_override('extension_drivers',
|
||||||
|
self._extension_drivers,
|
||||||
|
group='ml2')
|
||||||
|
super(SubnetDNSPublishFixedIPExtensionTestCase,
|
||||||
|
self).setUp()
|
||||||
|
|
||||||
|
def _create_subnet(
|
||||||
|
self, network, ip_version=constants.IP_VERSION_4, cidr=None,
|
||||||
|
**kwargs):
|
||||||
|
|
||||||
|
cidr = cidr or '192.0.2.0/24'
|
||||||
|
network_id = network['network']['id']
|
||||||
|
tenant_id = network['network']['tenant_id']
|
||||||
|
data = {'subnet': {
|
||||||
|
'network_id': network_id,
|
||||||
|
'ip_version': str(ip_version),
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'cidr': cidr}}
|
||||||
|
data['subnet'].update(kwargs)
|
||||||
|
subnet_req = self.new_create_request('subnets', data)
|
||||||
|
res = subnet_req.get_response(self.api)
|
||||||
|
|
||||||
|
return self.deserialize(self.fmt, res)['subnet']
|
||||||
|
|
||||||
|
def test_create_subnet_default(self):
|
||||||
|
with self.network() as network:
|
||||||
|
subnet = self._create_subnet(network)
|
||||||
|
self.assertIn('dns_publish_fixed_ip', subnet)
|
||||||
|
self.assertFalse(subnet['dns_publish_fixed_ip'])
|
||||||
|
data = {'subnet': {'dns_publish_fixed_ip': 'true'}}
|
||||||
|
req = self.new_update_request('subnets', data,
|
||||||
|
subnet['id'])
|
||||||
|
res = self.deserialize(self.fmt,
|
||||||
|
req.get_response(self.api))
|
||||||
|
self.assertTrue(res['subnet']['dns_publish_fixed_ip'])
|
||||||
|
|
||||||
|
data = {'subnet': {'dns_publish_fixed_ip': 'false'}}
|
||||||
|
req = self.new_update_request('subnets', data,
|
||||||
|
subnet['id'])
|
||||||
|
res = self.deserialize(self.fmt,
|
||||||
|
req.get_response(self.api))
|
||||||
|
self.assertFalse(res['subnet']['dns_publish_fixed_ip'])
|
||||||
|
|
||||||
|
def test_create_subnet_with_arg(self):
|
||||||
|
with self.network() as network:
|
||||||
|
subnet = self._create_subnet(network, dns_publish_fixed_ip=True)
|
||||||
|
self.assertIn('dns_publish_fixed_ip', subnet)
|
||||||
|
self.assertTrue(subnet['dns_publish_fixed_ip'])
|
@ -104,7 +104,8 @@ object_data = {
|
|||||||
'SegmentHostMapping': '1.0-521597cf82ead26217c3bd10738f00f0',
|
'SegmentHostMapping': '1.0-521597cf82ead26217c3bd10738f00f0',
|
||||||
'ServiceProfile': '1.0-9beafc9e7d081b8258f3c5cb66ac5eed',
|
'ServiceProfile': '1.0-9beafc9e7d081b8258f3c5cb66ac5eed',
|
||||||
'StandardAttribute': '1.0-617d4f46524c4ce734a6fc1cc0ac6a0b',
|
'StandardAttribute': '1.0-617d4f46524c4ce734a6fc1cc0ac6a0b',
|
||||||
'Subnet': '1.0-927155c1fdd5a615cbcb981dda97bce4',
|
'Subnet': '1.1-5b7e1789a1732259d1e28b4bd87eb1c2',
|
||||||
|
'SubnetDNSPublishFixedIP': '1.0-db22af6fa20b143986f0cbe06cbfe0ea',
|
||||||
'SubnetPool': '1.0-a0e03895d1a6e7b9d4ab7b0ca13c3867',
|
'SubnetPool': '1.0-a0e03895d1a6e7b9d4ab7b0ca13c3867',
|
||||||
'SubnetPoolPrefix': '1.0-13c15144135eb869faa4a76dc3ee3b6c',
|
'SubnetPoolPrefix': '1.0-13c15144135eb869faa4a76dc3ee3b6c',
|
||||||
'SubnetServiceType': '1.0-05ae4cdb2a9026a697b143926a1add8c',
|
'SubnetServiceType': '1.0-05ae4cdb2a9026a697b143926a1add8c',
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The ``subnet-dns-publish-fixed-ip`` extension adds a new attribute to
|
||||||
|
the definition of the subnet resource.
|
||||||
|
When set to ``true`` it will allow publishing DNS records for
|
||||||
|
fixed IPs from that subnet independent of the restrictions described
|
||||||
|
in the `DNS integration with an external service
|
||||||
|
<https://docs.openstack.org/neutron/latest/admin/config-dns-int-ext-serv.html#configuration-of-the-externally-accessible-network-for-use-case-3>`_
|
||||||
|
documentation.
|
@ -108,6 +108,7 @@ neutron.ml2.extension_drivers =
|
|||||||
data_plane_status = neutron.plugins.ml2.extensions.data_plane_status:DataPlaneStatusExtensionDriver
|
data_plane_status = neutron.plugins.ml2.extensions.data_plane_status:DataPlaneStatusExtensionDriver
|
||||||
dns_domain_ports = neutron.plugins.ml2.extensions.dns_integration:DNSDomainPortsExtensionDriver
|
dns_domain_ports = neutron.plugins.ml2.extensions.dns_integration:DNSDomainPortsExtensionDriver
|
||||||
uplink_status_propagation = neutron.plugins.ml2.extensions.uplink_status_propagation:UplinkStatusPropagationExtensionDriver
|
uplink_status_propagation = neutron.plugins.ml2.extensions.uplink_status_propagation:UplinkStatusPropagationExtensionDriver
|
||||||
|
subnet_dns_publish_fixed_ip = neutron.plugins.ml2.extensions.subnet_dns_publish_fixed_ip:SubnetDNSPublishFixedIPExtensionDriver
|
||||||
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