]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
samba-tool: add dns zoneoptions for aging control
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Mon, 19 Apr 2021 12:07:50 +0000 (00:07 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 21 Apr 2021 10:04:14 +0000 (10:04 +0000)
This adds a subcommand for altering zone parameters.

At the moment the only options are related to record aging (a.k.a
scavenging). The code is structured to make it easy to add more
integer or boolean options, but it is not clear that this would be
useful; many other parameters are not used or would only have
deleterious effects.

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Wed Apr 21 10:04:14 UTC 2021 on sn-devel-184

python/samba/netcmd/dns.py
python/samba/tests/samba_tool/dnscmd.py

index a267c4105b50fd776263cb2ea437c54e5c4ee8ab..11ca90b1f3eca53bd65b82dda88e5c84b19d45b4 100644 (file)
@@ -505,6 +505,94 @@ class cmd_serverinfo(Command):
         print_serverinfo(self.outf, typeid, res)
 
 
+def _add_integer_options(table, takes_options, integer_properties):
+    """Generate options for cmd_zoneoptions"""
+    for k, doc, _min, _max in table:
+        o = '--' + k.lower()
+        opt =  Option(o,
+                      help=f"{doc} [{_min}-{_max}]",
+                      type="int",
+                      dest=k)
+        takes_options.append(opt)
+        integer_properties.append((k, _min, _max, o))
+
+
+class cmd_zoneoptions(Command):
+    """Change zone aging options."""
+
+    synopsis = '%prog <server> <zone> [options]'
+
+    takes_args = ['server', 'zone']
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+    }
+
+    takes_options = [
+        Option('--client-version', help='Client Version',
+               default='longhorn', metavar='w2k|dotnet|longhorn',
+               choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
+    ]
+
+    integer_properties = []
+    # Any zone parameter that is stored as an integer (which is most of
+    # them) can be added to this table. The name should be the dnsp
+    # mixed case name, which will get munged into a lowercase name for
+    # the option. (e.g. "Aging" becomes "--aging").
+    #
+    # Note: just because we add a name here doesn't mean we will use
+    # it.
+    _add_integer_options([
+    #       ( name,   help-string,         min, max )
+            ('Aging', 'Enable record aging', 0, 1),
+            ('NoRefreshInterval',
+             'Aging no refresh interval in hours (0: use default)',
+             0, 10 * 365 * 24),
+            ('RefreshInterval',
+             'Aging refresh interval in hours (0: use default)',
+             0, 10 * 365 * 24),
+            ],
+                         takes_options,
+                         integer_properties)
+
+    def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
+            versionopts=None, **kwargs):
+        self.lp = sambaopts.get_loadparm()
+        self.creds = credopts.get_credentials(self.lp)
+        dns_conn = dns_connect(server, self.lp, self.creds)
+
+        client_version = dns_client_version(cli_ver)
+        nap_type = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM
+
+        for k, _min, _max, o in self.integer_properties:
+            if kwargs.get(k) is None:
+                continue
+            v = kwargs[k]
+            if _min is not None and v < _min:
+                raise CommandError(f"{o} must be at least {_min}")
+            if _max is not None and v > _max:
+                raise CommandError(f"{o} can't exceed {_max}")
+
+            name_param = dnsserver.DNS_RPC_NAME_AND_PARAM()
+            name_param.dwParam = v
+            name_param.pszNodeName = k
+            try:
+                dns_conn.DnssrvOperation2(client_version,
+                                          0,
+                                          server,
+                                          zone,
+                                          0,
+                                          'ResetDwordProperty',
+                                          nap_type,
+                                          name_param)
+            except WERRORError as e:
+                raise CommandError(f"Could not set {k} to {v}") from None
+
+            print(f"Set {k} to {v}", file=self.outf)
+
+
 class cmd_zoneinfo(Command):
     """Query for zone information."""
 
@@ -1065,6 +1153,7 @@ class cmd_dns(SuperCommand):
 
     subcommands = {}
     subcommands['serverinfo'] = cmd_serverinfo()
+    subcommands['zoneoptions'] = cmd_zoneoptions()
     subcommands['zoneinfo'] = cmd_zoneinfo()
     subcommands['zonelist'] = cmd_zonelist()
     subcommands['zonecreate'] = cmd_zonecreate()
index 356b2c46d05e0825118f1fd3590c851379189f21..0048e390ce589a60257dd65955bd35699c2bdfc0 100644 (file)
@@ -17,6 +17,7 @@
 
 import os
 import ldb
+import re
 
 from samba.auth import system_session
 from samba.samdb import SamDB
@@ -910,3 +911,56 @@ class DnsCmdTestCase(SambaToolCmdTest):
                               err,
                               "Failed to print zoneinfo")
         self.assertTrue(out != '')
+
+    def test_zoneoptions(self):
+        for options, vals, error in (
+                (['--aging=1'], {'fAging': 'TRUE'}, False),
+                (['--aging=0'], {'fAging': 'FALSE'}, False),
+                (['--aging=-1'], {'fAging': 'FALSE'}, True),
+                (['--aging=2'], {}, True),
+                (['--aging=2', '--norefreshinterval=1'], {}, True),
+                (['--aging=1', '--norefreshinterval=1'],
+                 {'fAging': 'TRUE', 'dwNoRefreshInterval': '1'}, False),
+                (['--aging=1', '--norefreshinterval=0'],
+                 {'fAging': 'TRUE', 'dwNoRefreshInterval': '0'}, False),
+                (['--aging=0', '--norefreshinterval=99', '--refreshinterval=99'],
+                 {'fAging': 'FALSE',
+                  'dwNoRefreshInterval': '99',
+                  'dwRefreshInterval': '99'}, False),
+                (['--aging=0', '--norefreshinterval=-99', '--refreshinterval=99'],
+                 {}, True),
+                (['--refreshinterval=9999999'], {}, True),
+                (['--norefreshinterval=9999999'], {}, True),
+                ):
+            result, out, err = self.runsubcmd("dns",
+                                              "zoneoptions",
+                                              os.environ["SERVER"],
+                                              self.zone,
+                                              self.creds_string,
+                                              *options)
+            if error:
+                self.assertCmdFail(result, "zoneoptions should fail")
+            else:
+                self.assertCmdSuccess(result,
+                                      out,
+                                      err,
+                                      "zoneoptions shouldn't fail")
+
+
+            info_r, info_out, info_err = self.runsubcmd("dns",
+                                                        "zoneinfo",
+                                                        os.environ["SERVER"],
+                                                        self.zone,
+                                                        self.creds_string)
+
+            self.assertCmdSuccess(info_r,
+                                  info_out,
+                                  info_err,
+                                  "zoneinfo shouldn't fail after zoneoptions")
+
+            info = {k: v for k, v in re.findall(r'^\s*(\w+)\s*:\s*(\w+)\s*$',
+                                                info_out,
+                                                re.MULTILINE)}
+            for k, v in vals.items():
+                self.assertIn(k, info)
+                self.assertEqual(v, info[k])