Skip to content

Commit

Permalink
Merge 1c76cf0 into a8afd33
Browse files Browse the repository at this point in the history
  • Loading branch information
AnalogJ committed Mar 30, 2016
2 parents a8afd33 + 1c76cf0 commit aec15f6
Show file tree
Hide file tree
Showing 19 changed files with 1,343 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The current supported providers are:
- EasyDNS ([docs](http://docs.sandbox.rest.easydns.net/))
- NS1 ([docs](https://ns1.com/api/))
- PointHQ ([docs](https://pointhq.com/api/docs))
- Rage4 ([docs](https://gbshouse.uservoice.com/knowledgebase/articles/109834-rage4-dns-developers-api))


Potential providers are as follows. If you would like to contribute one, please open a pull request.

Expand All @@ -38,7 +40,6 @@ Potential providers are as follows. If you would like to contribute one, please
- OnApp DNS ([docs](https://docs.onapp.com/display/3api/DNS+Zones))
- PowerDNS ([docs](https://doc.powerdns.com/md/httpapi/api_spec/))
- Rackspace ([docs](https://developer.rackspace.com/docs/cloud-dns/v1/developer-guide/))
- Rage4 ([docs](https://gbshouse.uservoice.com/knowledgebase/articles/109834-rage4-dns-developers-api))
- Transip ([docs](https://www.transip.nl/transip/api/))
- UltraDNS ([docs](https://restapi.ultradns.com/v1/docs))
- Yandex ([docs](https://tech.yandex.com/domain/doc/reference/dns-add-docpage/))
Expand Down
139 changes: 139 additions & 0 deletions lexicon/providers/rage4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
from base import Provider as BaseProvider
import requests
import json

class Provider(BaseProvider):

def __init__(self, options, provider_options={}):
super(Provider, self).__init__(options)
self.domain_id = None
self.api_endpoint = provider_options.get('api_endpoint') or 'https://rage4.com/rapi'

def authenticate(self):

payload = self._get('/getdomainbyname/', {'name': self.options['domain']})

if not payload['id']:
raise StandardError('No domain found')

self.domain_id = payload['id']

# Create record. If record already exists with the same content, do nothing'
def create_record(self, type, name, content):
record = {
'id': self.domain_id,
'name': self._clean_name(name),
'content': content,
'type': type
}
payload = {}
try:
payload = self._post('/createrecord/',{},record)
except requests.exceptions.HTTPError, e:
if e.response.status_code == 400:
payload = {}

# http 400 is ok here, because the record probably already exists
print 'create_record: {0}'.format(payload['status'])
return payload['status']

# List all records. Return an empty list if no records found
# type, name and content are used to filter records.
# If possible filter during the query, otherwise filter after response is received.
def list_records(self, type=None, name=None, content=None):
filter = {
'id': self.domain_id
}
if name:
filter['name'] = self._clean_name(name)
payload = self._get('/getrecords/', filter)

records = []
for record in payload:
processed_record = {
'type': record['type'],
'name': record['name'],
'ttl': record['ttl'],
'content': record['content'],
'id': record['id']
}
records.append(processed_record)


print 'list_records: {0}'.format(records)
return records

# Create or update a record.
def update_record(self, identifier, type=None, name=None, content=None):

data = {
'id': identifier
}

if name:
data['name'] = self._clean_name(name)
if content:
data['content'] = content
# if type:
# raise 'Type updating is not supported by this provider.'

payload = self._put('/updaterecord/', {}, data)

print 'update_record: {0}'.format(payload['status'])
return payload['status']

# Delete an existing record.
# If record does not exist, do nothing.
def delete_record(self, identifier=None, type=None, name=None, content=None):
if not identifier:
records = self.list_records(type, name, content)
print records
if len(records) == 1:
identifier = records[0]['id']
else:
raise StandardError('Record identifier could not be found.')
payload = self._post('/deleterecord/', {'id': identifier})

# is always True at this point, if a non 200 response is returned an error is raised.
print 'delete_record: {0}'.format(payload['status'])
return payload['status']


# Helpers

# record names can be in a variety of formats: relative (sub), full (sub.example.com), and fqdn (sub.example.com.)
# Rage4 handles full record names, so we need to make sure we clean up all user specified record_names before
# submitting them
def _clean_name(self, record_name):
record_name = record_name.rstrip('.') # strip trailing period from fqdn if present
#check if the record_name is fully specified
if not record_name.endswith(self.options['domain']):
record_name = "{0}.{1}".format(record_name, self.options['domain'])
return record_name

def _get(self, url='/', query_params={}):
return self._request('GET', url, query_params=query_params)

def _post(self, url='/', data={}, query_params={}):
return self._request('POST', url, data=data, query_params=query_params)

def _put(self, url='/', data={}, query_params={}):
return self._request('PUT', url, data=data, query_params=query_params)

def _delete(self, url='/', query_params={}):
return self._request('DELETE', url, query_params=query_params)

def _request(self, action='GET', url='/', data={}, query_params={}):

default_headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
default_auth = requests.auth.HTTPBasicAuth(self.options['auth_username'], self.options['auth_token'])

r = requests.request(action, self.api_endpoint + url, params=query_params,
data=json.dumps(data),
headers=default_headers,
auth=default_auth)
r.raise_for_status() # if the request fails for any reason, throw an error.
return r.json()
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
interactions:
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: GET
uri: https://rage4.com/rapi/getdomainbyname/?name=capsulecd.com
response:
body: {string: !!python/unicode '{"id":57039,"name":"capsulecd.com","owner_email":"[email protected]","type":0,"subnet_mask":0,"default_ns1":"ns1.r4ns.com","default_ns2":"ns2.r4ns.net"}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['157']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:15 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
transfer-encoding: [chunked]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
interactions:
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: GET
uri: https://rage4.com/rapi/getdomainbyname/?name=thisisadomainidonotown.com
response:
body: {string: !!python/unicode '{"status":false,"id":0,"error":"Unable to fetch
the item or API access not allowed"}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['84']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:16 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
transfer-encoding: [chunked]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
interactions:
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: GET
uri: https://rage4.com/rapi/getdomainbyname/?name=capsulecd.com
response:
body: {string: !!python/unicode '{"id":57039,"name":"capsulecd.com","owner_email":"[email protected]","type":0,"subnet_mask":0,"default_ns1":"ns1.r4ns.com","default_ns2":"ns2.r4ns.net"}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['157']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:23 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: POST
uri: https://rage4.com/rapi/createrecord/?content=127.0.0.1&type=A&id=57039&name=localhost.capsulecd.com
response:
body: {string: !!python/unicode '{"status":true,"id":1954621,"error":""}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['39']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:43 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
transfer-encoding: [chunked]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
interactions:
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: GET
uri: https://rage4.com/rapi/getdomainbyname/?name=capsulecd.com
response:
body: {string: !!python/unicode '{"id":57039,"name":"capsulecd.com","owner_email":"[email protected]","type":0,"subnet_mask":0,"default_ns1":"ns1.r4ns.com","default_ns2":"ns2.r4ns.net"}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['157']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:24 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: POST
uri: https://rage4.com/rapi/createrecord/?content=docs.example.com&type=CNAME&id=57039&name=docs.capsulecd.com
response:
body: {string: !!python/unicode '{"status":true,"id":1954623,"error":""}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['39']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:38 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
transfer-encoding: [chunked]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
interactions:
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: GET
uri: https://rage4.com/rapi/getdomainbyname/?name=capsulecd.com
response:
body: {string: !!python/unicode '{"id":57039,"name":"capsulecd.com","owner_email":"[email protected]","type":0,"subnet_mask":0,"default_ns1":"ns1.r4ns.com","default_ns2":"ns2.r4ns.net"}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['157']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:18 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
- request:
body: '{}'
headers:
Accept: [application/json]
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [python-requests/2.9.1]
method: POST
uri: https://rage4.com/rapi/createrecord/?content=challengetoken&type=TXT&id=57039&name=_acme-challenge.fqdn.capsulecd.com
response:
body: {string: !!python/unicode '{"status":true,"id":1954625,"error":""}'}
headers:
access-control-allow-origin: ['*']
cache-control: ['private, s-maxage=0']
content-length: ['39']
content-type: [text/plain; charset=utf-8]
date: ['Wed, 30 Mar 2016 06:39:45 GMT']
server: [GBSHouse/2.0]
strict-transport-security: [max-age=31536000; includeSubDomains; preload;]
transfer-encoding: [chunked]
x-frame-options: ['ALLOW-FROM https://www.google.com/maps/d/embed?mid=zuJ4aNyO7vT4.kxxuYpgxSxwo']
x-powered-by: [Rage4]
status: {code: 200, message: OK}
version: 1
Loading

0 comments on commit aec15f6

Please sign in to comment.