From 4c480b1c4bc0bceb3c6f5209b7aec9f919808300 Mon Sep 17 00:00:00 2001 From: badra001 Date: Tue, 25 Apr 2023 20:35:42 -0400 Subject: [PATCH] fix --- code/ddns-lambda.py | 487 ++++++++++++++++++++++--------------------- code/ddns-lambda.zip | Bin 21573 -> 21535 bytes 2 files changed, 244 insertions(+), 243 deletions(-) diff --git a/code/ddns-lambda.py b/code/ddns-lambda.py index f4853e2..29fce56 100755 --- a/code/ddns-lambda.py +++ b/code/ddns-lambda.py @@ -970,7 +970,8 @@ def lambda_handler( f"hostname {cname_host_name} in zone {cname_domain_suffix} with value {heritage_value}" if create_response == 'success': - dns_data.append(dns_data_tuple(cname_domain_suffix_id, cname_host_name_txt, cname_domain_suffix, 'TXT', heritage_value) + dns_data.append(dns_data_tuple( + cname_domain_suffix_id, cname_host_name_txt, cname_domain_suffix, 'TXT', heritage_value)) LOGGER.info("instance: %s, Created %s", instance_id, append_msg + lineno()) caller_response.append('Created ' + append_msg) @@ -987,7 +988,7 @@ def lambda_handler( # not running, so process delete CNAME and associated TXT record else: # Process and delete CNAME record and associated TXT record - process_response=process_delete_records( + process_response = process_delete_records( route53, instance_id, cname_domain_suffix_id, @@ -999,9 +1000,9 @@ def lambda_handler( ) # only true if existing delete_records and the delete_success from the subroutine is true - delete_records=delete_records and process_response['delete_success'] + delete_records = delete_records and process_response['delete_success'] # append to the lsit - caller_response=caller_response + process_response['msg'] + caller_response = caller_response + process_response['msg'] # Clean up DynamoDB after deleting records if state != 'running': @@ -1025,8 +1026,8 @@ def lambda_handler( LOGGER.info(f"dns_data records written:\n{pformat(dns_data)}") - count['end']=datetime.datetime.now() - count['elapsed_ms']=(count['end'] - count['start']).total_seconds() * 1000.0 + count['end'] = datetime.datetime.now() + count['elapsed_ms'] = (count['end'] - count['start']).total_seconds() * 1000.0 LOGGER.info(f"{APPNAME} stats: source={event_source} state={state} " + ' '.join([f"{c}={count[c]}" for c in sorted(count.keys())])) return caller_response @@ -1044,7 +1045,7 @@ def get_cname_from_tags(tags): LOGGER.debug("tag: %s", str(tag)) if TAGKEY_CNAME.upper() in tag.get('Key', {}).lstrip().upper(): - cname=tag.get('Value').lstrip().lower() + cname = tag.get('Value').lstrip().lower() return cname return None @@ -1058,12 +1059,12 @@ def get_instances(client, instance_id): :return: """ - i=0 - instance_data={} + i = 0 + instance_data = {} while i < MAX_API_RETRY: try: - instance_data=client.describe_instances(InstanceIds=[instance_id]) + instance_data = client.describe_instances(InstanceIds=[instance_id]) LOGGER.debug("%s", str(instance_data) + lineno()) break except ClientError as err: @@ -1104,16 +1105,16 @@ def new_list_hosted_zones(client, instance_id): :return: """ - i=0 - hosted_zones={} + i = 0 + hosted_zones = {} # retry to handle errors in the possible API call while i < MAX_API_RETRY: try: - hosted_zones=client.list_hosted_zones() + hosted_zones = client.list_hosted_zones() LOGGER.debug("list_hosted_zones returned without error. %s", lineno()) break except ClientError as err: - error_message=str(err) + error_message = str(err) if "(Throttling)" in str(err): LOGGER.debug( "list_hosted_zones throttled due to API limit, retrying: %s", str(err) + lineno()) @@ -1133,12 +1134,12 @@ def new_list_hosted_zones(client, instance_id): instance_id, str(i) + 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_hosted_zones' - sns_msg['message']='list_hosted_zones 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_hosted_zones' + sns_msg['message'] = 'list_hosted_zones 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()) @@ -1159,18 +1160,18 @@ def new_list_hosted_zones_by_vpc(client, instance_id, vpc_id, region): :return: """ - i=0 - hosted_zones={} + i = 0 + hosted_zones = {} # retry to handle errors in the possible API call while i < MAX_API_RETRY: try: - hosted_zones=client.list_hosted_zones_by_vpc( + hosted_zones = client.list_hosted_zones_by_vpc( VPCId=vpc_id, VPCRegion=region) LOGGER.debug( "list_hosted_zones_by_vpc returned without error. %s", lineno()) break except ClientError as err: - error_message=str(err) + error_message = str(err) if "(Throttling)" in str(err): LOGGER.debug( "list_hosted_zones_by_vpc throttled due to API limit, retrying: %s", str(err) + lineno()) @@ -1190,14 +1191,14 @@ def new_list_hosted_zones_by_vpc(client, instance_id, vpc_id, region): vpc_id, instance_id, str(i) + lineno()) if SNS_ENABLE: try: - sns_msg={} - sns_msg['instance_id']=instance_id - sns_msg['vpc_id']=vpc_id - sns_msg['region']=region - sns_msg['account_id']=get_caller_account_id() - sns_msg['client']='route53' - sns_msg['boto3_method']='list_hosted_zones_by_vpc' - sns_msg['message']='list_hosted_zones_by_vpc timed out' + sns_msg = {} + sns_msg['instance_id'] = instance_id + sns_msg['vpc_id'] = vpc_id + sns_msg['region'] = region + sns_msg['account_id'] = get_caller_account_id() + sns_msg['client'] = 'route53' + sns_msg['boto3_method'] = 'list_hosted_zones_by_vpc' + sns_msg['message'] = 'list_hosted_zones_by_vpc timed out' publish_to_sns(get_sns_client(), json.dumps(sns_msg)) LOGGER.info("vpc_id: %s, instance: %s, sending sns message %s", vpc_id, instance_id, json.dumps(sns_msg) + lineno()) @@ -1274,7 +1275,7 @@ def get_item_from_dynamodb_table(client, table, instance_id): """ try: # Fetch item from DynamoDB - item=client.get_item( + item = client.get_item( TableName=table, Key={ 'InstanceId': { @@ -1290,16 +1291,16 @@ def get_item_from_dynamodb_table(client, table, instance_id): LOGGER.debug("returned item:" " %s", str(item['Item']['InstanceAttributes']['S']) + lineno()) - instance_attribute=item['Item']['InstanceAttributes']['S'] + instance_attribute = item['Item']['InstanceAttributes']['S'] # these 7 lines are handling for legacy DDB items created prior how items were written - instance_attribute=instance_attribute.replace("'", '"') - instance_attribute=instance_attribute.replace(" True,", ' "True",') - instance_attribute=instance_attribute.replace(" True}", ' "True"}') - instance_attribute=instance_attribute.replace(" True,", ' "True",') - instance_attribute=instance_attribute.replace(" False,", ' "False",') - instance_attribute=instance_attribute.replace(" False,", ' "False",') - instance_attribute=instance_attribute.replace(" False,", ' "False",') + instance_attribute = instance_attribute.replace("'", '"') + instance_attribute = instance_attribute.replace(" True,", ' "True",') + instance_attribute = instance_attribute.replace(" True}", ' "True"}') + instance_attribute = instance_attribute.replace(" True,", ' "True",') + instance_attribute = instance_attribute.replace(" False,", ' "False",') + instance_attribute = instance_attribute.replace(" False,", ' "False",') + instance_attribute = instance_attribute.replace(" False,", ' "False",') LOGGER.debug("item: %s", str(instance_attribute) + lineno()) return json.loads(instance_attribute) @@ -1316,7 +1317,7 @@ def get_private_hosted_zone_collection(private_hosted_zones): :return: """ try: - private_hosted_zone_collection=[] + private_hosted_zone_collection = [] for item in private_hosted_zones: LOGGER.debug("item: %s", str(item) + lineno()) @@ -1334,11 +1335,11 @@ def get_private_hosted_zone_collection_by_vpc(private_hosted_zones): :return: """ try: - private_hosted_zone_collection=[] + private_hosted_zone_collection = [] for item in private_hosted_zones: LOGGER.debug("item: %s", str(item) + lineno()) - my_item={ + my_item = { 'name': item['Name'], 'zone_id': item['HostedZoneId'], 'owner_account': item['Owner'].get('OwningAccount', ''), @@ -1359,7 +1360,7 @@ def get_private_hosted_zones(hosted_zones): :return: """ try: - private_hosted_zones=[] + private_hosted_zones = [] for item in hosted_zones['HostedZones']: LOGGER.debug("item: %s", str(item) + lineno()) @@ -1381,7 +1382,7 @@ def get_private_hosted_zones_by_vpc(hosted_zones): :return: """ try: - private_hosted_zones=[] + private_hosted_zones = [] for item in hosted_zones['HostedZoneSummaries']: LOGGER.debug("item: %s", str(item) + lineno()) @@ -1401,19 +1402,19 @@ def get_dhcp_option_set_id_for_vpc(client, instance_id, vpc_id): :return: """ - i=0 + i = 0 while i < MAX_API_RETRY: try: - option_sets={} - results=client.describe_vpcs() + option_sets = {} + results = client.describe_vpcs() for item in results['Vpcs']: if 'DhcpOptionsId' in item: - option_sets[str(item['VpcId'])]=item['DhcpOptionsId'] + option_sets[str(item['VpcId'])] = item['DhcpOptionsId'] else: - option_sets[str(item['VpcId'])]=None - option_set_for_vpc=option_sets[vpc_id] + option_sets[str(item['VpcId'])] = None + option_set_for_vpc = option_sets[vpc_id] LOGGER.debug("option set for vpc: %s", str(option_set_for_vpc) + lineno()) break @@ -1517,24 +1518,24 @@ def new_change_resource_recordset(oclient, instance_id, zone_id, host_name, host # 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 - update_response={} + i = 0 + update_response = {} LOGGER.debug("Creating %s record %s in zone %s: %s", record_type, host_name, hosted_zone_name, lineno()) @@ -1544,7 +1545,7 @@ def new_change_resource_recordset(oclient, instance_id, zone_id, host_name, host try: LOGGER.debug("Try %s Creating %s record %s in zone %s: %s", str( i), record_type, host_name, hosted_zone_name, lineno()) - change_batch={ + change_batch = { "Comment": f"Updated by {APPNAME} v{VERSION} from {account_id} in {region}", "Changes": [ { @@ -1565,7 +1566,7 @@ def new_change_resource_recordset(oclient, instance_id, zone_id, host_name, host LOGGER.debug("change_resource_record_sets change_batch: %s", json.dumps(change_batch) + lineno()) - update_response=client.change_resource_record_sets( + update_response = client.change_resource_record_sets( HostedZoneId=zone_id, ChangeBatch=change_batch ) @@ -1576,12 +1577,12 @@ def new_change_resource_recordset(oclient, instance_id, zone_id, host_name, host except ClientError as err: if 'NoSuchHostedZone' in str(err) and 'No hosted zone found with ID' in str(err): LOGGER.error("Hosted zone not found error: %s", str(err) + lineno()) - update_response="NoSuchHostedZone" + update_response = "NoSuchHostedZone" break elif 'InvalidChangeBatch' in str(err) and 'is not permitted in zone' in str(err): LOGGER.error( "Cannot create record - most likely wrong zone name specified: %s", str(err) + lineno()) - update_response="InvalidChangeBatch-WrongZoneName" + update_response = "InvalidChangeBatch-WrongZoneName" break elif "(Throttling)" in str(err): LOGGER.debug("change_resource_record_sets UPSERT throttled due to API limit, retrying: %s", str( @@ -1605,13 +1606,13 @@ def new_change_resource_recordset(oclient, instance_id, zone_id, host_name, host if update_response == {}: 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 UPSERT 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 UPSERT 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, @@ -1646,7 +1647,7 @@ def create_resource_record(client, instance_id, zone_id, host_name, hosted_zone_ # To prevent rate throttling # time.sleep(1) - create_response=new_change_resource_recordset( + create_response = new_change_resource_recordset( client, instance_id, zone_id, @@ -1658,19 +1659,19 @@ def create_resource_record(client, instance_id, zone_id, host_name, hosted_zone_ if create_response == 'NoSuchHostedZone': LOGGER.debug("DNS Record create failed: %s", str(create_response) + lineno()) - msg='NoSuchHostedZone: ' + str(create_response) + msg = 'NoSuchHostedZone: ' + str(create_response) elif create_response == 'InvalidChangeBatch-WrongZoneName': LOGGER.debug("DNS Record create failed: %s", str(create_response) + lineno()) - msg='InvalidChangeBatch-WrongZoneName: ' + str(create_response) + msg = 'InvalidChangeBatch-WrongZoneName: ' + str(create_response) elif create_response == {}: LOGGER.debug("DNS Record create failed: %s", str(create_response) + lineno()) - msg='DNS Recored Create Failed: ' + str(create_response) + msg = 'DNS Recored Create Failed: ' + str(create_response) else: LOGGER.debug("DNS Record create success: %s", str(create_response) + lineno()) - msg='success' + msg = 'success' LOGGER.debug("response: %s", str(create_response) + lineno()) return msg @@ -1738,24 +1739,24 @@ def new_get_resource_record(oclient, instance_id, zone_id, host_name, hosted_zon # 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 - value='' + i = 0 + value = '' while i < MAX_API_RETRY: try: @@ -1768,8 +1769,8 @@ 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( + fqdn = create_fqdn(host_name, hosted_zone_name) + response = client.list_resource_record_sets( HostedZoneId=zone_id, # StartRecordName=host_name + hosted_zone_name, StartRecordName=fqdn, @@ -1780,12 +1781,12 @@ def new_get_resource_record(oclient, instance_id, zone_id, host_name, hosted_zon json.dumps(response) + lineno()) for rr_set in response['ResourceRecordSets']: - rr_name=rr_set['Name'] + 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 == fqdn: - value=rr_set['ResourceRecords'][0]['Value'] + value = rr_set['ResourceRecords'][0]['Value'] LOGGER.debug( f"list_resource_record_sets returned value {value}: {lineno()}") else: @@ -1821,12 +1822,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()) @@ -1853,24 +1854,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()) @@ -1885,7 +1886,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": [ { @@ -1906,7 +1907,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 ) @@ -1919,16 +1920,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( @@ -1951,13 +1952,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, @@ -1978,11 +1979,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: @@ -1991,9 +1992,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 @@ -2013,8 +2014,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", @@ -2628,7 +2629,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 @@ -2674,7 +2675,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) ) @@ -2699,20 +2700,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, @@ -2723,25 +2724,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: @@ -2750,13 +2751,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, @@ -2767,7 +2768,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()) @@ -2776,13 +2777,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", @@ -2793,31 +2794,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()) @@ -2831,13 +2832,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, @@ -2847,7 +2848,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()) @@ -2856,13 +2857,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", @@ -2872,11 +2873,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", @@ -2884,19 +2885,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()) @@ -2906,8 +2907,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 @@ -2920,13 +2921,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 @@ -2942,7 +2943,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) @@ -2956,10 +2957,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) @@ -2971,8 +2972,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) @@ -2984,8 +2985,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) @@ -2997,8 +2998,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) @@ -3010,26 +3011,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: @@ -3055,10 +3056,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( @@ -3079,6 +3080,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 bc571b760c503858aa29881e0306cefcf08595a5..f5d77dcb09e23eab0aa51b728900717a1af5aa73 100644 GIT binary patch delta 21429 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 Uky8Kwr0bInR5Aw5Qvd(}0BIGR$N&HU delta 21481 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 U=>Y%^005KhQ!NJbQvd(}0IrDA#{d8T