211 lines
7.8 KiB
Python
211 lines
7.8 KiB
Python
|
|
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
|