You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2013/01/06 00:35:45 UTC
svn commit: r1429425 - in /libcloud/trunk: ./ libcloud/dns/
libcloud/dns/drivers/ libcloud/test/ libcloud/test/dns/
libcloud/test/dns/fixtures/route53/
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('<Route53 response code=%s>' %
- (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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CreateHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
+ <HostedZone>
+ <Id>/hostedzone/47234</Id>
+ <Name>t.com</Name>
+ <CallerReference>some unique reference</CallerReference>
+ <Config>
+ <Comment>some comment</Comment>
+ </Config>
+ <ResourceRecordSetCount>0</ResourceRecordSetCount>
+ </HostedZone>
+ <DelegationSet>
+ <NameServers>
+ <NameServer>ns1.example.com</NameServer>
+ <NameServer>ns2.example.com</NameServer>
+ <NameServer>ns3.example.com</NameServer>
+ <NameServer>ns4.example.com</NameServer>
+ </NameServers>
+ </DelegationSet>
+</CreateHostedZoneResponse>
\ 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 @@
+
+<?xml version="1.0" encoding="UTF-8"?>
+<GetHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
+ <HostedZone>
+ <Id>/hostedzone/47234</Id>
+ <Name>t.com</Name>
+ <CallerReference>some unique reference</CallerReference>
+ <Config>
+ <Comment>some comment</Comment>
+ </Config>
+ <ResourceRecordSetCount>0</ResourceRecordSetCount>
+ </HostedZone>
+ <DelegationSet>
+ <NameServers>
+ <NameServer>ns1.example.com</NameServer>
+ <NameServer>ns2.example.com</NameServer>
+ <NameServer>ns3.example.com</NameServer>
+ <NameServer>ns4.example.com</NameServer>
+ </NameServers>
+ </DelegationSet>
+</GetHostedZoneResponse>
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 @@
+<?xml version="1.0"?>
+<ErrorResponse xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
+ <Error>
+ <Type>Sender</Type>
+ <Code>InvalidChangeBatch</Code>
+ <Message>Invalid change</Message>
+ </Error>
+ <RequestId>376c64a6-6194-11e1-847f-ddaa49e4c811</RequestId>
+</ErrorResponse>
\ 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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
+ <ResourceRecordSets>
+
+ <ResourceRecordSet>
+ <Name>wibble.t.com</Name>
+ <Type>CNAME</Type>
+ <TTL>86400</TTL>
+ <ResourceRecords>
+ <ResourceRecord>
+ <Value>t.com</Value>
+ </ResourceRecord>
+ </ResourceRecords>
+ </ResourceRecordSet>
+
+ <ResourceRecordSet>
+ <Name>www.t.com</Name>
+ <Type>A</Type>
+ <TTL>86400</TTL>
+ <ResourceRecords>
+ <ResourceRecord>
+ <Value>208.111.35.173</Value>
+ </ResourceRecord>
+ </ResourceRecords>
+ </ResourceRecordSet>
+
+ <ResourceRecordSet>
+ <Name>blahblah.t.com</Name>
+ <Type>A</Type>
+ <TTL>86400</TTL>
+ <ResourceRecords>
+ <ResourceRecord>
+ <Value>208.111.35.173</Value>
+ </ResourceRecord>
+ </ResourceRecords>
+ </ResourceRecordSet>
+
+ </ResourceRecordSets>
+</ListResourceRecordSetsResponse>
+
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ListHostedZonesResponse xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
+ <HostedZones>
+ <HostedZone>
+ <Id>/hostedzone/47234</Id>
+ <Name>t.com</Name>
+ <CallerReference>unique description</CallerReference>
+ <Config>
+ <Comment>some comment</Comment>
+ </Config>
+ <ResourceRecordSetCount>0</ResourceRecordSetCount>
+ </HostedZone>
+
+ <HostedZone>
+ <Id>/hostedzone/48170</Id>
+ <Name>newbug.net</Name>
+ <CallerReference>unique description</CallerReference>
+ <Config>
+ <Comment>some comment</Comment>
+ </Config>
+ <ResourceRecordSetCount>0</ResourceRecordSetCount>
+ </HostedZone>
+
+ <HostedZone>
+ <Id>/hostedzone/48017</Id>
+ <Name>newblah.com</Name>
+ <CallerReference>unique description</CallerReference>
+ <Config>
+ <Comment>some comment</Comment>
+ </Config>
+ <ResourceRecordSetCount>0</ResourceRecordSetCount>
+ </HostedZone>
+
+ <HostedZone>
+ <Id>/hostedzone/47288</Id>
+ <Name>fromapi.com</Name>
+ <CallerReference>unique description</CallerReference>
+ <Config>
+ <Comment>some comment</Comment>
+ </Config>
+ <ResourceRecordSetCount>0</ResourceRecordSetCount>
+ </HostedZone>
+
+ <HostedZone>
+ <Id>/hostedzone/48008</Id>
+ <Name>blahnew.com</Name>
+ <CallerReference>unique description</CallerReference>
+ <Config>
+ <Comment>some comment</Comment>
+ </Config>
+ <ResourceRecordSetCount>0</ResourceRecordSetCount>
+ </HostedZone>
+ </HostedZones>
+</ListHostedZonesResponse>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
+ <ResourceRecordSets>
+
+ <ResourceRecordSet>
+ <Name>definitely.not.what.you.askedfor.t.com</Name>
+ <Type>CNAME</Type>
+ <TTL>86400</TTL>
+ <ResourceRecords>
+ <ResourceRecord>
+ <Value>t.com</Value>
+ </ResourceRecord>
+ </ResourceRecords>
+ </ResourceRecordSet>
+
+ </ResourceRecordSets>
+</ListResourceRecordSetsResponse>
\ 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 @@
+<?xml version="1.0"?>
+<ErrorResponse xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
+ <Error>
+ <Type>Sender</Type>
+ <Code>NoSuchHostedZone</Code>
+ <Message>No hosted zone found with ID: 47234</Message>
+ </Error>
+ <RequestId>376c64a6-6194-11e1-847f-ddaa49e4c811</RequestId>
+</ErrorResponse>
\ 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')