#!/usr/share/ucs-test/runner python
## desc: Rename Class Function
## roles: [domaincontroller_master, domaincontroller_slave]
## tags: [apptest,ucsschool]
## exposure: dangerous
## packages: [ucs-school-import, ucs-school-singlemaster]

from univention.uldap import getMachineConnection
import glob
import grp
import os
import pwd
import subprocess
import tempfile
import univention.testing.strings as uts
import univention.testing.ucr as ucr_test
import univention.testing.ucsschool as utu
import univention.testing.utils as utils

BACKUP_PATH = '/home/backup/groups'


def ldap_info(cn):
	with ucr_test.UCSTestConfigRegistry() as ucr:
		basedn = ucr.get('ldap/base')
		lo = getMachineConnection()
		gidNumber = lo.search(filter="cn=%s" % cn, base=basedn)[0][1].get('gidNumber')
		memberUid = lo.search(filter="cn=%s" % cn, base=basedn)[0][1].get('memberUid')
		univentionShareGid = lo.search(filter="cn=%s" % cn, base=basedn)[1][1].get('univentionShareGid')
		univentionShareUid = lo.search(filter="cn=%s" % cn, base=basedn)[1][1].get('univentionShareUid')
	return {'gidNumber': gidNumber, 'memberUid': memberUid, 'univentionShareGid': univentionShareGid, 'univentionShareUid': univentionShareUid}


def check_backup(cn):
	print '** Checking backup.. '
	for _dir in glob.glob('%s/*' % BACKUP_PATH):
		if os.path.exists(_dir) and cn in _dir:
			utils.fail('%s was moved to backup' % cn)


def check_ldap(school, old_name, new_name):
	utils.verify_ldap_object(class_dn(old_name, school), should_exist=False)
	utils.verify_ldap_object(share_dn(old_name, school), should_exist=False)
	utils.verify_ldap_object(class_dn(new_name, school), should_exist=True)
	utils.verify_ldap_object(share_dn(new_name, school), should_exist=True)


def share_dn(class_name, school):
	with ucr_test.UCSTestConfigRegistry() as ucr:
		return 'cn=%s,cn=klassen,cn=shares,ou=%s,%s' % (class_name, school, ucr.get('ldap/base'))


def class_dn(class_name, school):
	with ucr_test.UCSTestConfigRegistry() as ucr:
		return 'cn=%s,cn=klassen,cn=schueler,cn=groups,ou=%s,%s' % (class_name, school, ucr.get('ldap/base'))


def share_path(class_name, school):
	path = '/home/%s/groups/klassen/%s' % (school, class_name)
	if os.path.exists(path):
		return path


def permissions(dir_path):
	"""Returns a tuple =
		(( dir_permissions(octal), owner), group ),
		{ path.basename(files): ( permissions(octal), owner, group ) }
	"""
	result = {}
	st = os.stat(dir_path)
	dir_permissions = ((oct(st.st_mode), pwd.getpwuid(st.st_uid)[0]), grp.getgrgid(st.st_gid)[0])
	for f in glob.glob('%s/*' % dir_path):
		if not os.path.islink(f):
			st = os.stat(f)
			result.update({os.path.basename(f): (oct(st.st_mode), pwd.getpwuid(st.st_uid)[0], grp.getgrgid(st.st_gid)[0])})
			if os.path.isdir(f):
				sub_result = permissions(f)
				result = dict(result.items() + sub_result.items())
	return dir_permissions, result


def check_permissions(old_dir, old_dir_permissions, new_dir, new_dir_permissions):
	current = old_dir_permissions[0][1]
	expected = os.path.basename(old_dir)
	if current != expected:
		utils.fail('%s is owned by the wrong group: %s, expected: %s' % (
			expected, current, expected))

	current = new_dir_permissions[0][1]
	expected = os.path.basename(new_dir)
	if current != expected:
		utils.fail('%s is owned by the wrong group: %s, expected: %s' % (
			expected, current, expected))

	if old_dir_permissions[0][0] != new_dir_permissions[0][0]:
		utils.fail('Permissions are changed old= %r, new= %r' % (
			old_dir_permissions[0][0], new_dir_permissions[0][0]))
	if old_dir_permissions[1] != new_dir_permissions[1]:
		utils.fail('Permissions are changed old= %r, new= %r' % (
			old_dir_permissions[1], new_dir_permissions[1]))


def test_rename_class(schoolenv, school, old_name, new_name, should_fail=False):
	old_dir = share_path(old_name, school)

	if old_dir:
		f = tempfile.NamedTemporaryFile(suffix='.import', dir=old_dir)
	else:
		if should_fail:
			f = tempfile.NamedTemporaryFile(suffix='.import')
		else:
			utils.fail('Share path does not exist: %s' % old_dir)
	print 'Import tempfile created %s' % f.name
	f.write('%s\t%s' % (old_name, new_name))
	f.flush()

	if old_dir:
		old_dir_permissions = permissions(old_dir)
		old_ldap_info = ldap_info(old_name)

	# Rename the class
	print '** renaming class %s to %s' % (old_name, new_name)
	cmd = ['/usr/share/ucs-school-import/scripts/rename_class', f.name]
	out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
	if 'ERROR' in out and not should_fail:
		utils.fail('Error not detected:\n%s' % out)
	elif 'ERROR' not in out:
		utils.wait_for_replication_and_postrun()

		# obvious check: old objects are renamed to new names in ldap
		check_ldap(school, old_name, new_name)

		# the share directory in file system should not be removed/moved to backup directory
		check_backup(old_name)

		# the share object should use the same uidNumber/gidNumber settings
		# the renamed group object should use the same gidNumber
		# the renamed group should still include the same users as before
		new_ldap_info = ldap_info(new_name)
		if old_ldap_info != new_ldap_info:
			utils.fail('%s has changed after renaming the class' % [x for x in old_ldap_info if old_ldap_info[x] != new_ldap_info[x]][0])

		# the renamed share object should be still accessable
		new_dir = share_path(new_name, school)
		if new_dir:
			new_dir_permissions = permissions(new_dir)
			check_permissions(old_dir, old_dir_permissions, new_dir, new_dir_permissions)


def create_two_users(schoolenv, school, class_name):
	tea, teadn = schoolenv.create_user(school, classes=class_name, is_teacher=True, use_cli=True)
	stu, studn = schoolenv.create_user(school, classes=class_name, use_cli=True)
	return tea, stu


def main():
	with ucr_test.UCSTestConfigRegistry() as ucr:
		with utu.UCSTestSchool() as schoolenv:
			school, oudn = schoolenv.create_ou(name_edudc=ucr.get('hostname'))

			old_name = '%s-%s' % (school, uts.random_name())
			new_name = '%s-%s' % (school, uts.random_name())
			test_rename_class(schoolenv, school, old_name, new_name, should_fail=True)

			old_name = '%s-%s' % (school, uts.random_name())
			new_name = '%s-%s' % (school, uts.random_name())
			create_two_users(schoolenv, school, old_name)
			test_rename_class(schoolenv, school, old_name, new_name)

			old_name = '%s-%s' % (school, uts.random_name())
			create_two_users(schoolenv, school, old_name)
			test_rename_class(schoolenv, school, old_name, new_name, should_fail=True)


if __name__ == '__main__':
	main()
