td-filter: new operators: starts with and ends with
[hband-tools.git] / aws / ddns_lambda.py
blob4bd210aa303c60d6fa0c3d6456616c8c9c464e4a
2 import botocore
3 import boto3
4 #import json
5 #import re
6 #import uuid
7 import time
8 from datetime import datetime
9 #import random
12 #boto3.setup_default_session(profile_name='sajat')
14 print('Loading function at ' + datetime.now().time().isoformat())
15 route53 = boto3.client('route53')
16 compute = boto3.client('ec2')
17 global_default_ttl = 3600
18 (UPDATE, DELETE) = range(2)
19 R53Cache = []
22 def lambda_handler(event, context):
23 # print(repr(event))
25 if event['detail'].has_key('eventName'):
26 region = event['detail']['awsRegion']
27 instance_id = event['detail']['requestParameters']['resourcesSet']['items'][0]['resourceId'] #FIXME iterate through on all instances
28 if event['detail']['eventName'] == 'CreateTags':
29 action = UPDATE
30 elif event['detail']['eventName'] == 'DeleteTags':
31 action = DELETE
32 else:
33 region = event['region']
34 instance_id = event['detail']['instance-id']
35 state = event['detail']['state']
36 if state == 'running':
37 action = UPDATE
38 else:
39 action = DELETE
41 # DELETE old record if exists
42 ZoneId = None
43 try:
44 (Name, ZoneId, Zone) = get_instance_name_by_id(instance_id)
45 except:
46 print("Old Name not found in DNS for %s" % instance_id)
47 if action == DELETE:
48 return False
50 if ZoneId is not None:
51 public_ip = ''
52 for record in iter_records():
53 if record['zone'] == Zone and record['type'] == 'A' and record['name'] == Name:
54 public_ip = record['content']
55 break
57 dereg = deregister_name(ZoneId, Zone, Name, instance_id, public_ip)
58 if action == DELETE:
59 return dereg
61 # Add new record
62 if action == UPDATE:
63 while True:
64 try:
65 instance = compute.describe_instances(InstanceIds=[instance_id])
66 break
67 except botocore.exceptions.ClientError:
68 pass
70 try:
71 public_ip = instance['Reservations'][0]['Instances'][0]['PublicIpAddress']
72 except BaseException as e:
73 print("Instance %s has no public IP" % instance_id)
74 return None
76 tags = dict(map(lambda t: (t['Key'], t['Value']), instance['Reservations'][0]['Instances'][0]['Tags']))
77 try:
78 DnsName = tags['dns-name']
79 except KeyError:
80 try:
81 Name = tags['Name']
82 Zone = tags['dns-zone']
83 if not Zone.endswith('.'):
84 Zone += '.'
85 try:
86 ZoneId = get_zone_id_by_name(Zone)
87 except ValueError:
88 print("No such Zone with name '%s'" % Zone)
89 return False
90 except KeyError:
91 print("Name and dns-zone tags or dns-name tag not found on instance %s" % instance_id)
92 return None
94 try:
95 if not DnsName.endswith('.'):
96 DnsName += '.'
97 for record in sorted(iter_records(), cmp = lambda a, b: cmp(len(b['zone']), len(a['zone']))):
98 suffix = '.' + record['zone']
99 if DnsName.endswith(suffix):
100 Name = DnsName[0:-len(suffix)]
101 Zone = record['zone']
102 ZoneId = record['zone_id']
103 break
104 try:
105 Zone
106 except NameError:
107 print("Zone not found for FQDN '%s'" % DnsName)
108 return False
109 except NameError:
110 pass
112 return register_name(ZoneId, Zone, Name, instance_id, public_ip)
115 def iter_records():
116 if len(R53Cache) > 0:
117 for record_dict in R53Cache:
118 yield record_dict
119 else:
120 zones = route53.list_hosted_zones()['HostedZones']
121 for zone in zones:
122 records = route53.list_resource_record_sets(HostedZoneId=zone['Id'])['ResourceRecordSets']
123 for record in records:
124 for rr in record['ResourceRecords']:
125 zone_name = zone['Name']
126 name = record['Name'][0:-len(zone_name)-1]
127 type = record['Type']
128 content = rr['Value']
129 if type == 'TXT':
130 content = content.strip('"')
131 record_dict = {'zone': zone_name, 'zone_id': zone['Id'], 'type': type, 'name': name, 'ttl': record['TTL'], 'content': content}
132 #R53Cache.append(record_dict)
133 yield record_dict
135 def get_instance_name_by_id(instance_id):
136 for record in iter_records():
137 if record['type'] == 'TXT' and record['name'] == instance_id + '.instance-id':
138 return (record['content'], record['zone_id'], record['zone'])
139 raise ValueError("No such TXT record with name '%s'" % instance_id)
141 def get_zone_id_by_name(zone_name):
142 for record in iter_records():
143 if record['zone'] == zone_name:
144 return record['zone_id']
145 raise ValueError("No such zone with name '%s'" % zone_name)
147 def register_name(zone_id, zone, name, instance_id, content):
148 fqdn = name + '.' + zone
149 print("Registering: %s IN A %s, Id=%s" % (fqdn, content, instance_id))
151 route53.change_resource_record_sets(
152 HostedZoneId = zone_id,
153 ChangeBatch = {
154 "Comment": "Updated by Lambda DDNS",
155 "Changes": [
157 "Action": "UPSERT",
158 "ResourceRecordSet": {
159 "Name": fqdn,
160 "Type": 'A',
161 "TTL": global_default_ttl,
162 "ResourceRecords": [{"Value": content}]
166 "Action": "UPSERT",
167 "ResourceRecordSet": {
168 "Name": instance_id + '.instance-id.' + zone,
169 "Type": 'TXT',
170 "TTL": global_default_ttl,
171 "ResourceRecords": [{"Value": '"' + name + '"'}]
177 return True
179 def deregister_name(zone_id, zone, name, instance_id, content):
180 fqdn = name + '.' + zone
181 print("Deregistering: %s IN A %s, Id=%s" % (fqdn, content, instance_id))
183 route53.change_resource_record_sets(
184 HostedZoneId = zone_id,
185 ChangeBatch = {
186 "Comment": "Deleted by Lambda DDNS",
187 "Changes": [
189 "Action": "DELETE",
190 "ResourceRecordSet": {
191 "Name": fqdn,
192 "Type": 'A',
193 "TTL": global_default_ttl,
194 "ResourceRecords": [{"Value": content}]
198 "Action": "DELETE",
199 "ResourceRecordSet": {
200 "Name": instance_id + '.instance-id.' + zone,
201 "Type": 'TXT',
202 "TTL": global_default_ttl,
203 "ResourceRecords": [{"Value": '"' + name + '"'}]
209 return True