launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #09916
[Merge] lp:~rvb/maas/maas-dns3 into lp:maas
Raphaël Badin has proposed merging lp:~rvb/maas/maas-dns3 into lp:maas with lp:~rvb/maas/maas-dns-utils as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~rvb/maas/maas-dns3/+merge/114832
WIP
--
https://code.launchpad.net/~rvb/maas/maas-dns3/+merge/114832
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/maas-dns3 into lp:maas.
=== modified file 'etc/celeryconfig.py'
--- etc/celeryconfig.py 2012-07-11 09:06:57 +0000
+++ etc/celeryconfig.py 2012-07-13 10:59:19 +0000
@@ -32,6 +32,10 @@
# Location of MAAS' bind configuration files.
DNS_CONFIG_DIR = '/var/cache/bind/maas'
+# RNDC port to be configured by MAAS to communicate with the BIND
+# server.
+DNS_RNDC_PORT = 954
+
# DHCP leases file, as maintained by ISC dhcpd.
DHCP_LEASES_FILE = '/var/lib/dhcp/dhcpd.leases'
=== modified file 'etc/democeleryconfig.py'
--- etc/democeleryconfig.py 2012-07-12 15:18:10 +0000
+++ etc/democeleryconfig.py 2012-07-13 10:59:19 +0000
@@ -26,3 +26,7 @@
DNS_CONFIG_DIR = os.path.join(
DEV_ROOT_DIRECTORY, 'run/named/')
+
+
+DNS_RNDC_PORT = 9154
+
=== modified file 'src/maasserver/dns.py'
--- src/maasserver/dns.py 2012-06-29 16:00:51 +0000
+++ src/maasserver/dns.py 2012-07-13 10:59:19 +0000
@@ -11,14 +11,22 @@
__metaclass__ = type
__all__ = [
+ 'add_zone',
+ 'change_dns_zone',
'next_zone_serial',
+ 'write_full_dns_config',
]
+from maasserver.models import (
+ DHCPLease,
+ NodeGroup,
+ )
from maasserver.sequence import (
INT_MAX,
Sequence,
)
+from provisioningserver import tasks
# A DNS zone's serial is a 32-bit integer. Also, we start with the
# value 1 because 0 has special meaning for some DNS servers. Even if
@@ -29,3 +37,45 @@
def next_zone_serial():
return '%0.10d' % zone_serial.nextval()
+
+
+def change_dns_zone(nodegroup):
+ mapping = DHCPLease.objects.get_hostname_ip_mapping(nodegroup)
+ zone_name = nodegroup.name
+ tasks.write_dns_zone_config.delay(
+ zone_name=zone_name, domain=zone_name,
+ serial=next_zone_serial(), hostname_ip_mapping=mapping,
+ callback=tasks.rndc_command.subtask(args=['reload', zone_name]))
+
+
+def add_zone(nodegroup):
+ zone_names = [
+ result[0]
+ for result in NodeGroup.objects.all().values_list('name')]
+ tasks.write_dns_config(zone_names=zone_names)
+ mapping = DHCPLease.objects.get_hostname_ip_mapping(nodegroup)
+ zone_name = nodegroup.name
+ tasks.write_dns_zone_config.delay(
+ zone_name=zone_name, domain=zone_name,
+ serial=next_zone_serial(), hostname_ip_mapping=mapping,
+ callback=tasks.write_dns_config.subtask(
+ zone_names=zone_names,
+ callback=tasks.rndc_command.subtask(args=['reconfig'])))
+
+
+def write_full_dns_config():
+ groups = NodeGroup.objects.all()
+ serial = next_zone_serial()
+ zones = {
+ group.name: {
+ 'serial': serial,
+ 'zone_name': group.name,
+ 'domain': group.name,
+ 'hostname_ip_mapping': (
+ DHCPLease.objects.get_hostname_ip_mapping(
+ group))
+ }
+ for group in groups
+ }
+ tasks.write_full_dns_config(
+ zones, callback=tasks.rndc_command.subtask(args=['reload']))
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2012-07-10 15:17:21 +0000
+++ src/maasserver/testing/factory.py 2012-07-13 10:59:19 +0000
@@ -90,7 +90,7 @@
created=None, **kwargs):
# hostname=None is a valid value, hence the set_hostname trick.
if hostname is '' and set_hostname:
- hostname = self.getRandomString(255)
+ hostname = self.getRandomString(20)
if status is None:
status = NODE_STATUS.DEFAULT_STATUS
node = Node(
=== modified file 'src/maasserver/tests/test_dns.py'
--- src/maasserver/tests/test_dns.py 2012-06-29 16:00:51 +0000
+++ src/maasserver/tests/test_dns.py 2012-07-13 10:59:19 +0000
@@ -12,11 +12,26 @@
__metaclass__ = type
__all__ = []
+
+from django.core.management import call_command
from maasserver.dns import (
+ add_zone,
+ change_dns_zone,
next_zone_serial,
+ write_full_dns_config,
zone_serial,
)
+from maasserver.testing.factory import factory
from maasserver.testing.testcase import TestCase
+from maastesting.bindfixture import BINDServer
+from maastesting.celery import CeleryFixture
+from maastesting.tests.test_bindfixture import dig_call
+from provisioningserver.dns.config import conf
+from provisioningserver.tasks import (
+ setup_rndc_configuration,
+ write_dns_config,
+ )
+from testresources import FixtureResource
from testtools.matchers import MatchesStructure
@@ -33,6 +48,66 @@
)
def test_next_zone_serial_returns_sequence(self):
+ initial = int(next_zone_serial())
self.assertSequenceEqual(
- ['%0.10d' % i for i in range(1, 11)],
- [next_zone_serial() for i in range(10)])
+ ['%0.10d' % i for i in range(initial + 1, initial + 11)],
+ [next_zone_serial() for i in range(initial, initial + 10)])
+
+
+class TestDNSConfigModifications(TestCase):
+
+ resources = (
+ ("celery", FixtureResource(CeleryFixture())),
+ )
+
+ def setUp(self):
+ super(TestDNSConfigModifications, self).setUp()
+ self.bind = self.useFixture(BINDServer())
+ self.patch(conf, 'DNS_CONFIG_DIR', self.bind.config.homedir)
+
+ call_command(
+ 'get_named_conf', edit=True,
+ config_path=self.bind.config.conf_file)
+ setup_rndc_configuration()
+ write_dns_config(inactive=True)
+ self.bind.runner.rndc('reload')
+
+ def create_nodegroup_with_lease(self, nodegroup=None):
+ if nodegroup is None:
+ nodegroup = factory.make_node_group()
+ node = factory.make_node(
+ nodegroup=nodegroup, set_hostname=True)
+ mac = factory.make_mac_address(node=node)
+ lease = factory.make_dhcp_lease(
+ nodegroup=nodegroup, mac=mac.mac_address)
+ return nodegroup, node, lease
+
+ def dig_resolve(self, fqdn):
+ return dig_call(
+ port=self.bind.config.port,
+ commands=[fqdn, '+short'])
+
+ def test_add_zone_loads_dns_zone(self):
+ nodegroup, node, lease = self.create_nodegroup_with_lease()
+ add_zone(nodegroup)
+
+ fqdn = "%s.%s" % (node.hostname, nodegroup.name)
+ self.assertEqual(lease.ip, self.dig_resolve(fqdn))
+
+ def test_change_zone_changes_dns_zone(self):
+ nodegroup, _, _ = self.create_nodegroup_with_lease()
+ write_full_dns_config()
+ nodegroup, new_node, new_lease = (
+ self.create_nodegroup_with_lease(nodegroup=nodegroup))
+ change_dns_zone(nodegroup)
+
+ fqdn = "%s.%s" % (new_node.hostname, nodegroup.name)
+ self.assertEqual(new_lease.ip, self.dig_resolve(fqdn))
+
+ def test_write_full_dns_loads_full_dns_config(self):
+ nodegroup, node, lease = self.create_nodegroup_with_lease()
+
+ write_full_dns_config()
+
+ fqdn = "%s.%s" % (node.hostname, nodegroup.name)
+ self.assertEqual(lease.ip, self.dig_resolve(fqdn))
=== modified file 'src/maastesting/tests/test_bindfixture.py'
--- src/maastesting/tests/test_bindfixture.py 2012-07-09 14:01:13 +0000
+++ src/maastesting/tests/test_bindfixture.py 2012-07-13 10:59:19 +0000
@@ -32,7 +32,7 @@
from testtools.testcase import gather_details
-def dig_call(port=53, server='127.0.0.1', command=''):
+def dig_call(port=53, server='127.0.0.1', commands=None):
"""Call `dig` with the given command.
Note that calling dig without a command will perform an NS
@@ -42,16 +42,19 @@
:param port: Port of the queried DNS server (defaults to 53).
:param server: IP address of the queried DNS server (defaults
to '127.0.0.1').
- :param command: Dig command to run (defaults to '').
+ :param commands: List of dig commands to run (defaults to None
+ which will perform an NS query for "." (the root)).
:return: The output as a string.
:rtype: basestring
"""
cmd = [
'dig', '+time=1', '+tries=1', '@%s' % server, '-p',
'%d' % port]
- if command != '':
- cmd.append(command)
- return check_output(cmd)
+ if commands != None:
+ if not isinstance(commands, list):
+ commands = (commands, )
+ cmd.extend(commands)
+ return check_output(cmd).strip()
class TestBINDFixture(TestCase):
=== modified file 'src/provisioningserver/dns/config.py'
--- src/provisioningserver/dns/config.py 2012-07-11 15:14:48 +0000
+++ src/provisioningserver/dns/config.py 2012-07-13 10:59:19 +0000
@@ -13,7 +13,6 @@
__all__ = [
'DNSConfig',
'DNSZoneConfig',
- 'InactiveDNSConfig',
'setup_rndc',
]
@@ -83,7 +82,8 @@
MAAS_RNDC_CONF_NAME and MAAS_NAMED_RNDC_CONF_NAME, both stored in
conf.DNS_CONFIG_DIR.
"""
- rndc_content, named_content = generate_rndc()
+ rndc_content, named_content = generate_rndc(
+ conf.DNS_RNDC_PORT)
target_file = get_rndc_conf_path()
with open(target_file, "wb") as f:
@@ -98,7 +98,10 @@
"""Execute a rndc command."""
rndc_conf = os.path.join(
conf.DNS_CONFIG_DIR, MAAS_RNDC_CONF_NAME)
- check_call(['rndc', '-c', rndc_conf] + map(str, arguments))
+ with open(os.devnull, "ab") as devnull:
+ check_call(
+ ['rndc', '-c', rndc_conf] + map(str, arguments),
+ stdout=devnull)
# Directory where the DNS configuration template files can be found.
@@ -184,16 +187,6 @@
return '\ninclude "%s";\n' % self.target_path
-class InactiveDNSConfig(DNSConfig):
- """A specialized version of DNSConfig that simply writes a blank/empty
- configuration file.
- """
-
- def get_template(self):
- """Return an empty template."""
- return tempita.Template('', 'empty template')
-
-
class DNSZoneConfig(DNSConfig):
"""A specialized version of DNSConfig that writes zone files."""
=== modified file 'src/provisioningserver/dns/templates/zone.template'
--- src/provisioningserver/dns/templates/zone.template 2012-07-11 11:13:24 +0000
+++ src/provisioningserver/dns/templates/zone.template 2012-07-13 10:59:19 +0000
@@ -8,6 +8,7 @@
)
IN NS {{domain}}.
-{{for host in hosts}}
-{{host['hostname']}} IN A {{host['ip']}}
+{{domain}}. IN A 127.0.0.1; # TODO: use master worker IP.
+{{for hostname, ip in hostname_ip_mapping.items()}}
+{{hostname}} IN A {{ip}}
{{endfor}}
=== modified file 'src/provisioningserver/dns/tests/test_config.py'
--- src/provisioningserver/dns/tests/test_config.py 2012-07-11 15:14:48 +0000
+++ src/provisioningserver/dns/tests/test_config.py 2012-07-13 10:59:19 +0000
@@ -30,7 +30,6 @@
DNSZoneConfig,
execute_rndc_command,
generate_rndc,
- InactiveDNSConfig,
MAAS_NAMED_CONF_NAME,
MAAS_NAMED_RNDC_CONF_NAME,
MAAS_RNDC_CONF_NAME,
@@ -147,19 +146,6 @@
Contains('include "%s"' % dnsconfig.target_path)))
-class TestInactiveDNSConfig(TestCase):
- """Tests for InactiveDNSConfig."""
-
- def test_write_config_writes_empty_config(self):
- target_dir = self.make_dir()
- self.patch(InactiveDNSConfig, 'target_dir', target_dir)
- dnsconfig = InactiveDNSConfig()
- dnsconfig.write_config()
- self.assertThat(
- os.path.join(target_dir, MAAS_NAMED_CONF_NAME),
- FileContains(''))
-
-
class TestDNSZoneConfig(TestCase):
"""Tests for DNSZoneConfig."""
@@ -185,17 +171,16 @@
dnszoneconfig = DNSZoneConfig(zone_name)
domain = factory.getRandomString()
serial = random.randint(1, 100)
- hosts = [{
- 'ip': factory.getRandomIPAddress(),
- 'hostname': factory.getRandomString()}]
+ hostname = factory.getRandomString()
+ ip = factory.getRandomIPAddress()
dnszoneconfig.write_config(
- domain=domain, serial=serial, hosts=hosts)
+ domain=domain, serial=serial,
+ hostname_ip_mapping={hostname: ip})
self.assertThat(
os.path.join(target_dir, 'zone.%s' % zone_name),
FileContains(
matcher=ContainsAll(
[
'IN NS %s.' % domain,
- '%s IN A %s' % (
- hosts[0]['hostname'], hosts[0]['ip']),
+ '%s IN A %s' % (hostname, ip),
])))
=== modified file 'src/provisioningserver/tasks.py'
--- src/provisioningserver/tasks.py 2012-07-11 11:25:25 +0000
+++ src/provisioningserver/tasks.py 2012-07-13 10:59:19 +0000
@@ -13,22 +13,19 @@
__all__ = [
'power_off',
'power_on',
- 'reload_dns_config',
- 'reload_zone_config',
+ 'rndc_command',
+ 'setup_rndc_configuration',
'write_dns_config',
'write_dns_zone_config',
'write_full_dns_config',
- 'setup_rndc_configuration',
]
from celery.task import task
-from celery.task.sets import subtask
from provisioningserver.dns.config import (
DNSConfig,
DNSZoneConfig,
execute_rndc_command,
- InactiveDNSConfig,
setup_rndc,
)
from provisioningserver.power.poweraction import (
@@ -97,20 +94,14 @@
@task
-def reload_dns_config():
- """Use rndc to reload the DNS configuration."""
- execute_rndc_command('reload')
-
-
-@task
-def reload_zone_config(zone_name):
- """Use rndc to reload the DNS configuration for a zone."""
- execute_rndc_command('reload', zone_name)
+def rndc_command(*arguments):
+ """Use rndc to execute a command."""
+ execute_rndc_command(*arguments)
@task
def write_full_dns_config(zones=None, reverse_zones=None,
- **kwargs):
+ callback=None, **kwargs):
"""Write out the DNS configuration files: the main configuration
file and the zone files.
:param zones: Mapping between zone names and the zone data used
@@ -120,6 +111,8 @@
reverse zone data used to write the reverse zone config
files.
:type reverse_zones: dict
+ :param callback: Callback subtask.
+ :type callback: callable
:param **kwargs: Keyword args passed to DNSConfig.write_config()
"""
if zones is None:
@@ -137,52 +130,57 @@
zone_names=list(zones),
reverse_zone_names=list(reverse_zones))
config.write_config(**kwargs)
- subtask(reload_dns_config.subtask()).delay()
+ if callback is not None:
+ callback.delay()
@task
-def write_dns_config(inactive=False, zone_names=(),
- reverse_zone_names=(), **kwargs):
+def write_dns_config(zone_names=(), reverse_zone_names=(),
+ callback=None, **kwargs):
"""Write out the DNS configuration file.
- :param inactive: Whether or not an inactive (i.e. blank)
- configuration should be written. False by default.
- :type inactive: boolean
:param zone_names: List of zone names to include as part of the
main config.
:type zone_names: list
:param reverse_zone_names: List of reverse zone names to include as part of
the main config.
:type reverse_zone_names: list
+ :param callback: Callback subtask.
+ :type callback: callable
:param **kwargs: Keyword args passed to DNSConfig.write_config()
"""
- if inactive:
- InactiveDNSConfig().write_config()
- else:
- config = DNSConfig(
- zone_names=zone_names,
- reverse_zone_names=reverse_zone_names)
- config.write_config(**kwargs)
- subtask(reload_dns_config.subtask()).delay()
+ config = DNSConfig(
+ zone_names=zone_names,
+ reverse_zone_names=reverse_zone_names)
+ config.write_config(**kwargs)
+ if callback is not None:
+ callback.delay()
@task
-def write_dns_zone_config(zone_name, **kwargs):
+def write_dns_zone_config(zone_name, callback=None, **kwargs):
"""Write out a DNS zone configuration file.
:param zone_name: The identifier of the zone to write the configuration
for.
:type zone_name: basestring
+ :param callback: Callback subtask.
+ :type callback: callable
:param **kwargs: Keyword args passed to DNSZoneConfig.write_config()
"""
DNSZoneConfig(zone_name).write_config(**kwargs)
- subtask(reload_zone_config.subtask(args=[zone_name])).delay()
+ if callback is not None:
+ callback.delay()
@task
-def setup_rndc_configuration():
+def setup_rndc_configuration(callback=None):
"""Write out the two rndc configuration files (rndc.conf and
named.conf.rndc).
+
+ :param callback: Callback subtask.
+ :type callback: callable
"""
setup_rndc()
- subtask(reload_dns_config.subtask()).delay()
+ if callback is not None:
+ callback.delay()
=== modified file 'src/provisioningserver/tests/test_tasks.py'
--- src/provisioningserver/tests/test_tasks.py 2012-07-11 15:25:05 +0000
+++ src/provisioningserver/tests/test_tasks.py 2012-07-13 10:59:19 +0000
@@ -33,8 +33,7 @@
from provisioningserver.tasks import (
power_off,
power_on,
- reload_dns_config,
- reload_zone_config,
+ rndc_command,
setup_rndc_configuration,
write_dns_config,
write_dns_zone_config,
@@ -130,7 +129,10 @@
def test_write_dns_config_writes_file(self):
zone_names = [random.randint(1, 100), random.randint(1, 100)]
- result = write_dns_config.delay(inactive=False, zone_names=zone_names)
+ command = factory.getRandomString()
+ result = write_dns_config.delay(
+ zone_names=zone_names,
+ callback=rndc_command.subtask(args=[command]))
self.assertThat(
(
@@ -142,32 +144,17 @@
(
Equals(True),
FileExists(),
- Equals([(('reload',), {})]),
- )),
- result)
-
- def test_write_dns_config_with_inactive_True(self):
- result = write_dns_config.delay(inactive=True)
-
- self.assertThat(
- (
- result.successful(),
- os.path.join(self.dns_conf_dir, MAAS_NAMED_CONF_NAME),
- self.rndc_recorder.calls,
- ),
- MatchesListwise(
- (
- Equals(True),
- FileContains(''),
- Equals([(('reload',), {})]),
+ Equals([((command,), {})]),
)),
result)
def test_write_dns_zone_config_writes_file(self):
+ command = factory.getRandomString()
zone_name = factory.getRandomString()
result = write_dns_zone_config.delay(
zone_name=zone_name, domain=factory.getRandomString(),
- serial=random.randint(1, 100), hosts=[])
+ serial=random.randint(1, 100), hostname_ip_mapping={},
+ callback=rndc_command.subtask(args=[command]))
self.assertThat(
(
@@ -179,12 +166,14 @@
(
Equals(True),
FileExists(),
- Equals([(('reload', zone_name), {})]),
+ Equals([((command, ), {})]),
)),
result)
def test_setup_rndc_configuration_writes_files(self):
- result = setup_rndc_configuration.delay()
+ command = factory.getRandomString()
+ result = setup_rndc_configuration.delay(
+ callback=rndc_command.subtask(args=[command]))
self.assertThat(
(
@@ -199,31 +188,20 @@
Equals(True),
FileExists(),
FileExists(),
- Equals([(('reload',), {})]),
+ Equals([((command,), {})]),
)),
result)
- def test_reload_dns_config_issues_reload_command(self):
- result = reload_dns_config.delay()
-
- self.assertThat(
- (result.successful(), self.rndc_recorder.calls),
- MatchesListwise(
- (
- Equals(True),
- Equals([(('reload',), {})]),
- )))
-
- def test_reload_zone_config_issues_zone_reload_command(self):
- zone_name = factory.getRandomString()
- result = reload_zone_config.delay(zone_name)
-
- self.assertThat(
- (result.successful(), self.rndc_recorder.calls),
- MatchesListwise(
- (
- Equals(True),
- Equals([(('reload', zone_name), {})]),
+ def test_rndc_command_execute_command(self):
+ command = factory.getRandomString()
+ result = rndc_command.delay(command)
+
+ self.assertThat(
+ (result.successful(), self.rndc_recorder.calls),
+ MatchesListwise(
+ (
+ Equals(True),
+ Equals([((command,), {})]),
)))
def test_write_full_dns_config_sets_up_config(self):
@@ -236,11 +214,14 @@
zones = {
zone_name: {
'serial': random.randint(1, 100),
- 'hosts': [{'hostname': hostname, 'ip': ip}],
+ 'hostname_ip_mapping': {hostname: ip},
'domain': domain,
}
}
- result = write_full_dns_config.delay(zones=zones)
+ command = factory.getRandomString()
+ result = write_full_dns_config.delay(
+ zones=zones,
+ callback=rndc_command.subtask(args=[command]))
self.assertThat(
(
@@ -254,7 +235,7 @@
MatchesListwise(
(
Equals(True),
- Equals([(('reload',), {})]),
+ Equals([((command,), {})]),
FileExists(),
FileExists(),
)))
Follow ups