mirror of
https://github.com/samba-team/samba.git
synced 2025-01-10 01:18:15 +03:00
samba-tool dns zoneoptions: timestamp manipulation options
There was a bug in Samba before 4.9 that marked all records intended to be static with a current timestamp, and all records intended to be dynamic with a zero timestamp. This was exactly the opposite of correct behaviour. It follows that a domain which has been upgraded past 4.9, but on which aging is not enabled, records intended to be static will have a timestamp from before the upgrade date (unless their nodes have suffered a DNS update, which due to another bug, will change the timestmap). The following command will make these truly static: $ samba-tool dns zoneoptions --mark-old-records-static=2018-07-23 -U... where '2018-07-23' should be replaced by the approximate date of the upgrade beyond 4.9. It seems riskier making blanket conversions of static records into dynamic records, but there are sometimes useful patterns in the names given to machines that we can exploit. For example, if there is a group of machines with names like 'desktop-123' that are all supposed to using dynamic DNS, the adminstrator can go $ samba-tool dns zoneoptions --mark-records-dynamic-regex='desktop-\d+' and there's a --mark-records-static-regex for symmetry. These options are deliberately long and cumbersome to type, so people have a chance to think before they get to the end. We also introduce a '--dry-run' (or '-n') option so they can inspect the likely results before going ahead. *NOTE* ageing will still not work properly after this commit, due to other bugs that will be fixed in other commits. Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
074f9e1486
commit
2f7aa81a9f
@ -23,8 +23,12 @@ from socket import inet_ntop
|
||||
from socket import AF_INET
|
||||
from socket import AF_INET6
|
||||
import struct
|
||||
import time
|
||||
import ldb
|
||||
from samba.ndr import ndr_unpack, ndr_pack
|
||||
import re
|
||||
|
||||
from samba import remove_dc
|
||||
from samba import remove_dc, dsdb_dns
|
||||
from samba.samdb import SamDB
|
||||
from samba.auth import system_session
|
||||
|
||||
@ -452,6 +456,14 @@ class cmd_zoneoptions(Command):
|
||||
Option('--client-version', help='Client Version',
|
||||
default='longhorn', metavar='w2k|dotnet|longhorn',
|
||||
choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
|
||||
Option('--mark-old-records-static',
|
||||
help="Make records older than this (YYYY-MM-DD) static"),
|
||||
Option('--mark-records-static-regex', metavar="REGEXP",
|
||||
help="Make records matching this regular expression static"),
|
||||
Option('--mark-records-dynamic-regex', metavar="REGEXP",
|
||||
help="Make records matching this regular expression dynamic"),
|
||||
Option('-n', '--dry-run', action='store_true',
|
||||
help="Don't change anything, say what would happen"),
|
||||
]
|
||||
|
||||
integer_properties = []
|
||||
@ -476,7 +488,11 @@ class cmd_zoneoptions(Command):
|
||||
integer_properties)
|
||||
|
||||
def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
|
||||
versionopts=None, **kwargs):
|
||||
versionopts=None, dry_run=False,
|
||||
mark_old_records_static=None,
|
||||
mark_records_static_regex=None,
|
||||
mark_records_dynamic_regex=None,
|
||||
**kwargs):
|
||||
self.lp = sambaopts.get_loadparm()
|
||||
self.creds = credopts.get_credentials(self.lp)
|
||||
dns_conn = dns_connect(server, self.lp, self.creds)
|
||||
@ -496,6 +512,9 @@ class cmd_zoneoptions(Command):
|
||||
name_param = dnsserver.DNS_RPC_NAME_AND_PARAM()
|
||||
name_param.dwParam = v
|
||||
name_param.pszNodeName = k
|
||||
if dry_run:
|
||||
print(f"would set {k} to {v} for {zone}", file=self.outf)
|
||||
continue
|
||||
try:
|
||||
dns_conn.DnssrvOperation2(client_version,
|
||||
0,
|
||||
@ -510,6 +529,187 @@ class cmd_zoneoptions(Command):
|
||||
|
||||
print(f"Set {k} to {v}", file=self.outf)
|
||||
|
||||
# We don't want to allow more than one of these --mark-*
|
||||
# options at a time, as they are sensitive to ordering and
|
||||
# the order is not documented.
|
||||
n_mark_options = 0
|
||||
for x in (mark_old_records_static,
|
||||
mark_records_static_regex,
|
||||
mark_records_dynamic_regex):
|
||||
if x is not None:
|
||||
n_mark_options += 1
|
||||
|
||||
if n_mark_options > 1:
|
||||
raise CommandError("Multiple --mark-* options will not work\n")
|
||||
|
||||
if mark_old_records_static is not None:
|
||||
self.mark_old_records_static(server, zone,
|
||||
mark_old_records_static,
|
||||
dry_run)
|
||||
|
||||
if mark_records_static_regex is not None:
|
||||
self.mark_records_static_regex(server,
|
||||
zone,
|
||||
mark_records_static_regex,
|
||||
dry_run)
|
||||
|
||||
if mark_records_dynamic_regex is not None:
|
||||
self.mark_records_dynamic_regex(server,
|
||||
zone,
|
||||
mark_records_dynamic_regex,
|
||||
dry_run)
|
||||
|
||||
|
||||
def _get_dns_nodes(self, server, zone_name):
|
||||
samdb = SamDB(url="ldap://%s" % server,
|
||||
session_info=system_session(),
|
||||
credentials=self.creds, lp=self.lp)
|
||||
|
||||
zone_dn = (f"DC={zone_name},CN=MicrosoftDNS,DC=DomainDNSZones,"
|
||||
f"{samdb.get_default_basedn()}")
|
||||
|
||||
nodes = samdb.search(base=zone_dn,
|
||||
scope=ldb.SCOPE_SUBTREE,
|
||||
expression=("(&(objectClass=dnsNode)"
|
||||
"(!(dNSTombstoned=TRUE)))"),
|
||||
attrs=["dnsRecord", "name"])
|
||||
return samdb, nodes
|
||||
|
||||
def mark_old_records_static(self, server, zone_name, date_string, dry_run):
|
||||
try:
|
||||
ts = time.strptime(date_string, "%Y-%m-%d")
|
||||
t = time.mktime(ts)
|
||||
except ValueError as e:
|
||||
raise CommandError(f"Invalid date {date_string}: should be YYY-MM-DD")
|
||||
threshold = dsdb_dns.unix_to_dns_timestamp(int(t))
|
||||
|
||||
samdb, nodes = self._get_dns_nodes(server, zone_name)
|
||||
|
||||
for node in nodes:
|
||||
if "dnsRecord" not in node:
|
||||
continue
|
||||
|
||||
values = list(node["dnsRecord"])
|
||||
changes = 0
|
||||
for i, v in enumerate(values):
|
||||
rec = ndr_unpack(dnsp.DnssrvRpcRecord, v)
|
||||
if rec.dwTimeStamp < threshold and rec.dwTimeStamp != 0:
|
||||
rec.dwTimeStamp = 0
|
||||
values[i] = ndr_pack(rec)
|
||||
changes += 1
|
||||
|
||||
if changes == 0:
|
||||
continue
|
||||
|
||||
name = node["name"][0].decode()
|
||||
|
||||
if dry_run:
|
||||
print(f"would make {changes}/{len(values)} records static "
|
||||
f"on {name}.{zone_name}.", file=self.outf)
|
||||
continue
|
||||
|
||||
msg = ldb.Message.from_dict(samdb,
|
||||
{'dn': node.dn,
|
||||
'dnsRecord': values
|
||||
},
|
||||
ldb.FLAG_MOD_REPLACE)
|
||||
samdb.modify(msg)
|
||||
print(f"made {changes}/{len(values)} records static on "
|
||||
f"{name}.{zone_name}.", file=self.outf)
|
||||
|
||||
def mark_records_static_regex(self, server, zone_name, regex, dry_run):
|
||||
"""Make the records of nodes with matching names static.
|
||||
"""
|
||||
r = re.compile(regex)
|
||||
samdb, nodes = self._get_dns_nodes(server, zone_name)
|
||||
|
||||
for node in nodes:
|
||||
name = node["name"][0].decode()
|
||||
if not r.search(name):
|
||||
continue
|
||||
if "dnsRecord" not in node:
|
||||
continue
|
||||
|
||||
values = list(node["dnsRecord"])
|
||||
if len(values) == 0:
|
||||
continue
|
||||
|
||||
changes = 0
|
||||
for i, v in enumerate(values):
|
||||
rec = ndr_unpack(dnsp.DnssrvRpcRecord, v)
|
||||
if rec.dwTimeStamp != 0:
|
||||
rec.dwTimeStamp = 0
|
||||
values[i] = ndr_pack(rec)
|
||||
changes += 1
|
||||
|
||||
if changes == 0:
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
print(f"would make {changes}/{len(values)} records static "
|
||||
f"on {name}.{zone_name}.", file=self.outf)
|
||||
continue
|
||||
|
||||
msg = ldb.Message.from_dict(samdb,
|
||||
{'dn': node.dn,
|
||||
'dnsRecord': values
|
||||
},
|
||||
ldb.FLAG_MOD_REPLACE)
|
||||
samdb.modify(msg)
|
||||
print(f"made {changes}/{len(values)} records static on "
|
||||
f"{name}.{zone_name}.", file=self.outf)
|
||||
|
||||
def mark_records_dynamic_regex(self, server, zone_name, regex, dry_run):
|
||||
"""Make the records of nodes with matching names dynamic, with a
|
||||
current timestamp. In this case we only adjust the A, AAAA,
|
||||
and TXT records.
|
||||
"""
|
||||
r = re.compile(regex)
|
||||
samdb, nodes = self._get_dns_nodes(server, zone_name)
|
||||
now = time.time()
|
||||
dns_timestamp = dsdb_dns.unix_to_dns_timestamp(int(now))
|
||||
safe_wtypes = {
|
||||
dnsp.DNS_TYPE_A,
|
||||
dnsp.DNS_TYPE_AAAA,
|
||||
dnsp.DNS_TYPE_TXT
|
||||
}
|
||||
|
||||
for node in nodes:
|
||||
name = node["name"][0].decode()
|
||||
if not r.search(name):
|
||||
continue
|
||||
if "dnsRecord" not in node:
|
||||
continue
|
||||
|
||||
values = list(node["dnsRecord"])
|
||||
if len(values) == 0:
|
||||
continue
|
||||
|
||||
changes = 0
|
||||
for i, v in enumerate(values):
|
||||
rec = ndr_unpack(dnsp.DnssrvRpcRecord, v)
|
||||
if rec.wType in safe_wtypes and rec.dwTimeStamp == 0:
|
||||
rec.dwTimeStamp = dns_timestamp
|
||||
values[i] = ndr_pack(rec)
|
||||
changes += 1
|
||||
|
||||
if changes == 0:
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
print(f"would make {changes}/{len(values)} records dynamic "
|
||||
f"on {name}.{zone_name}.", file=self.outf)
|
||||
continue
|
||||
|
||||
msg = ldb.Message.from_dict(samdb,
|
||||
{'dn': node.dn,
|
||||
'dnsRecord': values
|
||||
},
|
||||
ldb.FLAG_MOD_REPLACE)
|
||||
samdb.modify(msg)
|
||||
print(f"made {changes}/{len(values)} records dynamic on "
|
||||
f"{name}.{zone_name}.", file=self.outf)
|
||||
|
||||
|
||||
class cmd_zoneinfo(Command):
|
||||
"""Query for zone information."""
|
||||
|
@ -24,6 +24,8 @@ from samba.samdb import SamDB
|
||||
from samba.ndr import ndr_unpack, ndr_pack
|
||||
from samba.dcerpc import dnsp
|
||||
from samba.tests.samba_tool.base import SambaToolCmdTest
|
||||
import time
|
||||
from samba import dsdb_dns
|
||||
|
||||
|
||||
class DnsCmdTestCase(SambaToolCmdTest):
|
||||
@ -137,6 +139,23 @@ class DnsCmdTestCase(SambaToolCmdTest):
|
||||
self.creds_string)
|
||||
self.assertCmdSuccess(result, out, err)
|
||||
|
||||
def get_all_records(self, zone_name):
|
||||
zone_dn = (f"DC={zone_name},CN=MicrosoftDNS,DC=DomainDNSZones,"
|
||||
f"{self.samdb.get_default_basedn()}")
|
||||
|
||||
expression = "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))"
|
||||
|
||||
nodes = self.samdb.search(base=zone_dn, scope=ldb.SCOPE_SUBTREE,
|
||||
expression=expression,
|
||||
attrs=["dnsRecord", "name"])
|
||||
|
||||
record_map = {}
|
||||
for node in nodes:
|
||||
name = node["name"][0].decode()
|
||||
record_map[name] = list(node["dnsRecord"])
|
||||
|
||||
return record_map
|
||||
|
||||
def get_record_from_db(self, zone_name, record_name):
|
||||
zones = self.samdb.search(base="DC=DomainDnsZones,%s"
|
||||
% self.samdb.get_default_basedn(),
|
||||
@ -909,7 +928,7 @@ class DnsCmdTestCase(SambaToolCmdTest):
|
||||
"Failed to print zoneinfo")
|
||||
self.assertTrue(out != '')
|
||||
|
||||
def test_zoneoptions(self):
|
||||
def test_zoneoptions_aging(self):
|
||||
for options, vals, error in (
|
||||
(['--aging=1'], {'fAging': 'TRUE'}, False),
|
||||
(['--aging=0'], {'fAging': 'FALSE'}, False),
|
||||
@ -961,3 +980,430 @@ class DnsCmdTestCase(SambaToolCmdTest):
|
||||
for k, v in vals.items():
|
||||
self.assertIn(k, info)
|
||||
self.assertEqual(v, info[k])
|
||||
|
||||
|
||||
def ldap_add_node_with_records(self, name, records):
|
||||
dn = (f"DC={name},DC={self.zone},CN=MicrosoftDNS,DC=DomainDNSZones,"
|
||||
f"{self.samdb.get_default_basedn()}")
|
||||
|
||||
dns_records = []
|
||||
for r in records:
|
||||
rec = dnsp.DnssrvRpcRecord()
|
||||
rec.wType = r.get('wType', dnsp.DNS_TYPE_A)
|
||||
rec.rank = dnsp.DNS_RANK_ZONE
|
||||
rec.dwTtlSeconds = 900
|
||||
rec.dwTimeStamp = r.get('dwTimeStamp', 0)
|
||||
rec.data = r.get('data', '10.10.10.10')
|
||||
dns_records.append(ndr_pack(rec))
|
||||
|
||||
msg = ldb.Message.from_dict(self.samdb,
|
||||
{'dn': dn,
|
||||
"objectClass": ["top", "dnsNode"],
|
||||
'dnsRecord': dns_records
|
||||
})
|
||||
self.samdb.add(msg)
|
||||
|
||||
def get_timestamp_map(self):
|
||||
re_wtypes = (dnsp.DNS_TYPE_A,
|
||||
dnsp.DNS_TYPE_AAAA,
|
||||
dnsp.DNS_TYPE_TXT)
|
||||
|
||||
t = time.time()
|
||||
now = dsdb_dns.unix_to_dns_timestamp(int(t))
|
||||
|
||||
records = self.get_all_records(self.zone)
|
||||
tsmap = {}
|
||||
for k, recs in records.items():
|
||||
m = []
|
||||
tsmap[k] = m
|
||||
for rec in recs:
|
||||
r = ndr_unpack(dnsp.DnssrvRpcRecord, rec)
|
||||
timestamp = r.dwTimeStamp
|
||||
if abs(timestamp - now) < 3:
|
||||
timestamp = 'nowish'
|
||||
|
||||
if r.wType in re_wtypes:
|
||||
m.append(('R', timestamp))
|
||||
else:
|
||||
m.append(('-', timestamp))
|
||||
|
||||
return tsmap
|
||||
|
||||
|
||||
def test_zoneoptions_mark_records(self):
|
||||
self.maxDiff = 10000
|
||||
# We need a number of records to work with, so we'll use part
|
||||
# of our known good records list, using three different names
|
||||
# to test the regex. All these records will be static.
|
||||
for dnstype in self.good_records:
|
||||
for record in self.good_records[dnstype][:2]:
|
||||
self.runsubcmd("dns", "add",
|
||||
os.environ["SERVER"],
|
||||
self.zone, "frobitz",
|
||||
dnstype, record,
|
||||
self.creds_string)
|
||||
self.runsubcmd("dns", "add",
|
||||
os.environ["SERVER"],
|
||||
self.zone, "weergly",
|
||||
dnstype, record,
|
||||
self.creds_string)
|
||||
self.runsubcmd("dns", "add",
|
||||
os.environ["SERVER"],
|
||||
self.zone, "snizle",
|
||||
dnstype, record,
|
||||
self.creds_string)
|
||||
|
||||
# and we also want some that aren't static, and some mixed
|
||||
# static/dynamic records.
|
||||
# timestamps are in hours since 1601; now ~= 3.7 million
|
||||
for ts in (0, 100, 10 ** 6, 10 ** 7):
|
||||
name = f"ts-{ts}"
|
||||
self.ldap_add_node_with_records(name, [{"dwTimeStamp": ts}])
|
||||
|
||||
recs = []
|
||||
for ts in (0, 100, 10 ** 6, 10 ** 7):
|
||||
addr = f'10.{(ts >> 16) & 255}.{(ts >> 8) & 255}.{ts & 255}'
|
||||
recs.append({"dwTimeStamp": ts, "data": addr})
|
||||
|
||||
self.ldap_add_node_with_records("ts-multi", recs)
|
||||
|
||||
# get the state of ALL records.
|
||||
# then we make assertions about the diffs, keeping track of
|
||||
# the current state.
|
||||
|
||||
tsmap = self.get_timestamp_map()
|
||||
|
||||
|
||||
|
||||
for options, diff, output_substrings, error in (
|
||||
# --mark-old-records-static
|
||||
# --mark-records-static-regex
|
||||
# --mark-records-dynamic-regex
|
||||
(
|
||||
['--mark-old-records-static=1971-13-04'],
|
||||
{},
|
||||
[],
|
||||
"bad date"
|
||||
),
|
||||
(
|
||||
# using --dry-run, should be no change, but output.
|
||||
['--mark-old-records-static=1971-03-04', '--dry-run'],
|
||||
{},
|
||||
[
|
||||
"would make 1/1 records static on ts-1000000.zone.",
|
||||
"would make 1/1 records static on ts-100.zone.",
|
||||
"would make 2/4 records static on ts-multi.zone.",
|
||||
],
|
||||
False
|
||||
),
|
||||
(
|
||||
# timestamps < ~ 3.25 million are now static
|
||||
['--mark-old-records-static=1971-03-04'],
|
||||
{
|
||||
'ts-100': [('R', 0)],
|
||||
'ts-1000000': [('R', 0)],
|
||||
'ts-multi': [('R', 0), ('R', 0), ('R', 0), ('R', 10000000)]
|
||||
},
|
||||
[
|
||||
"made 1/1 records static on ts-1000000.zone.",
|
||||
"made 1/1 records static on ts-100.zone.",
|
||||
"made 2/4 records static on ts-multi.zone.",
|
||||
],
|
||||
False
|
||||
),
|
||||
(
|
||||
# no change, old records already static
|
||||
['--mark-old-records-static=1972-03-04'],
|
||||
{},
|
||||
[],
|
||||
False
|
||||
),
|
||||
(
|
||||
# no change, samba-tool added records already static
|
||||
['--mark-records-static-regex=sniz'],
|
||||
{},
|
||||
[],
|
||||
False
|
||||
),
|
||||
(
|
||||
# snizle has 2 A, 2 AAAA, 10 fancy, and 2 TXT records, in
|
||||
# that order.
|
||||
# the A, AAAA, and TXT recrods should be dynamic
|
||||
['--mark-records-dynamic-regex=sniz'],
|
||||
{'snizle': [('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish')]
|
||||
},
|
||||
['made 6/16 records dynamic on snizle.zone.'],
|
||||
False
|
||||
),
|
||||
(
|
||||
# This regex should catch snizle, weergly, and ts-*
|
||||
# but we're doing dry-run so no change
|
||||
['--mark-records-dynamic-regex=[sw]', '-n'],
|
||||
{},
|
||||
['would make 3/4 records dynamic on ts-multi.zone.',
|
||||
'would make 1/1 records dynamic on ts-0.zone.',
|
||||
'would make 1/1 records dynamic on ts-1000000.zone.',
|
||||
'would make 6/16 records dynamic on weergly.zone.',
|
||||
'would make 1/1 records dynamic on ts-100.zone.'
|
||||
],
|
||||
False
|
||||
),
|
||||
(
|
||||
# This regex should catch snizle and frobitz
|
||||
# but snizle has already been changed.
|
||||
['--mark-records-dynamic-regex=z'],
|
||||
{'frobitz': [('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish')]
|
||||
},
|
||||
['made 6/16 records dynamic on frobitz.zone.'],
|
||||
False
|
||||
),
|
||||
(
|
||||
# This regex should catch snizle, frobitz, and
|
||||
# ts-multi. Note that the 1e7 ts-multi record is
|
||||
# alreay dynamic and doesn't change.
|
||||
['--mark-records-dynamic-regex=[i]'],
|
||||
{'ts-multi': [('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 10000000)]
|
||||
},
|
||||
['made 3/4 records dynamic on ts-multi.zone.'],
|
||||
False
|
||||
),
|
||||
(
|
||||
# matches no records
|
||||
['--mark-records-dynamic-regex=^aloooooo[qw]+'],
|
||||
{},
|
||||
[],
|
||||
False
|
||||
),
|
||||
(
|
||||
# This should be an error, as only one --mark-*
|
||||
# argument is allowed at a time
|
||||
['--mark-records-dynamic-regex=.',
|
||||
'--mark-records-static-regex=.',
|
||||
],
|
||||
{},
|
||||
[],
|
||||
True
|
||||
),
|
||||
(
|
||||
# This should also be an error
|
||||
['--mark-old-records-static=1997-07-07',
|
||||
'--mark-records-static-regex=.',
|
||||
],
|
||||
{},
|
||||
[],
|
||||
True
|
||||
),
|
||||
(
|
||||
# This should not be an error. --aging and refresh
|
||||
# options can be mixed with --mark ones.
|
||||
['--mark-old-records-static=1997-07-07',
|
||||
'--aging=0',
|
||||
],
|
||||
{},
|
||||
['Set Aging to 0'],
|
||||
False
|
||||
),
|
||||
(
|
||||
# This regex should catch weergly, but all the
|
||||
# records are already static,
|
||||
['--mark-records-static-regex=wee'],
|
||||
{},
|
||||
[],
|
||||
False
|
||||
),
|
||||
(
|
||||
# Make frobitz static again.
|
||||
['--mark-records-static-regex=obi'],
|
||||
{'frobitz': [('R', 0),
|
||||
('R', 0),
|
||||
('R', 0),
|
||||
('R', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('R', 0),
|
||||
('R', 0)]
|
||||
},
|
||||
['made 6/16 records static on frobitz.zone.'],
|
||||
False
|
||||
),
|
||||
(
|
||||
# would make almost everything static, but --dry-run
|
||||
['--mark-old-records-static=2222-03-04', '--dry-run'],
|
||||
{},
|
||||
[
|
||||
'would make 6/16 records static on snizle.zone.',
|
||||
'would make 3/4 records static on ts-multi.zone.'
|
||||
],
|
||||
False
|
||||
),
|
||||
(
|
||||
# make everything static
|
||||
['--mark-records-static-regex=.'],
|
||||
{'snizle': [('R', 0),
|
||||
('R', 0),
|
||||
('R', 0),
|
||||
('R', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('R', 0),
|
||||
('R', 0)],
|
||||
'ts-10000000': [('R', 0)],
|
||||
'ts-multi': [('R', 0), ('R', 0), ('R', 0), ('R', 0)]
|
||||
},
|
||||
[
|
||||
'made 4/4 records static on ts-multi.zone.',
|
||||
'made 1/1 records static on ts-10000000.zone.',
|
||||
'made 6/16 records static on snizle.zone.',
|
||||
],
|
||||
False
|
||||
),
|
||||
(
|
||||
# make everything dynamic that can be
|
||||
['--mark-records-dynamic-regex=.'],
|
||||
{'frobitz': [('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish')],
|
||||
'snizle': [('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish')],
|
||||
'ts-0': [('R', 'nowish')],
|
||||
'ts-100': [('R', 'nowish')],
|
||||
'ts-1000000': [('R', 'nowish')],
|
||||
'ts-10000000': [('R', 'nowish')],
|
||||
'ts-multi': [('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish')],
|
||||
'weergly': [('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish'),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('-', 0),
|
||||
('R', 'nowish'),
|
||||
('R', 'nowish')]
|
||||
},
|
||||
[
|
||||
'made 4/4 records dynamic on ts-multi.zone.',
|
||||
'made 6/16 records dynamic on snizle.zone.',
|
||||
'made 1/1 records dynamic on ts-0.zone.',
|
||||
'made 1/1 records dynamic on ts-1000000.zone.',
|
||||
'made 1/1 records dynamic on ts-10000000.zone.',
|
||||
'made 1/1 records dynamic on ts-100.zone.',
|
||||
'made 6/16 records dynamic on frobitz.zone.',
|
||||
'made 6/16 records dynamic on weergly.zone.',
|
||||
],
|
||||
False
|
||||
),
|
||||
):
|
||||
result, out, err = self.runsubcmd("dns",
|
||||
"zoneoptions",
|
||||
os.environ["SERVER"],
|
||||
self.zone,
|
||||
self.creds_string,
|
||||
*options)
|
||||
if error:
|
||||
self.assertCmdFail(result, f"zoneoptions should fail ({error})")
|
||||
else:
|
||||
self.assertCmdSuccess(result,
|
||||
out,
|
||||
err,
|
||||
"zoneoptions shouldn't fail")
|
||||
|
||||
new_tsmap = self.get_timestamp_map()
|
||||
|
||||
# same keys, always
|
||||
self.assertEqual(sorted(new_tsmap), sorted(tsmap))
|
||||
changes = {}
|
||||
for k in tsmap:
|
||||
if tsmap[k] != new_tsmap[k]:
|
||||
changes[k] = new_tsmap[k]
|
||||
|
||||
self.assertEqual(diff, changes)
|
||||
|
||||
for s in output_substrings:
|
||||
self.assertIn(s, out)
|
||||
tsmap = new_tsmap
|
||||
|
Loading…
Reference in New Issue
Block a user