libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject [1/5] libcloud git commit: [LIBCLOUD-739] Add DNSimple provider implementation
Date Fri, 25 Sep 2015 13:06:05 GMT
Repository: libcloud
Updated Branches:
  refs/heads/trunk 73842ad81 -> b912123fd


[LIBCLOUD-739] Add DNSimple provider implementation


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/cacc8a9c
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/cacc8a9c
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/cacc8a9c

Branch: refs/heads/trunk
Commit: cacc8a9c9b915b5ab10a4ad59db034bd45d53b22
Parents: 1bf0f47
Author: Alejandro Pereira <alepereira86@gmail.com>
Authored: Mon Aug 31 19:20:41 2015 -0300
Committer: Alejandro Pereira <alepereira86@gmail.com>
Committed: Tue Sep 8 16:38:17 2015 -0300

----------------------------------------------------------------------
 docs/dns/drivers/dnsimple.rst                   |  22 ++
 .../examples/dns/dnsimple/instantiate_driver.py |   6 +
 libcloud/common/dnsimple.py                     |  52 ++++
 libcloud/dns/drivers/dnsimple.py                | 281 +++++++++++++++++++
 libcloud/dns/providers.py                       |   2 +
 libcloud/dns/types.py                           |   4 +
 .../dns/fixtures/dnsimple/create_domain.json    |  20 ++
 .../fixtures/dnsimple/create_domain_record.json |  13 +
 .../test/dns/fixtures/dnsimple/get_domain.json  |  20 ++
 .../fixtures/dnsimple/get_domain_record.json    |  13 +
 .../fixtures/dnsimple/list_domain_records.json  |  42 +++
 .../dns/fixtures/dnsimple/list_domains.json     |  42 +++
 .../fixtures/dnsimple/update_domain_record.json |  13 +
 libcloud/test/dns/test_dnsimple.py              | 254 +++++++++++++++++
 libcloud/test/secrets.py-dist                   |   1 +
 15 files changed, 785 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/docs/dns/drivers/dnsimple.rst
