获取代码如下
# coding=utf-8
import argparse
import json
import urllib
import logging
# 加载 ali 核心 SDK
from aliyunsdkcore.client import AcsClient
from aliyunsdkalidns.request.v20150109 import (
DescribeSubDomainRecordsRequest,
AddDomainRecordRequest,
UpdateDomainRecordRequest,
DeleteDomainRecordRequest,
)
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s[%(filename)s:%(lineno)d] %(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# get_public_ip 获取外网IP
# 备选url: http://www.3322.org/dyndns/getip 是最纯净的格式
# 备选url1: http://pv.sohu.com/cityjson?ie=utf-8 json格式, 对获取值作进一步处理才能使用
# 备选url2: curl -L tool.lu/ip curl方式获取, 对获取值作进一步处理才能使用
def get_public_ip():
url = "http://www.3322.org/dyndns/getip" # 可考虑将此URL作为配置项,以便于未来更改
try:
with urllib.request.urlopen(url) as response:
html = response.read()
# 使用strip()代替replace("\n", ""),以删除所有前导和尾随空白
ip = str(html, encoding="utf-8").strip()
return ip
except urllib.error.URLError as e:
logger.info(f"Error fetching IP: {e.reason}")
return None
# 查询记录
# {
# "TotalCount": 1,
# "PageSize": 20,
# "RequestId": "DAD4AEC3-3F09-569E-88FF-0732E93CBDDE",
# "DomainRecords": {
# "Record": [
# {
# "Status": "ENABLE",
# "Line": "default",
# "RR": "c",
# "Locked": false,
# "Type": "A",
# "DomainName": "baidu.cn",
# "Value": "127.0.0.1",
# "RecordId": "880481632183704576",
# "TTL": 600,
# "Weight": 1
# }
# ]
# },
# "PageNumber": 1
# }
def get_domain_info(client, sub_domain, domain_name):
request = DescribeSubDomainRecordsRequest.DescribeSubDomainRecordsRequest()
request.set_accept_format("json")
# 记录类型,A表示IP 地址记录,具体参考 https://ephen.me/2016/dns-rr/
request.set_Type("A")
request.set_SubDomain(sub_domain+"."+domain_name)
try:
response = client.do_action_with_exception(request)
response_str = response.decode("utf-8")
records = json.loads(response_str)
return records
except Exception as e:
logger.error(f"An error occurred: {e}")
return None
def _send_req(request, client, value, rr, domainname):
"""
发送DNS记录请求
Args:
request (DNSRequest): DNS请求对象
client (DnsClient): DNS客户端对象
value (str): 新的IP地址
rr (str): 子域名名称
domainname (str): 主域名
Returns:
dict: 包含TotalCount字段的字典,表示获取到的记录条数,0表示没有记录,其他数字表示有多少条相同记录,正常有记录的值应该为1,如果值大于1则应该检查是不是重复添加了相同的记录。
"""
request.set_accept_format("json")
# request.set_Priority('1') # MX 记录时的必选参数
request.set_TTL(
"600"
) # 可选值的范围取决于你的阿里云账户等级,免费版为 600 - 86400 单位为秒
request.set_Value(value) # 新增的 ip 地址
# 记录类型,IP 地址记录,具体参考 https://ephen.me/2016/dns-rr/
request.set_Type("A")
request.set_RR(rr) # 子域名名称
request.set_DomainName(domainname) # 主域名
# 获取记录信息,返回信息中包含 TotalCount 字段,表示获取到的记录条数 0 表示没有记录
# 其他数字为多少表示有多少条相同记录,正常有记录的值应该为1,如果值大于1则应该检查是不是重复添加了相同的记录
response = client.do_action_with_exception(request)
response = str(response, encoding="utf-8")
relsult = json.loads(response)
return relsult
def add_domain_record(client, value, rr, domain_name):
request = AddDomainRecordRequest.AddDomainRecordRequest()
return _send_req(request, client, value, rr, domain_name)
def update_domain_record(client, value, rr, record_id):
request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
return _send_req(request, client, value, rr, record_id)
def del_domain_record(client, sub_domain, domain_name):
try:
info = get_domain_info(client, sub_domain, domain_name)
if info["TotalCount"] == 0:
logger.info(f"{sub_domain}.{domain_name} not exist")
return 0
elif info["TotalCount"] == 1:
record_id = info["DomainRecords"]["Record"][0]["RecordId"]
request = DeleteDomainRecordRequest.DeleteDomainRecordRequest()
request.set_accept_format("json")
request.set_RecordId(record_id)
result = client.do_action_with_exception(request)
logger.info(f"del {sub_domain}.{domain_name} result {result}")
return 0
else:
logger.warning("multiple resource records, please check at https://dns.console.aliyun.com")
return -1
except Exception as e:
logger.error(f"error occurred {sub_domain}.{domain_name} result {e}")
return -2
def set_domain_record(client, value, sub_domain, domain_name):
info = get_domain_info(client, sub_domain, domain_name)
# 检查info是否为空以及其是否包含必要的键
if not info or "TotalCount" not in info:
logger.error("Failed to get domain info or invalid response format.")
return
if info["TotalCount"] == 0:
result = add_domain_record(client, value, sub_domain, domain_name)
logger.info(f"adding {value} for {sub_domain}.{domain_name} result {result}")
elif info["TotalCount"] == 1:
record_info = info.get("DomainRecords", {}).get("Record", [{}])[0]
record_id = record_info.get("RecordId")
old_ip = record_info.get("Value")
if value == old_ip:
logger.info(f"same val {value}, no need to update")
else:
result = update_domain_record(client, value, sub_domain, record_id)
logger.info(f"update {value} for {sub_domain}.{domain_name} result {result}")
else:
logger.warning("multiple resource records, please check at https://dns.console.aliyun.com")
# python alidns.py --key=xxx --secret=xxx --domain=baidu.cn
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.description='please enter correct para'
# AccessKey 和 Secret 建议使用 RAM 子账户的 KEY 和 SECRET 增加安全性
parser.add_argument("-k", "--key", help="accessKey", type=str, default="private accessKey")
parser.add_argument("-s", "--secret", help="secret", type=str, default="private secret")
parser.add_argument("-d", "--domain", help="domain name", type=str, default="baidu.cn")
args = parser.parse_args()
public_ip = get_public_ip()
logger.info(f"public_ip {public_ip}")
# 地区节点 可选地区取决于你的阿里云帐号等级,普通用户只有四个,分别是杭州、上海、深圳、河北,具体参考官网API
regionId = "cn-hangzhou"
client = AcsClient(args.key, args.secret, regionId) # 配置认证信息
domain_name = args.domain # 设置主域名
sub_domains = ["a", "b", "c"] # 子域名列表 列表参数可根据实际需求增加或减少值
# 删除记录测试
for sub_domain in sub_domains:
del_domain_record(client, sub_domain, domain_name)
# 获取记录测试
for sub_domain in sub_domains:
info = get_domain_info(client, sub_domain, domain_name)
print(f"get-info: {json.dumps(info)}")
for sub_domain in sub_domains: # 循环子域名列表进行批量操作
set_domain_record(client, public_ip, sub_domain, domain_name)
# 获取记录测试
for sub_domain in sub_domains:
info = get_domain_info(client, sub_domain, domain_name)
print(f"get-info: {json.dumps(info)}")
python alidns.py --key=xxx --secret=xxx --domain=baidu.cn
2024-02-23 17:46:13,408 INFO[alidns.py:187] public_ip xxx.xxx.xxx.xxx
2024-02-23 17:46:13,890 INFO[alidns.py:142] del a.baidu.cn result b'{"RequestId":"3FC56A68-678C-5039-A905-55151473925A","RecordId":"880481630604579840"}'
2024-02-23 17:46:14,313 INFO[alidns.py:142] del b.baidu.cn result b'{"RequestId":"E2F92A34-130F-5108-A6B6-05139A3666C3","RecordId":"880481631418256384"}'
2024-02-23 17:46:14,714 INFO[alidns.py:142] del c.baidu.cn result b'{"RequestId":"B6ACC80B-0F85-5603-8475-9B0583ED5B95","RecordId":"880481632183704576"}'
get-info: {"TotalCount": 0, "PageSize": 20, "RequestId": "4A21718E-8DD1-5878-AD8E-3EA708C53716", "DomainRecords": {"Record": []}, "PageNumber": 1}
get-info: {"TotalCount": 0, "PageSize": 20, "RequestId": "95405D16-2E81-54C5-B794-D73BEF51F35B", "DomainRecords": {"Record": []}, "PageNumber": 1}
get-info: {"TotalCount": 0, "PageSize": 20, "RequestId": "09C39264-2CA7-5BFF-9250-9AE83242890C", "DomainRecords": {"Record": []}, "PageNumber": 1}
2024-02-23 17:46:15,474 INFO[alidns.py:163] adding xxx.xxx.xxx.xxx for a.baidu.cn result {'RequestId': '11D855D8-1A9A-52C9-B0D4-0F0E5C161453', 'RecordId': '880482138503304192'}
2024-02-23 17:46:15,831 INFO[alidns.py:163] adding xxx.xxx.xxx.xxx for b.baidu.cn result {'RequestId': '93A11426-5992-58A6-9D52-83ACC2310769', 'RecordId': '880482139254086656'}
2024-02-23 17:46:16,188 INFO[alidns.py:163] adding xxx.xxx.xxx.xxx for c.baidu.cn result {'RequestId': 'DC94B74E-074E-5554-BCAC-BEEFD0AC8C30', 'RecordId': '880482140006968320'}
get-info: {"TotalCount": 1, "PageSize": 20, "RequestId": "0D90F3A3-6CD9-5295-80E7-8AAE7D9D2C34", "DomainRecords": {"Record": [{"Status": "ENABLE", "Line": "default", "RR": "a", "Locked": false, "Type": "A", "DomainName": "baidu.cn", "Value": "xxx.xxx.xxx.xxx", "RecordId": "880482138503304192", "TTL": 600, "Weight": 1}]}, "PageNumber": 1}
get-info: {"TotalCount": 1, "PageSize": 20, "RequestId": "0205D91F-123F-57B1-91F9-1C5D82B70911", "DomainRecords": {"Record": [{"Status": "ENABLE", "Line": "default", "RR": "b", "Locked": false, "Type": "A", "DomainName": "baidu.cn", "Value": "xxx.xxx.xxx.xxx", "RecordId": "880482139254086656", "TTL": 600, "Weight": 1}]}, "PageNumber": 1}
get-info: {"TotalCount": 1, "PageSize": 20, "RequestId": "7653F278-088F-5CDE-9ACC-78958FCDF662", "DomainRecords": {"Record": [{"Status": "ENABLE", "Line": "default", "RR": "c", "Locked": false, "Type": "A", "DomainName": "baidu.cn", "Value": "xxx.xxx.xxx.xxx", "RecordId": "880482140006968320", "TTL": 600, "Weight": 1}]}, "PageNumber": 1}