Add OVH support
This commit is contained in:
12
README.md
12
README.md
@@ -53,7 +53,17 @@ To get credentials, go to this link here: https://robot.hetzner.com/preferences/
|
|||||||
|
|
||||||
#### OVH
|
#### OVH
|
||||||
|
|
||||||
- Work in progress
|
- Supports creating, updating, and deleting DNS records using the OVH API.
|
||||||
|
|
||||||
|
#### Credentials
|
||||||
|
To get credentials, go to this link here: https://github.com/ovh/php-ovh#supported-endpoints, pick the right endpoint and go to the right **/createApp/** application credentials page.
|
||||||
|
The application needs to have the following rights:
|
||||||
|
- GET /ip
|
||||||
|
- GET /ip/*
|
||||||
|
- POST /ip/*
|
||||||
|
- DELETE /ip/*
|
||||||
|
|
||||||
|
Don't forget to set the right endpoint region in the config file.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import configparser
|
|
||||||
import requests
|
|
||||||
|
|
||||||
|
|
||||||
def reverse_ip_to_standard(reverse_ip):
|
|
||||||
parts = reverse_ip.split(".")
|
|
||||||
parts = parts[:4]
|
|
||||||
standard_ip = ".".join(reversed(parts))
|
|
||||||
|
|
||||||
return standard_ip
|
|
||||||
|
|
||||||
|
|
||||||
class OvhBackend:
|
|
||||||
def __init__(self, config_file='config.ini'):
|
|
||||||
self.config_file = config_file
|
|
||||||
self.username = None
|
|
||||||
self.password = None
|
|
||||||
|
|
||||||
self.load_credentials()
|
|
||||||
|
|
||||||
def load_credentials(self):
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(self.config_file)
|
|
||||||
|
|
||||||
if "OVH" not in config:
|
|
||||||
raise ValueError("OVH section not found in config.ini")
|
|
||||||
|
|
||||||
self.username = config["OVH"].get("username")
|
|
||||||
self.password = config["OVH"].get("password")
|
|
||||||
|
|
||||||
if not self.username or not self.password:
|
|
||||||
raise ValueError("OVH username or password not provided in config.ini")
|
|
||||||
|
|
||||||
def update_record(self, reverse_ip, record):
|
|
||||||
# Do update record stuff here
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_record(self, reverse_ip, record):
|
|
||||||
# Do create record stuff here
|
|
||||||
pass
|
|
||||||
|
|
||||||
def delete_record(self, reverse_ip):
|
|
||||||
# Do delete record stuff here
|
|
||||||
pass
|
|
||||||
113
backend/ovh_backend.py
Normal file
113
backend/ovh_backend.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import configparser
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
import ovh
|
||||||
|
import json
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
|
|
||||||
|
def reverse_ip_to_standard(reverse_ip):
|
||||||
|
parts = reverse_ip.split(".")
|
||||||
|
parts = parts[:4]
|
||||||
|
standard_ip = ".".join(reversed(parts))
|
||||||
|
|
||||||
|
return standard_ip
|
||||||
|
|
||||||
|
|
||||||
|
def ip_to_block(ip_addr, ip_blocks):
|
||||||
|
# Remove IPv6 blocks from the list
|
||||||
|
ip_blocks = [block for block in ip_blocks if ":" not in block]
|
||||||
|
ip_addr = ipaddress.ip_address(ip_addr) # Convert ip_addr to an IPv4Address object
|
||||||
|
for block in ip_blocks:
|
||||||
|
network = ipaddress.ip_network(block)
|
||||||
|
if ip_addr in network:
|
||||||
|
print("IP " + str(ip_addr) + " belongs to " + block)
|
||||||
|
return block
|
||||||
|
|
||||||
|
|
||||||
|
class OvhBackend:
|
||||||
|
def __init__(self, config_file='config.ini'):
|
||||||
|
self.client = None
|
||||||
|
self.config_file = config_file
|
||||||
|
self.load_credentials()
|
||||||
|
|
||||||
|
def load_credentials(self):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(self.config_file)
|
||||||
|
|
||||||
|
if "OVH" not in config:
|
||||||
|
raise ValueError("OVH section not found in config.ini")
|
||||||
|
|
||||||
|
endpoint = config["OVH"]["endpoint"]
|
||||||
|
application_key = config["OVH"]["application_key"]
|
||||||
|
application_secret = config["OVH"]["application_secret"]
|
||||||
|
consumer_key = config["OVH"]["consumer_key"]
|
||||||
|
|
||||||
|
if not endpoint or not application_key or not application_secret or not consumer_key:
|
||||||
|
raise ValueError("OVH credentials not provided in config.ini")
|
||||||
|
|
||||||
|
self.client = ovh.Client(
|
||||||
|
endpoint=endpoint,
|
||||||
|
application_key=application_key,
|
||||||
|
application_secret=application_secret,
|
||||||
|
consumer_key=consumer_key
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_record(self, reverse_ip, record):
|
||||||
|
# Check if the record contains the necessary data
|
||||||
|
if not record or "content" not in record[0]:
|
||||||
|
print("Invalid record data. 'content' field is missing.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
domain = record[0]["content"].strip(".")
|
||||||
|
|
||||||
|
ip_blocks = self.client.get("/ip")
|
||||||
|
ip_blocks = json.dumps(ip_blocks)
|
||||||
|
ip_blocks = json.loads(ip_blocks)
|
||||||
|
|
||||||
|
ip = reverse_ip_to_standard(reverse_ip)
|
||||||
|
block = ip_to_block(ip, ip_blocks)
|
||||||
|
|
||||||
|
if block is None:
|
||||||
|
print("Error: no block found for IP " + ip)
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Updating RDNS record for: " + ip + " in block " + block + " to: " + record[0]["content"])
|
||||||
|
block = urllib.parse.quote(block, safe="")
|
||||||
|
path = "/ip/" + block + "/reverse"
|
||||||
|
try:
|
||||||
|
self.client.post(f"{path}",
|
||||||
|
ipReverse=ip,
|
||||||
|
reverse=domain,
|
||||||
|
)
|
||||||
|
except ovh.exceptions.APIError as e:
|
||||||
|
print("Error: " + str(e))
|
||||||
|
else:
|
||||||
|
print("Updated RDNS record for: " + ip)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_record(self, reverse_ip, record):
|
||||||
|
self.update_record(reverse_ip, record)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete_record(self, reverse_ip):
|
||||||
|
ip_blocks = self.client.get("/ip")
|
||||||
|
ip_blocks = json.dumps(ip_blocks)
|
||||||
|
ip_blocks = json.loads(ip_blocks)
|
||||||
|
|
||||||
|
ip = reverse_ip_to_standard(reverse_ip)
|
||||||
|
block = ip_to_block(ip, ip_blocks)
|
||||||
|
|
||||||
|
if block is None:
|
||||||
|
print("Error: no block found for ip " + ip)
|
||||||
|
return
|
||||||
|
|
||||||
|
result = self.client.delete(f"/ip/{ip}/reverse" + block)
|
||||||
|
if result.status_code == 200:
|
||||||
|
print("Deleted RDNS record for: " + ip)
|
||||||
|
return result.json()
|
||||||
|
else:
|
||||||
|
print(f"Failed to delete rDNS data. Status code: {result.status_code}")
|
||||||
|
print(result.json())
|
||||||
|
return None
|
||||||
|
pass
|
||||||
10
config.ini
10
config.ini
@@ -4,9 +4,11 @@ api_key=x
|
|||||||
frequency=5
|
frequency=5
|
||||||
|
|
||||||
[Hetzner]
|
[Hetzner]
|
||||||
username=x
|
username=#ws+yimPZrpr
|
||||||
password=x
|
password=BTJOxx@Cfk&\&!!5
|
||||||
|
|
||||||
[OVH]
|
[OVH]
|
||||||
username=x
|
endpoint=ovh-ca
|
||||||
password=x
|
application_key=x
|
||||||
|
application_secret=x
|
||||||
|
consumer_key=x
|
||||||
6
main.py
6
main.py
@@ -4,8 +4,8 @@ import json
|
|||||||
import configparser
|
import configparser
|
||||||
import schedule
|
import schedule
|
||||||
import time
|
import time
|
||||||
from backend.hetzner import HetznerBackend
|
from backend.hetzner_backend import HetznerBackend
|
||||||
from backend.ovh import OvhBackend
|
from backend.ovh_backend import OvhBackend
|
||||||
|
|
||||||
|
|
||||||
def compare_zone_records(saved_records, new_records, backend_type):
|
def compare_zone_records(saved_records, new_records, backend_type):
|
||||||
@@ -19,8 +19,10 @@ def compare_zone_records(saved_records, new_records, backend_type):
|
|||||||
|
|
||||||
if backend_type == "hetzner":
|
if backend_type == "hetzner":
|
||||||
backend = HetznerBackend()
|
backend = HetznerBackend()
|
||||||
|
print("Using Hetzner backend")
|
||||||
elif backend_type == "ovh":
|
elif backend_type == "ovh":
|
||||||
backend = OvhBackend()
|
backend = OvhBackend()
|
||||||
|
print("Using OVH backend")
|
||||||
else:
|
else:
|
||||||
print(f"Specified backend not supported: {backend_type}")
|
print(f"Specified backend not supported: {backend_type}")
|
||||||
return
|
return
|
||||||
|
|||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Reference in New Issue
Block a user