----------------------------------------------------------------------
diff --git a/docs/dns/drivers/dnsimple.rst b/docs/dns/drivers/dnsimple.rst
new file mode 100644
index 0000000..24cf40c
--- /dev/null
+++ b/docs/dns/drivers/dnsimple.rst
@@ -0,0 +1,22 @@
+DNSimple DNS Driver Documentation
+==================================
+
+`DNSimple`_ is a hosted DNS Service Provider that you can use to manage your
+domains. Offers both a web interface and an iPhone application for adding and
+removing domains and DNS records as well as an HTTP API with various code
+libraries and tools.
+
+Instantiating the driver
+-------------------------------------
+
+.. literalinclude:: /examples/dns/dnsimple/instantiate_driver.py
+   :language: python
+
+API Docs
+--------
+
+.. autoclass:: libcloud.dns.drivers.dnsimple.DNSimpleDNSDriver
+    :members:
+    :inherited-members:
+
+.. _`DNSimple`: https://dnsimple.com/
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/docs/examples/dns/dnsimple/instantiate_driver.py
----------------------------------------------------------------------
diff --git a/docs/examples/dns/dnsimple/instantiate_driver.py b/docs/examples/dns/dnsimple/instantiate_driver.py
new file mode 100644
index 0000000..b69301d
--- /dev/null
+++ b/docs/examples/dns/dnsimple/instantiate_driver.py
@@ -0,0 +1,6 @@
+from libcloud.dns.types import Provider
+from libcloud.dns.providers import get_driver
+
+cls = get_driver(Provider.DNSIMPLE)
+
+driver = cls('username', 'apikey')

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/common/dnsimple.py
----------------------------------------------------------------------
diff --git a/libcloud/common/dnsimple.py b/libcloud/common/dnsimple.py
new file mode 100644
index 0000000..cd3d852
--- /dev/null
+++ b/libcloud/common/dnsimple.py
@@ -0,0 +1,52 @@
+# 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 httplib
+from libcloud.common.base import ConnectionUserAndKey
+from libcloud.common.base import JsonResponse
+
+
+class DNSimpleDNSResponse(JsonResponse):
+
+    def success(self):
+        """
+        Determine if our request was successful.
+
+        The meaning of this can be arbitrary; did we receive OK status? Did
+        the node get created? Were we authenticated?
+
+        :rtype: ``bool``
+        :return: ``True`` or ``False``
+        """
+        # response.success() only checks for 200 and 201 codes. Should we
+        # add 204?
+        return self.status in [httplib.OK, httplib.CREATED, httplib.NO_CONTENT]
+
+
+class DNSimpleDNSConnection(ConnectionUserAndKey):
+    host = 'api.dnsimple.com'
+    responseCls = DNSimpleDNSResponse
+
+    def add_default_headers(self, headers):
+        """
+        Add headers that are necessary for every request
+
+        This method adds ``token`` to the request.
+        """
+        # TODO: fijarse sobre que info se paso como parametro y en base
+        # a esto, fijar el header
+        headers['X-DNSimple-Token'] = '%s:%s' % (self.user_id, self.key)
+        headers['Accept'] = 'application/json'
+        headers['Content-Type'] = 'application/json'
+        return headers

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/dns/drivers/dnsimple.py
----------------------------------------------------------------------
diff --git a/libcloud/dns/drivers/dnsimple.py b/libcloud/dns/drivers/dnsimple.py
new file mode 100644
index 0000000..3c2e752
--- /dev/null
+++ b/libcloud/dns/drivers/dnsimple.py
@@ -0,0 +1,281 @@
+# 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.
+"""
+DNSimple DNS Driver
+"""
+
+__all__ = [
+    'DNSimpleDNSDriver'
+]
+
+import json
+
+from libcloud.common.dnsimple import DNSimpleDNSConnection
+from libcloud.dns.types import Provider, RecordType
+from libcloud.dns.base import DNSDriver, Zone, Record
+
+
+DEFAULT_ZONE_TTL = 3600
+
+
+class DNSimpleDNSDriver(DNSDriver):
+    type = Provider.DNSIMPLE
+    name = 'DNSimple'
+    website = 'https://dnsimple.com/'
+    connectionCls = DNSimpleDNSConnection
+
+    RECORD_TYPE_MAP = {
+        RecordType.A: 'A',
+        RecordType.AAAA: 'AAAA',
+        RecordType.ALIAS: 'ALIAS',
+        RecordType.CNAME: 'CNAME',
+        RecordType.HINFO: 'HINFO',
+        RecordType.MX: 'MX',
+        RecordType.NAPTR: 'NAPTR',
+        RecordType.NS: 'NS',
+        'POOL': 'POOL',
+        RecordType.SOA: 'SOA',
+        RecordType.SPF: 'SPF',
+        RecordType.SRV: 'SRV',
+        RecordType.SSHFP: 'SSHFP',
+        RecordType.TXT: 'TXT',
+        RecordType.URL: 'URL'
+    }
+
+    def list_zones(self):
+        """
+        Return a list of zones.
+
+        :return: ``list`` of :class:`Zone`
+        """
+        response = self.connection.request('/v1/domains')
+
+        zones = self._to_zones(response.object)
+        return zones
+
+    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`
+        """
+        response = self.connection.request('/v1/domains/%s/records' % zone.id)
+        records = self._to_records(response.object, zone)
+        return records
+
+    def get_zone(self, zone_id):
+        """
+        Return a Zone instance.
+
+        :param zone_id: ID of the required zone
+        :type  zone_id: ``str``
+
+        :rtype: :class:`Zone`
+        """
+        response = self.connection.request('/v1/domains/%s' % zone_id)
+        zone = self._to_zone(response.object)
+        return zone
+
+    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`
+        """
+        response = self.connection.request('/v1/domains/%s/records/%s' %
+                                           (zone_id, record_id))
+        record = self._to_record(response.object, zone_id=zone_id)
+        return record
+
+    def create_zone(self, domain, extra=None):
+        """
+        Create a new zone.
+
+        :param domain: Zone domain name (e.g. example.com)
+        :type domain: ``str``
+
+        :param extra: Extra attributes (driver specific). (optional)
+        :type extra: ``dict``
+
+        :rtype: :class:`Zone`
+
+        For more info, please see:
+        http://developer.dnsimple.com/v1/domains/
+        """
+        r_json = {'name': domain}
+        if extra is not None:
+            r_json.update(extra)
+        r_data = json.dumps({'domain': r_json})
+        response = self.connection.request('/v1/domains', method='POST',
+                                           data=r_data)
+        zone = self._to_zone(response.object)
+        return zone
+
+    def create_record(self, name, zone, type, data, extra=None):
+        """
+        Create a new 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 where the requested record is created.
+        :type  zone: :class:`Zone`
+
+        :param type: DNS record type (A, AAAA, ...).
+        :type  type: :class:`RecordType`
+
+        :param data: Data for the record (depends on the record type).
+        :type  data: ``str``
+
+        :param extra: Extra attributes (driver specific). (optional)
+        :type extra: ``dict``
+
+        :rtype: :class:`Record`
+        """
+        r_json = {'name': name, 'record_type': type, 'content': data}
+        if extra is not None:
+            r_json.update(extra)
+        r_data = json.dumps({'record': r_json})
+        response = self.connection.request('/v1/domains/%s/records' % zone.id,
+                                           method='POST', data=r_data)
+        record = self._to_record(response.object, zone=zone)
+        return record
+
+    def update_record(self, record, name, data, extra=None):
+        """
+        Update an existing record.
+
+        :param record: Record to update.
+        :type  record: :class:`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 data: Data for the record (depends on the record type).
+        :type  data: ``str``
+
+        :param extra: (optional) Extra attributes (driver specific).
+        :type  extra: ``dict``
+
+        :rtype: :class:`Record`
+        """
+        zone = record.zone
+        r_json = {'name': name, 'content': data}
+        if extra is not None:
+            r_json.update(extra)
+        r_data = json.dumps({'record': r_json})
+        response = self.connection.request('/v1/domains/%s/records/%s' %
+                                           (zone.id, record.id),
+                                           method='PUT', data=r_data)
+        record = self._to_record(response.object, zone=zone)
+        return record
+
+    def delete_zone(self, zone):
+        """
+        Delete a zone.
+
+        Note: This will delete all the records belonging to this zone.
+
+        :param zone: Zone to delete.
+        :type  zone: :class:`Zone`
+
+        :rtype: ``bool``
+        """
+        self.connection.request('/v1/domains/%s' % zone.id, method='DELETE')
+        return True
+
+    def delete_record(self, record):
+        """
+        Delete a record.
+
+        :param record: Record to delete.
+        :type  record: :class:`Record`
+
+        :rtype: ``bool``
+        """
+        zone_id = record.zone.id
+        self.connection.request('/v1/domains/%s/records/%s' % (zone_id,
+                                record.id), method='DELETE')
+        return True
+
+    def _to_zones(self, data):
+        zones = []
+        for zone in data:
+            _zone = self._to_zone(zone)
+            zones.append(_zone)
+
+        return zones
+
+    def _to_zone(self, data):
+        domain = data.get('domain')
+        id = domain.get('id')
+        name = domain.get('name')
+        extra = {'registrant_id': domain.get('registrant_id'),
+                 'user_id': domain.get('user_id'),
+                 'unicode_name': domain.get('unicode_name'),
+                 'token': domain.get('token'),
+                 'state': domain.get('state'),
+                 'language': domain.get('language'),
+                 'lockable': domain.get('lockable'),
+                 'auto_renew': domain.get('auto_renew'),
+                 'whois_protected': domain.get('whois_protected'),
+                 'record_count': domain.get('record_count'),
+                 'service_count': domain.get('service_count'),
+                 'expires_on': domain.get('expires_on'),
+                 'created_at': domain.get('created_at'),
+                 'updated_at': domain.get('updated_at')}
+
+        # All zones are primary by design
+        type = 'master'
+
+        return Zone(id=id, domain=name, type=type, ttl=DEFAULT_ZONE_TTL,
+                    driver=self, extra=extra)
+
+    def _to_records(self, data, zone):
+        records = []
+        for item in data:
+            record = self._to_record(item, zone=zone)
+            records.append(record)
+        return records
+
+    def _to_record(self, data, zone_id=None, zone=None):
+        if not zone:  # We need zone_id or zone
+            zone = self.get_zone(zone_id)
+        record = data.get('record')
+        id = record.get('id')
+        name = record.get('name')
+        type = record.get('record_type')
+        data = record.get('content')
+        extra = {'ttl': record.get('ttl'),
+                 'created_at': record.get('created_at'),
+                 'updated_at': record.get('updated_at'),
+                 'domain_id': record.get('domain_id'),
+                 'prio': record.get('prio')}
+        return Record(id, name, type, data, zone, self, extra=extra)

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/dns/providers.py
----------------------------------------------------------------------
diff --git a/libcloud/dns/providers.py b/libcloud/dns/providers.py
index aef0aa7..497b0d6 100644
--- a/libcloud/dns/providers.py
+++ b/libcloud/dns/providers.py
@@ -37,6 +37,8 @@ DRIVERS = {
     ('libcloud.dns.drivers.softlayer', 'SoftLayerDNSDriver'),
     Provider.DIGITAL_OCEAN:
     ('libcloud.dns.drivers.digitalocean', 'DigitalOceanDNSDriver'),
+    Provider.DNSIMPLE:
+    ('libcloud.dns.drivers.dnsimple', 'DNSimpleDNSDriver'),
     # Deprecated
     Provider.RACKSPACE_US:
     ('libcloud.dns.drivers.rackspace', 'RackspaceUSDNSDriver'),

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/dns/types.py
----------------------------------------------------------------------
diff --git a/libcloud/dns/types.py b/libcloud/dns/types.py
index ad2b34a..463a3fb 100644
--- a/libcloud/dns/types.py
+++ b/libcloud/dns/types.py
@@ -39,6 +39,7 @@ class Provider(object):
     SOFTLAYER = 'softlayer'
     DIGITAL_OCEAN = 'digitalocean'
     AURORADNS = 'auroradns'
+    DNSIMPLE = 'dnsimple'
 
     # Deprecated
     RACKSPACE_US = 'rackspace_us'
@@ -51,15 +52,18 @@ class RecordType(object):
     """
     A = 'A'
     AAAA = 'AAAA'
+    ALIAS = 'ALIAS'
     MX = 'MX'
     NS = 'NS'
     CNAME = 'CNAME'
     DNAME = 'DNAME'
+    HINFO = 'HINFO'
     TXT = 'TXT'
     PTR = 'PTR'
     SOA = 'SOA'
     SPF = 'SPF'
     SRV = 'SRV'
+    SSHFP = 'SSHFP'
     PTR = 'PTR'
     NAPTR = 'NAPTR'
     REDIRECT = 'REDIRECT'

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/fixtures/dnsimple/create_domain.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/dnsimple/create_domain.json b/libcloud/test/dns/fixtures/dnsimple/create_domain.json
new file mode 100644
index 0000000..ace9f32
--- /dev/null
+++ b/libcloud/test/dns/fixtures/dnsimple/create_domain.json
@@ -0,0 +1,20 @@
+{
+  "domain": {
+    "id": 1,
+    "user_id": 21,
+    "registrant_id": 321,
+    "name": "example.com",
+    "unicode_name": "example.com",
+    "token": "domain-token",
+    "state": "hosted",
+    "language": null,
+    "lockable": false,
+    "auto_renew": true,
+    "whois_protected": false,
+    "record_count": 5,
+    "service_count": 1,
+    "expires_on": null,
+    "created_at": "2012-09-27T14:25:57.646Z",
+    "updated_at": "2014-12-15T20:27:04.552Z"
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/fixtures/dnsimple/create_domain_record.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/dnsimple/create_domain_record.json b/libcloud/test/dns/fixtures/dnsimple/create_domain_record.json
new file mode 100644
index 0000000..4534a9d
--- /dev/null
+++ b/libcloud/test/dns/fixtures/dnsimple/create_domain_record.json
@@ -0,0 +1,13 @@
+{
+  "record": {
+    "content": "mail.example.com",
+    "created_at": "2013-01-29T14:25:38Z",
+    "domain_id": 28,
+    "id": 172,
+    "name": "",
+    "prio": 10,
+    "record_type": "MX",
+    "ttl": 3600,
+    "updated_at": "2013-01-29T14:25:38Z"
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/fixtures/dnsimple/get_domain.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/dnsimple/get_domain.json b/libcloud/test/dns/fixtures/dnsimple/get_domain.json
new file mode 100644
index 0000000..ae2dda9
--- /dev/null
+++ b/libcloud/test/dns/fixtures/dnsimple/get_domain.json
@@ -0,0 +1,20 @@
+{
+  "domain": {
+    "id": 1,
+    "user_id": 21,
+    "registrant_id": 321,
+    "name": "example.com",
+    "unicode_name": "example.com",
+    "token": "domain-token",
+    "state": "registered",
+    "language": null,
+    "lockable": false,
+    "auto_renew": true,
+    "whois_protected": false,
+    "record_count": 5,
+    "service_count": 1,
+    "expires_on": "2015-09-27",
+    "created_at": "2012-09-27T14:25:57.646Z",
+    "updated_at": "2014-12-15T20:27:04.552Z"
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/fixtures/dnsimple/get_domain_record.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/dnsimple/get_domain_record.json b/libcloud/test/dns/fixtures/dnsimple/get_domain_record.json
new file mode 100644
index 0000000..08d6665
--- /dev/null
+++ b/libcloud/test/dns/fixtures/dnsimple/get_domain_record.json
@@ -0,0 +1,13 @@
+{
+  "record": {
+    "name": "www",
+    "ttl": 3600,
+    "created_at": "2010-07-01T08:01:18Z",
+    "updated_at": "2010-10-21T15:47:47Z",
+    "domain_id": 1,
+    "id": 123,
+    "content": "example.com",
+    "record_type": "CNAME",
+    "prio": null
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/fixtures/dnsimple/list_domain_records.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/dnsimple/list_domain_records.json b/libcloud/test/dns/fixtures/dnsimple/list_domain_records.json
new file mode 100644
index 0000000..71a6ff9
--- /dev/null
+++ b/libcloud/test/dns/fixtures/dnsimple/list_domain_records.json
@@ -0,0 +1,42 @@
+[
+  {
+    "record": {
+      "name": "",
+      "ttl": 3600,
+      "created_at": "2010-07-04T04:41:31Z",
+      "updated_at": "2010-10-21T15:47:47Z",
+      "domain_id": 1,
+      "id": 31,
+      "content": "1.2.3.4",
+      "record_type": "A",
+      "prio": null
+    }
+  },
+  {
+    "record": {
+      "name": "www",
+      "ttl": 3600,
+      "created_at": "2010-07-01T08:01:18Z",
+      "updated_at": "2010-10-21T15:47:47Z",
+      "domain_id": 1,
+      "id": 2,
+      "content": "example.com",
+      "record_type": "CNAME",
+      "prio": null
+    }
+  },
+  {
+    "record": {
+      "name": "",
+      "ttl": 3600,
+      "created_at": "2010-07-04T04:42:11Z",
+      "updated_at": "2010-10-21T15:47:47Z",
+      "pdns_identifier": "40",
+      "domain_id": 1,
+      "id": 32,
+      "content": "mail.example.com",
+      "record_type": "MX",
+      "prio": 10
+    }
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/fixtures/dnsimple/list_domains.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/dnsimple/list_domains.json b/libcloud/test/dns/fixtures/dnsimple/list_domains.json
new file mode 100644
index 0000000..282cc30
--- /dev/null
+++ b/libcloud/test/dns/fixtures/dnsimple/list_domains.json
@@ -0,0 +1,42 @@
+[
+  {
+    "domain": {
+      "id": 1,
+      "user_id": 21,
+      "registrant_id": 321,
+      "name": "example.com",
+      "unicode_name": "example.com",
+      "token": "domain-token",
+      "state": "registered",
+      "language": null,
+      "lockable": false,
+      "auto_renew": true,
+      "whois_protected": false,
+      "record_count": 5,
+      "service_count": 1,
+      "expires_on": "2015-09-27",
+      "created_at": "2012-09-27T14:25:57.646Z",
+      "updated_at": "2014-12-15T20:27:04.552Z"
+    }
+  },
+  {
+    "domain": {
+      "id": 2,
+      "user_id": 22,
+      "registrant_id": 322,
+      "name": "example.com",
+      "unicode_name": "example.it",
+      "token": "domain-token",
+      "state": "hosted",
+      "language": null,
+      "lockable": false,
+      "auto_renew": true,
+      "whois_protected": false,
+      "record_count": 5,
+      "service_count": 1,
+      "expires_on": null,
+      "created_at": "2012-09-27T14:25:57.646Z",
+      "updated_at": "2014-12-15T20:27:04.552Z"
+    }
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/fixtures/dnsimple/update_domain_record.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/dnsimple/update_domain_record.json b/libcloud/test/dns/fixtures/dnsimple/update_domain_record.json
new file mode 100644
index 0000000..9203663
--- /dev/null
+++ b/libcloud/test/dns/fixtures/dnsimple/update_domain_record.json
@@ -0,0 +1,13 @@
+{
+  "record": {
+    "name": "www",
+    "ttl": 4500,
+    "created_at": "2010-07-01T08:01:18Z",
+    "updated_at": "2010-10-21T15:47:47Z",
+    "domain_id": 1,
+    "id": 123,
+    "content": "updated.com",
+    "record_type": "CNAME",
+    "prio": null
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/dns/test_dnsimple.py
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/test_dnsimple.py b/libcloud/test/dns/test_dnsimple.py
new file mode 100644
index 0000000..1f2f95c
--- /dev/null
+++ b/libcloud/test/dns/test_dnsimple.py
@@ -0,0 +1,254 @@
+# 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
+import sys
+import unittest
+
+from libcloud.utils.py3 import httplib
+
+from libcloud.dns.types import RecordType
+from libcloud.dns.drivers.dnsimple import DNSimpleDNSDriver
+
+from libcloud.test import MockHttp
+from libcloud.test.file_fixtures import DNSFileFixtures
+from libcloud.test.secrets import DNS_PARAMS_DNSIMPLE
+
+
+class DNSimpleDNSTests(unittest.TestCase):
+    def setUp(self):
+        DNSimpleDNSDriver.connectionCls.conn_classes = (
+            None, DNSimpleDNSMockHttp)
+        DNSimpleDNSMockHttp.type = None
+        self.driver = DNSimpleDNSDriver(*DNS_PARAMS_DNSIMPLE)
+
+    def assertHasKeys(self, dictionary, keys):
+        for key in keys:
+            self.assertTrue(key in dictionary, 'key "%s" not in dictionary' %
+                            (key))
+
+    def test_list_record_types(self):
+        record_types = self.driver.list_record_types()
+        self.assertEqual(len(record_types), 15)
+        self.assertTrue(RecordType.A in record_types)
+        self.assertTrue(RecordType.AAAA in record_types)
+        self.assertTrue(RecordType.ALIAS in record_types)
+        self.assertTrue(RecordType.CNAME in record_types)
+        self.assertTrue(RecordType.HINFO in record_types)
+        self.assertTrue(RecordType.MX in record_types)
+        self.assertTrue(RecordType.NAPTR in record_types)
+        self.assertTrue(RecordType.NS in record_types)
+        self.assertTrue('POOL' in record_types)
+        self.assertTrue(RecordType.SPF in record_types)
+        self.assertTrue(RecordType.SOA in record_types)
+        self.assertTrue(RecordType.SRV in record_types)
+        self.assertTrue(RecordType.SSHFP in record_types)
+        self.assertTrue(RecordType.TXT in record_types)
+        self.assertTrue(RecordType.URL in record_types)
+
+    def test_list_zones_success(self):
+        zones = self.driver.list_zones()
+        self.assertEqual(len(zones), 2)
+
+        zone1 = zones[0]
+        self.assertEqual(zone1.id, '1')
+        self.assertEqual(zone1.type, 'master')
+        self.assertEqual(zone1.domain, 'example.com')
+        self.assertEqual(zone1.ttl, 3600)
+        self.assertHasKeys(zone1.extra, ['registrant_id', 'user_id',
+                                         'unicode_name', 'token', 'state',
+                                         'language', 'lockable', 'auto_renew',
+                                         'whois_protected', 'record_count',
+                                         'service_count', 'expires_on',
+                                         'created_at', 'updated_at'])
+
+        zone2 = zones[1]
+        self.assertEqual(zone2.id, '2')
+        self.assertEqual(zone2.type, 'master')
+        self.assertEqual(zone2.domain, 'example.com')
+        self.assertEqual(zone2.ttl, 3600)
+        self.assertHasKeys(zone2.extra, ['registrant_id', 'user_id',
+                                         'unicode_name', 'token', 'state',
+                                         'language', 'lockable', 'auto_renew',
+                                         'whois_protected', 'record_count',
+                                         'service_count', 'expires_on',
+                                         'created_at', 'updated_at'])
+
+    def test_list_records_success(self):
+        zone = self.driver.list_zones()[0]
+        records = self.driver.list_records(zone=zone)
+        self.assertEqual(len(records), 3)
+
+        record1 = records[0]
+        self.assertEqual(record1.id, '31')
+        self.assertEqual(record1.name, '')
+        self.assertEqual(record1.type, RecordType.A)
+        self.assertEqual(record1.data, '1.2.3.4')
+        self.assertHasKeys(record1.extra, ['ttl', 'created_at', 'updated_at',
+                                           'domain_id', 'prio'])
+
+        record2 = records[1]
+        self.assertEqual(record2.id, '2')
+        self.assertEqual(record2.name, 'www')
+        self.assertEqual(record2.type, RecordType.CNAME)
+        self.assertEqual(record2.data, 'example.com')
+        self.assertHasKeys(record2.extra, ['ttl', 'created_at', 'updated_at',
+                                           'domain_id', 'prio'])
+
+        record3 = records[2]
+        self.assertEqual(record3.id, '32')
+        self.assertEqual(record3.name, '')
+        self.assertEqual(record3.type, RecordType.MX)
+        self.assertEqual(record3.data, 'mail.example.com')
+        self.assertHasKeys(record3.extra, ['ttl', 'created_at', 'updated_at',
+                                           'domain_id', 'prio'])
+
+    def test_get_zone_success(self):
+        zone1 = self.driver.get_zone(zone_id='1')
+        self.assertEqual(zone1.id, '1')
+        self.assertEqual(zone1.type, 'master')
+        self.assertEqual(zone1.domain, 'example.com')
+        self.assertEqual(zone1.ttl, 3600)
+        self.assertHasKeys(zone1.extra, ['registrant_id', 'user_id',
+                                         'unicode_name', 'token', 'state',
+                                         'language', 'lockable', 'auto_renew',
+                                         'whois_protected', 'record_count',
+                                         'service_count', 'expires_on',
+                                         'created_at', 'updated_at'])
+
+    def test_get_record_success(self):
+        record = self.driver.get_record(zone_id='1',
+                                        record_id='123')
+        self.assertEqual(record.id, '123')
+        self.assertEqual(record.name, 'www')
+        self.assertEqual(record.type, RecordType.CNAME)
+        self.assertEqual(record.data, 'example.com')
+        self.assertHasKeys(record.extra, ['ttl', 'created_at', 'updated_at',
+                                          'domain_id', 'prio'])
+
+    def test_create_zone_success(self):
+        DNSimpleDNSMockHttp.type = 'CREATE'
+        zone = self.driver.create_zone(domain='example.com')
+        self.assertEqual(zone.id, '1')
+        self.assertEqual(zone.domain, 'example.com')
+        self.assertEqual(zone.ttl, 3600)
+        self.assertEqual(zone.type, 'master')
+        self.assertHasKeys(zone.extra, ['registrant_id', 'user_id',
+                                        'unicode_name', 'token', 'state',
+                                        'language', 'lockable', 'auto_renew',
+                                        'whois_protected', 'record_count',
+                                        'service_count', 'expires_on',
+                                        'created_at', 'updated_at'])
+
+    def test_create_record_success(self):
+        zone = self.driver.list_zones()[0]
+        DNSimpleDNSMockHttp.type = 'CREATE'
+        record = self.driver.create_record(name='domain4', zone=zone,
+                                           type=RecordType.MX,
+                                           data='mail.example.com')
+        self.assertEqual(record.id, '172')
+        self.assertEqual(record.name, '')
+        self.assertEqual(record.type, RecordType.MX)
+        self.assertEqual(record.data, 'mail.example.com')
+        self.assertHasKeys(record.extra, ['ttl', 'created_at', 'updated_at',
+                                          'domain_id', 'prio'])
+
+    def test_update_record_success(self):
+        record = self.driver.get_record(zone_id='1',
+                                        record_id='123')
+        DNSimpleDNSMockHttp.type = 'UPDATE'
+        extra = {'ttl': 4500}
+        record1 = self.driver.update_record(record=record, name='www',
+                                            data='updated.com',
+                                            extra=extra)
+        self.assertEqual(record.data, 'example.com')
+        self.assertEqual(record.extra.get('ttl'), 3600)
+        self.assertEqual(record1.data, 'updated.com')
+        self.assertEqual(record1.extra.get('ttl'), 4500)
+
+    def test_delete_zone_success(self):
+        zone = self.driver.list_zones()[0]
+        DNSimpleDNSMockHttp.type = 'DELETE_200'
+        status = self.driver.delete_zone(zone=zone)
+        self.assertTrue(status)
+
+    def test_delete_zone_success_future_implementation(self):
+        zone = self.driver.list_zones()[0]
+        DNSimpleDNSMockHttp.type = 'DELETE_204'
+        status = self.driver.delete_zone(zone=zone)
+        self.assertTrue(status)
+
+    def test_delete_record_success(self):
+        zone = self.driver.list_zones()[0]
+        records = self.driver.list_records(zone=zone)
+        self.assertEqual(len(records), 3)
+        record = records[1]
+        DNSimpleDNSMockHttp.type = 'DELETE_200'
+        status = self.driver.delete_record(record=record)
+        self.assertTrue(status)
+
+    def test_delete_record_success_future_implementation(self):
+        zone = self.driver.list_zones()[0]
+        records = self.driver.list_records(zone=zone)
+        self.assertEqual(len(records), 3)
+        record = records[1]
+        DNSimpleDNSMockHttp.type = 'DELETE_204'
+        status = self.driver.delete_record(record=record)
+        self.assertTrue(status)
+
+
+class DNSimpleDNSMockHttp(MockHttp):
+    fixtures = DNSFileFixtures('dnsimple')
+
+    def _v1_domains(self, method, url, body, headers):
+        body = self.fixtures.load('list_domains.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_CREATE(self, method, url, body, headers):
+        body = self.fixtures.load('create_domain.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1(self, method, url, body, headers):
+        body = self.fixtures.load('get_domain.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1_records(self, method, url, body, headers):
+        body = self.fixtures.load('list_domain_records.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1_records_123(self, method, url, body, headers):
+        body = self.fixtures.load('get_domain_record.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1_records_CREATE(self, method, url, body, headers):
+        body = self.fixtures.load('create_domain_record.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1_records_123_UPDATE(self, method, url, body, headers):
+        body = self.fixtures.load('update_domain_record.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1_DELETE_200(self, method, url, body, headers):
+        return (httplib.OK, '', {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1_DELETE_204(self, method, url, body, headers):
+        return (httplib.OK, '', {}, httplib.responses[httplib.NO_CONTENT])
+
+    def _v1_domains_1_records_2_DELETE_200(self, method, url, body, headers):
+        return (httplib.OK, '', {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_1_records_2_DELETE_204(self, method, url, body, headers):
+        return (httplib.OK, '', {}, httplib.responses[httplib.NO_CONTENT])
+
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())

http://git-wip-us.apache.org/repos/asf/libcloud/blob/cacc8a9c/libcloud/test/secrets.py-dist
----------------------------------------------------------------------
diff --git a/libcloud/test/secrets.py-dist b/libcloud/test/secrets.py-dist
index 3ebcac3..c7b52f5 100644
--- a/libcloud/test/secrets.py-dist
+++ b/libcloud/test/secrets.py-dist
@@ -72,3 +72,4 @@ DNS_PARAMS_ROUTE53 = ('access_id', 'secret')
 DNS_GANDI = ('user', )
 DNS_PARAMS_GOOGLE = ('email_address', 'key')
 DNS_KEYWORD_PARAMS_GOOGLE = {'project': 'project_name'}
+DNS_PARAMS_DNSIMPLE = ('user', 'key')


Mime
View raw message