#!/usr/bin/python2.7 -OO
# -*- coding: utf-8 -*-
#
# Univention UCS@School
#
# Copyright 2007-2014 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/>.

#-> doesn't do:
# check usernames for correct values (no spaces)
# change the user's position in the tree (change the school i.e.)
# select Schools that are separated by kommata
# modify users school or type (pupil/teacher)

#-> needs definition:
# import access rights

# string of problems during process
problem_hints = ""
# separator char in infile (default: \t for Tabulator)
sepchar='\t'

# wipe the following trailing and leading characters from group names
wipe_char_group='-'

# dict of existing mail domains
mailDomains = {}

import codecs
import copy
import ipaddr
import optparse
import os
import random
import re
import string
import subprocess
import sys
import tempfile
import time
import traceback

import univention.debug
univention.debug.init('/var/log/univention/admin-cmd.log', 1, 0)

import ldap
import univention.admin.uldap
import univention.admin.modules
import univention.admin.objects
import univention.admin.config
import univention.config_registry
import univention.lib.policy_result
from ucsschool.lib.roles import role_pupil, role_teacher, role_staff
from ucsschool.lib.roleshares import roleshare_home_subdir
from ucsschool.lib.models.utils import stopped_notifier, add_stream_logger_to_schoollib

configRegistry=univention.config_registry.ConfigRegistry()
configRegistry.load()

add_stream_logger_to_schoollib(stream=sys.stdout)

baseDN=configRegistry['ldap/base']
domainname=configRegistry['domainname']

emptyLineRe = re.compile(r'^\W*$')

district_enabled = False
if configRegistry['ucsschool/ldap/district/enable'].lower () in ('1', 'yes', 'true'):
	district_enabled = True

# create edukativ DC by default
school_dcs = configRegistry.get('ucsschool/ldap/default/dcs', 'edukativ')

verified_ous          = []
verified_groups       = []
verified_group_shares = []

pwLengthOu = {}

cn_pupils   = configRegistry.get('ucsschool/ldap/default/container/pupils', 'schueler')
cn_teachers = configRegistry.get('ucsschool/ldap/default/container/teachers', 'lehrer')
cn_teachers_staff = configRegistry.get('ucsschool/ldap/default/container/teachers-and-staff', 'lehrer und mitarbeiter')
cn_admins	= configRegistry.get('ucsschool/ldap/default/container/admins', 'admins')
cn_staff	= configRegistry.get('ucsschool/ldap/default/container/staff', 'mitarbeiter')

grp_prefix_pupils   = configRegistry.get('ucsschool/ldap/default/groupprefix/pupils', 'schueler-')
grp_prefix_teachers = configRegistry.get('ucsschool/ldap/default/groupprefix/teachers', 'lehrer-')
grp_prefix_admins	= configRegistry.get('ucsschool/ldap/default/groupprefix/admins', 'admins-')
grp_prefix_staff	= configRegistry.get('ucsschool/ldap/default/groupprefix/staff', 'mitarbeiter-')

grp_policy_pupils	= configRegistry.get('ucsschool/ldap/default/policy/umc/pupils', 'cn=ucsschool-umc-pupils-default,cn=UMC,cn=policies,%s' % baseDN)
grp_policy_teachers = configRegistry.get('ucsschool/ldap/default/policy/umc/teachers', 'cn=ucsschool-umc-teachers-default,cn=UMC,cn=policies,%s' % baseDN)
grp_policy_admins	= configRegistry.get('ucsschool/ldap/default/policy/umc/admins', 'cn=ucsschool-umc-admins-default,cn=UMC,cn=policies,%s' % baseDN)
grp_policy_staff	= configRegistry.get('ucsschool/ldap/default/policy/umc/staff', 'cn=ucsschool-umc-staff-default,cn=UMC,cn=policies,%s' % baseDN)

UCRV_serverprofile_option = configRegistry.get('ucsschool/import/set/serverprofile/path', None)
UCRV_netlogon_script_path = configRegistry.get('ucsschool/import/set/netlogon/script/path', None)
UCRV_sambahome = configRegistry.get('ucsschool/import/set/sambahome', None)
UCRV_homedrive = configRegistry.get('ucsschool/import/set/homedrive')
UCRV_create_mail_domain = configRegistry.is_true("ucsschool/import/generate/mail/domain")

generate_logins = configRegistry.get('ucsschool/import/generate/logins', 'no').lower() in ( 'yes', 'true', '1' )

TYPE_DC_ADMINISTRATIVE = 'administrative'
TYPE_DC_EDUCATIONAL = 'educational'


# IP address prefix len conecerning the netmask
default_prefixlen = 24

if not (cn_pupils and cn_teachers and cn_teachers_staff and cn_admins and cn_staff):
	print '''ERROR: Unable to proceed: one of the following UCR variables is not set correctly:
	ucsschool/ldap/default/container/pupils
	ucsschool/ldap/default/container/teachers
	ucsschool/ldap/default/container/teachers-and-staff
	ucsschool/ldap/default/container/staff
	ucsschool/ldap/default/container/admins
'''
	sys.exit (1)


def is_valid_ou_name(name):
	""" check if given OU name is valid """
	return bool(re.match('^[a-zA-Z0-9](([a-zA-Z0-9_]*)([a-zA-Z0-9]$))?$', name))


def is_valid_hostname(name):
	""" check if given hostname is valid """
	return bool(re.match('^[a-zA-Z0-9](([a-zA-Z0-9-_]*)([a-zA-Z0-9]$))?$', name))


# exception classes
class DeleteObjectError( Exception ): pass
class CreateObjectError( Exception ): pass

class Hooks( object ):
	OBJECTS = ( 'user', 'group', 'printer', 'computer', 'network', 'router', 'ou' )
	OPERATIONS = { 'A' : 'create', 'M' : 'modify', 'D' : 'remove' }

	PATH = '/usr/share/ucs-school-import/hooks/'

	def _create_temp_file( self, line ):
		tmpfile = tempfile.NamedTemporaryFile()
		tmpfile.write( line )
		tmpfile.flush()
		return tmpfile

	def __run( self, phase, module, action, **kwargs ):
		# verify phase
		if not action in Hooks.OPERATIONS:
			return False

		# verify path
		path = os.path.join( Hooks.PATH, '%s_%s_%s.d' % ( module, Hooks.OPERATIONS[ action ], phase ) )
		if not os.path.isdir( path ) or not os.listdir( path ):
			return False

		# create temporary file with data
		if 'line' in kwargs:
			kwargs['line'] = kwargs['line'].strip() + "\n"
			tmpfile = self._create_temp_file( kwargs.get( 'line' ) )

		# invoke hook scripts
		# <script> <temporary file> [<ldap dn>]
		command =  [ 'run-parts', path ]
		if 'line' in kwargs:
			command.extend( ( '--arg', tmpfile.name ) )
		if 'dn' in kwargs:
			command.extend( ( '--arg', kwargs[ 'dn' ] ) )

		ret_code = subprocess.call( command )

		# close temporary file (also deletes the file)
		if 'line' in kwargs:
			tmpfile.close()

		return ret_code == 0

	def pre( self, module, action, **kwargs ):
		return self.__run( 'pre', module, action, **kwargs )

	def post( self, module, action, **kwargs ):
		return self.__run( 'post', module, action, **kwargs )

hooks = Hooks()

class ucsschool_person_modify:
	def __init__(self,line):

		# parse line here
		parsed = line.strip('\r\n').split(sepchar)

		self.modtype   = parsed[0] # A,M or D: add, modify or delete
		self.login     = parsed[1].lower ()
		self.sname     = parsed[2] # surename
		self.name      = parsed[3] # first name
		self.sNr       = parsed[4] # schoolNr(s)
		self.cNr       = parsed[5] # classNr
		self.rights    = parsed[6] # users which may change this entry
		self.mail      = parsed[7]

		def parseBoolstring (parsed, idx, default='0'):
			res = default
			if len(parsed) > idx:
				if parsed[idx].strip().startswith('1'):
					res = '1'
				elif parsed[idx].strip().startswith('0'):
					res = '0'
			return res

		self.isTeacher = parseBoolstring (parsed, 8)
		self.isActive  = parseBoolstring (parsed, 9, default='1')
		self.isStaff   = parseBoolstring (parsed, 10)

		if len(parsed) > 11:
			self.password = parsed[11]
		else:
			self.password = None

		self.problem_hints = ""

		if generate_logins:
			if self.isTeacher == "1" or self.isStaff == "1":
				self.login = ('%s.%s' % (self.name[0], self.sname[:8])).lower()
				print "DEBUG: Teacher found: creating login name " + self.login
			else:
				self.login = self.name[:6].lower()
				print "DEBUG: Student found: creating login name " + self.login

		if ',' in self.sNr:
			self.allsNrs= self.sNr.split(',')
			self.sNr=self.allsNrs[0]
			self.other_sNr=self.allsNrs[1:]
		else:
			self.allsNrs=[self.sNr]
			self.other_sNr=[]

		# split into multiple class number if comma is present
		if ',' in self.cNr:
			self.cNr = self.cNr.split(',')
		else:
			self.cNr = [ self.cNr ]
		# wipe invalid character from class numbers
		for i in range(len(self.cNr)):
			self.cNr[i] = self.wipeInvalidCharGroup( self.cNr[i] )
		# remove empty strings
		self.cNr = [ x for x in self.cNr if x ]
		# sort classes
		self.cNr.sort()

	def wipeInvalidCharGroup(self, groupName):
		global wipe_char_group

		ok = False
		while groupName and not ok:
			if groupName[0] in wipe_char_group:
				groupName = groupName[1:]
			else:
				ok = True
		ok = False
		while groupName and not ok:
			if groupName[-1] in wipe_char_group:
				groupName = groupName[0:-1]
			else:
				ok = True
		return groupName

	def getOtherPersons(self):
		persons=[]

		for schoolNr in self.other_sNr:
			person = copy.deepcopy(self)
			person.login = ("%s_%s"%(self.login,schoolNr)).lower ()
			person.sNr=schoolNr
			person.other_sNr=[] # has no "slave"-accounts
			self.mail="" # no extra mail address
			person.cNr=[] # no class in other ou
			person.isTeacher = '0'
			person.isStaff = '0'
			if role_teacher in self.getRole ():
				person.isTeacher = '1'
			if role_staff in self.getRole ():
				person.isStaff = '1'

			persons.append(person)

		return persons

	def getPosition_dn(self):
		# resolution order for the position is pupil, teacher, staff
		cn = cn_pupils
		if role_teacher in self.getRole() and role_staff in self.getRole():
			cn = cn_teachers_staff
		elif role_teacher in self.getRole ():
			cn = cn_teachers
		elif role_staff in self.getRole ():
			cn = cn_staff
		return "cn=%s,cn=users,%s" % (cn, getDN (self.sNr))

	def getDN(self):
		return "uid="+self.login+","+self.getPosition_dn()

	def default_groups(self):
		default_groups=[]

		# default group
		default_groups.append("cn=Domain Users "+self.sNr+",cn=groups,%s" % (getDN (self.sNr), ))

		for role in self.getRole ():
			user_grp_prefix = { role_teacher:grp_prefix_teachers,
							    role_pupil:grp_prefix_pupils,
							    role_staff:grp_prefix_staff }[role]
			if role == role_staff and not configRegistry.is_true('ucsschool/ldap/noneducational/create/objects', True):
				continue
			# class if available
			for cnr in self.cNr:
				default_groups.append("cn=" + cnr + ",cn=klassen,cn=%s,cn=groups,%s" % (cn_pupils, getDN (self.sNr)))

			default_groups.append("cn=%s%s,cn=groups,%s"%(user_grp_prefix, self.sNr, getDN (self.sNr)))

		return default_groups

	def getRole(self):
		"""returns the user's roles"""
		roles = []
		if self.isTeacher == '1':
			roles.append (role_teacher)
		if self.isStaff == '1':
			roles.append (role_staff)
		if not roles:
			roles.append (role_pupil)
		return roles

def extract_district (schoolNr):
	try:
		return schoolNr[:2]
	except IndexError:
		# TODO: add more debug output
		print "ERROR: Unable to extract district from school number: %s' % schoolNr + \
				'\n\tIf you don't use the district model deactivate UCR variable ucsschool/ldap/district/enable"

def getDN (schoolNr, base='school', basedn=baseDN):
	"""
	@param	base Values are either school, district or base
	@return	According to the base a specific part of dn is returned.
			Let's suppose the following school dn:
			ou=SCHOOL,ou=DISTRICT,dc=BASE,dc=DN

			The following is returned
			'base'		-> dc=BASE,dc=DN
			'district'	-> ou=DISTRICT,dc=BASE,dc=DN
			'school'	-> ou=SCHOOL,ou=DISTRICT,dc=BASE,dc=DN
	"""
	dn = '%(school)s%(district)s%(basedn)s'
	values = {'school':'ou=%s,'%schoolNr, 'district':'', 'basedn':basedn}
	if district_enabled:
		district = extract_district (schoolNr)
		if not district:
			print "ERROR: Unable to continue execution without district number. School number: %s" % schoolNr
			sys.exit(1)
		values['district'] = 'ou=%s,' % district
	if base == 'district':
		values['school'] = ''
	elif base == 'base':
		values['district'] = ''
		values['school'] = ''
	return dn % values


def verify_policy(objdn, schoolNr, policy_dn_list):
	# test if all policy objects exist and if all referenced objects are policy objects
	global problem_hints
	for poldn in policy_dn_list:
		oc = lo.get( poldn, ['objectClass'] )
		if not oc:
			print "Object to be referenced does not exist: " + poldn
			problem_hints = problem_hints + "there were problems verifying ou %s: Object to be referenced does not exist: %s\n" % (schoolNr, poldn)
			break
		elif not 'univentionPolicy' in oc['objectClass']:
			print "Object to be referenced is no valid policy: " + poldn
			problem_hints = problem_hints + "there were problems verifying ou %s: Object to be referenced is no valid policy: %s\n" % (schoolNr, poldn)
			break
	else:
		# add univentionPolicyReference if neccessary
		oc = lo.get( objdn, ['objectClass'] )
		if not 'univentionPolicyReference' in oc.get('objectClass',[]):
			try:
				lo.modify( objdn, [ ('objectClass','','univentionPolicyReference') ] )
			except:
				print 'Objectclass univentionPolicyReference cannot be added to %s\n' % (objdn)
				problem_hints = problem_hints + "there were problems verifying ou %s: Objectclass univentionPolicyReference cannot be added to %s\n" % (schoolNr, objdn)
		# add all missing policies
		pl = lo.get( objdn, ['univentionPolicyReference'] )
		modlist=[]
		for poldn in policy_dn_list:
			if not poldn in pl.get('univentionPolicyReference',[]):
				modlist.append(('univentionPolicyReference','',poldn))
				print 'need to attach policy: %s' % poldn
		try:
			lo.modify(objdn, modlist)
		except:
			print 'Policies %s cannot be referenced to %s\n' % (policy_dn_list, objdn)
			problem_hints = problem_hints + "there were problems verifying ou %s: policies %s cannot be referenced to %s\n" % (schoolNr, policy_dn_list, objdn)


