From a1b4e32bfb0282422a2dedbe81f329fd5c83d554 Mon Sep 17 00:00:00 2001 From: badra001 Date: Mon, 24 Apr 2023 22:02:40 -0400 Subject: [PATCH] fix --- code/ddns-lambda.py | 343 ++++++++++++++++++++++--------------------- code/ddns-lambda.zip | Bin 21060 -> 21131 bytes 2 files changed, 172 insertions(+), 171 deletions(-) diff --git a/code/ddns-lambda.py b/code/ddns-lambda.py index ac00be8..a549520 100755 --- a/code/ddns-lambda.py +++ b/code/ddns-lambda.py @@ -73,7 +73,7 @@ LOGGER = logging.getLogger() account_id = None region = None -VERSION = '1.2.0b43' +VERSION = '1.2.0b44' # Read Env variables DEBUG_LOG_LEVEL = os.environ.get('DebugLogLevel', 'INFO') @@ -494,14 +494,14 @@ def lambda_handler( return caller_response # These are collections of zones in Route 53. -## hosted_zones = new_list_hosted_zones(route53, instance_id) -## LOGGER.debug("hosted_zones for vpc_id %s: %s", vpc_id, str(hosted_zones) + lineno()) +# hosted_zones = new_list_hosted_zones(route53, instance_id) +# LOGGER.debug("hosted_zones for vpc_id %s: %s", vpc_id, str(hosted_zones) + lineno()) hosted_zones_by_vpc = new_list_hosted_zones_by_vpc( route53, instance_id, vpc_id, region) LOGGER.debug("hosted_zones_by_vpc for vpc_id %s: %s", vpc_id, str(hosted_zones_by_vpc) + lineno()) -## private_hosted_zones = get_private_hosted_zones(hosted_zones) -## LOGGER.debug("private_hosted_zones: %s", str(list(private_hosted_zones)) + lineno()) +# private_hosted_zones = get_private_hosted_zones(hosted_zones) +# LOGGER.debug("private_hosted_zones: %s", str(list(private_hosted_zones)) + lineno()) private_hosted_zones_by_vpc = get_private_hosted_zones_by_vpc(hosted_zones_by_vpc) LOGGER.debug("private_hosted_zones_by_vpc: vpc_id: %s, %s", vpc_id, str(list(private_hosted_zones_by_vpc)) + lineno()) @@ -687,9 +687,9 @@ def lambda_handler( *phz_collection_by_vpc[final_hosted_zone_name].values()) zone_data_reverse = zone_data_tuple( *phz_collection_by_vpc[tag_data['ptr_entry'].zonename].values()) -## final_hosted_zone_item = phz_collection_by_vpc[final_hosted_zone_name] -## final_hosted_zone_id = zone_data_forward.zone_id -## final_hosted_zone_owner = final_hosted_zone_item['owner_account'] +# final_hosted_zone_item = phz_collection_by_vpc[final_hosted_zone_name] +# final_hosted_zone_id = zone_data_forward.zone_id +# final_hosted_zone_owner = final_hosted_zone_item['owner_account'] LOGGER.info(f"zone_data_forward: {pformat(zone_data_forward)}") LOGGER.info(f"zone_data_reverse: {pformat(zone_data_reverse)}") @@ -1727,10 +1727,11 @@ def new_get_resource_record(oclient, instance_id, zone_id, host_name, hosted_zon LOGGER.debug("list_resource_record_sets looking for record %s in zone %s", str(host_name), str(hosted_zone_name) + lineno()) + fqdn = create_fqdn(host_name, hosted_zone_name) response = client.list_resource_record_sets( HostedZoneId=zone_id, # StartRecordName=host_name + hosted_zone_name, - StartRecordName=create_fqdn(host_name, hosted_zone_name), + StartRecordName=fqdn StartRecordType=record_type, MaxItems='1') @@ -1741,13 +1742,13 @@ def new_get_resource_record(oclient, instance_id, zone_id, host_name, hosted_zon rr_name = rr_set['Name'] # check if the return value matches the record, if not ignore # if the record isn't there, it returns the list_resource_record_sets returns the next record - if rr_name == (host_name + hosted_zone_name): +# if rr_name == (host_name + hosted_zone_name): + if rr_name == fqdn value = rr_set['ResourceRecords'][0]['Value'] - LOGGER.debug("list_resource_record_sets returned value. %s", - str(value) + lineno()) + LOGGER.debug( + f"list_resource_record_sets returned value {value}: {lineno()}") else: - LOGGER.debug("list_resource_record_sets returned different record ignoring. %s", - str(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()) @@ -1777,12 +1778,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()) @@ -1862,24 +1863,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()) @@ -1894,7 +1895,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": [ { @@ -1915,7 +1916,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 ) @@ -1928,16 +1929,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( @@ -1960,13 +1961,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, @@ -1987,11 +1988,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: @@ -2000,9 +2001,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 @@ -2022,8 +2023,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", @@ -2637,7 +2638,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 @@ -2683,7 +2684,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) ) @@ -2708,20 +2709,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, @@ -2732,25 +2733,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: @@ -2759,13 +2760,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, @@ -2776,7 +2777,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()) @@ -2785,13 +2786,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", @@ -2802,31 +2803,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()) @@ -2840,13 +2841,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, @@ -2856,7 +2857,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()) @@ -2865,13 +2866,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", @@ -2881,11 +2882,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", @@ -2893,19 +2894,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()) @@ -2915,8 +2916,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 @@ -2929,13 +2930,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 @@ -2951,7 +2952,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) @@ -2965,10 +2966,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) @@ -2980,8 +2981,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) @@ -2993,8 +2994,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) @@ -3006,8 +3007,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) @@ -3019,26 +3020,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: @@ -3064,10 +3065,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)) @@ -3087,6 +3088,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 56b41bb0390c87b84ea4a8295fa784e6687bfdb3..e875eb84ed223c6e492a6560aab4d1037174006e 100644 GIT binary patch literal 21131 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 literal 21060 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�aqK~wTVZt#%^!8NA^ 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 N8~k*hJ^cv){0DWnr8WQn