From f7112656291ba44c5017b24140071888ea4e7e5c Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Fri, 10 Jan 2025 04:25:06 +0100 Subject: [PATCH] UNTESTED: Support for syncing with openstack --- knotctl/__init__.py | 109 +++++++++++++++++++++++++++++++++++++++++++- requirements.txt | 1 + 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/knotctl/__init__.py b/knotctl/__init__.py index 4ad668a..9d6f197 100755 --- a/knotctl/__init__.py +++ b/knotctl/__init__.py @@ -12,6 +12,8 @@ from typing import Union from urllib.parse import urlparse import argcomplete +import openstack +import openstack.config.loader import requests import yaml from requests.models import HTTPBasicAuth @@ -43,6 +45,21 @@ def get_config(config_filename: str): return yaml.safe_load(fh.read()) +def get_openstack_addresses(name: str, cloud: str): + conn = openstack.connect(cloud=cloud) + + # List the servers + server = conn.compute.find_server(name) + if server is None: + print("Server not found") + exit(1) + openstack_addresses = [] + for network in server.addresses: + for address in network: + openstack_addresses.append(address) + return openstack_addresses + + def nested_out(input, tabs="") -> str: string = "" if isinstance(input, str) or isinstance(input, int): @@ -208,6 +225,84 @@ def run_list(url: str, output(string, jsonout) +def run_openstack_sync(cloud: str, name: str, zone: str, headers: dict, + baseurl: str, jsonout: bool): + url = setup_url( + baseurl, + zone=zone, + name=name, + ) + current_records = run_list(url, jsonout=True, headers=headers, ret=True) + openstack_addresses = get_openstack_addresses(cloud, name) + if current_records is None: + for address in openstack_addresses: + rtype = None + if address.version == 4: + rtype = "A" + elif address.version == 6: + rtype = "AAAA" + if rtype: + url = setup_url(baseurl, + zone=zone, + name=name, + rtype=rtype, + data=address.addr) + run_add(url, jsonout, headers) + else: + previpv4, previpv6, curripv4, curripv6 = False + for record in current_records: + if record["type"] == "A": + previpv4 = record["data"] + elif record["type"] == "AAAA": + previpv6 = record["data"] + for address in openstack_addresses: + rtype = None + if address.version == 4: + rtype = "A" + curripv4 = True + elif address.version == 6: + rtype = "AAAA" + curripv6 = True + if rtype and record["type"] == rtype: + if record["data"] == address.addr: + continue + else: + url = setup_url(baseurl, + zone=zone, + name=name, + rtype=record["type"], + data=address.addr) + run_update(url, jsonout, headers) + if previpv4 and not curripv4: + url = setup_url(baseurl, + zone=zone, + name=name, + rtype="A", + data=previpv4) + run_delete(url, jsonout, headers) + if previpv6 and not curripv6: + url = setup_url(baseurl, + zone=zone, + name=name, + rtype="AAAA", + data=previpv4) + run_delete(url, jsonout, headers) + if curripv4 and not previpv4: + url = setup_url(baseurl, + zone=zone, + name=name, + rtype="A", + data=curripv4) + run_add(url, jsonout, headers) + if curripv6 and not previpv6: + url = setup_url(baseurl, + zone=zone, + name=name, + rtype="AAAA", + data=curripv6) + run_add(url, jsonout, headers) + + def run_update(url: str, jsonout: bool, headers: dict): response = requests.patch(url, headers=headers) output(response.json(), jsonout) @@ -415,6 +510,13 @@ def get_parser() -> dict: listcmd.add_argument("-r", "--rtype") listcmd.add_argument("-z", "--zone", required=False) + openstack_description = "Sync records with openstack." + openstackcmd = subparsers.add_parser("openstack-sync", + description=openstack_description) + openstackcmd.add_argument("-n", "--name", required=True) + openstackcmd.add_argument("-c", "--cloud", required=True) + openstackcmd.add_argument("-z", "--zone", required=True) + user_description = "View user information." usercmd = subparsers.add_parser("user", description=user_description) usercmd.add_argument("-u", "--username", default=None) @@ -500,6 +602,9 @@ def run(url, args, headers, baseurl, parser, username): elif args.command == "zone": url = baseurl + "/zones" run_zone(url, args.json, headers) + elif args.command == "openstack-sync": + run_openstack_sync(args.cloud, args.name, args.zone, headers, + baseurl, args.json) else: parser.print_help(sys.stderr) return 2 @@ -564,7 +669,9 @@ def main() -> int: if args.command == "user": if args.username: user = args.username - if args.command in ["auditlog", "changelog", "user", "zone"]: + if args.command in [ + "auditlog", "changelog", "openstack-sync", "user", "zone" + ]: pass else: try: diff --git a/requirements.txt b/requirements.txt index 246f2bc..f8d007c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ argcomplete==2.0.0 pyyaml==6.0.1 requests==2.27.1 simplejson==3.17.6 +openstacksdk==4.2.0