#!/usr/bin/env python
# encoding: utf-8
# lmz-network-migration
#  Migrate
#
# Depends: udm
#
# Copyright (C) 2016-2018 Univention GmbH
#
# http://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
# <http://www.gnu.org/licenses/>.


import subprocess
import sys

import univention.admin.config as config
import univention.admin.modules as modules
import univention.admin.uldap as uldap
import univention.config_registry

from optparse import OptionParser

usage = "usage: %prog NETWORK-ADDRESS\n\tMigrate all Microsoft Windows clients except adminpc to the new network.\n\tExample: lmz-network-migration 10.2.0.0"
parser = OptionParser(usage=usage)
parser.add_option('-d', '--dry-run',
		action="store_true", dest="dryrun",
		help="Dry run making no changes", default=False)
parser.add_option('-u', '--uefi',
		action="store_true", dest="uefi",
		help="Attach UEFI boot policy instead of BIOS PXE boot policy", default=False)
(options, args) = parser.parse_args()

if not args or len(args) > 1:
	parser.print_usage()
	sys.exit(1)

network_ip = args[0]

lo, position = uldap.getAdminConnection()
co = config.config()
ucr = univention.config_registry.ConfigRegistry()
ucr.load()
ldap_base = ucr.get('ldap/base')

modules.update()
moduleNetworks = modules.get('networks/network')
modules.init(lo, position, moduleNetworks)
moduleWindows = modules.get('computers/windows')
modules.init(lo, position, moduleWindows)
moduleDhcp = modules.get('dhcp/host')
modules.init(lo, position, moduleDhcp)

networksListEncoded = []

# Initialize network object
networks = moduleNetworks.lookup(co, lo, filter_s='cn=schule-%s' % args[0])
if not networks:
	print >>sys.stderr, "Network not found: schule-%s" % network_ip
	sys.exit(1)
new_network = networks[0]
new_network.open()

for client in moduleWindows.lookup(co, lo, filter_s='cn=*'):
	client.open()
	# Check if current client is AdminVM
	if '10.1.0.13' in client["ip"]:
		print "Ignoring AdminVM: %s" % client['name']
		continue

	# Check if client has more than on IP address
	if len(client["ip"]) != 1:
		print "Ignoring client with multiple IP addresses: %s" % client['name']
	elif len(client["mac"]) > 1:
		print "Warning: client has multiple MAC addresses and has to be migrated manually: %s" % client['name']
	else:
		# Check if client is already in new network
		if client["network"] == new_network.dn:
			print "Ignoring client that is already in network: %s" % client['name']
		else:
			# Move client to new network
			print "Moving client to new network: %s" % client['name']
			# workaround, network can't be set directly now does't it work
			# to call udm command line which network is still filled
			client["network"] = None
			if not options.dryrun:
				client.modify()
				retcode = subprocess.call(['udm', 'computers/windows', 'modify', '--dn', client.dn, '--set', 'network=%s' % new_network.dn])
				if retcode != 0:
					print >>sys.stderr, "Error moving client %s, return code: %d" % (client['name'], retcode)

			# fix broken DHCP Host entry
			# reload client
			if not client['mac']:
				print >>sys.stderr, 'Warning: client has not MAC address: %s' % client['name']
				print 'Done.'
				continue
			client = moduleWindows.lookup(co, lo, filter_s='cn=%s' % client['name'])[0]
			client.open()

			# get dhcp/host entry
			if not client['dhcpEntryZone']:
				print >>sys.stderr, 'Warning: client has no DHCP host entry: %s' % client['name']
				print 'Done.'
				continue
			ipaddress = client['dhcpEntryZone'][0][1]
			base = client['dhcpEntryZone'][0][0]
			dhcp_entry = moduleDhcp.lookup(co, lo, filter_s='univentionDhcpFixedAddress=%s' % ipaddress, base=base)[0]
			dhcp_entry.open()

			modlist = []
			objectclasses = lo.get(dhcp_entry.dn, ['objectClass'])
			if 'univentionPolicyReference' not in objectclasses.get('objectClass', []):
				modlist.append(('objectClass', '', 'univentionPolicyReference'))

			policies = lo.get(dhcp_entry.dn, ['univentionPolicyReference'])
			boot = {True: 'uefi-', False: ''}
			opsi_boot_policy_dn = 'cn=opsi-%sboot,cn=boot,cn=dhcp,cn=policies,%s' % (boot[options.uefi], ldap_base)
			if opsi_boot_policy_dn not in policies.get('univentionPolicyReference', []):
				# policies['univentionPolicyReference'] = policies.get('univentionPolicyReference', [])
				# policies['univentionPolicyReference'].append(opsi_boot_policy_dn)
				modlist.append(('univentionPolicyReference', '', opsi_boot_policy_dn))
				if not options.dryrun:
					lo.modify(dhcp_entry.dn, modlist)
					print 'Reattaching opsi boot policy: opsi-%sboot' % boot[options.uefi]
			print 'Done.'
