Initial commit

This commit is contained in:
Nik Rozman
2023-07-24 22:02:37 +02:00
commit 3f29f7eaa6
6 changed files with 314 additions and 0 deletions

189
main.py Normal file
View File

@@ -0,0 +1,189 @@
import os
import requests
import json
import configparser
import schedule
import time
from backend.hetzner import HetznerBackend
from backend.ovh import OvhBackend
def compare_zone_records(saved_records, new_records, backend_type):
saved_records_dict = {}
for record in saved_records["rrsets"]:
saved_records_dict[record["name"]] = record["records"]
new_records_dict = {}
for record in new_records["rrsets"]:
new_records_dict[record["name"]] = record["records"]
if backend_type == "hetzner":
backend = HetznerBackend()
elif backend_type == "ovh":
backend = OvhBackend()
else:
print(f"Specified backend not supported: {backend_type}")
return
for name, new_records_list in new_records_dict.items():
if name not in saved_records_dict:
# Do create here
backend.create_record(name, new_records_list) # Pass the name along with the content
elif new_records_list != saved_records_dict[name]:
# Do update here
backend.update_record(name, new_records_list) # Pass the name along with the content
for name, saved_records_list in saved_records_dict.items():
if name not in new_records_dict:
# Do delete here
backend.delete_record(name) # Pass the name to the delete_record method
class PowerDNSProxy:
def __init__(self, config_file='config.ini', zones_file='zones.ini'):
self.api_url = None
self.api_key = None
self.config_file = config_file
self.freq = 5 # Default frequency
self.zones_with_backend = {} # Dictionary to store zones with their backend
self.zones_file = zones_file
# noinspection PyTypeChecker
def load_config(self):
config = configparser.ConfigParser()
config.read(self.config_file)
if "PowerDNS" not in config:
print("PowerDNS section not found in config.ini")
return False
else:
self.api_url = config["PowerDNS"]["api_endpoint"]
self.api_key = config["PowerDNS"]["api_key"]
if not self.api_url or not self.api_key:
print("API URL or API Key not provided. Please check the configuration.")
return False
self.freq = int(config["PowerDNS"].get("frequency", 5)) # Set the frequency from config
self.zones_with_backend = self.read_zones_from_config() # Call read_zones_from_config here
return True
def main(self):
if not self.load_config():
return
zones = self.get_zones()
if type(zones) is int:
print("Error getting zones: http code " + str(zones))
return
tracked_zones = {}
for zone in zones:
zone_name = zone["name"]
if zone_name in self.zones_with_backend: # Use self.zones_with_backend instead of config_zones
tracked_zones[zone_name] = {
"id": zone["id"],
"serial": zone["serial"],
"backend": self.zones_with_backend[zone_name]["backend"]
}
print("Zones tracked: " + str(len(tracked_zones)))
with open("states.json", "w") as f:
json.dump(tracked_zones, f, indent=4)
print("States saved to states.json")
# Define the scheduled job function
def scheduled_job():
self.check_zone_serial()
# Schedule the job to run every 'freq' seconds
schedule.every(self.freq).seconds.do(scheduled_job)
while True:
schedule.run_pending()
time.sleep(1)
def get_zones(self):
headers = {"X-API-Key": self.api_key}
response = requests.get(self.api_url + "/zones", headers=headers)
if response.status_code == 200:
return response.json()
else:
return response.status_code
def read_zones_from_config(self):
config = configparser.ConfigParser(allow_no_value=True)
config.read(self.zones_file)
zones_with_backend = {}
for section in config.sections():
backend = "default" # Initialize the backend variable
if "backend" in config[section]:
backend = config[section]["backend"].strip('"') # Remove any quotes around the backend value
zones_with_backend[section.strip()] = {"backend": backend}
return zones_with_backend
def get_zone_records(self, zone_id):
headers = {"X-API-Key": self.api_key}
response = requests.get(self.api_url + f"/zones/{zone_id}", headers=headers)
if response.status_code == 200:
return response.json()
else:
print(f"Error getting zone records: http code {response.status_code}")
return None
def save_zone_records(self, zone_data, backend):
zone_name = zone_data["name"]
zone_id = zone_data["id"]
records = self.get_zone_records(zone_id)
if records is not None:
states_dir = "states"
if not os.path.exists(states_dir):
os.makedirs(states_dir)
filename = os.path.join(states_dir, f"{zone_name}.json")
try:
with open(filename, "r") as f:
saved_records = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
print(f"File {filename} not found or invalid, creating new file")
saved_records = []
with open(filename, "w") as f:
json.dump(records, f, indent=4)
print(f"Zone records for {zone_name} saved to {filename}")
compare_zone_records(saved_records, records, backend)
else:
print(f"Error getting zone records for {zone_name}")
def check_zone_serial(self):
zones = self.get_zones()
# Open states.json
with open("states.json", "r") as f:
tracked_zones = json.load(f)
print("Checking zone serials from saved states")
for zone in zones:
if zone["name"] in tracked_zones:
if tracked_zones[zone["name"]]["serial"] != zone["serial"]:
print("Zone " + zone["name"] + " has changed!")
self.save_zone_records(zone, tracked_zones[zone["name"]]["backend"])
tracked_zones[zone["name"]]["serial"] = zone["serial"]
# Update the state.json file with the updated tracked_zones data
with open("states.json", "w") as f:
json.dump(tracked_zones, f, indent=4)
if __name__ == "__main__":
pdns_proxy = PowerDNSProxy()
pdns_proxy.main()