]>
Commit | Line | Data |
---|---|---|
e2dba705 | 1 | import json |
d29d5db7 | 2 | import time |
e2dba705 | 3 | import unittest |
ccfabd0d | 4 | from copy import deepcopy |
6754ef71 CH |
5 | from pprint import pprint |
6 | from test_helper import ApiTestCase, unique_zone_name, is_auth, is_recursor, get_db_records | |
7 | ||
8 | ||
9 | def get_rrset(data, qname, qtype): | |
10 | for rrset in data['rrsets']: | |
11 | if rrset['name'] == qname and rrset['type'] == qtype: | |
12 | return rrset | |
13 | return None | |
14 | ||
15 | ||
16 | def get_first_rec(data, qname, qtype): | |
17 | rrset = get_rrset(data, qname, qtype) | |
18 | if rrset: | |
19 | return rrset['records'][0] | |
20 | return None | |
21 | ||
22 | ||
23 | def eq_zone_rrsets(rrsets, expected): | |
24 | data_got = {} | |
25 | data_expected = {} | |
26 | for type_, expected_records in expected.iteritems(): | |
27 | type_ = str(type_) | |
28 | data_got[type_] = set() | |
29 | data_expected[type_] = set() | |
30 | uses_name = any(['name' in expected_record for expected_record in expected_records]) | |
31 | # minify + convert received data | |
32 | for rrset in [rrset for rrset in rrsets if rrset['type'] == type_]: | |
33 | print rrset | |
34 | for r in rrset['records']: | |
35 | data_got[type_].add((rrset['name'] if uses_name else '@', rrset['type'], r['content'])) | |
36 | # minify expected data | |
37 | for r in expected_records: | |
38 | data_expected[type_].add((r['name'] if uses_name else '@', type_, r['content'])) | |
39 | ||
40 | print "eq_zone_rrsets: got:" | |
41 | pprint(data_got) | |
42 | print "eq_zone_rrsets: expected:" | |
43 | pprint(data_expected) | |
44 | ||
45 | assert data_got == data_expected, "%r != %r" % (data_got, data_expected) | |
1a152698 CH |
46 | |
47 | ||
02945d9a | 48 | class Zones(ApiTestCase): |
1a152698 | 49 | |
c1374bdb | 50 | def test_list_zones(self): |
46d06a12 | 51 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
c1374bdb | 52 | self.assert_success_json(r) |
45de6290 | 53 | domains = r.json() |
02945d9a | 54 | example_com = [domain for domain in domains if domain['name'] in ('example.com', 'example.com.')] |
1a152698 CH |
55 | self.assertEquals(len(example_com), 1) |
56 | example_com = example_com[0] | |
02945d9a | 57 | required_fields = ['id', 'url', 'name', 'kind'] |
c1374bdb | 58 | if is_auth(): |
c04b5870 | 59 | required_fields = required_fields + ['masters', 'last_check', 'notified_serial', 'serial', 'account'] |
c1374bdb | 60 | elif is_recursor(): |
02945d9a CH |
61 | required_fields = required_fields + ['recursion_desired', 'servers'] |
62 | for field in required_fields: | |
63 | self.assertIn(field, example_com) | |
64 | ||
65 | ||
406497f5 | 66 | class AuthZonesHelperMixin(object): |
284fdfe9 | 67 | def create_zone(self, name=None, **kwargs): |
bee2acae CH |
68 | if name is None: |
69 | name = unique_zone_name() | |
e2dba705 | 70 | payload = { |
bee2acae | 71 | 'name': name, |
e2dba705 | 72 | 'kind': 'Native', |
1d6b70f9 | 73 | 'nameservers': ['ns1.example.com.', 'ns2.example.com.'] |
e2dba705 | 74 | } |
284fdfe9 | 75 | for k, v in kwargs.items(): |
4bdff352 CH |
76 | if v is None: |
77 | del payload[k] | |
78 | else: | |
79 | payload[k] = v | |
1d6b70f9 | 80 | print "sending", payload |
e2dba705 | 81 | r = self.session.post( |
46d06a12 | 82 | self.url("/api/v1/servers/localhost/zones"), |
e2dba705 CH |
83 | data=json.dumps(payload), |
84 | headers={'content-type': 'application/json'}) | |
c1374bdb | 85 | self.assert_success_json(r) |
64a36f0d | 86 | self.assertEquals(r.status_code, 201) |
1d6b70f9 CH |
87 | reply = r.json() |
88 | print "reply", reply | |
6754ef71 | 89 | return name, payload, reply |
bee2acae | 90 | |
406497f5 CH |
91 | |
92 | @unittest.skipIf(not is_auth(), "Not applicable") | |
93 | class AuthZones(ApiTestCase, AuthZonesHelperMixin): | |
94 | ||
c1374bdb | 95 | def test_create_zone(self): |
b0af9105 | 96 | # soa_edit_api has a default, override with empty for this test |
6754ef71 | 97 | name, payload, data = self.create_zone(serial=22, soa_edit_api='') |
8ffb7a9b | 98 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'soa_edit_api', 'soa_edit', 'account'): |
d29d5db7 CH |
99 | self.assertIn(k, data) |
100 | if k in payload: | |
101 | self.assertEquals(data[k], payload[k]) | |
f63168e6 | 102 | # validate generated SOA |
6754ef71 | 103 | expected_soa = "a.misconfigured.powerdns.server. hostmaster." + name + " " + \ |
1d6b70f9 | 104 | str(payload['serial']) + " 10800 3600 604800 3600" |
f63168e6 | 105 | self.assertEquals( |
6754ef71 | 106 | get_first_rec(data, name, 'SOA')['content'], |
1d6b70f9 | 107 | expected_soa |
f63168e6 | 108 | ) |
1d6b70f9 | 109 | # Because we had confusion about dots, check that the DB is without dots. |
6754ef71 | 110 | dbrecs = get_db_records(name, 'SOA') |
1d6b70f9 | 111 | self.assertEqual(dbrecs[0]['content'], expected_soa.replace('. ', ' ')) |
d29d5db7 | 112 | |
c1374bdb | 113 | def test_create_zone_with_soa_edit_api(self): |
f63168e6 | 114 | # soa_edit_api wins over serial |
6754ef71 | 115 | name, payload, data = self.create_zone(soa_edit_api='EPOCH', serial=10) |
f63168e6 | 116 | for k in ('soa_edit_api', ): |
e2dba705 CH |
117 | self.assertIn(k, data) |
118 | if k in payload: | |
119 | self.assertEquals(data[k], payload[k]) | |
f63168e6 CH |
120 | # generated EPOCH serial surely is > fixed serial we passed in |
121 | print data | |
122 | self.assertGreater(data['serial'], payload['serial']) | |
6754ef71 | 123 | soa_serial = int(get_first_rec(data, name, 'SOA')['content'].split(' ')[2]) |
f63168e6 CH |
124 | self.assertGreater(soa_serial, payload['serial']) |
125 | self.assertEquals(soa_serial, data['serial']) | |
6bb25159 | 126 | |
79532aa7 CH |
127 | def test_create_zone_with_account(self): |
128 | # soa_edit_api wins over serial | |
6754ef71 | 129 | name, payload, data = self.create_zone(account='anaccount', serial=10) |
79532aa7 CH |
130 | print data |
131 | for k in ('account', ): | |
132 | self.assertIn(k, data) | |
133 | if k in payload: | |
134 | self.assertEquals(data[k], payload[k]) | |
135 | ||
9440a9f0 CH |
136 | def test_create_zone_default_soa_edit_api(self): |
137 | name, payload, data = self.create_zone() | |
138 | print data | |
139 | self.assertEquals(data['soa_edit_api'], 'DEFAULT') | |
140 | ||
c1374bdb | 141 | def test_create_zone_with_records(self): |
f63168e6 | 142 | name = unique_zone_name() |
6754ef71 CH |
143 | rrset = { |
144 | "name": name, | |
145 | "type": "A", | |
146 | "ttl": 3600, | |
147 | "records": [{ | |
f63168e6 | 148 | "content": "4.3.2.1", |
6754ef71 CH |
149 | "disabled": False, |
150 | }], | |
151 | } | |
152 | name, payload, data = self.create_zone(name=name, rrsets=[rrset]) | |
f63168e6 | 153 | # check our record has appeared |
6754ef71 | 154 | self.assertEquals(get_rrset(data, name, 'A')['records'], rrset['records']) |
f63168e6 | 155 | |
d0953126 AT |
156 | def test_create_zone_with_wildcard_records(self): |
157 | name = unique_zone_name() | |
6754ef71 CH |
158 | rrset = { |
159 | "name": "*."+name, | |
160 | "type": "A", | |
161 | "ttl": 3600, | |
162 | "records": [{ | |
d0953126 | 163 | "content": "4.3.2.1", |
6754ef71 CH |
164 | "disabled": False, |
165 | }], | |
166 | } | |
167 | name, payload, data = self.create_zone(name=name, rrsets=[rrset]) | |
d0953126 | 168 | # check our record has appeared |
6754ef71 | 169 | self.assertEquals(get_rrset(data, rrset['name'], 'A')['records'], rrset['records']) |
d0953126 | 170 | |
c1374bdb | 171 | def test_create_zone_with_comments(self): |
f63168e6 | 172 | name = unique_zone_name() |
6754ef71 CH |
173 | rrset = { |
174 | "name": name, | |
175 | "type": "soa", # test uppercasing of type, too. | |
176 | "comments": [{ | |
177 | "account": "test1", | |
178 | "content": "blah blah", | |
179 | "modified_at": 11112, | |
180 | }], | |
181 | } | |
182 | name, payload, data = self.create_zone(name=name, rrsets=[rrset]) | |
f63168e6 | 183 | # check our comment has appeared |
6754ef71 | 184 | self.assertEquals(get_rrset(data, name, 'SOA')['comments'], rrset['comments']) |
f63168e6 | 185 | |
1d6b70f9 CH |
186 | def test_create_zone_uncanonical_nameservers(self): |
187 | name = unique_zone_name() | |
188 | payload = { | |
189 | 'name': name, | |
190 | 'kind': 'Native', | |
191 | 'nameservers': ['uncanon.example.com'] | |
192 | } | |
193 | print payload | |
194 | r = self.session.post( | |
195 | self.url("/api/v1/servers/localhost/zones"), | |
196 | data=json.dumps(payload), | |
197 | headers={'content-type': 'application/json'}) | |
198 | self.assertEquals(r.status_code, 422) | |
199 | self.assertIn('Nameserver is not canonical', r.json()['error']) | |
200 | ||
201 | def test_create_auth_zone_no_name(self): | |
202 | name = unique_zone_name() | |
203 | payload = { | |
204 | 'name': '', | |
205 | 'kind': 'Native', | |
206 | } | |
207 | print payload | |
208 | r = self.session.post( | |
209 | self.url("/api/v1/servers/localhost/zones"), | |
210 | data=json.dumps(payload), | |
211 | headers={'content-type': 'application/json'}) | |
212 | self.assertEquals(r.status_code, 422) | |
213 | self.assertIn('is not canonical', r.json()['error']) | |
214 | ||
c1374bdb | 215 | def test_create_zone_with_custom_soa(self): |
f63168e6 | 216 | name = unique_zone_name() |
6754ef71 CH |
217 | content = u"ns1.example.net. testmaster@example.net. 10 10800 3600 604800 3600" |
218 | rrset = { | |
219 | "name": name, | |
220 | "type": "soa", # test uppercasing of type, too. | |
221 | "ttl": 3600, | |
222 | "records": [{ | |
223 | "content": content, | |
224 | "disabled": False, | |
225 | }], | |
226 | } | |
227 | name, payload, data = self.create_zone(name=name, rrsets=[rrset], soa_edit_api='') | |
228 | self.assertEquals(get_rrset(data, name, 'SOA')['records'], rrset['records']) | |
229 | dbrecs = get_db_records(name, 'SOA') | |
230 | self.assertEqual(dbrecs[0]['content'], content.replace('. ', ' ')) | |
1d6b70f9 CH |
231 | |
232 | def test_create_zone_double_dot(self): | |
233 | name = 'test..' + unique_zone_name() | |
234 | payload = { | |
235 | 'name': name, | |
236 | 'kind': 'Native', | |
237 | 'nameservers': ['ns1.example.com.'] | |
238 | } | |
239 | print payload | |
240 | r = self.session.post( | |
241 | self.url("/api/v1/servers/localhost/zones"), | |
242 | data=json.dumps(payload), | |
243 | headers={'content-type': 'application/json'}) | |
244 | self.assertEquals(r.status_code, 422) | |
245 | self.assertIn('Unable to parse DNS Name', r.json()['error']) | |
05776d2f | 246 | |
1d6b70f9 CH |
247 | def test_create_zone_restricted_chars(self): |
248 | name = 'test:' + unique_zone_name() # : isn't good as a name. | |
249 | payload = { | |
250 | 'name': name, | |
251 | 'kind': 'Native', | |
252 | 'nameservers': ['ns1.example.com'] | |
253 | } | |
254 | print payload | |
255 | r = self.session.post( | |
256 | self.url("/api/v1/servers/localhost/zones"), | |
257 | data=json.dumps(payload), | |
258 | headers={'content-type': 'application/json'}) | |
259 | self.assertEquals(r.status_code, 422) | |
260 | self.assertIn('contains unsupported characters', r.json()['error']) | |
4ebf78b1 | 261 | |
c1374bdb | 262 | def test_create_zone_with_symbols(self): |
6754ef71 | 263 | name, payload, data = self.create_zone(name='foo/bar.'+unique_zone_name()) |
bee2acae | 264 | name = payload['name'] |
1d6b70f9 | 265 | expected_id = name.replace('/', '=2F') |
00a9b229 CH |
266 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial'): |
267 | self.assertIn(k, data) | |
268 | if k in payload: | |
269 | self.assertEquals(data[k], payload[k]) | |
bee2acae | 270 | self.assertEquals(data['id'], expected_id) |
1d6b70f9 CH |
271 | dbrecs = get_db_records(name, 'SOA') |
272 | self.assertEqual(dbrecs[0]['name'], name.rstrip('.')) | |
00a9b229 | 273 | |
c1374bdb | 274 | def test_create_zone_with_nameservers_non_string(self): |
e90b4e38 CH |
275 | # ensure we don't crash |
276 | name = unique_zone_name() | |
277 | payload = { | |
278 | 'name': name, | |
279 | 'kind': 'Native', | |
280 | 'nameservers': [{'a': 'ns1.example.com'}] # invalid | |
281 | } | |
282 | print payload | |
283 | r = self.session.post( | |
46d06a12 | 284 | self.url("/api/v1/servers/localhost/zones"), |
e90b4e38 CH |
285 | data=json.dumps(payload), |
286 | headers={'content-type': 'application/json'}) | |
287 | self.assertEquals(r.status_code, 422) | |
288 | ||
24e11043 CJ |
289 | def test_create_zone_metadata(self): |
290 | payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]} | |
291 | r = self.session.post(self.url("/api/v1/servers/localhost/zones/example.com/metadata"), | |
292 | data=json.dumps(payload_metadata)) | |
293 | rdata = r.json() | |
294 | self.assertEquals(r.status_code, 201) | |
295 | self.assertEquals(rdata["metadata"], payload_metadata["metadata"]) | |
296 | ||
297 | def test_create_zone_metadata_kind(self): | |
298 | payload_metadata = {"metadata": ["127.0.0.2"]} | |
299 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE"), | |
300 | data=json.dumps(payload_metadata)) | |
301 | rdata = r.json() | |
302 | self.assertEquals(r.status_code, 200) | |
303 | self.assertEquals(rdata["metadata"], payload_metadata["metadata"]) | |
304 | ||
305 | def test_create_protected_zone_metadata(self): | |
306 | # test whether it prevents modification of certain kinds | |
307 | for k in ("NSEC3NARROW", "NSEC3PARAM", "PRESIGNED", "LUA-AXFR-SCRIPT"): | |
308 | payload = {"metadata": ["FOO", "BAR"]} | |
309 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/example.com/metadata/%s" % k), | |
310 | data=json.dumps(payload)) | |
311 | self.assertEquals(r.status_code, 422) | |
312 | ||
313 | def test_retrieve_zone_metadata(self): | |
314 | payload_metadata = {"type": "Metadata", "kind": "AXFR-SOURCE", "metadata": ["127.0.0.2"]} | |
315 | self.session.post(self.url("/api/v1/servers/localhost/zones/example.com/metadata"), | |
316 | data=json.dumps(payload_metadata)) | |
317 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/example.com/metadata")) | |
318 | rdata = r.json() | |
319 | self.assertEquals(r.status_code, 200) | |
320 | self.assertIn(payload_metadata, rdata) | |
321 | ||
322 | def test_delete_zone_metadata(self): | |
323 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE")) | |
324 | self.assertEquals(r.status_code, 200) | |
325 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/example.com/metadata/AXFR-SOURCE")) | |
326 | rdata = r.json() | |
327 | self.assertEquals(r.status_code, 200) | |
328 | self.assertEquals(rdata["metadata"], []) | |
329 | ||
4bdff352 CH |
330 | def test_create_slave_zone(self): |
331 | # Test that nameservers can be absent for slave zones. | |
6754ef71 | 332 | name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) |
4bdff352 CH |
333 | for k in ('name', 'masters', 'kind'): |
334 | self.assertIn(k, data) | |
335 | self.assertEquals(data[k], payload[k]) | |
4de11a54 CH |
336 | print "payload:", payload |
337 | print "data:", data | |
338 | # Because slave zones don't get a SOA, we need to test that they'll show up in the zone list. | |
46d06a12 | 339 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
4de11a54 CH |
340 | zonelist = r.json() |
341 | print "zonelist:", zonelist | |
342 | self.assertIn(payload['name'], [zone['name'] for zone in zonelist]) | |
343 | # Also test that fetching the zone works. | |
46d06a12 | 344 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id'])) |
4de11a54 CH |
345 | data = r.json() |
346 | print "zone (fetched):", data | |
347 | for k in ('name', 'masters', 'kind'): | |
348 | self.assertIn(k, data) | |
349 | self.assertEquals(data[k], payload[k]) | |
350 | self.assertEqual(data['serial'], 0) | |
6754ef71 | 351 | self.assertEqual(data['rrsets'], []) |
4de11a54 CH |
352 | |
353 | def test_delete_slave_zone(self): | |
6754ef71 | 354 | name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) |
46d06a12 | 355 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + data['id'])) |
4de11a54 | 356 | r.raise_for_status() |
4bdff352 | 357 | |
a426cb89 | 358 | def test_retrieve_slave_zone(self): |
6754ef71 | 359 | name, payload, data = self.create_zone(kind='Slave', nameservers=None, masters=['127.0.0.2']) |
a426cb89 CH |
360 | print "payload:", payload |
361 | print "data:", data | |
46d06a12 | 362 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/axfr-retrieve")) |
a426cb89 CH |
363 | data = r.json() |
364 | print "status for axfr-retrieve:", data | |
365 | self.assertEqual(data['result'], u'Added retrieval request for \'' + payload['name'] + | |
366 | '\' from master 127.0.0.2') | |
367 | ||
368 | def test_notify_master_zone(self): | |
6754ef71 | 369 | name, payload, data = self.create_zone(kind='Master') |
a426cb89 CH |
370 | print "payload:", payload |
371 | print "data:", data | |
46d06a12 | 372 | r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/notify")) |
a426cb89 CH |
373 | data = r.json() |
374 | print "status for notify:", data | |
375 | self.assertEqual(data['result'], 'Notification queued') | |
376 | ||
c1374bdb | 377 | def test_get_zone_with_symbols(self): |
6754ef71 | 378 | name, payload, data = self.create_zone(name='foo/bar.'+unique_zone_name()) |
3c3c006b | 379 | name = payload['name'] |
1d6b70f9 | 380 | zone_id = (name.replace('/', '=2F')) |
46d06a12 | 381 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + zone_id)) |
c1374bdb | 382 | data = r.json() |
6bb25159 | 383 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'dnssec'): |
3c3c006b CH |
384 | self.assertIn(k, data) |
385 | if k in payload: | |
386 | self.assertEquals(data[k], payload[k]) | |
387 | ||
c1374bdb | 388 | def test_get_zone(self): |
46d06a12 | 389 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
05776d2f | 390 | domains = r.json() |
1d6b70f9 | 391 | example_com = [domain for domain in domains if domain['name'] == u'example.com.'][0] |
46d06a12 | 392 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + example_com['id'])) |
c1374bdb | 393 | self.assert_success_json(r) |
05776d2f CH |
394 | data = r.json() |
395 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial'): | |
396 | self.assertIn(k, data) | |
1d6b70f9 | 397 | self.assertEquals(data['name'], 'example.com.') |
7c0ba3d2 | 398 | |
0f0e73fe MS |
399 | def test_import_zone_broken(self): |
400 | payload = {} | |
401 | payload['zone'] = """ | |
402 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58571 | |
403 | flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 | |
404 | ;; WARNING: recursion requested but not available | |
405 | ||
406 | ;; OPT PSEUDOSECTION: | |
407 | ; EDNS: version: 0, flags:; udp: 1680 | |
408 | ;; QUESTION SECTION: | |
409 | ;powerdns.com. IN SOA | |
410 | ||
411 | ;; ANSWER SECTION: | |
412 | powerdns-broken.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
413 | powerdns-broken.com. 3600 IN NS powerdnssec2.ds9a.nl. | |
414 | powerdns-broken.com. 3600 IN AAAA 2001:888:2000:1d::2 | |
415 | powerdns-broken.com. 86400 IN A 82.94.213.34 | |
416 | powerdns-broken.com. 3600 IN MX 0 xs.powerdns.com. | |
417 | powerdns-broken.com. 3600 IN NS powerdnssec1.ds9a.nl. | |
418 | powerdns-broken.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
419 | """ | |
1d6b70f9 | 420 | payload['name'] = 'powerdns-broken.com.' |
0f0e73fe MS |
421 | payload['kind'] = 'Master' |
422 | payload['nameservers'] = [] | |
423 | r = self.session.post( | |
46d06a12 | 424 | self.url("/api/v1/servers/localhost/zones"), |
0f0e73fe MS |
425 | data=json.dumps(payload), |
426 | headers={'content-type': 'application/json'}) | |
427 | self.assertEquals(r.status_code, 422) | |
428 | ||
1d6b70f9 CH |
429 | def test_import_zone_axfr_outofzone(self): |
430 | # Ensure we don't create out-of-zone records | |
431 | name = unique_zone_name() | |
432 | payload = {} | |
433 | payload['zone'] = """ | |
434 | NAME 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
435 | NAME 3600 IN NS powerdnssec2.ds9a.nl. | |
436 | example.org. 3600 IN AAAA 2001:888:2000:1d::2 | |
437 | NAME 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
438 | """.replace('NAME', name) | |
439 | payload['name'] = name | |
440 | payload['kind'] = 'Master' | |
441 | payload['nameservers'] = [] | |
442 | r = self.session.post( | |
443 | self.url("/api/v1/servers/localhost/zones"), | |
444 | data=json.dumps(payload), | |
445 | headers={'content-type': 'application/json'}) | |
446 | self.assertEquals(r.status_code, 422) | |
447 | self.assertEqual(r.json()['error'], 'RRset example.org. IN AAAA: Name is out of zone') | |
448 | ||
0f0e73fe MS |
449 | def test_import_zone_axfr(self): |
450 | payload = {} | |
451 | payload['zone'] = """ | |
452 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58571 | |
453 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 | |
454 | ;; WARNING: recursion requested but not available | |
455 | ||
456 | ;; OPT PSEUDOSECTION: | |
457 | ; EDNS: version: 0, flags:; udp: 1680 | |
458 | ;; QUESTION SECTION: | |
459 | ;powerdns.com. IN SOA | |
460 | ||
461 | ;; ANSWER SECTION: | |
462 | powerdns.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
463 | powerdns.com. 3600 IN NS powerdnssec2.ds9a.nl. | |
464 | powerdns.com. 3600 IN AAAA 2001:888:2000:1d::2 | |
465 | powerdns.com. 86400 IN A 82.94.213.34 | |
466 | powerdns.com. 3600 IN MX 0 xs.powerdns.com. | |
467 | powerdns.com. 3600 IN NS powerdnssec1.ds9a.nl. | |
468 | powerdns.com. 86400 IN SOA powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800 | |
469 | """ | |
1d6b70f9 | 470 | payload['name'] = 'powerdns.com.' |
0f0e73fe MS |
471 | payload['kind'] = 'Master' |
472 | payload['nameservers'] = [] | |
1d6b70f9 | 473 | payload['soa_edit_api'] = '' # turn off so exact SOA comparison works. |
0f0e73fe | 474 | r = self.session.post( |
46d06a12 | 475 | self.url("/api/v1/servers/localhost/zones"), |
0f0e73fe MS |
476 | data=json.dumps(payload), |
477 | headers={'content-type': 'application/json'}) | |
478 | self.assert_success_json(r) | |
479 | data = r.json() | |
480 | self.assertIn('name', data) | |
0f0e73fe | 481 | |
90568eb2 MS |
482 | expected = { |
483 | 'NS': [ | |
6754ef71 CH |
484 | {'content': 'powerdnssec1.ds9a.nl.'}, |
485 | {'content': 'powerdnssec2.ds9a.nl.'}, | |
486 | ], | |
90568eb2 | 487 | 'SOA': [ |
6754ef71 CH |
488 | {'content': 'powerdnssec1.ds9a.nl. ahu.ds9a.nl. 1343746984 10800 3600 604800 10800'}, |
489 | ], | |
90568eb2 | 490 | 'MX': [ |
6754ef71 CH |
491 | {'content': '0 xs.powerdns.com.'}, |
492 | ], | |
90568eb2 | 493 | 'A': [ |
6754ef71 CH |
494 | {'content': '82.94.213.34', 'name': 'powerdns.com.'}, |
495 | ], | |
90568eb2 | 496 | 'AAAA': [ |
6754ef71 CH |
497 | {'content': '2001:888:2000:1d::2', 'name': 'powerdns.com.'}, |
498 | ], | |
90568eb2 | 499 | } |
0f0e73fe | 500 | |
6754ef71 | 501 | eq_zone_rrsets(data['rrsets'], expected) |
1d6b70f9 CH |
502 | |
503 | # noDot check | |
504 | dbrecs = get_db_records(payload['name'], 'NS') | |
505 | self.assertEqual(dbrecs[0]['content'], 'powerdnssec2.ds9a.nl') | |
0f0e73fe MS |
506 | |
507 | def test_import_zone_bind(self): | |
508 | payload = {} | |
509 | payload['zone'] = """ | |
510 | $TTL 86400 ; 24 hours could have been written as 24h or 1d | |
511 | ; $TTL used for all RRs without explicit TTL value | |
512 | $ORIGIN example.org. | |
513 | @ 1D IN SOA ns1.example.org. hostmaster.example.org. ( | |
514 | 2002022401 ; serial | |
515 | 3H ; refresh | |
516 | 15 ; retry | |
517 | 1w ; expire | |
518 | 3h ; minimum | |
519 | ) | |
520 | IN NS ns1.example.org. ; in the domain | |
521 | IN NS ns2.smokeyjoe.com. ; external to domain | |
522 | IN MX 10 mail.another.com. ; external mail provider | |
523 | ; server host definitions | |
1d6b70f9 | 524 | ns1 IN A 192.168.0.1 ;name server definition |
0f0e73fe MS |
525 | www IN A 192.168.0.2 ;web server definition |
526 | ftp IN CNAME www.example.org. ;ftp server definition | |
527 | ; non server domain hosts | |
528 | bill IN A 192.168.0.3 | |
1d6b70f9 | 529 | fred IN A 192.168.0.4 |
0f0e73fe | 530 | """ |
1d6b70f9 | 531 | payload['name'] = 'example.org.' |
0f0e73fe MS |
532 | payload['kind'] = 'Master' |
533 | payload['nameservers'] = [] | |
1d6b70f9 | 534 | payload['soa_edit_api'] = '' # turn off so exact SOA comparison works. |
0f0e73fe | 535 | r = self.session.post( |
46d06a12 | 536 | self.url("/api/v1/servers/localhost/zones"), |
0f0e73fe MS |
537 | data=json.dumps(payload), |
538 | headers={'content-type': 'application/json'}) | |
539 | self.assert_success_json(r) | |
540 | data = r.json() | |
541 | self.assertIn('name', data) | |
0f0e73fe | 542 | |
90568eb2 MS |
543 | expected = { |
544 | 'NS': [ | |
6754ef71 CH |
545 | {'content': 'ns1.example.org.'}, |
546 | {'content': 'ns2.smokeyjoe.com.'}, | |
547 | ], | |
90568eb2 | 548 | 'SOA': [ |
6754ef71 CH |
549 | {'content': 'ns1.example.org. hostmaster.example.org. 2002022401 10800 15 604800 10800'}, |
550 | ], | |
90568eb2 | 551 | 'MX': [ |
6754ef71 CH |
552 | {'content': '10 mail.another.com.'}, |
553 | ], | |
90568eb2 | 554 | 'A': [ |
6754ef71 CH |
555 | {'content': '192.168.0.1', 'name': 'ns1.example.org.'}, |
556 | {'content': '192.168.0.2', 'name': 'www.example.org.'}, | |
557 | {'content': '192.168.0.3', 'name': 'bill.example.org.'}, | |
558 | {'content': '192.168.0.4', 'name': 'fred.example.org.'}, | |
559 | ], | |
90568eb2 | 560 | 'CNAME': [ |
6754ef71 CH |
561 | {'content': 'www.example.org.', 'name': 'ftp.example.org.'}, |
562 | ], | |
90568eb2 | 563 | } |
0f0e73fe | 564 | |
6754ef71 | 565 | eq_zone_rrsets(data['rrsets'], expected) |
0f0e73fe | 566 | |
c1374bdb | 567 | def test_export_zone_json(self): |
6754ef71 | 568 | name, payload, zone = self.create_zone(nameservers=['ns1.foo.com.', 'ns2.foo.com.'], soa_edit_api='') |
a83004d3 CH |
569 | # export it |
570 | r = self.session.get( | |
46d06a12 | 571 | self.url("/api/v1/servers/localhost/zones/" + name + "/export"), |
a83004d3 CH |
572 | headers={'accept': 'application/json;q=0.9,*/*;q=0.8'} |
573 | ) | |
c1374bdb | 574 | self.assert_success_json(r) |
a83004d3 CH |
575 | data = r.json() |
576 | self.assertIn('zone', data) | |
1d6b70f9 CH |
577 | expected_data = [name + '\t3600\tNS\tns1.foo.com.', |
578 | name + '\t3600\tNS\tns2.foo.com.', | |
579 | name + '\t3600\tSOA\ta.misconfigured.powerdns.server. hostmaster.' + name + | |
580 | ' 0 10800 3600 604800 3600'] | |
a83004d3 CH |
581 | self.assertEquals(data['zone'].strip().split('\n'), expected_data) |
582 | ||
c1374bdb | 583 | def test_export_zone_text(self): |
6754ef71 | 584 | name, payload, zone = self.create_zone(nameservers=['ns1.foo.com.', 'ns2.foo.com.'], soa_edit_api='') |
a83004d3 CH |
585 | # export it |
586 | r = self.session.get( | |
46d06a12 | 587 | self.url("/api/v1/servers/localhost/zones/" + name + "/export"), |
a83004d3 CH |
588 | headers={'accept': '*/*'} |
589 | ) | |
590 | data = r.text.strip().split("\n") | |
1d6b70f9 CH |
591 | expected_data = [name + '\t3600\tNS\tns1.foo.com.', |
592 | name + '\t3600\tNS\tns2.foo.com.', | |
593 | name + '\t3600\tSOA\ta.misconfigured.powerdns.server. hostmaster.' + name + | |
594 | ' 0 10800 3600 604800 3600'] | |
a83004d3 CH |
595 | self.assertEquals(data, expected_data) |
596 | ||
c1374bdb | 597 | def test_update_zone(self): |
6754ef71 | 598 | name, payload, zone = self.create_zone() |
bee2acae | 599 | name = payload['name'] |
d29d5db7 | 600 | # update, set as Master and enable SOA-EDIT-API |
7c0ba3d2 CH |
601 | payload = { |
602 | 'kind': 'Master', | |
c1374bdb | 603 | 'masters': ['192.0.2.1', '192.0.2.2'], |
6bb25159 MS |
604 | 'soa_edit_api': 'EPOCH', |
605 | 'soa_edit': 'EPOCH' | |
7c0ba3d2 CH |
606 | } |
607 | r = self.session.put( | |
46d06a12 | 608 | self.url("/api/v1/servers/localhost/zones/" + name), |
7c0ba3d2 CH |
609 | data=json.dumps(payload), |
610 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
611 | self.assert_success(r) |
612 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() | |
7c0ba3d2 CH |
613 | for k in payload.keys(): |
614 | self.assertIn(k, data) | |
615 | self.assertEquals(data[k], payload[k]) | |
d29d5db7 | 616 | # update, back to Native and empty(off) |
7c0ba3d2 | 617 | payload = { |
d29d5db7 | 618 | 'kind': 'Native', |
6bb25159 MS |
619 | 'soa_edit_api': '', |
620 | 'soa_edit': '' | |
7c0ba3d2 CH |
621 | } |
622 | r = self.session.put( | |
46d06a12 | 623 | self.url("/api/v1/servers/localhost/zones/" + name), |
7c0ba3d2 CH |
624 | data=json.dumps(payload), |
625 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
626 | self.assert_success(r) |
627 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() | |
7c0ba3d2 CH |
628 | for k in payload.keys(): |
629 | self.assertIn(k, data) | |
630 | self.assertEquals(data[k], payload[k]) | |
b3905a3d | 631 | |
c1374bdb | 632 | def test_zone_rr_update(self): |
6754ef71 | 633 | name, payload, zone = self.create_zone() |
b3905a3d | 634 | # do a replace (= update) |
d708640f | 635 | rrset = { |
b3905a3d CH |
636 | 'changetype': 'replace', |
637 | 'name': name, | |
8ce0dc75 | 638 | 'type': 'ns', |
6754ef71 | 639 | 'ttl': 3600, |
b3905a3d CH |
640 | 'records': [ |
641 | { | |
1d6b70f9 | 642 | "content": "ns1.bar.com.", |
cea26350 CH |
643 | "disabled": False |
644 | }, | |
645 | { | |
1d6b70f9 | 646 | "content": "ns2-disabled.bar.com.", |
cea26350 | 647 | "disabled": True |
b3905a3d CH |
648 | } |
649 | ] | |
650 | } | |
d708640f | 651 | payload = {'rrsets': [rrset]} |
b3905a3d | 652 | r = self.session.patch( |
46d06a12 | 653 | self.url("/api/v1/servers/localhost/zones/" + name), |
b3905a3d CH |
654 | data=json.dumps(payload), |
655 | headers={'content-type': 'application/json'}) | |
f0e76cee | 656 | self.assert_success(r) |
b3905a3d | 657 | # verify that (only) the new record is there |
f0e76cee | 658 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 | 659 | self.assertEquals(get_rrset(data, name, 'NS')['records'], rrset['records']) |
b3905a3d | 660 | |
c1374bdb | 661 | def test_zone_rr_update_mx(self): |
05cf6a71 | 662 | # Important to test with MX records, as they have a priority field, which must end up in the content field. |
6754ef71 | 663 | name, payload, zone = self.create_zone() |
41e3b10e | 664 | # do a replace (= update) |
d708640f | 665 | rrset = { |
41e3b10e CH |
666 | 'changetype': 'replace', |
667 | 'name': name, | |
668 | 'type': 'MX', | |
6754ef71 | 669 | 'ttl': 3600, |
41e3b10e CH |
670 | 'records': [ |
671 | { | |
1d6b70f9 | 672 | "content": "10 mail.example.org.", |
41e3b10e CH |
673 | "disabled": False |
674 | } | |
675 | ] | |
676 | } | |
d708640f | 677 | payload = {'rrsets': [rrset]} |
41e3b10e | 678 | r = self.session.patch( |
46d06a12 | 679 | self.url("/api/v1/servers/localhost/zones/" + name), |
41e3b10e CH |
680 | data=json.dumps(payload), |
681 | headers={'content-type': 'application/json'}) | |
f0e76cee | 682 | self.assert_success(r) |
41e3b10e | 683 | # verify that (only) the new record is there |
f0e76cee | 684 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 | 685 | self.assertEquals(get_rrset(data, name, 'MX')['records'], rrset['records']) |
d708640f | 686 | |
c1374bdb | 687 | def test_zone_rr_update_multiple_rrsets(self): |
6754ef71 | 688 | name, payload, zone = self.create_zone() |
d708640f CH |
689 | rrset1 = { |
690 | 'changetype': 'replace', | |
691 | 'name': name, | |
692 | 'type': 'NS', | |
6754ef71 | 693 | 'ttl': 3600, |
d708640f CH |
694 | 'records': [ |
695 | { | |
6754ef71 | 696 | |
1d6b70f9 | 697 | "content": "ns9999.example.com.", |
d708640f CH |
698 | "disabled": False |
699 | } | |
700 | ] | |
701 | } | |
702 | rrset2 = { | |
703 | 'changetype': 'replace', | |
704 | 'name': name, | |
705 | 'type': 'MX', | |
6754ef71 | 706 | 'ttl': 3600, |
d708640f CH |
707 | 'records': [ |
708 | { | |
1d6b70f9 | 709 | "content": "10 mx444.example.com.", |
d708640f CH |
710 | "disabled": False |
711 | } | |
712 | ] | |
713 | } | |
714 | payload = {'rrsets': [rrset1, rrset2]} | |
715 | r = self.session.patch( | |
46d06a12 | 716 | self.url("/api/v1/servers/localhost/zones/" + name), |
d708640f CH |
717 | data=json.dumps(payload), |
718 | headers={'content-type': 'application/json'}) | |
f0e76cee | 719 | self.assert_success(r) |
d708640f | 720 | # verify that all rrsets have been updated |
f0e76cee | 721 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 CH |
722 | self.assertEquals(get_rrset(data, name, 'NS')['records'], rrset1['records']) |
723 | self.assertEquals(get_rrset(data, name, 'MX')['records'], rrset2['records']) | |
41e3b10e | 724 | |
c1374bdb | 725 | def test_zone_rr_delete(self): |
6754ef71 | 726 | name, payload, zone = self.create_zone() |
b3905a3d | 727 | # do a delete of all NS records (these are created with the zone) |
d708640f | 728 | rrset = { |
b3905a3d CH |
729 | 'changetype': 'delete', |
730 | 'name': name, | |
731 | 'type': 'NS' | |
732 | } | |
d708640f | 733 | payload = {'rrsets': [rrset]} |
b3905a3d | 734 | r = self.session.patch( |
46d06a12 | 735 | self.url("/api/v1/servers/localhost/zones/" + name), |
b3905a3d CH |
736 | data=json.dumps(payload), |
737 | headers={'content-type': 'application/json'}) | |
f0e76cee | 738 | self.assert_success(r) |
b3905a3d | 739 | # verify that the records are gone |
f0e76cee | 740 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
6754ef71 | 741 | self.assertIsNone(get_rrset(data, name, 'NS')) |
cea26350 | 742 | |
c1374bdb | 743 | def test_zone_disable_reenable(self): |
d29d5db7 | 744 | # This also tests that SOA-EDIT-API works. |
6754ef71 | 745 | name, payload, zone = self.create_zone(soa_edit_api='EPOCH') |
cea26350 | 746 | # disable zone by disabling SOA |
d708640f | 747 | rrset = { |
cea26350 CH |
748 | 'changetype': 'replace', |
749 | 'name': name, | |
750 | 'type': 'SOA', | |
6754ef71 | 751 | 'ttl': 3600, |
cea26350 CH |
752 | 'records': [ |
753 | { | |
1d6b70f9 | 754 | "content": "ns1.bar.com. hostmaster.foo.org. 1 1 1 1 1", |
cea26350 CH |
755 | "disabled": True |
756 | } | |
757 | ] | |
758 | } | |
d708640f | 759 | payload = {'rrsets': [rrset]} |
cea26350 | 760 | r = self.session.patch( |
46d06a12 | 761 | self.url("/api/v1/servers/localhost/zones/" + name), |
cea26350 CH |
762 | data=json.dumps(payload), |
763 | headers={'content-type': 'application/json'}) | |
f0e76cee | 764 | self.assert_success(r) |
d29d5db7 | 765 | # check SOA serial has been edited |
f0e76cee CH |
766 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
767 | soa_serial1 = get_first_rec(data, name, 'SOA')['content'].split()[2] | |
d29d5db7 CH |
768 | self.assertNotEquals(soa_serial1, '1') |
769 | # make sure domain is still in zone list (disabled SOA!) | |
46d06a12 | 770 | r = self.session.get(self.url("/api/v1/servers/localhost/zones")) |
cea26350 CH |
771 | domains = r.json() |
772 | self.assertEquals(len([domain for domain in domains if domain['name'] == name]), 1) | |
d29d5db7 CH |
773 | # sleep 1sec to ensure the EPOCH value changes for the next request |
774 | time.sleep(1) | |
cea26350 | 775 | # verify that modifying it still works |
d708640f CH |
776 | rrset['records'][0]['disabled'] = False |
777 | payload = {'rrsets': [rrset]} | |
cea26350 | 778 | r = self.session.patch( |
46d06a12 | 779 | self.url("/api/v1/servers/localhost/zones/" + name), |
cea26350 CH |
780 | data=json.dumps(payload), |
781 | headers={'content-type': 'application/json'}) | |
f0e76cee | 782 | self.assert_success(r) |
d29d5db7 | 783 | # check SOA serial has been edited again |
f0e76cee CH |
784 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
785 | soa_serial2 = get_first_rec(data, name, 'SOA')['content'].split()[2] | |
d29d5db7 CH |
786 | self.assertNotEquals(soa_serial2, '1') |
787 | self.assertNotEquals(soa_serial2, soa_serial1) | |
02945d9a | 788 | |
c1374bdb | 789 | def test_zone_rr_update_out_of_zone(self): |
6754ef71 | 790 | name, payload, zone = self.create_zone() |
35f26cc5 | 791 | # replace with qname mismatch |
d708640f | 792 | rrset = { |
35f26cc5 | 793 | 'changetype': 'replace', |
1d6b70f9 | 794 | 'name': 'not-in-zone.', |
35f26cc5 | 795 | 'type': 'NS', |
6754ef71 | 796 | 'ttl': 3600, |
35f26cc5 CH |
797 | 'records': [ |
798 | { | |
1d6b70f9 | 799 | "content": "ns1.bar.com.", |
35f26cc5 CH |
800 | "disabled": False |
801 | } | |
802 | ] | |
803 | } | |
d708640f | 804 | payload = {'rrsets': [rrset]} |
35f26cc5 | 805 | r = self.session.patch( |
46d06a12 | 806 | self.url("/api/v1/servers/localhost/zones/" + name), |
35f26cc5 CH |
807 | data=json.dumps(payload), |
808 | headers={'content-type': 'application/json'}) | |
809 | self.assertEquals(r.status_code, 422) | |
810 | self.assertIn('out of zone', r.json()['error']) | |
811 | ||
1d6b70f9 | 812 | def test_zone_rr_update_restricted_chars(self): |
6754ef71 | 813 | name, payload, zone = self.create_zone() |
1d6b70f9 CH |
814 | # replace with qname mismatch |
815 | rrset = { | |
816 | 'changetype': 'replace', | |
817 | 'name': 'test:' + name, | |
818 | 'type': 'NS', | |
6754ef71 | 819 | 'ttl': 3600, |
1d6b70f9 CH |
820 | 'records': [ |
821 | { | |
1d6b70f9 CH |
822 | "content": "ns1.bar.com.", |
823 | "disabled": False | |
824 | } | |
825 | ] | |
826 | } | |
827 | payload = {'rrsets': [rrset]} | |
828 | r = self.session.patch( | |
829 | self.url("/api/v1/servers/localhost/zones/" + name), | |
830 | data=json.dumps(payload), | |
831 | headers={'content-type': 'application/json'}) | |
832 | self.assertEquals(r.status_code, 422) | |
833 | self.assertIn('contains unsupported characters', r.json()['error']) | |
834 | ||
24cd86ca | 835 | def test_rrset_unknown_type(self): |
6754ef71 | 836 | name, payload, zone = self.create_zone() |
24cd86ca CH |
837 | rrset = { |
838 | 'changetype': 'replace', | |
839 | 'name': name, | |
840 | 'type': 'FAFAFA', | |
6754ef71 | 841 | 'ttl': 3600, |
24cd86ca CH |
842 | 'records': [ |
843 | { | |
24cd86ca CH |
844 | "content": "4.3.2.1", |
845 | "disabled": False | |
846 | } | |
847 | ] | |
848 | } | |
849 | payload = {'rrsets': [rrset]} | |
46d06a12 | 850 | r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), |
24cd86ca CH |
851 | headers={'content-type': 'application/json'}) |
852 | self.assertEquals(r.status_code, 422) | |
853 | self.assertIn('unknown type', r.json()['error']) | |
854 | ||
1e5b9ab9 CH |
855 | def test_create_zone_with_leading_space(self): |
856 | # Actual regression. | |
6754ef71 | 857 | name, payload, zone = self.create_zone() |
1e5b9ab9 CH |
858 | rrset = { |
859 | 'changetype': 'replace', | |
860 | 'name': name, | |
861 | 'type': 'A', | |
6754ef71 | 862 | 'ttl': 3600, |
1e5b9ab9 CH |
863 | 'records': [ |
864 | { | |
1e5b9ab9 CH |
865 | "content": " 4.3.2.1", |
866 | "disabled": False | |
867 | } | |
868 | ] | |
869 | } | |
870 | payload = {'rrsets': [rrset]} | |
46d06a12 | 871 | r = self.session.patch(self.url("/api/v1/servers/localhost/zones/" + name), data=json.dumps(payload), |
1e5b9ab9 CH |
872 | headers={'content-type': 'application/json'}) |
873 | self.assertEquals(r.status_code, 422) | |
874 | self.assertIn('Not in expected format', r.json()['error']) | |
875 | ||
c1374bdb | 876 | def test_zone_rr_delete_out_of_zone(self): |
6754ef71 | 877 | name, payload, zone = self.create_zone() |
d708640f | 878 | rrset = { |
35f26cc5 | 879 | 'changetype': 'delete', |
1d6b70f9 | 880 | 'name': 'not-in-zone.', |
35f26cc5 CH |
881 | 'type': 'NS' |
882 | } | |
d708640f | 883 | payload = {'rrsets': [rrset]} |
35f26cc5 | 884 | r = self.session.patch( |
46d06a12 | 885 | self.url("/api/v1/servers/localhost/zones/" + name), |
35f26cc5 CH |
886 | data=json.dumps(payload), |
887 | headers={'content-type': 'application/json'}) | |
34df6ecc | 888 | print r.content |
f0e76cee | 889 | self.assert_success(r) # succeed so users can fix their wrong, old data |
35f26cc5 | 890 | |
37663c3b | 891 | def test_zone_delete(self): |
6754ef71 | 892 | name, payload, zone = self.create_zone() |
46d06a12 | 893 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name)) |
37663c3b CH |
894 | self.assertEquals(r.status_code, 204) |
895 | self.assertNotIn('Content-Type', r.headers) | |
896 | ||
c1374bdb | 897 | def test_zone_comment_create(self): |
6754ef71 | 898 | name, payload, zone = self.create_zone() |
d708640f | 899 | rrset = { |
6cc98ddf CH |
900 | 'changetype': 'replace', |
901 | 'name': name, | |
902 | 'type': 'NS', | |
6754ef71 | 903 | 'ttl': 3600, |
6cc98ddf CH |
904 | 'comments': [ |
905 | { | |
906 | 'account': 'test1', | |
907 | 'content': 'blah blah', | |
908 | }, | |
909 | { | |
910 | 'account': 'test2', | |
911 | 'content': 'blah blah bleh', | |
912 | } | |
913 | ] | |
914 | } | |
d708640f | 915 | payload = {'rrsets': [rrset]} |
6cc98ddf | 916 | r = self.session.patch( |
46d06a12 | 917 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
918 | data=json.dumps(payload), |
919 | headers={'content-type': 'application/json'}) | |
f0e76cee | 920 | self.assert_success(r) |
6cc98ddf CH |
921 | # make sure the comments have been set, and that the NS |
922 | # records are still present | |
f0e76cee CH |
923 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
924 | serverset = get_rrset(data, name, 'NS') | |
6754ef71 CH |
925 | print serverset |
926 | self.assertNotEquals(serverset['records'], []) | |
927 | self.assertNotEquals(serverset['comments'], []) | |
6cc98ddf | 928 | # verify that modified_at has been set by pdns |
6754ef71 | 929 | self.assertNotEquals([c for c in serverset['comments']][0]['modified_at'], 0) |
6cc98ddf | 930 | |
c1374bdb | 931 | def test_zone_comment_delete(self): |
6cc98ddf | 932 | # Test: Delete ONLY comments. |
6754ef71 | 933 | name, payload, zone = self.create_zone() |
d708640f | 934 | rrset = { |
6cc98ddf CH |
935 | 'changetype': 'replace', |
936 | 'name': name, | |
937 | 'type': 'NS', | |
938 | 'comments': [] | |
939 | } | |
d708640f | 940 | payload = {'rrsets': [rrset]} |
6cc98ddf | 941 | r = self.session.patch( |
46d06a12 | 942 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
943 | data=json.dumps(payload), |
944 | headers={'content-type': 'application/json'}) | |
f0e76cee | 945 | self.assert_success(r) |
6cc98ddf | 946 | # make sure the NS records are still present |
f0e76cee CH |
947 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
948 | serverset = get_rrset(data, name, 'NS') | |
6754ef71 CH |
949 | print serverset |
950 | self.assertNotEquals(serverset['records'], []) | |
951 | self.assertEquals(serverset['comments'], []) | |
6cc98ddf | 952 | |
c1374bdb | 953 | def test_zone_comment_stay_intact(self): |
6cc98ddf | 954 | # Test if comments on an rrset stay intact if the rrset is replaced |
6754ef71 | 955 | name, payload, zone = self.create_zone() |
6cc98ddf | 956 | # create a comment |
d708640f | 957 | rrset = { |
6cc98ddf CH |
958 | 'changetype': 'replace', |
959 | 'name': name, | |
960 | 'type': 'NS', | |
961 | 'comments': [ | |
962 | { | |
963 | 'account': 'test1', | |
964 | 'content': 'oh hi there', | |
2696eea0 | 965 | 'modified_at': 1111 |
6cc98ddf CH |
966 | } |
967 | ] | |
968 | } | |
d708640f | 969 | payload = {'rrsets': [rrset]} |
6cc98ddf | 970 | r = self.session.patch( |
46d06a12 | 971 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
972 | data=json.dumps(payload), |
973 | headers={'content-type': 'application/json'}) | |
f0e76cee | 974 | self.assert_success(r) |
6cc98ddf | 975 | # replace rrset records |
d708640f | 976 | rrset2 = { |
6cc98ddf CH |
977 | 'changetype': 'replace', |
978 | 'name': name, | |
979 | 'type': 'NS', | |
6754ef71 | 980 | 'ttl': 3600, |
6cc98ddf CH |
981 | 'records': [ |
982 | { | |
1d6b70f9 | 983 | "content": "ns1.bar.com.", |
6cc98ddf CH |
984 | "disabled": False |
985 | } | |
986 | ] | |
987 | } | |
d708640f | 988 | payload2 = {'rrsets': [rrset2]} |
6cc98ddf | 989 | r = self.session.patch( |
46d06a12 | 990 | self.url("/api/v1/servers/localhost/zones/" + name), |
6cc98ddf CH |
991 | data=json.dumps(payload2), |
992 | headers={'content-type': 'application/json'}) | |
f0e76cee | 993 | self.assert_success(r) |
6cc98ddf | 994 | # make sure the comments still exist |
f0e76cee CH |
995 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json() |
996 | serverset = get_rrset(data, name, 'NS') | |
6754ef71 CH |
997 | print serverset |
998 | self.assertEquals(serverset['records'], rrset2['records']) | |
999 | self.assertEquals(serverset['comments'], rrset['comments']) | |
6cc98ddf | 1000 | |
3fe7c7d6 CH |
1001 | def test_zone_auto_ptr_ipv4_create(self): |
1002 | revzone = '4.2.192.in-addr.arpa.' | |
1003 | _, _, revzonedata = self.create_zone(name=revzone) | |
1004 | name = unique_zone_name() | |
1005 | rrset = { | |
1006 | "name": name, | |
1007 | "type": "A", | |
1008 | "ttl": 3600, | |
1009 | "records": [{ | |
1010 | "content": "192.2.4.44", | |
1011 | "disabled": False, | |
1012 | "set-ptr": True, | |
1013 | }], | |
1014 | } | |
1015 | name, payload, data = self.create_zone(name=name, rrsets=[rrset]) | |
1016 | del rrset['records'][0]['set-ptr'] | |
1017 | self.assertEquals(get_rrset(data, name, 'A')['records'], rrset['records']) | |
1018 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)).json() | |
1019 | revsets = [s for s in r['rrsets'] if s['type'] == 'PTR'] | |
1020 | print revsets | |
1021 | self.assertEquals(revsets, [{ | |
1022 | u'name': u'44.4.2.192.in-addr.arpa.', | |
1023 | u'ttl': 3600, | |
1024 | u'type': u'PTR', | |
1025 | u'comments': [], | |
1026 | u'records': [{ | |
1027 | u'content': name, | |
1028 | u'disabled': False, | |
1029 | }], | |
1030 | }]) | |
1031 | # with SOA-EDIT-API DEFAULT on the revzone, the serial should now be higher. | |
1032 | self.assertGreater(r['serial'], revzonedata['serial']) | |
1033 | ||
1034 | def test_zone_auto_ptr_ipv4_update(self): | |
1d6b70f9 | 1035 | revzone = '0.2.192.in-addr.arpa.' |
a41c038a | 1036 | _, _, revzonedata = self.create_zone(name=revzone) |
6754ef71 | 1037 | name, payload, zone = self.create_zone() |
d708640f | 1038 | rrset = { |
d1587ceb CH |
1039 | 'changetype': 'replace', |
1040 | 'name': name, | |
1041 | 'type': 'A', | |
6754ef71 | 1042 | 'ttl': 3600, |
d1587ceb CH |
1043 | 'records': [ |
1044 | { | |
d1587ceb CH |
1045 | "content": '192.2.0.2', |
1046 | "disabled": False, | |
1047 | "set-ptr": True | |
1048 | } | |
1049 | ] | |
1050 | } | |
d708640f | 1051 | payload = {'rrsets': [rrset]} |
d1587ceb | 1052 | r = self.session.patch( |
46d06a12 | 1053 | self.url("/api/v1/servers/localhost/zones/" + name), |
d1587ceb CH |
1054 | data=json.dumps(payload), |
1055 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1056 | self.assert_success(r) |
a41c038a CH |
1057 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)).json() |
1058 | revsets = [s for s in r['rrsets'] if s['type'] == 'PTR'] | |
6754ef71 CH |
1059 | print revsets |
1060 | self.assertEquals(revsets, [{ | |
1061 | u'name': u'2.0.2.192.in-addr.arpa.', | |
d1587ceb | 1062 | u'ttl': 3600, |
d1587ceb | 1063 | u'type': u'PTR', |
6754ef71 CH |
1064 | u'comments': [], |
1065 | u'records': [{ | |
1066 | u'content': name, | |
1067 | u'disabled': False, | |
1068 | }], | |
d1587ceb | 1069 | }]) |
a41c038a CH |
1070 | # with SOA-EDIT-API DEFAULT on the revzone, the serial should now be higher. |
1071 | self.assertGreater(r['serial'], revzonedata['serial']) | |
d1587ceb | 1072 | |
3fe7c7d6 | 1073 | def test_zone_auto_ptr_ipv6_update(self): |
d1587ceb | 1074 | # 2001:DB8::bb:aa |
1d6b70f9 | 1075 | revzone = '8.b.d.0.1.0.0.2.ip6.arpa.' |
a41c038a | 1076 | _, _, revzonedata = self.create_zone(name=revzone) |
6754ef71 | 1077 | name, payload, zone = self.create_zone() |
d708640f | 1078 | rrset = { |
d1587ceb CH |
1079 | 'changetype': 'replace', |
1080 | 'name': name, | |
1081 | 'type': 'AAAA', | |
6754ef71 | 1082 | 'ttl': 3600, |
d1587ceb CH |
1083 | 'records': [ |
1084 | { | |
d1587ceb CH |
1085 | "content": '2001:DB8::bb:aa', |
1086 | "disabled": False, | |
1087 | "set-ptr": True | |
1088 | } | |
1089 | ] | |
1090 | } | |
d708640f | 1091 | payload = {'rrsets': [rrset]} |
d1587ceb | 1092 | r = self.session.patch( |
46d06a12 | 1093 | self.url("/api/v1/servers/localhost/zones/" + name), |
d1587ceb CH |
1094 | data=json.dumps(payload), |
1095 | headers={'content-type': 'application/json'}) | |
f0e76cee | 1096 | self.assert_success(r) |
a41c038a CH |
1097 | r = self.session.get(self.url("/api/v1/servers/localhost/zones/" + revzone)).json() |
1098 | revsets = [s for s in r['rrsets'] if s['type'] == 'PTR'] | |
6754ef71 CH |
1099 | print revsets |
1100 | self.assertEquals(revsets, [{ | |
1101 | u'name': u'a.a.0.0.b.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.', | |
d1587ceb | 1102 | u'ttl': 3600, |
d1587ceb | 1103 | u'type': u'PTR', |
6754ef71 CH |
1104 | u'comments': [], |
1105 | u'records': [{ | |
1106 | u'content': name, | |
1107 | u'disabled': False, | |
1108 | }], | |
d1587ceb | 1109 | }]) |
a41c038a CH |
1110 | # with SOA-EDIT-API DEFAULT on the revzone, the serial should now be higher. |
1111 | self.assertGreater(r['serial'], revzonedata['serial']) | |
d1587ceb | 1112 | |
c1374bdb | 1113 | def test_search_rr_exact_zone(self): |
b1902fab | 1114 | name = unique_zone_name() |
1d6b70f9 CH |
1115 | self.create_zone(name=name, serial=22, soa_edit_api='') |
1116 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name.rstrip('.'))) | |
c1374bdb | 1117 | self.assert_success_json(r) |
b1902fab | 1118 | print r.json() |
1d6b70f9 CH |
1119 | self.assertEquals(r.json(), [ |
1120 | {u'object_type': u'zone', u'name': name, u'zone_id': name}, | |
1121 | {u'content': u'a.misconfigured.powerdns.server. hostmaster.'+name+' 22 10800 3600 604800 3600', | |
1122 | u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False, | |
1123 | u'ttl': 3600, u'type': u'SOA', u'name': name}, | |
1124 | {u'content': u'ns1.example.com.', | |
1125 | u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False, | |
1126 | u'ttl': 3600, u'type': u'NS', u'name': name}, | |
1127 | {u'content': u'ns2.example.com.', | |
1128 | u'zone_id': name, u'zone': name, u'object_type': u'record', u'disabled': False, | |
1129 | u'ttl': 3600, u'type': u'NS', u'name': name}, | |
1130 | ]) | |
b1902fab | 1131 | |
c1374bdb | 1132 | def test_search_rr_substring(self): |
1d6b70f9 | 1133 | name = 'search-rr-zone.name.' |
b1902fab | 1134 | self.create_zone(name=name) |
46d06a12 | 1135 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*rr-zone*")) |
c1374bdb | 1136 | self.assert_success_json(r) |
b1902fab CH |
1137 | print r.json() |
1138 | # should return zone, SOA, ns1, ns2 | |
60a8e825 | 1139 | self.assertEquals(len(r.json()), 4) |
b1902fab | 1140 | |
c1374bdb | 1141 | def test_search_rr_case_insensitive(self): |
1d6b70f9 | 1142 | name = 'search-rr-insenszone.name.' |
57cb86d8 | 1143 | self.create_zone(name=name) |
46d06a12 | 1144 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=*rr-insensZONE*")) |
c1374bdb | 1145 | self.assert_success_json(r) |
57cb86d8 CH |
1146 | print r.json() |
1147 | # should return zone, SOA, ns1, ns2 | |
60a8e825 | 1148 | self.assertEquals(len(r.json()), 4) |
57cb86d8 | 1149 | |
02945d9a | 1150 | |
406497f5 CH |
1151 | @unittest.skipIf(not is_auth(), "Not applicable") |
1152 | class AuthRootZone(ApiTestCase, AuthZonesHelperMixin): | |
1153 | ||
1154 | def setUp(self): | |
1155 | super(AuthRootZone, self).setUp() | |
1156 | # zone name is not unique, so delete the zone before each individual test. | |
46d06a12 | 1157 | self.session.delete(self.url("/api/v1/servers/localhost/zones/=2E")) |
406497f5 CH |
1158 | |
1159 | def test_create_zone(self): | |
6754ef71 | 1160 | name, payload, data = self.create_zone(name='.', serial=22, soa_edit_api='') |
406497f5 CH |
1161 | for k in ('id', 'url', 'name', 'masters', 'kind', 'last_check', 'notified_serial', 'serial', 'soa_edit_api', 'soa_edit', 'account'): |
1162 | self.assertIn(k, data) | |
1163 | if k in payload: | |
1164 | self.assertEquals(data[k], payload[k]) | |
406497f5 | 1165 | # validate generated SOA |
6754ef71 | 1166 | rec = get_first_rec(data, '.', 'SOA') |
406497f5 | 1167 | self.assertEquals( |
6754ef71 | 1168 | rec['content'], |
1d6b70f9 | 1169 | "a.misconfigured.powerdns.server. hostmaster. " + str(payload['serial']) + |
406497f5 CH |
1170 | " 10800 3600 604800 3600" |
1171 | ) | |
1172 | # Regression test: verify zone list works | |
46d06a12 | 1173 | zonelist = self.session.get(self.url("/api/v1/servers/localhost/zones")).json() |
406497f5 CH |
1174 | print "zonelist:", zonelist |
1175 | self.assertIn(payload['name'], [zone['name'] for zone in zonelist]) | |
1176 | # Also test that fetching the zone works. | |
1177 | print "id:", data['id'] | |
1178 | self.assertEquals(data['id'], '=2E') | |
46d06a12 | 1179 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + data['id'])).json() |
406497f5 CH |
1180 | print "zone (fetched):", data |
1181 | for k in ('name', 'kind'): | |
1182 | self.assertIn(k, data) | |
1183 | self.assertEquals(data[k], payload[k]) | |
6754ef71 | 1184 | self.assertEqual(data['rrsets'][0]['name'], '.') |
406497f5 CH |
1185 | |
1186 | def test_update_zone(self): | |
6754ef71 | 1187 | name, payload, zone = self.create_zone(name='.') |
406497f5 CH |
1188 | zone_id = '=2E' |
1189 | # update, set as Master and enable SOA-EDIT-API | |
1190 | payload = { | |
1191 | 'kind': 'Master', | |
1192 | 'masters': ['192.0.2.1', '192.0.2.2'], | |
1193 | 'soa_edit_api': 'EPOCH', | |
1194 | 'soa_edit': 'EPOCH' | |
1195 | } | |
1196 | r = self.session.put( | |
46d06a12 | 1197 | self.url("/api/v1/servers/localhost/zones/" + zone_id), |
406497f5 CH |
1198 | data=json.dumps(payload), |
1199 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
1200 | self.assert_success(r) |
1201 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + zone_id)).json() | |
406497f5 CH |
1202 | for k in payload.keys(): |
1203 | self.assertIn(k, data) | |
1204 | self.assertEquals(data[k], payload[k]) | |
1205 | # update, back to Native and empty(off) | |
1206 | payload = { | |
1207 | 'kind': 'Native', | |
1208 | 'soa_edit_api': '', | |
1209 | 'soa_edit': '' | |
1210 | } | |
1211 | r = self.session.put( | |
46d06a12 | 1212 | self.url("/api/v1/servers/localhost/zones/" + zone_id), |
406497f5 CH |
1213 | data=json.dumps(payload), |
1214 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
1215 | self.assert_success(r) |
1216 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + zone_id)).json() | |
406497f5 CH |
1217 | for k in payload.keys(): |
1218 | self.assertIn(k, data) | |
1219 | self.assertEquals(data[k], payload[k]) | |
1220 | ||
1221 | ||
c1374bdb | 1222 | @unittest.skipIf(not is_recursor(), "Not applicable") |
02945d9a CH |
1223 | class RecursorZones(ApiTestCase): |
1224 | ||
37bc3d01 CH |
1225 | def create_zone(self, name=None, kind=None, rd=False, servers=None): |
1226 | if name is None: | |
1227 | name = unique_zone_name() | |
1228 | if servers is None: | |
1229 | servers = [] | |
02945d9a | 1230 | payload = { |
37bc3d01 CH |
1231 | 'name': name, |
1232 | 'kind': kind, | |
1233 | 'servers': servers, | |
1234 | 'recursion_desired': rd | |
02945d9a CH |
1235 | } |
1236 | r = self.session.post( | |
46d06a12 | 1237 | self.url("/api/v1/servers/localhost/zones"), |
02945d9a CH |
1238 | data=json.dumps(payload), |
1239 | headers={'content-type': 'application/json'}) | |
c1374bdb CH |
1240 | self.assert_success_json(r) |
1241 | return payload, r.json() | |
37bc3d01 | 1242 | |
c1374bdb | 1243 | def test_create_auth_zone(self): |
37bc3d01 | 1244 | payload, data = self.create_zone(kind='Native') |
02945d9a CH |
1245 | for k in payload.keys(): |
1246 | self.assertEquals(data[k], payload[k]) | |
1247 | ||
1d6b70f9 CH |
1248 | def test_create_zone_no_name(self): |
1249 | name = unique_zone_name() | |
1250 | payload = { | |
1251 | 'name': '', | |
1252 | 'kind': 'Native', | |
1253 | 'servers': ['8.8.8.8'], | |
1254 | 'recursion_desired': False, | |
1255 | } | |
1256 | print payload | |
1257 | r = self.session.post( | |
1258 | self.url("/api/v1/servers/localhost/zones"), | |
1259 | data=json.dumps(payload), | |
1260 | headers={'content-type': 'application/json'}) | |
1261 | self.assertEquals(r.status_code, 422) | |
1262 | self.assertIn('is not canonical', r.json()['error']) | |
1263 | ||
c1374bdb | 1264 | def test_create_forwarded_zone(self): |
37bc3d01 | 1265 | payload, data = self.create_zone(kind='Forwarded', rd=False, servers=['8.8.8.8']) |
02945d9a CH |
1266 | # return values are normalized |
1267 | payload['servers'][0] += ':53' | |
02945d9a CH |
1268 | for k in payload.keys(): |
1269 | self.assertEquals(data[k], payload[k]) | |
1270 | ||
c1374bdb | 1271 | def test_create_forwarded_rd_zone(self): |
1d6b70f9 | 1272 | payload, data = self.create_zone(name='google.com.', kind='Forwarded', rd=True, servers=['8.8.8.8']) |
02945d9a CH |
1273 | # return values are normalized |
1274 | payload['servers'][0] += ':53' | |
02945d9a CH |
1275 | for k in payload.keys(): |
1276 | self.assertEquals(data[k], payload[k]) | |
1277 | ||
c1374bdb | 1278 | def test_create_auth_zone_with_symbols(self): |
37bc3d01 | 1279 | payload, data = self.create_zone(name='foo/bar.'+unique_zone_name(), kind='Native') |
1dbe38ba | 1280 | expected_id = (payload['name'].replace('/', '=2F')) |
02945d9a CH |
1281 | for k in payload.keys(): |
1282 | self.assertEquals(data[k], payload[k]) | |
1283 | self.assertEquals(data['id'], expected_id) | |
e2367534 | 1284 | |
c1374bdb | 1285 | def test_rename_auth_zone(self): |
37bc3d01 | 1286 | payload, data = self.create_zone(kind='Native') |
1d6b70f9 | 1287 | name = payload['name'] |
e2367534 CH |
1288 | # now rename it |
1289 | payload = { | |
1290 | 'name': 'renamed-'+name, | |
1291 | 'kind': 'Native', | |
1292 | 'recursion_desired': False | |
1293 | } | |
1294 | r = self.session.put( | |
46d06a12 | 1295 | self.url("/api/v1/servers/localhost/zones/" + name), |
e2367534 CH |
1296 | data=json.dumps(payload), |
1297 | headers={'content-type': 'application/json'}) | |
f0e76cee CH |
1298 | self.assert_success(r) |
1299 | data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + payload['name'])).json() | |
e2367534 CH |
1300 | for k in payload.keys(): |
1301 | self.assertEquals(data[k], payload[k]) | |
37bc3d01 | 1302 | |
37663c3b CH |
1303 | def test_zone_delete(self): |
1304 | payload, zone = self.create_zone(kind='Native') | |
1305 | name = payload['name'] | |
46d06a12 | 1306 | r = self.session.delete(self.url("/api/v1/servers/localhost/zones/" + name)) |
37663c3b CH |
1307 | self.assertEquals(r.status_code, 204) |
1308 | self.assertNotIn('Content-Type', r.headers) | |
1309 | ||
c1374bdb | 1310 | def test_search_rr_exact_zone(self): |
1d6b70f9 | 1311 | name = unique_zone_name() |
37bc3d01 | 1312 | self.create_zone(name=name, kind='Native') |
46d06a12 | 1313 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=" + name)) |
c1374bdb | 1314 | self.assert_success_json(r) |
37bc3d01 CH |
1315 | print r.json() |
1316 | self.assertEquals(r.json(), [{u'type': u'zone', u'name': name, u'zone_id': name}]) | |
1317 | ||
c1374bdb | 1318 | def test_search_rr_substring(self): |
1d6b70f9 | 1319 | name = 'search-rr-zone.name.' |
37bc3d01 | 1320 | self.create_zone(name=name, kind='Native') |
46d06a12 | 1321 | r = self.session.get(self.url("/api/v1/servers/localhost/search-data?q=rr-zone")) |
c1374bdb | 1322 | self.assert_success_json(r) |
37bc3d01 CH |
1323 | print r.json() |
1324 | # should return zone, SOA | |
1325 | self.assertEquals(len(r.json()), 2) | |
ccfabd0d CH |
1326 | |
1327 | ||
1328 | @unittest.skipIf(not is_auth(), "Not applicable") | |
1329 | class AuthZoneKeys(ApiTestCase, AuthZonesHelperMixin): | |
1330 | ||
1331 | def test_get_keys(self): | |
1332 | r = self.session.get( | |
1333 | self.url("/api/v1/servers/localhost/zones/powerdnssec.org./cryptokeys")) | |
1334 | self.assert_success_json(r) | |
1335 | keys = r.json() | |
1336 | self.assertGreater(len(keys), 0) | |
1337 | ||
1338 | key0 = deepcopy(keys[0]) | |
1339 | del key0['dnskey'] | |
b6bd795c | 1340 | del key0['ds'] |
ccfabd0d CH |
1341 | expected = { |
1342 | u'active': True, | |
1343 | u'type': u'Cryptokey', | |
b6bd795c PL |
1344 | u'keytype': u'csk', |
1345 | u'flags': 257, | |
ccfabd0d CH |
1346 | u'id': 1} |
1347 | self.assertEquals(key0, expected) | |
1348 | ||
1349 | keydata = keys[0]['dnskey'].split() | |
1350 | self.assertEqual(len(keydata), 4) |