diff --git a/Dockerfile b/Dockerfile index d79fb1f..3b7c0ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,4 +8,4 @@ COPY helper.py /automatic-dns-failover RUN pip install requests -CMD ["python3", "-u", "main.py"] +CMD ["python3", "-u", "main.py"] \ No newline at end of file diff --git a/cloudflare_dns.py b/cloudflare_dns.py index fa3716e..0b459ae 100755 --- a/cloudflare_dns.py +++ b/cloudflare_dns.py @@ -51,7 +51,8 @@ def add_record(ip_addr, subdomain, zone_id): payload = { "content": f"{ip_addr}", "name": subdomain, - "type": "A" + "type": "A", + "comment": "Managed by automatic-dns-failover project" } headers = { @@ -61,11 +62,10 @@ def add_record(ip_addr, subdomain, zone_id): response = requests.request("POST", url, json=payload, headers=headers).json() return response["success"] - def find_zones_under_account(): # returns a dictionary of domain name: zone_id - + url = "https://api.cloudflare.com/client/v4/zones" headers = { @@ -75,7 +75,7 @@ def find_zones_under_account(): response = requests.request("GET", url, headers=headers).json() if response["success"]: - domain_dict = {} + domain_zone_id = {} count = response["result_info"]["count"] per_page = response["result_info"]["per_page"] total_pages = int(count/per_page) + (count%per_page>0) @@ -83,6 +83,6 @@ def find_zones_under_account(): response = requests.request("GET", url+f"?page={i}", headers=headers).json() result = response["result"] for j in range(len(result)): - domain_dict[result[j]["name"]] = result[j]["id"] + domain_zone_id[result[j]["name"]] = result[j]["id"] - return domain_dict + return domain_zone_id \ No newline at end of file diff --git a/helper.py b/helper.py index cb2b46d..5faa01d 100755 --- a/helper.py +++ b/helper.py @@ -36,46 +36,46 @@ def within_range(status_code_range, status_code): return False def string_match(string1, string2): - return string1==string2 + return string1==string2 or string1 == "random_string" -def monitor(subdomain, FQDN, ip_addr_list, uptime, downtime, recorded, zone_id): - for i in range(len(ip_addr_list)): +def monitor(subdomain, FQDN, record_list, uptime, downtime, recorded, zone_id): + for i in range(len(record_list)): try: - dns_cache[FQDN] = ip_addr_list[i]["ip_address"] - # TODO: add path in the URL - response = requests.get(f"http://{FQDN}:{ip_addr_list[i]['port']}", timeout=get_timeout) - # TODO: make default range an environment variable - status_code_range = ip_addr_list[i]["status_code_range"] if ip_addr_list[i].get("status_code_range") else [[200, 299]] - if not within_range(status_code_range, int(response.status_code)) or not string_match(ip_addr_list[i]["match_string"], response.text): + dns_cache[FQDN] = record_list[i]["ip_address"] + response = requests.get(f"http://{FQDN}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')}", timeout=get_timeout) + status_code_range = record_list[i].get("status_code_range", [[200,299]]) + if not within_range(status_code_range, int(response.status_code)) or not string_match(record_list[i].get("match_string", "random_string"), response.text): raise requests.RequestException() - print(f"{FQDN}: {ip_addr_list[i]['ip_address']} with port {ip_addr_list[i]['port']} and path {ip_addr_list[i]['path']} UP") + + # node is up + print(f"{record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} UP") uptime[i] += 1 downtime[i] = 0 if not recorded[i] and uptime[i] >= up_threshold: # add record to cloudflare dns zone - success = add_record(ip_addr_list[i]["ip_address"], subdomain, zone_id) + success = add_record(record_list[i]['ip_address'], subdomain, zone_id) if success: - print(f"{FQDN}: {ip_addr_list[i]['ip_address']} ADD SUCCESSFUL") + print(f"{record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} ADD SUCCESSFUL") recorded[i] = True else: - print(f"{FQDN}: {ip_addr_list[i]['ip_address']} ADD FAILED") + print(f"{record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} ADD FAILED") uptime[i] == 0 except: - # failed connection (node is potentially down) - print(f"{FQDN}: {ip_addr_list[i]['ip_address']} NO ANSWER") + # node is not right + print(f"{record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} NO ANSWER") uptime[i] = 0 downtime[i] += 1 if downtime[i] == down_threshold and recorded[i]: - # find dns id of the host - record_id, found = find_record(ip_addr_list[i]["ip_address"], FQDN, zone_id) + # node is down + record_id, found = find_record(record_list[i]['ip_address'], FQDN, zone_id) if found: # delete record from cloudflare dns zone success = delete_record(record_id, zone_id) if success: recorded[i] = False - print(f"{FQDN}: {ip_addr_list[i]['ip_address']} DELETE SUCCESSFUL") + print(f"{record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} DELETE SUCCESSFUL") else: - print(f"{FQDN}: ATTENTION! couldn't delete {ip_addr_list[i]['ip_address']} with its record id") + print(f"{record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} DELETE FAILED") else: - print(f"{FQDN}: record id of {ip_addr_list[i]['ip_address']} cannot be found on cloudflare") - downtime[i] = 0 + print(f"{record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} not found on cloudflare") + downtime[i] = 0 \ No newline at end of file diff --git a/main.py b/main.py index 29de541..be668f7 100755 --- a/main.py +++ b/main.py @@ -11,26 +11,29 @@ cloudflare_refresh_period_ticks = int(os.environ["CLOUDFLARE_REFRESH_PERIOD_TICKS"]) # dictionary of domain to zone_id -domain_dict = find_zones_under_account() +domain_zone_id = find_zones_under_account() -# check domain_subdomain_ips -# print(json.dumps(domain_subdomain_ips, indent=2)) -print(domain_dict) +print(domain_subdomain_ips) +print(domain_zone_id) + +# this delay is needed because of the overlapping existence of a terminating container/pod and a new container/pod +time.sleep(30) # initialization of variables uptime = {} downtime = {} recorded = {} -for domain in domain_subdomain_ips["domains"]: +for j, domain in enumerate(domain_subdomain_ips["domains"]): domain_name = domain["domain_name"] - subdomains = domain_subdomain_ips["domains"][domain_name]["subdomains"] + subdomains = domain_subdomain_ips["domains"][j]["subdomains"] for subdomain in subdomains: subdomain_name = subdomain["subdomain_name"] FQDN = subdomain_name + '.' + domain_name - ip_addr_list = subdomain["ip_addresses"] - uptime[FQDN] = [0] * len(ip_addr_list) - downtime[FQDN] = [0] * len(ip_addr_list) - recorded[FQDN] = [find_record(ip_addr_list[i], FQDN, domain_dict[domain_name])[1] for i in range(len(ip_addr_list))] + record_list = subdomain["records"] + uptime[FQDN] = [0] * len(record_list) + downtime[FQDN] = [0] * len(record_list) + recorded[FQDN] = [find_record(record_list[i]["ip_address"], FQDN, domain_zone_id[domain_name])[1] for i in range(len(record_list))] + print(f"{FQDN} status: {recorded[FQDN]}") # infinite loop count = 0 @@ -41,22 +44,22 @@ # will add the record back up if someone manually deletes it on cloudflare by accident count += 1 if count == cloudflare_refresh_period_ticks: - for domain in domain_subdomain_ips["domains"]: + for j, domain in enumerate(domain_subdomain_ips["domains"]): domain_name = domain["domain_name"] - subdomains = domain_subdomain_ips["domains"][domain_name]["subdomains"] + subdomains = domain_subdomain_ips["domains"][j]["subdomains"] for subdomain in subdomains: subdomain_name = subdomain["subdomain_name"] FQDN = subdomain_name + '.' + domain_name - ip_addr_list = subdomain["ip_addresses"] - recorded[FQDN] = [find_record(ip_addr_list[i], FQDN, domain_dict[domain_name])[1] for i in range(len(ip_addr_list))] + record_list = subdomain["records"] + recorded[FQDN] = [find_record(record_list[i]["ip_address"], FQDN, domain_zone_id[domain_name])[1] for i in range(len(record_list))] count = 0 # actual work - for domain in domain_subdomain_ips["domains"]: + for j, domain in enumerate(domain_subdomain_ips["domains"]): domain_name = domain["domain_name"] - subdomains = domain_subdomain_ips["domains"][domain_name]["subdomains"] + subdomains = domain_subdomain_ips["domains"][j]["subdomains"] for subdomain in subdomains: subdomain_name = subdomain["subdomain_name"] FQDN = subdomain_name + '.' + domain_name - ip_addr_list = subdomain["ip_addresses"] - monitor(subdomain_name, FQDN, ip_addr_list, uptime[FQDN], downtime[FQDN], recorded[FQDN], domain_dict[domain_name]) + record_list = subdomain["records"] + monitor(subdomain_name, FQDN, record_list, uptime[FQDN], downtime[FQDN], recorded[FQDN], domain_zone_id[domain_name]) \ No newline at end of file diff --git a/parsing.py b/parsing.py deleted file mode 100644 index 76c8b9a..0000000 --- a/parsing.py +++ /dev/null @@ -1,9 +0,0 @@ -import json, os, time - -json_string = os.environ["HOSTS"] - -json_object = json.loads(json_string) -formatted_json_string = json.dumps(json_object, indent=2) -print(formatted_json_string) - -time.sleep(20) diff --git a/test.py b/test.py deleted file mode 100644 index bab8d4e..0000000 --- a/test.py +++ /dev/null @@ -1,6 +0,0 @@ -import requests - -response = requests.request("GET", "http://10.0.50.114:8080") -print(type(response.text)) # str -print(response.text) -print(response.status_code)