Initial YakPanel commit
This commit is contained in:
9
class/sewer/dns_providers/__init__.py
Normal file
9
class/sewer/dns_providers/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from .common import BaseDns # noqa: F401
|
||||
from .auroradns import AuroraDns # noqa: F401
|
||||
from .cloudflare import CloudFlareDns # noqa: F401
|
||||
from .acmedns import AcmeDnsDns # noqa: F401
|
||||
from .aliyundns import AliyunDns # noqa: F401
|
||||
from .hurricane import HurricaneDns # noqa: F401
|
||||
from .rackspace import RackspaceDns # noqa: F401
|
||||
from .dnspod import DNSPodDns
|
||||
from .duckdns import DuckDNSDns
|
||||
75
class/sewer/dns_providers/acmedns.py
Normal file
75
class/sewer/dns_providers/acmedns.py
Normal file
@@ -0,0 +1,75 @@
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except:
|
||||
import urlparse
|
||||
|
||||
try:
|
||||
acmedns_dependencies = True
|
||||
from dns.resolver import Resolver
|
||||
except ImportError:
|
||||
acmedns_dependencies = False
|
||||
import requests
|
||||
|
||||
from . import common
|
||||
|
||||
|
||||
class AcmeDnsDns(common.BaseDns):
|
||||
"""
|
||||
"""
|
||||
|
||||
dns_provider_name = "acmedns"
|
||||
|
||||
def __init__(self, ACME_DNS_API_USER, ACME_DNS_API_KEY, ACME_DNS_API_BASE_URL):
|
||||
|
||||
if not acmedns_dependencies:
|
||||
raise ImportError(
|
||||
"""You need to install AcmeDnsDns dependencies. run; pip3 install sewer[acmedns]"""
|
||||
)
|
||||
|
||||
self.ACME_DNS_API_USER = ACME_DNS_API_USER
|
||||
self.ACME_DNS_API_KEY = ACME_DNS_API_KEY
|
||||
self.HTTP_TIMEOUT = 65 # seconds
|
||||
|
||||
if ACME_DNS_API_BASE_URL[-1] != "/":
|
||||
self.ACME_DNS_API_BASE_URL = ACME_DNS_API_BASE_URL + "/"
|
||||
else:
|
||||
self.ACME_DNS_API_BASE_URL = ACME_DNS_API_BASE_URL
|
||||
super(AcmeDnsDns, self).__init__()
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("create_dns_record")
|
||||
# if we have been given a wildcard name, strip wildcard
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
|
||||
resolver = Resolver(configure=False)
|
||||
resolver.nameservers = ["8.8.8.8"]
|
||||
answer = resolver.query("_acme-challenge.{0}.".format(domain_name), "TXT")
|
||||
subdomain, _ = str(answer.canonical_name).split(".", 1)
|
||||
|
||||
url = urlparse.urljoin(self.ACME_DNS_API_BASE_URL, "update")
|
||||
headers = {"X-Api-User": self.ACME_DNS_API_USER, "X-Api-Key": self.ACME_DNS_API_KEY}
|
||||
body = {"subdomain": subdomain, "txt": domain_dns_value}
|
||||
update_acmedns_dns_record_response = requests.post(
|
||||
url, headers=headers, json=body, timeout=self.HTTP_TIMEOUT
|
||||
)
|
||||
self.logger.debug(
|
||||
"update_acmedns_dns_record_response. status_code={0}. response={1}".format(
|
||||
update_acmedns_dns_record_response.status_code,
|
||||
self.log_response(update_acmedns_dns_record_response),
|
||||
)
|
||||
)
|
||||
if update_acmedns_dns_record_response.status_code != 200:
|
||||
# raise error so that we do not continue to make calls to ACME
|
||||
# server
|
||||
raise ValueError(
|
||||
"Error creating acme-model model record: status_code={status_code} response={response}".format(
|
||||
status_code=update_acmedns_dns_record_response.status_code,
|
||||
response=self.log_response(update_acmedns_dns_record_response),
|
||||
)
|
||||
)
|
||||
self.logger.info("create_dns_record_end")
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("delete_dns_record")
|
||||
# acme-model doesn't support this
|
||||
self.logger.info("delete_dns_record_success")
|
||||
210
class/sewer/dns_providers/aliyundns.py
Normal file
210
class/sewer/dns_providers/aliyundns.py
Normal file
@@ -0,0 +1,210 @@
|
||||
import json
|
||||
|
||||
try:
|
||||
aliyun_dependencies = True
|
||||
from aliyunsdkcore import client
|
||||
from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest
|
||||
from aliyunsdkalidns.request.v20150109 import AddDomainRecordRequest
|
||||
from aliyunsdkalidns.request.v20150109 import DeleteDomainRecordRequest
|
||||
except ImportError:
|
||||
aliyun_dependencies = False
|
||||
|
||||
from . import common
|
||||
|
||||
|
||||
class _ResponseForAliyun(object):
|
||||
"""
|
||||
wrapper aliyun resp to the format sewer wanted.
|
||||
"""
|
||||
|
||||
def __init__(self, status_code=200, content=None, headers=None):
|
||||
self.status_code = status_code
|
||||
self.headers = headers or {}
|
||||
self.content = content or {}
|
||||
self.content = json.dumps(content)
|
||||
super(_ResponseForAliyun, self).__init__()
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.content)
|
||||
|
||||
|
||||
class AliyunDns(common.BaseDns):
|
||||
def __init__(self, key, secret, endpoint="cn-beijing", debug=False):
|
||||
"""
|
||||
aliyun model client
|
||||
:param str key: access key
|
||||
:param str secret: access sceret
|
||||
:param str endpoint: endpoint
|
||||
:param bool debug: if debug?
|
||||
"""
|
||||
super(AliyunDns, self).__init__()
|
||||
if not aliyun_dependencies:
|
||||
raise ImportError(
|
||||
"""You need to install aliyunDns dependencies. run; pip3 install sewer[aliyun]"""
|
||||
)
|
||||
self._key = key
|
||||
self._secret = secret
|
||||
self._endpoint = endpoint
|
||||
self._debug = debug
|
||||
self.clt = client.AcsClient(self._key, self._secret, self._endpoint, debug=self._debug)
|
||||
|
||||
def _send_reqeust(self, request):
|
||||
"""
|
||||
send request to aliyun
|
||||
"""
|
||||
request.set_accept_format("json")
|
||||
try:
|
||||
status, headers, result = self.clt.implementation_of_do_action(request)
|
||||
result = json.loads(result)
|
||||
if "Message" in result or "Code" in result:
|
||||
result["Success"] = False
|
||||
self.logger.warning("aliyundns resp error: %s", result)
|
||||
except Exception as exc:
|
||||
self.logger.warning("aliyundns failed to send request: %s, %s", str(exc), request)
|
||||
status, headers, result = 502, {}, '{"Success": false}'
|
||||
result = json.loads(result)
|
||||
|
||||
if self._debug:
|
||||
self.logger.info("aliyundns request name: %s", request.__class__.__name__)
|
||||
self.logger.info("aliyundns request query: %s", request.get_query_params())
|
||||
return _ResponseForAliyun(status, result, headers)
|
||||
|
||||
def query_recored_items(self, host, zone=None, tipe=None, page=1, psize=200):
|
||||
"""
|
||||
query recored items.
|
||||
:param str host: like example.com
|
||||
:param str zone: like menduo.example.com
|
||||
:param str tipe: TXT, CNAME, IP or other
|
||||
:param int page:
|
||||
:param int psize:
|
||||
:return dict: res = {
|
||||
'DomainRecords':
|
||||
{'Record': [
|
||||
{
|
||||
'DomainName': 'menduo.net',
|
||||
'Line': 'default',
|
||||
'Locked': False,
|
||||
'RR': 'zb',
|
||||
'RecordId': '3989515483698964',
|
||||
'Status': 'ENABLE',
|
||||
'TTL': 600,
|
||||
'Type': 'A',
|
||||
'Value': '127.0.0.1',
|
||||
'Weight': 1
|
||||
},
|
||||
{
|
||||
'DomainName': 'menduo.net',
|
||||
'Line': 'default',
|
||||
'Locked': False,
|
||||
'RR': 'a.sub',
|
||||
'RecordId': '3989515480778964',
|
||||
'Status': 'ENABLE',
|
||||
'TTL': 600,
|
||||
'Type': 'CNAME',
|
||||
'Value': 'h.p.menduo.net',
|
||||
'Weight': 1
|
||||
}
|
||||
]
|
||||
},
|
||||
'PageNumber': 1,
|
||||
'PageSize': 20,
|
||||
'RequestId': 'FC4D02CD-EDCC-4EE8-942F-1497CCC3B10E',
|
||||
'TotalCount': 95
|
||||
}
|
||||
"""
|
||||
request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
|
||||
request.get_action_name()
|
||||
request.set_DomainName(host)
|
||||
request.set_PageNumber(page)
|
||||
request.set_PageSize(psize)
|
||||
if zone:
|
||||
request.set_RRKeyWord(zone)
|
||||
if tipe:
|
||||
request.set_TypeKeyWord(tipe)
|
||||
resp = self._send_reqeust(request)
|
||||
body = resp.json()
|
||||
return body
|
||||
|
||||
def query_recored_id(self, root, zone, tipe="TXT"):
|
||||
"""
|
||||
find recored
|
||||
:param str root: root host, like example.com
|
||||
:param str zone: sub zone, like menduo.example.com
|
||||
:param str tipe: record tipe, TXT, CNAME, IP. we use TXT
|
||||
:return str:
|
||||
"""
|
||||
record_id = None
|
||||
recoreds = self.query_recored_items(root, zone, tipe=tipe)
|
||||
recored_list = recoreds.get("DomainRecords", {}).get("Record", [])
|
||||
recored_item_list = [i for i in recored_list if i["RR"] == zone]
|
||||
if len(recored_item_list):
|
||||
record_id = recored_item_list[0]["RecordId"]
|
||||
return record_id
|
||||
|
||||
@staticmethod
|
||||
def extract_zone(domain_name):
|
||||
"""
|
||||
extract domain to root, sub, acme_txt
|
||||
:param str domain_name: the value sewer client passed in, like *.menduo.example.com
|
||||
:return tuple: root, zone, acme_txt
|
||||
"""
|
||||
# if we have been given a wildcard name, strip wildcard
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
if domain_name.count(".") > 1:
|
||||
zone, middle, last = str(domain_name).rsplit(".", 2)
|
||||
root = ".".join([middle, last])
|
||||
acme_txt = "_acme-challenge.%s" % zone
|
||||
else:
|
||||
zone = ""
|
||||
root = domain_name
|
||||
acme_txt = "_acme-challenge"
|
||||
return root, zone, acme_txt
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
"""
|
||||
create a model record
|
||||
:param str domain_name: the value sewer client passed in, like *.menduo.example.com
|
||||
:param str domain_dns_value: the value sewer client passed in.
|
||||
:return _ResponseForAliyun:
|
||||
"""
|
||||
self.logger.info("create_dns_record start: %s", (domain_name, domain_dns_value))
|
||||
root, _, acme_txt = self.extract_zone(domain_name)
|
||||
|
||||
request = AddDomainRecordRequest.AddDomainRecordRequest()
|
||||
request.set_DomainName(root)
|
||||
request.set_TTL(600)
|
||||
request.set_RR(acme_txt)
|
||||
request.set_Type("TXT")
|
||||
request.set_Value(domain_dns_value)
|
||||
resp = self._send_reqeust(request)
|
||||
|
||||
self.logger.info("create_dns_record end: %s", (domain_name, domain_dns_value, resp.json()))
|
||||
|
||||
return resp
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
"""
|
||||
delete a txt record we created just now.
|
||||
:param str domain_name: the value sewer client passed in, like *.menduo.example.com
|
||||
:param str domain_dns_value: the value sewer client passed in. we do not use this.
|
||||
:return _ResponseForAliyun:
|
||||
:return:
|
||||
"""
|
||||
self.logger.info("delete_dns_record start: %s", (domain_name, domain_dns_value))
|
||||
|
||||
root, _, acme_txt = self.extract_zone(domain_name)
|
||||
|
||||
record_id = self.query_recored_id(root, acme_txt)
|
||||
if not record_id:
|
||||
msg = "failed to find record_id of domain: %s, value: %s", domain_name, domain_dns_value
|
||||
self.logger.warning(msg)
|
||||
return
|
||||
|
||||
self.logger.info("start to delete model record, id: %s", record_id)
|
||||
|
||||
request = DeleteDomainRecordRequest.DeleteDomainRecordRequest()
|
||||
request.set_RecordId(record_id)
|
||||
resp = self._send_reqeust(request)
|
||||
|
||||
self.logger.info("delete_dns_record end: %s", (domain_name, domain_dns_value, resp.json()))
|
||||
return resp
|
||||
100
class/sewer/dns_providers/auroradns.py
Normal file
100
class/sewer/dns_providers/auroradns.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# DNS Provider for AuroRa DNS from the dutch hosting provider pcextreme
|
||||
# https://www.pcextreme.nl/aurora/dns
|
||||
# Aurora uses libcloud from apache
|
||||
# https://libcloud.apache.org/
|
||||
try:
|
||||
aurora_dependencies = True
|
||||
from libcloud.dns.providers import get_driver
|
||||
from libcloud.dns.types import Provider, RecordType
|
||||
import tldextract
|
||||
except ImportError:
|
||||
aurora_dependencies = False
|
||||
from . import common
|
||||
|
||||
|
||||
class AuroraDns(common.BaseDns):
|
||||
"""
|
||||
Todo: re-organize this class so that we make it easier to mock things out to
|
||||
facilitate better tests.
|
||||
"""
|
||||
|
||||
dns_provider_name = "aurora"
|
||||
|
||||
def __init__(self, AURORA_API_KEY, AURORA_SECRET_KEY):
|
||||
|
||||
if not aurora_dependencies:
|
||||
raise ImportError(
|
||||
"""You need to install AuroraDns dependencies. run; pip3 install sewer[aurora]"""
|
||||
)
|
||||
|
||||
self.AURORA_API_KEY = AURORA_API_KEY
|
||||
self.AURORA_SECRET_KEY = AURORA_SECRET_KEY
|
||||
super(AuroraDns, self).__init__()
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("create_dns_record")
|
||||
# if we have been given a wildcard name, strip wildcard
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
|
||||
extractedDomain = tldextract.extract(domain_name)
|
||||
domainSuffix = extractedDomain.domain + "." + extractedDomain.suffix
|
||||
|
||||
if extractedDomain.subdomain is "":
|
||||
subDomain = "_acme-challenge"
|
||||
else:
|
||||
subDomain = "_acme-challenge." + extractedDomain.subdomain
|
||||
|
||||
cls = get_driver(Provider.AURORADNS)
|
||||
driver = cls(key=self.AURORA_API_KEY, secret=self.AURORA_SECRET_KEY)
|
||||
zone = driver.get_zone(domainSuffix)
|
||||
zone.create_record(name=subDomain, type=RecordType.TXT, data=domain_dns_value)
|
||||
|
||||
self.logger.info("create_dns_record_success")
|
||||
return
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("delete_dns_record")
|
||||
|
||||
extractedDomain = tldextract.extract(domain_name)
|
||||
domainSuffix = extractedDomain.domain + "." + extractedDomain.suffix
|
||||
|
||||
if extractedDomain.subdomain is "":
|
||||
subDomain = "_acme-challenge"
|
||||
else:
|
||||
subDomain = "_acme-challenge." + extractedDomain.subdomain
|
||||
|
||||
cls = get_driver(Provider.AURORADNS)
|
||||
driver = cls(key=self.AURORA_API_KEY, secret=self.AURORA_SECRET_KEY)
|
||||
zone = driver.get_zone(domainSuffix)
|
||||
|
||||
records = driver.list_records(zone)
|
||||
for x in records:
|
||||
if x.name == subDomain and x.type == "TXT":
|
||||
record_id = x.id
|
||||
self.logger.info(
|
||||
"Found record "
|
||||
+ subDomain
|
||||
+ "."
|
||||
+ domainSuffix
|
||||
+ " with id : "
|
||||
+ record_id
|
||||
+ "."
|
||||
)
|
||||
record = driver.get_record(zone_id=zone.id, record_id=record_id)
|
||||
driver.delete_record(record)
|
||||
self.logger.info(
|
||||
"Deleted record "
|
||||
+ subDomain
|
||||
+ "."
|
||||
+ domainSuffix
|
||||
+ " with id : "
|
||||
+ record_id
|
||||
+ "."
|
||||
)
|
||||
else:
|
||||
self.logger.info(
|
||||
"Record " + subDomain + "." + domainSuffix + " not found. No record to delete."
|
||||
)
|
||||
|
||||
self.logger.info("delete_dns_record_success")
|
||||
return
|
||||
155
class/sewer/dns_providers/cloudflare.py
Normal file
155
class/sewer/dns_providers/cloudflare.py
Normal file
@@ -0,0 +1,155 @@
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except:
|
||||
import urlparse
|
||||
|
||||
import requests
|
||||
|
||||
from . import common
|
||||
|
||||
|
||||
class CloudFlareDns(common.BaseDns):
|
||||
"""
|
||||
"""
|
||||
|
||||
dns_provider_name = "cloudflare"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
CLOUDFLARE_EMAIL,
|
||||
CLOUDFLARE_API_KEY,
|
||||
CLOUDFLARE_API_BASE_URL="https://api.cloudflare.com/client/v4/",
|
||||
):
|
||||
self.CLOUDFLARE_DNS_ZONE_ID = None
|
||||
self.CLOUDFLARE_EMAIL = CLOUDFLARE_EMAIL
|
||||
self.CLOUDFLARE_API_KEY = CLOUDFLARE_API_KEY
|
||||
self.CLOUDFLARE_API_BASE_URL = CLOUDFLARE_API_BASE_URL
|
||||
self.HTTP_TIMEOUT = 65 # seconds
|
||||
|
||||
if CLOUDFLARE_API_BASE_URL[-1] != "/":
|
||||
self.CLOUDFLARE_API_BASE_URL = CLOUDFLARE_API_BASE_URL + "/"
|
||||
else:
|
||||
self.CLOUDFLARE_API_BASE_URL = CLOUDFLARE_API_BASE_URL
|
||||
super(CloudFlareDns, self).__init__()
|
||||
|
||||
def find_dns_zone(self, domain_name):
|
||||
self.logger.debug("find_dns_zone")
|
||||
url = urlparse.urljoin(self.CLOUDFLARE_API_BASE_URL, "zones?status=active")
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
find_dns_zone_response = requests.get(url, headers=headers, timeout=self.HTTP_TIMEOUT)
|
||||
self.logger.debug(
|
||||
"find_dns_zone_response. status_code={0}".format(find_dns_zone_response.status_code)
|
||||
)
|
||||
if find_dns_zone_response.status_code != 200:
|
||||
raise ValueError(
|
||||
"Error creating cloudflare model record: status_code={status_code} response={response}".format(
|
||||
status_code=find_dns_zone_response.status_code,
|
||||
response=self.log_response(find_dns_zone_response),
|
||||
)
|
||||
)
|
||||
|
||||
result = find_dns_zone_response.json()["result"]
|
||||
for i in result:
|
||||
if i["name"] in domain_name:
|
||||
setattr(self, "CLOUDFLARE_DNS_ZONE_ID", i["id"])
|
||||
if isinstance(self.CLOUDFLARE_DNS_ZONE_ID, type(None)):
|
||||
raise ValueError(
|
||||
"Error unable to get DNS zone for domain_name={domain_name}: status_code={status_code} response={response}".format(
|
||||
domain_name=domain_name,
|
||||
status_code=find_dns_zone_response.status_code,
|
||||
response=self.log_response(find_dns_zone_response),
|
||||
)
|
||||
)
|
||||
|
||||
self.logger.debug("find_dns_zone_success")
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("create_dns_record")
|
||||
# if we have been given a wildcard name, strip wildcard
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
self.find_dns_zone(domain_name)
|
||||
|
||||
url = urllib.parse.urljoin(
|
||||
self.CLOUDFLARE_API_BASE_URL,
|
||||
"zones/{0}/dns_records".format(self.CLOUDFLARE_DNS_ZONE_ID),
|
||||
)
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
body = {
|
||||
"type": "TXT",
|
||||
"name": "_acme-challenge" + "." + domain_name + ".",
|
||||
"content": "{0}".format(domain_dns_value),
|
||||
}
|
||||
create_cloudflare_dns_record_response = requests.post(
|
||||
url, headers=headers, json=body, timeout=self.HTTP_TIMEOUT
|
||||
)
|
||||
self.logger.debug(
|
||||
"create_cloudflare_dns_record_response. status_code={0}. response={1}".format(
|
||||
create_cloudflare_dns_record_response.status_code,
|
||||
self.log_response(create_cloudflare_dns_record_response),
|
||||
)
|
||||
)
|
||||
if create_cloudflare_dns_record_response.status_code != 200:
|
||||
# raise error so that we do not continue to make calls to ACME
|
||||
# server
|
||||
raise ValueError(
|
||||
"Error creating cloudflare model record: status_code={status_code} response={response}".format(
|
||||
status_code=create_cloudflare_dns_record_response.status_code,
|
||||
response=self.log_response(create_cloudflare_dns_record_response),
|
||||
)
|
||||
)
|
||||
self.logger.info("create_dns_record_end")
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("delete_dns_record")
|
||||
|
||||
class MockResponse(object):
|
||||
def __init__(self, status_code=200, content="mock-response"):
|
||||
self.status_code = status_code
|
||||
self.content = content
|
||||
super(MockResponse, self).__init__()
|
||||
|
||||
def json(self):
|
||||
return {}
|
||||
|
||||
delete_dns_record_response = MockResponse()
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
|
||||
dns_name = "_acme-challenge" + "." + domain_name
|
||||
list_dns_payload = {"type": "TXT", "name": dns_name}
|
||||
list_dns_url = urllib.parse.urljoin(
|
||||
self.CLOUDFLARE_API_BASE_URL,
|
||||
"zones/{0}/dns_records".format(self.CLOUDFLARE_DNS_ZONE_ID),
|
||||
)
|
||||
|
||||
list_dns_response = requests.get(
|
||||
list_dns_url, params=list_dns_payload, headers=headers, timeout=self.HTTP_TIMEOUT
|
||||
)
|
||||
|
||||
for i in range(0, len(list_dns_response.json()["result"])):
|
||||
dns_record_id = list_dns_response.json()["result"][i]["id"]
|
||||
url = urllib.parse.urljoin(
|
||||
self.CLOUDFLARE_API_BASE_URL,
|
||||
"zones/{0}/dns_records/{1}".format(self.CLOUDFLARE_DNS_ZONE_ID, dns_record_id),
|
||||
)
|
||||
headers = {"X-Auth-Email": self.CLOUDFLARE_EMAIL, "X-Auth-Key": self.CLOUDFLARE_API_KEY}
|
||||
delete_dns_record_response = requests.delete(
|
||||
url, headers=headers, timeout=self.HTTP_TIMEOUT
|
||||
)
|
||||
self.logger.debug(
|
||||
"delete_dns_record_response. status_code={0}. response={1}".format(
|
||||
delete_dns_record_response.status_code,
|
||||
self.log_response(delete_dns_record_response),
|
||||
)
|
||||
)
|
||||
if delete_dns_record_response.status_code != 200:
|
||||
# extended logging for debugging
|
||||
# we do not need to raise exception
|
||||
self.logger.error(
|
||||
"delete_dns_record_response. status_code={0}. response={1}".format(
|
||||
delete_dns_record_response.status_code,
|
||||
self.log_response(delete_dns_record_response),
|
||||
)
|
||||
)
|
||||
|
||||
self.logger.info("delete_dns_record_success")
|
||||
|
||||
77
class/sewer/dns_providers/common.py
Normal file
77
class/sewer/dns_providers/common.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import logging
|
||||
|
||||
|
||||
class BaseDns(object):
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, LOG_LEVEL="INFO"):
|
||||
self.LOG_LEVEL = LOG_LEVEL
|
||||
self.dns_provider_name = self.__class__.__name__
|
||||
|
||||
self.logger = logging.getLogger("sewer")
|
||||
handler = logging.StreamHandler()
|
||||
formatter = logging.Formatter("%(message)s")
|
||||
handler.setFormatter(formatter)
|
||||
if not self.logger.handlers:
|
||||
self.logger.addHandler(handler)
|
||||
self.logger.setLevel(self.LOG_LEVEL)
|
||||
|
||||
def log_response(self, response):
|
||||
"""
|
||||
renders a python-requests response as json or as a string
|
||||
"""
|
||||
try:
|
||||
log_body = response.json()
|
||||
except ValueError:
|
||||
log_body = response.content
|
||||
return log_body
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
"""
|
||||
Method that creates/adds a model TXT record for a domain/subdomain name on
|
||||
a chosen DNS provider.
|
||||
|
||||
:param domain_name: :string: The domain/subdomain name whose model record ought to be
|
||||
created/added on a chosen DNS provider.
|
||||
:param domain_dns_value: :string: The value/content of the TXT record that will be
|
||||
created/added for the given domain/subdomain
|
||||
|
||||
This method should return None
|
||||
|
||||
Basic Usage:
|
||||
If the value of the `domain_name` variable is example.com and the value of
|
||||
`domain_dns_value` is HAJA_4MkowIFByHhFaP8u035skaM91lTKplKld
|
||||
Then, your implementation of this method ought to create a DNS TXT record
|
||||
whose name is '_acme-challenge' + '.' + domain_name + '.' (ie: _acme-challenge.example.com. )
|
||||
and whose value/content is HAJA_4MkowIFByHhFaP8u035skaM91lTKplKld
|
||||
|
||||
Using a model client like dig(https://linux.die.net/man/1/dig) to do a model lookup should result
|
||||
in something like:
|
||||
dig TXT _acme-challenge.example.com
|
||||
...
|
||||
;; ANSWER SECTION:
|
||||
_acme-challenge.example.com. 120 IN TXT "HAJA_4MkowIFByHhFaP8u035skaM91lTKplKld"
|
||||
_acme-challenge.singularity.brandur.org. 120 IN TXT "9C0DqKC_4MkowIFByHhFaP8u0Zv4z7Wz2IHM91lTKec"
|
||||
Optionally, you may also use an online model client like: https://toolbox.googleapps.com/apps/dig/#TXT/
|
||||
|
||||
Please consult your model provider on how/format of their DNS TXT records.
|
||||
You may also want to consult the cloudflare DNS implementation that is found in this repository.
|
||||
"""
|
||||
self.logger.info("create_dns_record")
|
||||
raise NotImplementedError("create_dns_record method must be implemented.")
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
"""
|
||||
Method that deletes/removes a model TXT record for a domain/subdomain name on
|
||||
a chosen DNS provider.
|
||||
|
||||
:param domain_name: :string: The domain/subdomain name whose model record ought to be
|
||||
deleted/removed on a chosen DNS provider.
|
||||
:param domain_dns_value: :string: The value/content of the TXT record that will be
|
||||
deleted/removed for the given domain/subdomain
|
||||
|
||||
This method should return None
|
||||
"""
|
||||
self.logger.info("delete_dns_record")
|
||||
raise NotImplementedError("delete_dns_record method must be implemented.")
|
||||
121
class/sewer/dns_providers/dnspod.py
Normal file
121
class/sewer/dns_providers/dnspod.py
Normal file
@@ -0,0 +1,121 @@
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except:
|
||||
import urlparse
|
||||
|
||||
import requests
|
||||
|
||||
from . import common
|
||||
|
||||
|
||||
class DNSPodDns(common.BaseDns):
|
||||
"""
|
||||
"""
|
||||
|
||||
dns_provider_name = "dnspod"
|
||||
|
||||
def __init__(self, DNSPOD_ID, DNSPOD_API_KEY, DNSPOD_API_BASE_URL="https://dnsapi.cn/"):
|
||||
self.DNSPOD_ID = DNSPOD_ID
|
||||
self.DNSPOD_API_KEY = DNSPOD_API_KEY
|
||||
self.DNSPOD_API_BASE_URL = DNSPOD_API_BASE_URL
|
||||
self.HTTP_TIMEOUT = 65 # seconds
|
||||
self.DNSPOD_LOGIN = "{0},{1}".format(self.DNSPOD_ID, self.DNSPOD_API_KEY)
|
||||
|
||||
if DNSPOD_API_BASE_URL[-1] != "/":
|
||||
self.DNSPOD_API_BASE_URL = DNSPOD_API_BASE_URL + "/"
|
||||
else:
|
||||
self.DNSPOD_API_BASE_URL = DNSPOD_API_BASE_URL
|
||||
super(DNSPodDns, self).__init__()
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("create_dns_record")
|
||||
# if we have been given a wildcard name, strip wildcard
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
subd = ""
|
||||
if domain_name.count(".") != 1: # not top level domain
|
||||
pos = domain_name.rfind(".", 0, domain_name.rfind("."))
|
||||
subd = domain_name[:pos]
|
||||
domain_name = domain_name[pos + 1 :]
|
||||
if subd != "":
|
||||
subd = "." + subd
|
||||
|
||||
url = urlparse.urljoin(self.DNSPOD_API_BASE_URL, "Record.Create")
|
||||
body = {
|
||||
"record_type": "TXT",
|
||||
"domain": domain_name,
|
||||
"sub_domain": "_acme-challenge" + subd,
|
||||
"value": domain_dns_value,
|
||||
"record_line_id": "0",
|
||||
"format": "json",
|
||||
"login_token": self.DNSPOD_LOGIN,
|
||||
}
|
||||
create_dnspod_dns_record_response = requests.post(
|
||||
url, data=body, timeout=self.HTTP_TIMEOUT
|
||||
).json()
|
||||
self.logger.debug(
|
||||
"create_dnspod_dns_record_response. status_code={0}. response={1}".format(
|
||||
create_dnspod_dns_record_response["status"]["code"],
|
||||
create_dnspod_dns_record_response["status"]["message"],
|
||||
)
|
||||
)
|
||||
if create_dnspod_dns_record_response["status"]["code"] != "1":
|
||||
# raise error so that we do not continue to make calls to ACME
|
||||
# server
|
||||
raise ValueError(
|
||||
"Error creating dnspod model record: status_code={status_code} response={response}".format(
|
||||
status_code=create_dnspod_dns_record_response["status"]["code"],
|
||||
response=create_dnspod_dns_record_response["status"]["message"],
|
||||
)
|
||||
)
|
||||
self.logger.info("create_dns_record_end")
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("delete_dns_record")
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
subd = ""
|
||||
if domain_name.count(".") != 1: # not top level domain
|
||||
pos = domain_name.rfind(".", 0, domain_name.rfind("."))
|
||||
subd = domain_name[:pos]
|
||||
domain_name = domain_name[pos + 1 :]
|
||||
if subd != "":
|
||||
subd = "." + subd
|
||||
|
||||
url = urllib.parse.urljoin(self.DNSPOD_API_BASE_URL, "Record.List")
|
||||
# pos = domain_name.rfind(".",0, domain_name.rfind("."))
|
||||
subdomain = "_acme-challenge." + subd
|
||||
rootdomain = domain_name
|
||||
body = {
|
||||
"login_token": self.DNSPOD_LOGIN,
|
||||
"format": "json",
|
||||
"domain": rootdomain,
|
||||
"subdomain": subdomain,
|
||||
"record_type": "TXT",
|
||||
}
|
||||
list_dns_response = requests.post(url, data=body, timeout=self.HTTP_TIMEOUT).json()
|
||||
if list_dns_response["status"]["code"] != "1":
|
||||
self.logger.error(
|
||||
"list_dns_record_response. status_code={0}. message={1}".format(
|
||||
list_dns_response["status"]["code"], list_dns_response["status"]["message"]
|
||||
)
|
||||
)
|
||||
for i in range(0, len(list_dns_response["records"])):
|
||||
rid = list_dns_response["records"][i]["id"]
|
||||
urlr = urllib.parse.urljoin(self.DNSPOD_API_BASE_URL, "Record.Remove")
|
||||
bodyr = {
|
||||
"login_token": self.DNSPOD_LOGIN,
|
||||
"format": "json",
|
||||
"domain": rootdomain,
|
||||
"record_id": rid,
|
||||
}
|
||||
delete_dns_record_response = requests.post(
|
||||
urlr, data=bodyr, timeout=self.HTTP_TIMEOUT
|
||||
).json()
|
||||
if delete_dns_record_response["status"]["code"] != "1":
|
||||
self.logger.error(
|
||||
"delete_dns_record_response. status_code={0}. message={1}".format(
|
||||
delete_dns_record_response["status"]["code"],
|
||||
delete_dns_record_response["status"]["message"],
|
||||
)
|
||||
)
|
||||
|
||||
self.logger.info("delete_dns_record_success")
|
||||
63
class/sewer/dns_providers/duckdns.py
Normal file
63
class/sewer/dns_providers/duckdns.py
Normal file
@@ -0,0 +1,63 @@
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except:
|
||||
import urlparse
|
||||
import requests
|
||||
|
||||
from . import common
|
||||
|
||||
|
||||
class DuckDNSDns(common.BaseDns):
|
||||
|
||||
dns_provider_name = "duckdns"
|
||||
|
||||
def __init__(self, duckdns_token, DUCKDNS_API_BASE_URL="https://www.duckdns.org"):
|
||||
|
||||
self.duckdns_token = duckdns_token
|
||||
self.HTTP_TIMEOUT = 65 # seconds
|
||||
|
||||
if DUCKDNS_API_BASE_URL[-1] != "/":
|
||||
self.DUCKDNS_API_BASE_URL = DUCKDNS_API_BASE_URL + "/"
|
||||
else:
|
||||
self.DUCKDNS_API_BASE_URL = DUCKDNS_API_BASE_URL
|
||||
super(DuckDNSDns, self).__init__()
|
||||
|
||||
def _common_dns_record(self, logger_info, domain_name, payload_end_arg):
|
||||
self.logger.info("{0}".format(logger_info))
|
||||
# if we have been given a wildcard name, strip wildcard
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
# add provider domain to the domain name if not present
|
||||
provider_domain = ".duckdns.org"
|
||||
if domain_name.rfind(provider_domain) == -1:
|
||||
"".join((domain_name, provider_domain))
|
||||
|
||||
url = urlparse.urljoin(self.DUCKDNS_API_BASE_URL, "update")
|
||||
|
||||
payload = dict([("domains", domain_name), ("token", self.duckdns_token), payload_end_arg])
|
||||
update_duckdns_dns_record_response = requests.get(
|
||||
url, params=payload, timeout=self.HTTP_TIMEOUT
|
||||
)
|
||||
|
||||
normalized_response = update_duckdns_dns_record_response.text
|
||||
self.logger.debug(
|
||||
"update_duckdns_dns_record_response. status_code={0}. response={1}".format(
|
||||
update_duckdns_dns_record_response.status_code, normalized_response
|
||||
)
|
||||
)
|
||||
|
||||
if update_duckdns_dns_record_response.status_code != 200 or normalized_response != "OK":
|
||||
# raise error so that we do not continue to make calls to DuckDNS
|
||||
# server
|
||||
raise ValueError(
|
||||
"Error creating DuckDNS model record: status_code={status_code} response={response}".format(
|
||||
status_code=update_duckdns_dns_record_response.status_code,
|
||||
response=normalized_response,
|
||||
)
|
||||
)
|
||||
self.logger.info("{0}_success".format(logger_info))
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
self._common_dns_record("create_dns_record", domain_name, ("txt", domain_dns_value))
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
self._common_dns_record("delete_dns_record", domain_name, ("clear", "true"))
|
||||
79
class/sewer/dns_providers/hurricane.py
Normal file
79
class/sewer/dns_providers/hurricane.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Hurricane Electric DNS Support
|
||||
"""
|
||||
import json
|
||||
|
||||
try:
|
||||
hedns_dependencies = True
|
||||
import HurricaneDNS as _hurricanedns
|
||||
except ImportError:
|
||||
hedns_dependencies = False
|
||||
|
||||
from . import common
|
||||
|
||||
|
||||
class _Response(object):
|
||||
"""
|
||||
wrapper aliyun resp to the format sewer wanted.
|
||||
"""
|
||||
|
||||
def __init__(self, status_code=200, content=None, headers=None):
|
||||
self.status_code = status_code
|
||||
self.headers = headers or {}
|
||||
self.content = content or {}
|
||||
self.content = json.dumps(content)
|
||||
super(_Response, self).__init__()
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.content)
|
||||
|
||||
|
||||
class HurricaneDns(common.BaseDns):
|
||||
def __init__(self, username, password):
|
||||
super(HurricaneDns, self).__init__()
|
||||
if not hedns_dependencies:
|
||||
raise ImportError(
|
||||
"""You need to install HurricaneDns dependencies. run: pip3 install sewer[hurricane]"""
|
||||
)
|
||||
|
||||
self.clt = _hurricanedns.HurricaneDNS(username, password)
|
||||
|
||||
@staticmethod
|
||||
def extract_zone(domain_name):
|
||||
"""
|
||||
extract domain to root, sub, acme_txt
|
||||
:param str domain_name: the value sewer client passed in, like *.menduo.example.com
|
||||
:return tuple: root, zone, acme_txt
|
||||
"""
|
||||
# if we have been given a wildcard name, strip wildcard
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
if domain_name.count(".") > 1:
|
||||
zone, middle, last = str(domain_name).rsplit(".", 2)
|
||||
root = ".".join([middle, last])
|
||||
acme_txt = "_acme-challenge.%s" % zone
|
||||
else:
|
||||
zone = ""
|
||||
root = domain_name
|
||||
acme_txt = "_acme-challenge"
|
||||
return root, zone, acme_txt
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("create_dns_record start: %s", (domain_name, domain_dns_value))
|
||||
|
||||
root, _, acme_txt = self.extract_zone(domain_name)
|
||||
self.clt.add_record(root, acme_txt, "TXT", domain_dns_value, ttl=300)
|
||||
|
||||
self.logger.info("create_dns_record end: %s", (domain_name, domain_dns_value))
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("delete_dns_record start: %s", (domain_name, domain_dns_value))
|
||||
|
||||
root, _, acme_txt = self.extract_zone(domain_name)
|
||||
host = "%s.%s" % (acme_txt, root)
|
||||
|
||||
recored_list = self.clt.get_records(root, host, "TXT")
|
||||
|
||||
for i in recored_list:
|
||||
self.clt.del_record(root, i["id"])
|
||||
|
||||
self.logger.info("delete_dns_record end: %s", (domain_name, domain_dns_value))
|
||||
242
class/sewer/dns_providers/rackspace.py
Normal file
242
class/sewer/dns_providers/rackspace.py
Normal file
@@ -0,0 +1,242 @@
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except:
|
||||
import urlparse
|
||||
import requests
|
||||
from . import common
|
||||
|
||||
try:
|
||||
rackspace_dependencies = True
|
||||
import tldextract
|
||||
except ImportError:
|
||||
rackspace_dependencies = False
|
||||
|
||||
import time
|
||||
|
||||
|
||||
class RackspaceDns(common.BaseDns):
|
||||
"""
|
||||
"""
|
||||
|
||||
dns_providername = "rackspace"
|
||||
|
||||
def get_rackspace_credentials(self):
|
||||
self.logger.debug("get_rackspace_credentials")
|
||||
RACKSPACE_IDENTITY_URL = "https://identity.api.rackspacecloud.com/v2.0/tokens"
|
||||
payload = {
|
||||
"auth": {
|
||||
"RAX-KSKEY:apiKeyCredentials": {
|
||||
"username": self.RACKSPACE_USERNAME,
|
||||
"apiKey": self.RACKSPACE_API_KEY,
|
||||
}
|
||||
}
|
||||
}
|
||||
find_rackspace_api_details_response = requests.post(RACKSPACE_IDENTITY_URL, json=payload)
|
||||
self.logger.debug(
|
||||
"find_rackspace_api_details_response. status_code={0}".format(
|
||||
find_rackspace_api_details_response.status_code
|
||||
)
|
||||
)
|
||||
if find_rackspace_api_details_response.status_code != 200:
|
||||
raise ValueError(
|
||||
"Error getting token and URL details from rackspace identity server: status_code={status_code} response={response}".format(
|
||||
status_code=find_rackspace_api_details_response.status_code,
|
||||
response=self.log_response(find_rackspace_api_details_response),
|
||||
)
|
||||
)
|
||||
data = find_rackspace_api_details_response.json()
|
||||
api_token = data["access"]["token"]["id"]
|
||||
url_data = next(
|
||||
(item for item in data["access"]["serviceCatalog"] if item["type"] == "rax:model"), None
|
||||
)
|
||||
if url_data is None:
|
||||
raise ValueError(
|
||||
"Error finding url data for the rackspace model api in the response from the identity server"
|
||||
)
|
||||
else:
|
||||
api_base_url = url_data["endpoints"][0]["publicURL"] + "/"
|
||||
return (api_token, api_base_url)
|
||||
|
||||
def __init__(self, RACKSPACE_USERNAME, RACKSPACE_API_KEY):
|
||||
|
||||
if not rackspace_dependencies:
|
||||
raise ImportError(
|
||||
"""You need to install RackspaceDns dependencies. run; pip3 install sewer[rackspace]"""
|
||||
)
|
||||
self.RACKSPACE_DNS_ZONE_ID = None
|
||||
self.RACKSPACE_USERNAME = RACKSPACE_USERNAME
|
||||
self.RACKSPACE_API_KEY = RACKSPACE_API_KEY
|
||||
self.HTTP_TIMEOUT = 65 # seconds
|
||||
super(RackspaceDns, self).__init__()
|
||||
self.RACKSPACE_API_TOKEN, self.RACKSPACE_API_BASE_URL = self.get_rackspace_credentials()
|
||||
self.RACKSPACE_HEADERS = {
|
||||
"X-Auth-Token": self.RACKSPACE_API_TOKEN,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
def get_dns_zone(self, domain_name):
|
||||
self.logger.debug("get_dns_zone")
|
||||
extracted_domain = tldextract.extract(domain_name)
|
||||
self.RACKSPACE_DNS_ZONE = ".".join([extracted_domain.domain, extracted_domain.suffix])
|
||||
|
||||
def find_dns_zone_id(self, domain_name):
|
||||
self.logger.debug("find_dns_zone_id")
|
||||
self.get_dns_zone(domain_name)
|
||||
url = self.RACKSPACE_API_BASE_URL + "domains"
|
||||
find_dns_zone_id_response = requests.get(url, headers=self.RACKSPACE_HEADERS)
|
||||
self.logger.debug(
|
||||
"find_dns_zone_id_response. status_code={0}".format(
|
||||
find_dns_zone_id_response.status_code
|
||||
)
|
||||
)
|
||||
if find_dns_zone_id_response.status_code != 200:
|
||||
raise ValueError(
|
||||
"Error getting rackspace model domain info: status_code={status_code} response={response}".format(
|
||||
status_code=find_dns_zone_id_response.status_code,
|
||||
response=self.log_response(find_dns_zone_id_response),
|
||||
)
|
||||
)
|
||||
result = find_dns_zone_id_response.json()
|
||||
domain_data = next(
|
||||
(item for item in result["domains"] if item["name"] == self.RACKSPACE_DNS_ZONE), None
|
||||
)
|
||||
if domain_data is None:
|
||||
raise ValueError(
|
||||
"Error finding information for {dns_zone} in model response data:\n{response_data})".format(
|
||||
dns_zone=self.RACKSPACE_DNS_ZONE,
|
||||
response_data=self.log_response(find_dns_zone_id_response),
|
||||
)
|
||||
)
|
||||
dns_zone_id = domain_data["id"]
|
||||
self.logger.debug("find_dns_zone_id_success")
|
||||
return dns_zone_id
|
||||
|
||||
def find_dns_record_id(self, domain_name, domain_dns_value):
|
||||
self.logger.debug("find_dns_record_id")
|
||||
self.RACKSPACE_DNS_ZONE_ID = self.find_dns_zone_id(domain_name)
|
||||
url = self.RACKSPACE_API_BASE_URL + "domains/{0}/records".format(self.RACKSPACE_DNS_ZONE_ID)
|
||||
find_dns_record_id_response = requests.get(url, headers=self.RACKSPACE_HEADERS)
|
||||
self.logger.debug(
|
||||
"find_dns_record_id_response. status_code={0}".format(
|
||||
find_dns_record_id_response.status_code
|
||||
)
|
||||
)
|
||||
self.logger.debug(url)
|
||||
if find_dns_record_id_response.status_code != 200:
|
||||
raise ValueError(
|
||||
"Error finding model records for {dns_zone}: status_code={status_code} response={response}".format(
|
||||
dns_zone=self.RACKSPACE_DNS_ZONE,
|
||||
status_code=find_dns_record_id_response.status_code,
|
||||
response=self.log_response(find_dns_record_id_response),
|
||||
)
|
||||
)
|
||||
records = find_dns_record_id_response.json()["records"]
|
||||
RACKSPACE_RECORD_DATA = next(
|
||||
(item for item in records if item["data"] == domain_dns_value), None
|
||||
)
|
||||
if RACKSPACE_RECORD_DATA is None:
|
||||
raise ValueError(
|
||||
"Couldn't find record with name {domain_name}\ncontaining data: {domain_dns_value}\nin the response data:{response_data}".format(
|
||||
domain_name=domain_name,
|
||||
domain_dns_value=domain_dns_value,
|
||||
response_data=self.log_response(find_dns_record_id_response),
|
||||
)
|
||||
)
|
||||
record_id = RACKSPACE_RECORD_DATA["id"]
|
||||
self.logger.debug("find_dns_record_id success")
|
||||
return record_id
|
||||
|
||||
def poll_callback_url(self, callback_url):
|
||||
start_time = time.time()
|
||||
while True:
|
||||
callback_url_response = requests.get(callback_url, headers=self.RACKSPACE_HEADERS)
|
||||
if time.time() > start_time + self.HTTP_TIMEOUT:
|
||||
raise ValueError(
|
||||
"Timed out polling callbackurl for model record status. Last status_code={status_code} last response={response}".format(
|
||||
status_code=callback_url_response.status_code,
|
||||
response=self.log_response(callback_url_response),
|
||||
)
|
||||
)
|
||||
if callback_url_response.status_code != 200:
|
||||
raise Exception(
|
||||
"Could not get model record status from callback url. Status code ={status_code}. response={response}".format(
|
||||
status_code=callback_url_response.status_code,
|
||||
response=self.log_response(callback_url_response),
|
||||
)
|
||||
)
|
||||
if callback_url_response.json()["status"] == "ERROR":
|
||||
raise Exception(
|
||||
"Error in creating/deleting model record: status_Code={status_code}. response={response}".format(
|
||||
status_code=callback_url_response.status_code,
|
||||
response=self.log_response(callback_url_response),
|
||||
)
|
||||
)
|
||||
if callback_url_response.json()["status"] == "COMPLETED":
|
||||
break
|
||||
|
||||
def create_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("create_dns_record")
|
||||
# strip wildcard if present
|
||||
domain_name = domain_name.lstrip("*.")
|
||||
self.RACKSPACE_DNS_ZONE_ID = self.find_dns_zone_id(domain_name)
|
||||
record_name = "_acme-challenge." + domain_name
|
||||
url = urlparse.urljoin(
|
||||
self.RACKSPACE_API_BASE_URL, "domains/{0}/records".format(self.RACKSPACE_DNS_ZONE_ID)
|
||||
)
|
||||
body = {
|
||||
"records": [{"name": record_name, "type": "TXT", "data": domain_dns_value, "ttl": 3600}]
|
||||
}
|
||||
create_rackspace_dns_record_response = requests.post(
|
||||
url, headers=self.RACKSPACE_HEADERS, json=body, timeout=self.HTTP_TIMEOUT
|
||||
)
|
||||
self.logger.debug(
|
||||
"create_rackspace_dns_record_response. status_code={status_code}".format(
|
||||
status_code=create_rackspace_dns_record_response.status_code
|
||||
)
|
||||
)
|
||||
if create_rackspace_dns_record_response.status_code != 202:
|
||||
raise ValueError(
|
||||
"Error creating rackspace model record: status_code={status_code} response={response}".format(
|
||||
status_code=create_rackspace_dns_record_response.status_code,
|
||||
response=create_rackspace_dns_record_response.text,
|
||||
)
|
||||
)
|
||||
# response=self.log_response(create_rackspace_dns_record_response)))
|
||||
# After posting the model record we want created, the response gives us a url to check that will
|
||||
# update when the job is done
|
||||
callback_url = create_rackspace_dns_record_response.json()["callbackUrl"]
|
||||
self.poll_callback_url(callback_url)
|
||||
self.logger.info(
|
||||
"create_dns_record_success. Name: {record_name} Data: {data}".format(
|
||||
record_name=record_name, data=domain_dns_value
|
||||
)
|
||||
)
|
||||
|
||||
def delete_dns_record(self, domain_name, domain_dns_value):
|
||||
self.logger.info("delete_dns_record")
|
||||
record_name = "_acme-challenge." + domain_name
|
||||
self.RACKSPACE_DNS_ZONE_ID = self.find_dns_zone_id(domain_name)
|
||||
self.RACKSPACE_RECORD_ID = self.find_dns_record_id(domain_name, domain_dns_value)
|
||||
url = self.RACKSPACE_API_BASE_URL + "domains/{domain_id}/records/?id={record_id}".format(
|
||||
domain_id=self.RACKSPACE_DNS_ZONE_ID, record_id=self.RACKSPACE_RECORD_ID
|
||||
)
|
||||
delete_dns_record_response = requests.delete(url, headers=self.RACKSPACE_HEADERS)
|
||||
# After sending a delete request, if all goes well, we get a 202 from the server and a URL that we can poll
|
||||
# to see when the job is done
|
||||
self.logger.debug(
|
||||
"delete_dns_record_response={0}".format(delete_dns_record_response.status_code)
|
||||
)
|
||||
if delete_dns_record_response.status_code != 202:
|
||||
raise ValueError(
|
||||
"Error deleting rackspace model record: status_code={status_code} response={response}".format(
|
||||
status_code=delete_dns_record_response.status_code,
|
||||
response=self.log_response(delete_dns_record_response),
|
||||
)
|
||||
)
|
||||
callback_url = delete_dns_record_response.json()["callbackUrl"]
|
||||
self.poll_callback_url(callback_url)
|
||||
self.logger.info(
|
||||
"delete_dns_record_success. Name: {record_name} Data: {data}".format(
|
||||
record_name=record_name, data=domain_dns_value
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user