Skip to content

Commit

Permalink
message
Browse files Browse the repository at this point in the history
  • Loading branch information
Feinburger committed Apr 29, 2024
1 parent c33ba59 commit 32cab31
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 52 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ __pycache__
*.swp
TODO
*.json
parsing.py
test.py
32 changes: 18 additions & 14 deletions cloudflare_dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import requests, json, os

def find_record(ip_addr, FQDN, zone_id):
# finds the record with the corresponding ip_addr and
# returns the record id and the status of the operation
# notice that the parameter FQDN is not actually necessary
# finds the record specified by the zone id and ip address and
# returns the record id and status of the operation

url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records"

headers = {
Expand All @@ -17,7 +17,6 @@ def find_record(ip_addr, FQDN, zone_id):
found = False
record_id = ""
response = requests.request("GET", url, headers=headers).json()

if response["success"]:
count = response["result_info"]["count"]
per_page = response["result_info"]["per_page"]
Expand All @@ -33,8 +32,9 @@ def find_record(ip_addr, FQDN, zone_id):
return (record_id, found)

def delete_record(record_id, zone_id):
# deletes the record with record_id

# deletes a record specified by the record id and zone id and
# returns the status of the operation

url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{record_id}"

headers = {
Expand All @@ -46,12 +46,16 @@ def delete_record(record_id, zone_id):
return response["success"]

def add_record(ip_addr, subdomain, zone_id):
# adds a DNS A record to the zone and
# returns the status of the operation

url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records"

payload = {
"content": f"{ip_addr}",
"name": subdomain,
"type": "A"
"type": "A",
"comment": "Managed by automatic-dns-failover project"
}

headers = {
Expand All @@ -60,12 +64,12 @@ def add_record(ip_addr, subdomain, zone_id):
}

response = requests.request("POST", url, json=payload, headers=headers).json()
return response["success"]

return response["success"]

def find_zones_under_account():
# returns a dictionary of domain name: zone_id

# traverses all DNS zones under the account and
# returns a dictionary of domain name to zone id

url = "https://api.cloudflare.com/client/v4/zones"

headers = {
Expand All @@ -75,14 +79,14 @@ def find_zones_under_account():

response = requests.request("GET", url, headers=headers).json()
if response["success"]:
domain_dict = {}
domain_to_zone_id = {}
count = response["result_info"]["count"]
per_page = response["result_info"]["per_page"]
total_pages = int(count/per_page) + (count%per_page>0)
for i in range(1, total_pages+1):
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_to_zone_id[result[j]["name"]] = result[j]["id"]

return domain_dict
return domain_to_zone_id
37 changes: 18 additions & 19 deletions helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

dns_cache = {}

# override the default dns resolver that requests.get calls under the hood
# override the default dns resolver that requests.get calls
prv_getaddrinfo = socket.getaddrinfo
def new_getaddrinfo(*args):
if args[0] in dns_cache:
Expand All @@ -23,6 +23,7 @@ def get_hosts_from_env():
return json.loads(os.environ["HOSTS"])

def within_range(status_code_range, status_code):
# checks whether the status code is within the specified range
for code_or_range in status_code_range:
if isinstance(code_or_range, int):
if status_code == code_or_range:
Expand All @@ -36,46 +37,44 @@ 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")
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")
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)
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")
print(f"record id of {record_list[i]['ip_address']}:{record_list[i].get('port', 80)}{record_list[i].get('path', '/')} cannot be found on cloudflare")
downtime[i] = 0
40 changes: 21 additions & 19 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,30 @@
cloudflare_refresh_period_ticks = int(os.environ["CLOUDFLARE_REFRESH_PERIOD_TICKS"])

# dictionary of domain to zone_id
domain_dict = find_zones_under_account()
domain_to_zone_id = find_zones_under_account()

# check domain_subdomain_ips
# print(json.dumps(domain_subdomain_ips, indent=2))
print(domain_dict)
print(domain_to_zone_id)
print(domain_subdomain_ips)
print("printed both environment variables")
time.sleep(10)

# initialization of variables
uptime = {}
downtime = {}
recorded = {}
for domain in domain_subdomain_ips["domains"]:
for domain_index, 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"][domain_index]["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_to_zone_id[domain_name])[1] for i in range(len(record_list))]
print(f"{FQDN} is {recorded[FQDN]}")

# infinite loop
count = 0
while True:
time.sleep(delay)
Expand All @@ -41,22 +43,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 domain_index, 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"][domain_index]["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_to_zone_id[domain_name])[1] for i in range(len(record_list))]
count = 0

# actual work
for domain in domain_subdomain_ips["domains"]:
# checking health and perform DELETE/ADD when appropriate
for domain_index, 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"][domain_index]["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_to_zone_id[domain_name])

0 comments on commit 32cab31

Please sign in to comment.