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 <mba...@redhat.com>
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 <mba...@redhat.com>
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

Reply via email to