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 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 TestSamba4(object): def __init__(self): """ Test class constructor """ self.UCR = ConfigRegistry() self.client = None self.admin_username = '' self.admin_password = '' self.ldap_master = '' self.gpo_reference = '' def return_code_result_skip(self): """ Stops the test returning the code 77 (RESULT_SKIP). """ exit(TestCodes.REASON_INSTALL) def remove_samba_warnings(self, input_str): """ Removes the Samba Warning/Note from the given input_str. """ # ignoring following messages (Bug #37362): input_str = input_str.replace( 'WARNING: No path in service IPC$ - making it unavailable!', '') return input_str.replace('NOTE: Service IPC$ is flagged unavailable.', '').strip() def create_and_run_process(self, cmd, stdin=None, std_input=None, shell=False, stdout=PIPE): """ Creates a process as a Popen instance with a given 'cmd' and executes it. When stdin is needed, it can be provided with kwargs. To write to a file an istance can be provided to stdout. """ print '\n create_and_run_process(%r, shell=%r)' % (cmd, shell) proc = Popen(cmd, stdin=stdin, stdout=stdout, stderr=PIPE, shell=shell, close_fds=True) stdout, stderr = proc.communicate(input=std_input) if stderr: stderr = self.remove_samba_warnings(stderr) if stdout: stdout = self.remove_samba_warnings(stdout) return stdout, stderr def start_stop_service(self, service, action): """ Starts, stops or restarts the given 'service' depending on the given 'action' is 'start', 'stop', 'restart' respectively. """ if action in ("start", "stop", "restart"): cmd = ("service", service, action) print "\nExecuting command:", cmd stdout, stderr = self.create_and_run_process(cmd) if stderr: utils.fail( "An error occured during %sing the '%s' service: %s" % (action, service, stderr)) stdout = stdout.strip() if not stdout: utils.fail( "The %s command did not produce any output to stdout, while a confirmation was expected" % action) print stdout else: print( "\nUnknown state '%s' is given for the service '%s', accepted 'start' to start it 'stop' to stop or 'restart' to restart" % (action, service)) def dc_master_has_samba4(self): """ Returns 'True' when DC-Master has Samba4 according to "service=Samba 4" """ if not self.ldap_master: self.ldap_master = self.UCR.get('ldap/master') if self.ldap_master in self.get_udm_list_dcs('domaincontroller_master', with_samba4=True): return True def is_a_school_branch_site(self, host_dn): """ Returns True if the given 'host_dn' is located in the School branch site. """ if schoolldap.SchoolSearchBase.getOU(host_dn): return True def grep_for_key(self, grep_in, key): """ Runs grep on given 'grep_in' with a given 'key'. Returns the output. """ stdout, stderr = self.create_and_run_process(("grep", key), PIPE, grep_in) if stderr: utils.fail( "An error occured while running a grep with a keyword '%s':\n'%s'" % (key, stderr)) return stdout def sed_for_key(self, input, key): """ Runs sed on given 'input' with a given 'key'. Returns the output. """ cmd = ("sed", "-n", "s/%s//p" % (key, )) stdout, stderr = self.create_and_run_process(cmd, PIPE, input) if stderr: utils.fail( "An error occured while running a sed command '%s':\n'%s'" % (" ".join(cmd), stderr)) return stdout def get_udm_list_dcs(self, dc_type, with_samba4=True, with_ucsschool=False): """ Runs the "udm computers/'dc_type' list" and returns the output. If 'with_samba4' is 'True' returns only those running Samba 4. """ if dc_type not in ('domaincontroller_master', 'domaincontroller_backup', 'domaincontroller_slave'): print "\nThe given DC type '%s' is unknown" % dc_type self.return_code_result_skip() cmd = ("udm", "computers/" + dc_type, "list") if with_samba4: cmd += ("--filter", "service=Samba 4") if with_ucsschool: cmd += ("--filter", "service=UCS@school") stdout, stderr = self.create_and_run_process(cmd) if stderr: utils.fail( "An error occured while running a '%s' command to find all '%s' in the domain:\n'%s'" % (" ".join(cmd), dc_type, stderr)) return stdout def get_udm_list_dc_slaves_with_samba4(self, with_ucsschool=False): """ Returns the output of "udm computers/domaincontroller_slave list --filter service=Samba 4" command. """ return self.get_udm_list_dcs("domaincontroller_slave", with_ucsschool=with_ucsschool) def select_school_ou(self, schoolname_only=False): """ Returns the first found School OU from the list of DC-Slaves in domain. """ print "\nSelecting the School OU for the test" sed_stdout = self.sed_for_key( self.get_udm_list_dc_slaves_with_samba4(), "^DN: ") ous = [ schoolldap.SchoolSearchBase.getOUDN(x) for x in sed_stdout.split() ] ous = [ schoolldap.SchoolSearchBase.getOU(ou) if schoolname_only else ou for ou in ous if ou ] print "\nselect_school_ou: SchoolSearchBase found these OUs: %s" % ( ous, ) try: return ous[0] except IndexError: print "\nselect_school_ou: split: %s" % (sed_stdout.split(), ) utils.fail( "Could not find the DN in the udm list output, thus cannot select the School OU to use as a container" ) def get_samba_sam_ldb_path(self): """ Returns the 'sam.ldb' path using samba conf or defaults. """ print( "\nObtaining the Samba configuration to determine Samba private path" ) smb_conf_path = getenv("SMB_CONF_PATH") SambaLP = LoadParm() if smb_conf_path: SambaLP.load(smb_conf_path) else: SambaLP.load_default() return SambaLP.private_path('sam.ldb') def get_ucr_test_credentials(self): """ Loads the UCR to get credentials for the test. """ print( "\nObtaining Administrator username and password for the test from the UCR" ) try: self.UCR.load() self.admin_username = self.UCR['tests/domainadmin/account'] # extracting the 'uid' value of the administrator username string: self.admin_username = self.admin_username.split( ',')[0][len('uid='):] self.admin_password = self.UCR['tests/domainadmin/pwd'] except KeyError as exc: print( "\nAn exception while trying to read data from the UCR for the test: '%s'. Skipping the test." % exc) self.return_code_result_skip() def create_umc_connection_authenticate(self): """ Creates UMC connection and authenticates to DC-Master with the test user credentials. """ if not self.ldap_master: self.ldap_master = self.UCR.get('ldap/master') try: self.client = Client(self.ldap_master, self.admin_username, self.admin_password) except (ConnectionError, HTTPError) as exc: print( "An HTTP Error occured while trying to authenticate to UMC: %r" % exc) print "Waiting 10 seconds and making another attempt" sleep(10) self.client.authenticate(self.admin_username, self.admin_password) def delete_samba_gpo(self): """ Deletes the Group Policy Object using the 'samba-tool gpo del'. """ print( "\nRemoving previously created Group Policy Object (GPO) with a reference: %s" % self.gpo_reference) cmd = ("samba-tool", "gpo", "del", self.gpo_reference, "--username="******"--password="******"\nExecuting cmd:", cmd print( "\nAn error message while removing the GPO using 'samba-tool':\n%s" % stderr) print "\nSamba-tool produced the following output:\n", stdout
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