def create_policy(cn, container, values, policy_module):
	"""
	@param cn					name of policy
	@param container			target ldap container
	@param properties			policy object properties
	@param policy_module		UDM module
	returns DN if policy has been created, otherwise None
	"""
	created = False
	exists = False
	verify_containers(container, cn_module, co, lo, superordinate, baseDN, path='policyPath')

	objlist = univention.admin.modules.lookup(policy_module, co, lo, scope='one', superordinate=superordinate,
											  base=container, filter=univention.admin.filter.expression('cn', cn))
	exists = bool(objlist)
	if not objlist:
		cnpos=univention.admin.uldap.position(baseDN)
		cnpos.setDn(container)
		policy_object = policy_module.object(co, lo, position=cnpos, superordinate=superordinate)
		policy_object.open()
		for k, v in values.iteritems():
			policy_object[k] = v
		try:
			# FIXME: the UDM function create() of simplePolicy does not have a return value
			create_object(policy_object)
			created = True
		except:
			print "WARNING: Error creating policy cn=%s,%s failed" % (cn, container)
			traceback.print_exc(100, sys.stdout)
	return ( exists, created )

def object_exists(module, co, lo, scope, superordinate, base, filter, dn):

	objects = univention.admin.modules.lookup(
		module, co, lo, scope=scope,
		superordinate=superordinate, 
		base=base, filter=filter)

	if objects:
		if not dn:
			if len(objects) == 1:
				return objects[0]
			return None
		for o in objects:
			if o.dn.lower() == dn.lower():
				return o
	
	return None

