def user_can_view_jobs(self, job_list, username=None): """ Name ---- user_can_view_jobs (`job_list`) Administrators only: user_can_view_jobs (`job_list`, `username`) Description ----------- Check the access permissions on a list of jobs. Admins can specify a username as the second argument to run the query on behalf of that user. Arguments --------- job_list: list list of job ids to query username: string username of the user to query (admins only) Return value ------------ Returns a dictionary where the key is a string of the job_id from the job_list, if it exists in the queried instance. The value is a boolean for whether the user can access that job. { '1234': True, '1543': False } If the job number does not exist, that job will be omitted from the returned dictionary. This function requires authentication with a username and token. Example ------- server.system.user_can_view_jobs([1, 2, 3, 4, 99999, 4000]) {'1': True, '4000': True, '3': True, '2': True, '4': True} # if using the username and token of an admin, a different user can be queried: server.system.user_can_view_jobs([1, 2, 3, 4, 99999, 4000], 'firstname.lastname') {'1': True, '4000': False, '3': True, '2': True, '4': False} """ self._authenticate() if type(job_list) is not list: raise xmlrpclib.Fault(errors.BAD_REQUEST, "job list argument must be a list") username = self._switch_user(username) retval = {} for job_id in job_list: try: get_restricted_job(username, job_id) except Http404: continue except PermissionDenied: retval[str(job_id)] = False continue retval[str(job_id)] = True return retval
def testcase(request, job, pk, case): """ Each testcase can appear multiple times in the same testsuite and testjob, the action_data.action_level distinguishes each testcase. :param request: http request object :param job: ID of the TestJob :param pk: the name of the TestSuite :param case: the name of one or more TestCase objects in the TestSuite """ test_suite = get_object_or_404(TestSuite, name=pk, job=job) job = get_restricted_job(request.user, pk=job, request=request) test_cases = TestCase.objects.filter(name=case, suite=test_suite) template = loader.get_template("lava_results_app/case.html") return HttpResponse( template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to( testcase, pk=pk, job=job.id, case=case), 'job': job, 'suite': test_suite, 'job_link': pklink(job), 'test_cases': test_cases, 'bug_links': BugLink.objects.filter( object_id__in=test_cases.values_list('id', flat=True), content_type_id=ContentType.objects.get_for_model( TestCase).id, ) }, request=request))
def job_status(self, job_id): """ Name ---- `job_status` (`job_id`) Description ----------- Get the status of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job status with the following fields. The user is authenticated with an username and token. `job_status`: string ['Submitted'|'Running'|'Complete'|'Incomplete'|'Canceled'|'Canceling'] `bundle_sha1`: string The sha1 hash code of the bundle, if it existed. Otherwise it will be an empty string. (LAVA V1 only) """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") if job.is_pipeline: return { 'job_status': job.get_status_display(), 'bundle_sha1': "" } # DEPRECATED bundle_sha1 = "" if job.results_link: try: bundle_sha1 = job.results_link.split('/')[-2] except IndexError: pass job_status = { 'job_status': job.get_status_display(), 'bundle_sha1': bundle_sha1 } return job_status
def notify_incomplete_job(self, job_id): """ Name ---- `notify_incomplete_job` (`job_id`) Description ----------- Internal call to notify the master scheduler that a job on a remote worker ended in the Incomplete state. This allows the master to send the notification emails, if any. The status of the TestJob is not altered. Arguments --------- The TestJob.id which ended in status Incomplete. Return value ------------ None. The user should be authenticated with a username and token. """ if not self.user: raise xmlrpclib.Fault( 401, "Authentication with user and token required for this API.") if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not specified.") try: job = get_restricted_job(self.user, job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "TestJob with id '%s' was not found." % job_id) job.send_summary_mails()
def notify_incomplete_job(self, job_id): """ Name ---- `notify_incomplete_job` (`job_id`) Description ----------- Internal call to notify the master scheduler that a job on a remote worker ended in the Incomplete state. This allows the master to send the notification emails, if any. The status of the TestJob is not altered. Arguments --------- The TestJob.id which ended in status Incomplete. Return value ------------ None. The user should be authenticated with a username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault( 400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault( 404, "TestJob with id '%s' was not found." % job_id) job.send_summary_mails()
def resubmit_job(self, job_id): """ Name ---- `resubmit_job` (`job_id`) Description ----------- Resubmit the given job reffered by its id. Arguments --------- `job_id`: string The job's id which should be re-submitted. Return value ------------ This function returns an XML-RPC integer which is the newly created job's id, provided the user is authenticated with an username and token. """ try: job = get_restricted_job(self.user, job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") if job.is_multinode: return self.submit_job(job.multinode_definition) elif job.is_vmgroup: return self.submit_job(job.vmgroup_definition) else: return self.submit_job(job.definition)
def suite(request, job, pk): job = get_restricted_job(request.user, pk=job, request=request) test_suite = get_object_or_404(TestSuite, name=pk, job=job) data = SuiteView(request, model=TestCase, table_class=SuiteTable) suite_table = SuiteTable(data.get_table_data().filter(suite=test_suite)) RequestConfig(request, paginate={"per_page": suite_table.length}).configure( suite_table ) template = loader.get_template("lava_results_app/suite.html") return HttpResponse( template.render( { "bread_crumb_trail": BreadCrumbTrail.leading_to( suite, pk=pk, job=job.id ), "job": job, "job_link": pklink(job), "testsuite_content_type_id": ContentType.objects.get_for_model( TestSuite ).id, "suite_name": pk, "suite_id": test_suite.id, "suite_table": suite_table, "bug_links": BugLink.objects.filter( object_id=test_suite.id, content_type_id=ContentType.objects.get_for_model(TestSuite).id, ), }, request=request, ) )
def job_details(self, job_id): """ Name ---- `job_details` (`job_id`) Description ----------- Get the details of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job details, provided the user is authenticated with an username and token. """ if not self.user: raise xmlrpclib.Fault( 401, "Authentication with user and token required for this " "API.") try: job = get_restricted_job(self.user, job_id) job.status = job.get_status_display() except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") return job
def resubmit_job(self, job_id): """ Name ---- `resubmit_job` (`job_id`) Description ----------- Resubmit the given job reffered by its id. Arguments --------- `job_id`: string The job's id which should be re-submitted. Return value ------------ This function returns an XML-RPC integer which is the newly created job's id, provided the user is authenticated with an username and token. """ self._authenticate() try: job = get_restricted_job(self.user, job_id) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") if job.is_multinode: return self.submit_job(job.multinode_definition) else: return self.submit_job(job.definition)
def job_details(self, job_id): """ Name ---- `job_details` (`job_id`) Description ----------- Get the details of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job details, provided the user is authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault( 400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) job.status = job.get_status_display() except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") return job
def job_details(self, job_id): """ Name ---- `job_details` (`job_id`) Description ----------- Get the details of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job details, provided the user is authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) job.status = job.get_status_display() except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") return job
def suite(request, job, pk): job = get_restricted_job(request.user, pk=job, request=request) test_suite = get_object_or_404(TestSuite, name=pk, job=job) data = SuiteView(request, model=TestCase, table_class=SuiteTable) suite_table = SuiteTable( data.get_table_data().filter(suite=test_suite) ) RequestConfig(request, paginate={"per_page": suite_table.length}).configure(suite_table) template = loader.get_template("lava_results_app/suite.html") return HttpResponse(template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to(suite, pk=pk, job=job.id), 'job': job, 'job_link': pklink(job), 'testsuite_content_type_id': ContentType.objects.get_for_model( TestSuite).id, 'suite_name': pk, 'suite_id': test_suite.id, 'suite_table': suite_table, 'bug_links': BugLink.objects.filter( object_id=test_suite.id, content_type_id=ContentType.objects.get_for_model( TestSuite).id, ) }, request=request))
def job_output(self, job_id): """ Name ---- `job_output` (`job_id`) Description ----------- Get the output of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC binary data of output file, provided the user is authenticated with an username and token. """ if not self.user: raise xmlrpclib.Fault( 401, "Authentication with user and token required for this " "API.") try: job = get_restricted_job(self.user, job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") return xmlrpclib.Binary(job.output_file().read())
def testjob(request, job): job = get_restricted_job(request.user, pk=job, request=request) data = ResultsView(request, model=TestSuite, table_class=TestJobResultsTable) suite_table = TestJobResultsTable( data.get_table_data().filter(job=job) ) failed_definitions = [] yaml_dict = OrderedDict() if TestData.objects.filter(testjob=job).exists(): # some duplicates can exist, so get would fail here and [0] is quicker than try except. testdata = TestData.objects.filter( testjob=job).prefetch_related('actionlevels__testcase', 'actionlevels__testcase__suite')[0] if job.state == TestJob.STATE_FINISHED: # returns something like ['singlenode-advanced', 'smoke-tests-basic', 'smoke-tests-basic'] executed = [ { case.action_metadata['test_definition_start']: case.action_metadata.get('success', '')} for case in TestCase.objects.filter( suite__in=TestSuite.objects.filter(job=job)) if case.action_metadata and 'test_definition_start' in case.action_metadata and case.suite.name == 'lava'] submitted = [ actiondata.testcase.action_metadata for actiondata in testdata.actionlevels.all() if actiondata.testcase and 'test-runscript-overlay' in actiondata.action_name] # compare with a dict similar to created in executed for item in submitted: if executed and {item['name']: item['success']} not in executed: comparison = {} if item['from'] != 'inline': comparison['repository'] = item['repository'] comparison['path'] = item['path'] comparison['name'] = item['name'] comparison['uuid'] = item['success'] failed_definitions.append(comparison) # hide internal python objects, like OrderedDict for data in testdata.attributes.all().order_by('name'): yaml_dict[str(data.name)] = str(data.value) RequestConfig(request, paginate={"per_page": suite_table.length}).configure(suite_table) template = loader.get_template("lava_results_app/job.html") return HttpResponse(template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to(testjob, job=job.id), 'job': job, 'job_link': pklink(job), 'suite_table': suite_table, 'metadata': yaml_dict, 'failed_definitions': failed_definitions, 'condition_choices': simplejson.dumps( QueryCondition.get_condition_choices(job) ), 'available_content_types': simplejson.dumps( QueryCondition.get_similar_job_content_types() ), }, request=request))
def testjob(request, job): job = get_restricted_job(request.user, pk=job, request=request) data = ResultsView(request, model=TestSuite, table_class=TestJobResultsTable) suite_table = TestJobResultsTable( data.get_table_data().filter(job=job), request=request ) failed_definitions = [] yaml_dict = OrderedDict() if hasattr(job, "testdata"): for data in job.testdata.attributes.all().order_by("name"): yaml_dict[str(data.name)] = str(data.value) RequestConfig(request, paginate={"per_page": suite_table.length}).configure( suite_table ) template = loader.get_template("lava_results_app/job.html") return HttpResponse( template.render( { "bread_crumb_trail": BreadCrumbTrail.leading_to(testjob, job=job.id), "job": job, "job_link": pklink(job), "suite_table": suite_table, "metadata": yaml_dict, "condition_choices": simplejson.dumps( QueryCondition.get_condition_choices(job) ), "available_content_types": simplejson.dumps( QueryCondition.get_similar_job_content_types() ), }, request=request, ) )
def testcase(request, job, pk, case): """ Each testcase can appear multiple times in the same testsuite and testjob, the action_data.action_level distinguishes each testcase. :param request: http request object :param job: ID of the TestJob :param pk: the name of the TestSuite :param case: the name of one or more TestCase objects in the TestSuite """ test_suite = get_object_or_404(TestSuite, name=pk, job=job) job = get_restricted_job(request.user, pk=job, request=request) test_cases = TestCase.objects.filter(name=case, suite=test_suite) test_sets = TestSet.objects.filter(name=case, suite=test_suite) extra_source = {} logger = logging.getLogger('dispatcher-master') for extra_case in test_cases: try: f_metadata = yaml.load(extra_case.metadata, Loader=yaml.CLoader) except TypeError: logger.info("Unable to load extra case metadata for %s", extra_case) f_metadata = {} extra_data = f_metadata.get('extra', None) if extra_data and isinstance(extra_data, unicode) and os.path.exists(extra_data): with open(f_metadata['extra'], 'r') as extra_file: items = yaml.load(extra_file, Loader=yaml.CLoader) # hide the !!python OrderedDict prefix from the output. for key, value in items.items(): extra_source.setdefault(extra_case.id, '') extra_source[extra_case.id] += "%s: %s\n" % (key, value) template = loader.get_template("lava_results_app/case.html") return HttpResponse( template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to( testcase, pk=pk, job=job.id, case=case), 'job': job, 'sets': test_sets, 'suite': test_suite, 'job_link': pklink(job), 'extra_source': extra_source, 'test_cases': test_cases, 'bug_links': BugLink.objects.filter( object_id__in=test_cases.values_list('id', flat=True), content_type_id=ContentType.objects.get_for_model( TestCase).id, ) }, request=request))
def job_status(self, job_id): """ Name ---- DEPRECATED - use `job_health` or `job_state` instead. `job_status` (`job_id`) Description ----------- Get the status of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job status with the following fields. The user is authenticated with an username and token. `job_status`: string ['Submitted'|'Running'|'Complete'|'Incomplete'|'Canceled'|'Canceling'] `bundle_sha1`: string The sha1 hash code of the bundle, if it existed. Otherwise it will be an empty string. (LAVA V1 only) """ self._authenticate() if not job_id: raise xmlrpc.client.Fault( 400, "Bad request: TestJob id was not specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpc.client.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") job_status = {'job_id': job.id} if job.is_multinode: job_status.update({ 'sub_id': job.sub_id }) job_status.update({ 'job_status': job.get_legacy_status_display(), 'bundle_sha1': "" }) return job_status
def job_status(self, job_id): """ Name ---- DEPRECATED - use `job_health` or `job_state` instead. `job_status` (`job_id`) Description ----------- Get the status of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job status with the following fields. The user is authenticated with an username and token. `job_status`: string ['Submitted'|'Running'|'Complete'|'Incomplete'|'Canceled'|'Canceling'] `bundle_sha1`: string The sha1 hash code of the bundle, if it existed. Otherwise it will be an empty string. (LAVA V1 only) """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") job_status = {'job_id': job.id} if job.is_multinode: job_status.update({ 'sub_id': job.sub_id }) job_status.update({ 'job_status': job.get_legacy_status_display(), 'bundle_sha1': "" }) return job_status
def job_status(self, job_id): """ Name ---- `job_status` (`job_id`) Description ----------- Get the status of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job status with the following fields. The user is authenticated with an username and token. `job_status`: string ['Submitted'|'Running'|'Complete'|'Incomplete'|'Canceled'|'Canceling'] `bundle_sha1`: string The sha1 hash code of the bundle, if it existed. Otherwise it will be an empty string. """ self._authenticate() if not job_id: raise xmlrpclib.Fault( 400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") bundle_sha1 = "" try: bundle_sha1 = job.results_link.split('/')[-2] except: pass job_status = { 'job_status': job.get_status_display(), 'bundle_sha1': bundle_sha1 } return job_status
def job_status(self, job_id): """ Name ---- `job_status` (`job_id`) Description ----------- Get the status of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job status with the following fields. The user is authenticated with an username and token. `job_status`: string ['Submitted'|'Running'|'Complete'|'Incomplete'|'Canceled'|'Canceling'] `bundle_sha1`: string The sha1 hash code of the bundle, if it existed. Otherwise it will be an empty string. """ if not self.user: raise xmlrpclib.Fault( 401, "Authentication with user and token required for this " "API.") try: job = get_restricted_job(self.user, job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") bundle_sha1 = "" try: bundle_sha1 = job.results_link.split('/')[-2] except: pass job_status = { 'job_status': job.get_status_display(), 'bundle_sha1': bundle_sha1 } return job_status
def cancel_job(self, job_id): """ Name ---- `cancel_job` (`job_id`) Description ----------- Cancel the given job reffered by its id. Arguments --------- `job_id`: string Job id which should be canceled. Return value ------------ None. The user should be authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") with transaction.atomic(): try: job = get_restricted_job(self.user, job_id, for_update=True) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") if job.state in [TestJob.STATE_CANCELING, TestJob.STATE_FINISHED]: # Don't do anything for jobs that ended already return True if not job.can_cancel(self.user): raise xmlrpclib.Fault(403, "Permission denied.") if job.is_multinode: multinode_jobs = TestJob.objects.select_for_update().filter( target_group=job.target_group) for multinode_job in multinode_jobs: multinode_job.go_state_canceling() multinode_job.save() else: job.go_state_canceling() job.save() return True
def cancel_job(self, job_id): """ Name ---- `cancel_job` (`job_id`) Description ----------- Cancel the given job reffered by its id. Arguments --------- `job_id`: string Job id which should be canceled. Return value ------------ None. The user should be authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpc.client.Fault( 400, "Bad request: TestJob id was not specified.") with transaction.atomic(): try: job = get_restricted_job(self.user, job_id, for_update=True) except PermissionDenied: raise xmlrpc.client.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") if job.state in [TestJob.STATE_CANCELING, TestJob.STATE_FINISHED]: # Don't do anything for jobs that ended already return True if not job.can_cancel(self.user): raise xmlrpc.client.Fault(403, "Permission denied.") if job.is_multinode: multinode_jobs = TestJob.objects.select_for_update().filter( target_group=job.target_group) for multinode_job in multinode_jobs: multinode_job.go_state_canceling() multinode_job.save() else: job.go_state_canceling() job.save() return True
def testset(request, job, ts, pk, case): job = get_restricted_job(request.user, pk=job, request=request) test_suite = get_object_or_404(TestSuite, name=pk, job=job) test_set = get_object_or_404(TestSet, name=ts, suite=test_suite) test_cases = TestCase.objects.filter(name=case, test_set=test_set) template = loader.get_template("lava_results_app/case.html") return HttpResponse(template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to( testset, pk=pk, job=job.id, ts=ts, case=case), 'job': job, 'suite': test_suite, 'job_link': pklink(job), 'test_cases': test_cases, }, request=request))
def job_details(self, job_id): """ Name ---- `job_details` (`job_id`) Description ----------- Get the details of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job details, provided the user is authenticated with an username and token. The elements available in XML-RPC structure include: _state, submitter_id, is_pipeline, id, failure_comment, multinode_definition, user_id, priority, _actual_device_cache, original_definition, status, health_check, description, admin_notifications, start_time, target_group, visibility, pipeline_compatibility, submit_time, is_public, _old_status, actual_device_id, definition, sub_id, requested_device_type_id, end_time, group_id, absolute_url, submitter_username """ self._authenticate() if not job_id: raise xmlrpc.client.Fault( 400, "Bad request: TestJob id was not specified.") try: job = get_restricted_job(self.user, job_id) job.status = job.get_legacy_status_display() job.state = job.get_state_display() job.health = job.get_health_display() job.submitter_username = job.submitter.username job.absolute_url = job.get_absolute_url() job.is_pipeline = True except PermissionDenied: raise xmlrpc.client.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") return job
def cancel_job(self, job_id): """ Name ---- `cancel_job` (`job_id`) Description ----------- Cancel the given job reffered by its id. Arguments --------- `job_id`: string Job id which should be canceled. Return value ------------ None. The user should be authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault( 400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") if job.status > TestJob.RUNNING: # Don't do anything for jobs that ended already return True if not job.can_cancel(self.user): raise xmlrpclib.Fault(403, "Permission denied.") if job.is_multinode: multinode_jobs = TestJob.objects.filter( target_group=job.target_group) for multinode_job in multinode_jobs: multinode_job.cancel(self.user) elif job.is_vmgroup: for vmgroup_job in job.sub_jobs_list: vmgroup_job.cancel(self.user) else: job.cancel(self.user) return True
def job_details(self, job_id): """ Name ---- `job_details` (`job_id`) Description ----------- Get the details of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job details, provided the user is authenticated with an username and token. The elements available in XML-RPC structure include: _state, submitter_id, is_pipeline, id, failure_comment, multinode_definition, user_id, priority, _actual_device_cache, original_definition, status, health_check, description, admin_notifications, start_time, target_group, visibility, pipeline_compatibility, submit_time, is_public, _old_status, actual_device_id, definition, sub_id, requested_device_type_id, end_time, group_id, absolute_url, submitter_username """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) job.status = job.get_legacy_status_display() job.state = job.get_state_display() job.health = job.get_health_display() job.submitter_username = job.submitter.username job.absolute_url = job.get_absolute_url() job.is_pipeline = True except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") return job
def job_health(self, job_id): """ Name ---- `job_health` (`job_id`) Description ----------- Get the health of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job health with the following fields. The user is authenticated with an username and token. `job_health`: string ['Unknown'|'Complete'|'Incomplete'|'Canceled'] """ self._authenticate() if not job_id: raise xmlrpc.client.Fault( 400, "Bad request: TestJob id was not specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpc.client.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") job_health = { 'job_id': job.id, 'job_health': job.get_health_display() } if job.is_multinode: job_health.update({ 'sub_id': job.sub_id }) return job_health
def job_state(self, job_id): """ Name ---- `job_state` (`job_id`) Description ----------- Get the state of given job id. Arguments --------- `job_id`: string Job id for which the output is required. Return value ------------ This function returns an XML-RPC structures of job state with the following fields. The user is authenticated with an username and token. `job_state`: string ['Submitted'|'Scheduling'|'Scheduled'|'Running'|'Canceling'|'Finished'] """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") job_state = { 'job_id': job.id, 'job_state': job.get_state_display() } if job.is_multinode: job_state.update({ 'sub_id': job.sub_id }) return job_state
def job_output(self, job_id, offset=0): """ Name ---- `job_output` (`job_id`, `offset=0`) Description ----------- Get the output of given job id. Arguments --------- `job_id`: string Job id for which the output is required. `offset`: integer Offset from which to start reading the output file specified in bytes. It defaults to 0. Return value ------------ This function returns an XML-RPC binary data of output file, provided the user is authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpc.client.Fault( 400, "Bad request: TestJob id was not specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpc.client.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") # Open the logs output_path = os.path.join(job.output_dir, "output.yaml") try: with open(output_path, encoding="utf-8", errors="replace") as f_logs: if f_logs: f_logs.seek(offset) return xmlrpc.client.Binary(f_logs.read().encode("UTF-8")) except OSError: raise xmlrpc.client.Fault(404, "Job output not found.")
def testcase(request, job, pk, case): """ Each testcase can appear multiple times in the same testsuite and testjob, the action_data.action_level distinguishes each testcase. :param request: http request object :param job: ID of the TestJob :param pk: the name of the TestSuite :param case: the name of one or more TestCase objects in the TestSuite """ test_suite = get_object_or_404(TestSuite, name=pk, job=job) job = get_restricted_job(request.user, pk=job, request=request) test_cases = TestCase.objects.filter(name=case, suite=test_suite) test_sets = TestSet.objects.filter(name=case, suite=test_suite) extra_source = '' logger = logging.getLogger('dispatcher-master') for extra_case in test_cases: try: f_metadata = yaml.load(extra_case.metadata, Loader=yaml.CLoader) except TypeError: logger.info("Unable to load extra case metadata for %s", extra_case) f_metadata = {} extra_data = f_metadata.get('extra', None) if extra_data and isinstance(extra_data, unicode) and os.path.exists(extra_data): with open(f_metadata['extra'], 'r') as extra_file: items = yaml.load(extra_file, Loader=yaml.CLoader) # hide the !!python OrderedDict prefix from the output. for key, value in items.items(): extra_source += "%s: %s\n" % (key, value) template = loader.get_template("lava_results_app/case.html") return HttpResponse(template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to(testcase, pk=pk, job=job.id, case=case), 'job': job, 'sets': test_sets, 'suite': test_suite, 'job_link': pklink(job), 'extra_source': extra_source, 'test_cases': test_cases, 'bug_links': BugLink.objects.filter( object_id__in=test_cases.values_list('id', flat=True), content_type_id=ContentType.objects.get_for_model( TestCase).id, ) }, request=request))
def cancel_job(self, job_id): """ Name ---- `cancel_job` (`job_id`) Description ----------- Cancel the given job reffered by its id. Arguments --------- `job_id`: string Job id which should be canceled. Return value ------------ None. The user should be authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") if not job.can_cancel(self.user): raise xmlrpclib.Fault(403, "Permission denied.") if job.is_multinode: multinode_jobs = TestJob.objects.filter( target_group=job.target_group) for multinode_job in multinode_jobs: multinode_job.cancel(self.user) elif job.is_vmgroup: for vmgroup_job in job.sub_jobs_list: vmgroup_job.cancel(self.user) else: job.cancel(self.user) return True
def testjob(request, job): job = get_restricted_job(request.user, pk=job, request=request) data = ResultsView(request, model=TestSuite, table_class=TestJobResultsTable) suite_table = TestJobResultsTable(data.get_table_data().filter(job=job), request=request) failed_definitions = [] yaml_dict = OrderedDict() with contextlib.suppress(TestData.DoesNotExist): # some duplicates can exist, so get would fail here and [0] is quicker than try except. testdata = TestData.objects.filter(testjob=job)[0] # hide internal python objects, like OrderedDict for data in testdata.attributes.all().order_by("name"): yaml_dict[str(data.name)] = str(data.value) RequestConfig(request, paginate={ "per_page": suite_table.length }).configure(suite_table) template = loader.get_template("lava_results_app/job.html") return HttpResponse( template.render( { "bread_crumb_trail": BreadCrumbTrail.leading_to(testjob, job=job.id), "job": job, "job_link": pklink(job), "suite_table": suite_table, "metadata": yaml_dict, "condition_choices": simplejson.dumps(QueryCondition.get_condition_choices(job)), "available_content_types": simplejson.dumps( QueryCondition.get_similar_job_content_types()), }, request=request, ))
def job_output(self, job_id, offset=0): """ Name ---- `job_output` (`job_id`, `offset=0`) Description ----------- Get the output of given job id. Arguments --------- `job_id`: string Job id for which the output is required. `offset`: integer Offset from which to start reading the output file specified in bytes. It defaults to 0. Return value ------------ This function returns an XML-RPC binary data of output file, provided the user is authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault( 400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") output_file = job.output_file() if output_file: output_file.seek(offset) return xmlrpclib.Binary(output_file.read().encode('UTF-8')) else: raise xmlrpclib.Fault(404, "Job output not found.")
def testset(request, job, ts, pk, case): job = get_restricted_job(request.user, pk=job, request=request) test_suite = get_object_or_404(TestSuite, name=pk, job=job) test_set = get_object_or_404(TestSet, name=ts, suite=test_suite) test_cases = TestCase.objects.filter(name=case, test_set=test_set) template = loader.get_template("lava_results_app/case.html") return HttpResponse( template.render( { "bread_crumb_trail": BreadCrumbTrail.leading_to( testset, pk=pk, job=job.id, ts=ts, case=case ), "job": job, "suite": test_suite, "job_link": pklink(job), "test_cases": test_cases, }, request=request, ) )
def job_output(self, job_id, offset=0): """ Name ---- `job_output` (`job_id`, `offset=0`) Description ----------- Get the output of given job id. Arguments --------- `job_id`: string Job id for which the output is required. `offset`: integer Offset from which to start reading the output file specified in bytes. It defaults to 0. Return value ------------ This function returns an XML-RPC binary data of output file, provided the user is authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpclib.Fault(400, "Bad request: TestJob id was not " "specified.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault( 401, "Permission denied for user to job %s" % job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") output_file = job.output_file() if output_file: output_file.seek(offset) return xmlrpclib.Binary(output_file.read().encode('UTF-8')) else: raise xmlrpclib.Fault(404, "Job output not found.")
def testcase(request, job, pk, case): """ Each testcase can appear multiple times in the same testsuite and testjob, the action_data.action_level distinguishes each testcase. :param request: http request object :param job: ID of the TestJob :param pk: the name of the TestSuite :param case: the name of one or more TestCase objects in the TestSuite """ test_suite = get_object_or_404(TestSuite, name=pk, job=job) job = get_restricted_job(request.user, pk=job, request=request) test_cases = TestCase.objects.filter(name=case, suite=test_suite) template = loader.get_template("lava_results_app/case.html") return HttpResponse(template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to(testcase, pk=pk, job=job.id, case=case), 'job': job, 'suite': test_suite, 'job_link': pklink(job), 'test_cases': test_cases, }, request=request))
def cancel_job(self, job_id): """ Name ---- `cancel_job` (`job_id`) Description ----------- Cancel the given job reffered by its id. Arguments --------- `job_id`: string Job id which should be canceled. Return value ------------ None. The user should be authenticated with an username and token. """ self._authenticate() if not job_id: raise xmlrpc.client.Fault(400, "Bad request: TestJob id was not specified.") with transaction.atomic(): try: job = get_restricted_job(self.user, job_id, for_update=True) except PermissionDenied: raise xmlrpc.client.Fault( 401, "Permission denied for user to job %s" % job_id ) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") try: job.cancel(self.user) except PermissionDenied: raise xmlrpc.client.Fault(403, "Permission denied.") return True
def resubmit_job(self, job_id): """ Name ---- `resubmit_job` (`job_id`) Description ----------- Resubmit the given job reffered by its id. Arguments --------- `job_id`: string The job's id which should be re-submitted. Return value ------------ This function returns an XML-RPC integer which is the newly created job's id, provided the user is authenticated with an username and token. """ self._authenticate() if not self.user.has_perm("lava_scheduler_app.add_testjob"): raise xmlrpc.client.Fault( 403, "Permission denied. User %r does not have the " "'lava_scheduler_app.add_testjob' permission. Contact " "the administrators." % self.user.username, ) try: job = get_restricted_job(self.user, job_id) except TestJob.DoesNotExist: raise xmlrpc.client.Fault(404, "Specified job not found.") if job.is_multinode: return self.submit_job(job.multinode_definition) else: return self.submit_job(job.definition)
def resubmit_job(self, job_id): """ Name ---- `resubmit_job` (`job_id`) Description ----------- Resubmit the given job reffered by its id. Arguments --------- `job_id`: string The job's id which should be re-submitted. Return value ------------ This function returns an XML-RPC integer which is the newly created job's id, provided the user is authenticated with an username and token. """ self._authenticate() if not self.user.has_perm('lava_scheduler_app.add_testjob'): raise xmlrpclib.Fault( 403, "Permission denied. User %r does not have the " "'lava_scheduler_app.add_testjob' permission. Contact " "the administrators." % self.user.username) try: job = get_restricted_job(self.user, job_id) except TestJob.DoesNotExist: raise xmlrpclib.Fault(404, "Specified job not found.") if job.is_multinode: return self.submit_job(job.multinode_definition) elif job.is_vmgroup: return self.submit_job(job.vmgroup_definition) else: return self.submit_job(job.definition)
def cancel_job(self, job_id): """ Name ---- `cancel_job` (`job_id`) Description ----------- Cancel the given job reffered by its id. Arguments --------- `job_id`: string Job id which should be canceled. Return value ------------ None. The user should be authenticated with an username and token. """ if not self.user: raise xmlrpclib.Fault(401, "Authentication required.") try: job = get_restricted_job(self.user, job_id) except PermissionDenied: raise xmlrpclib.Fault(403, "Permission denied") if not job.can_cancel(self.user): raise xmlrpclib.Fault(403, "Permission denied.") if job.is_multinode: for multinode_job in job.sub_jobs_list: multinode_job.cancel(self.user) elif job.is_vmgroup: for vmgroup_job in job.sub_jobs_list: vmgroup_job.cancel(self.user) else: job.cancel(self.user) return True
def testjob(request, job): job = get_restricted_job(request.user, pk=job, request=request) data = ResultsView(request, model=TestSuite, table_class=TestJobResultsTable) suite_table = TestJobResultsTable(data.get_table_data().filter(job=job)) failed_definitions = [] yaml_dict = OrderedDict() if TestData.objects.filter(testjob=job).exists(): # some duplicates can exist, so get would fail here and [0] is quicker than try except. testdata = TestData.objects.filter(testjob=job).prefetch_related( "actionlevels__testcase", "actionlevels__testcase__suite" )[0] if job.state == TestJob.STATE_FINISHED: # returns something like ['singlenode-advanced', 'smoke-tests-basic', 'smoke-tests-basic'] executed = [ { case.action_metadata[ "test_definition_start" ]: case.action_metadata.get("success", "") } for case in TestCase.objects.filter( suite__in=TestSuite.objects.filter(job=job) ) if case.action_metadata and "test_definition_start" in case.action_metadata and case.suite.name == "lava" ] submitted = [ actiondata.testcase.action_metadata for actiondata in testdata.actionlevels.all() if actiondata.testcase and "test-runscript-overlay" in actiondata.action_name ] # compare with a dict similar to created in executed for item in submitted: if executed and {item["name"]: item["success"]} not in executed: comparison = {} if item["from"] != "inline": comparison["repository"] = item["repository"] comparison["path"] = item["path"] comparison["name"] = item["name"] comparison["uuid"] = item["success"] failed_definitions.append(comparison) # hide internal python objects, like OrderedDict for data in testdata.attributes.all().order_by("name"): yaml_dict[str(data.name)] = str(data.value) RequestConfig(request, paginate={"per_page": suite_table.length}).configure( suite_table ) template = loader.get_template("lava_results_app/job.html") return HttpResponse( template.render( { "bread_crumb_trail": BreadCrumbTrail.leading_to(testjob, job=job.id), "job": job, "job_link": pklink(job), "suite_table": suite_table, "metadata": yaml_dict, "failed_definitions": failed_definitions, "condition_choices": simplejson.dumps( QueryCondition.get_condition_choices(job) ), "available_content_types": simplejson.dumps( QueryCondition.get_similar_job_content_types() ), }, request=request, ) )
def testcase(request, case_id, job=None, pk=None): """ Each testcase can appear multiple times in the same testsuite and testjob, the action_data.action_level distinguishes each testcase. This view supports multiple ways to obtain test case/set. First is by test case ID, second by job ID, suite name and test case name and third by job ID, suite name and test set name. :param request: http request object :param job: ID of the TestJob :param pk: the name of the TestSuite :param case_id: the name or ID of one TestCase object in the TestSuite """ test_sets = None try: case = TestCase.objects.get(pk=case_id) except (TestCase.DoesNotExist, ValueError): case = TestCase.objects.filter(name=case_id, suite__name=pk, suite__job__id=job).first() if not case: test_sets = TestSet.objects.filter(name=case_id) if not test_sets: raise Http404( "No TestCase/TestSet matches the given parameters.") if not job: job = case.suite.job # Auth check purposes only. job = get_restricted_job(request.user, pk=job.id, request=request) else: job = get_restricted_job(request.user, pk=job, request=request) if not pk: test_suite = case.suite else: test_suite = get_object_or_404(TestSuite, name=pk, job=job) if test_sets: # No test case was found. test_sets = test_sets.filter(suite=test_suite) test_cases = TestCase.objects.none() else: test_cases = TestCase.objects.filter(name=case.name, suite=test_suite) extra_source = {} logger = logging.getLogger('lava-master') for extra_case in test_cases: try: f_metadata = yaml.load(extra_case.metadata, Loader=yaml.CLoader) if not f_metadata: continue except TypeError: logger.info("Unable to load extra case metadata for %s", extra_case) continue extra_data = f_metadata.get('extra') try: if extra_data and os.path.exists(extra_data): with open(f_metadata['extra'], 'r') as extra_file: items = yaml.load(extra_file, Loader=yaml.CLoader) # hide the !!python OrderedDict prefix from the output. for key, value in items.items(): extra_source.setdefault(extra_case.id, '') extra_source[extra_case.id] += "%s: %s\n" % (key, value) except TypeError: # In some old version of LAVA, extra_data is not a string but an OrderedDict # In this case, just skip it. pass template = loader.get_template("lava_results_app/case.html") trail_id = case.id if case else test_sets.first().name return HttpResponse(template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to(testcase, pk=test_suite.name, job=job.id, case_id=trail_id), 'job': job, 'sets': test_sets, 'suite': test_suite, 'job_link': pklink(job), 'extra_source': extra_source, 'test_cases': test_cases, 'bug_links': BugLink.objects.filter( object_id__in=test_cases.values_list('id', flat=True), content_type_id=ContentType.objects.get_for_model( TestCase).id, ) }, request=request))
def test_get_restricted_job(client, check_request_auth_patched, setup): user = User.objects.get(username="******") job = get_restricted_job(user, TestJob.objects.get(description="test job 01").pk) assert job.description == "test job 01" # nosec
def test_get_restricted_job_non_existing(client, check_request_auth_patched, setup): user = User.objects.get(username="******") with pytest.raises(Http404): get_restricted_job(user, -1)
def testjob(request, job): job = get_restricted_job(request.user, pk=job, request=request) data = ResultsView(request, model=TestSuite, table_class=ResultsTable) suite_table = ResultsTable(data.get_table_data().filter(job=job)) failed_definitions = [] yaml_dict = OrderedDict() if TestData.objects.filter(testjob=job).exists(): # some duplicates can exist, so get would fail here and [0] is quicker than try except. testdata = TestData.objects.filter(testjob=job).prefetch_related( 'actionlevels__testcase', 'actionlevels__testcase__suite')[0] if job.status in [TestJob.INCOMPLETE, TestJob.COMPLETE]: # returns something like ['singlenode-advanced', 'smoke-tests-basic', 'smoke-tests-basic'] executed = [{ case.action_metadata['test_definition_start']: case.action_metadata.get('success', '') } for case in TestCase.objects.filter( suite__in=TestSuite.objects.filter(job=job)) if case.action_metadata and 'test_definition_start' in case.action_metadata and case.suite.name == 'lava'] submitted = [ actiondata.testcase.action_metadata for actiondata in testdata.actionlevels.all() if actiondata.testcase and 'test-runscript-overlay' in actiondata.action_name ] # compare with a dict similar to created in executed for item in submitted: if executed and { item['name']: item['success'] } not in executed: comparison = {} if item['from'] != 'inline': comparison['repository'] = item['repository'] comparison['path'] = item['path'] comparison['name'] = item['name'] comparison['uuid'] = item['success'] failed_definitions.append(comparison) # hide internal python objects, like OrderedDict for data in testdata.attributes.all().order_by('name'): yaml_dict[str(data.name)] = str(data.value) RequestConfig(request, paginate={ "per_page": suite_table.length }).configure(suite_table) template = loader.get_template("lava_results_app/job.html") return HttpResponse( template.render( { 'bread_crumb_trail': BreadCrumbTrail.leading_to(testjob, job=job.id), 'job': job, 'job_link': pklink(job), 'suite_table': suite_table, 'metadata': yaml_dict, 'content_type_id': ContentType.objects.get_for_model(TestSuite).id, 'failed_definitions': failed_definitions, 'condition_choices': simplejson.dumps(QueryCondition.get_condition_choices(job)), 'available_content_types': simplejson.dumps( QueryCondition.get_similar_job_content_types()), }, request=request))
""" test_sets = None try: case = TestCase.objects.get(pk=case_id) except (TestCase.DoesNotExist, ValueError): case = TestCase.objects.filter( name=case_id, suite__name=pk, suite__job__id=job ).first() if not case: test_sets = TestSet.objects.filter(name=case_id) if not test_sets: raise Http404("No TestCase/TestSet matches the given parameters.") if not job: job = case.suite.job # Auth check purposes only. job = get_restricted_job(request.user, pk=job.id, request=request) else: job = get_restricted_job(request.user, pk=job, request=request) if not pk: test_suite = case.suite else: test_suite = get_object_or_404(TestSuite, name=pk, job=job) if test_sets: # No test case was found. test_sets = test_sets.filter(suite=test_suite) test_cases = TestCase.objects.none() else: test_cases = TestCase.objects.filter(name=case.name, suite=test_suite) extra_source = {} logger = logging.getLogger("lava-master") for extra_case in test_cases:
def user_can_view_jobs(self, job_list, username=None): """ Name ---- user_can_view_jobs (`job_list`) Administrators only: user_can_view_jobs (`job_list`, `username`) Description ----------- Check the access permissions on a list of jobs. Admins can specify a username as the second argument to run the query on behalf of that user. Arguments --------- job_list: list list of job ids to query username: string username of the user to query (admins only) Return value ------------ Returns a dictionary where the key is a string of the job_id from the job_list, if it exists in the queried instance. The value is a boolean for whether the user can access that job. { '1234': True, '1543': False } If the job number does not exist, that job will be omitted from the returned dictionary. This function requires authentication with a username and token. Example ------- server.system.user_can_view_jobs([1, 2, 3, 4, 99999, 4000]) {'1': True, '4000': True, '3': True, '2': True, '4': True} # if using the username and token of an admin, a different user can be queried: server.system.user_can_view_jobs([1, 2, 3, 4, 99999, 4000], 'firstname.lastname') {'1': True, '4000': False, '3': True, '2': True, '4': False} """ self._authenticate() if not isinstance(job_list, list): raise xmlrpc.client.Fault(errors.BAD_REQUEST, "job list argument must be a list") username = self._switch_user(username) retval = {} for job_id in job_list: try: get_restricted_job(username, job_id) except Http404: continue except PermissionDenied: retval[str(job_id)] = False continue retval[str(job_id)] = True return retval