#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright 2004-2021 Univention GmbH
#
# https://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <https://www.gnu.org/licenses/>.

'''
Script will only add missing associatedDomain with [-a]
without that missing Attributes will only be shown.

Use -v to also get a list of computer objects, which have multiple
DNSZones and so cannot be associated to one asscoiatedDomain entry.
'''

from __future__ import print_function

import sys
from argparse import ArgumentParser

import univention.admin.uldap as uldap


def parse_args():
	parser = ArgumentParser(description=__doc__)
	parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", help="print more output")
	parser.add_argument("-a", "--auto-add", action="store_true", dest="auto_add", help="automatically add missing associatedDomain")
	parser.add_argument("-c", "--complete-search", action="store_true", dest="complete_search", help="search complete ldap for computer objects")
	options = parser.parse_args()

	return options


def main():
	# type: () -> None
	options = parse_args()

	lo, position = uldap.getAdminConnection()
	ldap_base = position.getBase()

	if options.complete_search:
		computers_base = ldap_base
	else:
		computers_base = 'cn=computers,' + ldap_base
	dns_base = ldap_base

	# get computer objects without associatedDomain
	# computers = {{'cn': ['dn']}
	computers = {}
	for dn, attribute in lo.search('(&(objectClass=univentionHost)(!(associatedDomain=*)))', attr=['cn'], base=computers_base):
		cn = attribute['cn'][0]
		if cn not in computers:
			computers[cn] = []
		computers[cn].append(dn)

	if not computers:
		print("No Hosts with missing associatedDomain found")
		sys.exit(0)

	# get dnszones for computer objects
	# computer_zones = {{'cn': ['zone', 'zone', '...']}
	computer_zones = {}
	for dn, attribute in lo.search('objectClass=dNSZone', attr=['relativeDomainName', 'zoneName'], base=dns_base):
		computer = attribute['relativeDomainName'][0]
		if computer in computers:
			if computer not in computer_zones:
				computer_zones[computer] = []
			computer_zones[computer].append(attribute['zoneName'][0])

	if not computer_zones:
		print("No Hosts with associated DNSZOne found.")
		sys.exit(0)

	# add missing associatedDomain attribute if dnszone is obvious
	modified_objects = 0
	objects_to_modify = 0
	for computer in computers:
		dn = computers[computer][0]
		try:
			if len(computer_zones[computer]) == 1:
				if options.auto_add:
					lo.modify(dn, [('associatedDomain', b'', computer_zones[computer])])
					modified_objects += 1
					if options.verbose:
						print("Added associatedDomain %s to Host %s" % (computer_zones[computer][0].decode('utf-8'), dn))
				else:
					objects_to_modify += 1
					print("Missing associatedDomain %s at Host %s" % (computer_zones[computer][0].decode('utf-8'), dn))
			else:
				if options.verbose:
					print("Multiple DNSZones for Host %s:" % dn)
					for dns_zone in computer_zones[computer]:
						print("DNSZone: %s" % dns_zone.decode('UTF-8'))
		except Exception:
			pass  # no zone for host - nothing to do

	if options.auto_add:
		print("%s Objects modified" % modified_objects)
	else:
		print("%s Objects could be modified" % objects_to_modify)
