diff --git a/knotctl/__init__.py b/knotctl/__init__.py index a768fd2..4a9e1e7 100755 --- a/knotctl/__init__.py +++ b/knotctl/__init__.py @@ -232,7 +232,8 @@ def run_list(url: str, def run_openstack_sync(cloud: str, name: str, zone: str, headers: dict, - baseurl: str, jsonout: bool): + baseurl: str, jsonout: bool, ipv4: bool, ipv6: bool): + # List DNS records for name url = setup_url( baseurl, None, # arguments, @@ -242,103 +243,72 @@ def run_openstack_sync(cloud: str, name: str, zone: str, headers: dict, None, # ttl, zone, ) - current_records = run_list(url, jsonout=True, headers=headers, ret=True) + response = run_list(url, jsonout=True, headers=headers, ret=True) + if isinstance(response, dict): + if response["Code"] == 404: + response = [] + else: + output(error(f'{name}: could not list records', 'API Error')) + sys.exit(1) + + # List of DNS records for name + dns_records = [] + for record in response: + rtype = record['rtype'] + rname = record['name'].rstrip('.') + raddr = record['data'] + if rtype not in ('A', 'AAAA'): + continue + if f'{name}.{zone}' != rname: + output(error(f'{rname}: not the DNS name asked for', 'API Error')) + sys.exit(1) + dns_records.append((rname, rtype, raddr)) + + # List instance addresses in openstack openstack_addresses = get_openstack_addresses(cloud, name) - if current_records["Code"] == 404: - 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, - None, # arguments, - address["addr"], # data, - name, - rtype, - None, # ttl, - zone, - ) - run_add(url, jsonout, headers) - else: - previpv4 = False - previpv6 = False - curripv4 = False - 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 recor.type == rtype: - if record.data == address.addr: - continue - else: - url = setup_url( - baseurl, - None, # arguments, - address.addr, # data, - name, - record.type, - None, # ttl, - zone, - ) - run_update(url, jsonout, headers) - if previpv4 and not curripv4: + + # Add missing DNS records + for address in openstack_addresses: + rtype = None + if address["version"] == 4 and ipv4: + rtype = "A" + elif address["version"] == 6 and ipv6: + rtype = "AAAA" + if not rtype: + continue + + raddr = address['addr'] + + wanted_record = (f'{name}.{zone}', rtype, raddr) + + if wanted_record in dns_records: + dns_records.remove(wanted_record) + else: url = setup_url( baseurl, None, # arguments, - previpv4, # data, + raddr, # data, name, - "A", - None, # ttl, - zone, - ) - run_delete(url, jsonout, headers) - if previpv6 and not curripv6: - url = setup_url( - baseurl, - None, # arguments, - previpv6, # data, - name, - "AAAA", - None, # ttl, - zone, - ) - run_delete(url, jsonout, headers) - if curripv4 and not previpv4: - url = setup_url( - baseurl, - None, # arguments, - curripv4, # data, - name, - "A", - None, # ttl, - zone, - ) - run_add(url, jsonout, headers) - if curripv6 and not previpv6: - url = setup_url( - baseurl, - None, # arguments, - curripv6, # data, - name, - "AAAA", + rtype, # rtype, None, # ttl, zone, ) run_add(url, jsonout, headers) + # Remove obsolete DNS records + for record in dns_records: + rname, rtype, raddr = record + + url = setup_url( + baseurl, + None, # arguments, + raddr, # data, + name, + rtype, # rtype, + None, # ttl, + zone, + ) + run_delete(url, jsonout, headers) def run_update(url: str, jsonout: bool, headers: dict): response = requests.patch(url, headers=headers) @@ -557,6 +527,8 @@ def get_parser() -> dict: openstackcmd.add_argument("-n", "--name", required=True) openstackcmd.add_argument("-c", "--cloud", required=True) openstackcmd.add_argument("-z", "--zone", required=True) + openstackcmd.add_argument("--no-ipv4", action='store_true') + openstackcmd.add_argument("--no-ipv6", action='store_true') user_description = "View user information." usercmd = subparsers.add_parser("user", description=user_description) @@ -645,7 +617,8 @@ def run(url, args, headers, baseurl, parser, username): run_zone(url, args.json, headers) elif args.command == "openstack-sync": run_openstack_sync(args.cloud, args.name, args.zone, headers, - baseurl, args.json) + baseurl, args.json, + args.no_ipv4 is False, args.no_ipv6 is False) else: parser.print_help(sys.stderr) return 2