diff --git a/code/ddns-lambda.py b/code/ddns-lambda.py index 5e1c313..2c64928 100755 --- a/code/ddns-lambda.py +++ b/code/ddns-lambda.py @@ -1748,7 +1748,8 @@ def new_get_resource_record(oclient, instance_id, zone_id, host_name, hosted_zon LOGGER.debug( f"list_resource_record_sets returned value {value}: {lineno()}") else: - LOGGER.debug(f"list_resource_record_sets returned different record ignoring, fqdn [{fqdn}] != rr_name [{rr_name}]: {lineno()}" + LOGGER.debug( + f"list_resource_record_sets returned different record ignoring, fqdn [{fqdn}] != rr_name [{rr_name}]: {lineno()}") LOGGER.debug( "list_resource_record_sets returned without error. %s", lineno()) @@ -1778,12 +1779,12 @@ def new_get_resource_record(oclient, instance_id, zone_id, host_name, hosted_zon instance_id, str(MAX_API_RETRY) + lineno()) if SNS_ENABLE: try: - sns_msg={} - sns_msg['instance_id']=instance_id - sns_msg['account_id']=get_caller_account_id() - sns_msg['client']='route53' - sns_msg['boto3_method']='list_resource_record_sets' - sns_msg['message']='list_resource_record_sets timed out' + sns_msg = {} + sns_msg['instance_id'] = instance_id + sns_msg['account_id'] = get_caller_account_id() + sns_msg['client'] = 'route53' + sns_msg['boto3_method'] = 'list_resource_record_sets' + sns_msg['message'] = 'list_resource_record_sets timed out' publish_to_sns(get_sns_client(), json.dumps(sns_msg)) LOGGER.info("instance: %s, sending sns message %s", instance_id, json.dumps(sns_msg) + lineno()) @@ -1863,24 +1864,24 @@ def new_delete_resource_record(oclient, instance_id, zone_id, host_name, hosted_ # this ignores the client, and uses the session from sessions[account] with a new route53 client global phz_collection_by_vpc - zone_item=phz_collection_by_vpc[hosted_zone_name] + zone_item = phz_collection_by_vpc[hosted_zone_name] LOGGER.debug("Using zone %s, zone item %s: %s", str( hosted_zone_name), str(zone_item), lineno()) - zone_account=zone_item['owner_account'] + zone_account = zone_item['owner_account'] try: LOGGER.debug("Calling get_session_assume_role() on account %s: %s", zone_account, lineno()) - this_session=get_session_assume_role(zone_account) + this_session = get_session_assume_role(zone_account) except Exception as err: LOGGER.error("Unable to esablish assume_role session in account %s: %s", str(zone_account), str(err) + lineno()) - update_response="AssumeRoleFailed" + update_response = "AssumeRoleFailed" return update_response - client=this_session.client('route53') + client = this_session.client('route53') - i=0 - delete_response={} + i = 0 + delete_response = {} LOGGER.debug("Deleting %s record %s in zone %s: %s", record_type, host_name, hosted_zone_name, lineno()) @@ -1895,7 +1896,7 @@ def new_delete_resource_record(oclient, instance_id, zone_id, host_name, hosted_ try: LOGGER.debug("Try %s Deleting %s record %s in zone %s: %s", str( i), record_type, host_name, hosted_zone_name, lineno()) - change_batch={ + change_batch = { "Comment": f"Deleted by {APPNAME} v{VERSION} from {account_id} in {region}", "Changes": [ { @@ -1916,7 +1917,7 @@ def new_delete_resource_record(oclient, instance_id, zone_id, host_name, hosted_ LOGGER.debug("change_resource_record_sets change_batch: %s", json.dumps(change_batch) + lineno()) - delete_response=client.change_resource_record_sets( + delete_response = client.change_resource_record_sets( HostedZoneId=zone_id, ChangeBatch=change_batch ) @@ -1929,16 +1930,16 @@ def new_delete_resource_record(oclient, instance_id, zone_id, host_name, hosted_ if 'NoSuchHostedZone' in str(err) and 'No hosted zone found with ID' in str(err): LOGGER.debug("Hosted zone not found error: %s", str(err) + lineno()) - delete_response="NoSuchHostedZone" + delete_response = "NoSuchHostedZone" break elif 'InvalidChangeBatch' in str(err) and 'it was not found' in str(err): LOGGER.debug("Record not found error: %s", str(err) + lineno()) - delete_response="InvalidChangeBatch-RecordNotFound" + delete_response = "InvalidChangeBatch-RecordNotFound" break elif 'InvalidChangeBatch' in str(err) and 'values provided do not match the current values' in str(err): LOGGER.debug("Record do not match current value error: %s", str(err) + lineno()) - delete_response="InvalidChangeBatch-RecordDoNotMatch" + delete_response = "InvalidChangeBatch-RecordDoNotMatch" break elif '(Throttling)' in str(err): LOGGER.debug("change_resource_record_sets DELETE throttled due to API limit, retrying: %s", str( @@ -1961,13 +1962,13 @@ def new_delete_resource_record(oclient, instance_id, zone_id, host_name, hosted_ if (delete_response == {} or delete_response == "InvalidChangeBatch-RecordDoNotMatch"): if SNS_ENABLE: try: - sns_msg={} - sns_msg['instance_id']=instance_id - sns_msg['account_id']=get_caller_account_id() - sns_msg['client']='route53' - sns_msg['boto3_method']='change_resource_record_sets' - sns_msg['message']='change_resource_record_sets could not DELETE record' - sns_msg['change_resource_record_sets']={ + sns_msg = {} + sns_msg['instance_id'] = instance_id + sns_msg['account_id'] = get_caller_account_id() + sns_msg['client'] = 'route53' + sns_msg['boto3_method'] = 'change_resource_record_sets' + sns_msg['message'] = 'change_resource_record_sets could not DELETE record' + sns_msg['change_resource_record_sets'] = { 'HostedZoneId': zone_id, 'ChangeBatch': change_batch} publish_to_sns(get_sns_client(), json.dumps(sns_msg)) LOGGER.info("instance: %s, sending sns message %s", instance_id, @@ -1988,11 +1989,11 @@ def get_zone_id(zone_name, hosted_zones, private_zone=True): """ try: if zone_name[-1] != '.': - zone_name=zone_name + '.' + zone_name = zone_name + '.' LOGGER.debug("zone name: %s", str(zone_name) + lineno()) LOGGER.debug("hosted_zones: %s", str(hosted_zones) + lineno()) - zones=[] + zones = [] for record in hosted_zones['HostedZones']: LOGGER.debug("record: %s", str(record) + lineno()) if record['Config']['PrivateZone'] == private_zone: @@ -2001,9 +2002,9 @@ def get_zone_id(zone_name, hosted_zones, private_zone=True): LOGGER.debug("zones: %s", str(zones) + lineno()) try: - zone_id_long=zones[0]['Id'] + zone_id_long = zones[0]['Id'] LOGGER.debug("zone id: %s", str(zone_id_long) + lineno()) - zone_id=str.split(str(zone_id_long), '/')[2] + zone_id = str.split(str(zone_id_long), '/')[2] return zone_id except: return None @@ -2023,8 +2024,8 @@ def is_valid_hostname(hostname): if hostname is None or len(hostname) > 255: return False if hostname[-1] == ".": - hostname=hostname[:-1] - allowed=re.compile(r"(?!-)[A-Z\d-]{1,63}(? 1: # remove beginning quote if info[0] == '"': - info=info[1:] + info = info[1:] # remove ending quote if info[-1] == '"': - info=info[:-1] + info = info[:-1] - kv_results={} - kv=info.split(',') + kv_results = {} + kv = info.split(',') LOGGER.debug("heritage split result: %s", str(kv) + lineno()) - header=kv.pop(0).split('=') + header = kv.pop(0).split('=') if header[0] != 'heritage': LOGGER.debug("heritage analysis: does not contain heritage header, returning: %s", str(kv_results) + lineno()) return kv_results else: - appname=header[1] - kv_results['application_name']=appname + appname = header[1] + kv_results['application_name'] = appname LOGGER.debug("heritage analysis: assigning application_name: %s", str(appname) + lineno()) try: for item in kv: - k, v=item.split('=', 2) + k, v = item.split('=', 2) LOGGER.debug("heritage item: key: %s, value: %s", str(k), str(v) + lineno()) # print('appname',appname,'k',k,'v',v) if appname + '/' in k: - nk=k.replace(appname + '/', '') - kv_results[nk]=v + nk = k.replace(appname + '/', '') + kv_results[nk] = v # print('nk',nk) if kv_results.get('version') is None: # version=kv_result.pop('version') # else: - version='null' + version = 'null' # return initialize_heritage(appname,version,kv_results) LOGGER.debug("heritage parsed dictionary: %s", @@ -2638,7 +2639,7 @@ def get_heritage_item(data, key): str(data), str(type(data)) + lineno()) return None else: - result=data.get(key, None) + result = data.get(key, None) LOGGER.debug("get_heritage_item: getting key %s value %s", str(key), str(result) + lineno()) return result @@ -2684,7 +2685,7 @@ def publish_to_sns(client, message): if SNS_TOPIC_ARN != '': try: - response=client.publish( + response = client.publish( TopicArn=SNS_TOPIC_ARN, Message=str(message) ) @@ -2709,20 +2710,20 @@ def process_delete_records(route53, instance_id, zone_id, :return response: # dictionary of 'delete_success' and 'msg' """ - response={} - response_delete_success=True - response_msg=[] + response = {} + response_delete_success = True + response_msg = [] LOGGER.info("instance: %s, Delete %s Record. Checking TXT record association for %s in zone %s", instance_id, record_type, record_name, zone_name + lineno()) # if record type is CNAME, we need to add the TXT RR prefix if record_type == 'CNAME': - txt_record_name=TXT_RR_PREFIX + '.' + record_name + txt_record_name = TXT_RR_PREFIX + '.' + record_name else: - txt_record_name=record_name + txt_record_name = record_name - heritage_value=new_get_resource_record( + heritage_value = new_get_resource_record( route53, instance_id, zone_id, @@ -2733,25 +2734,25 @@ def process_delete_records(route53, instance_id, zone_id, ) # Return the dictionary of the value with comma separated - heritage=parse_heritage(heritage_value) + heritage = parse_heritage(heritage_value) LOGGER.debug("heritage parsed data in string format: %s", str(heritage) + lineno()) # check if the TXT record was created by the Lambda as match instance-id if verify_heritage_owner(heritage, HERITAGE_TAG): LOGGER.debug("TXT record was created by Lambda DDNS %s", HERITAGE_TAG + lineno()) - heritage_own=True + heritage_own = True else: LOGGER.info("TXT record was not created by Lambda DDNS %s", HERITAGE_TAG + lineno()) - heritage_own=False + heritage_own = False if compare_heritage(heritage, 'instance_id', instance_id): LOGGER.debug("TXT record matches instance_id: %s", instance_id + lineno()) - heritage_instance_match=True + heritage_instance_match = True else: LOGGER.info("TXT record does not match instance_id: %s", instance_id + lineno()) - heritage_instance_match=False + heritage_instance_match = False # delete A/PTR/AAAA/CNAME record if heritage_own and heritage_instance_match: @@ -2760,13 +2761,13 @@ def process_delete_records(route53, instance_id, zone_id, LOGGER.info("Deleting %s resource record %s, in zone %s, with a value of %s", record_type, record_name, zone_name, record_value + lineno()) - response_text='Delete ' + record_type + \ + response_text = 'Delete ' + record_type + \ ' record in zone id: ' + zone_id + \ ' for record ' + record_name + \ ' in zone named ' + zone_name + \ ' with value: ' + record_value - delete_response=new_delete_resource_record( + delete_response = new_delete_resource_record( route53, instance_id, zone_id, @@ -2777,7 +2778,7 @@ def process_delete_records(route53, instance_id, zone_id, ) if delete_response == 'NoSuchHostedZone': - response_delete_success=False + response_delete_success = False response_msg.append("Failed, no such zone: " + response_text) LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) @@ -2786,13 +2787,13 @@ def process_delete_records(route53, instance_id, zone_id, LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': - response_delete_success=False + response_delete_success = False response_msg.append( "Failed, requested delete do not match existing record: " + response_text) LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", instance_id, response_text + lineno()) elif delete_response == {}: - response_delete_success=False + response_delete_success = False response_msg.append( "Failed, could NOT delete Record: " + response_text) LOGGER.info("instance: %s, Failed, could NOT delete Record: %s", @@ -2803,31 +2804,31 @@ def process_delete_records(route53, instance_id, zone_id, response_msg.append("Success: " + response_text) except BaseException as err: - response_delete_success=False + response_delete_success = False LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno()) else: - response_delete_success=False + response_delete_success = False response_msg.append("Failed, the TXT record for the " + record_type + " record does not match expected value. Will not delete the " + record_type + " record.") LOGGER.error("instance: %s, the TXT record for the %s record does not match expected value. Will not delete the %s record. %s\n", instance_id, record_type, record_type, lineno()) if SNS_ENABLE: try: - sns_msg={} - sns_msg['instance_id']=instance_id - sns_msg['account_id']=get_caller_account_id() - sns_msg['message']='TXT record does not match. Will not delete the A/PTR/CNAME/AAAA record.' - - sns_heritage={} - sns_heritage['record_type']=record_type - sns_heritage['record_name']=record_name - sns_heritage['zone_name']=zone_name - sns_heritage['zone_id']=zone_id - sns_heritage['heritage_value']=heritage_value - - sns_msg['heritage']=sns_heritage + sns_msg = {} + sns_msg['instance_id'] = instance_id + sns_msg['account_id'] = get_caller_account_id() + sns_msg['message'] = 'TXT record does not match. Will not delete the A/PTR/CNAME/AAAA record.' + + sns_heritage = {} + sns_heritage['record_type'] = record_type + sns_heritage['record_name'] = record_name + sns_heritage['zone_name'] = zone_name + sns_heritage['zone_id'] = zone_id + sns_heritage['heritage_value'] = heritage_value + + sns_msg['heritage'] = sns_heritage publish_to_sns(get_sns_client(), json.dumps(sns_msg)) LOGGER.info("instance: %s, sending sns message %s", instance_id, json.dumps(sns_msg) + lineno()) @@ -2841,13 +2842,13 @@ def process_delete_records(route53, instance_id, zone_id, LOGGER.info("Deleting heritage TXT resource record %s, in the zone %s, with value of %s", txt_record_name, zone_name, str(heritage_value) + lineno()) - response_text='Delete ' + 'TXT' + \ + response_text = 'Delete ' + 'TXT' + \ ' record in zone id: ' + zone_id + \ ' for record ' + txt_record_name + \ ' in zone named ' + zone_name + \ ' with value: ' + str(heritage_value) - delete_response=new_delete_resource_record( + delete_response = new_delete_resource_record( route53, instance_id, zone_id, @@ -2857,7 +2858,7 @@ def process_delete_records(route53, instance_id, zone_id, str(heritage_value)) if delete_response == 'NoSuchHostedZone': - response_delete_success=False + response_delete_success = False response_msg.append("Failed, no such zone: " + response_text) LOGGER.info("instance: %s, NoSuchHostedZone: %s", instance_id, response_text + lineno()) @@ -2866,13 +2867,13 @@ def process_delete_records(route53, instance_id, zone_id, LOGGER.info("instance: %s, InvalidChangeBatch-RecordNotFound: %s", instance_id, response_text + lineno()) elif delete_response == 'InvalidChangeBatch-RecordDoNotMatch': - response_delete_success=False + response_delete_success = False response_msg.append( "Failed, requested delete do not match existing record: " + response_text) LOGGER.info("instance: %s, InvalidChangeBatch-RecordDoNotMatch: %s", instance_id, response_text + lineno()) elif delete_response == {}: - response_delete_success=False + response_delete_success = False response_msg.append( "Failed, could NOT delete Record: " + response_text) LOGGER.info("instance: %s, Failed Could NOT delete Record: %s", @@ -2882,11 +2883,11 @@ def process_delete_records(route53, instance_id, zone_id, LOGGER.info("instance: %s, Success: %s", instance_id, response_text + lineno()) except BaseException as err: - response_delete_success=False + response_delete_success = False LOGGER.error("instance: %s, unexpected error. %s\n", instance_id, str(err) + lineno()) else: - response_delete_success=False + response_delete_success = False response_msg.append("Failed, the TXT record for " + record_type + " does not match expected value. Will not delete the TXT record.") LOGGER.error("instance: %s, the TXT record for the %s does not match expected value. Will not delete TXT record. %s", @@ -2894,19 +2895,19 @@ def process_delete_records(route53, instance_id, zone_id, if SNS_ENABLE: try: - sns_msg={} - sns_msg['instance_id']=instance_id - sns_msg['account_id']=get_caller_account_id() - sns_msg['message']='TXT record does not match. Will not delete the TXT record.' - - sns_heritage={} - sns_heritage['record_type']='TXT' - sns_heritage['record_name']=txt_record_name - sns_heritage['zone_name']=zone_name - sns_heritage['zone_id']=zone_id - sns_heritage['heritage_value']=heritage_value - - sns_msg['heritage']=sns_heritage + sns_msg = {} + sns_msg['instance_id'] = instance_id + sns_msg['account_id'] = get_caller_account_id() + sns_msg['message'] = 'TXT record does not match. Will not delete the TXT record.' + + sns_heritage = {} + sns_heritage['record_type'] = 'TXT' + sns_heritage['record_name'] = txt_record_name + sns_heritage['zone_name'] = zone_name + sns_heritage['zone_id'] = zone_id + sns_heritage['heritage_value'] = heritage_value + + sns_msg['heritage'] = sns_heritage publish_to_sns(get_sns_client(), json.dumps(sns_msg)) LOGGER.info("instance: %s, sending sns message %s", instance_id, json.dumps(sns_msg) + lineno()) @@ -2916,8 +2917,8 @@ def process_delete_records(route53, instance_id, zone_id, str(sys.exc_info()[0]) + lineno()) # create a dictionary to return - response['delete_success']=response_delete_success - response['msg']=response_msg + response['delete_success'] = response_delete_success + response['msg'] = response_msg return response @@ -2930,13 +2931,13 @@ def process_tags_flags(tags): :return dict(string): flag settings in defaultdict for controlling which names are registered and when """ - tag_dict={tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} - flags_dict=defaultdict(lambda: False) - flags=tag_dict.get(TAGKEY_FLAGS.lower(), '').split(',') + tag_dict = {tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} + flags_dict = defaultdict(lambda: False) + flags = tag_dict.get(TAGKEY_FLAGS.lower(), '').split(',') for flag in flags: if flag != '': LOGGER.debug("Setting 'flags' to True: %s", str(flag) + lineno()) - flags_dict[flag]=True + flags_dict[flag] = True return flags_dict @@ -2952,7 +2953,7 @@ def process_tags_value(name): """ if name != '': - components=parse_hostname_to_components(name) + components = parse_hostname_to_components(name) if components: return (True, components[0], components[1]) return (False, None, None) @@ -2966,10 +2967,10 @@ def process_tags_option_cname(tags): : return tuple(bool, str, str): true | false if vaid, hostname, domainname """ - tag_dict={tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} + tag_dict = {tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} # value = tag_dict.get(TAGKEY_CNAME.lower(), '').split(',') # need additional work to handle a comma-separated list - value=tag_dict.get(TAGKEY_CNAME.lower(), '') + value = tag_dict.get(TAGKEY_CNAME.lower(), '') return process_tags_value(value) @@ -2981,8 +2982,8 @@ def process_tags_option_zone(tags): : return tuple(bool, str, str): true | false if vaid, hostname, domainname """ - tag_dict={tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} - value=tag_dict.get(TAGKEY_ZONE.lower(), '') + tag_dict = {tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} + value = tag_dict.get(TAGKEY_ZONE.lower(), '') return process_tags_value(value) @@ -2994,8 +2995,8 @@ def process_tags_option_name(tags): : return: """ - tag_dict={tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} - value=tag_dict.get(TAGKEY_HOSTNAME.lower(), '') + tag_dict = {tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} + value = tag_dict.get(TAGKEY_HOSTNAME.lower(), '') return process_tags_value(value) @@ -3007,8 +3008,8 @@ def process_tags_name(tags): : return: """ - tag_dict={tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} - value=tag_dict.get('name', '') + tag_dict = {tag['Key'].lstrip().lower(): tag['Value'] for tag in tags} + value = tag_dict.get('name', '') return process_tags_value(value) @@ -3020,26 +3021,26 @@ def get_session_assume_role(account): : return: boto3.session corresonding to the assumed role """ - this_session=sessions.get(account, None) + this_session = sessions.get(account, None) try: if this_session is None: LOGGER.debug("Existing session not found for account %s: %s", account, lineno()) - role_arn=format(REMOTE_ROLE_ARN_FORMAT % (partition, account)) - response=sts_client.assume_role(RoleArn=role_arn, RoleSessionName=APPNAME) + role_arn = format(REMOTE_ROLE_ARN_FORMAT % (partition, account)) + response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName=APPNAME) LOGGER.debug("Called sts:assumerole for arn %s: %s", str(role_arn), lineno()) - credentials=response['Credentials'] + credentials = response['Credentials'] LOGGER.info( f"Called assume_role for {account} ARN {role_arn}, got credentials with expiration {credentials['Expiration']}: {lineno()}") count['assumed_role.new'] += 1 - this_session=boto3.Session( + this_session = boto3.Session( aws_access_key_id=credentials["AccessKeyId"], aws_secret_access_key=credentials["SecretAccessKey"], aws_session_token=credentials["SessionToken"], region_name=region) - sessions[account]=this_session + sessions[account] = this_session LOGGER.debug("Crated new session for account %s: %s", str(account), lineno()) else: @@ -3065,10 +3066,10 @@ def parse_hostname_to_components(name): """ global phz_collection_by_vpc - names=name.rstrip('.').split('.') + names = name.rstrip('.').split('.') for i in range(len(names)): - host='.'.join(names[0:i]) - domain='.'.join(names[i:]) + '.' + host = '.'.join(names[0:i]) + domain = '.'.join(names[i:]) + '.' if phz_collection_by_vpc.get(domain): return (host, domain) LOGGER.error('No PHZ found for any domain components of %s: %s', name, str(lineno)) @@ -3088,6 +3089,6 @@ def create_fqdn(host, zone): :return (str,str): Tuple containing hostname components (may include dot) and domain name for which a PHZ exists. None is returned if not found. """ - fqdn=host.replace(zone, '').rstrip('.') + '.' + zone + fqdn = host.replace(zone, '').rstrip('.') + '.' + zone fqdn += '.' if fqdn[-1] != '.' else '' return fqdn diff --git a/code/ddns-lambda.zip b/code/ddns-lambda.zip index 106dce3..505f4d7 100644 Binary files a/code/ddns-lambda.zip and b/code/ddns-lambda.zip differ