gcx/scripts/alidns_manager.py

219 lines
8.5 KiB
Python

#!/usr/bin/env python3
"""
Alibaba Cloud DNS Record Manager
Supports: list, add, update, delete, enable, disable operations on Alidns records.
Usage:
python alidns_manager.py list --domain example.com [--rr www] [--type A]
python alidns_manager.py add --domain example.com --rr www --type A --value 1.2.3.4 [--ttl 600]
python alidns_manager.py update --record-id 123456 --rr www --type A --value 5.6.7.8 [--ttl 600]
python alidns_manager.py delete --record-id 123456
python alidns_manager.py enable --record-id 123456
python alidns_manager.py disable --record-id 123456
Environment Variables:
ALIBABA_CLOUD_ACCESS_KEY_ID - RAM sub-user Access Key ID
ALIBABA_CLOUD_ACCESS_KEY_SECRET - RAM sub-user Access Key Secret
"""
import argparse
import json
import os
import sys
try:
from alibabacloud_alidns20150109.client import Client as AlidnsClient
from alibabacloud_alidns20150109 import models as alidns_models
from alibabacloud_tea_openapi import models as open_api_models
except ImportError:
print("ERROR: Required packages not installed. Run:")
print(" pip install alibabacloud_alidns20150109 alibabacloud_tea_openapi --break-system-packages")
sys.exit(1)
def create_client(region_id: str = "cn-hangzhou") -> AlidnsClient:
"""Create an authenticated Alidns client."""
access_key_id = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
access_key_secret = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
if not access_key_id or not access_key_secret:
print("ERROR: Set ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET env vars.")
sys.exit(1)
config = open_api_models.Config(
access_key_id=access_key_id,
access_key_secret=access_key_secret,
)
config.endpoint = f"alidns.{region_id}.aliyuncs.com"
return AlidnsClient(config)
def list_records(client: AlidnsClient, domain: str, rr: str = None, record_type: str = None, page_size: int = 100):
"""List all DNS records for a domain, with optional filtering."""
request = alidns_models.DescribeDomainRecordsRequest(
domain_name=domain,
page_size=page_size,
)
if rr:
request.rrkey_word = rr
if record_type:
request.type = record_type
try:
response = client.describe_domain_records(request)
records = response.body.domain_records.record
total = response.body.total_count
print(f"\n📋 DNS Records for {domain} (Total: {total})\n")
print(f"{'RecordId':<20} {'RR':<25} {'Type':<8} {'Value':<40} {'TTL':<6} {'Status'}")
print("-" * 120)
for r in records:
status_icon = "" if r.status == "ENABLE" else "⏸️"
rr_display = f"{r.rr}.{domain}" if r.rr != "@" else domain
print(f"{r.record_id:<20} {rr_display:<25} {r.type:<8} {r.value:<40} {r.ttl:<6} {status_icon} {r.status}")
return records
except Exception as e:
print(f"ERROR listing records: {e}")
sys.exit(1)
def add_record(client: AlidnsClient, domain: str, rr: str, record_type: str, value: str, ttl: int = 600):
"""Add a new DNS record."""
request = alidns_models.AddDomainRecordRequest(
domain_name=domain,
rr=rr,
type=record_type,
value=value,
ttl=ttl,
)
try:
response = client.add_domain_record(request)
record_id = response.body.record_id
fqdn = f"{rr}.{domain}" if rr != "@" else domain
print(f"✅ Added: {fqdn} -> {record_type} {value} (TTL: {ttl}, RecordId: {record_id})")
return record_id
except Exception as e:
error_msg = str(e)
if "DomainRecordDuplicate" in error_msg:
print(f"⚠️ Record already exists: {rr}.{domain} {record_type} {value}")
elif "DomainRecordConflict" in error_msg:
print(f"❌ Conflict: CNAME records cannot coexist with other record types for {rr}.{domain}")
else:
print(f"ERROR adding record: {e}")
return None
def update_record(client: AlidnsClient, record_id: str, rr: str, record_type: str, value: str, ttl: int = 600):
"""Update an existing DNS record by RecordId."""
request = alidns_models.UpdateDomainRecordRequest(
record_id=record_id,
rr=rr,
type=record_type,
value=value,
ttl=ttl,
)
try:
client.update_domain_record(request)
print(f"✅ Updated RecordId {record_id}: {rr} -> {record_type} {value} (TTL: {ttl})")
except Exception as e:
print(f"ERROR updating record: {e}")
def delete_record(client: AlidnsClient, record_id: str):
"""Delete a DNS record by RecordId."""
request = alidns_models.DeleteDomainRecordRequest(record_id=record_id)
try:
client.delete_domain_record(request)
print(f"✅ Deleted RecordId: {record_id}")
except Exception as e:
print(f"ERROR deleting record: {e}")
def set_record_status(client: AlidnsClient, record_id: str, status: str):
"""Enable or disable a DNS record."""
request = alidns_models.SetDomainRecordStatusRequest(
record_id=record_id,
status=status, # "Enable" or "Disable"
)
try:
client.set_domain_record_status(request)
icon = "" if status == "Enable" else "⏸️"
print(f"{icon} RecordId {record_id} status set to: {status}")
except Exception as e:
print(f"ERROR setting status: {e}")
def main():
parser = argparse.ArgumentParser(description="Alibaba Cloud DNS Record Manager")
subparsers = parser.add_subparsers(dest="action", help="Action to perform")
# List
list_parser = subparsers.add_parser("list", help="List DNS records")
list_parser.add_argument("--domain", required=True, help="Domain name (e.g., gogenex.com)")
list_parser.add_argument("--rr", help="Filter by subdomain prefix")
list_parser.add_argument("--type", help="Filter by record type (A, CNAME, etc.)")
list_parser.add_argument("--region", default="cn-hangzhou", help="Alidns region (default: cn-hangzhou)")
# Add
add_parser = subparsers.add_parser("add", help="Add a DNS record")
add_parser.add_argument("--domain", required=True)
add_parser.add_argument("--rr", required=True, help="Subdomain prefix (e.g., api, www, @)")
add_parser.add_argument("--type", required=True, help="Record type (A, AAAA, CNAME, MX, TXT, etc.)")
add_parser.add_argument("--value", required=True, help="Record value (IP, domain, text)")
add_parser.add_argument("--ttl", type=int, default=600, help="TTL in seconds (default: 600)")
add_parser.add_argument("--region", default="cn-hangzhou")
# Update
update_parser = subparsers.add_parser("update", help="Update a DNS record")
update_parser.add_argument("--record-id", required=True, help="RecordId to update")
update_parser.add_argument("--rr", required=True)
update_parser.add_argument("--type", required=True)
update_parser.add_argument("--value", required=True)
update_parser.add_argument("--ttl", type=int, default=600)
update_parser.add_argument("--region", default="cn-hangzhou")
# Delete
delete_parser = subparsers.add_parser("delete", help="Delete a DNS record")
delete_parser.add_argument("--record-id", required=True, help="RecordId to delete")
delete_parser.add_argument("--region", default="cn-hangzhou")
# Enable / Disable
enable_parser = subparsers.add_parser("enable", help="Enable a DNS record")
enable_parser.add_argument("--record-id", required=True)
enable_parser.add_argument("--region", default="cn-hangzhou")
disable_parser = subparsers.add_parser("disable", help="Disable a DNS record")
disable_parser.add_argument("--record-id", required=True)
disable_parser.add_argument("--region", default="cn-hangzhou")
args = parser.parse_args()
if not args.action:
parser.print_help()
sys.exit(1)
client = create_client(args.region)
if args.action == "list":
list_records(client, args.domain, rr=args.rr, record_type=getattr(args, "type", None))
elif args.action == "add":
add_record(client, args.domain, args.rr, args.type, args.value, args.ttl)
elif args.action == "update":
update_record(client, args.record_id, args.rr, args.type, args.value, args.ttl)
elif args.action == "delete":
delete_record(client, args.record_id)
elif args.action == "enable":
set_record_status(client, args.record_id, "Enable")
elif args.action == "disable":
set_record_status(client, args.record_id, "Disable")
if __name__ == "__main__":
main()