【工具类】阿里域名关联ip(python版)

本文介绍了一个Python脚本,用于操作阿里云DNS服务,获取外网公共IP并将其动态绑定到指定的子域名上,支持添加、更新和删除记录功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

获取代码如下

# 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}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值