使用boto更新route53中的DNS记录
我在用boto更新R53中的dns记录时遇到了以下错误:
Traceback (most recent call last):
File "testing.py", line 106, in <module>
updateDns(load_balancer_dns)
File "testing.py", line 102, in updateDns
change.commit()
File "/usr/lib/python2.6/site-packages/boto/route53/record.py", line 149, in commit
return self.connection.change_rrsets(self.hosted_zone_id, self.to_xml())
File "/usr/lib/python2.6/site-packages/boto/route53/connection.py", line 320, in change_rrsets
body)
boto.route53.exception.DNSServerError: DNSServerError: 505 HTTP Version Not Supported
这是我用来更新dns条目的函数:
def updateDns(load_balancer_dns):
r53 = boto.route53.connection.Route53Connection(aws_access_key_id=<access_key>,aws_secret_access_key=<secret_key>)
zone_id = r53.get_hosted_zone_by_name(<domain_name>)
print zone_id
change = boto.route53.record.ResourceRecordSets(connection=r53,hosted_zone_id=zone_id)
change.add_change_record("UPSERT", boto.route53.record.Record(name=<name>, type="CNAME", resource_records=load_balancer_dns, ttl=300))
change.commit()
print "record changed"
return None
updateDns(load_balancer_dns)
有没有其他人之前遇到过这样的情况?
4 个回答
0
仅供参考:
先获取变更的ID,然后通过boto连接(conn)检查状态,直到显示“INSYNC”(同步完成)为止。
例如:
def updateDns(load_balancer_dns):
r53 = boto.route53.connection.Route53Connection(aws_access_key_id=<access_key>,aws_secret_access_key=<secret_key>)
zone_id = r53.get_hosted_zone_by_name(<domain_name>)
print zone_id
change = boto.route53.record.ResourceRecordSets(connection=r53,hosted_zone_id=zone_id)
change.add_change_record("UPSERT", boto.route53.record.Record(name=<name>, type="CNAME", resource_records=load_balancer_dns, ttl=300))
_changes = change.commit()
change_id = _changes["ChangeResourceRecordSetsResponse"]["ChangeInfo"]["Id"].split("/")[-1]
while True:
status = r53.get_change(change_id)["GetChangeResponse"]["ChangeInfo"]["Status"]
if status == "INSYNC": break
sleep(10)
0
我遇到了一个跟这个类似的问题,所以作为一个“调试”的练习,我做了一个
print zone_id
我注意到这个对象是一个字典/JSON响应,所以我把我的代码改成了
zone_id = self.r53.get_hosted_zone_by_name(self.domain).get("GetHostedZoneResponse").get("HostedZone").get("Id")
这对我来说似乎有效了 - 现在我得到了一个403错误,但至少这个错误应该更容易解决。
声明一下 - 我是Python新手,所以不确定这是不是正确的方法!
1
在编程中,有时候我们需要让程序做一些特定的事情,比如在某个条件下执行某段代码。这就像给程序设定了一些规则,让它知道在什么情况下该做什么。
比如说,如果你想让程序在用户输入的数字大于10时,显示一条消息,你就需要用到“条件语句”。这就像是在问:“如果这个条件成立,我就做这件事。”
条件语句通常是用“if”这个词来开始的,后面跟着你想要检查的条件。如果条件成立,程序就会执行你指定的代码。如果不成立,程序可以选择不做任何事情,或者执行另一段代码,这通常是用“else”来表示的。
总之,条件语句帮助程序根据不同的情况做出不同的反应,就像我们在生活中根据情况做出不同的决定一样。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# src: https://stackoverflow.com/a/47166985/65706
# courtesy of: Naftuli Kay
#
from __future__ import absolute_import, print_function
from time import sleep
import argparse
import boto
import pprint
import sys
def set_vars():
usage_examples = '''
clear ; poetry run python process_dns.py --action upsert --dns-zone cgfinics.com --dns-name dev.cgfinics.com --dns-value 168.10.172.10 --record-type A
clear ; poetry run python process_dns.py --action upsert --dns-zone cgfinics.com --dns-name www.dev.cgfinics.com --dns-value www.dev.cgfinics.com --record-type CNAME
clear ; poetry run python process_dns.py --action delete --dns-zone cgfinics.com --dns-name dev.cgfinics.com
'''
parser = argparse.ArgumentParser('A quick and dirty DNS upsert to aws with boto \n\n' + usage_examples)
parser.add_argument('--action', required=True, nargs='?',
help="The action to perform - upsert or delete ")
parser.add_argument('--dns-zone', required=True, nargs='?',
help="The DNS zone to process ")
parser.add_argument('--dns-name', required=True, nargs='?',
help="The DNS name to process ")
parser.add_argument('--dns-value', required=False, nargs='?',
help="The DNS value to process ")
parser.add_argument('--record-type', required=True, nargs='?',
help="The DNS record type - could be A, CNAME ")
args = parser.parse_args()
return args
def main():
"""Entrypoint."""
args = set_vars()
action = args.action
r53 = boto.connect_route53()
zones = r53.get_all_hosted_zones()['ListHostedZonesResponse']['HostedZones']
dns_zone = args.dns_zone + '.' if not args.dns_zone.endswith('.') else args.dns_zone
public_zone = find_public_zone(dns_zone , zones)
dns_name = args.dns_name + '.' if not args.dns_name.endswith('.') else args.dns_name
dns_value = args.dns_value
record_type = args.record_type
if action == "upsert":
upsert_record(r53, public_zone, dns_name, dns_value, record_type, wait=True)
sys.exit(0)
if action == "delete":
delete_record(r53, public_zone, dns_name, dns_value, record_type, wait=True)
sys.exit(0)
print("only the upser and delete actions are supported !!!")
sys.exit(1)
def find_public_zone(name, zones):
for zone in zones:
if zone.get('Name') == name and zone.get('Config', {}).get('PrivateZone') in [True, 'false']:
return zone
return None
def find_record(r53, zone_id, name, record_type):
records = r53.get_all_rrsets(zone_id)
for record in records:
if record.name == name and record.type == record_type:
return record
return None
def upsert_record(r53, zone, name, record, record_type, ttl=60, wait=False):
print("Inserting record {}[{}] -> {}; TTL={}".format(name, record_type, record, ttl))
recordset = boto.route53.record.ResourceRecordSets(connection=r53, hosted_zone_id=zone.get('Id').split('/')[-1])
recordset.add_change_record('UPSERT', boto.route53.record.Record(
name=name,
type=record_type,
resource_records=[record],
ttl=ttl
))
changeset = recordset.commit()
change_id = changeset['ChangeResourceRecordSetsResponse']['ChangeInfo']['Id'].split('/')[-1]
while wait:
status = r53.get_change(change_id)['GetChangeResponse']['ChangeInfo']['Status']
if status == 'INSYNC':
break
sleep(6)
def delete_record(r53, zone, name, record, record_type, wait=False):
print("Deleting record {}[{}] -> {}".format(name, record_type, record))
zone_id = zone.get('Id').split('/')[-1]
record = find_record(r53, zone_id, name, record_type)
if not record:
print("No record exists.")
return
recordset = boto.route53.record.ResourceRecordSets(connection=r53, hosted_zone_id=zone.get('Id').split('/')[-1])
recordset.add_change_record('DELETE', record)
changeset = recordset.commit()
change_id = changeset['ChangeResourceRecordSetsResponse']['ChangeInfo']['Id'].split('/')[-1]
while wait:
status = r53.get_change(change_id)['GetChangeResponse']['ChangeInfo']['Status']
if status == 'INSYNC':
break
sleep(10)
if __name__ == "__main__":
main()
3
因为这个问题没有一个权威的答案,所以我这里有一个我刚拼凑出来的可用脚本:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function
from time import sleep
import boto
def main():
"""Entrypoint."""
r53 = boto.connect_route53()
zones = r53.get_all_hosted_zones()['ListHostedZonesResponse']['HostedZones']
private_zone = find_private_zone('sub.mydomain.com.', zones)
reverse_zone = find_private_zone('100.10.in-addr.arpa.', zones)
upsert_record(r53, private_zone, 'dangus.sub.mydomain.com.', '127.0.0.1', 'A', wait=True)
delete_record(r53, private_zone, 'dangus.sub.mydomain.com.', '127.0.0.1', 'A', wait=True)
def find_private_zone(name, zones):
for zone in zones:
if zone.get('Name') == name and zone.get('Config', {}).get('PrivateZone') in [True, 'true']:
return zone
return None
def find_record(r53, zone_id, name, record_type):
records = r53.get_all_rrsets(zone_id)
for record in records:
if record.name == name and record.type == record_type:
return record
return None
def upsert_record(r53, zone, name, record, record_type, ttl=60, wait=False):
print("Inserting record {}[{}] -> {}; TTL={}".format(name, record_type, record, ttl))
recordset = boto.route53.record.ResourceRecordSets(connection=r53, hosted_zone_id=zone.get('Id').split('/')[-1])
recordset.add_change_record('UPSERT', boto.route53.record.Record(
name=name,
type=record_type,
resource_records=[record],
ttl=ttl
))
changeset = recordset.commit()
change_id = changeset['ChangeResourceRecordSetsResponse']['ChangeInfo']['Id'].split('/')[-1]
while wait:
status = r53.get_change(change_id)['GetChangeResponse']['ChangeInfo']['Status']
if status == 'INSYNC':
break
sleep(10)
def delete_record(r53, zone, name, record, record_type, wait=False):
print("Deleting record {}[{}] -> {}".format(name, record_type, record))
zone_id = zone.get('Id').split('/')[-1]
record = find_record(r53, zone_id, name, record_type)
if not record:
print("No record exists.")
return
recordset = boto.route53.record.ResourceRecordSets(connection=r53, hosted_zone_id=zone.get('Id').split('/')[-1])
recordset.add_change_record('DELETE', record)
changeset = recordset.commit()
change_id = changeset['ChangeResourceRecordSetsResponse']['ChangeInfo']['Id'].split('/')[-1]
while wait:
status = r53.get_change(change_id)['GetChangeResponse']['ChangeInfo']['Status']
if status == 'INSYNC':
break
sleep(10)
if __name__ == "__main__":
main()
不幸的是,我用过的API都没有很好的Route 53实现,最终你得用字典查找来查看服务实际返回的XML。
一些注意事项:
- 一定要使用完全合格的域名(FQDN),这意味着每个记录都应该以点号结尾。
- 你不能只获取你想要的托管区域,你需要获取所有的托管区域,然后再搜索你想要的那个(比如:一个与给定名称匹配的私有区域)。
- 你需要从你的托管区域中解析出ID。
- 你需要从你的变更集(change sets)中解析出ID。
- 在删除记录时,必须先获取当前记录的状态(如果存在的话),然后将这个值作为删除操作的一部分发送给Route 53。
这让API的使用变得非常麻烦,但至少它符合RFC-1925的第一条规则:它能工作。