def verify_school_ou(schoolNr, co, lo, baseDN, dcName=None, ShareFileServer=None, displayName=None):
	global problem_hints

	for verified_ou in verified_ous:
		if schoolNr.lower() == verified_ou.lower():
			print "verify ou for school nr %s already done"%schoolNr
			return True

	# create ucsschool container if it does not exists
	created, dn = verify_container ('cn=ucsschool,cn=groups,%s' % baseDN, cn_module, co, lo, superordinate, baseDN, 'groupPath')

	if district_enabled:
		verify_container(getDN (schoolNr, base='district'), ou_module, co, lo, superordinate, baseDN)

	print "verify ou for school nr %s"%schoolNr
	# list of needed sub-containers, the dictionary-key adds the container as default during create in verify_container
	container={'0printerPath':['cn=printers'],
			   '1userPath':['cn=users','cn=%s,cn=users' % cn_pupils,'cn=%s,cn=users' % cn_teachers,'cn=%s,cn=users' % cn_admins],
			   '2computerPath':['cn=computers','cn=server,cn=computers','cn=dc,cn=server,cn=computers'],
			   '3networkPath':['cn=networks'],
			   '4groupPath':['cn=groups','cn=%s,cn=groups' % cn_pupils,'cn=%s,cn=groups' % cn_teachers,'cn=klassen,cn=%s,cn=groups' % cn_pupils,'cn=raeume,cn=groups'],
			   '5dhcpPath':['cn=dhcp'],
			   '6policyPath':['cn=policies'],
			   '7sharePath':['cn=shares','cn=klassen,cn=shares'],
			   '8none':['cn=dc,cn=server,cn=computers']
			   }
	if configRegistry.is_true('ucsschool/ldap/noneducational/create/objects', True):
		container['1userPath'].extend(['cn=%s,cn=users' % cn_staff, 'cn=%s,cn=users' % cn_teachers_staff])
		container['4groupPath'].append('cn=%s,cn=groups' % cn_staff)
	# FIXME: die Policies sollten besser mit der Gruppe verknüpft werden, um
	# z.B. Mitarbeiter und Lehrer im selben Container pflegen zu können
	#container_policies = { 'cn=%s,cn=users' % cn_teachers: ['cn=default-lehrer,cn=UMC,cn=policies,' + baseDN] }
	container_policies = { }

	ou_base = getDN (schoolNr)

	# invoke pre hooks
	if dcName:
		dccn = dcName
	else:
		dccn = ''
	myline = '%s\t%s' % ( schoolNr, dccn )
	hooks.pre( 'ou', 'A', line = myline )

	# verify global dc groups
	groups_administrative = [
		"cn=DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % baseDN,
		"cn=Member-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % baseDN]
	groups_education=[
		"cn=DC-Edukativnetz,cn=ucsschool,cn=groups,%s" % baseDN,
		"cn=Member-Edukativnetz,cn=ucsschool,cn=groups,%s" % baseDN]
	groups_administrativeOU=[
		"cn=OU%s-DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN),
		"cn=OU%s-Member-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN)]
	groups_educationOU=[
		"cn=OU%s-DC-Edukativnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN),
		"cn=OU%s-Member-Edukativnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN)]

	if configRegistry.is_true('ucsschool/ldap/noneducational/create/objects', True):
		groups = groups_administrative + groups_education + groups_administrativeOU + groups_educationOU
	else:
		groups = groups_education + groups_educationOU
	for group in groups:
		verify_group(group, co, lo, superordinate, baseDN)

	# add already existing dc (slave) to global dc groups
	# for proper ldap replication
	if dccn:
		dcobject = object_exists(
			server_module, co, lo, 'sub', superordinate, baseDN,
			univention.admin.filter.expression('cn', dccn), None)
	
		if dcobject:
			zone = "edukativ"
			dcobject.open()
			# TODO FIXME The following snippet does not make any sense:
			# if the DC is member of DC-Verwaltungsnetz then is added again to that group?!? Looks like this code is unused.
			for grp in dcobject['groups']:
				if grp.startswith("cn=DC-Verwaltungsnetz,"):
					zone = "verwaltung"
			groups = []
			if zone == "edukativ":
				groups.append("cn=DC-Edukativnetz,cn=ucsschool,cn=groups,%s" % baseDN)
				groups.append("cn=OU%s-DC-Edukativnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN))
			if zone == "verwaltung":
				groups.append("cn=DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % baseDN)
				groups.append("cn=OU%s-DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN))
			modified = False
			for grp in groups:
				if not grp in dcobject['groups']:
					modified = True
					dcobject['groups'].append(grp)
			if modified:
				dcobject.modify()
		

	created, dn = verify_container(ou_base, ou_module, co, lo, superordinate, baseDN, path='')
	if created:
		# get name of new dc
		finalDCname = 'dc%s-01' % schoolNr.lower ()
		if configRegistry.is_true('ucsschool/singlemaster', False):
			finalDCname = configRegistry.get('hostname')
		if dcName:
			finalDCname = dcName

		# if fileserver is not set then use dc name
		if ShareFileServer is None:
			ShareFileServer = finalDCname

		# looking for DN of fileserver
		objects = lo.searchDn(filter='(&(objectClass=univentionHost)(cn=%s))' % ShareFileServer, base=baseDN)
		if len(objects) != 1:
			# no object found
			if ShareFileServer == 'dc%s-01' % schoolNr.lower() or (dcName and ShareFileServer == dcName):
				# ignore warning - dc object will be created later
				ShareFileServer = "cn=%s,cn=dc,cn=server,cn=computers,%s" % (ShareFileServer, getDN(schoolNr))
			else:
				print 'WARNING: sharefileserver "%s" not found! Using "%s" as sharefileserver' % (ShareFileServer, configRegistry.get('hostname'))
				ShareFileServer = configRegistry.get('ldap/hostdn')
		else:
			# use found dn
			ShareFileServer = objects[0]

		r = lo.modify(dn, [ ('objectClass', '', 'ucsschoolOrganizationalUnit'),
							('ucsschoolClassShareFileServer', '', ShareFileServer), # set file server for class shares
							('ucsschoolHomeShareFileServer', '', ShareFileServer),  # set file server for home shares
							('displayName', '', displayName),                       # set display name for UCS@school
							] )
	else:
		if ShareFileServer is not None:
			print "WARNING: sharefileserver ignored because OU already exists!"
			print "WARNING: please modify school OU via UDM or UMC"
		if displayName is not None:
			r = lo.modify(ou_base, [('displayName', lo.get(ou_base, ['displayName']).get('displayName',[]), [displayName])])

	keys=container.keys()
	keys.sort()
	for path in keys:
		for dn in container[path]:
			if path[1:]=='none': path=' '
			verify_container('%s,%s'%(dn,ou_base),cn_module, co, lo, superordinate, baseDN, path=path[1:])

	# create groups if not existant
	groups=[ ( "cn=%s%s,cn=ouadmins,cn=groups,%s" % (grp_prefix_admins, schoolNr.lower(), baseDN), grp_policy_admins ),
			 ( "cn=%s%s,cn=groups,%s" % (grp_prefix_pupils, schoolNr.lower(), getDN(schoolNr)),	grp_policy_pupils ),
			 ( "cn=%s%s,cn=groups,%s" % (grp_prefix_teachers, schoolNr.lower(), getDN(schoolNr)),	grp_policy_teachers ),
		 ]
	if configRegistry.is_true('ucsschool/ldap/noneducational/create/objects', True):
		groups.append(
			 ( "cn=%s%s,cn=groups,%s" % (grp_prefix_staff, schoolNr.lower(), getDN(schoolNr)),	grp_policy_staff ),
			 )
	if configRegistry.is_true('ucsschool/import/attach/policy/default-umc-users', True):
		domain_users_school = "cn=Domain Users %s,cn=groups,%s" % (schoolNr.lower(), getDN(schoolNr))
		groups.append((domain_users_school, "cn=default-umc-users,cn=UMC,cn=policies,%s" % (baseDN,)))

	for group, grppolicy in groups:
		verify_group(group, co, lo, superordinate, baseDN)
		if grppolicy and grppolicy.lower() != 'none':
			verify_policy(group, schoolNr, [ grppolicy ])

	# In a single server environment the default DHCP container must
	# be set to the DHCP container in the school ou. Otherwise newly
	# imported computers have the DHCP objects in the wrong DHCP container
	dhcpd_ldap_base = configRegistry.get('dhcpd/ldap/base')
	if configRegistry.is_true('ucsschool/singlemaster', False):
		if not dhcpd_ldap_base:
			dhcpd_ldap_base = 'cn=dhcp,%s' % (getDN(schoolNr),)
			univention.config_registry.handler_set(['dhcpd/ldap/base=%s' % (dhcpd_ldap_base,)])

	for dc in school_dcs.split ():
		if dcName:
			dccn = dcName
		else:
			if configRegistry.is_true('ucsschool/singlemaster', False):
				dccn = configRegistry.get('hostname')
			else:
				dccn = 'dc%s-01' % schoolNr.lower ()

		dcgroups = ["cn=OU%s-DC-Edukativnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower (), baseDN),
					"cn=DC-Edukativnetz,cn=ucsschool,cn=groups,%s" % (baseDN, )]

		if dc == 'verwaltung':
			if not configRegistry.is_true('ucsschool/ldap/noneducational/create/objects', True):
				continue
			if dcName:
				dccn = '%sv' % dcName
			else:
				if configRegistry.is_true('ucsschool/singlemaster', False):
					dccn = configRegistry.get('hostname')
				else:
					dccn = 'dc%sv-01' % schoolNr.lower () # this is the naming convention, a trailing v for Verwaltungsnetz DCs
			dcgroups = ["cn=OU%s-DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower (), baseDN),
					"cn=DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % (baseDN, )]

		# create server if not exsistant
		objects = univention.admin.modules.lookup(computer_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
												  filter=univention.admin.filter.expression('cn', dccn))

		# * check if computer exists
		# * if computer is exists, test the following things
		#   * is computer a dc master or dc backup ==> do not add computer to any groups
		#   * is computer a dc slave ==> add computer to groups specified by dcgroups
		#   * is computer no dc ==> return error and exit
		#

		# create server if not existent
		server_exists = (len(objects) > 0) # cannot create another server with this id
		is_master_or_backup = False
		is_slave = False

		if not server_exists and not dcName:
			try:
				if dc == 'verwaltung':
					grpdn = 'cn=OU%s-DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s' % (schoolNr.lower (), baseDN)
				else:
					grpdn = 'cn=OU%s-DC-Edukativnetz,cn=ucsschool,cn=groups,%s' % (schoolNr.lower (), baseDN)
				hostlist = lo.get(grpdn, ['uniqueMember']).get('uniqueMember',[])
			except ldap.NO_SUCH_OBJECT:
				hostlist = []
			except Exception, e:
				print 'ERROR: cannot read %s: %s' % (grpdn, e)

			if hostlist:
				continue # if at least one DC has control over this OU then jump to next 'school_dcs' item

		if server_exists:
			# check if existing system is a DC master or DC backup
			masterobjs = univention.admin.modules.lookup(dcmaster_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
														 filter=univention.admin.filter.expression('cn', dccn))
			backupobjs = univention.admin.modules.lookup(dcbackup_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
														 filter=univention.admin.filter.expression('cn', dccn))
			is_master_or_backup = ( masterobjs or backupobjs )
			# check if existing system is a DC slave
			slaveobjs = univention.admin.modules.lookup(server_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
														 filter=univention.admin.filter.expression('cn', dccn))
			is_slave = len(slaveobjs) > 0

			if dcName:
				# manual dc name has been specified by user

				if not is_master_or_backup and not is_slave:
					print 'WARNING: given system name %s is already in use and no domaincontroller system. Please choose another name' % dcName
					sys.exit(1)

				if is_master_or_backup and is_slave:
					print 'ERROR: implementation error: %s seems to be dc slave and dc master at the same time' % dcName
					sys.exit(1)

				if len(slaveobjs) > 1:
					print 'ERROR: more than one system with cn=%s found' % dcName
					sys.exit(1)

				if is_slave:
					obj = slaveobjs[0]
					obj.open()
					for dcgroup in dcgroups:
						if dcgroup not in obj["groups"]:
							obj["groups"].append(dcgroup)
					modify_object(obj)

		if not server_exists:
			position.setDn("cn=dc,cn=server,cn=computers,"+getDN (schoolNr))
			object=server_module.object(co, lo, position=position, superordinate=superordinate)
			object.open()
			object["name"]=dccn
			object["unixhome"]="/dev/null"
			object["shell"]="/bin/bash"
			object["primaryGroup"]="cn=DC Slave Hosts,cn=groups,%s"%baseDN
			for dcgroup in dcgroups:
				if dcgroup not in object["groups"]:
					object["groups"].append (dcgroup)
			try:
				create_object(object)
			except:
				pass
			print "need to create server: %s"%object.dn

		# create dhcp-service if not existant
		objects = univention.admin.modules.lookup(
			dhcp_service_module, co, lo, scope='sub', superordinate=superordinate,
			base=baseDN, filter=univention.admin.filter.expression('cn', schoolNr.lower()))
		service_exists=0
		if objects:
			service = "cn=%s,cn=dhcp,%s" % (schoolNr, getDN(schoolNr))
			for object in objects:
				if object.dn.lower() == service.lower():
					service_exists=1
					dhcp_service_object=object
					break
		if not service_exists:
			position.setDn("cn=dhcp,%s" % (getDN (schoolNr), ))
			object=dhcp_service_module.object(co, lo, position=position, superordinate=superordinate)
			object.open()
			object["service"]=schoolNr.lower ()
			object[ 'option' ] = [ 'wpad "http://%s.%s/proxy.pac"' % ( dccn, domainname ) ]
			try:
				create_object(object)
			except:
				pass
			dhcp_service_object=object
			print "need to create dhcp service: %s"%object.dn

		# create dhcp-server if not exsistant
		objects = lo.searchDn( filter = '(&(objectClass=dhcpServer)(cn=%s))' % dccn, base=baseDN )
		if objects:
			# move existing dhcp server object to OU
			newDHCPServerDN = "cn=%s,cn=%s,cn=dhcp,%s" % (dccn, schoolNr.lower(), getDN(schoolNr))
			if len(objects) > 1:
				print 'WARNING: more than one dhcp-server object found! Moving only one!'
			obj = univention.admin.objects.get(dhcp_server_module, co, lo, position='', dn=objects[0])
			obj.open()
			dhcpServerContainer = ','.join(objects[0].split(',')[1:])

			# only move if
			# - in multiserver environments OR
			# - desired dhcp server DN matches with UCR config
			if not(configRegistry.is_true('ucsschool/singlemaster', False)) or (newDHCPServerDN.endswith(',%s' % dhcpd_ldap_base)):
				if obj.dn.lower() != newDHCPServerDN.lower():
					attr_server = obj["server"]
					print "need to remove dhcp server: %s" % obj.dn
					try:
						obj.remove()
					except:
						print "Failed to remove dhcp server: %s" % (obj.dn, )
						problem_hints = problem_hints + "there were problems verifying this ou %s\n" % schoolNr
					position.setDn("cn=%s,cn=dhcp,%s"%(schoolNr.lower (), getDN (schoolNr)))
					print "need to create dhcp server: %s" % (position.getDn(), )
					object=dhcp_server_module.object(co, lo, position=position, superordinate=dhcp_service_object)
					object.open()
					object["server"]=attr_server
					try:
						create_object(object)
					except:
						print "Failed to create dhcp server: %s" % (position.getDn(), )
						problem_hints = problem_hints + "there were problems verifying this ou %s\n" % schoolNr
			################
			# copy subnets #
			################
			# find local interfaces
			interfaces = []
			for interface_name in set([ key.split('/')[1] for key in configRegistry.keys() if key.startswith('interfaces/eth')]):
				try:
					address = ipaddr.IPv4Network('%s/%s' % (configRegistry['interfaces/%s/address' % interface_name],
					                                        configRegistry['interfaces/%s/netmask' % interface_name], ))
					interfaces.append(address)
				except ValueError, error:
					print "Skipping invalid interface %s:\n%s" % (interface_name, error, )
			objects = lo.searchDn( filter = '(objectClass=univentionDhcpSubnet)', base=dhcpServerContainer )
			for objectDN in objects:
				object = univention.admin.objects.get(dhcp_subnet_module, co, lo, position='', dn=objectDN)
				object.open()
				subnet = ipaddr.IPv4Network('%s/%s' % (object['subnet'], object['subnetmask'], ))
				if subnet in interfaces: # subnet matches any local subnet
					position.setDn("cn=%s,cn=dhcp,%s"%(schoolNr.lower (), getDN (schoolNr)))
					if lo.searchDn( filter='(&(objectClass=univentionDhcpSubnet)(cn=%s))' % object['subnet'], base=position.getDn()):
						print "do not need to copy dhcp subnet %s: %s (target already exists)" % (subnet, object.dn, )
					else:
						print "need to copy dhcp subnet %s: %s" % (subnet, object.dn, )
						newobject=dhcp_subnet_module.object(co, lo, position=position, superordinate=dhcp_service_object)
						newobject.open()
						for key in object.keys():
							value = object[key]
							newobject[key] = value
						try:
							create_object(newobject)
						except:
							print "Failed to copy dhcp subnet %s to %s" % (object.dn, position.getDn(), )
							problem_hints = problem_hints + "there were problems verifying this ou %s\n" % schoolNr
				else:
					print "Skipping non-local subnet %s" % subnet
		else:
			# create fresh dhcp server object
			position.setDn("cn=%s,cn=dhcp,%s"%(schoolNr.lower (), getDN (schoolNr)))
			object=dhcp_server_module.object(co, lo, position=position, superordinate=dhcp_service_object)
			object.open()
			object["server"]=dccn
			print "need to create dhcp server: %s" % object.dn
			try:
				create_object(object)
			except:
				pass

	if not schoolNr in verified_ous:
		verified_ous.append(schoolNr)

	# if requested, then create dhcp_dns policy that clears univentionDhcpDomainNameServers at OU level
	# to prevent problems with "wrong" DHCP DNS policy connected to ldap base
	if configRegistry.is_true('ucsschool/import/generate/policy/dhcp/dns/clearou', False):
		name = 'dhcp-dns-clear'
		container = 'cn=policies,%s' % getDN(schoolNr)
		dn = 'cn=%s,%s' % (name, container)
		values = {
			'name': name,
			'emptyAttributes': [ 'univentionDhcpDomainNameServers' ],
			}
		exists, created = create_policy( name, container, values, policy_dhcp_dns_module )
		if not created and not exists:
				print "Policy object %s cannot be created"  % dn
				problem_hints = problem_hints + "there were problems creating policy object %s\n" % dn
		else:
			if not getDN(schoolNr) in container_policies:
				container_policies[''] = [ dn ]
			else:
				container_policies[''].append( dn )

	# attach policies if needed
	for cnpart in container_policies.keys():
		# verify for each container that container does exist
		containerdn = '%s,%s' % (cnpart,ou_base)
		if not cnpart:
			containerdn = ou_base
		else:
			verify_container(containerdn, cn_module, co, lo, superordinate, baseDN, '')
		pollist = container_policies[cnpart]

		# test if all policy objects exist and if all referenced objects are policy objects
		for poldn in pollist:
			oc = lo.get( poldn, ['objectClass'] )
			if not oc:
				print "Object to be referenced does not exist: " + poldn
				problem_hints = problem_hints + "there were problems verifying this ou %s\n" % schoolNr
				break
			elif not 'univentionPolicy' in oc['objectClass']:
				print "Object to be referenced is no valid policy: " + poldn
				problem_hints = problem_hints + "there were problems verifying this ou %s\n" % schoolNr
				break
		else:
			# add univentionPolicyReference if neccessary
			oc = lo.get( containerdn, ['objectClass'] )
			if not 'univentionPolicyReference' in oc.get('objectClass',[]):
				lo.modify(containerdn, [ ('objectClass','','univentionPolicyReference') ] )
			# add all missing policies
			pl = lo.get( containerdn, ['univentionPolicyReference'] )
			modlist=[]
			for poldn in pollist:
				if not poldn.lower() in map(lambda x: x.lower(), pl.get('univentionPolicyReference',[])):
					modlist.append(('univentionPolicyReference','',poldn))
					print 'need to attach policy: %s' % poldn
			lo.modify(containerdn,modlist)

	# invoke post hooks
	if created:
		# for OUs the temporary file contains the LDAP DN
		hooks.post( 'ou', 'A', dn = ou_base, line = myline )

def move_object(o, newdn):
	if o.dn == newdn:
		return

	try:
		print 'moving object: old DN=%s   new DN=%s' % (o.dn, newdn)
		o.move(newdn)
	except univention.admin.uexceptions.noObject:
		print "ERROR while moving object %s: object not found" % o.dn
		print 'ERROR:', sys.exc_info()[0], sys.exc_info()[1]
		traceback.print_exc(100, sys.stdout)
	except univention.admin.uexceptions.ldapError:
		print "ERROR while moving object %s: ldap error" % o.dn
		print 'ERROR:', sys.exc_info()[0], sys.exc_info()[1]
		traceback.print_exc(100, sys.stdout)
	except univention.admin.uexceptions.invalidOperation, msg:
		print "ERROR while moving object %s: invalid operation: %s" % (o.dn, msg)
		print 'ERROR:', sys.exc_info()[0], sys.exc_info()[1]
		traceback.print_exc(100, sys.stdout)


def modify_object(o):
	try:
		dn = o.modify()
		print 'modifed object:', o.dn
	except:
		print "ERROR while modifying object %s" % o.dn
		print 'ERROR:', sys.exc_info()[0], sys.exc_info()[1]
		traceback.print_exc(100, sys.stdout)


def create_object( o, ignore_exists = False ):
	exists = False
	msg = ''
	dn = None
	try:
		dn=o.create()
		print 'creating object', dn
	except univention.admin.uexceptions.objectExists:
		if not ignore_exists:
			msg = 'ERROR: Object exists-1 (objectExists)'
		else:
			exists = True
	except univention.admin.uexceptions.uidAlreadyUsed:
		if not ignore_exists:
			msg = 'ERROR: Object exists-2 (uidAlreadyUsed)'
		else:
			exists = True
	except univention.admin.uexceptions.dhcpServerAlreadyUsed:
		if not ignore_exists:
			msg = 'ERROR: Object exists-3 (dhcpServerAlreadyUsed)'
		else:
			exists = True
	except univention.admin.uexceptions.noLock:
		if not ignore_exists:
			msg = 'ERROR: Object exists-4 (noLock)'
		else:
			exists = True

	if msg:
		print msg
		raise CreateObjectError( exists, dn )

	return ( exists, dn )


def verify_containers (position_dn, module, co, lo, superordinate, base, path=""):
	"""
	verifies all containers starting at the ldap base

	@param	module	Param is ignored! cn_module is the only supported module!!
	"""
	split_dn = position_dn.split (',')
	# with this variable it's ensured that within a row of containers there is
	# no other object/container which can not be created with method
	started_cn_row = False
	for i in xrange (len (split_dn) -1, -1, -1):
		if split_dn[i].startswith ('cn='):
			verify_container (','.join (split_dn[i:]), cn_module, co, lo, superordinate, base, path=path)
			started_cn_row = True
		elif started_cn_row:
			# found an object/container that can not be created
			break

def verify_container(position_dn, module, co, lo, superordinate, base, path="userPath"):
	"""
	look if goal-container exists, else create it
	"""
	container_exists = 0
	type=position_dn[0:string.find(position_dn,"=")]
	name=position_dn[string.find(position_dn,"=")+1:string.find(position_dn,",")]
	position=position_dn[string.find(position_dn,",")+1:]
	objects=''
	try:
		objects = univention.admin.modules.lookup(
			module, co, lo, scope='sub', superordinate=superordinate,
			base=position, filter=univention.admin.filter.expression(type,name))
	except:
		pass
	if objects:
		for object in objects:
			if codecs.latin_1_encode(univention.admin.objects.dn(object))[0].lower()==position_dn.lower():
				container_exists = 1
				break

	created = False
	dn = None
	if not container_exists:
		print "need to create container %s"%position_dn
		position=univention.admin.uldap.position(base)
		position.setDn(position_dn[string.find(position_dn,",")+1:])
		object = module.object(co, lo, position, superordinate=superordinate)
		object.open()
		object["name"] = position_dn[3:string.find(position_dn,",")]

		if position_dn[:2] == "cn" and path and not name == "users": # is path container
			object[path] = codecs.latin_1_encode("1")[0]

		try:
			exists, dn = create_object(object)
			created = True
		except CreateObjectError, e:
			exists, dn = e.args

	return ( created, dn )

def verify_group_share(schoolNr, classNr, co, lo, superordinate, base):

	if (schoolNr, classNr.lower()) in verified_group_shares:
		return True

	position_dn="cn=%s,cn=klassen,cn=shares,%s"%(classNr, getDN (schoolNr, basedn=base))
	module = univention.admin.modules.get("shares/share")
	position_basedn = univention.admin.uldap.position(baseDN)
	univention.admin.modules.init (lo, position_basedn, module)
	more_instances = 1
	container_position = base
	while more_instances:
		container_position = position_dn[string.rfind(position_dn,",",0,len(position_dn)-len(container_position)-1)+1:]
		if container_position == position_dn:
			more_instances = 0
		elif container_position[:2] == "cn":
			verify_container(container_position, cn_module, co, lo, superordinate, base, path="groupPath")
		elif container_position[:2] == "ou":
			verify_container(container_position, ou_module, co, lo, superordinate, base)
		else: print "WARNING: unknown container type",container_position

	# look if share exists, else create it
	share_exists = 0
	type=position_dn[0:string.find(position_dn,"=")]
	name=position_dn[string.find(position_dn,"=")+1:string.find(position_dn,",")]
	position=position_dn[string.find(position_dn,",")+1:]
	objects = univention.admin.modules.lookup(module, co, lo, scope='sub', superordinate=superordinate, base=position,
											  filter=univention.admin.filter.expression(type,name))
	if objects:
		for object in objects:
			if codecs.latin_1_encode(univention.admin.objects.dn(object))[0].lower() == position_dn.lower():
				share_exists = 1
				break

	if not share_exists:
		print "need to create groupshare %s"%position_dn

		# get gid form corresponding group
		group_dn="cn=%s,cn=klassen,cn=%s,cn=groups,%s"%(classNr, cn_pupils, getDN (schoolNr, basedn=base))
		gids=lo.get(group_dn,['gidNumber'])
		gid = 0
		if len(gids) > 1: # TODO FIXME This doesn't look correct to me - gids is a dict and not a list!
			print "WARNING: more than one corresponding gid found"
			gid=gids["gidNumber"][0]
		elif len(gids) < 1: # TODO FIXME This doesn't look correct to me - gids is a dict and not a list!
			print "WARNING: no corresponding gid found"
		else:
			gid=gids["gidNumber"][0]


		# set default server
		serverfqdn = "dc%s-01.%s"%(schoolNr.lower (),domainname)

		# get alternative server (defined at ou object if a dc slave is responsible for more than one ou)
		ou_dn = getDN (schoolNr, basedn=base)
		ou_attr_LDAPAccessWrite = lo.get(ou_dn,['univentionLDAPAccessWrite'])
		alternativeServer_dn = None
		if len(ou_attr_LDAPAccessWrite) > 0:
			alternativeServer_dn = ou_attr_LDAPAccessWrite["univentionLDAPAccessWrite"][0]
			if len(ou_attr_LDAPAccessWrite) > 1: # TODO FIXME This doesn't look correct to me - ou_attr_LDAPAccessWrite is a dict and not a list!
				print "WARNING: more than one corresponding univentionLDAPAccessWrite found at ou=%s" % schoolNr

		# build fqdn of alternative server and set serverfqdn
		if alternativeServer_dn:
			alternativeServer_attr = lo.get(alternativeServer_dn,['uid'])
			if len(alternativeServer_attr) > 0:
				alternativeServer_uid = alternativeServer_attr['uid'][0]
				alternativeServer_uid = alternativeServer_uid.replace('$','')
				if len(alternativeServer_uid) > 0:
					serverfqdn = "%s.%s" % (alternativeServer_uid, domainname)
		# fetch serverfqdn from OU
		result = lo.get(getDN (schoolNr, basedn=base), ['ucsschoolClassShareFileServer'])
		if result:
			serverDomainName = lo.get(result['ucsschoolClassShareFileServer'][0], ['associatedDomain'])
			if serverDomainName:
				serverDomainName = serverDomainName['associatedDomain'][0]
			else:
				serverDomainName = domainname
			result = lo.get(result['ucsschoolClassShareFileServer'][0], ['cn'])
			if result:
				serverfqdn = "%s.%s" % (result['cn'][0], serverDomainName)


		position=univention.admin.uldap.position(base)
		position.setDn(position_dn[string.find(position_dn,",")+1:])
		object = module.object(co, lo, position, superordinate=superordinate)
		object.open()
		object["name"] = "%s"%classNr
		object["host"] = serverfqdn
		if configRegistry.is_true('ucsschool/import/roleshare', True):
			object["path"] = "/home/" + os.path.join(schoolNr, "groups/klassen/%s" % (classNr,))
		else:
			object["path"] = "/home/groups/klassen/%s" % (classNr,)
		object["writeable"] = "1"
		object["sambaWriteable"] = "1"
		object["sambaBrowseable"] = "1"
		object["sambaForceGroup"] = "+%s"%classNr
		object["sambaCreateMode"] = "0770"
		object["sambaDirectoryMode"] = "0770"
		object["owner"]="0"
		object["group"]=gid
		object["directorymode"]="0770"

		try:
			create_object(object)
		except:
			pass

	if not classNr in verified_group_shares:
		verified_group_shares.append((schoolNr, classNr.lower()))


def verify_group(position_dn, co, lo, superordinate, base):
	# look if group exists, else create it

	if position_dn in verified_groups:
		return True

	group_exists = 0
	type=position_dn[0:string.find(position_dn,"=")]
	name=position_dn[string.find(position_dn,"=")+1:string.find(position_dn,",")]
	position=position_dn[string.find(position_dn,",")+1:]

	more_instances = 1
	container_position = base
	while more_instances:
		container_position = position_dn[string.rfind(position_dn,",",0,len(position_dn)-len(container_position)-1)+1:]
		if container_position == position_dn:
			more_instances = 0
		elif container_position[:2] == "cn":
			verify_container(container_position, cn_module, co, lo, superordinate, base, path="groupPath")
		elif container_position[:2] == "ou":
			verify_container(container_position, ou_module, co, lo, superordinate, base)
		else: print "WARNING: unknown container type",container_position

	objects = univention.admin.modules.lookup(group_module, co, lo, scope='sub', superordinate=superordinate, base=position,
											  filter=univention.admin.filter.expression(type,name))
	if objects:
		for object in objects:
			if codecs.latin_1_encode(univention.admin.objects.dn(object))[0].lower() == position_dn.lower():
				group_exists = 1
				break

	if not group_exists:
		print "need to create group %s"%position_dn
		position=univention.admin.uldap.position(base)
		position.setDn(position_dn[string.find(position_dn,",")+1:])
		object = group_module.object(co, lo, position, superordinate=superordinate)
		object.open()
		object["name"] = position_dn[3:string.find(position_dn,",")]


		try:
			create_object(object)
		except:
			pass

	if not position_dn in verified_groups:
		verified_groups.append(position_dn)

def delete_dn(dn, module, co, lo):

	try:
		lo.search(base=dn, scope="base")
	except univention.admin.uexceptions.noObject:
		return

	position.setDn(dn[string.find(dn,",")+1:])
	object=univention.admin.objects.get(module, co, lo, position=position, dn=dn)
	try:
		object.open()
		object.remove()
		print "deleted:",dn
		return True
	except univention.admin.uexceptions.noObject:
		message = 'ERROR: delete this user manually (not found): ' + dn
	except:
		message = 'ERROR: delete this user manually (maybe not found):' + dn

	print message
	traceback.print_exc( 100, sys.stdout )
	raise DeleteObjectError( message )

def delete_user(person, module, co, lo):

	success = True

	# delete account
	try:
		delete_dn(person.getDN(), module, co, lo)
	except DeleteObjectError, e:
		person.problem_hints += str(e) + '\n'
		success = False

	return success

def verifyMailDomain(email):

	global mailDomains

	if not UCRV_create_mail_domain:
		return

	if not "@" in email:
		return

	domain = email.rsplit("@",1)[-1]
	if not mailDomains.get(domain):
		print "need to create mail domain %s " % domain
		pos = univention.admin.uldap.position("cn=domain,cn=mail,%s" % baseDN)
		obj = maildomain_module.object(co, lo, position=pos)
		obj["name"] = domain
		create_object(obj)
		mailDomains[domain] = 1
	return

def modify_user(person, module, co, lo):
	default_groups = person.default_groups()
	object=univention.admin.objects.get(module, co, lo, position='', dn=person.getDN())
	object.open()
	object["username"]=person.login
	object["primaryGroup"]=default_groups[0]
	subdir = roleshare_home_subdir(person.sNr, person.getRole(), configRegistry)
	object["unixhome"]="/home/" + os.path.join(subdir, person.login)
	object["firstname"]=person.name
	object["lastname"]=person.sname
	object["e-mail"]=person.mail
	if object.has_key('mailPrimaryAddress'):
		object['mailPrimaryAddress']=person.mail
		verifyMailDomain(person.mail)
	if person.isActive == "1":
		object["disabled"]="none"
	else:
		object["disabled"]="all"

	remove_groups = []
	for group in object['groups']:
		if group not in default_groups:
			parts = univention.admin.uldap.explodeDn( group )
			if len(parts) <= 2:
				continue

			# FIXME / TODO
			# Test should be following:
			# if ( ( ( parts[0].startswith( 'cn=%s' % grp_prefix_pupils) or parts[0].startswith( 'cn=%s' % grp_prefix_pupils) ) and parts[1] == 'cn=groups' and parts[2].startswith('ou=') ) or
			# 	 ( parts[1] == 'cn=klassen' and parts[2] == 'cn=%s' % cn_pupils and parts[3] == 'cn=groups' and parts[4].startswith('ou=') ) ):

			if ( parts[0].startswith( 'cn=%s' % grp_prefix_pupils ) or
				 parts[0].startswith( 'cn=%s' % grp_prefix_teachers ) or
				 ( parts[1] == 'cn=klassen' and parts[2] == 'cn=%s' % cn_pupils ) ):
				# group looks like a default group, so we don't need it anymore
				print "remove from group: %s"%group
				remove_groups.append(group)
	for group in remove_groups:
		object['groups'].remove(group)

	for group in default_groups:
		if group not in object["groups"]:
			object["groups"].append(group)
			verify_group(group, co, lo, superordinate, baseDN)
			print "need to add group %s"%group

	#print "user is in groups: %s" % object['groups']

	for cnr in person.cNr:
		verify_group_share(person.sNr,cnr,co,lo,superordinate,baseDN)

	try:
		object.modify()
		o_sNr=lo.search(base=person.getDN(), scope='base', attr=['departmentNumber'])
		if not o_sNr == person.allsNrs:
			lo.modify(person.getDN(),[("departmentNumber",o_sNr,person.allsNrs)])
		print "modified:",person.getDN()
	except:
		print "ERROR:", sys.exc_info()[0], sys.exc_info()[1]
		traceback.print_exc (100, sys.stdout)
		print "modify this user manually:",person.getDN()
		person.problem_hints = person.problem_hints + "modify this user manually: "+person.getDN()+"\n"


def check_user(person, baseDN, module):
	objects = univention.admin.modules.lookup(
		module, co, lo, scope='sub', superordinate=superordinate, 
		base=person.getPosition_dn(),
		filter=univention.admin.filter.expression('uid',person.login))
	if objects:
		for object in objects:
			if object.dn == person.getDN():
				return 1
	return None

def create_passwd(dn):

	# default
	pwLength = 8

	if dn:
		# get baseDn pw policy
		if not pwLengthOu.get(baseDN):
			try:
				results, policies = univention.lib.policy_result.policy_result(baseDN)
				pwLengthOu[baseDN] = int(results.get("univentionPWLength", ["8"])[0])
			except Exception:
				pass
		pwLength = pwLengthOu.get(baseDN, pwLength)

		# get ou pw policy
		ou = 'ou=' + dn[dn.find('ou=') + 3 :]
		if not pwLengthOu.get(ou):
			try:
				results, policies = univention.lib.policy_result.policy_result(ou)
				pwLengthOu[ou] = int(results.get("univentionPWLength", ["8"])[0])
			except Exception:
				pass
		pwLength = pwLengthOu.get(ou, pwLength)

	charlist = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!"$%&/()=?'
	passwd = ''
	for i in range(pwLength):
		passwd += charlist[random.randrange(0,len(charlist))]

	return passwd

def findComputerNameWithService(service, ou):
	computers = univention.admin.modules.lookup(computer_module, co, lo, scope='sub', base=ou, filter=univention.admin.filter.expression('univentionService', service))
	for computer in computers:
		return computer['name']
	return None

def calculateProfilePath(ou):
	if UCRV_serverprofile_option is not None:
		return UCRV_serverprofile_option
	if ou in calculateProfilePath.results:
		return calculateProfilePath.results[ou]
	computer = findComputerNameWithService('Windows Profile Server', ou)
	if computer is not None:
		calculateProfilePath.results[ou] = calculateProfilePath.string % ('\\\\%s' % (computer, ), )
	else:
		calculateProfilePath.results[ou] = calculateProfilePath.string % ('%LOGONSERVER%', )
	return calculateProfilePath.results[ou]
calculateProfilePath.results = {}
calculateProfilePath.string = '%s\\%%USERNAME%%\\windows-profiles\\default'

def tryFindHomeShareFileServer(ouDn):
	# if defined then use UCR value
	if UCRV_sambahome is not None:
		return '\\\\%s' % UCRV_sambahome.strip('\\')
	# in single server environments the master is always the fileserver
	if configRegistry.is_true('ucsschool/singlemaster', False):
		return '\\\\%s' % configRegistry.get('hostname')
	# if there's a cached result then use it
	if ouDn in tryFindHomeShareFileServer.results:
		return tryFindHomeShareFileServer.results[ouDn]
	# get windows home server from OU object
	oulist = univention.admin.modules.lookup(ou_module, co, lo, base=ouDn, scope='base')
	for ou in oulist:
		if ou['ucsschoolHomeShareFileServer']:
			serverlist = univention.admin.modules.lookup(computer_module, co, lo, base=ou['ucsschoolHomeShareFileServer'], scope='base')
			for server in serverlist:
				tryFindHomeShareFileServer.results[ouDn] = '\\\\%s' % server['name']
	return tryFindHomeShareFileServer.results.get(ouDn)
tryFindHomeShareFileServer.results = {}

def calculateSambahomePath(ouDn, username):
	if tryFindHomeShareFileServer(ouDn) is not None:
		return "%s\\%s" % (tryFindHomeShareFileServer(ouDn), username, )

def create_user(person, baseDN, module, password, OUTFILE, line):
	success = True

	if generate_logins:
		try:
			loginname = person.login
			i = 1
			while(check_user(person, baseDN, user_module)):
				print "WARNING: User already exists: %s"%person.getDN()
				person.problem_hints +="WARNING: Username already exists: %s" % person.getDN()+" appended counted number\n"
				person.login = loginname+str(i)
				print "INFO: Trying with new username: ", person.login
				i = i+1
		except: # happens if container does not exsist
			pass

	verify_school_ou(person.sNr, co, lo, baseDN)
	# check if needed subtree exists, this should not be neccessary be after 
	# verify of the ou but we may crash if someone makes changes by hand
	more_instances = 1
	container_position = baseDN
	while more_instances:
		container_position = person.getPosition_dn()[string.rfind(person.getPosition_dn(),",",0,
															  len(person.getPosition_dn())-len(container_position)-1)+1:]
		if container_position == person.getPosition_dn():
			more_instances = 0
		if container_position[:2] == "cn":
			verify_container(container_position, cn_module, co, lo, superordinate, baseDN)
		elif container_position[:2] == "ou":
			verify_container(container_position, ou_module, co, lo, superordinate, baseDN)
		else: print "WARNING: unknown container type",container_position


	groups = person.default_groups()
	for group in groups:
		verify_group(group, co, lo, superordinate, baseDN)
	for cnr in person.cNr:
		verify_group_share(person.sNr,cnr,co,lo,superordinate,baseDN)

	position=univention.admin.uldap.position(baseDN)
	position.setDn(person.getPosition_dn())

	object=user_module.object(co, lo, position=position, superordinate=superordinate)
	object.open()

	object["username"]=person.login
	object["primaryGroup"]=groups[0]
	if len(groups) > 1:
		object["groups"]=groups[1:]
	subdir = roleshare_home_subdir(person.sNr, person.getRole(), configRegistry)
	object["unixhome"]="/home/" + os.path.join(subdir, person.login)
	if object.has_key('mailbox'):
		object["mailbox"]="/var/spool/%s/"%person.login
	object["password"]=password
	object['overridePWHistory'] = '1'
	object['overridePWLength'] = '1'
	object["firstname"]=person.name
	object["lastname"]=person.sname
	object["e-mail"]=person.mail
	if object.has_key('mailPrimaryAddress'):
		object['mailPrimaryAddress']=person.mail
		verifyMailDomain(person.mail)
	if person.isActive == "1":
		object["disabled"]="none"
	else:
		object["disabled"]="all"

	# Bug #34179: do not set sambaHomePath for staff users
	if set(person.getRole()) != set([role_staff]):
		if UCRV_netlogon_script_path is not None:
			object["scriptpath"] = UCRV_netlogon_script_path
		if calculateSambahomePath(getDN(person.sNr, basedn=baseDN), object["username"]) is not None:
			object["sambahome"] = calculateSambahomePath(getDN(person.sNr, basedn=baseDN), object["username"])
		if UCRV_homedrive:
			object["homedrive"] = UCRV_homedrive
		profilePath = calculateProfilePath(getDN(person.sNr, basedn=baseDN))
		if profilePath:
			object["profilepath"] = profilePath

	try:
		try:
			exists, dn = create_object(object)
			lo.modify(person.getDN(),[("departmentNumber",[],person.allsNrs)])
			print "created:",person.getDN()
			if OUTFILE: OUTFILE.write("%s\t%s\t%s" % (person.login, password, line))
		except CreateObjectError, e:
			success = False
			print "ERROR: there were problems creating this user:",person.getDN()
			person.problem_hints += "ERROR: there were problems creating this user: "+person.getDN()+"\n"

	except:
		success = False
		print "ERROR:", sys.exc_info()[0], sys.exc_info()[1]
		print "ERROR: create this user manually:",person.getDN()
		traceback.print_exc (100, sys.stdout)
		person.problem_hints += "ERROR: create this user manually: " + person.getDN()+"\n"

	return success

def import_user():

	# usage
	usage  = "%prog [options] <importFile>\n"
	usage += "  creates/modifies/deletes user accounts according to import file for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <importFile> user import file"
	parser = optparse.OptionParser(usage=usage)
	parser.add_option("-o", "--outfile", dest="outfile", help="output file with passwords of created users")
	options, args = parser.parse_args()

	# argument is infile
	if len(args) > 0:
		infile = args[0]
	else:
		parser.print_help()
		print "ERROR: must have one argument for infile"
		sys.exit(1)

	print "infile is: " + infile
	inf = open(infile,'r')

	# output file
	OUT = None
	if options.outfile:
		if os.path.isfile(options.outfile):
			print "ERROR: outfile exists, will not overwrite existing file"
			sys.exit(1)
		print "outfile is: " + options.outfile 
		OUT = open(options.outfile, "w")


	with stopped_notifier():
		problem_hints=""
		linecnt=0

		for line in inf:
			linecnt += 1

			if emptyLineRe.match(line):
				print 'Skipping empty line'
				continue

			print 'Processing line %d: %s' % (linecnt, line),
			try:
				main_person = ucsschool_person_modify(line)
			except:
				msg = 'ERROR: Problems parsing line, skipped: %d: %s' % (linecnt, line)
				print msg
				traceback.print_exc (100, sys.stdout)
				problem_hints += msg + '\n'
				continue

			persons = [main_person]
			persons.extend(main_person.getOtherPersons())
			if main_person.password:
				passwd = main_person.password
			else:
				passwd = create_passwd(persons[0].getDN())

			# invoke pre hooks
			hooks.pre( 'user', main_person.modtype, line = line )

			# variable to store success of operation and LDAP dn
			success = True
			dn = None
			if main_person.modtype == "A":
				for person in persons:
					try:
						result = create_user(person, baseDN, user_module, passwd, OUT, line)
						success = result
					except:
						msg = "ERROR: There were problems creating this user: " + str(person.getDN())
						print msg
						traceback.print_exc (100, sys.stdout)
						person.problem_hints += msg + "\n"
						success = False
					if not dn:
						dn = person.getDN()
			elif main_person.modtype in ["M","D"]:
				# look if there are exsiting persons in other ou's remaining, this will fail if the users's departmentNumbers
				# are changed manually
				filter = univention.admin.filter.conjunction(
					"|",[univention.admin.filter.expression('uid',"%s_*"%main_person.login),
					univention.admin.filter.expression('uid',"%s"%main_person.login)])
				objects = univention.admin.modules.lookup(
					user_module, co, lo, scope='sub', superordinate=superordinate,
					base=baseDN, filter=filter)
				found = []
				for object in objects:
					ou=object.dn[string.find(object.dn,"ou=")+3:string.find(object.dn,",",string.find(object.dn,"ou=")+3)]
					# get from LDAP the user type: teacher, staff or student
					main_person.isTeacher = '0'
					main_person.isStaff   = '0'

					if object.dn.endswith(',cn=%s,cn=users,%s' % (cn_teachers_staff, getDN(ou))):
						main_person.isTeacher = '1'
						main_person.isStaff = '1'
					elif object.dn.endswith(',cn=%s,cn=users,%s' % (cn_teachers, getDN(ou))):
						main_person.isTeacher = '1'
					elif object.dn.endswith(',cn=%s,cn=users,%s' % (cn_staff, getDN(ou))):
						main_person.isStaff = '1'

					if ou in main_person.allsNrs:
						found.append(ou)
					else:
						main_person.problem_hints += "delete unmentioned user %s \n"%object.dn
						try:
							delete_dn(object.dn,user_module,co,lo)
						except DeleteObjectError, e:
							success = False
							main_person.problem_hints += str( e ) + '\n'

				for person in persons:
					if person.modtype == "M":
						if person.sNr in found:
							try:
								modify_user(person, user_module, co, lo)
							except:
								msg = "ERROR: There were problems modifying this user: "  + str(person.getDN())
								print msg
								traceback.print_exc (100, sys.stdout)
								person.problem_hints += msg + "\n"
								success = False
						else:
							try:
								print "WARNING: user to modify not found, ",
								result = create_user(person, baseDN, user_module, passwd, OUT, line)
							except:
								msg = "ERROR: There were problems creating this user: "  + str(person.getDN())
								print msg
								traceback.print_exc (100, sys.stdout)
								person.problem_hints += msg + "\n"
								success = False

					else: # main_person.modtype == "D":
						# NOTE: there is no need for this try-catch-block because delete_dn handles exceptions by itself
						success = delete_user(person, user_module, co, lo)

					if not dn:
						dn = person.getDN()
					main_person.problem_hints += person.problem_hints
			else:
				print "WARNING: unknown operation type:",main_person.modtype
				main_person.problem_hints += "unknown operation type for this user: "+main_person.getDN()+"\n"
				success = False

			problem_hints += main_person.problem_hints

			# invoke post hooks on success
			if success:
				hooks.post( 'user', main_person.modtype, dn = dn, line = line )

		# close files
		inf.close()
		if OUT:
			OUT.close()

		# report created users
		message=""
		if not problem_hints == "":
			message = message + "\n\nProblems during last run:\n\n" + problem_hints

		print message

def create_network(schoolNr, network, iprange=None, defaultrouter=None, nameserver=None, netbiosserver=None):
	"""
	@param	network	The network address MUST contain the netmask!! it could also be a ipaddr.IPv4Network object
	@param	defaultrouter	IP-Adress, if no netmask is profided it's derived from network
	@param	nameserver	IP-Adress, if no netmask is profided it's derived from network
	@param	netbiosserver	IP-Adress, if no netmask is profided it's derived from network
	"""
	success = True
	dn = None
	verify_school_ou(schoolNr, co, lo, baseDN)


	if check_network(schoolNr, network):
		print "Network %s/%d exists in school %s!" % (network.network, network.prefixlen, schoolNr)
		return ( False, dn )

	print "generate network %s/%d" % (network.network, network.prefixlen)
	if iprange:
		print "iprange: %s-%s" % (iprange[0], iprange[1])
	if defaultrouter:
		print "defaultrouter: %s" % defaultrouter
	if nameserver:
		print "nameserver: %s" % nameserver
	if netbiosserver:
		print "netbiosserver: %s" % netbiosserver

	# WORKAROUND for Bug #14795
	subnetbytes = 0
	tmp = str(network.netmask).split ('.')
	for i in tmp:
		if i == '255':
			subnetbytes += 1
		else:
			break
	subnet = '.'.join (str(network.network).split ('.')[:subnetbytes])
	# END WORKAROUND

	position.setDn("cn=dns,%s"%(baseDN))
	object=dns_reverse_zone_module.object(co, lo, position=position, superordinate=superordinate)
	object.open()
	object['subnet']     = subnet
	# the nameserver/SOA at the dns_reverse_zone object is always the ldap/master server!
	object['nameserver'] = configRegistry['ldap/master']
	object['contact']    = 'root@%s'%domainname
	try:
		create_object( object, ignore_exists = True )
	except:
		pass

	position.setDn("cn=%s,cn=dhcp,%s"%(schoolNr.lower (), getDN (schoolNr)))
	object=dhcp_service_module.object(co, lo, position=position, superordinate=superordinate)
	object=dhcp_subnet_module.object(co, lo, position=position, superordinate=dhcp_subnet_module)
	object.open()
	object['subnet'] = str(network.network)
	object['subnetmask'] = str(network.netmask)
	object['broadcastaddress'] = str(network.broadcast)
	try:
		create_object( object, ignore_exists = True )
	except:
		success = False

	position.setDn("cn=networks,%s" % (getDN (schoolNr), ))
	object=network_module.object(co, lo, position=position, superordinate=superordinate)
	object.open()
	object['name']='%s-%s'%(schoolNr.lower (), network.network)
	object['netmask']=str (network.prefixlen)
	object['network']=str(network.network)
	if iprange:
		object['ipRange']=[[str(iprange[0]), str(iprange[1])]]
	object['dhcpEntryZone']='cn=%s,cn=dhcp,%s'%(schoolNr.lower (), getDN (schoolNr))
	object['dnsEntryZoneForward']='zoneName=%s,cn=dns,%s'%(domainname, baseDN)
	object['dnsEntryZoneReverse']='zoneName=%s.in-addr.arpa,cn=dns,%s'%('.'.join (reversed (subnet.split ('.'))),baseDN)

	try:
		exists, dn = create_object( object, ignore_exists = True )
	except:
		success = False

	# set netbios and router for dhcp subnet
	if defaultrouter:
		print 'setting default router'
		set_router_for_subnet (network, defaultrouter, schoolNr)

	if netbiosserver:
		print 'setting netbios server'
		set_netbiosserver_for_subnet (network, netbiosserver, schoolNr)

	# set default value for nameserver
	if nameserver:
		print 'setting nameserver'
		set_nameserver_for_subnet (network, nameserver, schoolNr)

	return ( success, dn )

def check_network(schoolNr, network):
	"""
	@param	network	ipaddr.IPv4Network object
	"""
	objects = univention.admin.modules.lookup(
		network_module, co, lo, scope='sub', superordinate=superordinate,
		base='cn=networks,%s'%(getDN (schoolNr)),
		filter=univention.admin.filter.expression('cn','%s-%s'%(schoolNr.lower (), network.network)))
	if objects:
		n = "cn=%s-%s,cn=networks,%s"%(schoolNr.lower (), network.network, getDN (schoolNr))
		for object in objects:
			if object.dn == n:
				return 1

	return None

def check_macAddress(mac,base=''):
	if not base:
		base=baseDN
	result=lo.search(base=base, scope='sub',filter='(macAddress=%s)'%mac,attr=['macAddress'])

	if result and result[0][1]['macAddress'][0]==mac:
		return result[0][0]

def generate_new_mac(schoolNr,IP):
	ipnum=string.replace(IP,'.','')
	if IP:
		mac_start = '%s:%s:%s:%s:'%(schoolNr[:2],schoolNr[-2:],ipnum[:2],ipnum[-2:])
	else:
		# FIXME: following line seems to be broken
		mac_start = '%s:%s:00:00:'%(schoolNr[:2],schoolNr[-2:],ipnum[:2],ipnum[-2:])
	mac='%s00:00'%mac_start
	i=10
	j=10
	while check_macAddress(mac,base='cn=computers,%s' % (getDN (schoolNr), )):
		if i==99:
			i=10
			j+=1
		else:
			i+=1
		mac='%s%s:%s'%(mac_start,i,j)

	return mac

def check_computer(module, name, schoolNr):
	objects = univention.admin.modules.lookup(
		module, co, lo, scope='sub', superordinate=superordinate,
		base='cn=computers,%s' % (getDN (schoolNr), ),
		filter=univention.admin.filter.expression('cn','%s'%name))
	if objects:
		for object in objects:
			if object.dn == "cn=%s,cn=computers,%s"%(name, getDN (schoolNr)):
				return 1
	return None

def get_computer_dn(name):
	objects = univention.admin.modules.lookup(
		computer_module, co, lo, scope='sub', superordinate=superordinate,
		filter=univention.admin.filter.expression('cn','%s'%name))
	if objects:
		for object in objects:
			if object.dn.startswith("cn=%s," %name):
				return object.dn
	return None

def import_networks(router_only=False):

	# usage
	usage = "%prog [options] <importFile>\n"
	if router_only:
		usage += "  modifies default routers according to import file for ucs@school\n\n"
	else:
		usage += "  creates network objects according to import file for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <importFile> network import file"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	# argument is infile
	if len(args) > 0:
		infile = args[0]
	else:
		parser.print_help()
		print "must have one Argument for infile"
		sys.exit()

	print "infile is  : "+infile
	inf = open(infile,'r')

	problem_hints=""

	with stopped_notifier():
		for line in inf:	

			if emptyLineRe.match(line):
				continue

			success = True
			parsed=line.strip('\r\n').split(sepchar)

			schoolNr      = parsed[0]
			# the network MUST include the netmask
			network       = parsed[1] # netmask new
			# the broadcast address is generated automatically by the
			# combination of network and netmask
			def parseString (parsed, idx, default=None):
				res = default
				if len (parsed) > idx:
					string = parsed[idx]
					if string:
						res = string
				return res
			iprange       = parseString (parsed, 2) # new
			defaultrouter = parseString (parsed, 3) # new
			nameserver    = parseString (parsed, 4) # new
			netbiosserver = parseString (parsed, 5) # new

			# invoke pre hooks
			if router_only:
				hooks.pre( 'router', 'A', line = line )
			else:
				hooks.pre( 'network', 'A', line = line )
			dn = None
			success = True

			# convert parameters to ipaddr.IPv4Network
			if '/' not in network:
				network = '%s/%s' % (network, default_prefixlen, )
				try:
					network = ipaddr.IPv4Network(network)
				except ipaddr.AddressValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid ip address" % e
					continue
				print 'WARNING: no netmask specified for network %s using %s' % (network.network, network.netmask)
			else:
				try:
					network = ipaddr.IPv4Network(network)
				except ipaddr.AddressValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid ip address" % e
					continue
				except ipaddr.NetmaskValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid netmask" % e
					continue

			if iprange:
				tmp = iprange.split ('-')
				if len (tmp) != 2:
					print 'ERROR: IP host range not valid: %s' % iprange
					continue
				iprangeStart = ""
				iprangeEnd = ""
				try:
					iprangeStart = ipaddr.IPv4Address(tmp[0])
				except ipaddr.AddressValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid ip address" % e
					continue
				try:
					iprangeEnd = ipaddr.IPv4Address(tmp[1])
				except ipaddr.AddressValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid ip address" % e
					continue
				iprange = (iprangeStart, iprangeEnd)
				if iprange[0] < network.network + 1 \
						or iprange[0] > iprange[1] \
						or iprange[1] >= network.broadcast:
					print line.strip()
					print 'ERROR: IP host range not valid: %s-%s' % (iprange[0], iprange[1])
					continue
			elif network.prefixlen == 24:
				iprange = (network.network + 20,
						   network.network + 250)

			if defaultrouter:
				try:
					defaultrouter = ipaddr.IPv4Address(defaultrouter)
				except ipaddr.AddressValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid ip address" % e
					continue
			if nameserver:
				try:
					nameserver = ipaddr.IPv4Address(nameserver)
				except ipaddr.AddressValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid ip address" % e
					continue
			if netbiosserver:
				try:
					netbiosserver = ipaddr.IPv4Address(netbiosserver)
				except ipaddr.AddressValueError, e:
					print line.strip()
					print "ERROR: %s is not a valid ip address" % e
					continue

			if router_only:
				if defaultrouter:
					success, dn = set_router_for_subnet (network, defaultrouter, schoolNr, overwrite_policy=True)
					print 'Router set for subnet.'
				else:
					print 'No router specified for subnet.'
			else:
				success, dn = create_network( schoolNr, network, iprange=iprange, defaultrouter=defaultrouter, nameserver=nameserver, netbiosserver=netbiosserver )

			# invoke post hooks
			if success:
				if router_only:
					hooks.post( 'router', 'A', dn = dn, line = line )
				else:
					hooks.post( 'network', 'A', dn = dn, line = line )

		inf.close()

def set_inventory_number_for_computer():

	# usage
	usage = "%prog [options] <importFile>\n"
	usage += "  modifies computer inventory number according to import file for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <importFile> computer import file"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	# argument is infile
	if len(args) > 0:
		infile = args[0]
	else:
		parser.print_help()
		print "must have one Argument for infile"
		sys.exit()

	print "infile is  : "+infile
	inf = open(infile,'r')

	problem_hints=""

	with stopped_notifier():
		for line in inf:

			if emptyLineRe.match(line):
				continue

			parsed=line.strip('\r\n').split(sepchar)
			ctype    = parsed[0]
			name     = parsed[1]
			MAC      = parsed[2]
			inventoryNumbers = parsed[3].strip().split(',')

			dn = get_computer_dn(name)
			if not dn:
				print "Computer to modify not found: %s" % name
				problem_hints = problem_hints + "Computer to modify not found: %s" % name
			else:
				try:
					computerModule = univention.admin.modules.get("computers/%s"%ctype)
					position_basedn = univention.admin.uldap.position(baseDN)
					univention.admin.modules.init (lo, position_basedn, computerModule)
				except:
					print "failed to get module of type computers/%s" % ctype
					traceback.print_exc (100, sys.stdout)
					problem_hints = problem_hints + "failed to get module of type computers/%s" % ctype
					continue
				computerObject = univention.admin.objects.get(computerModule, co, lo, position='', dn=dn)
				computerObject.open()
				if MAC.replace('-',':').replace(' ',':').lower() in computerObject['mac']:
					try:
						computerObject['inventoryNumber'] = inventoryNumbers
						computerObject.modify()
						print "modified inventory Number for %s" % dn
					except:
						print "failed to modify computer %s" % name
						traceback.print_exc (100, sys.stdout)
						problem_hints = problem_hints + "failed to modify computer %s" % name
				else:
					print "mac address does not match while modifying %s" % name
					problem_hints = problem_hints + "mac address does not match while modifying %s" % name

		inf.close()

def set_policy_for_dhcp_subnet (network, schoolNr, policy_module, policy_dn, cn, values=None, overwrite_policy=False):
	"""
	@param	network	network where policy object shall be added
	@param	schoolNr	school number
	"""
	dn = None
	success = True
	if values is None:
		values = {}

	# check if routing policy needs to be created
	so = "cn=%s,cn=dhcp,%s" % (schoolNr.lower (), getDN (schoolNr))
	objects = univention.admin.modules.lookup(dhcp_subnet_module, co, lo, scope='sub', superordinate=so,
			base=baseDN, filter=univention.admin.filter.expression('cn', str(network.network)))
	dhcp_subnet_object=None
	subnet = "cn=%s,cn=%s,cn=dhcp,%s" % (str(network.network), schoolNr.lower (), getDN (schoolNr))
	if objects:
		for o in objects:
			if o.dn == subnet:
				dhcp_subnet_object = o
	if not dhcp_subnet_object:
		print "ERROR: Unable to set policy for dhcp subnet because it does not exist: %s" % subnet
		return ( False, dn )

	objects = univention.admin.modules.lookup(policy_module, co, lo, scope='sub', superordinate=superordinate,
			base=baseDN, filter=univention.admin.filter.expression('cn', cn))
	policy_object = None
	if objects:
		for o in objects:
			if o.dn == policy_dn:
				policy_object = o
	if not policy_object:
		position_dn = ','.join (policy_dn.split (',')[1:])
		verify_containers (position_dn, cn_module, co, lo, superordinate, baseDN, path='policyPath')
		position.setDn (position_dn)
		policy_object = policy_module.object(co, lo, position=position, superordinate=superordinate)
		policy_object.open()
		for k, v in values.iteritems ():
			policy_object[k] = v
		try:
			create_object(policy_object)
		except:
			print "WARNING: Error creating policy %s" % policy_object.dn
			traceback.print_exc (100, sys.stdout)
			policy_object = None
			success = False
	elif overwrite_policy:
		policy_object.open ()
		for k, v in values.iteritems ():
			policy_object[k] = v
		try:
			policy_object.modify ()
		except:
			print "WARNING: Error modifying policy %s" % policy_object.dn
			traceback.print_exc (100, sys.stdout)
			policy_object = None

	if policy_object:
		# add policy reference to dhcp_subnet_object
		dn = policy_object.dn
		try:
			lob = lo.get (dhcp_subnet_object.dn, ['objectClass'])
			if 'univentionPolicyReference' not in lob.get ('objectClass', []):
				lo.modify (dhcp_subnet_object.dn, [ ('objectClass','','univentionPolicyReference') ])

			lob = lo.get (dhcp_subnet_object.dn, ['univentionPolicyReference'])
			if policy_object.dn not in lob.get ('univentionPolicyReference', []):
				print 'connecting dhcp subnet (%s) with policy (%s)' % (dhcp_subnet_object.dn, policy_object.dn)
				lo.modify (dhcp_subnet_object.dn, [ ('univentionPolicyReference','',policy_object.dn) ])
		except:
			print "WARNING: Error modifying policy %s" % policy_object.dn
			traceback.print_exc (100, sys.stdout)
			policy_object = None
			success = False

	return ( success, dn )

def set_netbiosserver_for_subnet (network, ip, schoolNr, overwrite_policy=False):
	"""
	@param	ip	ipaddr object for the netbios server
	@param	schoolNr	school number

	The Netbios node type is set to 8 H-node: Hybrid - WINS, then broadcast
	"""
	values = {
			'name':'%s-%s' % (schoolNr.lower (), network.network),
			'netbios_node_type':'8',
			'netbios_name_servers':str(ip)}
	dn = "cn=%s,cn=netbios,cn=dhcp,cn=policies,%s" % (values['name'], getDN (schoolNr))
	return set_policy_for_dhcp_subnet (network, schoolNr, policy_dhcp_netbios_module, dn, values['name'], values=values, overwrite_policy=overwrite_policy)

def set_nameserver_for_subnet (network, ip, schoolNr, overwrite_policy=False):
	"""
	@param	ip	ipaddr object for the netbios server
	@param	schoolNr	school number
	"""
	values = {
			'name':'%s-%s' % (schoolNr.lower (), network.network),
			'domain_name':domainname,
			'domain_name_servers':str(ip)}
	dn = "cn=%s,cn=dns,cn=dhcp,cn=policies,%s" % (values['name'], getDN (schoolNr))
	return set_policy_for_dhcp_subnet (network, schoolNr, policy_dhcp_dns_module, dn, values['name'], values=values, overwrite_policy=overwrite_policy)

def set_router_for_subnet (network, ip, schoolNr, overwrite_policy=False):
	"""
	@param	ip	ipaddr object for the router
	@param	schoolNr	school number
	"""
	values = {
			'name':'%s-%s' % (schoolNr.lower (), network.network),
			'routers':str(ip)}
	dn = "cn=%s,cn=routing,cn=dhcp,cn=policies,%s" % (values['name'], getDN (schoolNr))
	return set_policy_for_dhcp_subnet (network, schoolNr, policy_dhcp_routing_module, dn, values['name'], values=values, overwrite_policy=overwrite_policy)

def import_computer():

	# usage
	usage = "%prog [options] <importFile>\n"
	usage += "  creates computer objects according to import file for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <importFile> computer import file"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	# argument is infile
	if len(args) > 0:
		infile = args[0]
	else:
		parser.print_help()
		print "must have one Argument for infile"
		sys.exit()

	print "infile is  : "+infile
	inf = open(infile,'r')

	problem_hints=""
	linecnt=0

	with stopped_notifier():
		for line in inf:
			linecnt += 1

			if emptyLineRe.match(line):
				print 'Skipping empty line'
				continue

			print 'Processing line %d: %s' % (linecnt, line),
			parsed=line.strip('\r\n').split(sepchar)
			ctype = parsed[0]
			name  = parsed[1].lower ()
			MAC      = parsed[2]
			schoolNr = parsed[3]
			IP       = parsed[4]
			inventoryNumbers = None
			if len(parsed) > 5:
				inventoryNumbers = parsed[5].strip().split(',')
			zone = None
			if len(parsed) > 6:
				zone = parsed[6]

			verify_school_ou(schoolNr, co, lo, baseDN)

			try:
				ip = ipaddr.IPv4Network(IP)
			except ipaddr.AddressValueError, e:
				print line
				print "%s is not a valid ip address" % e
				sys.exit(0)
			except ipaddr.NetmaskValueError, e:
				print line
				print "%s is not a valid netmask" % e
				sys.exit(0)

			# groups
			groups = {}
			if ctype == "memberserver":
				if zone == "edukativ":
					groups["cn=OU%s-Member-Edukativnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN)] = 1
					groups["cn=Member-Edukativnetz,cn=ucsschool,cn=groups,%s" % baseDN] = 1
				if zone == "verwaltung":
					groups["cn=OU%s-Member-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % (schoolNr.lower(), baseDN)] = 1
					groups["cn=Member-Verwaltungsnetz,cn=ucsschool,cn=groups,%s" % baseDN] = 1

			# invoke pre hooks
			hooks.pre( 'computer', 'A', line = line )

			success = True
			dn = None
			if '/' not in IP:
				ip = ipaddr.IPv4Network('%s/%s' % (IP, default_prefixlen, ))
				print 'WARNING: no netmask specified for ip address %s using %s' % (ip.ip, ip.netmask)

			module = univention.admin.modules.get("computers/%s"%ctype)
			if not module:
				print 'ERROR: Type "%s" not found for computer "%s".' % (ctype, name)
				print 'Ignoring of line %d' % linecnt
				success = False
			else:
				position_basedn = univention.admin.uldap.position(baseDN)
				univention.admin.modules.init (lo, position_basedn, module)

				if check_computer(module,name,schoolNr):
					print "computer %s already exists (school %s)"%(name,schoolNr)
					success = False
				elif check_macAddress(MAC):
					print 'mac %s for computer %s already used (school %s)' % (MAC, name, schoolNr, )
					success = False
				else:
					print "generate computer %s (school %s)"%(name,schoolNr)

					success = create_network(schoolNr, ip)

					position.setDn("cn=computers,%s" % (getDN (schoolNr), ))
					object=module.object(co, lo, position=position, superordinate=superordinate)
					object.open()
					object['name'] = name
					object['mac']  = MAC
					if inventoryNumbers:
						object['inventoryNumber']=inventoryNumbers
					if not ip.ip == ip.network:
						object['ip'] = str(ip.ip)
						print "set ip to %s is not net %s" % (ip.ip, ip.network)
					object['network'] = 'cn=%s-%s,cn=networks,%s' % (schoolNr.lower (), ip.network, getDN (schoolNr))
					for grp in groups:
						if not grp in object['groups']:
							object['groups'].append(grp)
					try:
						create_object(object)
					except:
						print "WARNING: Error creating computer %s"%object.dn
						traceback.print_exc (100, sys.stdout)
						success = False

				print 'Processing of line %d completed' % linecnt

			# invoke post hooks
			if success:
				hooks.post( 'computer', 'A', dn = object.dn, line = line )

		inf.close()

def import_router():
	"""
	No computer object is created on importing routers! But the dhcp-routing
	policy is set to the IP address of the router
	"""
	import_networks (router_only=True)

def import_group():

	# usage
	usage = "%prog [options] <importFile>\n"
	usage += "  creates/modifies/deletes group objects accoring to import file for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <importFile> group import file"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	# argument is infile
	if len(args) > 0:
		infile = args[0]
	else:
		parser.print_help()
		print "must have one Argument for infile"
		sys.exit()

	print "infile is  : "+infile
	inf = open(infile,'r')

	problem_hints=""

	with stopped_notifier():
		for line in inf:

			if emptyLineRe.match(line):
				continue

			parsed=line.strip('\r\n').split(sepchar)

			action = parsed[0]   # action=A(dd, M(odify, D(elete
			schoolNr = parsed[1]
			ClassID = parsed[2]
			Descrpt = parsed[3]

			group_dn="cn=%s,cn=klassen,cn=%s,cn=groups,%s"%(ClassID, cn_pupils, getDN (schoolNr))
			share_dn="cn=%s,cn=klassen,cn=shares,%s"%(ClassID, getDN (schoolNr))

			verify_school_ou(schoolNr, co, lo, baseDN)

			# invoke pre hooks
			hooks.pre( 'group', action, line = line )
			success = True
			dn = None

			if action in ['A','M']:	# Add/Modify group
	## 			verify_group(group_dn, co, lo, superordinate, baseDN)
	## 			object = univention.admin.objects.get(group_module, co, lo, position='', dn=group_dn)
	## 			object.open()
	## 			object['description']=Descrpt
	## 			object.modify()
	## 			verify_group_share(schoolNr, ClassID, co, lo, superordinate, baseDN)


	## 		elif action == 'M':	# M)odify group
				verify_group(group_dn, co, lo, superordinate, baseDN)
				object = univention.admin.objects.get(group_module, co, lo, position='', dn=group_dn)
				object.open()
				oldDescrpt = object['description']
				object['description']=Descrpt
				print "Changed Description of %s from %s to %s"%(group_dn, oldDescrpt, Descrpt)
				object.modify()
				verify_group_share(schoolNr, ClassID, co, lo, superordinate, baseDN)
				dn = object.dn
			elif action == 'D':	# D)elete group
				try:
					delete_dn(group_dn, group_module, co, lo)
					delete_dn(share_dn, group_module, co, lo)
					dn = group_dn
				except:
					success = False

			if success:
				hooks.post( 'group', action, dn = dn, line = line )

		inf.close()

def import_printer():

	# usage
	usage = "%prog [options] <importFile>\n"
	usage += "  creates/modifies/deletes printer objects according to import file for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <importFile> printer import file"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	# argument is infile
	if len(args) > 0:
		infile = args[0]
	else:
		parser.print_help()
		print "must have one Argument for infile"
		sys.exit()

	print "infile is  : "+infile
	inf = open(infile,'r')

	with stopped_notifier():
		for line in inf:

			if emptyLineRe.match(line):
				continue

			parsed=line.strip('\r\n').split(sepchar)

			# completely new
			action = parsed[0]   # action=A(dd, M(odify, D(elete
			schoolNr = parsed[1]
			spoolHost = parsed[2].lower ()
			if '.' not in spoolHost:
				spoolHost = '%s.%s' % (spoolHost, configRegistry['domainname'])
			printerName = parsed[3]
			printerUri = parsed[4]
			def parseString (parsed, idx, default=None):
				res = default
				if len (parsed) > idx:
					string = parsed[idx]
					if string:
						res = string
				return res
			printerModel = parseString (parsed, 5)

			position_dn ="cn=%s,cn=printers,%s" % (printerName, getDN (schoolNr))
			container ="cn=printers,%s" % (getDN (schoolNr), )

			# invoke pre hooks
			hooks.pre( 'printer', action, line = line )
			dn = None
			success = True

			if action in ['A','M']: # Add/Modify printer
				position=univention.admin.uldap.position(baseDN)
				position.setDn(container)
				if action == 'A':
					object = printer_module.object(co, lo, position, superordinate=superordinate)
				else:
					object = univention.admin.objects.get(printer_module, co, lo, position='', dn=position_dn)
				object.open()
				object['name'] = printerName
				object['spoolHost'] = [spoolHost]
				object['setQuota'] = '0' # WORKAROUND for Bug #14858
				object['uri'] = printer_module.unmapPrinterURI((printerUri, ))
				if printerModel:
					object['model'] = printerModel
				else:
					object['model'] = 'None'
				if action == 'A':
					try:
						create_object(object)
					except:
						success = False
					dn = object.dn
				else:
					try:
						print 'modifying object %s' % position_dn
						object.modify()
					except:
						traceback.print_exc (100, sys.stdout)
						success = False
					dn = object.dn

			elif action == 'D':	# D)elete printer
				dn = position_dn
				try:
					delete_dn(position_dn, printer_module, co, lo)
				except:
					success = False

			# invoke post hooks
			if success:
				hooks.post( 'printer', action, dn = dn, line = line )

		inf.close()

def activate_groupmembers():
	#  syntax: $0 <groupXXX> [0|1] [0|1]"
	# <groupXXX>    group
	# [0|1]         optional: deactivate | activate
	# [0|1]         optional: keep passwords | set random passwords

	chgrp = 1
	chpasswd = 0
	status = None


	# usage
	usage  = "%prog [options] <groupName> <newStatus> <changePassword>\n"
	usage += "  (de)activates group members for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <groupName>      group that shall be dis-/enabled\n"
	usage += "  <newStatus>      0=disabled, 1=enabled\n"
	usage += "  <changePassword> 0=keep passwords, 1=set random passwords\n"
	usage += "                   the logfile groupName.csv contains all activated users"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	if len(args) < 2:
		parser.print_help()
		print 'must have at least one argument'
		sys.exit()
	if len(args) > 0:
		actgrp = args[0]
	if len(args) > 1:
		try:
			status = int(args[1])
		except:
			parser.print_help()
			print 'invalid value for <newstatus>!'
			sys.exit()
	if len(args) > 2:
		try:
			chpasswd = int(args[2])
		except:
			parser.print_help()
			print 'invalid value for <changePassword>!'
			sys.exit()

	if status not in [0,1]:
		parser.print_help()
		print 'invalid value for <newstatus>!'
		sys.exit()
	elif chpasswd not in [0,1]:
		parser.print_help()
		print 'invalid value for <changePassword>!'
		sys.exit()

	# inverted logic
	if status == 0:
		disabled = "all"
	else:
		disabled = "none"

	###

	with stopped_notifier():
		# outfile = os.path.basename(actgrp+'.csv')
		outfile = "/var/lib/ucs-school-import/activate-grp-%s-%s.csv" % (time.strftime("%Y%m%d-%H%M%S"), actgrp)
		print "outfile is : "+outfile
		if os.path.exists(outfile):
			print 'outfile %s does already exist. Stopping here.' % outfile
			sys.exit()
		outf = open(outfile,'w')


		# get group
		objects = univention.admin.modules.lookup(group_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
												  filter=univention.admin.filter.expression('cn',actgrp))
		out_line = ('%%s%(sep)s' * 7 + '\n') % {'sep':sepchar}
		for grp in objects:

			# get group members
			if "uniqueMember" in grp.oldattr:
				grpmembers = grp.oldattr["uniqueMember"]
				for memberdn in grpmembers:

					membergrplist = ""
					grpobjlist = univention.admin.modules.lookup(group_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
															  filter=univention.admin.filter.expression('uniqueMember', memberdn))
					for grpobj in grpobjlist:
						if grpobj.dn != grp.dn:
							if len(membergrplist):
								membergrplist += "," + grpobj.oldattr["cn"][0]
							else:
								membergrplist += grpobj.oldattr["cn"][0]

					# modify each member
					memberobj = univention.admin.objects.get(user_module, co, lo, position='', dn=memberdn)
					memberobj.open()

					memberobj['disabled'] = disabled

					print memberdn,
					if status == 0:
						print 'deactivated',
					else:
						print 'activated',

					passwd = '*'
					if chpasswd:
						passwd = create_passwd(memberdn)
						memberobj['password'] = passwd
						memberobj['overridePWHistory'] = '1'
						memberobj['overridePWLength'] = '1'
						print 'and set random password',

					memberobj.modify()

					# write csv line
					snr = memberdn[ memberdn.find('ou=') + 3 : ]
					snr = snr[ : snr.find(',') ]
					outf.write(out_line % (snr, actgrp, memberobj['firstname'], memberobj['lastname'], passwd, memberobj['mailPrimaryAddress'], membergrplist))

					print

		outf.close()

def export_computer():
	#  syntax: $0 <ou> <filename>"
	# <ou>        computers of OU <ou> shall be exported
	# <filename>  output filename

	# usage
	usage = "%prog [options] <ouName> <fileName>\n"
	usage += "  export computer for specified ou in ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <ouName>         name of the ou from which the computers shall be exported\n"
	usage += "  <fileName>       export file"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	if len(args) != 2:
		parser.print_help()
		print 'must have at least two arguments'
		sys.exit()
	ou = args[0]
	outfile = args[1]

	# outfile = os.path.basename(actgrp+'.csv')
	print "outfile is : "+outfile
	if os.path.exists(outfile):
		print 'outfile %s does already exist. Stopping here.' % outfile
		sys.exit()
	outf = open(outfile,'w')

	ouBaseDN = getDN (ou)

	# get group
	print 'looking for objects... please wait...'
	objects = univention.admin.modules.lookup(computer_module, co, lo, scope='sub', superordinate=superordinate, base=ouBaseDN, filter=None)
	print 'writing data of %d objects...' % len(objects)
	out_line = ('%%s%(sep)s' * 5 + '\n') % {'sep':sepchar}
	for host in objects:
		host.open()
		inventoryNumber = ''
		ip = ''
		mac = ''
		if host.has_key('inventoryNumber') and host['inventoryNumber']:
			inventoryNumber = host['inventoryNumber'][0]
		if host.has_key('ip') and host['ip']:
			ip = host['ip'][0]
		if host.has_key('mac') and host['mac']:
			mac = host['mac'][0]
#		Name,OU,1.Zeile Inventar-Nr.,IP-Adresse
		outf.write(out_line % (host['name'], ou, inventoryNumber, ip, mac))
	outf.close()

def set_ou_sharefileserver():

	# usage
	usage = "%prog [options] <ouName> <shareFileServer>\n"
	usage += "  sets shareFileServer for ou in ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <ouName>          name of the ou\n"
	usage += "  <shareFileServer> name of the shareFileServer"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	if len(args) != 2:
		parser.print_help()
		print 'ouname and ShareFileServer is required'
		sys.exit(1)
	ouname = args[0]
	ShareFileServer = args[1]

	if not re.match('^[a-zA-Z0-9](([a-zA-Z0-9-_]*)([a-zA-Z0-9]$))?$', ouname):
		print 'ERROR: invalid ouname name given'
		sys.exit(1)
	if not re.match('^[a-zA-Z0-9](([a-zA-Z0-9-_]*)([a-zA-Z0-9]$))?$', ShareFileServer):
		print 'ERROR: invalid ShareFileServer name given'
		sys.exit(1)
	objects = lo.searchDn(filter='(&(objectClass=univentionHost)(cn=%s))' % ShareFileServer, base=baseDN)
	if len(objects) != 1:
		print 'ERROR: sharefileserver "%s" not found!' % ShareFileServer
		sys.exit(1)
	else:
		ShareFileServer = objects[0]


	with stopped_notifier():
		r = lo.get(getDN(ouname), ['objectClass'])
		if 'ucsschoolOrganizationalUnit' not in r['objectClass']:
			r = lo.modify(getDN(ouname), [ ('objectClass','','ucsschoolOrganizationalUnit'),
							   ('ucsschoolClassShareFileServer','',ShareFileServer)
							   ] )
		else:
			r = lo.get(getDN(ouname), ['ucsschoolClassShareFileServer'])
			if r:
				r = r['ucsschoolClassShareFileServer'][0]
			else:
				r = ''
			r = lo.modify(getDN(ouname), [ ('ucsschoolClassShareFileServer',r,ShareFileServer)
							   ] )
	sys.exit(0)

def rename_class():

	# usage
	usage = "%prog [options] <importFile>\n"
	usage += "  renames class groups and shares according to import file for ucs@school\n\n"
	usage += "Arguments:\n"
	usage += "  <importFile> rename class import file"
	parser = optparse.OptionParser(usage=usage)
	options, args = parser.parse_args()

	# argument is infile
	if len(args) > 0:
		infile = args[0]
	else:
		parser.print_help()
		print "must have one Argument for infile"
		sys.exit()

	# get all groups and shares
	myGroups = {}
	myShares = {}
	for group in univention.admin.modules.lookup(
		group_module, co, lo, scope="sub",
		superordinate=superordinate, 
		base=baseDN, filter="(objectClass=univentionGroup)"):
		cn = group.dn.split(",")[0].replace("cn=", "", 1)
		myGroups[cn] = group
	for share in univention.admin.modules.lookup(
		share_module, co, lo, scope="sub",
		superordinate=superordinate, 
		base=baseDN, filter="(objectClass=univentionShare)"):
		cn = share.dn.split(",")[0].replace("cn=", "", 1)
		myShares[cn] = share

	print "infile is  : "+infile
	inf = open(infile,'r')

	# get old and new group name from infile
	newGroups = []
	lineCounter = 0
	for line in inf:
		lineCounter += 1
		if emptyLineRe.match(line):
			continue
		parsed = line.strip('\r\n').split(sepchar)

		# save old and new name and the udm share and group
		# object
		groupObject = myGroups.get(parsed[0], None)
		shareObject = myShares.get(parsed[0], None)
		newGroups.append({
			"oldName":parsed[0],
			"newName":parsed[1],
			"share":shareObject,
			"group":groupObject,
			"line":line.strip(),
			"lineCounter":"%s" % lineCounter})

	# check groups
	error = ""
	for i in newGroups:
		oldName = i.get("oldName")
		newName = i.get("newName")
		line = i.get("line")
		lineCounter = i.get("lineCounter")

		# groups
		if not myGroups.get(oldName):
			error = "old group %s not found" % oldName
		elif myGroups.get(newName):
			error = "new group %s already exists" % newName
		else:
			del myGroups[oldName]
			myGroups[newName] = 1

		# shares
		if not myShares.get(oldName):
			error = "old share %s not found" % oldName
		elif myShares.get(newName):
			error = "new share %s already exists" % newName
		else:
			del myShares[oldName]
			myShares[newName] = 1

		if error:
			print "ERROR: %s (line %s: %s)" % (error, lineCounter, line)
			print "do nothing"
			sys.exit(0)

	with stopped_notifier():
		# do it, rename groups and shares and modify share path
		messages = []
		for i in newGroups:
			groupObject = i.get("group")
			shareObject = i.get("share")
			newName = i.get("newName")
			oldName = i.get("oldName")
			line = i.get("line")
			if groupObject and shareObject and newName and oldName:

				success = True

				# group pre mod hook
				hooks.pre("group", "M", line=line)

				# rename group
				groupObject.open()
				groupObject['name'] = newName
				try:
					groupObject.modify()
					messages.append("Renaming of group %s to %s successfully" % (oldName, newName))
				except Exception, e:
					success = False
					messages.append("Renaming of group %s to %s failed: %s" % (oldName, newName, str(e)))

				if success:
					# rename share and share path
					shareObject.open()
					path = shareObject['path'].split(os.path.sep)
					path[-1] = newName
					shareObject['name'] = newName
					if shareObject['sambaName'] == oldName:
						shareObject['sambaName'] = newName
					if shareObject['sambaForceGroup'] == '+%s' % (oldName, ):
						shareObject['sambaForceGroup'] = '+%s' % (newName, )
					shareObject['path'] = os.path.sep.join(path)	
					try:
						shareObject.modify()
						messages.append("Renaming of share %s to %s successfully" % (oldName, newName))
					except Exception, e:
						success = False
						messages.append("Renaming of share %s to %s failed: %s" % (oldName, newName, str(e)))

				# group post mod hook
				if success:
					hooks.post('group', "M", dn=groupObject.dn, line=line)


	# print warnings
	for msg in messages:
		print msg

	return


def move_domaincontroller_to_ou(co, lo, baseDN, options):
	"""
	Move specified domaincontroller (options.dcname) to specified OU (options.ou).
	"""
	slaves = univention.admin.modules.lookup(server_module, co, lo, scope='sub', base=baseDN, filter=univention.admin.filter.expression('cn', options.dcname))
	if not slaves:
		print 'ERROR: cannot find domaincontroller slave with hostname "%s"' % options.dcname
		sys.exit(1)
	if len(slaves) > 1:
		print 'ERROR: found more than one domaincontroller slave with hostname "%s"' % options.dcname
		sys.exit(1)

	oulist = univention.admin.modules.lookup(ou_module, co, lo, scope='sub', base=baseDN, filter=univention.admin.filter.expression('ou', options.ou))
	if not oulist:
		print 'ERROR: the given ou "%s" does not exist' % options.ou
		sys.exit(1)
	if len(oulist) > 1:
		print 'ERROR: found more than one ou "%s" in LDAP' % options.ou
		sys.exit(1)

	slave = slaves[0]
	ouDn = oulist[0].dn

	group_filter = univention.admin.filter.conjunction('&', [
			univention.admin.filter.conjunction('|', [
					univention.admin.filter.expression('cn', 'OU%s-DC-Edukativnetz' % options.ou),
					univention.admin.filter.expression('cn', 'OU%s-DC-Verwaltungsnetz' % options.ou),
					]),
			univention.admin.filter.expression('uniqueMember', slave.dn),
			])
	groups = univention.admin.modules.lookup(group_module, co, lo, scope='sub', base=baseDN, filter=group_filter)
	if not groups:
		print 'ERROR: cannot move domaincontroller slave with hostname "%s" to OU "%s"' % (options.dcname, options.ou)
		print 'ERROR: The system has no LDAP access to the OU'
		sys.exit(1)


	if slave.dn.endswith(',%s' % ouDn):
		print 'domaincontroller slave "%s" is already located below ou "%s" - stopping here' % (options.dcname, options.ou)
		sys.exit(0)

	REis_in_ou = re.compile('^.+,ou=[^,]+,%s$' % baseDN)
	if REis_in_ou.match(slave.dn) and not options.force:
		print 'ERROR: domaincontroller slave "%s" is located in another OU' % (options.dcname)
		print 'ERROR: DN of domaincontroller slave: %s' % slave.dn
		print 'ERROR: use --force to override'
		sys.exit(1)

	cnDn = 'cn=dc,cn=server,cn=computers,%s' % (getDN(options.ou),)
	containers = univention.admin.modules.lookup(cn_module, co, lo, scope='base', base=cnDn)
	if not containers:
		print 'ERROR: cannot move domaincontroller slave object - target container does not exist: %s' % cnDn
		sys.exit(1)

	oldDn = slave.dn
	newDn = 'cn=%s,cn=dc,cn=server,cn=computers,%s' % (options.dcname, getDN(options.ou))
	slave.open()
	print 'Moving %s to %s ...' % (oldDn, newDn)
	slave.move(newDn, ignore_license=1)

	oulist = univention.admin.modules.lookup(ou_module, co, lo, scope='sub', base=baseDN)
	for ou in oulist:
		ou.open()
		changed = False
		for property in ('ucsschoolClassShareFileServer', 'ucsschoolHomeShareFileServer'):
			if oldDn in ou[property]:
				ou[property] = newDn
				changed = True
		if changed:
			print 'Updating %s ...' % ou.dn
			ou.modify()


	removed = False
	ou_dhcpservice = None

	# find dhcp server object by checking all dhcp service objects
	dhcpservicelist = univention.admin.modules.lookup(dhcp_service_module, co, lo, scope='sub', base=baseDN)
	for dhcpservice in dhcpservicelist:
		# remember dhcpservice of target OU
		if dhcpservice.dn.endswith(',%s' % ouDn):
			ou_dhcpservice = dhcpservice

		# find dhcp server
		serverlist = univention.admin.modules.lookup(dhcp_server_module, co, lo, scope='sub', superordinate=dhcpservice, base=baseDN, filter=univention.admin.filter.expression('cn', options.dcname))
		if serverlist:
			if not serverlist[0].dn.endswith(',%s' % ouDn):
				removed = True
				print 'Removing %s' % serverlist[0].dn
				serverlist[0].open()
				attr_server = serverlist[0]['server']
				serverlist[0].remove()

	if removed:
		position = univention.admin.uldap.position('cn=%s,cn=dhcp,%s' % (options.ou, getDN(options.ou)))
		dhcpserver = dhcp_server_module.object(co, lo, position=position, superordinate=ou_dhcpservice)
		dhcpserver.open()
		dhcpserver["server"] = attr_server
		try:
			create_object(dhcpserver)
		except:
			print "ERROR: Failed to create dhcp server: %s" % (position.getDn(),)


	print 'Move complete'
	print 'HINT: the domaincontroller slave has to be rejoined into the domain!'


def create_dc(co, lo, baseDN, ou_name, dc_name, dc_type):
	if not dc_type in (TYPE_DC_EDUCATIONAL, TYPE_DC_ADMINISTRATIVE):
		print 'ERROR: invalid type for domaincontroller given in create_dc(): %r' % (dc_type, )
		sys.exit(1)

	if dc_type == TYPE_DC_ADMINISTRATIVE and not configRegistry.is_true('ucsschool/ldap/noneducational/create/objects', True):
		print 'ERROR: cannot create administrative DC - noneducational objects disabled via ucsschool/ldap/noneducational/create/objects'
		sys.exit(1)

	if not dc_name or not is_valid_hostname(dc_name):
		print 'ERROR: invalid administrative server name given: %r' % dc_name
		sys.exit(1)

	# verify if OU already exists - abort otherwise
	ou_objects = univention.admin.modules.lookup(ou_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
												 filter=univention.admin.filter.expression('ou', ou_name))
	if not ou_objects:
		print 'ERROR: specified OU %r does not exist' % ou_name
		sys.exit(1)


	# get list of desired group memberships
	group_dn_list = { TYPE_DC_ADMINISTRATIVE: ['cn=OU%s-DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s' % (ou_name.lower(), baseDN),
										   'cn=DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%s' % (baseDN,),
										   ],
					  TYPE_DC_EDUCATIONAL: ['cn=DC-Edukativnetz,cn=ucsschool,cn=groups,%s' % (baseDN,),
										  'cn=OU%s-DC-Edukativnetz,cn=ucsschool,cn=groups,%s' % (ou_name.lower(), baseDN),
										  ],
					  }[dc_type]
	for grpdn in group_dn_list:
		verify_group(grpdn, co, lo, superordinate, baseDN)


	host_objects = univention.admin.modules.lookup(computer_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
												   filter=univention.admin.filter.expression('cn', dc_name))
	slave_objects = univention.admin.modules.lookup(server_module, co, lo, scope='sub', superordinate=superordinate, base=baseDN,
													filter=univention.admin.filter.expression('cn', dc_name))
	if host_objects and not slave_objects:
		print 'ERROR: given host name "%s" is already in use and no domaincontroller slave system. Please choose another name.' % dc_name
		sys.exit(1)

	if slave_objects:
		# slave object does exist
		if len(slave_objects) > 1:
			print 'ERROR: more than one domaincontroller slave with cn=%s found in LDAP: %r' % (dc_name, slave_objects)
			sys.exit(1)

		slave_object = slave_objects[0]
		slave_object.open()
		modified = False
		for grpdn in group_dn_list:
			if grpdn not in slave_object["groups"]:
				slave_object["groups"].append(grpdn)
				modified = True
		if modified:
			modify_object(slave_object)
	else:
		# slave object does NOT exist - create missing DC object
		position = univention.admin.uldap.position(baseDN)
		position.setDn('cn=dc,cn=server,cn=computers,%s' % getDN(ou_name))
		slave_object = server_module.object(co, lo, position=position, superordinate=superordinate)
		slave_object.open()
		slave_object['name'] = dc_name
		slave_object['unixhome'] = '/dev/null'
		slave_object['shell'] = '/bin/bash'
		slave_object['primaryGroup'] = 'cn=DC Slave Hosts,cn=groups,%s' % (baseDN,)
		for grpdn in group_dn_list:
			if grpdn not in slave_object["groups"]:
				slave_object["groups"].append(grpdn)
		create_object(slave_object)


#------------------------------------------------------------------------------------------#
# MAIN                                                                                     #
#------------------------------------------------------------------------------------------#

# init univention-directory-manager
tls=2

bindpwd = None
with open('/etc/ldap.secret','r') as secretFile:
	bindpwd = secretFile.read()
binddn = 'cn=admin,' + baseDN

lo = None
try:
	lo = univention.admin.uldap.access(
		host=configRegistry['ldap/master'], base=baseDN,
		binddn=binddn, bindpw=bindpwd, start_tls=tls)
except Exception, e:
	univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'authentication error: %s' % str(e))
	print 'ERROR: authentication error: %s' % str(e)
	sys.exit(1)

co            = univention.admin.config.config()
position      = univention.admin.uldap.position(baseDN)
superordinate = None

univention.admin.modules.update()
user_module                = univention.admin.modules.get("users/user")
group_module               = univention.admin.modules.get("groups/group")
cn_module                  = univention.admin.modules.get("container/cn")
ou_module                  = univention.admin.modules.get("container/ou")
dcmaster_module            = univention.admin.modules.get("computers/domaincontroller_master")
dcbackup_module            = univention.admin.modules.get("computers/domaincontroller_backup")
server_module              = univention.admin.modules.get("computers/domaincontroller_slave")
share_module               = univention.admin.modules.get("shares/share")
printer_module             = univention.admin.modules.get("shares/printer")
computer_module            = univention.admin.modules.get("computers/computer")
dhcp_server_module         = univention.admin.modules.get("dhcp/server")
dhcp_service_module        = univention.admin.modules.get("dhcp/service")
dhcp_subnet_module         = univention.admin.modules.get("dhcp/subnet")
dns_reverse_zone_module    = univention.admin.modules.get("dns/reverse_zone")
network_module             = univention.admin.modules.get("networks/network")
policy_dhcp_routing_module = univention.admin.modules.get("policies/dhcp_routing")
policy_dhcp_netbios_module = univention.admin.modules.get("policies/dhcp_netbios")
policy_dhcp_dns_module     = univention.admin.modules.get("policies/dhcp_dns")
setting_module             = univention.admin.modules.get("settings/default")
maildomain_module          = univention.admin.modules.get("mail/domain")

for m in (
		user_module,
		group_module,
		cn_module,
		ou_module,
		server_module,
		share_module,
		printer_module,
		computer_module,
		dhcp_server_module,
		dhcp_service_module,
		dhcp_subnet_module,
		dns_reverse_zone_module,
		network_module,
		policy_dhcp_routing_module,
		policy_dhcp_netbios_module,
		policy_dhcp_dns_module,
		setting_module,
		maildomain_module,
		):
	univention.admin.modules.init (lo, position, m)

# get mail domains
for result in lo.search(base=baseDN, filter="(objectClass=univentionMailDomainname)"):
	cn = result[1].get("cn", [""])[0]
	if cn:
		mailDomains[cn] = 1

# get default group
default_group = "cn=Domain Users,cn=groups,%s"%baseDN # fallback
for object in univention.admin.modules.lookup(setting_module, co, lo, scope='sub', superordinate=superordinate, base='', filter=''):
	if object.has_key("defaultGroup"):
		default_group=object["defaultGroup"]
	else: print "WARNING, no default group found"

# select action
if sys.argv[0].endswith("import_user") or sys.argv[0].endswith("ucs-school-import"):
	import_user()
elif sys.argv[0].endswith("import_networks"):
	import_networks()
elif sys.argv[0].endswith("import_computer"):
	import_computer()
elif sys.argv[0].endswith("import_router"):
	import_router()
elif sys.argv[0].endswith("import_inventory_number"):
	set_inventory_number_for_computer()
elif sys.argv[0].endswith("import_group"):
	import_group()
elif sys.argv[0].endswith("import_printer"):
	import_printer()
elif sys.argv[0].endswith("create_ou"):
	dcNameEducational = None
	dcNameAdministrative = None

	usage  = '''%prog [options] <ou_name> [<educational_dc_name> [<administrative_dc_name>]]
 create school container for UCS@school

Arguments:
  <ou_name>		        name of ou that shall be created or verified
  <educational_dc_name>	hostname of the educational DC for specified OU
						(if dc_name is not specified "dc<ou_name>-01" will be
						used as the default for the domaincontroller name)
  <administrative_dc_name>  hostname of the administrative DC for the specified OU'''
	parser = optparse.OptionParser(usage=usage)
	parser.add_option(
		'--sharefileserver', action='store', type='string',
		dest='ShareFileServer', default=None,
		help="hostname of the fileserver within given OU that is used for class shares and user home shares")
	parser.add_option(
		'--displayName', action='store', type='string',
		dest='displayName', default=None,
		help='Descriptive name of the school OU that is shown within UCS@school modules as school name (e.g. "Gymnasium Mitte")')

	(options, args, ) = parser.parse_args()
	if len(args) > 0:
		ouname = args[0]
		if len(args) > 1:
			dcNameEducational = args[1]
		if len(args) > 2:
			dcNameAdministrative = args[2]

		if not is_valid_ou_name(ouname):
			print 'ERROR: invalid ou name given'
			sys.exit(1)

		for name in (dcNameEducational, dcNameAdministrative):
			if name and not is_valid_hostname(name):
				print 'ERROR: invalid domaincontroller name given: %r' % name
				sys.exit(1)

		if dcNameEducational and dcNameAdministrative and dcNameEducational == dcNameAdministrative:
			print 'ERROR: hostname of educational DC and administrative DC may not be equal!'
			sys.exit(1)

		if not options.ShareFileServer is None:
			if options.ShareFileServer and not is_valid_hostname(options.ShareFileServer):
				print 'ERROR: invalid share server name given: "%s"' % options.ShareFileServer
				sys.exit(1)
			objects = lo.searchDn(filter='(&(objectClass=univentionHost)(cn=%s))' % options.ShareFileServer, base=baseDN)
			if not objects:
				print 'ERROR: cannot find share server object with given name "%s"' % options.ShareFileServer
				sys.exit(1)

		# In a single server environment the default school DC is the master
		if not dcNameEducational and configRegistry.is_true('ucsschool/singlemaster', False):
			dcNameEducational = configRegistry.get('hostname')

		with stopped_notifier():
			verify_school_ou(ouname, co, lo, baseDN, dcName=dcNameEducational, ShareFileServer=options.ShareFileServer, displayName=options.displayName)
			if dcNameAdministrative:
				create_dc(co, lo, baseDN, ouname, dcNameAdministrative, TYPE_DC_ADMINISTRATIVE)
		sys.exit(0)
	parser.print_help()
	sys.exit(1)

elif sys.argv[0].endswith("create_dc"):
	usage = '''%prog --ou <ou> --name <dcname> --type <dctype>

%prog creates domaincontroller slave objects within the given OU.'''

	parser = optparse.OptionParser(usage=usage)
	parser.add_option('--name', action='store', dest='dc_name', default=None, help='hostname of the domaincontroller slave object that shall be created')
	parser.add_option('--ou', action='store', dest='ou_name', default=None, help='target OU where the domaincontroller slave object will be created in')
	parser.add_option('--type', action='store', dest='dc_type', default=None, help='UCS@school type of the domaincontroller slave object (valid values: %r, %r)' % (TYPE_DC_EDUCATIONAL, TYPE_DC_ADMINISTRATIVE))
	(options, args, ) = parser.parse_args()

	if not options.dc_name or not options.ou_name:
		print 'ERROR: no domaincontroller hostname or ou specified'
		print
		parser.print_help()
		sys.exit(1)

	if not is_valid_ou_name(options.ou_name):
		print 'ERROR: invalid ou name given'
		print
		parser.print_help()
		sys.exit(1)

	if not is_valid_hostname(options.dc_name):
		print 'ERROR: invalid domaincontroller name given'
		print
		parser.print_help()
		sys.exit(1)

	if not options.dc_type in (TYPE_DC_EDUCATIONAL, TYPE_DC_ADMINISTRATIVE):
		print 'ERROR: invalid type specified'
		print
		parser.print_help()
		sys.exit(1)

	with stopped_notifier():
		create_dc(co, lo, baseDN, options.ou_name, options.dc_name, options.dc_type)

elif sys.argv[0].endswith("move_domaincontroller_to_ou"):
	usage = '''%prog --dcname <dcname> --ou <ou>

%prog moves domaincontroller slave objects that are not located
within an OU LDAP subtree to the given OU.'''

	parser = optparse.OptionParser(usage=usage)
	parser.add_option('--dcname', action='store', dest='dcname', default=None, help='hostname of the domaincontroller slave object that shall be moved')
	parser.add_option('--ou', action='store', dest='ou', default=None, help='target OU where the domaincontroller slave object is moved to')
	parser.add_option('--force', action='store_true', dest='force', default=False, help='force move of domaincontroller slave object if located in other OU')
	(options, args, ) = parser.parse_args()

	if not options.dcname or not options.ou:
		print 'ERROR: no domaincontroller hostname or ou specified'
		print
		parser.print_help()
		sys.exit(1)

	if not is_valid_ou_name(options.ou):
		print 'ERROR: invalid ou name given'
		print
		parser.print_help()
		sys.exit(1)

	if not is_valid_hostname(options.dcname):
		print 'ERROR: invalid domaincontroller name given'
		print
		parser.print_help()
		sys.exit(1)

	with stopped_notifier():
		move_domaincontroller_to_ou(co, lo, baseDN, options)

elif sys.argv[0].endswith("activate_groupmembers"):
	activate_groupmembers()
elif sys.argv[0].endswith("export_computer"):
	export_computer()
elif sys.argv[0].endswith("set_ou_sharefileserver"):
	set_ou_sharefileserver()
elif sys.argv[0].endswith("rename_class"):
	rename_class()
else:
	print "unknown action defined by the filename"
