Return-Path: X-Original-To: apmail-libcloud-commits-archive@www.apache.org Delivered-To: apmail-libcloud-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B1660E766 for ; Sat, 5 Jan 2013 23:36:07 +0000 (UTC) Received: (qmail 4970 invoked by uid 500); 5 Jan 2013 23:36:07 -0000 Delivered-To: apmail-libcloud-commits-archive@libcloud.apache.org Received: (qmail 4940 invoked by uid 500); 5 Jan 2013 23:36:07 -0000 Mailing-List: contact commits-help@libcloud.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@libcloud.apache.org Delivered-To: mailing list commits@libcloud.apache.org Received: (qmail 4932 invoked by uid 99); 5 Jan 2013 23:36:07 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 05 Jan 2013 23:36:07 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED,T_FILL_THIS_FORM_SHORT X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 05 Jan 2013 23:36:05 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id A1BEE23889BF; Sat, 5 Jan 2013 23:35:45 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1429425 - in /libcloud/trunk: ./ libcloud/dns/ libcloud/dns/drivers/ libcloud/test/ libcloud/test/dns/ libcloud/test/dns/fixtures/route53/ Date: Sat, 05 Jan 2013 23:35:45 -0000 To: commits@libcloud.apache.org From: tomaz@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20130105233545.A1BEE23889BF@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: tomaz Date: Sat Jan 5 23:35:44 2013 New Revision: 1429425 URL: http://svn.apache.org/viewvc?rev=1429425&view=rev Log: Add / finish Route53 driver (add all of the non-read functionality and tests). Contributed by John Carr, part of LIBCLOUD-162. Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/ libcloud/trunk/libcloud/test/dns/fixtures/route53/create_zone.xml libcloud/trunk/libcloud/test/dns/fixtures/route53/get_zone.xml libcloud/trunk/libcloud/test/dns/fixtures/route53/invalid_change_batch.xml libcloud/trunk/libcloud/test/dns/fixtures/route53/list_records.xml libcloud/trunk/libcloud/test/dns/fixtures/route53/list_zones.xml libcloud/trunk/libcloud/test/dns/fixtures/route53/record_does_not_exist.xml libcloud/trunk/libcloud/test/dns/fixtures/route53/zone_does_not_exist.xml libcloud/trunk/libcloud/test/dns/test_route53.py Modified: libcloud/trunk/CHANGES libcloud/trunk/libcloud/dns/drivers/route53.py libcloud/trunk/libcloud/dns/providers.py libcloud/trunk/libcloud/test/secrets.py-dist Modified: libcloud/trunk/CHANGES URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1429425&r1=1429424&r2=1429425&view=diff ============================================================================== --- libcloud/trunk/CHANGES (original) +++ libcloud/trunk/CHANGES Sat Jan 5 23:35:44 2013 @@ -133,6 +133,9 @@ Changes with Apache Libcloud in developm - New driver for HostVirtual provider (www.vr.org). (LIBCLOUD-249) [Dinesh Bhoopathy] + - Finish Amazon Route53 driver. (LIBCLOUD-132) + [John Carr] + Changes with Apache Libcloud 0.11.4: *) General Modified: libcloud/trunk/libcloud/dns/drivers/route53.py URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/dns/drivers/route53.py?rev=1429425&r1=1429424&r2=1429425&view=diff ============================================================================== --- libcloud/trunk/libcloud/dns/drivers/route53.py (original) +++ libcloud/trunk/libcloud/dns/drivers/route53.py Sat Jan 5 23:35:44 2013 @@ -20,11 +20,13 @@ __all__ = [ import base64 import hmac import datetime +import uuid +from libcloud.utils.py3 import httplib from hashlib import sha1 from xml.etree import ElementTree as ET -from libcloud.utils.py3 import b +from libcloud.utils.py3 import b, urlencode from libcloud.utils.xml import findtext, findall, fixxpath from libcloud.dns.types import Provider, RecordType @@ -42,17 +44,8 @@ API_ROOT = '/%s/' % (API_VERSION) NAMESPACE = 'https://%s/doc%s' % (API_HOST, API_ROOT) -class Route53Error(LibcloudError): - def __init__(self, code, errors): - self.code = code - self.errors = errors or [] - - def __str__(self): - return 'Errors: %s' % (', '.join(self.errors)) - - def __repr__(self): - return('' % - (self.code, len(self.errors))) +class InvalidChangeBatch(LibcloudError): + pass class Route53DNSResponse(AWSBaseResponse): @@ -62,28 +55,42 @@ class Route53DNSResponse(AWSBaseResponse def success(self): return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED] - def error(self): + def parse_error(self): + context = self.connection.context status = int(self.status) - if status == 403: + if status == httplib.FORBIDDEN: if not self.body: raise InvalidCredsError(str(self.status) + ': ' + self.error) else: raise InvalidCredsError(self.body) - elif status == 400: - context = self.connection.context - messages = [] - if context['InvalidChangeBatch']['Messages']: - for message in context['InvalidChangeBatch']['Messages']: - messages.append(message['Message']) + try: + body = ET.XML(self.body) + except Exception: + raise MalformedResponseError('Failed to parse XML', + body=self.body, driver=self.driver) + + errs = findall(element=body, xpath='Error', namespace=NAMESPACE) + + if errs: + t, code, message = errs[0].getchildren() + + if code.text == 'NoSuchHostedZone': + zone_id = context.get('zone_id', None) + raise ZoneDoesNotExistError(value=message.text, driver=self, + zone_id=zone_id) + elif code.text == 'InvalidChangeBatch': + raise InvalidChangeBatch(value=message.text) + else: + return message.text - raise Route53Error('InvalidChangeBatch message(s): %s ', - messages) + return self.body class Route53Connection(ConnectionUserAndKey): host = API_HOST + responseCls = Route53DNSResponse def pre_connect_hook(self, params, headers): time_string = datetime.datetime.utcnow() \ @@ -123,36 +130,139 @@ class Route53DNSDriver(DNSDriver): RecordType.AAAA: 'AAAA', RecordType.CNAME: 'CNAME', RecordType.TXT: 'TXT', - RecordType.SRV: 'SRV' + RecordType.SRV: 'SRV', + RecordType.PTR: 'PTR', + RecordType.SOA: 'SOA', + RecordType.SPF: 'SPF', + RecordType.TXT: 'TXT' } def list_zones(self): - data = ET.XML(self.connection.request(API_ROOT + 'hostedzone').object) + data = self.connection.request(API_ROOT + 'hostedzone').object zones = self._to_zones(data=data) return zones def list_records(self, zone): - data = ET.XML(self.connection.request(API_ROOT + 'hostedzone/' - + zone.id + '/rrset').object) + self.connection.set_context({'zone_id': zone.id}) + uri = API_ROOT + 'hostedzone/' + zone.id + '/rrset' + data = self.connection.request(uri).object records = self._to_records(data=data, zone=zone) return records def get_zone(self, zone_id): - data = ET.XML(self.connection.request(API_ROOT + 'hostedzone/' - + zone_id).object) - zone = self._to_zone(elem=findall(element=data, xpath='HostedZone', - namespace=NAMESPACE)[0]) - return zone + self.connection.set_context({'zone_id': zone_id}) + uri = API_ROOT + 'hostedzone/' + zone_id + data = self.connection.request(uri).object + elem = findall(element=data, xpath='HostedZone', + namespace=NAMESPACE)[0] + return self._to_zone(elem) def get_record(self, zone_id, record_id): zone = self.get_zone(zone_id=zone_id) - data = ET.XML(self.connection.request(API_ROOT + 'hostedzone/' - + zone_id + '/rrset?maxitems=1&name=' + record_id) - .object) + record_type, name = record_id.split(':', 1) + self.connection.set_context({'zone_id': zone_id}) + params = urlencode({'name': name, 'type': record_type}) + uri = API_ROOT + 'hostedzone/' + zone_id + '/rrset?' + params + data = self.connection.request(uri).object + + record = self._to_records(data=data, zone=zone)[0] + + # A cute aspect of the /rrset filters is that they are more pagination + # hints than filters!! + # So will return a result even if its not what you asked for. + record_type_num = self._string_to_record_type(record_type) + if record.name != name or record.type != record_type_num: + raise RecordDoesNotExistError(value='', driver=self, + record_id=record_id) - record = self._to_records(data=data, zone=zone) return record + def create_zone(self, domain, type='master', ttl=None, extra=None): + zone = ET.Element('CreateHostedZoneRequest', {'xmlns': NAMESPACE}) + ET.SubElement(zone, 'Name').text = domain + ET.SubElement(zone, 'CallerReference').text = str(uuid.uuid4()) + + if extra and 'Comment' in extra: + hzg = ET.SubElement(zone, 'HostedZoneConfig') + ET.SubElement(hzg, 'Comment').text = extra['Comment'] + + uri = API_ROOT + 'hostedzone' + data = ET.tostring(zone) + rsp = self.connection.request(uri, method='POST', data=data).object + + elem = findall(element=rsp, xpath='HostedZone', namespace=NAMESPACE)[0] + return self._to_zone(elem=elem) + + def delete_zone(self, zone, ex_delete_records=False): + self.connection.set_context({'zone_id': zone.id}) + + if ex_delete_records: + self.ex_delete_all_records(zone=zone) + + uri = API_ROOT + 'hostedzone/%s' % (zone.id) + response = self.connection.request(uri, method='DELETE') + return response.status in [httplib.OK] + + def ex_delete_all_records(self, zone): + deletions = [] + for r in zone.list_records(): + if r.type in (RecordType.NS, RecordType.SOA): + continue + deletions.append(('DELETE', r.name, r.type, r.data, r.extra)) + + if deletions: + self._post_changeset(zone, deletions) + + def create_record(self, name, zone, type, data, extra=None): + batch = [('CREATE', name, type, data, extra)] + self._post_changeset(zone, batch) + id = ':'.join((self.RECORD_TYPE_MAP[type], name)) + return Record(id=id, name=name, type=type, data=data, zone=zone, + driver=self, extra=extra) + + def update_record(self, record, name, type, data, extra): + batch = [ + ('DELETE', record.name, record.type, record.data, record.extra), + ('CREATE', name, type, data, extra)] + self._post_changeset(record.zone, batch) + id = ':'.join((self.RECORD_TYPE_MAP[type], name)) + return Record(id=id, name=name, type=type, data=data, zone=record.zone, + driver=self, extra=extra) + + def delete_record(self, record): + try: + r = record + batch = [('DELETE', r.name, r.type, r.data, r.extra)] + self._post_changeset(record.zone, batch) + except InvalidChangeBatch: + raise RecordDoesNotExistError(value='', driver=self, + record_id=r.id) + return True + + def _post_changeset(self, zone, changes_list): + attrs = {'xmlns': NAMESPACE} + changeset = ET.Element('ChangeResourceRecordSetsRequest', attrs) + batch = ET.SubElement(changeset, 'ChangeBatch') + changes = ET.SubElement(batch, 'Changes') + + for action, name, type_, data, extra in changes_list: + change = ET.SubElement(changes, 'Change') + ET.SubElement(change, 'Action').text = action + + rrs = ET.SubElement(change, 'ResourceRecordSet') + ET.SubElement(rrs, 'Name').text = name + "." + zone.domain + ET.SubElement(rrs, 'Type').text = self.RECORD_TYPE_MAP[type_] + ET.SubElement(rrs, 'TTL').text = extra.get('ttl', '0') + + rrecs = ET.SubElement(rrs, 'ResourceRecords') + rrec = ET.SubElement(rrecs, 'ResourceRecord') + ET.SubElement(rrec, 'Value').text = data + + uri = API_ROOT + 'hostedzone/' + zone.id + '/rrset' + data = ET.tostring(changeset) + self.connection.set_context({'zone_id': zone.id}) + rsp = self.connection.request(uri, method='POST', data=data).object + def _to_zones(self, data): zones = [] for element in data.findall(fixxpath(xpath='HostedZones/HostedZone', @@ -180,9 +290,10 @@ class Route53DNSDriver(DNSDriver): def _to_records(self, data, zone): records = [] - for elem in data.findall( + elems = data.findall( fixxpath(xpath='ResourceRecordSets/ResourceRecordSet', - namespace=NAMESPACE)): + namespace=NAMESPACE)) + for elem in elems: records.append(self._to_record(elem, zone)) return records @@ -190,6 +301,8 @@ class Route53DNSDriver(DNSDriver): def _to_record(self, elem, zone): name = findtext(element=elem, xpath='Name', namespace=NAMESPACE) + name = name[:-len(zone.domain) - 1] + type = self._string_to_record_type(findtext(element=elem, xpath='Type', namespace=NAMESPACE)) ttl = findtext(element=elem, xpath='TTL', namespace=NAMESPACE) @@ -202,6 +315,8 @@ class Route53DNSDriver(DNSDriver): namespace=NAMESPACE) extra = {'ttl': ttl} - record = Record(id=name, name=name, type=type, data=data, zone=zone, + + id = ':'.join((self.RECORD_TYPE_MAP[type], name)) + record = Record(id=id, name=name, type=type, data=data, zone=zone, driver=self, extra=extra) return record Modified: libcloud/trunk/libcloud/dns/providers.py URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/dns/providers.py?rev=1429425&r1=1429424&r2=1429425&view=diff ============================================================================== --- libcloud/trunk/libcloud/dns/providers.py (original) +++ libcloud/trunk/libcloud/dns/providers.py Sat Jan 5 23:35:44 2013 @@ -30,6 +30,8 @@ DRIVERS = { ('libcloud.dns.drivers.rackspace', 'RackspaceUKDNSDriver'), Provider.HOSTVIRTUAL: ('libcloud.dns.drivers.hostvirtual', 'HostVirtualDNSDriver'), + Provider.ROUTE53: + ('libcloud.dns.drivers.route53', 'Route53DNSDriver'), } Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/create_zone.xml URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/route53/create_zone.xml?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/fixtures/route53/create_zone.xml (added) +++ libcloud/trunk/libcloud/test/dns/fixtures/route53/create_zone.xml Sat Jan 5 23:35:44 2013 @@ -0,0 +1,20 @@ + + + + /hostedzone/47234 + t.com + some unique reference + + some comment + + 0 + + + + ns1.example.com + ns2.example.com + ns3.example.com + ns4.example.com + + + \ No newline at end of file Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/get_zone.xml URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/route53/get_zone.xml?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/fixtures/route53/get_zone.xml (added) +++ libcloud/trunk/libcloud/test/dns/fixtures/route53/get_zone.xml Sat Jan 5 23:35:44 2013 @@ -0,0 +1,21 @@ + + + + + /hostedzone/47234 + t.com + some unique reference + + some comment + + 0 + + + + ns1.example.com + ns2.example.com + ns3.example.com + ns4.example.com + + + Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/invalid_change_batch.xml URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/route53/invalid_change_batch.xml?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/fixtures/route53/invalid_change_batch.xml (added) +++ libcloud/trunk/libcloud/test/dns/fixtures/route53/invalid_change_batch.xml Sat Jan 5 23:35:44 2013 @@ -0,0 +1,9 @@ + + + + Sender + InvalidChangeBatch + Invalid change + + 376c64a6-6194-11e1-847f-ddaa49e4c811 + \ No newline at end of file Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/list_records.xml URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/route53/list_records.xml?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/fixtures/route53/list_records.xml (added) +++ libcloud/trunk/libcloud/test/dns/fixtures/route53/list_records.xml Sat Jan 5 23:35:44 2013 @@ -0,0 +1,40 @@ + + + + + + wibble.t.com + CNAME + 86400 + + + t.com + + + + + + www.t.com + A + 86400 + + + 208.111.35.173 + + + + + + blahblah.t.com + A + 86400 + + + 208.111.35.173 + + + + + + + Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/list_zones.xml URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/route53/list_zones.xml?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/fixtures/route53/list_zones.xml (added) +++ libcloud/trunk/libcloud/test/dns/fixtures/route53/list_zones.xml Sat Jan 5 23:35:44 2013 @@ -0,0 +1,54 @@ + + + + + /hostedzone/47234 + t.com + unique description + + some comment + + 0 + + + + /hostedzone/48170 + newbug.net + unique description + + some comment + + 0 + + + + /hostedzone/48017 + newblah.com + unique description + + some comment + + 0 + + + + /hostedzone/47288 + fromapi.com + unique description + + some comment + + 0 + + + + /hostedzone/48008 + blahnew.com + unique description + + some comment + + 0 + + + Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/record_does_not_exist.xml URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/route53/record_does_not_exist.xml?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/fixtures/route53/record_does_not_exist.xml (added) +++ libcloud/trunk/libcloud/test/dns/fixtures/route53/record_does_not_exist.xml Sat Jan 5 23:35:44 2013 @@ -0,0 +1,17 @@ + + + + + + definitely.not.what.you.askedfor.t.com + CNAME + 86400 + + + t.com + + + + + + \ No newline at end of file Added: libcloud/trunk/libcloud/test/dns/fixtures/route53/zone_does_not_exist.xml URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/fixtures/route53/zone_does_not_exist.xml?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/fixtures/route53/zone_does_not_exist.xml (added) +++ libcloud/trunk/libcloud/test/dns/fixtures/route53/zone_does_not_exist.xml Sat Jan 5 23:35:44 2013 @@ -0,0 +1,9 @@ + + + + Sender + NoSuchHostedZone + No hosted zone found with ID: 47234 + + 376c64a6-6194-11e1-847f-ddaa49e4c811 + \ No newline at end of file Added: libcloud/trunk/libcloud/test/dns/test_route53.py URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/dns/test_route53.py?rev=1429425&view=auto ============================================================================== --- libcloud/trunk/libcloud/test/dns/test_route53.py (added) +++ libcloud/trunk/libcloud/test/dns/test_route53.py Sat Jan 5 23:35:44 2013 @@ -0,0 +1,255 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import unittest + +from libcloud.utils.py3 import httplib + +from libcloud.dns.types import RecordType, ZoneDoesNotExistError +from libcloud.dns.types import RecordDoesNotExistError +from libcloud.dns.drivers.route53 import Route53DNSDriver +from libcloud.test import MockHttp +from libcloud.test.file_fixtures import DNSFileFixtures +from libcloud.test.secrets import DNS_PARAMS_ROUTE53 + + +class Route53Tests(unittest.TestCase): + def setUp(self): + Route53DNSDriver.connectionCls.conn_classes = ( + Route53MockHttp, Route53MockHttp) + Route53MockHttp.type = None + self.driver = Route53DNSDriver(*DNS_PARAMS_ROUTE53) + + def test_list_record_types(self): + record_types = self.driver.list_record_types() + self.assertEqual(len(record_types), 10) + self.assertTrue(RecordType.A in record_types) + + def test_list_zones(self): + zones = self.driver.list_zones() + self.assertEqual(len(zones), 5) + + zone = zones[0] + self.assertEqual(zone.id, '47234') + self.assertEqual(zone.type, 'master') + self.assertEqual(zone.domain, 't.com') + + def test_list_records(self): + zone = self.driver.list_zones()[0] + records = self.driver.list_records(zone=zone) + self.assertEqual(len(records), 3) + + record = records[1] + self.assertEqual(record.name, 'www') + self.assertEqual(record.id, 'A:www') + self.assertEqual(record.type, RecordType.A) + self.assertEqual(record.data, '208.111.35.173') + + def test_get_zone(self): + zone = self.driver.get_zone(zone_id='47234') + self.assertEqual(zone.id, '47234') + self.assertEqual(zone.type, 'master') + self.assertEqual(zone.domain, 't.com') + + def test_get_record(self): + record = self.driver.get_record(zone_id='47234', + record_id='CNAME:wibble') + self.assertEqual(record.name, 'wibble') + self.assertEqual(record.type, RecordType.CNAME) + self.assertEqual(record.data, 't.com') + + def test_list_records_zone_does_not_exist(self): + zone = self.driver.list_zones()[0] + + Route53MockHttp.type = 'ZONE_DOES_NOT_EXIST' + + try: + self.driver.list_records(zone=zone) + except ZoneDoesNotExistError: + e = sys.exc_info()[1] + self.assertEqual(e.zone_id, zone.id) + else: + self.fail('Exception was not thrown') + + def test_get_zone_does_not_exist(self): + Route53MockHttp.type = 'ZONE_DOES_NOT_EXIST' + + try: + self.driver.get_zone(zone_id='47234') + except ZoneDoesNotExistError: + e = sys.exc_info()[1] + self.assertEqual(e.zone_id, '47234') + else: + self.fail('Exception was not thrown') + + def test_get_record_zone_does_not_exist(self): + Route53MockHttp.type = 'ZONE_DOES_NOT_EXIST' + + try: + self.driver.get_record(zone_id='4444', record_id='28536') + except ZoneDoesNotExistError: + pass + else: + self.fail('Exception was not thrown') + + def test_get_record_record_does_not_exist(self): + Route53MockHttp.type = 'RECORD_DOES_NOT_EXIST' + + rid = 'CNAME:doesnotexist.t.com' + try: + self.driver.get_record(zone_id='47234', + record_id=rid) + except RecordDoesNotExistError: + pass + else: + self.fail('Exception was not thrown') + + def test_create_zone(self): + zone = self.driver.create_zone(domain='t.com', type='master', + ttl=None, extra=None) + self.assertEqual(zone.id, '47234') + self.assertEqual(zone.domain, 't.com') + + def test_create_record(self): + zone = self.driver.list_zones()[0] + record = self.driver.create_record( + name='www', zone=zone, + type=RecordType.A, data='127.0.0.1', + extra={'ttl': 0} + ) + + self.assertEqual(record.id, 'A:www') + self.assertEqual(record.name, 'www') + self.assertEqual(record.zone, zone) + self.assertEqual(record.type, RecordType.A) + self.assertEqual(record.data, '127.0.0.1') + + def test_update_record(self): + zone = self.driver.list_zones()[0] + record = self.driver.list_records(zone=zone)[1] + + params = { + 'record': record, + 'name': 'www', + 'type': RecordType.A, + 'data': '::1', + 'extra': {'ttle': 0}} + updated_record = self.driver.update_record(**params) + + self.assertEqual(record.data, '208.111.35.173') + + self.assertEqual(updated_record.id, 'A:www') + self.assertEqual(updated_record.name, 'www') + self.assertEqual(updated_record.zone, record.zone) + self.assertEqual(updated_record.type, RecordType.A) + self.assertEqual(updated_record.data, '::1') + + def test_delete_zone(self): + zone = self.driver.list_zones()[0] + status = self.driver.delete_zone(zone=zone) + self.assertTrue(status) + + def test_delete_zone_does_not_exist(self): + zone = self.driver.list_zones()[0] + + Route53MockHttp.type = 'ZONE_DOES_NOT_EXIST' + + try: + self.driver.delete_zone(zone=zone) + except ZoneDoesNotExistError: + e = sys.exc_info()[1] + self.assertEqual(e.zone_id, zone.id) + else: + self.fail('Exception was not thrown') + + def test_delete_record(self): + zone = self.driver.list_zones()[0] + record = self.driver.list_records(zone=zone)[0] + status = self.driver.delete_record(record=record) + self.assertTrue(status) + + def test_delete_record_does_not_exist(self): + zone = self.driver.list_zones()[0] + record = self.driver.list_records(zone=zone)[0] + Route53MockHttp.type = 'RECORD_DOES_NOT_EXIST' + try: + self.driver.delete_record(record=record) + except RecordDoesNotExistError: + e = sys.exc_info()[1] + self.assertEqual(e.record_id, record.id) + else: + self.fail('Exception was not thrown') + + +class Route53MockHttp(MockHttp): + fixtures = DNSFileFixtures('route53') + + def _2012_02_29_hostedzone_47234(self, method, url, body, headers): + body = self.fixtures.load('get_zone.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _2012_02_29_hostedzone(self, method, url, body, headers): + #print method, url, body, headers + if method == "POST": + body = self.fixtures.load("create_zone.xml") + return (httplib.CREATED, body, {}, httplib.responses[httplib.OK]) + body = self.fixtures.load('list_zones.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _2012_02_29_hostedzone_47234_rrset(self, method, url, body, headers): + body = self.fixtures.load('list_records.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _2012_02_29_hostedzone_47234_rrset_ZONE_DOES_NOT_EXIST(self, method, + url, body, headers): + body = self.fixtures.load('zone_does_not_exist.xml') + return (httplib.NOT_FOUND, body, + {}, httplib.responses[httplib.NOT_FOUND]) + + def _2012_02_29_hostedzone_4444_ZONE_DOES_NOT_EXIST(self, method, + url, body, headers): + body = self.fixtures.load('zone_does_not_exist.xml') + return (httplib.NOT_FOUND, body, + {}, httplib.responses[httplib.NOT_FOUND]) + + def _2012_02_29_hostedzone_47234_ZONE_DOES_NOT_EXIST(self, method, + url, body, headers): + body = self.fixtures.load('zone_does_not_exist.xml') + return (httplib.NOT_FOUND, body, + {}, httplib.responses[httplib.NOT_FOUND]) + + def _2012_02_29_hostedzone_47234_rrset_ZONE_DOES_NOT_EXIST(self, method, + url, body, headers): + body = self.fixtures.load('zone_does_not_exist.xml') + return (httplib.NOT_FOUND, body, + {}, httplib.responses[httplib.NOT_FOUND]) + + def _2012_02_29_hostedzone_47234_rrset_RECORD_DOES_NOT_EXIST(self, method, + url, body, headers): + if method == "POST": + body = self.fixtures.load('invalid_change_batch.xml') + return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.BAD_REQUEST]) + body = self.fixtures.load('record_does_not_exist.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _2012_02_29_hostedzone_47234_RECORD_DOES_NOT_EXIST(self, method, + url, body, headers): + body = self.fixtures.load('get_zone.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + +if __name__ == '__main__': + sys.exit(unittest.main()) Modified: libcloud/trunk/libcloud/test/secrets.py-dist URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/secrets.py-dist?rev=1429425&r1=1429424&r2=1429425&view=diff ============================================================================== --- libcloud/trunk/libcloud/test/secrets.py-dist (original) +++ libcloud/trunk/libcloud/test/secrets.py-dist Sat Jan 5 23:35:44 2013 @@ -52,3 +52,4 @@ DNS_PARAMS_LINODE = ('user', 'key') DNS_PARAMS_ZERIGO = ('email', 'api token') DNS_PARAMS_RACKSPACE = ('user', 'key') DNS_PARAMS_HOSTVIRTUAL = ('key',) +DNS_PARAMS_ROUTE53 = ('access_id', 'secret')