Initial commit
This commit is contained in:
189
main.py
Normal file
189
main.py
Normal 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()
|
||||
Reference in New Issue
Block a user