Repository: libcloud Updated Branches: refs/heads/trunk 1ae9f1c6a -> defff8acd
adding dns support for dnspod provider Closes #787 Signed-off-by: Tomaz Muraus <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/707a7255 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/707a7255 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/707a7255 Branch: refs/heads/trunk Commit: 707a725542445b93919eb567a2a3b64bd6222b28 Parents: 1ae9f1c Author: oltjano <[email protected]> Authored: Sun May 15 16:57:47 2016 -0700 Committer: Tomaz Muraus <[email protected]> Committed: Wed May 25 14:37:30 2016 +0200 ---------------------------------------------------------------------- libcloud/common/dnspod.py | 65 ++++ libcloud/dns/drivers/dnspod.py | 345 +++++++++++++++++++ libcloud/dns/types.py | 1 + .../fixtures/dnspod/create_zone_success.json | 12 + .../delete_record_record_does_not_exist.json | 1 + .../fixtures/dnspod/delete_record_success.json | 0 .../fixtures/dnspod/delete_zone_success.json | 0 .../dns/fixtures/dnspod/empty_zones_list.json | 1 + .../test/dns/fixtures/dnspod/get_record.json | 26 ++ .../dns/fixtures/dnspod/get_zone_success.json | 28 ++ .../test/dns/fixtures/dnspod/list_records.json | 93 +++++ .../test/dns/fixtures/dnspod/list_zones.json | 42 +++ .../fixtures/dnspod/record_already_exists.json | 8 + .../fixtures/dnspod/zone_already_exists.json | 1 + .../fixtures/dnspod/zone_does_not_exist.json | 1 + libcloud/test/dns/test_dnspod.py | 266 ++++++++++++++ 16 files changed, 890 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/common/dnspod.py ---------------------------------------------------------------------- diff --git a/libcloud/common/dnspod.py b/libcloud/common/dnspod.py new file mode 100644 index 0000000..792ab57 --- /dev/null +++ b/libcloud/common/dnspod.py @@ -0,0 +1,65 @@ +from libcloud.common.base import ConnectionKey, JsonResponse + + +__all__ = [ + 'API_HOST', + 'DNSPodException', + 'DNSPodResponse', + 'DNSPodConnection' +] + +# Endpoint for dnspod api +API_HOST = 'api.dnspod.com' + + +class DNSPodResponse(JsonResponse): + errors = [] + objects = [] + + def __init__(self, response, connection): + super(DNSPodResponse, self).__init__(response=response, + connection=connection) + self.errors, self.objects = self.parse_body_and_errors() + if not self.success(): + raise DNSPodException(code=self.status, + message=self.errors.pop() + ['status']['message']) + + def parse_body_and_errors(self): + js = super(DNSPodResponse, self).parse_body() + if 'status' in js and js['status']['code'] != '1': + self.errors.append(js) + else: + self.objects.append(js) + + return self.errors, self.objects + + def success(self): + return len(self.errors) == 0 + + +class DNSPodConnection(ConnectionKey): + host = API_HOST + responseCls = DNSPodResponse + + def add_default_headers(self, headers): + headers['Content-Type'] = 'application/x-www-form-urlencoded' + headers['Accept'] = 'text/json' + headers['User-Agent'] = \ + 'dnspod-python/0.01 ([email protected]; DNSPod.CN API v2.8)' + + return headers + + +class DNSPodException(Exception): + + def __init__(self, code, message): + self.code = code + self.message = message + self.args = (code, message) + + def __str__(self): + return "%s %s" % (self.code, self.message) + + def __repr__(self): + return "DNSPodException %s %s" % (self.code, self.message) http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/dns/drivers/dnspod.py ---------------------------------------------------------------------- diff --git a/libcloud/dns/drivers/dnspod.py b/libcloud/dns/drivers/dnspod.py new file mode 100644 index 0000000..0c0515b --- /dev/null +++ b/libcloud/dns/drivers/dnspod.py @@ -0,0 +1,345 @@ +# 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 urllib + + +from libcloud.dns.types import Provider, ZoneDoesNotExistError, \ + ZoneAlreadyExistsError, RecordDoesNotExistError, RecordAlreadyExistsError +from libcloud.dns.base import DNSDriver, Zone, Record, RecordType +from libcloud.common.dnspod import DNSPodConnection, DNSPodResponse, \ + DNSPodException + +__all__ = [ + 'DNSPodDNSDriver' +] + + +class DNSPodDNSResponse(DNSPodResponse): + pass + + +class DNSPodDNSConnection(DNSPodConnection): + responseCls = DNSPodDNSResponse + + +class DNSPodDNSDriver(DNSDriver): + name = 'DNSPod' + website = 'https://dnspod.com' + type = Provider.DNSPOD + connectionCls = DNSPodDNSConnection + + RECORD_TYPE_MAP = { + RecordType.A: 'A', + RecordType.AAAA: 'AAAA', + RecordType.CNAME: 'CNAME', + RecordType.MX: 'MX', + RecordType.NS: 'NS', + RecordType.PTR: 'PTR', + RecordType.SOA: 'SOA', + RecordType.SRV: 'SRV', + RecordType.TXT: 'TXT' + } + + def list_zones(self): + action = '/Domain.List' + data = {'user_token': self.key, 'format': 'json'} + data = urllib.urlencode(data) + try: + response = self.connection.request(action=action, method='POST', + data=data) + except DNSPodException: + e = sys.exc_info()[1] + if e.message == 'No domains': + return [] + zones = self._to_zones(items=response.parse_body()['domains']) + + return zones + + def delete_zone(self, zone): + """ + :param zone: Zone to be deleted. + :type zone: :class:`Zone` + + :return: Boolean + """ + action = '/Domain.Remove' + data = {'user_token': self.key, 'format': 'json', 'domain_id': zone.id} + data = urllib.urlencode(data) + + try: + self.connection.request(action=action, method='POST', + data=data) + except DNSPodException: + e = sys.exc_info()[1] + if e.message == 'Domain id invalid': + raise ZoneDoesNotExistError(value=e.message, driver=self, + zone_id=zone.id) + else: + raise e + + return True + + def get_zone(self, zone_id): + """ + :param zone_id: Zone domain name (e.g. example.com) + :return: :class:`Zone` + """ + action = '/Domain.Info' + data = {'user_token': self.key, 'format': 'json', 'domain_id': zone_id} + data = urllib.urlencode(data) + + try: + response = self.connection.request(action=action, method='POST', + data=data) + except DNSPodException: + e = sys.exc_info()[1] + if e.message in ['Domain not under you or your user', + 'Domain id invalid']: + raise ZoneDoesNotExistError(value=e.message, driver=self, + zone_id=zone_id) + else: + raise e + zone = self._to_zone(response.parse_body()['domain']) + + return zone + + def create_zone(self, domain, type='master', ttl=None, extra=None): + """ + :param domain: Zone domain name (e.g. example.com) + :type domain: ``str`` + + :param type: Zone type (This is not really used. See API docs for extra + parameters) + :type type: ``str`` + + :param ttl: TTL for new records (This is used through the extra param) + :type ttl: ``int`` + + :param extra: Extra attributes that are specific to the driver + such as ttl. + :type extra: ``dict`` + + :rtype: :class:`Zone` + """ + action = '/Domain.Create' + data = {'user_token': self.key, 'format': 'json', 'domain': domain} + if extra is not None: + data.update(extra) + data = urllib.urlencode(data) + try: + response = self.connection.request(action=action, method='POST', + data=data) + except DNSPodException: + e = sys.exc_info()[1] + if e.message in ['Domain is exists', + 'Domain already exists as ' + 'an alias of another domain']: + raise ZoneAlreadyExistsError(value=e.message, driver=self, + zone_id=domain) + else: + raise e + + zone = self._to_zone(response.parse_body()['domain']) + + return zone + + def list_records(self, zone): + """ + Return a list of records for the provided zone. + + :param zone: Zone to list records for. + :type zone: :class:`Zone` + + :return: ``list`` of :class:`Record` + """ + action = '/Record.List' + data = {'user_token': self.key, 'format': 'json', + 'domain_id': zone.id} + data = urllib.urlencode(data) + try: + response = self.connection.request(action=action, data=data, + method='POST') + except DNSPodException: + e = sys.exc_info()[1] + if e.message == 'Domain id invalid': + raise ZoneDoesNotExistError(value='', driver=self, + zone_id=zone.id) + else: + raise e + records = self._to_records(response.parse_body()['records'], zone=zone) + + return records + + def delete_record(self, record): + """ + Delete a record. + + :param record: Record to delete. + :type record: :class:`Record` + + :rtype: ``bool`` + """ + action = '/Record.Remove' + data = {'user_token': self.key, 'format': 'json', + 'domain_id': record.zone.id, 'record_id': record.id} + data = urllib.urlencode(data) + try: + self.connection.request(action=action, method='POST', + data=data) + except DNSPodException: + e = sys.exc_info()[1] + if e.message == 'Record id invalid': + raise RecordDoesNotExistError(record_id=record.id, driver=self, + value='') + elif e.message == 'Domain id invalid': + raise ZoneDoesNotExistError(zone_id=record.zone.id, + driver=self, value='') + else: + raise e + + return True + + def get_record(self, zone_id, record_id): + """ + Return a Record instance. + + :param zone_id: ID of the required zone + :type zone_id: ``str`` + + :param record_id: ID of the required record + :type record_id: ``str`` + + :rtype: :class:`Record` + """ + zone = self.get_zone(zone_id=zone_id) + action = '/Record.Info' + data = {'user_token': self.key, 'format': 'json', + 'domain_id': zone_id, 'record_id': record_id} + data = urllib.urlencode(data) + try: + response = self.connection.request(action=action, method='POST', + data=data) + except DNSPodException: + e = sys.exc_info()[1] + if e.message == 'Record id invalid': + raise RecordDoesNotExistError(record_id=record_id, driver=self, + value='') + elif e.message == 'Domain id invalid': + raise ZoneDoesNotExistError(zone_id=zone_id, driver=self, + value='') + else: + raise e + + record = self._to_record(response.parse_body()['record'], zone=zone) + + return record + + def create_record(self, name, zone, type, data, extra=None): + """ + Create a record. + + :param name: Record name without the domain name (e.g. www). + Note: If you want to create a record for a base domain + name, you should specify empty string ('') for this + argument. + :type name: ``str`` + + :param zone: Zone which the records will be created for. + :type zone: :class:`Zone` + + :param type: DNS record type ( 'A', 'AAAA', 'CNAME', 'MX', 'NS', + 'PTR', 'SOA', 'SRV', 'TXT'). + :type type: :class:`RecordType` + + :param data: Data for the record (depends on the record type). + :type data: ``str`` + + :param extra: (optional) Extra attributes ('prio', 'ttl'). + :type extra: ``dict`` + + :rtype: :class:`Record` + """ + action = '/Record.Create' + to_post = {'user_token': self.key, 'format': 'json', + 'sub_domain': name, 'value': data, + 'record_type': type, 'domain_id': zone.id} + # ttl is optional + # pass it through extra like this: extra={'ttl':ttl} + # record_line is a required parameter + # pass it through extra like this: extra={'record_line':'default'} + # when creating MX records you need to pass mx through extra + # mx ranges from 1 to 20 + # extra = {'ttl': '13', 'record_line': default, 'mx': 1} + if extra is not None: + to_post.update(extra) + data = urllib.urlencode(to_post) + try: + response = self.connection.request(action=action, + method='POST', + data=data) + except DNSPodException: + e = sys.exc_info()[1] + if e.message == 'Record impacted, same record' \ + 'exists or CNAME/URL impacted': + raise RecordAlreadyExistsError(record_id='', driver=self, + value=name) + raise e + + record_id = response.parse_body()['record'].get('id') + record = self.get_record(zone_id=zone.id, record_id=record_id) + + return record + + def _to_zone(self, item): + common_attr = ['name', 'id', 'ttl'] + extra = {} + for key in item.keys(): + if key not in common_attr: + extra[key] = item.get(key) + + zone = Zone(domain=item.get('name') or item.get('domain'), + id=item.get('id'), type=None, extra=extra, + ttl=item.get('ttl'), driver=self) + + return zone + + def _to_zones(self, items): + zones = [] + for item in items: + zones.append(self._to_zone(item)) + + return zones + + def _to_record(self, item, zone): + common_attr = ['id', 'value', 'name', 'type'] + extra = {} + for key in item: + if key not in common_attr: + extra[key] = item.get(key) + record = Record(id=item.get('id'), + name=item.get('name') or item.get('sub_domain'), + type=item.get('type') or item.get('record_type'), + data=item.get('value'), zone=zone, driver=self, + extra=extra) + + return record + + def _to_records(self, items, zone): + records = [] + for item in items: + records.append(self._to_record(item, zone)) + + return records http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/dns/types.py ---------------------------------------------------------------------- diff --git a/libcloud/dns/types.py b/libcloud/dns/types.py index e6fc28d..f05deb3 100644 --- a/libcloud/dns/types.py +++ b/libcloud/dns/types.py @@ -61,6 +61,7 @@ class Provider(object): WORLDWIDEDNS = 'worldwidedns' ZERIGO = 'zerigo' ZONOMI = 'zonomi' + DNSPOD = 'dnspod' # Deprecated RACKSPACE_US = 'rackspace_us' RACKSPACE_UK = 'rackspace_uk' http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/create_zone_success.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/create_zone_success.json b/libcloud/test/dns/fixtures/dnspod/create_zone_success.json new file mode 100644 index 0000000..663db1c --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/create_zone_success.json @@ -0,0 +1,12 @@ +{ + "status": { + "code":"1", + "message":"Action completed successful", + "created_at":"2012-08-29 22:12:35" + }, + "domain": { + "id":"3", + "punycode":"api2.com", + "domain":"api2.com" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/delete_record_record_does_not_exist.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/delete_record_record_does_not_exist.json b/libcloud/test/dns/fixtures/dnspod/delete_record_record_does_not_exist.json new file mode 100644 index 0000000..937cc46 --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/delete_record_record_does_not_exist.json @@ -0,0 +1 @@ +{"status": {"message": "Record id invalid", "code": "8", "created_at": "2016-05-13 15:05:46"}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/delete_record_success.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/delete_record_success.json b/libcloud/test/dns/fixtures/dnspod/delete_record_success.json new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/delete_zone_success.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/delete_zone_success.json b/libcloud/test/dns/fixtures/dnspod/delete_zone_success.json new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/empty_zones_list.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/empty_zones_list.json b/libcloud/test/dns/fixtures/dnspod/empty_zones_list.json new file mode 100644 index 0000000..4b97ed2 --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/empty_zones_list.json @@ -0,0 +1 @@ +{"status":{"code":"9","message":"No domains","created_at":"2016-04-24 19:02:23"}} http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/get_record.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/get_record.json b/libcloud/test/dns/fixtures/dnspod/get_record.json new file mode 100644 index 0000000..8462239 --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/get_record.json @@ -0,0 +1,26 @@ +{ + "status": { + "code": "1", + "message": "Action completed successful", + "created_at": "2014-06-05 10:07:05" + }, + "domain": { + "id": "9", + "domain": "dnspod.com", + "domain_grade": "DP_Free" + }, + "record": { + "id": "50", + "sub_domain": "@", + "record_type": "A", + "record_line": "Default", + "value": "96.126.115.73", + "mx": "0", + "ttl": "600", + "enabled": "1", + "monitor_status": "", + "remark": "", + "updated_on": "2014-06-05 09:47:59", + "domain_id": "9" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/get_zone_success.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/get_zone_success.json b/libcloud/test/dns/fixtures/dnspod/get_zone_success.json new file mode 100644 index 0000000..725eef7 --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/get_zone_success.json @@ -0,0 +1,28 @@ +{ + "status": { + "code": "1", + "message": "Action completed successful", + "created_at": "2014-06-04 21:25:21" + }, + "domain": { + "id": "6", + "name": "dnspod.com", + "punycode": "dnspod.com", + "grade": "DP_Free", + "grade_title": "Free", + "status": "enable", + "ext_status": "notexist", + "records": "3", + "group_id": "1", + "is_mark": "no", + "remark": false, + "is_vip": "no", + "searchengine_push": "yes", + "beian": "no", + "user_id": "730060", + "created_on": "2014-06-04 16:19:31", + "updated_on": "2014-06-04 16:20:05", + "ttl": "600", + "owner": "[email protected]" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/list_records.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/list_records.json b/libcloud/test/dns/fixtures/dnspod/list_records.json new file mode 100644 index 0000000..c44c12b --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/list_records.json @@ -0,0 +1,93 @@ +{ + "status": { + "code": "1", + "message": "Action completed successful", + "created_at": "2014-06-05 09:58:40" + }, + "domain": { + "id": "9", + "name": "dnspod.com", + "punycode": "dnspod.com", + "grade": "DP_Free", + "owner": "[email protected]" + }, + "info": { + "sub_domains": "5", + "record_total": "5" + }, + "records": [ + { + "id": "50", + "name": "@", + "line": "Default", + "type": "A", + "ttl": "600", + "value": "96.126.115.73", + "mx": "0", + "enabled": "1", + "status": "enabled", + "monitor_status": "", + "remark": "", + "updated_on": "2014-06-05 09:47:59" + }, + { + "id": "49", + "name": "@", + "line": "Default", + "type": "MX", + "ttl": "600", + "value": "cloudmx.qq.com.", + "mx": "5", + "enabled": "1", + "status": "enabled", + "monitor_status": "", + "remark": "", + "updated_on": "2014-06-05 09:47:59" + }, + { + "id": "46", + "name": "@", + "line": "Default", + "type": "NS", + "ttl": "600", + "value": "a.dnspod.com.", + "mx": "0", + "enabled": "1", + "status": "enabled", + "monitor_status": "", + "remark": "", + "updated_on": "2014-06-05 09:47:40", + "hold": "hold" + }, + { + "id": "47", + "name": "@", + "line": "Default", + "type": "NS", + "ttl": "600", + "value": "b.dnspod.com.", + "mx": "0", + "enabled": "1", + "status": "enabled", + "monitor_status": "", + "remark": "", + "updated_on": "2014-06-05 09:47:40", + "hold": "hold" + }, + { + "id": "48", + "name": "@", + "line": "Default", + "type": "NS", + "ttl": "600", + "value": "c.dnspod.com.", + "mx": "0", + "enabled": "1", + "status": "enabled", + "monitor_status": "", + "remark": "", + "updated_on": "2014-06-05 09:47:40", + "hold": "hold" + } + ] + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/list_zones.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/list_zones.json b/libcloud/test/dns/fixtures/dnspod/list_zones.json new file mode 100644 index 0000000..c8c367c --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/list_zones.json @@ -0,0 +1,42 @@ +{ + "status": { + "code": "1", + "message": "Action completed successful", + "created_at": "2014-06-04 21:22:08" + }, + "info": { + "domain_total": 1, + "all_total": 1, + "mine_total": 1, + "share_total": 0, + "vip_total": 0, + "ismark_total": 0, + "pause_total": 0, + "error_total": 1, + "lock_total": 0, + "spam_total": 0, + "vip_expire": 0, + "share_out_total": 0 + }, + "domains": [ + { + "id": 6, + "name": "dnspod.com", + "grade": "DP_Free", + "grade_title": "Free", + "status": "enable", + "ext_status": "notexist", + "records": "3", + "group_id": "1", + "is_mark": "no", + "remark": "", + "is_vip": "no", + "searchengine_push": "yes", + "beian": "no", + "created_on": "2014-06-04 16:19:31", + "updated_on": "2014-06-04 16:20:05", + "ttl": "600", + "owner": "[email protected]" + } + ] +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/record_already_exists.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/record_already_exists.json b/libcloud/test/dns/fixtures/dnspod/record_already_exists.json new file mode 100644 index 0000000..e7ed523 --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/record_already_exists.json @@ -0,0 +1,8 @@ +{ + "status": + { + "message": "Record impacted, same record exists or CNAME/URL impacted", + "code": "31", + "created_at": "2016-05-15 16:16:41" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/zone_already_exists.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/zone_already_exists.json b/libcloud/test/dns/fixtures/dnspod/zone_already_exists.json new file mode 100644 index 0000000..4eddb97 --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/zone_already_exists.json @@ -0,0 +1 @@ +{"status": {"message": "Domain is exists", "code": "7", "created_at": "2016-05-08 20:24:50"}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/fixtures/dnspod/zone_does_not_exist.json ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/fixtures/dnspod/zone_does_not_exist.json b/libcloud/test/dns/fixtures/dnspod/zone_does_not_exist.json new file mode 100644 index 0000000..e529872 --- /dev/null +++ b/libcloud/test/dns/fixtures/dnspod/zone_does_not_exist.json @@ -0,0 +1 @@ +{"status": {"message": "Domain id invalid", "code": "6", "created_at": "2016-05-08 18:50:57"}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/707a7255/libcloud/test/dns/test_dnspod.py ---------------------------------------------------------------------- diff --git a/libcloud/test/dns/test_dnspod.py b/libcloud/test/dns/test_dnspod.py new file mode 100644 index 0000000..7fcc4da --- /dev/null +++ b/libcloud/test/dns/test_dnspod.py @@ -0,0 +1,266 @@ +import sys +import unittest + + +from libcloud.test import MockHttp +from libcloud.test.file_fixtures import DNSFileFixtures +from libcloud.test.secrets import DNS_PARAMS_DNSPOD +from libcloud.dns.drivers.dnspod import DNSPodDNSDriver +from libcloud.utils.py3 import httplib +from libcloud.dns.types import ZoneDoesNotExistError, ZoneAlreadyExistsError,\ + RecordType, RecordDoesNotExistError, RecordAlreadyExistsError +from libcloud.dns.base import Zone, Record + + +class DNSPodDNSTests(unittest.TestCase): + def setUp(self): + DNSPodMockHttp.type = None + DNSPodDNSDriver.connectionCls.conn_classes = (None, DNSPodMockHttp) + self.driver = DNSPodDNSDriver(*DNS_PARAMS_DNSPOD) + self.test_zone = Zone(id='11', type='master', ttl=None, + domain='test.com', extra={}, driver=self.driver) + self.test_record = Record(id='13', type=RecordType.A, + name='example.com', zone=self.test_zone, + data='127.0.0.1', driver=self, extra={}) + + def test_one_equals_one(self): + self.assertEqual(1, 1) + + def test_list_zones_empty(self): + DNSPodMockHttp.type = 'EMPTY_ZONES_LIST' + zones = self.driver.list_zones() + + self.assertEqual(zones, []) + + def test_list_zones_success(self): + DNSPodMockHttp.type = 'LIST_ZONES' + zones = self.driver.list_zones() + + self.assertEqual(len(zones), 1) + + zone = zones[0] + self.assertEqual(zone.id, '6') + self.assertEqual(zone.domain, 'dnspod.com') + self.assertEqual(zone.type, None) + self.assertEqual(zone.driver, self.driver) + self.assertEqual(zone.ttl, '600') + + def test_get_zone_zone_does_not_exist(self): + DNSPodMockHttp.type = 'ZONE_DOES_NOT_EXIST' + try: + self.driver.get_zone(zone_id='13') + except ZoneDoesNotExistError: + e = sys.exc_info()[1] + self.assertEqual(e.zone_id, '13') + else: + self.fail('Exception was not thrown') + + def test_get_zone_success(self): + DNSPodMockHttp.type = 'GET_ZONE_SUCCESS' + zone = self.driver.get_zone(zone_id='6') + + self.assertEqual(zone.id, '6') + self.assertEqual(zone.domain, 'dnspod.com') + self.assertEqual(zone.type, None) + self.assertEqual(zone.ttl, '600') + self.assertEqual(zone.driver, self.driver) + + def test_delete_zone_success(self): + DNSPodMockHttp.type = 'DELETE_ZONE_SUCCESS' + zone = self.test_zone + status = self.driver.delete_zone(zone=zone) + + self.assertEqual(status, True) + + def test_delete_zone_zone_does_not_exist(self): + DNSPodMockHttp.type = 'DELETE_ZONE_ZONE_DOES_NOT_EXIST' + zone = self.test_zone + try: + self.driver.delete_zone(zone=zone) + except ZoneDoesNotExistError: + e = sys.exc_info()[1] + self.assertEqual(e.zone_id, '11') + else: + self.fail('Exception was not thrown') + + def test_create_zone_success(self): + DNSPodMockHttp.type = 'CREATE_ZONE_SUCCESS' + zone = self.driver.create_zone(domain='example.org') + + self.assertEqual(zone.id, '3') + self.assertEqual(zone.domain, 'api2.com') + self.assertEqual(zone.type, None) + self.assertEqual(zone.ttl, None) + self.assertEqual(zone.driver, self.driver) + + def test_create_zone_zone_zone_already_exists(self): + DNSPodMockHttp.type = 'CREATE_ZONE_ZONE_ALREADY_EXISTS' + try: + self.driver.create_zone(domain='test.com') + except ZoneAlreadyExistsError: + e = sys.exc_info()[1] + self.assertEqual(e.zone_id, 'test.com') + else: + self.fail('Exception was not thrown') + + def test_list_records_success(self): + DNSPodMockHttp.type = 'LIST_RECORDS_SUCCESS' + zone = self.test_zone + records = self.driver.list_records(zone=zone) + first_record = records[0] + + self.assertEqual(len(records), 5) + self.assertEqual(first_record.zone, zone) + self.assertEqual(first_record.type, 'A') + self.assertEqual(first_record.name, '@') + self.assertEqual(first_record.id, '50') + + def test_get_record_success(self): + DNSPodMockHttp.type = 'GET_RECORD_SUCCESS' + record = self.driver.get_record(zone_id='31', record_id='31') + + self.assertEqual(record.id, '50') + self.assertEqual(record.type, 'A') + self.assertEqual(record.name, '@') + self.assertEqual(record.data, '96.126.115.73') + + def test_delete_record_success(self): + DNSPodMockHttp.type = 'DELETE_RECORD_SUCCESS' + record = self.test_record + status = self.driver.delete_record(record=record) + + self.assertEqual(status, True) + + def test_delete_record_RECORD_DOES_NOT_EXIST_ERROR(self): + DNSPodMockHttp.type = 'DELETE_RECORD_RECORD_DOES_NOT_EXIST' + record = self.test_record + try: + self.driver.delete_record(record=record) + except RecordDoesNotExistError: + e = sys.exc_info()[1] + self.assertEqual(e.record_id, '13') + else: + self.fail('Exception was not thrown') + + def test_create_record_success(self): + DNSPodMockHttp.type = 'CREATE_RECORD_SUCCESS' + record = self.driver.create_record(name='@', zone=self.test_zone, + type='A', data='96.126.115.73', + extra={'ttl': 13, + 'record_line': 'default'}) + self.assertEqual(record.id, '50') + self.assertEqual(record.name, '@') + self.assertEqual(record.data, '96.126.115.73') + self.assertEqual(record.ttl, None) + + def test_create_record_already_exists_error(self): + DNSPodMockHttp.type = 'RECORD_EXISTS' + try: + self.driver.create_record(name='@', zone=self.test_zone, + type='A', data='92.126.115.73', + extra={'ttl': 13, + 'record_line': 'default'}) + except RecordAlreadyExistsError: + e = sys.exc_info()[1] + self.assertEqual(e.value, '@') + else: + self.fail('Exception was not thrown') + + +class DNSPodMockHttp(MockHttp): + fixtures = DNSFileFixtures('dnspod') + + def _Domain_List_EMPTY_ZONES_LIST(self, method, url, body, headers): + body = self.fixtures.load('empty_zones_list.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_List_LIST_ZONES(self, method, url, body, headers): + body = self.fixtures.load('list_zones.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_Info_ZONE_DOES_NOT_EXIST(self, method, url, body, headers): + body = self.fixtures.load('zone_does_not_exist.json') + + return 404, body, {}, httplib.responses[httplib.OK] + + def _Domain_Info_GET_ZONE_SUCCESS(self, method, url, body, headers): + body = self.fixtures.load('get_zone_success.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_Remove_DELETE_ZONE_SUCCESS(self, method, url, + body, headers): + body = self.fixtures.load('delete_zone_success.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_Remove_DELETE_ZONE_ZONE_DOES_NOT_EXIST( + self, method, url, body, headers): + body = self.fixtures.load('zone_does_not_exist.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_Create_CREATE_ZONE_SUCCESS(self, method, url, body, headers): + body = self.fixtures.load('create_zone_success.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_Create_CREATE_ZONE_ZONE_ALREADY_EXISTS( + self, method, url, body, headers): + body = self.fixtures.load('zone_already_exists.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Record_List_LIST_RECORDS_SUCCESS(self, method, url, body, headers): + body = self.fixtures.load('list_records.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Record_Info_GET_RECORD_SUCCESS(self, method, url, body, headers): + body = self.fixtures.load('get_record.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_Info_GET_RECORD_SUCCESS(self, method, url, body, headers): + body = self.fixtures.load('get_zone_success.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Record_Remove_DELETE_RECORD_SUCCESS(self, method, url, body, headers): + body = self.fixtures.load('delete_record_success.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Record_Remove_DELETE_RECORD_RECORD_DOES_NOT_EXIST(self, method, + url, body, headers): + body = self.fixtures.load('delete_record_record_does_not_exist.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Record_Create_CREATE_RECORD_SUCCESS(self, method, + url, body, headers): + body = self.fixtures.load('get_record.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Domain_Info_CREATE_RECORD_SUCCESS(self, method, url, body, headers): + body = self.fixtures.load('get_zone_success.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Record_Info_CREATE_RECORD_SUCCESS(self, method, + url, body, headers): + body = self.fixtures.load('get_record.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + def _Record_Create_RECORD_EXISTS(self, method, url, body, headers): + body = self.fixtures.load('record_already_exists.json') + + return httplib.OK, body, {}, httplib.responses[httplib.OK] + + +if __name__ == '__main__': + sys.exit(unittest.main())
