166 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env python3
 | |
| # -*- coding: utf-8 -*-
 | |
| import argparse
 | |
| import os
 | |
| import sys
 | |
| import json
 | |
| import requests
 | |
| 
 | |
| import django
 | |
| 
 | |
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dnmgmt.settings")
 | |
| BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 | |
| sys.path.append(BASE_DIR)
 | |
| django.setup()
 | |
| 
 | |
| from django.db.models import Count
 | |
| from domains.models import Domain, ReverseZone
 | |
| 
 | |
| 
 | |
| __VERSION__ = '0.1'
 | |
| 
 | |
| 
 | |
| def _parser():
 | |
| 	parser = argparse.ArgumentParser(
 | |
| 	             #prog='foo',
 | |
| 	             #description='do some awesome foo',
 | |
| 	)
 | |
| 
 | |
| 	#parser.add_argument("-p", "--port", default=2323, type=int, help="Your port")
 | |
| 	#parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Be more verbose")
 | |
| 
 | |
| 	parser.add_argument("--version", action="version", version="%(prog)s " + __VERSION__)
 | |
| 
 | |
| 	return parser
 | |
| 
 | |
| def mergeDomains(zoneData, pdnsData):
 | |
| 	rrAdd = []
 | |
| 	rrData = pdnsData['rrsets']
 | |
| 
 | |
| 	for domain, rrType, records in zoneData:
 | |
| 		found = False
 | |
| 		pdnsDom = list(filter(lambda _x: _x['name'] == domain and _x['type'] == rrType, rrData))
 | |
| 		if len(pdnsDom) > 0:
 | |
| 			rrSet = set(_x['content'] for _x in pdnsDom[0]['records'])
 | |
| 			if rrSet == set(records):
 | |
| 				found = True
 | |
| 
 | |
| 		if not found:
 | |
| 			# new domain!
 | |
| 			rrAdd.append({
 | |
| 				"name": domain,
 | |
| 				"type": rrType,
 | |
| 				"ttl": 60*60,
 | |
| 				"changetype": "REPLACE",
 | |
| 				"records": [{"content": record, "disabled": False} for record in records],
 | |
| 			})
 | |
| 
 | |
| 	return rrAdd
 | |
| 
 | |
| 
 | |
| def removeOldDomains(zoneData, pdnsData):
 | |
| 	rrDel = []
 | |
| 
 | |
| 	for entry in pdnsData['rrsets']:
 | |
| 		# search for name/type in domain dict. if non-existtant mark for deletion
 | |
| 		# this could be much more efficient with a dict! name: [rrset...]
 | |
| 		if not any(entry['name'] == _x[0] and entry['type'] == _x[1] for _x in zoneData):
 | |
| 			rrDel.append({
 | |
| 				"changetype": "DELETE",
 | |
| 				"name": entry["name"],
 | |
| 				"type": entry["type"],
 | |
| 			})
 | |
| 
 | |
| 	return rrDel
 | |
| 
 | |
| def handleNameserver(ns, servers, usedNameservers, domains):
 | |
| 	servers.append(ns.name)
 | |
| 
 | |
| 	if ns.name not in usedNameservers and (ns.glueIPv4 or ns.glueIPv6):
 | |
| 		usedNameservers.append(ns.name)
 | |
| 		if ns.glueIPv4:
 | |
| 			domains.append((ns.name, "A", [ns.glueIPv4]))
 | |
| 		if ns.glueIPv6:
 | |
| 			domains.append((ns.name, "AAAA", [ns.glueIPv6]))
 | |
| 
 | |
| 
 | |
| def getDomainsFromQueryset(qs, zone, glueRecords, usedNameservers):
 | |
| 	for domain in qs:
 | |
| 		servers = []
 | |
| 		for ns in domain.nameservers.all():
 | |
| 			servers.append(ns.name)
 | |
| 
 | |
