URL: https://github.com/freeipa/freeipa/pull/423 Author: MartinBasti Title: #423: dns-update-system-records: add support for nsupdate output format Action: synchronized
To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/423/head:pr423 git checkout pr423
From 23c3ca4761ab6cfd9a2ecee631cc2f8e4a5c3dc9 Mon Sep 17 00:00:00 2001 From: Martin Basti <[email protected]> Date: Fri, 27 Jan 2017 13:42:19 +0100 Subject: [PATCH 1/2] DNS: dns-update-system-record can create nsupdate file Added option --out <path> creates a file with IPA DNS data in nsupdate format. https://fedorahosted.org/freeipa/ticket/6585 --- ipaclient/plugins/dns.py | 71 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/ipaclient/plugins/dns.py b/ipaclient/plugins/dns.py index 42ccd3d..944546f 100644 --- a/ipaclient/plugins/dns.py +++ b/ipaclient/plugins/dns.py @@ -35,6 +35,7 @@ from ipalib.parameters import Bool, Str from ipalib.plugable import Registry from ipalib import _, ngettext +from ipalib import util from ipapython.dnsutil import DNSName if six.PY3: @@ -417,6 +418,63 @@ def interactive_prompt_callback(self, kw): @register(override=True, no_fail=True) class dns_update_system_records(MethodOverride): + record_groups = ('ipa_records', 'location_records') + + def get_options(self): + for option in super(dns_update_system_records, self).get_options(): + yield option + yield Str( + 'out?', + include='cli', + doc=_('file to store DNS records in nsupdate format') + ) + + def _standard_output(self, textui, result, labels): + """Print output in standard format common across the other plugins""" + for key in self.record_groups: + if result.get(key): + textui.print_indented(u'{}:'.format(labels[key]), indent=1) + for val in sorted(result[key]): + textui.print_indented(val, indent=2) + textui.print_line(u'') + + def _nsupdate_output_file(self, file, result, labels): + """Store data in nsupdate format in file""" + def parse_rname_rtype(record): + """Get rname and rtype from textual representation of record""" + l = record.split(' ', 5) + return l[0], l[3] + + already_removed = set() + for key in self.record_groups: + if result.get(key): + file.write("; {}\n".format(labels[key])) # comment + for val in sorted(result[key]): + # delete old first + r_name_type = parse_rname_rtype(val) + if r_name_type not in already_removed: + # remove it only once + already_removed.add(r_name_type) + file.write("update delete {rname} {rtype}\n".format( + rname=r_name_type[0], rtype=r_name_type[1] + )) + # add new + file.write("update add {}\n".format(val)) + file.write("send\n\n") + + def forward(self, *keys, **options): + # pop `out` before sending to server as it is only client side option + out = options.pop('out', None) + if out: + util.check_writable_file(out) + + res = super(dns_update_system_records, self).forward(*keys, **options) + + if out: + options['out'] = out + + return res + def output_for_cli(self, textui, output, *args, **options): output_super = copy.deepcopy(output) super_res = output_super.get('result', {}) @@ -431,11 +489,12 @@ def output_for_cli(self, textui, output, *args, **options): } result = output.get('result', {}) - for key in ('ipa_records', 'location_records'): - if result.get(key): - textui.print_indented(u'{}:'.format(labels[key]), indent=1) - for val in sorted(result[key]): - textui.print_indented(val, indent=2) - textui.print_line(u'') + + self._standard_output(textui, result, labels) + + out = options.get('out') # output to file + if out: + with open(out, "w") as f: + self._nsupdate_output_file(f, result, labels) return int(not output['value']) From 6da4085429f505d68923a48e38b3e09c10393b09 Mon Sep 17 00:00:00 2001 From: Martin Basti <[email protected]> Date: Mon, 30 Jan 2017 21:18:46 +0100 Subject: [PATCH 2/2] Test: DNS nsupdate from dns-update-system-records Get nsupdate data from dns-update-system-records, remove system records and run nsupdate to verify that all system records were updated https://fedorahosted.org/freeipa/ticket/6585 --- ipatests/test_integration/test_dns_locations.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ipatests/test_integration/test_dns_locations.py b/ipatests/test_integration/test_dns_locations.py index e37e1dd..9bd075c 100644 --- a/ipatests/test_integration/test_dns_locations.py +++ b/ipatests/test_integration/test_dns_locations.py @@ -104,6 +104,31 @@ def test_without_locations(self): for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip): self._test_against_server(ip, domain, expected_servers) + def test_nsupdate_without_locations(self): + """Test nsupdate file generated by dns-update-system-records + Remove all records and the use nsupdate to restore state and test if + all record are there as expected""" + domain = DNSName(self.master.domain.name).make_absolute() + filepath = '/tmp/ipa.nsupdate' + self.master.run_command([ + 'ipa', 'dns-update-system-records', '--dry-run', '--out', filepath + ]) + + # delete original records first + for rname, _port in IPA_DEFAULT_MASTER_SRV_REC: + self.master.run_command([ + 'ipa', 'dnsrecord-del', str(domain), str(rname), '--del-all' + ]) + + # allow unauthenticates nsupdate (no need to testing authentication) + self.master.run_command([ + 'ipa', 'dnszone-mod', str(domain), + '--update-policy=grant * wildcard *;' + ]) + self.master.run_command(['nsupdate', '-g', filepath]) + time.sleep(5) # give time to named to process everything from update + self.test_without_locations() + def test_one_replica_in_location(self): """Put one replica to location and test if records changed properly """
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code
