Merge "Allow to select subnets to publish DNS records"
This commit is contained in:
commit
250d8a61c8
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
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.
|
||||
* 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
|
||||
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
|
||||
``example.org.`` was created previously.
|
||||
* 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
|
||||
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.
|
||||
|
||||
|
||||
@ -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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
value of ipv6_ptr_zone_prefix_size is 116.
|
||||
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.
|
||||
|
||||
.. 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
|
||||
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:
|
||||
|
||||
Performance considerations
|
||||
@ -798,10 +913,10 @@ use case.
|
||||
|
||||
.. _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:
|
||||
|
||||
* 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
|
||||
outside the ranges assigned to project networks.
|
||||
|
||||
This usually implies that this use case only works for networks specifically
|
||||
created for this purpose by an admin, it does not work for networks
|
||||
which tenants can create.
|
||||
This usually implies that these use cases only work for networks specifically
|
||||
created for this purpose by an admin, they do not work for networks
|
||||
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
|
||||
the Networking service. The functionality described covers DNS from two points
|
||||
of view:
|
||||
the Networking service and its interaction with the Compute service.
|
||||
|
||||
* The internal DNS functionality offered by the Networking service and its
|
||||
interaction with the Compute service.
|
||||
* Integration of the Compute service and the Networking service with an
|
||||
external DNSaaS (DNS-as-a-Service).
|
||||
The integration of the Networking service with an external DNSaaS
|
||||
(DNS-as-a-Service) is described in :ref:`config-dns-int-ext-serv`.
|
||||
|
||||
Users can control the behavior of the Networking service in regards to DNS
|
||||
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
|
||||
following is an example:
|
||||
|
||||
.. code-block:: console
|
||||
.. code-block:: ini
|
||||
|
||||
[ml2]
|
||||
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
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy import sql
|
||||
|
||||
from neutron.db.models import l3 as l3_models
|
||||
from neutron.db import models_v2
|
||||
@ -97,3 +98,25 @@ class PortDNS(model_base.BASEV2):
|
||||
uselist=False,
|
||||
cascade='delete'))
|
||||
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.utils import net as net_utils
|
||||
|
||||
from oslo_utils import versionutils
|
||||
from oslo_versionedobjects import fields as obj_fields
|
||||
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 subnet_service_type
|
||||
from neutron.db import models_v2
|
||||
@ -190,7 +192,8 @@ class SubnetServiceType(base.NeutronDbObject):
|
||||
@base.NeutronObjectRegistry.register
|
||||
class Subnet(base.NeutronDbObject):
|
||||
# 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
|
||||
new_facade = True
|
||||
@ -213,13 +216,15 @@ class Subnet(base.NeutronDbObject):
|
||||
'shared': obj_fields.BooleanField(nullable=True),
|
||||
'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer',
|
||||
nullable=True),
|
||||
'dns_publish_fixed_ip': obj_fields.BooleanField(nullable=True),
|
||||
'host_routes': obj_fields.ListOfObjectsField('Route', nullable=True),
|
||||
'ipv6_ra_mode': common_types.IPV6ModeEnumField(nullable=True),
|
||||
'ipv6_address_mode': common_types.IPV6ModeEnumField(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']
|
||||
|
||||
foreign_keys = {'Network': {'network_id': 'id'}}
|
||||
@ -235,12 +240,29 @@ class Subnet(base.NeutronDbObject):
|
||||
self.add_extra_filter_name('shared')
|
||||
|
||||
def obj_load_attr(self, attrname):
|
||||
if attrname == 'dns_publish_fixed_ip':
|
||||
return self._load_dns_publish_fixed_ip()
|
||||
if attrname == 'shared':
|
||||
return self._load_shared()
|
||||
if attrname == 'service_types':
|
||||
return self._load_service_types()
|
||||
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):
|
||||
if db_obj:
|
||||
# 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):
|
||||
super(Subnet, self).from_db_object(db_obj)
|
||||
self._load_dns_publish_fixed_ip(db_obj)
|
||||
self._load_shared(db_obj)
|
||||
self._load_service_types(db_obj)
|
||||
|
||||
@ -412,6 +435,11 @@ class Subnet(base.NeutronDbObject):
|
||||
raise ipam_exceptions.DeferIpam()
|
||||
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
|
||||
class NetworkSubnetLock(base.NeutronDbObject):
|
||||
@ -438,3 +466,18 @@ class NetworkSubnetLock(base.NeutronDbObject):
|
||||
subnet_lock = NetworkSubnetLock(context, network_id=network_id,
|
||||
subnet_id=subnet_id)
|
||||
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.objects import network as net_obj
|
||||
from neutron.objects import ports as port_obj
|
||||
from neutron.objects import subnet as subnet_obj
|
||||
from neutron.services.externaldns import driver
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -88,18 +89,19 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
request_data)
|
||||
if is_dns_domain_default:
|
||||
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,
|
||||
network, dns_name)
|
||||
network, subnets, dns_name)
|
||||
|
||||
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
|
||||
network.get(dns_apidef.DNSDOMAIN))
|
||||
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,
|
||||
self.external_dns_not_needed(plugin_context, network)))
|
||||
dns_name, external_dns_domain, flag))
|
||||
|
||||
dns_data_obj = port_obj.PortDNS(
|
||||
plugin_context,
|
||||
@ -131,7 +133,8 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
return '', ''
|
||||
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_domain = request_data.get(dns_apidef.DNSDOMAIN)
|
||||
has_fixed_ips = 'fixed_ips' in request_data
|
||||
@ -162,7 +165,8 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
return dns_data_db
|
||||
if dns_name or dns_domain:
|
||||
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
|
||||
|
||||
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,
|
||||
db_data, None)
|
||||
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
|
||||
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
|
||||
# dns_name or dns_domain attributes if necessary
|
||||
if has_dns_name or has_dns_domain:
|
||||
@ -216,7 +221,7 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
plugin_context, request_data, db_data)
|
||||
else:
|
||||
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,
|
||||
dns_data_db)
|
||||
|
||||
@ -247,14 +252,15 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
dns_data_db.create()
|
||||
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.
|
||||
|
||||
:param context: plugin request context
|
||||
:param network: network dictionary
|
||||
:param subnets: list of subnets in network
|
||||
:return: True or False
|
||||
"""
|
||||
pass
|
||||
return False
|
||||
|
||||
def extend_network_dict(self, session, db_data, response_data):
|
||||
response_data[dns_apidef.DNSDOMAIN] = ''
|
||||
@ -324,9 +330,11 @@ class DNSExtensionDriver(api.ExtensionDriver):
|
||||
return self._extend_port_dict(session, db_data, response_data,
|
||||
dns_data_db)
|
||||
|
||||
def _get_network(self, context, network_id):
|
||||
def _get_details(self, context, network_id):
|
||||
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):
|
||||
@ -361,10 +369,13 @@ class DNSExtensionDriverML2(DNSExtensionDriver):
|
||||
if vlan_range[0] <= segmentation_id <= vlan_range[1]:
|
||||
return True
|
||||
|
||||
def external_dns_not_needed(self, context, network):
|
||||
def external_dns_not_needed(self, context, network, subnets):
|
||||
dns_driver = _get_dns_driver()
|
||||
if not dns_driver:
|
||||
return True
|
||||
for subnet in subnets:
|
||||
if subnet.get('dns_publish_fixed_ip'):
|
||||
return False
|
||||
if network['router:external']:
|
||||
return True
|
||||
segments = segments_db.get_network_segments(context, network['id'])
|
||||
@ -424,6 +435,24 @@ def _get_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):
|
||||
dns_driver = _get_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'])
|
||||
if not (dns_data_db and dns_data_db['current_dns_name']):
|
||||
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,
|
||||
dns_data_db['current_dns_domain'],
|
||||
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')
|
||||
if not original_port:
|
||||
return
|
||||
original_ips = [ip['ip_address'] for ip in original_port['fixed_ips']]
|
||||
updated_ips = [ip['ip_address'] for ip in updated_port['fixed_ips']]
|
||||
original_ips = _filter_by_subnet(context, original_port['fixed_ips'])
|
||||
updated_ips = _filter_by_subnet(context, updated_port['fixed_ips'])
|
||||
is_dns_name_changed = (updated_port[dns_apidef.DNSNAME] !=
|
||||
original_port[dns_apidef.DNSNAME])
|
||||
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']:
|
||||
ip_allocations = port_obj.IPAllocation.get_objects(context,
|
||||
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(
|
||||
context, dns_driver, dns_data_db['current_dns_domain'],
|
||||
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-tag"
|
||||
NETWORK_API_EXTENSIONS+=",subnet_allocation"
|
||||
NETWORK_API_EXTENSIONS+=",subnet-dns-publish-fixed-ip"
|
||||
NETWORK_API_EXTENSIONS+=",trunk"
|
||||
NETWORK_API_EXTENSIONS+=",trunk-details"
|
||||
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',
|
||||
'ServiceProfile': '1.0-9beafc9e7d081b8258f3c5cb66ac5eed',
|
||||
'StandardAttribute': '1.0-617d4f46524c4ce734a6fc1cc0ac6a0b',
|
||||
'Subnet': '1.0-927155c1fdd5a615cbcb981dda97bce4',
|
||||
'Subnet': '1.1-5b7e1789a1732259d1e28b4bd87eb1c2',
|
||||
'SubnetDNSPublishFixedIP': '1.0-db22af6fa20b143986f0cbe06cbfe0ea',
|
||||
'SubnetPool': '1.0-a0e03895d1a6e7b9d4ab7b0ca13c3867',
|
||||
'SubnetPoolPrefix': '1.0-13c15144135eb869faa4a76dc3ee3b6c',
|
||||
'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
|
||||
dns_domain_ports = neutron.plugins.ml2.extensions.dns_integration:DNSDomainPortsExtensionDriver
|
||||
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 =
|
||||
fake = neutron.tests.unit.ipam.fake_driver:FakeDriver
|
||||
internal = neutron.ipam.drivers.neutrondb_ipam.driver:NeutronDbPool
|
||||
|
Loading…
Reference in New Issue
Block a user