| 			if ns.name not in usedNameservers and (ns.glueIPv4 or ns.glueIPv6):
 | |
| 				usedNameservers.append(ns.name)
 | |
| 				if ns.glueIPv4:
 | |
| 					glueRecords.append((ns.name, "A", [ns.glueIPv4]))
 | |
| 				if ns.glueIPv6:
 | |
| 					glueRecords.append((ns.name, "AAAA", [ns.glueIPv6]))
 | |
| 
 | |
| 		zone.append((domain.getZone(), "NS", servers))
 | |
| 
 | |
| def mergeDomainsWithPdns(s, args, zone, zoneData, protectedRecords=[]):
 | |
| 	url = "http://potatos.portal.dn:8081/api/v1/servers/localhost/zones/%s" % (zone,)
 | |
| 	pdnsData = s.get(url).json()
 | |
| 
 | |
| 	# add dn. (NS + glue Nameservers)
 | |
| 	newDomains = mergeDomains(zoneData, pdnsData)
 | |
| 	print("Add/replace", newDomains)
 | |
| 
 | |
| 	if len(newDomains) > 0:
 | |
| 		r = s.patch(url, data=json.dumps({'rrsets': newDomains}))
 | |
| 		if r.status_code != 204:
 | |
| 			raise RuntimeError("Could not update records in powerdns")
 | |
| 
 | |
| 	delDomains = removeOldDomains(zoneData, zoneData + protectedRecords)
 | |
| 	print("Del", delDomains)
 | |
| 	r = s.patch(url, data=json.dumps({'rrsets': delDomains}))
 | |
| 	if r.status_code != 204:
 | |
| 		raise RuntimeError("Could not update records in powerdns")
 | |
| 
 | |
| 
 | |
| def main():
 | |
| 	parser = _parser()
 | |
| 	args = parser.parse_args()
 | |
| 
 | |
| 	s = requests.Session()
 | |
| 	s.headers = {
 | |
| 		'X-API-Key': 'bei2aequ2OBi',
 | |
| 		'Accept': 'application/json'
 | |
| 	}
 | |
| 
 | |
| 	domains = []
 | |
| 	rzone4 = []
 | |
| 	rzone6 = []
 | |
| 	usedNameservers = []
 | |
| 
 | |
| 	# assenble domain data
 | |
| 	# dn.
 | |
| 	qs = Domain.objects.annotate(nsCount=Count('nameservers')).filter(nsCount__gt=0).order_by("name")
 | |
| 	getDomainsFromQueryset(qs, domains, domains, usedNameservers)
 | |
| 
 | |
| 	# reverse zones
 | |
| 	qs = ReverseZone.objects.annotate(nsCount=Count('nameservers')).filter(nsCount__gt=0).order_by("parentNet__address")
 | |
| 	qs4 = filter(lambda _revz: _revz.getNetwork().version == 4, qs)
 | |
| 	qs6 = filter(lambda _revz: _revz.getNetwork().version == 6, qs)
 | |
| 	getDomainsFromQueryset(qs4, rzone4, domains, usedNameservers)
 | |
| 	getDomainsFromQueryset(qs6, rzone6, domains, usedNameservers)
 | |
| 
 | |
| 	print("dn.", domains)
 | |
| 	print("v4", rzone4)
 | |
| 	print("v6", rzone6)
 | |
| 
 | |
| 	protectedRecords = [
 | |
| 		("dn.", "NS", []),
 | |
| 		("dn.", "SOA", []),
 | |
| 	]
 | |
| 
 | |
| 	mergeDomainsWithPdns(s, args, "dn.", domains, protectedRecords)
 | |
| 	mergeDomainsWithPdns(s, args, "10.in-addr.arpa.", rzone4)
 | |
| 	mergeDomainsWithPdns(s, args, "3.2.7.c.3.a.d.f.ip6.arpa.")
 | |
| 
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
| 	main()
 |