Keep dns nameserver order consistency
Currently, there is no dns servers prioritization for subnets for Neutron. Generally speaking, it is useful to keep the order of dns nameservers consistent. Add a new column named 'order' in table 'dnsnameservers' and add nameserver into DB one by one. Closes-Bug: #1218629 Implements: blueprint keep-dns-nameserver-orderconsistency Change-Id: Id937aea411397d39370368a4eb45be26c4eefa9e
This commit is contained in:
parent
6b0ab7fd63
commit
cb60d0bb4e
|
@ -0,0 +1,74 @@
|
||||||
|
Keep DNS Nameserver Order Consistency In Neutron
|
||||||
|
================================================
|
||||||
|
|
||||||
|
In Neutron subnets, DNS nameservers are given priority when created or updated.
|
||||||
|
This means if you create a subnet with multiple DNS servers, the order will
|
||||||
|
be retained and guests will receive the DNS servers in the order you
|
||||||
|
created them in when the subnet was created. The same thing applies for update
|
||||||
|
operations on subnets to add, remove, or update DNS servers.
|
||||||
|
|
||||||
|
Get Subnet Details Info
|
||||||
|
-----------------------
|
||||||
|
::
|
||||||
|
|
||||||
|
changzhi@stack:~/devstack$ neutron subnet-list
|
||||||
|
+--------------------------------------+------+-------------+--------------------------------------------+
|
||||||
|
| id | name | cidr | allocation_pools |
|
||||||
|
+--------------------------------------+------+-------------+--------------------------------------------+
|
||||||
|
| 1a2d261b-b233-3ab9-902e-88576a82afa6 | | 10.0.0.0/24 | {"start": "10.0.0.2", "end": "10.0.0.254"} |
|
||||||
|
+--------------------------------------+------+-------------+--------------------------------------------+
|
||||||
|
|
||||||
|
changzhi@stack:~/devstack$ neutron subnet-show 1a2d261b-b233-3ab9-902e-88576a82afa6
|
||||||
|
+------------------+--------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+------------------+--------------------------------------------+
|
||||||
|
| allocation_pools | {"start": "10.0.0.2", "end": "10.0.0.254"} |
|
||||||
|
| cidr | 10.0.0.0/24 |
|
||||||
|
| dns_nameservers | 1.1.1.1 |
|
||||||
|
| | 2.2.2.2 |
|
||||||
|
| | 3.3.3.3 |
|
||||||
|
| enable_dhcp | True |
|
||||||
|
| gateway_ip | 10.0.0.1 |
|
||||||
|
| host_routes | |
|
||||||
|
| id | 1a2d26fb-b733-4ab3-992e-88554a87afa6 |
|
||||||
|
| ip_version | 4 |
|
||||||
|
| name | |
|
||||||
|
| network_id | a404518c-800d-2353-9193-57dbb42ac5ee |
|
||||||
|
| tenant_id | 3868290ab10f417390acbb754160dbb2 |
|
||||||
|
+------------------+--------------------------------------------+
|
||||||
|
|
||||||
|
Update Subnet DNS Nameservers
|
||||||
|
-----------------------------
|
||||||
|
::
|
||||||
|
|
||||||
|
neutron subnet-update 1a2d261b-b233-3ab9-902e-88576a82afa6 \
|
||||||
|
--dns_nameservers list=true 3.3.3.3 2.2.2.2 1.1.1.1
|
||||||
|
|
||||||
|
changzhi@stack:~/devstack$ neutron subnet-show 1a2d261b-b233-3ab9-902e-88576a82afa6
|
||||||
|
+------------------+--------------------------------------------+
|
||||||
|
| Field | Value |
|
||||||
|
+------------------+--------------------------------------------+
|
||||||
|
| allocation_pools | {"start": "10.0.0.2", "end": "10.0.0.254"} |
|
||||||
|
| cidr | 10.0.0.0/24 |
|
||||||
|
| dns_nameservers | 3.3.3.3 |
|
||||||
|
| | 2.2.2.2 |
|
||||||
|
| | 1.1.1.1 |
|
||||||
|
| enable_dhcp | True |
|
||||||
|
| gateway_ip | 10.0.0.1 |
|
||||||
|
| host_routes | |
|
||||||
|
| id | 1a2d26fb-b733-4ab3-992e-88554a87afa6 |
|
||||||
|
| ip_version | 4 |
|
||||||
|
| name | |
|
||||||
|
| network_id | a404518c-800d-2353-9193-57dbb42ac5ee |
|
||||||
|
| tenant_id | 3868290ab10f417390acbb754160dbb2 |
|
||||||
|
+------------------+--------------------------------------------+
|
||||||
|
|
||||||
|
As shown in above output, the order of the DNS nameservers has been updated.
|
||||||
|
New virtual machines deployed to this subnet will receive the DNS nameservers
|
||||||
|
in this new priority order. Existing virtual machines that have already been
|
||||||
|
deployed will not be immediately affected by changing the DNS nameserver order
|
||||||
|
on the neutron subnet. Virtual machines that are configured to get their IP
|
||||||
|
address via DHCP will detect the DNS nameserver order change
|
||||||
|
when their DHCP lease expires or when the virtual machine is restarted.
|
||||||
|
Existing virtual machines configured with a static IP address will never
|
||||||
|
detect the updated DNS nameserver order.
|
|
@ -53,6 +53,7 @@ Neutron Internals
|
||||||
advanced_services
|
advanced_services
|
||||||
oslo-incubator
|
oslo-incubator
|
||||||
callbacks
|
callbacks
|
||||||
|
dns_order
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -172,7 +172,8 @@ class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
|
||||||
|
|
||||||
def _get_dns_by_subnet(self, context, subnet_id):
|
def _get_dns_by_subnet(self, context, subnet_id):
|
||||||
dns_qry = context.session.query(models_v2.DNSNameServer)
|
dns_qry = context.session.query(models_v2.DNSNameServer)
|
||||||
return dns_qry.filter_by(subnet_id=subnet_id).all()
|
return dns_qry.filter_by(subnet_id=subnet_id).order_by(
|
||||||
|
models_v2.DNSNameServer.order).all()
|
||||||
|
|
||||||
def _get_route_by_subnet(self, context, subnet_id):
|
def _get_route_by_subnet(self, context, subnet_id):
|
||||||
route_qry = context.session.query(models_v2.SubnetRoute)
|
route_qry = context.session.query(models_v2.SubnetRoute)
|
||||||
|
|
|
@ -138,22 +138,22 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||||
|
|
||||||
def _update_subnet_dns_nameservers(self, context, id, s):
|
def _update_subnet_dns_nameservers(self, context, id, s):
|
||||||
old_dns_list = self._get_dns_by_subnet(context, id)
|
old_dns_list = self._get_dns_by_subnet(context, id)
|
||||||
new_dns_addr_set = set(s["dns_nameservers"])
|
new_dns_addr_list = s["dns_nameservers"]
|
||||||
old_dns_addr_set = set([dns['address']
|
|
||||||
for dns in old_dns_list])
|
|
||||||
|
|
||||||
new_dns = list(new_dns_addr_set)
|
# NOTE(changzhi) delete all dns nameservers from db
|
||||||
for dns_addr in old_dns_addr_set - new_dns_addr_set:
|
# when update subnet's DNS nameservers. And store new
|
||||||
for dns in old_dns_list:
|
# nameservers with order one by one.
|
||||||
if dns['address'] == dns_addr:
|
for dns in old_dns_list:
|
||||||
context.session.delete(dns)
|
context.session.delete(dns)
|
||||||
for dns_addr in new_dns_addr_set - old_dns_addr_set:
|
|
||||||
|
for order, server in enumerate(new_dns_addr_list):
|
||||||
dns = models_v2.DNSNameServer(
|
dns = models_v2.DNSNameServer(
|
||||||
address=dns_addr,
|
address=server,
|
||||||
|
order=order,
|
||||||
subnet_id=id)
|
subnet_id=id)
|
||||||
context.session.add(dns)
|
context.session.add(dns)
|
||||||
del s["dns_nameservers"]
|
del s["dns_nameservers"]
|
||||||
return new_dns
|
return new_dns_addr_list
|
||||||
|
|
||||||
def _update_subnet_allocation_pools(self, context, subnet_id, s):
|
def _update_subnet_allocation_pools(self, context, subnet_id, s):
|
||||||
context.session.query(models_v2.IPAllocationPool).filter_by(
|
context.session.query(models_v2.IPAllocationPool).filter_by(
|
||||||
|
@ -424,11 +424,15 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||||
|
|
||||||
subnet = models_v2.Subnet(**subnet_args)
|
subnet = models_v2.Subnet(**subnet_args)
|
||||||
context.session.add(subnet)
|
context.session.add(subnet)
|
||||||
|
# NOTE(changzhi) Store DNS nameservers with order into DB one
|
||||||
|
# by one when create subnet with DNS nameservers
|
||||||
if attributes.is_attr_set(dns_nameservers):
|
if attributes.is_attr_set(dns_nameservers):
|
||||||
for addr in dns_nameservers:
|
for order, server in enumerate(dns_nameservers):
|
||||||
ns = models_v2.DNSNameServer(address=addr,
|
dns = models_v2.DNSNameServer(
|
||||||
subnet_id=subnet.id)
|
address=server,
|
||||||
context.session.add(ns)
|
order=order,
|
||||||
|
subnet_id=subnet.id)
|
||||||
|
context.session.add(dns)
|
||||||
|
|
||||||
if attributes.is_attr_set(host_routes):
|
if attributes.is_attr_set(host_routes):
|
||||||
for rt in host_routes:
|
for rt in host_routes:
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
2a16083502f3
|
1c844d1677f7
|
||||||
45f955889773
|
45f955889773
|
||||||
kilo
|
kilo
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Copyright 2015 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""add order to dnsnameservers
|
||||||
|
|
||||||
|
Revision ID: 1c844d1677f7
|
||||||
|
Revises: 2a16083502f3
|
||||||
|
Create Date: 2015-07-21 22:59:03.383850
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '1c844d1677f7'
|
||||||
|
down_revision = '2a16083502f3'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('dnsnameservers',
|
||||||
|
sa.Column('order', sa.Integer(),
|
||||||
|
server_default='0', nullable=False))
|
|
@ -178,6 +178,7 @@ class DNSNameServer(model_base.BASEV2):
|
||||||
sa.ForeignKey('subnets.id',
|
sa.ForeignKey('subnets.id',
|
||||||
ondelete="CASCADE"),
|
ondelete="CASCADE"),
|
||||||
primary_key=True)
|
primary_key=True)
|
||||||
|
order = sa.Column(sa.Integer, nullable=False, server_default='0')
|
||||||
|
|
||||||
|
|
||||||
class Subnet(model_base.BASEV2, HasId, HasTenant):
|
class Subnet(model_base.BASEV2, HasId, HasTenant):
|
||||||
|
@ -201,6 +202,7 @@ class Subnet(model_base.BASEV2, HasId, HasTenant):
|
||||||
dns_nameservers = orm.relationship(DNSNameServer,
|
dns_nameservers = orm.relationship(DNSNameServer,
|
||||||
backref='subnet',
|
backref='subnet',
|
||||||
cascade='all, delete, delete-orphan',
|
cascade='all, delete, delete-orphan',
|
||||||
|
order_by=DNSNameServer.order,
|
||||||
lazy='joined')
|
lazy='joined')
|
||||||
routes = orm.relationship(SubnetRoute,
|
routes = orm.relationship(SubnetRoute,
|
||||||
backref='subnet',
|
backref='subnet',
|
||||||
|
|
|
@ -333,6 +333,17 @@ class FakeV4SubnetMultipleAgentsWithoutDnsProvided(object):
|
||||||
host_routes = []
|
host_routes = []
|
||||||
|
|
||||||
|
|
||||||
|
class FakeV4SubnetAgentWithManyDnsProvided(object):
|
||||||
|
id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
|
||||||
|
ip_version = 4
|
||||||
|
cidr = '192.168.0.0/24'
|
||||||
|
gateway_ip = '192.168.0.1'
|
||||||
|
enable_dhcp = True
|
||||||
|
dns_nameservers = ['2.2.2.2', '9.9.9.9', '1.1.1.1',
|
||||||
|
'3.3.3.3']
|
||||||
|
host_routes = []
|
||||||
|
|
||||||
|
|
||||||
class FakeV4MultipleAgentsWithoutDnsProvided(object):
|
class FakeV4MultipleAgentsWithoutDnsProvided(object):
|
||||||
id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
|
id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
|
||||||
subnets = [FakeV4SubnetMultipleAgentsWithoutDnsProvided()]
|
subnets = [FakeV4SubnetMultipleAgentsWithoutDnsProvided()]
|
||||||
|
@ -341,6 +352,14 @@ class FakeV4MultipleAgentsWithoutDnsProvided(object):
|
||||||
namespace = 'qdhcp-ns'
|
namespace = 'qdhcp-ns'
|
||||||
|
|
||||||
|
|
||||||
|
class FakeV4AgentWithManyDnsProvided(object):
|
||||||
|
id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
|
||||||
|
subnets = [FakeV4SubnetAgentWithManyDnsProvided()]
|
||||||
|
ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(),
|
||||||
|
FakePortMultipleAgents1()]
|
||||||
|
namespace = 'qdhcp-ns'
|
||||||
|
|
||||||
|
|
||||||
class FakeV4SubnetMultipleAgentsWithDnsProvided(object):
|
class FakeV4SubnetMultipleAgentsWithDnsProvided(object):
|
||||||
id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
|
id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
|
||||||
ip_version = 4
|
ip_version = 4
|
||||||
|
@ -1135,6 +1154,18 @@ class TestDnsmasq(TestBase):
|
||||||
self._test_output_opts_file(expected,
|
self._test_output_opts_file(expected,
|
||||||
FakeV4MultipleAgentsWithoutDnsProvided())
|
FakeV4MultipleAgentsWithoutDnsProvided())
|
||||||
|
|
||||||
|
def test_output_opts_file_agent_with_many_dns_provided(self):
|
||||||
|
expected = ('tag:tag0,'
|
||||||
|
'option:dns-server,2.2.2.2,9.9.9.9,1.1.1.1,3.3.3.3\n'
|
||||||
|
'tag:tag0,option:classless-static-route,'
|
||||||
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n'
|
||||||
|
'tag:tag0,249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,'
|
||||||
|
'192.168.0.1\n'
|
||||||
|
'tag:tag0,option:router,192.168.0.1').lstrip()
|
||||||
|
|
||||||
|
self._test_output_opts_file(expected,
|
||||||
|
FakeV4AgentWithManyDnsProvided())
|
||||||
|
|
||||||
def test_output_opts_file_multiple_agents_with_dns_provided(self):
|
def test_output_opts_file_multiple_agents_with_dns_provided(self):
|
||||||
expected = ('tag:tag0,option:dns-server,8.8.8.8\n'
|
expected = ('tag:tag0,option:dns-server,8.8.8.8\n'
|
||||||
'tag:tag0,option:classless-static-route,'
|
'tag:tag0,option:classless-static-route,'
|
||||||
|
|
|
@ -3921,8 +3921,8 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||||
self.assertEqual(sorted(res['subnet']['host_routes']),
|
self.assertEqual(sorted(res['subnet']['host_routes']),
|
||||||
sorted(host_routes))
|
sorted(host_routes))
|
||||||
self.assertEqual(sorted(res['subnet']['dns_nameservers']),
|
self.assertEqual(res['subnet']['dns_nameservers'],
|
||||||
sorted(dns_nameservers))
|
dns_nameservers)
|
||||||
|
|
||||||
def test_update_subnet_shared_returns_400(self):
|
def test_update_subnet_shared_returns_400(self):
|
||||||
with self.network(shared=True) as network:
|
with self.network(shared=True) as network:
|
||||||
|
@ -4463,6 +4463,27 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||||
self.assertEqual(res['subnet']['dns_nameservers'],
|
self.assertEqual(res['subnet']['dns_nameservers'],
|
||||||
data['subnet']['dns_nameservers'])
|
data['subnet']['dns_nameservers'])
|
||||||
|
|
||||||
|
def test_subnet_lifecycle_dns_retains_order(self):
|
||||||
|
cfg.CONF.set_override('max_dns_nameservers', 3)
|
||||||
|
with self.subnet(dns_nameservers=['1.1.1.1', '2.2.2.2',
|
||||||
|
'3.3.3.3']) as subnet:
|
||||||
|
subnets = self._show('subnets', subnet['subnet']['id'],
|
||||||
|
expected_code=webob.exc.HTTPOk.code)
|
||||||
|
self.assertEqual(['1.1.1.1', '2.2.2.2', '3.3.3.3'],
|
||||||
|
subnets['subnet']['dns_nameservers'])
|
||||||
|
data = {'subnet': {'dns_nameservers': ['2.2.2.2', '3.3.3.3',
|
||||||
|
'1.1.1.1']}}
|
||||||
|
req = self.new_update_request('subnets',
|
||||||
|
data,
|
||||||
|
subnet['subnet']['id'])
|
||||||
|
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||||
|
self.assertEqual(data['subnet']['dns_nameservers'],
|
||||||
|
res['subnet']['dns_nameservers'])
|
||||||
|
subnets = self._show('subnets', subnet['subnet']['id'],
|
||||||
|
expected_code=webob.exc.HTTPOk.code)
|
||||||
|
self.assertEqual(data['subnet']['dns_nameservers'],
|
||||||
|
subnets['subnet']['dns_nameservers'])
|
||||||
|
|
||||||
def test_update_subnet_dns_to_None(self):
|
def test_update_subnet_dns_to_None(self):
|
||||||
with self.subnet(dns_nameservers=['11.0.0.1']) as subnet:
|
with self.subnet(dns_nameservers=['11.0.0.1']) as subnet:
|
||||||
data = {'subnet': {'dns_nameservers': None}}
|
data = {'subnet': {'dns_nameservers': None}}
|
||||||
|
|
Loading…
Reference in New Issue