class SelfServiceUser(object): def __init__(self, username, password): self._client = Client() self.username = username self.password = password def request(self, uri, **kwargs): options = {'username': self.username, 'password': self.password} options.update(kwargs) return self._client.umc_command(uri, options) # TODO: kill all self-service UMC module processes because 1 process per request sums up and blocks resources for 15 minutes def get_contact(self): return dict( (data['id'], data['value']) for data in self.request('passwordreset/get_contact').result) def set_contact(self, email='', mobile=''): return self.request('passwordreset/set_contact', email=email, mobile=mobile).result def get_reset_methods(self): return [ x['id'] for x in self.request('passwordreset/get_reset_methods').result ] def send_token(self, method): return self.request('passwordreset/send_token', method=method).result def set_password(self, token, password): return self.request('passwordreset/set_password', token=token, password=password).result
class UmcComputer(object): def __init__(self, school, typ, name=None, ip_address=None, subnet_mask=None, mac_address=None, inventory_number=None): self.school = school self.typ = typ self.name = name if name else uts.random_name() self.ip_address = ip_address if ip_address else random_ip() self.subnet_mask = subnet_mask if subnet_mask else '255.255.255.0' self.mac_address = mac_address.lower() if mac_address else random_mac() self.inventory_number = inventory_number if inventory_number else '' self.ucr = ucr_test.UCSTestConfigRegistry() self.ucr.load() host = self.ucr.get('ldap/master') self.client = Client(host) account = utils.UCSTestDomainAdminCredentials() admin = account.username passwd = account.bindpw self.client.authenticate(admin, passwd) def create(self, should_succeed=True): """Creates object Computer""" flavor = 'schoolwizards/computers' param = [{ 'object': { 'school': self.school, 'type': self.typ, 'name': self.name, 'ip_address': self.ip_address, 'mac_address': self.mac_address.lower(), 'subnet_mask': self.subnet_mask, 'inventory_number': self.inventory_number }, 'options': None }] print 'Creating Computer %s' % (self.name, ) print 'param = %s' % (param, ) reqResult = self.client.umc_command('schoolwizards/computers/add', param, flavor).result if reqResult[0] == should_succeed: utils.wait_for_replication() elif should_succeed in reqResult[0]['result']['message']: print 'Expected creation fail for computer (%r)\nReturn Message: %r' % ( self.name, reqResult[0]['result']['message']) else: raise CreateFail( 'Unable to create computer (%r)\nRequest Result: %r' % (param, reqResult)) def remove(self): """Remove computer""" flavor = 'schoolwizards/computers' param = [{ 'object': { '$dn$': self.dn(), 'school': self.school, }, 'options': None }] reqResult = self.client.umc_command('schoolwizards/computers/remove', param, flavor).result if not reqResult[0]: raise RemoveFail('Unable to remove computer (%s)' % self.name) else: utils.wait_for_replication() def dn(self): return 'cn=%s,cn=computers,%s' % ( self.name, utu.UCSTestSchool().get_ou_base_dn(self.school)) def get(self): """Get Computer""" flavor = 'schoolwizards/computers' param = [{'object': {'$dn$': self.dn(), 'school': self.school}}] reqResult = self.client.umc_command('schoolwizards/computers/get', param, flavor).result if not reqResult[0]: raise GetFail('Unable to get computer (%s)' % self.name) else: return reqResult[0] def check_get(self): info = { '$dn$': self.dn(), 'school': self.school, 'type': self.typ, 'name': self.name, 'ip_address': [self.ip_address], 'mac_address': [self.mac_address.lower()], 'subnet_mask': self.subnet_mask, 'inventory_number': self.inventory_number, 'zone': None, 'type_name': self.type_name(), 'objectType': 'computers/%s' % self.typ } get_result = self.get() if get_result != info: diff = set(x for x in get_result if get_result[x] != info[x]) raise GetCheckFail( 'Failed get request for computer %s.\nReturned result: %r.\nExpected result: %r,\nDifference = %r' % (self.name, get_result, info, diff)) def type_name(self): if self.typ == 'windows': return 'Windows-System' elif self.typ == 'macos': return 'Mac OS X' elif self.typ == 'ipmanagedclient': return 'Gerät mit IP-Adresse' def edit(self, new_attributes): """Edit object computer""" flavor = 'schoolwizards/computers' param = [{ 'object': { '$dn$': self.dn(), 'name': self.name, 'school': self.school, 'type': self.typ, 'ip_address': new_attributes.get('ip_address') if new_attributes.get('ip_address') else self.ip_address, 'mac_address': new_attributes.get('mac_address').lower() if new_attributes.get('mac_address') else self.mac_address, 'subnet_mask': new_attributes.get('subnet_mask') if new_attributes.get('subnet_mask') else self.subnet_mask, 'inventory_number': new_attributes.get('inventory_number') if new_attributes.get('inventory_number') else self.inventory_number, }, 'options': None }] print 'Editing computer %s' % (self.name, ) print 'param = %s' % (param, ) reqResult = self.client.umc_command('schoolwizards/computers/put', param, flavor).result if not reqResult[0]: raise EditFail( 'Unable to edit computer (%s) with the parameters (%r)' % (self.name, param)) else: self.ip_address = new_attributes.get('ip_address') self.mac_address = new_attributes.get('mac_address').lower() self.subnet_mask = new_attributes.get('subnet_mask') self.inventory_number = new_attributes.get('inventory_number') utils.wait_for_replication() def query(self): """get the list of existing computer in the school""" flavor = 'schoolwizards/computers' param = {'school': self.school, 'filter': "", 'type': 'all'} reqResult = self.client.umc_command('schoolwizards/computers/query', param, flavor).result return reqResult def check_query(self, computers): q = self.query() k = [x['name'] for x in q] if not set(computers).issubset(set(k)): raise QueryCheckFail( 'computers from query do not contain the existing computers, found (%r), expected (%r)' % (k, computers)) def verify_ldap(self, should_exist): print 'verifying computer %s' % self.name utils.verify_ldap_object(self.dn(), should_exist=should_exist)
class QuotaCheck(object): def __init__(self, quota_type="usrquota", fs_type="ext4"): ucr = ucr_test.UCSTestConfigRegistry() ucr.load() self.ldap_base = ucr.get('ldap/base') self.my_fqdn = '%s.%s' % (ucr.get('hostname'), ucr.get('domainname')) account = utils.UCSTestDomainAdminCredentials() self.umc_client = Client(self.my_fqdn, username=account.username, password=account.bindpw) self.share_name = uts.random_name() self.share_name2 = uts.random_name() self.username = uts.random_name() self.quota_type = quota_type self.fs_type = fs_type def _activate_quota(self, loop_dev): print("Enable quota") options = {"partitionDevice": loop_dev} self.umc_client.umc_command('quota/partitions/activate', options).result def _check_quota_settings(self, loop_dev, expected_values={}): print("Check quota settings") options = {"filter": "*", "partitionDevice": loop_dev} user_quotas = self.umc_client.umc_command('quota/users/query', options).result expected_user_quota = { u'fileLimitHard': u'{}'.format(expected_values.get('fhard', 15)), u'fileLimitSoft': u'{}'.format(expected_values.get('fsoft', 10)), u'fileLimitTime': u'-', u'fileLimitUsed': u'1', u'id': u'{}@{}'.format(self.username, loop_dev), u'partitionDevice': u'{}'.format(loop_dev), u'sizeLimitHard': float(expected_values.get('bhard', 4)), u'sizeLimitSoft': float(expected_values.get('bsoft', 1)), u'sizeLimitTime': u'-', u'sizeLimitUsed': float(0), u'user': u'{}'.format(self.username), } print(expected_user_quota) assert expected_user_quota in user_quotas, "Quota was not set through pam" def test_quota_pam(self): with TempFilesystem(self.quota_type, fs_type=self.fs_type) as tfs, udm_test.UCSTestUDM() as udm: quota_policy = { "inodeSoftLimit": '10', "inodeHardLimit": '15', "spaceSoftLimit": str(1024 ** 2), "spaceHardLimit": str(2048 ** 2), "reapplyQuota": 'TRUE', "name": uts.random_name(), } self._activate_quota(tfs.loop_dev) print("Create Share") share = udm.create_object( 'shares/share', name=self.share_name, path=tfs.mount_point, host=self.my_fqdn, directorymode="0777" ) utils.wait_for_replication_and_postrun() qc.cache_must_exists(share) print("Create user") udm.create_user(username=self.username, check_for_drs_replication=False, wait_for=False) print("Create quota policy") policy = self.create_quota_policy(udm, quota_policy) print("Append quota policy") udm.modify_object("shares/share", dn=share, policy_reference=policy) utils.wait_for_replication_and_postrun() qc.check_values( share, quota_policy["inodeSoftLimit"], quota_policy["inodeHardLimit"], quota_policy["spaceSoftLimit"], quota_policy["spaceHardLimit"], quota_policy["reapplyQuota"] ) self.touch_file(tfs.mount_point) self._check_quota_settings(tfs.loop_dev) def test_quota_pam_policy_removal(self): with TempFilesystem(self.quota_type, fs_type=self.fs_type) as tfs, udm_test.UCSTestUDM() as udm: quota_policy = { "inodeSoftLimit": '10', "inodeHardLimit": '15', "spaceSoftLimit": str(1024 ** 2), "spaceHardLimit": str(2048 ** 2), "reapplyQuota": 'TRUE', "name": uts.random_name(), } expected_result = { 'bsoft': 1, 'bhard': 4, 'fsoft': 10, 'fhard': 15 } self._activate_quota(tfs.loop_dev) print("Create Share") share = udm.create_object( 'shares/share', name=self.share_name, path=tfs.mount_point, host=self.my_fqdn, directorymode="0777" ) utils.wait_for_replication_and_postrun() qc.cache_must_exists(share) print("Create user") udm.create_user(username=self.username, check_for_drs_replication=False) print("Create quota policy") policy = self.create_quota_policy(udm, quota_policy) print("Append quota policy") udm.modify_object("shares/share", dn=share, policy_reference=policy) utils.wait_for_replication_and_postrun() qc.check_values( share, quota_policy["inodeSoftLimit"], quota_policy["inodeHardLimit"], quota_policy["spaceSoftLimit"], quota_policy["spaceHardLimit"], quota_policy["reapplyQuota"] ) print("Simulate login") self.touch_file(tfs.mount_point) print("Remove quota policy") udm.modify_object("shares/share", dn=share, policy_dereference=policy) utils.wait_for_replication_and_postrun() print("Simulate login") self.touch_file(tfs.mount_point) self._check_quota_settings(tfs.loop_dev, expected_result) def test_two_shares_on_one_mount(self, quota_policies, expected_result): with TempFilesystem(self.quota_type, fs_type=self.fs_type) as tfs, udm_test.UCSTestUDM() as udm: self._activate_quota(tfs.loop_dev) print("Create Shares") share1_path = os.path.join(tfs.mount_point, self.share_name) share2_path = os.path.join(tfs.mount_point, self.share_name2) os.mkdir(share1_path) os.mkdir(share2_path) share1 = udm.create_object( 'shares/share', name=self.share_name, path=share1_path, host=self.my_fqdn, directorymode="0777" ) share2 = udm.create_object( 'shares/share', name=self.share_name2, path=share2_path, host=self.my_fqdn, directorymode="0777" ) utils.wait_for_replication_and_postrun() qc.cache_must_exists(share1) qc.cache_must_exists(share2) print("Create user") udm.create_user(username=self.username, check_for_drs_replication=False) print("Create quota policies") policies = [] for quota_policy in quota_policies: policies.append(self.create_quota_policy(udm, quota_policy)) print("Append quota policies") udm.modify_object("shares/share", dn=share1, policy_reference=policies[0]) udm.modify_object("shares/share", dn=share2, policy_reference=policies[1]) utils.wait_for_replication_and_postrun() self.touch_file(tfs.mount_point) self._check_quota_settings(tfs.loop_dev, expected_result) def test_two_shares_on_one_mount_only_one_policy(self): with TempFilesystem(self.quota_type, fs_type=self.fs_type) as tfs, udm_test.UCSTestUDM() as udm: quota_policy = { "inodeSoftLimit": '10', "inodeHardLimit": '15', "spaceSoftLimit": str(1024 ** 2), "spaceHardLimit": str(2048 ** 2), "reapplyQuota": 'TRUE', "name": uts.random_name(), } self._activate_quota(tfs.loop_dev) print("Create Shares") share1_path = os.path.join(tfs.mount_point, self.share_name) share2_path = os.path.join(tfs.mount_point, self.share_name2) os.mkdir(share1_path) os.mkdir(share2_path) share1 = udm.create_object( 'shares/share', name=self.share_name, path=share1_path, host=self.my_fqdn, directorymode="0777" ) share2 = udm.create_object( 'shares/share', name=self.share_name2, path=share2_path, host=self.my_fqdn, directorymode="0777" ) utils.wait_for_replication_and_postrun() qc.cache_must_exists(share1) qc.cache_must_exists(share2) print("Create user") udm.create_user(username=self.username, check_for_drs_replication=False) print("Create quota policy") policy = self.create_quota_policy(udm, quota_policy) print("Append quota policy") udm.modify_object("shares/share", dn=share1, policy_reference=policy) utils.wait_for_replication_and_postrun() self.touch_file(tfs.mount_point) self._check_quota_settings(tfs.loop_dev) def touch_file(self, mountpoint): print("Write file on filesystem as user: {}".format(self.username)) subprocess.check_call([ "sudo", "--user", self.username, "touch", os.path.join(mountpoint, "foo"), ]) def create_quota_policy(self, udm, quota_policy): return udm.create_object( 'policies/share_userquota', position='cn=userquota,cn=shares,cn=policies,%s' % self.ldap_base, name=quota_policy["name"], softLimitSpace=quota_policy["spaceSoftLimit"], hardLimitSpace=quota_policy["spaceHardLimit"], softLimitInodes=quota_policy["inodeSoftLimit"], hardLimitInodes=quota_policy["inodeHardLimit"], reapplyeverylogin=quota_policy["reapplyQuota"], )
class User(Person): """Contains the needed functuality for users in the UMC module schoolwizards/users.\n :param school: school name of the user :type school: str :param role: role of the user :type role: str ['student', 'teacher', 'staff', 'teacherAndStaff'] :param school_classes: dictionary of school -> list of names of the class which contain the user :type school_classes: dict """ def __init__(self, school, role, school_classes, mode='A', username=None, firstname=None, lastname=None, password=None, mail=None, schools=None): super(User, self).__init__(school, role) if username: self.username = username self.dn = self.make_dn() if firstname: self.firstname = firstname if lastname: self.lastname = lastname if mail: self.mail = mail if school_classes: self.school_classes = school_classes self.schools = schools or [self.school] self.typ = 'teachersAndStaff' if self.role == 'teacher_staff' else self.role self.mode = mode utils.wait_for_replication() self.ucr = ucr_test.UCSTestConfigRegistry() self.ucr.load() host = self.ucr.get('ldap/master') self.client = Client(host) account = utils.UCSTestDomainAdminCredentials() admin = account.username passwd = account.bindpw self.password = password if password else passwd self.client.authenticate(admin, passwd) def append_random_groups(self): pass def __enter__(self): return self def __exit__(self, type, value, trace_back): self.ucr.revert_to_original_registry() def create(self): """Creates object user""" flavor = 'schoolwizards/users' param = [{ 'object': { 'school': self.school, 'schools': self.schools, 'school_classes': self.school_classes, 'email': self.mail, 'name': self.username, 'type': self.typ, 'firstname': self.firstname, 'lastname': self.lastname, 'password': self.password }, 'options': None }] print '#### Creating user %s' % (self.username,) print '#### param = %s' % (param,) reqResult = self.client.umc_command('schoolwizards/users/add', param, flavor).result if not reqResult[0]: raise CreateFail('Unable to create user (%r)' % (param,)) else: utils.wait_for_replication() def get(self): """Get user""" flavor = 'schoolwizards/users' param = [{ 'object': { '$dn$': self.dn, 'school': self.school } }] try: reqResult = self.client.umc_command('schoolwizards/users/get', param, flavor).result except BadRequest as exc: if exc.status == 400: reqResult = [''] else: raise if not reqResult[0]: raise GetFail('Unable to get user (%s)' % self.username) else: return reqResult[0] def check_get(self, expected_attrs={}): info = { '$dn$': self.dn, 'display_name': ' '.join([self.firstname, self.lastname]), 'name': self.username, 'firstname': self.firstname, 'lastname': self.lastname, 'type_name': self.type_name(), 'school': self.school, 'schools': set(self.schools), 'disabled': 'none', 'birthday': None, 'password': None, 'type': self.typ, 'email': self.mail, 'objectType': 'users/user', 'school_classes': {}, } if self.is_student() or self.is_teacher() or self.is_teacher_staff(): info.update({'school_classes': self.school_classes}) if expected_attrs: info.update(expected_attrs) get_result = self.get() # Type_name is only used for display, Ignored info['type_name'] = get_result['type_name'] # ignore OU order get_result['schools'] = set(get_result['schools']) if get_result != info: diff = [] for key in (set(get_result.keys()) | set(info.keys())): if get_result.get(key) != info.get(key): diff.append('%s: Got:\n%r; expected:\n%r' % (key, get_result.get(key), info.get(key))) raise GetCheckFail('Failed get request for user %s:\n%s' % (self.username, '\n'.join(diff))) def type_name(self): if self.typ == 'student': return 'Student' elif self.typ == 'teacher': return 'Teacher' elif self.typ == 'staff': return 'Staff' elif self.typ == 'teacherAndStaff': return 'Teacher and Staff' def query(self): """get the list of existing users in the school""" flavor = 'schoolwizards/users' param = { 'school': self.school, 'type': 'all', 'filter': "" } reqResult = self.client.umc_command('schoolwizards/users/query', param, flavor).result return reqResult def check_query(self, users_dn): q = self.query() k = [x['$dn$'] for x in q] if not set(users_dn).issubset(set(k)): raise QueryCheckFail('users from query do not contain the existing users, found (%r), expected (%r)' % ( k, users_dn)) def remove(self, remove_from_school=None): """Remove user""" remove_from_school = remove_from_school or self.school print('#### Removing User %r (%s) from school %r.' % (self.username, self.dn, remove_from_school)) flavor = 'schoolwizards/users' param = [{ 'object': { 'remove_from_school': remove_from_school, '$dn$': self.dn, }, 'options': None }] reqResult = self.client.umc_command('schoolwizards/users/remove', param, flavor).result if not reqResult[0]: raise RemoveFail('Unable to remove user (%s)' % self.username) else: schools = self.schools[:] schools.remove(remove_from_school) if not schools: self.set_mode_to_delete() else: self.update(school=sorted(schools)[0], schools=schools, mode='M') try: del self.school_classes[remove_from_school] except KeyError: pass def edit(self, new_attributes): """Edit object user""" flavor = 'schoolwizards/users' object_props = { 'school': self.school, 'schools': self.schools, 'email': new_attributes.get('email') if new_attributes.get('email') else self.mail, 'name': self.username, 'type': self.typ, 'firstname': new_attributes.get('firstname') if new_attributes.get('firstname') else self.firstname, 'lastname': new_attributes.get('lastname') if new_attributes.get('lastname') else self.lastname, 'password': new_attributes.get('password') if new_attributes.get('password') else self.password, '$dn$': self.dn, } if self.typ not in ('teacher', 'staff', 'teacherAndStaff'): object_props['school_classes'] = new_attributes.get('school_classes', self.school_classes) param = [{ 'object': object_props, 'options': None }] print '#### Editing user %s' % (self.username,) print '#### param = %s' % (param,) reqResult = self.client.umc_command('schoolwizards/users/put', param, flavor).result if not reqResult[0]: raise EditFail('Unable to edit user (%s) with the parameters (%r)' % (self.username, param)) else: self.set_mode_to_modify() self.school_classes = new_attributes.get('school_classes', self.school_classes) self.mail = new_attributes.get('email') if new_attributes.get('email') else self.mail self.firstname = new_attributes.get('firstname') if new_attributes.get('firstname') else self.firstname self.lastname = new_attributes.get('lastname') if new_attributes.get('lastname') else self.lastname self.password = new_attributes.get('password') if new_attributes.get('password') else self.password
class Distribution(object): """Contains the needed functionality for Materials distribution. By default the distribution is manual.\n :param school: name of the ou :type school: str :param connection: :type connection: UMC connection object :param ucr: :type ucr: UCR object :param name: name of distribution project to be added later :type name: str :param description: description of distribution project to be added later :type description: str :param sender: name of the creater user (teacher or admin) :type sender: str :param flavor: flavor of the acting user :type flavor: str ('teacher' or 'admin') :param distributeTime: time for automatic distribution :type distributeTime: str ('%I:%M') :param distributionDate: date for automatic distribution :type distributionDate: str ('%Y-%m-%d) :param collectionTime: time for automatic collection :type collectionTime: str ('%I:%M') :param collectionDate: date for automatic collection :type collectionDate: str ('%Y-%m-%d) :param distributeType: type of the distribution :type distributionType: str ('automatic' or 'manual') :param collectionTye: type of the collection :type collectionType: str ('automatic' or 'manual') :param files: names of material files for the distribution project :type files: list of str :param recipients: groups which are included in the distribution project :type recipients: list of group objects """ def __init__(self, school, connection=None, sender=None, flavor=None, ucr=None, description=None, name=None, distributeType='manual', distributeTime=None, distributeDate=None, collectType='manual', collectTime=None, collectDate=None, files=[], recipients=[]): account = utils.UCSTestDomainAdminCredentials() admin = account.username passwd = account.bindpw self.school = school self.name = name if name else uts.random_string() self.description = description if description else uts.random_string() if distributeTime: self.distributeTime = distributeTime else: self.distributeTime = time.strftime('%I:%M') if distributeDate: self.distributeDate = distributeDate else: self.distributeDate = time.strftime('%Y-%m-%d') self.collectTime = collectTime if collectTime else time.strftime( '%I:%M') self.collectDate = collectDate if collectDate else time.strftime( '%Y-%m-%d') self.distributeType = distributeType self.collectType = collectType self.files = files self.recipients = recipients self.ucr = ucr if ucr else ucr_test.UCSTestConfigRegistry() self.sender = sender if sender else admin self.flavor = flavor if flavor else 'admin' if connection: self.client = connection else: self.client = Client(None, admin, passwd) def query(self, filt='private', pattern=''): """Calles 'distribution/query' :param pattern: the pattern to use in the search :type pattern: str """ flavor = self.flavor param = {'filter': filt, 'pattern': pattern} reqResult = self.client.umc_command('distribution/query', param, flavor).result result = [x['name'] for x in reqResult if reqResult is not None] return result def get(self): """Calls 'distribute/get'""" name = [self.name] reqResult = self.client.umc_command('distribution/get', name, self.flavor).result return reqResult[0] def idir(self, path): """Dir a specific path.\n :param path: wanted path :type path: str :return: list of file names """ files = [] for root, _, filenames in os.walk(path): for f in filenames: files.append(os.path.relpath(os.path.join(root, f), path)) return files def genData(self, file_name, content_type, boundary, flavor): """Generates data in the form to be sent via http POST request.\n :param file_name: file name to be uploaded :type file_name: str :param content_type: type of the content of the file :type content_type: str ('text/plain',..) :param boundary: the boundary :type boundary: str (-------123091) :param flavor: flavor of the acting user :type flavor: str """ with open(file_name, 'r') as f: data = r"""--{0} Content-Disposition: form-data; name="uploadedfile"; filename="{1}" Content-Type: {2} {3} --{0} Content-Disposition: form-data; name="flavor" {4} --{0} Content-Disposition: form-data; name="iframe" false --{0} Content-Disposition: form-data; name="uploadType" html5 --{0}-- """.format(boundary, file_name, content_type, f.read(), flavor) return data.replace("\n", "\r\n") def uploadFile(self, file_name, content_type): """Uploads a file via http POST request.\n :param file_name: file name to be uploaded :type file_name: str :param content_type: type of the content of the file :type content_type: str ('text/plain',..) """ print 'Uploading a file' boundary = '---------------------------103454444410473823401882756' data = self.genData(file_name, content_type, boundary, self.flavor) header_content = { 'Content-Type': 'multipart/form-data; boundary=%s' % (boundary, ) } self.client.request('POST', 'upload/distribution/upload', data, headers=header_content).result def add(self): """Create files and upload them then add the project, calls: 'distribution/add' """ # creatng and uploading the files content_type = 'text/plain' for filename, encoding in self.files: with open(filename, 'w') as g: g.write('test_content') self.uploadFile(filename, content_type) print 'Adding Project %s' % (self.name) flavor = self.flavor recipients = [] for item in self.recipients: recipients.append(item.dn()) print 'recipients=', recipients files = [ file_name.decode(encoding).encode('UTF-8') for file_name, encoding in self.files ] param = [{ 'object': { 'collectDate': self.collectDate, 'collectTime': self.collectTime, 'collectType': self.collectType, 'description': self.description, 'distributeDate': self.distributeDate, 'distributeTime': self.distributeTime, 'distributeType': self.distributeType, 'files': files, 'name': self.name, 'recipients': recipients }, 'options': None }] print 'param=', param reqResult = self.client.umc_command('distribution/add', param, flavor).result print 'reqResult =', reqResult if not reqResult[0]['success']: utils.fail('Unable to add project (%r)' % (param, )) def check_add(self): """Calls 'distribution/query' and check the existance of the added project """ print 'Checking %s addition' % (self.name, ) current = self.query(pattern=self.name) if not (self.name in current): utils.fail('Project %s was not added successfully' % (self.name, )) def put(self, description=None, distributeType=None, distributeTime=None, distributeDate=None, collectType=None, collectTime=None, collectDate=None, files=[], recipients=[]): """Modifies the already existing project.\n :param description: description of the project to be added later :type description: str :param distributeTime: time for automatic distribution :type distributeTime: str ('%I:%M') :param distributionDate: date for automatic distribution :type distributionDate: str ('%Y-%m-%d) :param collectionTime: time for automatic collection :type collectionTime: str ('%I:%M') :param collectionDate: date for automatic collection :type collectionDate: str ('%Y-%m-%d) :param distributeType: type of the distribution :type distributionType: str ('automatic' or 'manual') :param collectionTye: type of the collection :type collectionType: str ('automatic' or 'manual') :param files: names of material files for the distribution project :type files: list of str :param recipients: groups which are included in the project :type recipients: list of group objects """ print 'Editing Project %s' % (self.name) description = description if description else self.description if distributeType: distributeType = distributeType else: distributeType = self.distributeType if distributeTime: distributeTime = distributeTime else: distributeTime = self.distributeTime if distributeDate: distributeDate = distributeDate else: distributeDate = self.distributeDate collectType = collectType if collectType else self.collectType collectTime = collectTime if collectTime else self.collectTime collectDate = collectDate if collectDate else self.collectDate files = files if files else [x for x, y in self.files] recipients = recipients if recipients else self.recipients new_recipients = [] for item in recipients: new_recipients.append(item.dn()) flavor = self.flavor param = [{ 'object': { 'collectDate': collectDate, 'collectTime': collectTime, 'collectType': collectType, 'description': description, 'distributeDate': distributeDate, 'distributeTime': distributeTime, 'distributeType': distributeType, 'files': files, 'name': self.name, 'recipients': new_recipients }, 'options': None }] reqResult = self.client.umc_command('distribution/put', param, flavor).result print 'reqResult =', reqResult if not reqResult[0]['success']: utils.fail('Unable to edit project with params =(%r)' % (param, )) else: self.description = description self.distributeType = distributeType self.distributeTime = distributeTime self.distributeDate = distributeDate self.collectType = collectType self.collectTime = collectTime self.collectDate = collectDate self.files = [(x, 'utf8') for x in files] self.recipients = recipients def check_put(self, previousGetResult): """Calls 'distribution/get' and check the modified project :param previousGetResult: info from previous get :type previousGetResult: dict check changing sates for distribution and collection """ print 'Checking %s modification' % (self.name, ) found = self.get() supposed = { 'files': found['files'], 'sender': found['sender'], 'description': found['description'], 'recipients': found['recipients'], 'distributeType': found['distributeType'], '__type__': found['__type__'], 'collectType': found['collectType'], 'name': found['name'], 'starttime': found['starttime'], 'deadline': found['deadline'] } recips = [{'id': y.dn(), 'label': y.name} for y in self.recipients] if self.distributeType != 'automatic': sTime = None else: sTime = '%s %s' % (self.distributeDate, self.distributeTime) if self.collectType != 'automatic': dTime = None else: dTime = '%s %s' % (self.collectDate, self.collectTime) current = { 'files': [x for x, y in self.files], 'sender': self.sender, 'description': self.description, 'recipients': recips, 'distributeType': self.distributeType, '__type__': 'PROJECT', 'collectType': self.collectType, 'name': self.name, 'starttime': sTime, 'deadline': dTime, } print 'supposed = ', supposed print 'current = ', current fail_state = supposed != current if fail_state: utils.fail( 'Project %s was not modified successfully,supposed!=current' % (self.name, )) # check distribute check = 'distribution' before_type = previousGetResult['distributeType'] after_type = found['distributeType'] before_time = previousGetResult['starttime'] after_time = found['starttime'] before_atJob = previousGetResult['atJobNumDistribute'] after_atJob = found['atJobNumDistribute'] fail_state = fail_state or self.put_fail( before_type, after_type, before_time, after_time, before_atJob, after_atJob) if fail_state: utils.fail( 'Project %s was not modified successfully, %s: %s -> %s' % (self.name, check, before_type, after_type)) # check collect check = 'collection' before_type = previousGetResult['collectType'] after_type = found['collectType'] before_time = previousGetResult['deadline'] after_time = found['deadline'] before_atJob = previousGetResult['atJobNumCollect'] after_atJob = found['atJobNumCollect'] fail_state = fail_state or self.put_fail( before_type, after_type, before_time, after_time, before_atJob, after_atJob) if fail_state: utils.fail( 'Project %s was not modified successfully, %s: %s -> %s' % (self.name, check, before_type, after_type)) def put_fail(self, before_type, after_type, before_time, after_time, before_atJob, after_atJob): """Checks if the atjobs are in the expected formats :param before_type: type before using put command :type before_type: str :param after_type: type after using put command :type after_type: str :param before_atJob: atJobNum before using put command :type before_atJob: str or None :param after_atJob: atJobNum after using put command :type after_atJob: str or None :param before_time: time before using put command :type before_time: str :param after_time: time after using put command :type after_time: str """ fail_state = False # manual -> manual # atJobs == don't care if before_type == 'manual' and after_type == 'manual': pass # manual -> automatic # atJobs don't care -> int if before_type == 'manual' and after_type == 'automatic': fail_state = not (isinstance(after_atJob, (int, long))) # automatic -> manual # atJobs int -> don't care if before_type == 'automatic' and after_type == 'manual': fail_state = not (isinstance(before_atJob, (int, long))) # automatic -> automatic # atJobs int1 -> int2 & int1 < int2 if before_type == 'automatic' and after_type == 'automatic': fail1 = not (isinstance(before_atJob, (int, long)) and isinstance(after_atJob, (int, long))) fail2 = not (before_time != after_time and (before_atJob < after_atJob)) fail_state = fail1 or fail2 return fail_state def distribute(self): """Calls 'distribution/distribute'""" print 'Distributing Project %s' % (self.name) flavor = self.flavor reqResult = self.client.umc_command('distribution/distribute', [self.name], flavor).result if not reqResult[0]['success']: utils.fail('Unable to distribute project (%r)' % (self.name, )) def check_distribute(self, users): """Checks if the distribution was successful by checking the file system.\n :param users: names of users to have the material distributed for :type users: list of str """ print 'Checking %s distribution' % (self.name, ) for user in users: path = self.getUserFilesPath(user, 'distribute') print 'file_path=', path existingFiles = self.idir(path) print 'existingFiles=', existingFiles files = [x for x, y in self.files] if files != existingFiles: utils.fail('Project files were not distributed for user %s' % (user, )) def collect(self): """Calls 'distribution/collect'""" print 'Collecting Project %s' % (self.name) flavor = self.flavor reqResult = self.client.umc_command('distribution/collect', [self.name], flavor).result if not reqResult[0]['success']: utils.fail('Unable to collect project (%r)' % (self.name, )) def check_collect(self, users): """Checks if the collection was successful by checking the file system.\n :param users: names of users to have the material collected form :type users: list of str """ print 'Checking %s collection' % (self.name, ) for user in users: path = self.getUserFilesPath(user, 'collect') print 'file_path=', path existingFiles = self.idir(path) print 'existingFiles=', existingFiles files = [x for x, y in self.files] if files != existingFiles: utils.fail('Project files were not collected for user %s' % (user, )) def remove(self): """Calls 'distribution/remove'""" print 'Removing Project %s' % (self.name) flavor = self.flavor param = [{'object': self.name, 'options': None}] reqResult = self.client.umc_command('distribution/remove', param, flavor).result if reqResult: utils.fail('Unable to remove project (%r)' % (param, )) def check_remove(self): """Calls 'distribution/query' and check the existance of the removed project """ print 'Checking %s removal' % (self.name, ) current = self.query(pattern=self.name) if self.name in current: utils.fail('Project %s was not removed successfully' % (self.name, )) def checkFiles(self, files): """Calls 'distribution/checkfiles'""" print 'Checking files Project %s' % (self.name) flavor = self.flavor param = {'project': self.name, 'filenames': files} reqResult = self.client.umc_command('distribution/checkfiles', param, flavor).result if reqResult: utils.fail('Unable to chack files for project (%r)' % (param, )) def adopt(self, project_name): """Calls 'distribute/adopt'""" print 'Adopting project', self.name flavor = self.flavor reqResult = self.client.umc_command('distribution/adopt', [project_name], flavor).result if reqResult: utils.fail('Failed to adopt project (%r)' % (project_name, )) def check_adopt(self, project_name): print 'Checking adopting' q = self.query(pattern=project_name) if not (project_name in q): utils.fail('Project %s was not adopted successfully' % (project_name, )) def getUserFilesPath(self, user, purpose='distribute'): """Gets the correct files path for a specific user depending on the value of the ucr variable ucsschool/import/roleshare.\n :param user: user name :type user: str :param purpose: either for distribution or collection :type purpose: str ('distribute' or 'collect') """ path = '' self.ucr.load() roleshare = self.ucr.get('ucsschool/import/roleshare') sender_dir_name = self.ucr.get( 'ucsschool/datadistribution/datadir/sender', 'Unterrichtsmaterial') project_dir_suffix = self.ucr.get( 'ucsschool/datadistribution/datadir/sender/project/suffix', '-Ergebnisse') recipient_dir_name = self.ucr.get( 'ucsschool/datadistribution/datadir/recipient', 'Unterrichtsmaterial') if purpose == 'distribute': if roleshare == 'no' or roleshare is False: path = '/home/{0}/{1}/{2}/'.format(user, recipient_dir_name, self.name) else: path = '/home/{0}/schueler/{1}/{2}/{3}'.format( self.school, user, recipient_dir_name, self.name) elif purpose == 'collect': if roleshare == 'no' or roleshare is False: path = '/home/{0}/{1}/{2}{3}/{4}/'.format( self.sender, sender_dir_suffix, self.name, project_dir_name, user) else: path = '/home/{0}/lehrer/{1}/{2}/{3}{4}/{5}'.format( self.school, self.sender, sender_dir_name, self.name, project_dir_suffix, user) return path
class CSVImport(object): """CSVImport class, inclues all the needed operations to perform a user import""" def __init__(self, school, user_type): self.school = school self.user_type = user_type self.ucr = ucr_test.UCSTestConfigRegistry() self.ucr.load() host = self.ucr.get('hostname') self.client = Client(host) account = utils.UCSTestDomainAdminCredentials() admin = account.username passwd = account.bindpw self.client.authenticate(admin, passwd) def genData(self, boundary, file_name, content_type, school, user_type, delete_not_mentioned): """Generates data in the form to be sent via http POST request.\n :param file_name: file name to be uploaded :type file_name: str :param content_type: type of the content of the file :type content_type: str = 'text/csv' :param boundary: the boundary :type boundary: str (-------123091) :param flavor: flavor of the acting user :type flavor: str """ with open(file_name, 'r') as f: if delete_not_mentioned: data = r"""--{0} Content-Disposition: form-data; name="uploadedfile"; filename="{1}" Content-Type: {2} {3} --{0} Content-Disposition: form-data; name="school" {4} --{0} Content-Disposition: form-data; name="type" {5} --{0} Content-Disposition: form-data; name="delete_not_mentioned" true --{0} Content-Disposition: form-data; name="iframe" false --{0} Content-Disposition: form-data; name="uploadType" html5 --{0}-- """.format(boundary, file_name, content_type, f.read(), school, user_type) else: data = r"""--{0} Content-Disposition: form-data; name="uploadedfile"; filename="{1}" Content-Type: {2} {3} --{0} Content-Disposition: form-data; name="school" {4} --{0} Content-Disposition: form-data; name="type" {5} --{0} Content-Disposition: form-data; name="iframe" false --{0} Content-Disposition: form-data; name="uploadType" html5 --{0}-- """.format(boundary, file_name, content_type, f.read(), school, user_type) return data.replace("\n", "\r\n") def uploadFile(self, file_name, content_type, delete_not_mentioned, expected_upload_status): """Uploads a file via http POST request.\n :param file_name: file name to be uploaded :type file_name: str :param content_type: type of the content of the file :type content_type: str ('text/csv') """ print 'Uploading file %r' % file_name boundary = '---------------------------18209455381072592677374099768' data = self.genData(boundary, file_name, content_type, self.school, self.user_type, delete_not_mentioned) header_content = { 'Content-Type': 'multipart/form-data; boundary=%s' % (boundary, ) } try: response = self.client.request('POST', 'upload/schoolcsvimport/save', data, headers=header_content) except HTTPError as exc: response = exc.response status = response.status if status != expected_upload_status: raise FailHTTPStatus('Unexpected response status=%r' % status) elif status == 200: self.file_id = response.result[0]['file_id'] self.id_nr = 1 else: print 'Expected http_status = %r' % status return status def show(self): param = { 'file_id': self.file_id, 'columns': [ "name", "firstname", "lastname", "birthday", "password", "email", "school_classes" ], } if self.user_type == 'staff': param['columns'].remove('school_classes') try: reqResult = self.client.umc_command('schoolcsvimport/show', param).result self.id_nr = reqResult['id'] except FailShow: raise def progress(self): param = {'progress_id': self.id_nr} try: reqResult = self.client.umc_command('schoolcsvimport/progress', param).result except FailProgress: raise return reqResult def recheck(self, user): param = {'file_id': self.file_id, 'user_attrs': [user]} try: reqResult = self.client.umc_command('schoolcsvimport/recheck', param).result print 'RECHECK RESULT = ', reqResult return reqResult except FailRecheck: raise def schools(self): try: reqResult = self.client.umc_command('schoolcsvimport/schools', {}).result except FailSchools: raise return [x['id'] for x in reqResult] def check_schools(self): if self.school not in self.schools(): raise FailSchools( 'School %s not found by request: schoolcsvimport/schools' % (self.school)) def write_import_file(self, filename, lines, has_header=True): with open(filename, 'wb') as f: f.write(''.join(lines)) f.flush() def read_import_file(self, filename, has_header=True): with open(filename, 'rb') as f: lines = f.readlines() if has_header: columns = lines[0][:-1].split(',') lines = lines[1:] else: columns = [] lines = [x[:-1] for x in lines] return lines, columns def import_users(self, users): line_nr = 1 param = [] def get_type_name(typ): if typ == 'cSVStudent': return 'Student' elif typ == 'cSVTeacher': return 'Teacher' elif typ == 'cSVStaff': return 'Staff' elif typ == 'cSVTeachersAndStaff': return 'Teacher and Staff' for user in users: user.update({'line': line_nr}) user.update({'type_name': get_type_name(user['type'])}) options = {'file_id': self.file_id, 'attrs': user} line_nr += 1 param.append(options) try: pprint(('Importing users with parameters=', param)) reqResult = self.client.umc_command('schoolcsvimport/import', param).result self.id_nr = reqResult['id'] utils.wait_for_replication() except FailImport: raise
class QuoataCheck(object): def __init__(self, quota_type="usrquota", fs_type="ext4"): ucr = ucr_test.UCSTestConfigRegistry() ucr.load() self.my_fqdn = '%s.%s' % (ucr.get('hostname'), ucr.get('domainname')) account = utils.UCSTestDomainAdminCredentials() self.umc_client = Client(self.my_fqdn, username=account.username, password=account.bindpw) self.share_name = uts.random_name() self.username = uts.random_name() self.quota_type = quota_type self.fs_type = fs_type self.quota_policy = { "inodeSoftLimit": '10', "inodeHardLimit": '15', "spaceSoftLimit": str(1024**2), "spaceHardLimit": str(2048**2), "reapplyQuota": 'TRUE', "name": uts.random_name(), "position": 'cn=userquota,cn=shares,cn=policies,%s' % ucr.get('ldap/base'), } def _activate_quota(self, loop_dev): print("Enable quota") options = {"partitionDevice": loop_dev} result = self.umc_client.umc_command('quota/partitions/activate', options).result if not result.get('success'): utils.fail("Activating quota failed:\n{}".format(result)) def _check_quota_settings(self, loop_dev): print("Check quota settings") options = {"filter": "*", "partitionDevice": loop_dev} user_quotas = self.umc_client.umc_command('quota/users/query', options).result expected_user_quota = { u'fileLimitHard': u'15', u'fileLimitSoft': u'10', u'fileLimitTime': u'-', u'fileLimitUsed': u'1', u'id': u'{}@{}'.format(self.username, loop_dev), u'partitionDevice': u'{}'.format(loop_dev), u'sizeLimitHard': float(4), u'sizeLimitSoft': float(1), u'sizeLimitTime': u'-', u'sizeLimitUsed': float(0), u'user': u'{}'.format(self.username), } if expected_user_quota not in user_quotas: utils.fail("Quota was not set through pam") def test_quota_pam(self): with TempFilesystem( self.quota_type, fs_type=self.fs_type) as tfs, udm_test.UCSTestUDM() as udm: self._activate_quota(tfs.loop_dev) print("Create Share") share = udm.create_object('shares/share', name=self.share_name, path=tfs.mount_point, host=self.my_fqdn, directorymode="0777") utils.wait_for_replication_and_postrun() qc.cache_must_exists(share) print("Create user") udm.create_user(username=self.username, check_for_drs_replication=False) print("Create quota policy") policy = udm.create_object( 'policies/share_userquota', position=self.quota_policy["position"], name=self.quota_policy["name"], softLimitSpace=self.quota_policy["spaceSoftLimit"], hardLimitSpace=self.quota_policy["spaceHardLimit"], softLimitInodes=self.quota_policy["inodeSoftLimit"], hardLimitInodes=self.quota_policy["inodeHardLimit"], reapplyeverylogin=self.quota_policy["reapplyQuota"], ) print("Append quota policy") udm.modify_object("shares/share", dn=share, policy_reference=policy) utils.wait_for_replication_and_postrun() qc.check_values(share, self.quota_policy["inodeSoftLimit"], self.quota_policy["inodeHardLimit"], self.quota_policy["spaceSoftLimit"], self.quota_policy["spaceHardLimit"], self.quota_policy["reapplyQuota"]) print("Write file on filesystem as user: {}".format(self.username)) subprocess.check_call([ "sudo", "--user", self.username, "touch", os.path.join(tfs.mount_point, "foo"), ]) self._check_quota_settings(tfs.loop_dev)