ctdb-scripts: Improve update and listing code
[samba4-gss.git] / python / samba / tests / samba_tool / dnscmd.py
blobd372bc5851e601cba2fca307e0bdd2cee50952fa
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Andrew Bartlett <abartlet@catalyst.net.nz>
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import os
19 import ldb
20 import re
22 from samba.ndr import ndr_unpack, ndr_pack
23 from samba.dcerpc import dnsp
24 from samba.tests.samba_tool.base import SambaToolCmdTest
25 import time
26 from samba import dsdb_dns
29 class DnsCmdTestCase(SambaToolCmdTest):
30 def setUp(self):
31 super().setUp()
33 self.dburl = "ldap://%s" % os.environ["SERVER"]
34 self.creds_string = "-U%s%%%s" % (os.environ["DC_USERNAME"],
35 os.environ["DC_PASSWORD"])
37 self.samdb = self.getSamDB("-H", self.dburl, self.creds_string)
38 self.config_dn = str(self.samdb.get_config_basedn())
40 self.testip = "192.168.0.193"
41 self.testip2 = "192.168.0.194"
43 self.addCleanup(self.deleteZone)
44 self.addZone()
46 # Note: SOA types don't work (and shouldn't), as we only have one zone per DNS record.
48 good_dns = ["SAMDOM.EXAMPLE.COM",
49 "1.EXAMPLE.COM",
50 "%sEXAMPLE.COM" % ("1." * 100),
51 "EXAMPLE",
52 "!@#$%^&*()_",
53 "HIGH\xFFBYTE",
54 "@.EXAMPLE.COM",
55 "."]
56 bad_dns = ["...",
57 ".EXAMPLE.COM",
58 ".EXAMPLE.",
59 "",
60 "SAMDOM..EXAMPLE.COM"]
62 good_mx = ["SAMDOM.EXAMPLE.COM 65530",
63 "SAMDOM.EXAMPLE.COM 0"]
64 bad_mx = ["SAMDOM.EXAMPLE.COM -1",
65 "SAMDOM.EXAMPLE.COM",
66 " ",
67 "SAMDOM.EXAMPLE.COM 1 1",
68 "SAMDOM.EXAMPLE.COM SAMDOM.EXAMPLE.COM"]
70 good_srv = ["SAMDOM.EXAMPLE.COM 65530 65530 65530",
71 "SAMDOM.EXAMPLE.COM 1 1 1"]
72 bad_srv = ["SAMDOM.EXAMPLE.COM 0 65536 0",
73 "SAMDOM.EXAMPLE.COM 0 0 65536",
74 "SAMDOM.EXAMPLE.COM 65536 0 0"]
76 for bad_dn in bad_dns:
77 bad_mx.append("%s 1" % bad_dn)
78 bad_srv.append("%s 0 0 0" % bad_dn)
79 for good_dn in good_dns:
80 good_mx.append("%s 1" % good_dn)
81 good_srv.append("%s 0 0 0" % good_dn)
83 self.good_records = {
84 "A":["192.168.0.1", "255.255.255.255"],
85 "AAAA":["1234:5678:9ABC:DEF0:0000:0000:0000:0000",
86 "0000:0000:0000:0000:0000:0000:0000:0000",
87 "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0",
88 "1234:1234:1234::",
89 "1234:5678:9ABC:DEF0::",
90 "0000:0000::0000",
91 "1234::5678:9ABC:0000:0000:0000:0000",
92 "::1",
93 "::",
94 "1:1:1:1:1:1:1:1"],
95 "PTR": good_dns,
96 "CNAME": good_dns,
97 "NS": good_dns,
98 "MX": good_mx,
99 "SRV": good_srv,
100 "TXT": ["text", "", "@#!", "\n"]
103 self.bad_records = {
104 "A":["192.168.0.500",
105 "255.255.255.255/32"],
106 "AAAA":["GGGG:1234:5678:9ABC:0000:0000:0000:0000",
107 "0000:0000:0000:0000:0000:0000:0000:0000/1",
108 "AAAA:AAAA:AAAA:AAAA:G000:0000:0000:1234",
109 "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0:1234",
110 "1234:5678:9ABC:DEF0:1234:5678:9ABC",
111 "1111::1111::1111"],
112 "PTR": bad_dns,
113 "CNAME": bad_dns,
114 "NS": bad_dns,
115 "MX": bad_mx,
116 "SRV": bad_srv
119 def resetZone(self):
120 self.deleteZone()
121 self.addZone()
123 def addZone(self):
124 self.zone = "zone"
125 result, out, err = self.runsubcmd("dns",
126 "zonecreate",
127 os.environ["SERVER"],
128 self.zone,
129 self.creds_string)
130 self.assertCmdSuccess(result, out, err)
132 def deleteZone(self):
133 result, out, err = self.runsubcmd("dns",
134 "zonedelete",
135 os.environ["SERVER"],
136 self.zone,
137 self.creds_string)
138 self.assertCmdSuccess(result, out, err)
140 def get_all_records(self, zone_name):
141 zone_dn = (f"DC={zone_name},CN=MicrosoftDNS,DC=DomainDNSZones,"
142 f"{self.samdb.get_default_basedn()}")
144 expression = "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))"
146 nodes = self.samdb.search(base=zone_dn, scope=ldb.SCOPE_SUBTREE,
147 expression=expression,
148 attrs=["dnsRecord", "name"])
150 record_map = {}
151 for node in nodes:
152 name = node["name"][0].decode()
153 record_map[name] = list(node["dnsRecord"])
155 return record_map
157 def get_record_from_db(self, zone_name, record_name):
158 zones = self.samdb.search(base="DC=DomainDnsZones,%s"
159 % self.samdb.get_default_basedn(),
160 scope=ldb.SCOPE_SUBTREE,
161 expression="(objectClass=dnsZone)",
162 attrs=["cn"])
164 for zone in zones:
165 if zone_name in str(zone.dn):
166 zone_dn = zone.dn
167 break
169 records = self.samdb.search(base=zone_dn, scope=ldb.SCOPE_SUBTREE,
170 expression="(objectClass=dnsNode)",
171 attrs=["dnsRecord"])
173 for old_packed_record in records:
174 if record_name in str(old_packed_record.dn):
175 return (old_packed_record.dn,
176 ndr_unpack(dnsp.DnssrvRpcRecord,
177 old_packed_record["dnsRecord"][0]))
179 def test_rank_none(self):
180 record_str = "192.168.50.50"
181 record_type_str = "A"
183 result, out, err = self.runsubcmd("dns", "add", os.environ["SERVER"],
184 self.zone, "testrecord", record_type_str,
185 record_str, self.creds_string)
186 self.assertCmdSuccess(result, out, err,
187 "Failed to add record '%s' with type %s."
188 % (record_str, record_type_str))
190 dn, record = self.get_record_from_db(self.zone, "testrecord")
191 record.rank = 0 # DNS_RANK_NONE
192 res = self.samdb.dns_replace_by_dn(dn, [record])
193 if res is not None:
194 self.fail("Unable to update dns record to have DNS_RANK_NONE.")
196 errors = []
198 # The record should still exist
199 result, out, err = self.runsubcmd("dns", "query", os.environ["SERVER"],
200 self.zone, "testrecord", record_type_str,
201 self.creds_string)
202 try:
203 self.assertCmdSuccess(result, out, err,
204 "Failed to query for a record"
205 "which had DNS_RANK_NONE.")
206 self.assertTrue("testrecord" in out and record_str in out,
207 "Query for a record which had DNS_RANK_NONE"
208 "succeeded but produced no resulting records.")
209 except AssertionError:
210 # Windows produces no resulting records
211 pass
213 # We should not be able to add a duplicate
214 result, out, err = self.runsubcmd("dns", "add", os.environ["SERVER"],
215 self.zone, "testrecord", record_type_str,
216 record_str, self.creds_string)
217 try:
218 self.assertCmdFail(result, "Successfully added duplicate record"
219 "of one which had DNS_RANK_NONE.")
220 except AssertionError as e:
221 errors.append(e)
223 # We should be able to delete it
224 result, out, err = self.runsubcmd("dns", "delete", os.environ["SERVER"],
225 self.zone, "testrecord", record_type_str,
226 record_str, self.creds_string)
227 try:
228 self.assertCmdSuccess(result, out, err, "Failed to delete record"
229 "which had DNS_RANK_NONE.")
230 except AssertionError as e:
231 errors.append(e)
233 # Now the record should not exist
234 result, out, err = self.runsubcmd("dns", "query", os.environ["SERVER"],
235 self.zone, "testrecord",
236 record_type_str, self.creds_string)
237 try:
238 self.assertCmdFail(result, "Successfully queried for deleted record"
239 "which had DNS_RANK_NONE.")
240 except AssertionError as e:
241 errors.append(e)
243 if len(errors) > 0:
244 err_str = "Failed appropriate behaviour with DNS_RANK_NONE:"
245 for error in errors:
246 err_str = err_str + "\n" + str(error)
247 raise AssertionError(err_str)
249 def test_accept_valid_commands(self):
251 For all good records, attempt to add, query and delete them.
253 num_failures = 0
254 failure_msgs = []
255 for dnstype in self.good_records:
256 for record in self.good_records[dnstype]:
257 try:
258 result, out, err = self.runsubcmd("dns", "add",
259 os.environ["SERVER"],
260 self.zone, "testrecord",
261 dnstype, record,
262 self.creds_string)
263 self.assertCmdSuccess(result, out, err, "Failed to add"
264 "record %s with type %s."
265 % (record, dnstype))
267 result, out, err = self.runsubcmd("dns", "query",
268 os.environ["SERVER"],
269 self.zone, "testrecord",
270 dnstype,
271 self.creds_string)
272 self.assertCmdSuccess(result, out, err, "Failed to query"
273 "record %s with qualifier %s."
274 % (record, dnstype))
276 result, out, err = self.runsubcmd("dns", "delete",
277 os.environ["SERVER"],
278 self.zone, "testrecord",
279 dnstype, record,
280 self.creds_string)
281 self.assertCmdSuccess(result, out, err, "Failed to remove"
282 "record %s with type %s."
283 % (record, dnstype))
284 except AssertionError as e:
285 num_failures = num_failures + 1
286 failure_msgs.append(e)
288 if num_failures > 0:
289 for msg in failure_msgs:
290 print(msg)
291 self.fail("Failed to accept valid commands. %d total failures."
292 "Errors above." % num_failures)
294 def test_reject_invalid_commands(self):
296 For all bad records, attempt to add them and update to them,
297 making sure that both operations fail.
299 num_failures = 0
300 failure_msgs = []
302 # Add invalid records and make sure they fail to be added
303 for dnstype in self.bad_records:
304 for record in self.bad_records[dnstype]:
305 try:
306 result, out, err = self.runsubcmd("dns", "add",
307 os.environ["SERVER"],
308 self.zone, "testrecord",
309 dnstype, record,
310 self.creds_string)
311 self.assertCmdFail(result, "Successfully added invalid"
312 "record '%s' of type '%s'."
313 % (record, dnstype))
314 except AssertionError as e:
315 num_failures = num_failures + 1
316 failure_msgs.append(e)
317 self.resetZone()
318 try:
319 result, out, err = self.runsubcmd("dns", "delete",
320 os.environ["SERVER"],
321 self.zone, "testrecord",
322 dnstype, record,
323 self.creds_string)
324 self.assertCmdFail(result, "Successfully deleted invalid"
325 "record '%s' of type '%s' which"
326 "shouldn't exist." % (record, dnstype))
327 except AssertionError as e:
328 num_failures = num_failures + 1
329 failure_msgs.append(e)
330 self.resetZone()
332 # Update valid records to invalid ones and make sure they
333 # fail to be updated
334 for dnstype in self.bad_records:
335 for bad_record in self.bad_records[dnstype]:
336 good_record = self.good_records[dnstype][0]
338 try:
339 result, out, err = self.runsubcmd("dns", "add",
340 os.environ["SERVER"],
341 self.zone, "testrecord",
342 dnstype, good_record,
343 self.creds_string)
344 self.assertCmdSuccess(result, out, err, "Failed to add "
345 "record '%s' with type %s."
346 % (record, dnstype))
348 result, out, err = self.runsubcmd("dns", "update",
349 os.environ["SERVER"],
350 self.zone, "testrecord",
351 dnstype, good_record,
352 bad_record,
353 self.creds_string)
354 self.assertCmdFail(result, "Successfully updated valid "
355 "record '%s' of type '%s' to invalid "
356 "record '%s' of the same type."
357 % (good_record, dnstype, bad_record))
359 result, out, err = self.runsubcmd("dns", "delete",
360 os.environ["SERVER"],
361 self.zone, "testrecord",
362 dnstype, good_record,
363 self.creds_string)
364 self.assertCmdSuccess(result, out, err, "Could not delete "
365 "valid record '%s' of type '%s'."
366 % (good_record, dnstype))
367 except AssertionError as e:
368 num_failures = num_failures + 1
369 failure_msgs.append(e)
370 self.resetZone()
372 if num_failures > 0:
373 for msg in failure_msgs:
374 print(msg)
375 self.fail("Failed to reject invalid commands. %d total failures. "
376 "Errors above." % num_failures)
378 def test_update_invalid_type(self):
379 """Make sure that a record can't be updated to another type leaving
380 the data the same, where that data would be incompatible with
381 the new type. This is not always enforced at the C level.
383 We don't try with all types, because many types are compatible
384 in their representations (e.g. A records could be TXT or CNAME
385 records; PTR record values are exactly the same as CNAME
386 record values, etc).
388 dnstypes = ('A', 'AAAA', 'SRV')
389 for dnstype1 in dnstypes:
390 record1 = self.good_records[dnstype1][0]
391 result, out, err = self.runsubcmd("dns", "add",
392 os.environ["SERVER"],
393 self.zone, "testrecord",
394 dnstype1, record1,
395 self.creds_string)
396 self.assertCmdSuccess(result, out, err, "Failed to add "
397 "record %s with type %s."
398 % (record1, dnstype1))
400 for dnstype2 in dnstypes:
401 if dnstype1 == dnstype2:
402 continue
404 record2 = self.good_records[dnstype2][0]
406 # Check both ways: Give the current type and try to update,
407 # and give the new type and try to update.
408 result, out, err = self.runsubcmd("dns", "update",
409 os.environ["SERVER"],
410 self.zone, "testrecord",
411 dnstype1, record1,
412 record2, self.creds_string)
413 self.assertCmdFail(result, "Successfully updated record '%s' "
414 "to '%s', even though the latter is of "
415 "type '%s' where '%s' was expected."
416 % (record1, record2, dnstype2, dnstype1))
418 result, out, err = self.runsubcmd("dns", "update",
419 os.environ["SERVER"],
420 self.zone, "testrecord",
421 dnstype2, record1, record2,
422 self.creds_string)
423 self.assertCmdFail(result, "Successfully updated record "
424 "'%s' to '%s', even though the former "
425 "is of type '%s' where '%s' was expected."
426 % (record1, record2, dnstype1, dnstype2))
428 def test_update_valid_type(self):
429 for dnstype in self.good_records:
430 for record in self.good_records[dnstype]:
431 result, out, err = self.runsubcmd("dns", "add",
432 os.environ["SERVER"],
433 self.zone, "testrecord",
434 dnstype, record,
435 self.creds_string)
436 self.assertCmdSuccess(result, out, err, "Failed to add "
437 "record %s with type %s."
438 % (record, dnstype))
440 if record == '.' and dnstype != 'TXT':
441 # This will fail because the update finds a match
442 # for "." that is actually "" (in
443 # dns_record_match()), then uses the "" record in
444 # a call to dns_to_dnsp_convert() which calls
445 # dns_name_check() which rejects "" as a bad DNS
446 # name. Maybe FIXME, maybe not.
447 continue
449 # Update the record to be the same.
450 result, out, err = self.runsubcmd("dns", "update",
451 os.environ["SERVER"],
452 self.zone, "testrecord",
453 dnstype, record, record,
454 self.creds_string)
455 self.assertCmdSuccess(result, out, err,
456 "Could not update record "
457 "'%s' to be exactly the same." % record)
459 result, out, err = self.runsubcmd("dns", "delete",
460 os.environ["SERVER"],
461 self.zone, "testrecord",
462 dnstype, record,
463 self.creds_string)
464 self.assertCmdSuccess(result, out, err, "Could not delete "
465 "valid record '%s' of type '%s'."
466 % (record, dnstype))
468 for record in self.good_records["SRV"]:
469 result, out, err = self.runsubcmd("dns", "add",
470 os.environ["SERVER"],
471 self.zone, "testrecord",
472 "SRV", record,
473 self.creds_string)
474 self.assertCmdSuccess(result, out, err, "Failed to add "
475 "record %s with type 'SRV'." % record)
477 split = record.split()
478 new_bit = str(int(split[3]) + 1)
479 new_record = '%s %s %s %s' % (split[0], split[1], split[2], new_bit)
481 result, out, err = self.runsubcmd("dns", "update",
482 os.environ["SERVER"],
483 self.zone, "testrecord",
484 "SRV", record,
485 new_record, self.creds_string)
486 self.assertCmdSuccess(result, out, err, "Failed to update record "
487 "'%s' of type '%s' to '%s'."
488 % (record, "SRV", new_record))
490 result, out, err = self.runsubcmd("dns", "query",
491 os.environ["SERVER"],
492 self.zone, "testrecord",
493 "SRV", self.creds_string)
494 self.assertCmdSuccess(result, out, err, "Failed to query for "
495 "record '%s' of type '%s'."
496 % (new_record, "SRV"))
498 result, out, err = self.runsubcmd("dns", "delete",
499 os.environ["SERVER"],
500 self.zone, "testrecord",
501 "SRV", new_record,
502 self.creds_string)
503 self.assertCmdSuccess(result, out, err, "Could not delete "
504 "valid record '%s' of type '%s'."
505 % (new_record, "SRV"))
507 # Since 'dns update' takes the current value as a parameter, make sure
508 # we can't enter the wrong current value for a given record.
509 for dnstype in self.good_records:
510 if len(self.good_records[dnstype]) < 3:
511 continue # Not enough records of this type to do this test
513 used_record = self.good_records[dnstype][0]
514 unused_record = self.good_records[dnstype][1]
515 new_record = self.good_records[dnstype][2]
517 result, out, err = self.runsubcmd("dns", "add",
518 os.environ["SERVER"],
519 self.zone, "testrecord",
520 dnstype, used_record,
521 self.creds_string)
522 self.assertCmdSuccess(result, out, err, "Failed to add record %s "
523 "with type %s." % (used_record, dnstype))
525 result, out, err = self.runsubcmd("dns", "update",
526 os.environ["SERVER"],
527 self.zone, "testrecord",
528 dnstype, unused_record,
529 new_record,
530 self.creds_string)
531 self.assertCmdFail(result, "Successfully updated record '%s' "
532 "from '%s' to '%s', even though the given "
533 "source record is incorrect."
534 % (used_record, unused_record, new_record))
536 def test_invalid_types(self):
537 result, out, err = self.runsubcmd("dns", "add",
538 os.environ["SERVER"],
539 self.zone, "testrecord",
540 "SOA", "test",
541 self.creds_string)
542 self.assertCmdFail(result, "Successfully added record of type SOA, "
543 "when this type should not be available.")
544 self.assertTrue("type SOA is not supported" in err,
545 "Invalid error message '%s' when attempting to "
546 "add record of type SOA." % err)
548 def test_add_overlapping_different_type(self):
550 Make sure that we can add an entry with the same name as an existing one but a different type.
553 i = 0
554 for dnstype1 in self.good_records:
555 record1 = self.good_records[dnstype1][0]
556 for dnstype2 in self.good_records:
557 # Only do some subset of dns types, otherwise it takes a long time.
558 i += 1
559 if i % 4 != 0:
560 continue
562 if dnstype1 == dnstype2:
563 continue
565 record2 = self.good_records[dnstype2][0]
567 result, out, err = self.runsubcmd("dns", "add",
568 os.environ["SERVER"],
569 self.zone, "testrecord",
570 dnstype1, record1,
571 self.creds_string)
572 self.assertCmdSuccess(result, out, err, "Failed to add record "
573 "'%s' of type '%s'." % (record1, dnstype1))
575 result, out, err = self.runsubcmd("dns", "add",
576 os.environ["SERVER"],
577 self.zone, "testrecord",
578 dnstype2, record2,
579 self.creds_string)
580 self.assertCmdSuccess(result, out, err, "Failed to add record "
581 "'%s' of type '%s' when a record '%s' "
582 "of type '%s' with the same name exists."
583 % (record1, dnstype1, record2, dnstype2))
585 result, out, err = self.runsubcmd("dns", "query",
586 os.environ["SERVER"],
587 self.zone, "testrecord",
588 dnstype1, self.creds_string)
589 self.assertCmdSuccess(result, out, err, "Failed to query for "
590 "record '%s' of type '%s' when a new "
591 "record '%s' of type '%s' with the same "
592 "name was added."
593 % (record1, dnstype1, record2, dnstype2))
595 result, out, err = self.runsubcmd("dns", "query",
596 os.environ["SERVER"],
597 self.zone, "testrecord",
598 dnstype2, self.creds_string)
599 self.assertCmdSuccess(result, out, err, "Failed to query "
600 "record '%s' of type '%s' which should "
601 "have been added with the same name as "
602 "record '%s' of type '%s'."
603 % (record2, dnstype2, record1, dnstype1))
605 result, out, err = self.runsubcmd("dns", "delete",
606 os.environ["SERVER"],
607 self.zone, "testrecord",
608 dnstype1, record1,
609 self.creds_string)
610 self.assertCmdSuccess(result, out, err, "Failed to delete "
611 "record '%s' of type '%s'."
612 % (record1, dnstype1))
614 result, out, err = self.runsubcmd("dns", "delete",
615 os.environ["SERVER"],
616 self.zone, "testrecord",
617 dnstype2, record2,
618 self.creds_string)
619 self.assertCmdSuccess(result, out, err, "Failed to delete "
620 "record '%s' of type '%s'."
621 % (record2, dnstype2))
623 def test_query_deleted_record(self):
624 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
625 "testrecord", "A", self.testip, self.creds_string)
626 self.runsubcmd("dns", "delete", os.environ["SERVER"], self.zone,
627 "testrecord", "A", self.testip, self.creds_string)
629 result, out, err = self.runsubcmd("dns", "query",
630 os.environ["SERVER"],
631 self.zone, "testrecord",
632 "A", self.creds_string)
633 self.assertCmdFail(result)
635 def test_add_duplicate_record(self):
636 for record_type in self.good_records:
637 result, out, err = self.runsubcmd("dns", "add",
638 os.environ["SERVER"],
639 self.zone, "testrecord",
640 record_type,
641 self.good_records[record_type][0],
642 self.creds_string)
643 self.assertCmdSuccess(result, out, err)
644 result, out, err = self.runsubcmd("dns", "add",
645 os.environ["SERVER"],
646 self.zone, "testrecord",
647 record_type,
648 self.good_records[record_type][0],
649 self.creds_string)
650 self.assertCmdFail(result)
651 result, out, err = self.runsubcmd("dns", "query",
652 os.environ["SERVER"],
653 self.zone, "testrecord",
654 record_type, self.creds_string)
655 self.assertCmdSuccess(result, out, err)
656 result, out, err = self.runsubcmd("dns", "delete",
657 os.environ["SERVER"],
658 self.zone, "testrecord",
659 record_type,
660 self.good_records[record_type][0],
661 self.creds_string)
662 self.assertCmdSuccess(result, out, err)
664 def test_remove_deleted_record(self):
665 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
666 "testrecord", "A", self.testip, self.creds_string)
667 self.runsubcmd("dns", "delete", os.environ["SERVER"], self.zone,
668 "testrecord", "A", self.testip, self.creds_string)
670 # Attempting to delete a record that has already been deleted or has never existed should fail
671 result, out, err = self.runsubcmd("dns", "delete",
672 os.environ["SERVER"],
673 self.zone, "testrecord",
674 "A", self.testip, self.creds_string)
675 self.assertCmdFail(result)
676 result, out, err = self.runsubcmd("dns", "query",
677 os.environ["SERVER"],
678 self.zone, "testrecord",
679 "A", self.creds_string)
680 self.assertCmdFail(result)
681 result, out, err = self.runsubcmd("dns", "delete",
682 os.environ["SERVER"],
683 self.zone, "testrecord2",
684 "A", self.testip, self.creds_string)
685 self.assertCmdFail(result)
687 def test_cleanup_record(self):
689 Test dns cleanup command is working fine.
692 # add a A record
693 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
694 'testa', "A", self.testip, self.creds_string)
696 # the above A record points to this host
697 dnshostname = '{0}.{1}'.format('testa', self.zone.lower())
699 # add a CNAME record points to above host
700 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
701 'testcname', "CNAME", dnshostname, self.creds_string)
703 # add a NS record
704 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
705 'testns', "NS", dnshostname, self.creds_string)
707 # add a PTR record points to above host
708 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
709 'testptr', "PTR", dnshostname, self.creds_string)
711 # add a SRV record points to above host
712 srv_record = "{0} 65530 65530 65530".format(dnshostname)
713 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
714 'testsrv', "SRV", srv_record, self.creds_string)
716 # cleanup record for this dns host
717 self.runsubcmd("dns", "cleanup", os.environ["SERVER"],
718 dnshostname, self.creds_string)
720 # all records should be marked as dNSTombstoned
721 for record_name in ['testa', 'testcname', 'testns', 'testptr', 'testsrv']:
723 records = self.samdb.search(
724 base="DC=DomainDnsZones,{0}".format(self.samdb.get_default_basedn()),
725 scope=ldb.SCOPE_SUBTREE,
726 expression="(&(objectClass=dnsNode)(name={0}))".format(record_name),
727 attrs=["dNSTombstoned"])
729 self.assertEqual(len(records), 1)
730 for record in records:
731 self.assertEqual(str(record['dNSTombstoned']), 'TRUE')
733 def test_cleanup_record_no_A_record(self):
735 Test dns cleanup command works with no A record.
738 # add a A record
739 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
740 'notesta', "A", self.testip, self.creds_string)
742 # the above A record points to this host
743 dnshostname = '{0}.{1}'.format('testa', self.zone.lower())
745 # add a CNAME record points to above host
746 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
747 'notestcname', "CNAME", dnshostname, self.creds_string)
749 # add a NS record
750 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
751 'notestns', "NS", dnshostname, self.creds_string)
753 # add a PTR record points to above host
754 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
755 'notestptr', "PTR", dnshostname, self.creds_string)
757 # add a SRV record points to above host
758 srv_record = "{0} 65530 65530 65530".format(dnshostname)
759 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
760 'notestsrv', "SRV", srv_record, self.creds_string)
762 # Remove the initial A record (leading to hanging references)
763 self.runsubcmd("dns", "delete", os.environ["SERVER"], self.zone,
764 'notesta', "A", self.testip, self.creds_string)
766 # cleanup record for this dns host
767 self.runsubcmd("dns", "cleanup", os.environ["SERVER"],
768 dnshostname, self.creds_string)
770 # all records should be marked as dNSTombstoned
771 for record_name in ['notestcname', 'notestns', 'notestptr', 'notestsrv']:
773 records = self.samdb.search(
774 base="DC=DomainDnsZones,{0}".format(self.samdb.get_default_basedn()),
775 scope=ldb.SCOPE_SUBTREE,
776 expression="(&(objectClass=dnsNode)(name={0}))".format(record_name),
777 attrs=["dNSTombstoned"])
779 self.assertEqual(len(records), 1)
780 for record in records:
781 self.assertEqual(str(record['dNSTombstoned']), 'TRUE')
783 def test_cleanup_multi_srv_record(self):
785 Test dns cleanup command for multi-valued SRV record.
787 Steps:
788 - Add 2 A records host1 and host2
789 - Add a SRV record srv1 and points to both host1 and host2
790 - Run cleanup command for host1
791 - Check records for srv1, data for host1 should be gone and host2 is kept.
794 hosts = ['host1', 'host2'] # A record names
795 srv_name = 'srv1'
797 # add A records
798 for host in hosts:
799 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
800 host, "A", self.testip, self.creds_string)
802 # the above A record points to this host
803 dnshostname = '{0}.{1}'.format(host, self.zone.lower())
805 # add a SRV record points to above host
806 srv_record = "{0} 65530 65530 65530".format(dnshostname)
807 self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone,
808 srv_name, "SRV", srv_record, self.creds_string)
810 records = self.samdb.search(
811 base="DC=DomainDnsZones,{0}".format(self.samdb.get_default_basedn()),
812 scope=ldb.SCOPE_SUBTREE,
813 expression="(&(objectClass=dnsNode)(name={0}))".format(srv_name),
814 attrs=['dnsRecord'])
815 # should have 2 records here
816 self.assertEqual(len(records[0]['dnsRecord']), 2)
818 # cleanup record for dns host1
819 dnshostname1 = 'host1.{0}'.format(self.zone.lower())
820 self.runsubcmd("dns", "cleanup", os.environ["SERVER"],
821 dnshostname1, self.creds_string)
823 records = self.samdb.search(
824 base="DC=DomainDnsZones,{0}".format(self.samdb.get_default_basedn()),
825 scope=ldb.SCOPE_SUBTREE,
826 expression="(&(objectClass=dnsNode)(name={0}))".format(srv_name),
827 attrs=['dnsRecord', 'dNSTombstoned'])
829 # dnsRecord for host1 should be deleted
830 self.assertEqual(len(records[0]['dnsRecord']), 1)
832 # unpack data
833 dns_record_bin = records[0]['dnsRecord'][0]
834 dns_record_obj = ndr_unpack(dnsp.DnssrvRpcRecord, dns_record_bin)
836 # dnsRecord for host2 is still there and is the only one
837 dnshostname2 = 'host2.{0}'.format(self.zone.lower())
838 self.assertEqual(dns_record_obj.data.nameTarget, dnshostname2)
840 # assert that the record isn't spuriously tombstoned
841 self.assertTrue('dNSTombstoned' not in records[0] or
842 str(records[0]['dNSTombstoned']) == 'FALSE')
844 def test_dns_wildcards(self):
846 Ensure that DNS wild card entries can be added deleted and queried
848 num_failures = 0
849 failure_msgs = []
850 records = [("*.", "MISS", "A", "1.1.1.1"),
851 ("*.SAMDOM", "MISS.SAMDOM", "A", "1.1.1.2")]
852 for (name, miss, dnstype, record) in records:
853 try:
854 result, out, err = self.runsubcmd("dns", "add",
855 os.environ["SERVER"],
856 self.zone, name,
857 dnstype, record,
858 self.creds_string)
859 self.assertCmdSuccess(
860 result,
861 out,
862 err,
863 ("Failed to add record %s (%s) with type %s."
864 % (name, record, dnstype)))
866 result, out, err = self.runsubcmd("dns", "query",
867 os.environ["SERVER"],
868 self.zone, name,
869 dnstype,
870 self.creds_string)
871 self.assertCmdSuccess(
872 result,
873 out,
874 err,
875 ("Failed to query record %s with qualifier %s."
876 % (record, dnstype)))
878 # dns tool does not perform dns wildcard search if the name
879 # does not match
880 result, out, err = self.runsubcmd("dns", "query",
881 os.environ["SERVER"],
882 self.zone, miss,
883 dnstype,
884 self.creds_string)
885 self.assertCmdFail(
886 result,
887 ("Failed to query record %s with qualifier %s."
888 % (record, dnstype)))
890 result, out, err = self.runsubcmd("dns", "delete",
891 os.environ["SERVER"],
892 self.zone, name,
893 dnstype, record,
894 self.creds_string)
895 self.assertCmdSuccess(
896 result,
897 out,
898 err,
899 ("Failed to remove record %s with type %s."
900 % (record, dnstype)))
901 except AssertionError as e:
902 num_failures = num_failures + 1
903 failure_msgs.append(e)
905 if num_failures > 0:
906 for msg in failure_msgs:
907 print(msg)
908 self.fail("Failed to accept valid commands. %d total failures."
909 "Errors above." % num_failures)
911 def test_serverinfo(self):
912 for v in ['w2k', 'dotnet', 'longhorn']:
913 result, out, err = self.runsubcmd("dns",
914 "serverinfo",
915 "--client-version", v,
916 os.environ["SERVER"],
917 self.creds_string)
918 self.assertCmdSuccess(result,
919 out,
920 err,
921 "Failed to print serverinfo with "
922 "client version %s" % v)
923 self.assertTrue(out != '')
925 def test_zoneinfo(self):
926 result, out, err = self.runsubcmd("dns",
927 "zoneinfo",
928 os.environ["SERVER"],
929 self.zone,
930 self.creds_string)
931 self.assertCmdSuccess(result,
932 out,
933 err,
934 "Failed to print zoneinfo")
935 self.assertTrue(out != '')
937 def test_zoneoptions_aging(self):
938 for options, vals, error in (
939 (['--aging=1'], {'fAging': 'TRUE'}, False),
940 (['--aging=0'], {'fAging': 'FALSE'}, False),
941 (['--aging=-1'], {'fAging': 'FALSE'}, True),
942 (['--aging=2'], {}, True),
943 (['--aging=2', '--norefreshinterval=1'], {}, True),
944 (['--aging=1', '--norefreshinterval=1'],
945 {'fAging': 'TRUE', 'dwNoRefreshInterval': '1'}, False),
946 (['--aging=1', '--norefreshinterval=0'],
947 {'fAging': 'TRUE', 'dwNoRefreshInterval': '0'}, False),
948 (['--aging=0', '--norefreshinterval=99', '--refreshinterval=99'],
949 {'fAging': 'FALSE',
950 'dwNoRefreshInterval': '99',
951 'dwRefreshInterval': '99'}, False),
952 (['--aging=0', '--norefreshinterval=-99', '--refreshinterval=99'],
953 {}, True),
954 (['--refreshinterval=9999999'], {}, True),
955 (['--norefreshinterval=9999999'], {}, True),
957 result, out, err = self.runsubcmd("dns",
958 "zoneoptions",
959 os.environ["SERVER"],
960 self.zone,
961 self.creds_string,
962 *options)
963 if error:
964 self.assertCmdFail(result, "zoneoptions should fail")
965 else:
966 self.assertCmdSuccess(result,
967 out,
968 err,
969 "zoneoptions shouldn't fail")
972 info_r, info_out, info_err = self.runsubcmd("dns",
973 "zoneinfo",
974 os.environ["SERVER"],
975 self.zone,
976 self.creds_string)
978 self.assertCmdSuccess(info_r,
979 info_out,
980 info_err,
981 "zoneinfo shouldn't fail after zoneoptions")
983 info = {k: v for k, v in re.findall(r'^\s*(\w+)\s*:\s*(\w+)\s*$',
984 info_out,
985 re.MULTILINE)}
986 for k, v in vals.items():
987 self.assertIn(k, info)
988 self.assertEqual(v, info[k])
991 def ldap_add_node_with_records(self, name, records):
992 dn = (f"DC={name},DC={self.zone},CN=MicrosoftDNS,DC=DomainDNSZones,"
993 f"{self.samdb.get_default_basedn()}")
995 dns_records = []
996 for r in records:
997 rec = dnsp.DnssrvRpcRecord()
998 rec.wType = r.get('wType', dnsp.DNS_TYPE_A)
999 rec.rank = dnsp.DNS_RANK_ZONE
1000 rec.dwTtlSeconds = 900
1001 rec.dwTimeStamp = r.get('dwTimeStamp', 0)
1002 rec.data = r.get('data', '10.10.10.10')
1003 dns_records.append(ndr_pack(rec))
1005 msg = ldb.Message.from_dict(self.samdb,
1006 {'dn': dn,
1007 "objectClass": ["top", "dnsNode"],
1008 'dnsRecord': dns_records
1010 self.samdb.add(msg)
1012 def get_timestamp_map(self):
1013 re_wtypes = (dnsp.DNS_TYPE_A,
1014 dnsp.DNS_TYPE_AAAA,
1015 dnsp.DNS_TYPE_TXT)
1017 t = time.time()
1018 now = dsdb_dns.unix_to_dns_timestamp(int(t))
1020 records = self.get_all_records(self.zone)
1021 tsmap = {}
1022 for k, recs in records.items():
1023 m = []
1024 tsmap[k] = m
1025 for rec in recs:
1026 r = ndr_unpack(dnsp.DnssrvRpcRecord, rec)
1027 timestamp = r.dwTimeStamp
1028 if abs(timestamp - now) < 3:
1029 timestamp = 'nowish'
1031 if r.wType in re_wtypes:
1032 m.append(('R', timestamp))
1033 else:
1034 m.append(('-', timestamp))
1036 return tsmap
1039 def test_zoneoptions_mark_records(self):
1040 self.maxDiff = 10000
1041 # We need a number of records to work with, so we'll use part
1042 # of our known good records list, using three different names
1043 # to test the regex. All these records will be static.
1044 for dnstype in self.good_records:
1045 for record in self.good_records[dnstype][:2]:
1046 self.runsubcmd("dns", "add",
1047 os.environ["SERVER"],
1048 self.zone, "frobitz",
1049 dnstype, record,
1050 self.creds_string)
1051 self.runsubcmd("dns", "add",
1052 os.environ["SERVER"],
1053 self.zone, "weergly",
1054 dnstype, record,
1055 self.creds_string)
1056 self.runsubcmd("dns", "add",
1057 os.environ["SERVER"],
1058 self.zone, "snizle",
1059 dnstype, record,
1060 self.creds_string)
1062 # and we also want some that aren't static, and some mixed
1063 # static/dynamic records.
1064 # timestamps are in hours since 1601; now ~= 3.7 million
1065 for ts in (0, 100, 10 ** 6, 10 ** 7):
1066 name = f"ts-{ts}"
1067 self.ldap_add_node_with_records(name, [{"dwTimeStamp": ts}])
1069 recs = []
1070 for ts in (0, 100, 10 ** 6, 10 ** 7):
1071 addr = f'10.{(ts >> 16) & 255}.{(ts >> 8) & 255}.{ts & 255}'
1072 recs.append({"dwTimeStamp": ts, "data": addr})
1074 self.ldap_add_node_with_records("ts-multi", recs)
1076 # get the state of ALL records.
1077 # then we make assertions about the diffs, keeping track of
1078 # the current state.
1080 tsmap = self.get_timestamp_map()
1084 for options, diff, output_substrings, error in (
1085 # --mark-old-records-static
1086 # --mark-records-static-regex
1087 # --mark-records-dynamic-regex
1089 ['--mark-old-records-static=1971-13-04'],
1092 "bad date"
1095 # using --dry-run, should be no change, but output.
1096 ['--mark-old-records-static=1971-03-04', '--dry-run'],
1099 "would make 1/1 records static on ts-1000000.zone.",
1100 "would make 1/1 records static on ts-100.zone.",
1101 "would make 2/4 records static on ts-multi.zone.",
1103 False
1106 # timestamps < ~ 3.25 million are now static
1107 ['--mark-old-records-static=1971-03-04'],
1109 'ts-100': [('R', 0)],
1110 'ts-1000000': [('R', 0)],
1111 'ts-multi': [('R', 0), ('R', 0), ('R', 0), ('R', 10000000)]
1114 "made 1/1 records static on ts-1000000.zone.",
1115 "made 1/1 records static on ts-100.zone.",
1116 "made 2/4 records static on ts-multi.zone.",
1118 False
1121 # no change, old records already static
1122 ['--mark-old-records-static=1972-03-04'],
1125 False
1128 # no change, samba-tool added records already static
1129 ['--mark-records-static-regex=sniz'],
1132 False
1135 # snizle has 2 A, 2 AAAA, 10 fancy, and 2 TXT records, in
1136 # that order.
1137 # the A, AAAA, and TXT records should be dynamic
1138 ['--mark-records-dynamic-regex=sniz'],
1139 {'snizle': [('R', 'nowish'),
1140 ('R', 'nowish'),
1141 ('R', 'nowish'),
1142 ('R', 'nowish'),
1143 ('-', 0),
1144 ('-', 0),
1145 ('-', 0),
1146 ('-', 0),
1147 ('-', 0),
1148 ('-', 0),
1149 ('-', 0),
1150 ('-', 0),
1151 ('-', 0),
1152 ('-', 0),
1153 ('R', 'nowish'),
1154 ('R', 'nowish')]
1156 ['made 6/16 records dynamic on snizle.zone.'],
1157 False
1160 # This regex should catch snizle, weergly, and ts-*
1161 # but we're doing dry-run so no change
1162 ['--mark-records-dynamic-regex=[sw]', '-n'],
1164 ['would make 3/4 records dynamic on ts-multi.zone.',
1165 'would make 1/1 records dynamic on ts-0.zone.',
1166 'would make 1/1 records dynamic on ts-1000000.zone.',
1167 'would make 6/16 records dynamic on weergly.zone.',
1168 'would make 1/1 records dynamic on ts-100.zone.'
1170 False
1173 # This regex should catch snizle and frobitz
1174 # but snizle has already been changed.
1175 ['--mark-records-dynamic-regex=z'],
1176 {'frobitz': [('R', 'nowish'),
1177 ('R', 'nowish'),
1178 ('R', 'nowish'),
1179 ('R', 'nowish'),
1180 ('-', 0),
1181 ('-', 0),
1182 ('-', 0),
1183 ('-', 0),
1184 ('-', 0),
1185 ('-', 0),
1186 ('-', 0),
1187 ('-', 0),
1188 ('-', 0),
1189 ('-', 0),
1190 ('R', 'nowish'),
1191 ('R', 'nowish')]
1193 ['made 6/16 records dynamic on frobitz.zone.'],
1194 False
1197 # This regex should catch snizle, frobitz, and
1198 # ts-multi. Note that the 1e7 ts-multi record is
1199 # already dynamic and doesn't change.
1200 ['--mark-records-dynamic-regex=[i]'],
1201 {'ts-multi': [('R', 'nowish'),
1202 ('R', 'nowish'),
1203 ('R', 'nowish'),
1204 ('R', 10000000)]
1206 ['made 3/4 records dynamic on ts-multi.zone.'],
1207 False
1210 # matches no records
1211 ['--mark-records-dynamic-regex=^aloooooo[qw]+'],
1214 False
1217 # This should be an error, as only one --mark-*
1218 # argument is allowed at a time
1219 ['--mark-records-dynamic-regex=.',
1220 '--mark-records-static-regex=.',
1224 True
1227 # This should also be an error
1228 ['--mark-old-records-static=1997-07-07',
1229 '--mark-records-static-regex=.',
1233 True
1236 # This should not be an error. --aging and refresh
1237 # options can be mixed with --mark ones.
1238 ['--mark-old-records-static=1997-07-07',
1239 '--aging=0',
1242 ['Set Aging to 0'],
1243 False
1246 # This regex should catch weergly, but all the
1247 # records are already static,
1248 ['--mark-records-static-regex=wee'],
1251 False
1254 # Make frobitz static again.
1255 ['--mark-records-static-regex=obi'],
1256 {'frobitz': [('R', 0),
1257 ('R', 0),
1258 ('R', 0),
1259 ('R', 0),
1260 ('-', 0),
1261 ('-', 0),
1262 ('-', 0),
1263 ('-', 0),
1264 ('-', 0),
1265 ('-', 0),
1266 ('-', 0),
1267 ('-', 0),
1268 ('-', 0),
1269 ('-', 0),
1270 ('R', 0),
1271 ('R', 0)]
1273 ['made 6/16 records static on frobitz.zone.'],
1274 False
1277 # would make almost everything static, but --dry-run
1278 ['--mark-old-records-static=2222-03-04', '--dry-run'],
1281 'would make 6/16 records static on snizle.zone.',
1282 'would make 3/4 records static on ts-multi.zone.'
1284 False
1287 # make everything static
1288 ['--mark-records-static-regex=.'],
1289 {'snizle': [('R', 0),
1290 ('R', 0),
1291 ('R', 0),
1292 ('R', 0),
1293 ('-', 0),
1294 ('-', 0),
1295 ('-', 0),
1296 ('-', 0),
1297 ('-', 0),
1298 ('-', 0),
1299 ('-', 0),
1300 ('-', 0),
1301 ('-', 0),
1302 ('-', 0),
1303 ('R', 0),
1304 ('R', 0)],
1305 'ts-10000000': [('R', 0)],
1306 'ts-multi': [('R', 0), ('R', 0), ('R', 0), ('R', 0)]
1309 'made 4/4 records static on ts-multi.zone.',
1310 'made 1/1 records static on ts-10000000.zone.',
1311 'made 6/16 records static on snizle.zone.',
1313 False
1316 # make everything dynamic that can be
1317 ['--mark-records-dynamic-regex=.'],
1318 {'frobitz': [('R', 'nowish'),
1319 ('R', 'nowish'),
1320 ('R', 'nowish'),
1321 ('R', 'nowish'),
1322 ('-', 0),
1323 ('-', 0),
1324 ('-', 0),
1325 ('-', 0),
1326 ('-', 0),
1327 ('-', 0),
1328 ('-', 0),
1329 ('-', 0),
1330 ('-', 0),
1331 ('-', 0),
1332 ('R', 'nowish'),
1333 ('R', 'nowish')],
1334 'snizle': [('R', 'nowish'),
1335 ('R', 'nowish'),
1336 ('R', 'nowish'),
1337 ('R', 'nowish'),
1338 ('-', 0),
1339 ('-', 0),
1340 ('-', 0),
1341 ('-', 0),
1342 ('-', 0),
1343 ('-', 0),
1344 ('-', 0),
1345 ('-', 0),
1346 ('-', 0),
1347 ('-', 0),
1348 ('R', 'nowish'),
1349 ('R', 'nowish')],
1350 'ts-0': [('R', 'nowish')],
1351 'ts-100': [('R', 'nowish')],
1352 'ts-1000000': [('R', 'nowish')],
1353 'ts-10000000': [('R', 'nowish')],
1354 'ts-multi': [('R', 'nowish'),
1355 ('R', 'nowish'),
1356 ('R', 'nowish'),
1357 ('R', 'nowish')],
1358 'weergly': [('R', 'nowish'),
1359 ('R', 'nowish'),
1360 ('R', 'nowish'),
1361 ('R', 'nowish'),
1362 ('-', 0),
1363 ('-', 0),
1364 ('-', 0),
1365 ('-', 0),
1366 ('-', 0),
1367 ('-', 0),
1368 ('-', 0),
1369 ('-', 0),
1370 ('-', 0),
1371 ('-', 0),
1372 ('R', 'nowish'),
1373 ('R', 'nowish')]
1376 'made 4/4 records dynamic on ts-multi.zone.',
1377 'made 6/16 records dynamic on snizle.zone.',
1378 'made 1/1 records dynamic on ts-0.zone.',
1379 'made 1/1 records dynamic on ts-1000000.zone.',
1380 'made 1/1 records dynamic on ts-10000000.zone.',
1381 'made 1/1 records dynamic on ts-100.zone.',
1382 'made 6/16 records dynamic on frobitz.zone.',
1383 'made 6/16 records dynamic on weergly.zone.',
1385 False
1388 result, out, err = self.runsubcmd("dns",
1389 "zoneoptions",
1390 os.environ["SERVER"],
1391 self.zone,
1392 self.creds_string,
1393 *options)
1394 if error:
1395 self.assertCmdFail(result, f"zoneoptions should fail ({error})")
1396 else:
1397 self.assertCmdSuccess(result,
1398 out,
1399 err,
1400 "zoneoptions shouldn't fail")
1402 new_tsmap = self.get_timestamp_map()
1404 # same keys, always
1405 self.assertEqual(sorted(new_tsmap), sorted(tsmap))
1406 changes = {}
1407 for k in tsmap:
1408 if tsmap[k] != new_tsmap[k]:
1409 changes[k] = new_tsmap[k]
1411 self.assertEqual(diff, changes)
1413 for s in output_substrings:
1414 self.assertIn(s, out)
1415 tsmap = new_tsmap
1417 def test_zonecreate_dns_domain_directory_partition(self):
1418 zone = "test-dns-domain-dp-zone"
1419 dns_dp_opt = "--dns-directory-partition=domain"
1421 result, out, err = self.runsubcmd("dns",
1422 "zonecreate",
1423 os.environ["SERVER"],
1424 zone,
1425 self.creds_string,
1426 dns_dp_opt)
1427 self.assertCmdSuccess(result,
1428 out,
1429 err,
1430 "Failed to create zone with "
1431 "--dns-directory-partition option")
1432 self.assertTrue('Zone %s created successfully' % zone in out,
1433 "Unexpected output: %s")
1435 result, out, err = self.runsubcmd("dns",
1436 "zoneinfo",
1437 os.environ["SERVER"],
1438 zone,
1439 self.creds_string)
1440 self.assertCmdSuccess(result, out, err)
1441 self.assertTrue("DNS_DP_DOMAIN_DEFAULT" in out,
1442 "Missing DNS_DP_DOMAIN_DEFAULT flag")
1444 result, out, err = self.runsubcmd("dns",
1445 "zonedelete",
1446 os.environ["SERVER"],
1447 zone,
1448 self.creds_string)
1449 self.assertCmdSuccess(result, out, err,
1450 "Failed to delete zone in domain DNS directory "
1451 "partition")
1452 result, out, err = self.runsubcmd("dns",
1453 "zonelist",
1454 os.environ["SERVER"],
1455 self.creds_string)
1456 self.assertCmdSuccess(result, out, err,
1457 "Failed to delete zone in domain DNS directory "
1458 "partition")
1459 self.assertTrue(zone not in out,
1460 "Deleted zone still exists")
1462 def test_zonecreate_dns_forest_directory_partition(self):
1463 zone = "test-dns-forest-dp-zone"
1464 dns_dp_opt = "--dns-directory-partition=forest"
1466 result, out, err = self.runsubcmd("dns",
1467 "zonecreate",
1468 os.environ["SERVER"],
1469 zone,
1470 self.creds_string,
1471 dns_dp_opt)
1472 self.assertCmdSuccess(result,
1473 out,
1474 err,
1475 "Failed to create zone with "
1476 "--dns-directory-partition option")
1477 self.assertTrue('Zone %s created successfully' % zone in out,
1478 "Unexpected output: %s")
1480 result, out, err = self.runsubcmd("dns",
1481 "zoneinfo",
1482 os.environ["SERVER"],
1483 zone,
1484 self.creds_string)
1485 self.assertCmdSuccess(result, out, err)
1486 self.assertTrue("DNS_DP_FOREST_DEFAULT" in out,
1487 "Missing DNS_DP_FOREST_DEFAULT flag")
1489 result, out, err = self.runsubcmd("dns",
1490 "zonedelete",
1491 os.environ["SERVER"],
1492 zone,
1493 self.creds_string)
1494 self.assertCmdSuccess(result, out, err,
1495 "Failed to delete zone in forest DNS directory "
1496 "partition")
1498 result, out, err = self.runsubcmd("dns",
1499 "zonelist",
1500 os.environ["SERVER"],
1501 self.creds_string)
1502 self.assertCmdSuccess(result, out, err,
1503 "Failed to delete zone in forest DNS directory "
1504 "partition")
1505 self.assertTrue(zone not in out,
1506 "Deleted zone still exists")