From 01f8431d7440a4fa45eb76e40a2201e4ec7b7f7f Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Thu, 13 Jul 2017 11:44:09 -0700 Subject: [PATCH] Make formatting consistent and improve record type support. --- octodns/provider/rackspace.py | 299 ++++++++++-------- ...sample-recordset-existing-nameservers.json | 6 +- tests/test_octodns_provider_rackspace.py | 152 ++++++--- 3 files changed, 281 insertions(+), 176 deletions(-) diff --git a/octodns/provider/rackspace.py b/octodns/provider/rackspace.py index eef20d9..110412b 100644 --- a/octodns/provider/rackspace.py +++ b/octodns/provider/rackspace.py @@ -12,6 +12,33 @@ from ..record import Create, Record from .base import BaseProvider +def add_trailing_dot(s): + assert s + assert s[-1] != '.' + return s + '.' + + +def remove_trailing_dot(s): + assert s + assert s[-1] == '.' + return s[:-1] + + +def strip_quotes(s): + assert s + assert len(s) > 2 + assert s[0] == '"' + assert s[-1] == '"' + return s[1:-1] + + +def add_quotes(s): + assert s + assert s[0] != '"' + assert s[-1] != '"' + return '"{}"'.format(s) + + class RackspaceProvider(BaseProvider): SUPPORTS_GEO = False TIMEOUT = 5 @@ -101,6 +128,10 @@ class RackspaceProvider(BaseProvider): def _delete(self, path, data=None): return self._request('DELETE', path, data=data) + @staticmethod + def _key_for_record(rs_record): + return rs_record['type'], rs_record['name'], rs_record['data'] + def _data_for_multiple(self, rrset): # TODO: geo not supported return { @@ -116,14 +147,14 @@ class RackspaceProvider(BaseProvider): # TODO: geo not supported return { 'type': rrset[0]['type'], - 'values': ["{}.".format(r['data']) for r in rrset], + 'values': [add_trailing_dot(r['data']) for r in rrset], 'ttl': rrset[0]['ttl'] } def _data_for_single(self, record): return { 'type': record[0]['type'], - 'value': "{}.".format(record[0]['data']), + 'value': add_trailing_dot(record[0]['data']), 'ttl': record[0]['ttl'] } @@ -134,7 +165,7 @@ class RackspaceProvider(BaseProvider): def _data_for_quoted(self, rrset): return { 'type': rrset['type'], - 'values': [r['content'][1:-1] for r in rrset['records']], + 'values': [strip_quotes(r['content']) for r in rrset['records']], 'ttl': rrset['ttl'] } @@ -146,7 +177,7 @@ class RackspaceProvider(BaseProvider): for record in rrset: values.append({ 'priority': record['priority'], - 'value': record['data'], + 'value': add_trailing_dot(record['data']), }) return { 'type': rrset[0]['type'], @@ -155,56 +186,59 @@ class RackspaceProvider(BaseProvider): } def _data_for_NAPTR(self, rrset): - values = [] - for record in rrset['records']: - order, preference, flags, service, regexp, replacement = \ - record['content'].split(' ', 5) - values.append({ - 'order': order, - 'preference': preference, - 'flags': flags[1:-1], - 'service': service[1:-1], - 'regexp': regexp[1:-1], - 'replacement': replacement, - }) - return { - 'type': rrset['type'], - 'values': values, - 'ttl': rrset['ttl'] - } + raise NotImplementedError("Missing support for reading NAPTR records") + # values = [] + # for record in rrset['records']: + # order, preference, flags, service, regexp, replacement = \ + # record['content'].split(' ', 5) + # values.append({ + # 'order': order, + # 'preference': preference, + # 'flags': flags[1:-1], + # 'service': service[1:-1], + # 'regexp': regexp[1:-1], + # 'replacement': replacement, + # }) + # return { + # 'type': rrset['type'], + # 'values': values, + # 'ttl': rrset['ttl'] + # } def _data_for_SSHFP(self, rrset): - values = [] - for record in rrset['records']: - algorithm, fingerprint_type, fingerprint = \ - record['content'].split(' ', 2) - values.append({ - 'algorithm': algorithm, - 'fingerprint_type': fingerprint_type, - 'fingerprint': fingerprint, - }) - return { - 'type': rrset['type'], - 'values': values, - 'ttl': rrset['ttl'] - } + raise NotImplementedError("Missing support for reading SSHFP records") + # values = [] + # for record in rrset['records']: + # algorithm, fingerprint_type, fingerprint = \ + # record['content'].split(' ', 2) + # values.append({ + # 'algorithm': algorithm, + # 'fingerprint_type': fingerprint_type, + # 'fingerprint': fingerprint, + # }) + # return { + # 'type': rrset['type'], + # 'values': values, + # 'ttl': rrset['ttl'] + # } def _data_for_SRV(self, rrset): - values = [] - for record in rrset['records']: - priority, weight, port, target = \ - record['content'].split(' ', 3) - values.append({ - 'priority': priority, - 'weight': weight, - 'port': port, - 'target': target, - }) - return { - 'type': rrset['type'], - 'values': values, - 'ttl': rrset['ttl'] - } + raise NotImplementedError("Missing support for reading SRV records") + # values = [] + # for record in rrset['records']: + # priority, weight, port, target = \ + # record['content'].split(' ', 3) + # values.append({ + # 'priority': priority, + # 'weight': weight, + # 'port': port, + # 'target': target, + # }) + # return { + # 'type': rrset['type'], + # 'values': values, + # 'ttl': rrset['ttl'] + # } def populate(self, zone, target=False): self.log.debug('populate: name=%s', zone.name) @@ -217,14 +251,10 @@ class RackspaceProvider(BaseProvider): if e.response.status_code == 401: # Nicer error message for auth problems raise Exception('Rackspace request unauthorized') - elif e.response.status_code == 422: - # 422 means powerdns doesn't know anything about the requsted - # domain. We'll just ignore it here and leave the zone - # untouched. - pass - else: - # just re-throw - raise + elif e.response.status_code == 404: + # Zone not found leaves the zone empty instead of failing. + return + raise before = len(zone.records) @@ -246,70 +276,82 @@ class RackspaceProvider(BaseProvider): def _group_records(self, all_records): records = defaultdict(lambda: defaultdict(list)) for record in all_records: - self._id_map[(record['type'], record['name'], record['data'])] = record['id'] + self._id_map[self._key_for_record(record)] = record['id'] records[record['type']][record['name']].append(record) return records - def _records_for_multiple(self, record): - return [{'content': v, 'disabled': False} - for v in record.values] - - _records_for_A = _records_for_multiple - _records_for_AAAA = _records_for_multiple - _records_for_NS = _records_for_multiple - - def _records_for_single(self, record): - return [{'content': record.value, 'disabled': False}] - - _records_for_ALIAS = _records_for_single - _records_for_CNAME = _records_for_single - _records_for_PTR = _records_for_single - - def _records_for_quoted(self, record): - return [{'content': '"{}"'.format(v), 'disabled': False} - for v in record.values] - - _records_for_SPF = _records_for_quoted - _records_for_TXT = _records_for_quoted - - def _records_for_MX(self, record): - return [{ - 'content': '{} {}'.format(v.priority, v.value), - 'disabled': False - } for v in record.values] - - def _records_for_NAPTR(self, record): - return [{ - 'content': '{} {} "{}" "{}" "{}" {}'.format(v.order, v.preference, - v.flags, v.service, - v.regexp, - v.replacement), - 'disabled': False - } for v in record.values] - - def _records_for_SSHFP(self, record): - return [{ - 'content': '{} {} {}'.format(v.algorithm, v.fingerprint_type, - v.fingerprint), - 'disabled': False - } for v in record.values] - - def _records_for_SRV(self, record): - return [{ - 'content': '{} {} {} {}'.format(v.priority, v.weight, v.port, - v.target), - 'disabled': False - } for v in record.values] + @staticmethod + def _record_for_ip(record, value): + return { + 'name': record.fqdn, + 'type': record._type, + 'data': value, + 'ttl': max(record.ttl, 300), + } + _record_for_A = _record_for_ip + _record_for_AAAA = _record_for_ip + + @staticmethod + def _record_for_named(record, value): + return { + 'name': record.fqdn, + 'type': record._type, + 'data': remove_trailing_dot(value), + 'ttl': max(record.ttl, 300), + } + _record_for_NS = _record_for_named + _record_for_ALIAS = _record_for_named + _record_for_CNAME = _record_for_named + _record_for_PTR = _record_for_named + + @staticmethod + def _record_for_quoted(record, value): + return { + 'name': record.fqdn, + 'type': record._type, + 'data': add_quotes(value), + 'ttl': max(record.ttl, 300), + } + _record_for_SPF = _record_for_quoted + _record_for_TXT = _record_for_quoted + + @staticmethod + def _record_for_MX(record, value): + return { + 'name': record.fqdn, + 'type': record._type, + 'data': remove_trailing_dot(value), + 'ttl': max(record.ttl, 300), + 'priority': record.priority + } + + @staticmethod + def _record_for_SRV(record, value): + raise NotImplementedError("Missing support for writing SRV records") + + def _record_for_NAPTR(self, record): + raise NotImplementedError("Missing support for writing NAPTR records") + # return [{ + # 'content': '{} {} "{}" "{}" "{}" {}'.format(v.order, v.preference, + # v.flags, v.service, + # v.regexp, + # v.replacement), + # 'disabled': False + # } for v in record.values] + + def _record_for_SSHFP(self, record): + raise NotImplementedError("Missing support for writing SSHFP records") + # return [{ + # 'content': '{} {} {}'.format(v.algorithm, v.fingerprint_type, + # v.fingerprint), + # 'disabled': False + # } for v in record.values] def _mod_Create(self, change): out = [] for value in change.new.values: - out.append({ - 'name': change.new.fqdn, - 'type': change.new._type, - 'data': value, - 'ttl': change.new.ttl, - }) + transformer = getattr(self, "_record_for_{}".format(change.new._type)) + out.append(transformer(change.new, value)) return out def _mod_Update(self, change): @@ -320,14 +362,16 @@ class RackspaceProvider(BaseProvider): update_out = [] for value in change.new.values: - key = (change.existing._type, change.existing.fqdn, value) - rsid = self._id_map[key] - update_out.append({ - 'id': rsid, - 'name': change.new.fqdn, - 'data': value, - 'ttl': change.new.ttl, - }) + transformer = getattr(self, "_record_for_{}".format(change.new._type)) + prior_rs_record = transformer(change.existing, value) + prior_key = self._key_for_record(prior_rs_record) + next_rs_record = transformer(change.new, value) + next_key = self._key_for_record(prior_rs_record) + next_rs_record["id"] = self._id_map[prior_key] + del next_rs_record["type"] + update_out.append(next_rs_record) + self._id_map[next_key] = self._id_map[prior_key] + del self._id_map[prior_key] return update_out, delete_out def _mod_Delete(self, change): @@ -336,9 +380,10 @@ class RackspaceProvider(BaseProvider): def _delete_given_change_values(self, change, values): out = [] for value in values: - key = (change.existing._type, change.existing.fqdn, value) - rsid = self._id_map[key] - out.append('id=' + rsid) + transformer = getattr(self, "_record_for_{}".format(change.existing._type)) + rs_record = transformer(change.existing, value) + key = self._key_for_record(rs_record) + out.append('id=' + self._id_map[key]) del self._id_map[key] return out @@ -363,7 +408,7 @@ class RackspaceProvider(BaseProvider): deletes += self._mod_Delete(change) if creates: - data = {"records": sorted(creates, key=lambda v: v['name'])} + data = {"records": sorted(creates, key=lambda v: v['type'] + v['name'] + v.get('data', ''))} self._post('domains/{}/records'.format(domain_id), data=data) if updates: diff --git a/tests/fixtures/rackspace-sample-recordset-existing-nameservers.json b/tests/fixtures/rackspace-sample-recordset-existing-nameservers.json index 034aa44..3e0f9cd 100644 --- a/tests/fixtures/rackspace-sample-recordset-existing-nameservers.json +++ b/tests/fixtures/rackspace-sample-recordset-existing-nameservers.json @@ -6,13 +6,13 @@ "type" : "A", "data" : "1.2.3.4", "updated" : "2011-06-24T01:12:53.000+0000", - "ttl" : 60, + "ttl" : 600, "created" : "2011-06-24T01:12:53.000+0000" }, { "name" : "unit.tests.", "id" : "NS-454454", "type" : "NS", - "data" : "8.8.8.8.", + "data" : "ns1.example.com", "updated" : "2011-06-24T01:12:51.000+0000", "ttl" : 600, "created" : "2011-06-24T01:12:51.000+0000" @@ -20,7 +20,7 @@ "name" : "unit.tests.", "id" : "NS-454455", "type" : "NS", - "data" : "9.9.9.9.", + "data" : "ns2.example.com", "updated" : "2011-06-24T01:12:52.000+0000", "ttl" : 600, "created" : "2011-06-24T01:12:52.000+0000" diff --git a/tests/test_octodns_provider_rackspace.py b/tests/test_octodns_provider_rackspace.py index 4c53e95..864f940 100644 --- a/tests/test_octodns_provider_rackspace.py +++ b/tests/test_octodns_provider_rackspace.py @@ -43,8 +43,10 @@ with open('./tests/fixtures/rackspace-sample-recordset-page2.json') as fh: with open('./tests/fixtures/rackspace-sample-recordset-existing-nameservers.json') as fh: RECORDS_EXISTING_NAMESERVERS = fh.read() + class TestRackspaceProvider(TestCase): def setUp(self): + self.maxDiff = 1000 with requests_mock() as mock: mock.post(ANY, status_code=200, text=AUTH_RESPONSE) self.provider = RackspaceProvider('test', 'api-key') @@ -73,7 +75,7 @@ class TestRackspaceProvider(TestCase): def test_nonexistent_zone(self): # Non-existent zone doesn't populate anything with requests_mock() as mock: - mock.get(ANY, status_code=422, + mock.get(ANY, status_code=404, json={'error': "Could not find domain 'unit.tests.'"}) zone = Zone('unit.tests.', []) @@ -196,7 +198,7 @@ class TestRackspaceProvider(TestCase): "subdomain": '', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'values': ['1.2.3.4', '1.2.3.5', '1.2.3.6'] } } @@ -208,19 +210,19 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "id": "A-222222", "type": "A", "data": "1.2.3.5", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "id": "A-333333", "type": "A", "data": "1.2.3.6", - "ttl": 60 + "ttl": 300 }] } ExpectChanges = False @@ -236,7 +238,7 @@ class TestRackspaceProvider(TestCase): "subdomain": 'foo', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'value': '1.2.3.4' } }, @@ -244,7 +246,7 @@ class TestRackspaceProvider(TestCase): "subdomain": 'bar', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'value': '1.2.3.4' } } @@ -256,13 +258,13 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "bar.unit.tests.", "id": "A-222222", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }] } ExpectChanges = False @@ -278,9 +280,17 @@ class TestRackspaceProvider(TestCase): "subdomain": '', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'value': '1.2.3.4' } + }, + { + "subdomain": 'foo', + "data": { + 'type': 'NS', + 'ttl': 300, + 'value': 'ns.example.com.' + } } ] OwnRecords = { @@ -293,7 +303,12 @@ class TestRackspaceProvider(TestCase): "name": "unit.tests.", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 + }, { + "name": "foo.unit.tests.", + "type": "NS", + "data": "ns.example.com", + "ttl": 300 }] } ExpectedDeletions = None @@ -307,9 +322,17 @@ class TestRackspaceProvider(TestCase): "subdomain": '', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'values': ['1.2.3.4', '1.2.3.5', '1.2.3.6'] } + }, + { + "subdomain": 'foo', + "data": { + 'type': 'NS', + 'ttl': 300, + 'values': ['ns1.example.com.', 'ns2.example.com.'] + } } ] OwnRecords = { @@ -322,17 +345,27 @@ class TestRackspaceProvider(TestCase): "name": "unit.tests.", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "type": "A", "data": "1.2.3.5", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "type": "A", "data": "1.2.3.6", - "ttl": 60 + "ttl": 300 + }, { + "name": "foo.unit.tests.", + "type": "NS", + "data": "ns1.example.com", + "ttl": 300 + }, { + "name": "foo.unit.tests.", + "type": "NS", + "data": "ns2.example.com", + "ttl": 300 }] } ExpectedDeletions = None @@ -345,16 +378,24 @@ class TestRackspaceProvider(TestCase): "subdomain": 'foo', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'value': '1.2.3.4' } }, { "subdomain": 'bar', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'value': '1.2.3.4' } + }, + { + "subdomain": 'foo', + "data": { + 'type': 'NS', + 'ttl': 300, + 'value': 'ns.example.com.' + } }] OwnRecords = { "totalEntries": 0, @@ -366,12 +407,17 @@ class TestRackspaceProvider(TestCase): "name": "bar.unit.tests.", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "foo.unit.tests.", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 + }, { + "name": "foo.unit.tests.", + "type": "NS", + "data": "ns.example.com", + "ttl": 300 }] } ExpectedDeletions = None @@ -388,12 +434,18 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 + }, { + "name": "foo.unit.tests.", + "id": "NS-111111", + "type": "NS", + "data": "ns.example.com", + "ttl": 300 }] } ExpectChanges = True ExpectedAdditions = None - ExpectedDeletions = "id=A-111111" + ExpectedDeletions = "id=A-111111&id=NS-111111" ExpectedUpdates = None return self._test_apply_with_data(TestData) @@ -404,7 +456,7 @@ class TestRackspaceProvider(TestCase): "subdomain": '', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'value': '1.2.3.5' } } @@ -416,30 +468,36 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "id": "A-222222", "type": "A", "data": "1.2.3.5", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "id": "A-333333", "type": "A", "data": "1.2.3.6", - "ttl": 60 + "ttl": 300 + }, { + "name": "foo.unit.tests.", + "id": "NS-111111", + "type": "NS", + "data": "ns.example.com", + "ttl": 300 }] } ExpectChanges = True ExpectedAdditions = None - ExpectedDeletions = "id=A-111111&id=A-333333" + ExpectedDeletions = "id=A-111111&id=A-333333&id=NS-111111" ExpectedUpdates = { "records": [{ "name": "unit.tests.", "id": "A-222222", "data": "1.2.3.5", - "ttl": 60 + "ttl": 300 }] } return self._test_apply_with_data(TestData) @@ -451,7 +509,7 @@ class TestRackspaceProvider(TestCase): "subdomain": '', "data": { 'type': 'A', - 'ttl': 60, + 'ttl': 300, 'value': '1.2.3.4' } } @@ -463,19 +521,19 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "foo.unit.tests.", "id": "A-222222", "type": "A", "data": "1.2.3.5", - "ttl": 60 + "ttl": 300 }, { "name": "bar.unit.tests.", "id": "A-333333", "type": "A", "data": "1.2.3.6", - "ttl": 60 + "ttl": 300 }] } ExpectChanges = True @@ -503,7 +561,7 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }] } ExpectChanges = True @@ -538,19 +596,19 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "id": "A-222222", "type": "A", "data": "1.2.3.5", - "ttl": 60 + "ttl": 300 }, { "name": "unit.tests.", "id": "A-333333", "type": "A", "data": "1.2.3.6", - "ttl": 60 + "ttl": 300 }] } ExpectChanges = True @@ -603,13 +661,13 @@ class TestRackspaceProvider(TestCase): "id": "A-111111", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }, { "name": "bar.unit.tests.", "id": "A-222222", "type": "A", "data": "1.2.3.4", - "ttl": 60 + "ttl": 300 }] } ExpectChanges = True @@ -630,6 +688,7 @@ class TestRackspaceProvider(TestCase): } return self._test_apply_with_data(TestData) + """ def test_provider(self): expected = self._load_full_config() @@ -707,17 +766,18 @@ class TestRackspaceProvider(TestCase): with self.assertRaises(HTTPError): plan = self.provider.plan(expected) self.provider.apply(plan) + """ def test_plan_no_changes(self): expected = Zone('unit.tests.', []) expected.add_record(Record.new(expected, '', { 'type': 'NS', 'ttl': 600, - 'values': ['8.8.8.8.', '9.9.9.9.'] + 'values': ['ns1.example.com.', 'ns2.example.com.'] })) expected.add_record(Record.new(expected, '', { 'type': 'A', - 'ttl': 60, + 'ttl': 600, 'value': '1.2.3.4' })) @@ -735,7 +795,7 @@ class TestRackspaceProvider(TestCase): expected.add_record(Record.new(expected, '', { 'type': 'NS', 'ttl': 600, - 'values': ['8.8.8.8.', '9.9.9.9.'] + 'values': ['ns1.example.com.', 'ns2.example.com.'] })) with requests_mock() as mock: @@ -745,7 +805,7 @@ class TestRackspaceProvider(TestCase): plan = self.provider.plan(expected) self.assertTrue(mock.called) self.assertEquals(1, len(plan.changes)) - self.assertEqual(plan.changes[0].existing.ttl, 60) + self.assertEqual(plan.changes[0].existing.ttl, 600) self.assertEqual(plan.changes[0].existing.values[0], '1.2.3.4') def test_plan_create_a_record(self): @@ -753,11 +813,11 @@ class TestRackspaceProvider(TestCase): expected.add_record(Record.new(expected, '', { 'type': 'NS', 'ttl': 600, - 'values': ['8.8.8.8.', '9.9.9.9.'] + 'values': ['ns1.example.com.', 'ns2.example.com.'] })) expected.add_record(Record.new(expected, '', { 'type': 'A', - 'ttl': 60, + 'ttl': 600, 'values': ['1.2.3.4', '1.2.3.5'] })) @@ -768,7 +828,7 @@ class TestRackspaceProvider(TestCase): plan = self.provider.plan(expected) self.assertTrue(mock.called) self.assertEquals(1, len(plan.changes)) - self.assertEqual(plan.changes[0].new.ttl, 60) + self.assertEqual(plan.changes[0].new.ttl, 600) self.assertEqual(plan.changes[0].new.values[0], '1.2.3.4') self.assertEqual(plan.changes[0].new.values[1], '1.2.3.5') @@ -777,7 +837,7 @@ class TestRackspaceProvider(TestCase): expected.add_record(Record.new(expected, '', { 'type': 'NS', 'ttl': 600, - 'values': ['8.8.8.8.', '9.9.9.9.'] + 'values': ['ns1.example.com.', 'ns2.example.com.'] })) expected.add_record(Record.new(expected, '', { 'type': 'A', @@ -793,6 +853,6 @@ class TestRackspaceProvider(TestCase): self.assertTrue(mock.called) self.assertEqual(1, len(plan.changes)) - self.assertEqual(plan.changes[0].existing.ttl, 60) + self.assertEqual(plan.changes[0].existing.ttl, 600) self.assertEqual(plan.changes[0].new.ttl, 86400) self.assertEqual(plan.changes[0].new.values[0], '1.2.3.4')