def submit(self, job): """Submit the job to treeherder. :param job: Treeherder job instance to use for submission. """ job.add_submit_timestamp(int(time.time())) # We can only submit job info once, so it has to be done in completed if self._job_details: job.add_artifact('Job Info', 'json', {'job_details': self._job_details}) job_collection = TreeherderJobCollection() job_collection.add(job) logger.info('Sending results to Treeherder: {}'.format(job_collection.to_json())) url = urlparse(self.url) client = TreeherderClient(protocol=url.scheme, host=url.hostname, client_id=self.client_id, secret=self.secret) client.post_collection(self.repository, job_collection) logger.info('Results are available to view at: {}'.format( urljoin(self.url, JOB_FRAGMENT.format(repository=self.repository, revision=self.revision))))
def submit(self, job): """Submit the job to treeherder. :param job: Treeherder job instance to use for submission. """ job.add_submit_timestamp(int(time.time())) if self._job_details: job.add_artifact('Job Info', 'json', {'job_details': copy.deepcopy(self._job_details)}) self._job_details = [] job_collection = TreeherderJobCollection() job_collection.add(job) logger.info('Sending results to Treeherder: {}'.format( job_collection.to_json())) self.client.post_collection(self.repository, job_collection) logger.info('Results are available to view at: {}'.format( urljoin( self.client.server_url, JOB_FRAGMENT.format(repository=self.repository, revision=self.revision))))
def post_treeherder_jobs(client, revisions, s3=None): """ Processes each file and submits a treeherder job with the data from each file. :param client: The TreeherderClient to use. :param revisions: A dictionary of revisions and their associated data. :param s3: Optional Amazon S3 bucket to upload logs to. """ successful = [] for (revision, test_set) in revisions.iteritems(): nodes = test_set['nodes'] repo = test_set.get('repo', 'mozilla-inbound') tjc = TreeherderJobCollection() try: tjc.add(create_treeherder_job(repo, revision, client, nodes, s3)) except KeyError as e: print "Failed to generate data for %s: %s, probably still running" % (revision, e) continue try: # NB: In theory we could batch these, but each collection has to be from # the same repo and it's possible we have different repos in our # dataset. client.post_collection(repo, tjc) #print tjc.to_json() successful.append(revision) print "Submitted perf data for %s to %s" % (revision, client.server_url) except Exception as e: print "Failed to submit data for %s: %s" % (revision, e) return successful
def submit_pending(self, jobs): """Submit jobs pending notifications to Treeherder :param jobs: Lists of jobs to be reported. (TestJob) """ self.logger.debug(type(self).__name__ + '.submit_pending: jobs =\n%s' % jobs) if not self.url or not jobs: self.logger.debug(type(self).__name__ + '.submit_pending: no url/job') return tjc = TreeherderJobCollection() for j in jobs: project = j.build['repo'] revision = j.build['revision'] revision_hash = self.request_revision_hash(project, revision) if not revision_hash: self.logger.debug(type(self).__name__ + '.submit_pending: no revision hash') return j.submit_timestamp = timestamp_now() self.logger.info('creating Treeherder job %s for %s %s, ' 'revision_hash: %s' % (j.job_guid, j.name, project, revision_hash)) tj = tjc.get_job() tj.add_description(j.description) tj.add_reason(j.reason) tj.add_tier(self.tier) tj.add_revision_hash(revision_hash) tj.add_project(project) tj.add_who(j.who) tj.add_job_guid(j.job_guid) tj.add_job_name(j.job_name) tj.add_job_symbol(j.job_symbol) tj.add_group_name(j.group_name) tj.add_group_symbol(j.group_symbol) tj.add_product_name(j.build['product']) tj.add_state(JobState.PENDING) tj.add_submit_timestamp(j.submit_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_start_timestamp(j.submit_timestamp) tj.add_end_timestamp(j.submit_timestamp) tj.add_build_url(j.build_url) tj.add_build_info(j.build['os_name'], j.build['platform'], j.build['architecture']) tj.add_machine(j.machine['host']) tj.add_machine_info(j.machine['os_name'], j.machine['platform'], j.machine['architecture']) # TODO determine type of build tj.add_option_collection({'opt': True}) tjc.add(tj) self.post_request(project, tjc, j.job_guid)
def post_treeherder_jobs(client, fileNames): """ Processes each file and submits a treeherder job with the data from each file. :param client: The TreeherderClient to use. :param fileNames: The files to process. """ for name in fileNames: with gzip.open(name) as f: data = json.load(f) test_set = data['Slimtest-TalosTP5-Slow'] nodes = test_set['nodes'] repo = test_set.get('repo', 'mozilla-inbound') # Attempt to retrieve the revision from the metadata, otherwise parse # it from the file name which has the form <revision>.json.gz if 'revision' in test_set: revsion = test_set['revision'] else: revision = os.path.basename(name).split('.')[0] tjc = TreeherderJobCollection() try: tjc.add(create_treeherder_job(repo, revision, client, nodes)) except KeyError as e: print "Failed to generate data for %s: %s" % (revision, e) continue # NB: In theory we could batch these, but each collection has to be from # the same repo and it's possible we have different repos in our # dataset. client.post_collection(repo, tjc)
def serve_forever(self): logger = utils.getLogger() while not self.shutdown_requested: wait_seconds = 1 # avoid busy loop job = self.jobs.get_next_treeherder_job() if job: tjc = TreeherderJobCollection() for data in job['job_collection']: tj = TreeherderJob(data) tjc.add(tj) if self.post_request(job['machine'], job['project'], tjc, job['attempts'], job['last_attempt']): self.jobs.treeherder_job_completed(job['id']) wait_seconds = 0 else: attempts = int(job['attempts']) wait_seconds = min(self.retry_wait * attempts, 3600) logger.debug('AutophoneTreeherder waiting for %d seconds after ' 'failed attempt %d', wait_seconds, attempts) if wait_seconds > 0: for i in range(wait_seconds): if self.shutdown_requested: break time.sleep(1)
def submit(self, job, logs=None): logs = logs or [] # We can only submit job info once, so it has to be done in completed if self._job_details: job.add_artifact('Job Info', 'json', {'job_details': self._job_details}) job_collection = TreeherderJobCollection() job_collection.add(job) print('Sending results to Treeherder: {}'.format( job_collection.to_json())) url = urlparse(self.url) client = TreeherderClient(protocol=url.scheme, host=url.hostname, client_id=self.client_id, secret=self.secret) client.post_collection(self.repository, job_collection) print('Results are available to view at: {}'.format( urljoin( self.url, JOB_FRAGMENT.format(repository=self.repository, revision=self.revision))))
def test_objectstore_create(job_sample, jm): """ test posting data to the objectstore via webtest. extected result are: - return code 200 - return message successful - 1 job stored in the objectstore """ tjc = TreeherderJobCollection() tj = tjc.get_job(job_sample) tjc.add(tj) resp = test_utils.post_collection(jm.project, tjc) assert resp.status_int == 200 assert resp.json['message'] == 'well-formed JSON stored' stored_objs = jm.get_os_dhub().execute( proc="objectstore_test.selects.row_by_guid", placeholders=[job_sample["job"]["job_guid"]] ) assert len(stored_objs) == 1 assert stored_objs[0]['job_guid'] == job_sample["job"]["job_guid"] jm.disconnect()
def serve_forever(self): logger = utils.getLogger() while not self.shutdown_requested: wait_seconds = 1 # avoid busy loop job = self.jobs.get_next_treeherder_job() if job: tjc = TreeherderJobCollection() for data in job['job_collection']: tj = TreeherderJob(data) tjc.add(tj) if self.post_request(job['machine'], job['project'], tjc, job['attempts'], job['last_attempt']): self.jobs.treeherder_job_completed(job['id']) wait_seconds = 0 else: attempts = int(job['attempts']) wait_seconds = min(self.retry_wait * attempts, 3600) logger.debug( 'AutophoneTreeherder waiting for %d seconds after ' 'failed attempt %d', wait_seconds, attempts) if wait_seconds > 0: for i in range(wait_seconds): if self.shutdown_requested: break time.sleep(1)
def test_objectstore_create(job_sample, jm): """ test posting data to the objectstore via webtest. extected result are: - return code 200 - return message successful - 1 job stored in the objectstore """ tjc = TreeherderJobCollection() tj = tjc.get_job(job_sample) tjc.add(tj) resp = test_utils.post_collection(jm.project, tjc) assert resp.status_int == 200 assert resp.json['message'] == 'well-formed JSON stored' stored_objs = jm.get_os_dhub().execute( proc="objectstore_test.selects.row_by_guid", placeholders=[job_sample["job"]["job_guid"]] ) assert len(stored_objs) == 1 assert stored_objs[0]['job_guid'] == job_sample["job"]["job_guid"]
def submit_pending(self, jobs): """Submit jobs pending notifications to Treeherder :param jobs: Lists of jobs to be reported. (TestJob) """ self.logger.debug(type(self).__name__ + '.submit_pending: jobs =\n%s' % jobs) if not self.url or not jobs: self.logger.debug(type(self).__name__ + '.submit_pending: no url/job') return tjc = TreeherderJobCollection() for j in jobs: project = j.build['repo'] revision = j.build['revision'] revision_hash = self.request_revision_hash(project, revision) if not revision_hash: self.logger.debug(type(self).__name__ + '.submit_pending: no revision hash') return j.submit_timestamp = timestamp_now() self.logger.info('creating Treeherder job %s for %s %s, ' 'revision_hash: %s' % (j.job_guid, j.name, project, revision_hash)) tj = tjc.get_job() tj.add_description(j.description) tj.add_reason(j.reason) tj.add_tier(self.tier) tj.add_revision_hash(revision_hash) tj.add_project(project) tj.add_who(j.who) tj.add_job_guid(j.job_guid) tj.add_job_name(j.job_name) tj.add_job_symbol(j.job_symbol) tj.add_group_name(j.group_name) tj.add_group_symbol(j.group_symbol) tj.add_product_name(j.build['product']) tj.add_state(JobState.PENDING) tj.add_submit_timestamp(j.submit_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_start_timestamp(j.submit_timestamp) tj.add_end_timestamp(j.submit_timestamp) tj.add_build_info(j.build['os_name'], j.build['platform'], j.build['architecture']) tj.add_machine(j.machine['host']) tj.add_machine_info(j.machine['os_name'], j.machine['platform'], j.machine['architecture']) # TODO determine type of build tj.add_option_collection({'opt': True}) tjc.add(tj) self.post_request(project, tjc, j.job_guid)
def submit_running(self, machine, build_url, project, revision_hash, tests=None): """Submit tests running notifications to Treeherder :param machine: machine id :param build_url: url to build being tested. :param project: repository of build. :param revision_hash: Treeherder revision hash of build. :param tests: Lists of tests to be reported. """ if tests is None: tests = [] logger.debug('AutophoneTreeherder.submit_running: %s' % tests) if not self.url or not revision_hash: logger.debug('AutophoneTreeherder.submit_running: no url/revision hash') return tjc = TreeherderJobCollection() for t in tests: logger.debug('AutophoneTreeherder.submit_running: ' 'for %s %s' % (t.name, project)) t.submit_timestamp = timestamp_now() t.start_timestamp = timestamp_now() tj = tjc.get_job() tj.add_tier(self.options.treeherder_tier) tj.add_revision_hash(revision_hash) tj.add_project(project) tj.add_job_guid(t.job_guid) tj.add_job_name(t.job_name) tj.add_job_symbol(t.job_symbol) tj.add_group_name(t.group_name) tj.add_group_symbol(t.group_symbol) tj.add_product_name('fennec') tj.add_state(TestState.RUNNING) tj.add_submit_timestamp(t.submit_timestamp) tj.add_start_timestamp(t.start_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_end_timestamp(0) # tj.add_machine(machine) tj.add_build_info('android', t.phone.platform, t.phone.architecture) tj.add_machine_info('android',t.phone.platform, t.phone.architecture) tj.add_option_collection({'opt': True}) tj.add_artifact('buildapi', 'json', { 'buildername': t.get_buildername(project)}) tj.add_artifact('privatebuild', 'json', { 'build_url': build_url, 'config_file': t.config_file, 'chunk': t.chunk}) tjc.add(tj) logger.debug('AutophoneTreeherder.submit_running: tjc: %s' % tjc.to_json()) self.queue_request(machine, project, tjc)
def test_job_collection(self): """Confirm the collection matches the sample data""" tjc = TreeherderJobCollection() for job in self.job_data: tj = TreeherderJob(job) tjc.add(tj) self.assertTrue( len(self.job_data) == len(tjc.data) )
def create_job_collection(dataset): print("[DEBUG] Job Collection:") print(dataset) tjc = TreeherderJobCollection() for data in dataset: tj = tjc.get_job() tj.add_revision(data['revision']) tj.add_project(data['project']) tj.add_coalesced_guid(data['job']['coalesced']) tj.add_job_guid(data['job']['job_guid']) tj.add_job_name(data['job']['name']) tj.add_job_symbol(data['job']['job_symbol']) tj.add_group_name(data['job']['group_name']) tj.add_group_symbol(data['job']['group_symbol']) tj.add_description(data['job']['desc']) tj.add_product_name(data['job']['product_name']) tj.add_state(data['job']['state']) tj.add_result(data['job']['result']) tj.add_reason(data['job']['reason']) tj.add_who(data['job']['who']) tj.add_tier(data['job']['tier']) tj.add_submit_timestamp(data['job']['submit_timestamp']) tj.add_start_timestamp(data['job']['start_timestamp']) tj.add_end_timestamp(data['job']['end_timestamp']) tj.add_machine(data['job']['machine']) tj.add_build_info( data['job']['build_platform']['os_name'], data['job']['build_platform']['platform'], data['job']['build_platform']['architecture'] ) tj.add_machine_info( data['job']['machine_platform']['os_name'], data['job']['machine_platform']['platform'], data['job']['machine_platform']['architecture'] ) tj.add_option_collection(data['job']['option_collection']) # for log_reference in data['job']['log_references']: # tj.add_log_reference( 'buildbot_text', log_reference['url']) # data['artifact'] is a list of artifacts for artifact_data in data['job']['artifacts']: tj.add_artifact( artifact_data['name'], artifact_data['type'], artifact_data['blob'] ) tjc.add(tj) return tjc
def test_job_collection(self): """Confirm the collection matches the sample data""" tjc = TreeherderJobCollection() for job in self.job_data: tj = TreeherderJob(job) tjc.add(tj) self.assertTrue(len(self.job_data) == len(tjc.data))
def submit_pending(self, tests=[]): self.worker.loggerdeco.debug('AutophoneTreeherder.submit_pending: %s' % tests) if not self.url or not self.worker.build.revision_hash: self.worker.loggerdeco.debug('AutophoneTreeherder.submit_pending: no url/revision hash') return tjc = TreeherderJobCollection(job_type='update') if not tests: tests = self.worker.runnable_tests for t in tests: t.message = None t.submit_timestamp = timestamp_now() t.job_guid = generate_guid() t.job_details = [] self.worker.loggerdeco.info('creating Treeherder job %s for %s %s, ' 'revision: %s, revision_hash: %s' % ( t.job_guid, t.name, t.build.tree, t.build.revision, t.build.revision_hash)) self.worker.loggerdeco.debug('AutophoneTreeherder.submit_pending: ' 'test config_file=%s, config sections=%s' % ( t.config_file, t.cfg.sections())) tj = tjc.get_job() tj.add_revision_hash(self.worker.build.revision_hash) tj.add_project(self.worker.build.tree) tj.add_job_guid(t.job_guid) tj.add_job_name(t.job_name) tj.add_job_symbol(t.job_symbol) tj.add_group_name(t.group_name) tj.add_group_symbol(t.group_symbol) tj.add_product_name('fennec') tj.add_state(TestState.PENDING) tj.add_submit_timestamp(t.submit_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_start_timestamp(t.submit_timestamp) tj.add_end_timestamp(t.submit_timestamp) # tj.add_machine(t.phone.id) tj.add_build_url(self.worker.build.url) tj.add_build_info('android', t.phone.platform, t.phone.architecture) tj.add_machine_info('android',t.phone.platform, t.phone.architecture) tj.add_option_collection({'opt': True}) # Fake the buildername from buildbot... tj.add_artifact('buildapi', 'json', {'buildername': t.buildername}) tjc.add(tj) self.worker.loggerdeco.debug('AutophoneTreeherder.submit_pending: tjc: %s' % ( tjc.to_json())) self.post_request(tjc)
def running_jobs_stored(jm, running_jobs, result_set_stored): """ stores a list of buildapi running jobs into the objectstore """ running_jobs.update(result_set_stored[0]) tjc = TreeherderJobCollection(job_type='update') tj = tjc.get_job(running_jobs) tjc.add(tj) test_utils.post_collection(jm.project, tjc)
def completed_jobs_stored(jm, completed_jobs, result_set_stored, mock_send_request): """ stores a list of buildapi completed jobs into the objectstore """ completed_jobs['revision_hash'] = result_set_stored[0]['revision_hash'] tjc = TreeherderJobCollection() tj = tjc.get_job(completed_jobs) tjc.add(tj) test_utils.post_collection(jm.project, tjc)
def completed_jobs_stored( jm, completed_jobs, result_set_stored, mock_send_request): """ stores a list of buildapi completed jobs into the objectstore """ completed_jobs['revision_hash'] = result_set_stored[0]['revision_hash'] tjc = TreeherderJobCollection() tj = tjc.get_job(completed_jobs) tjc.add(tj) test_utils.post_collection(jm.project, tjc)
def running_jobs_stored( jm, running_jobs, result_set_stored): """ stores a list of buildapi running jobs into the objectstore """ running_jobs.update(result_set_stored[0]) tjc = TreeherderJobCollection(job_type='update') tj = tjc.get_job(running_jobs) tjc.add(tj) test_utils.post_collection(jm.project, tjc)
def create_job_collection(dataset): print("[DEBUG] Job Collection:") print(dataset) tjc = TreeherderJobCollection() for data in dataset: tj = tjc.get_job() tj.add_revision(data['revision']) tj.add_project(data['project']) tj.add_coalesced_guid(data['job']['coalesced']) tj.add_job_guid(data['job']['job_guid']) tj.add_job_name(data['job']['name']) tj.add_job_symbol(data['job']['job_symbol']) tj.add_group_name(data['job']['group_name']) tj.add_group_symbol(data['job']['group_symbol']) tj.add_description(data['job']['desc']) tj.add_product_name(data['job']['product_name']) tj.add_state(data['job']['state']) tj.add_result(data['job']['result']) tj.add_reason(data['job']['reason']) tj.add_who(data['job']['who']) tj.add_tier(data['job']['tier']) tj.add_submit_timestamp(data['job']['submit_timestamp']) tj.add_start_timestamp(data['job']['start_timestamp']) tj.add_end_timestamp(data['job']['end_timestamp']) tj.add_machine(data['job']['machine']) tj.add_build_info( data['job']['build_platform']['os_name'], data['job']['build_platform']['platform'], data['job']['build_platform']['architecture'] ) tj.add_machine_info( data['job']['machine_platform']['os_name'], data['job']['machine_platform']['platform'], data['job']['machine_platform']['architecture'] ) tj.add_option_collection(data['job']['option_collection']) for artifact_data in data['job']['artifacts']: tj.add_artifact( artifact_data['name'], artifact_data['type'], artifact_data['blob'] ) tjc.add(tj) return tjc
def pending_jobs_stored(jm, pending_jobs, result_set_stored): """ stores a list of buildapi pending jobs into the jobs store using BuildApiTreeHerderAdapter """ pending_jobs.update(result_set_stored[0]) tjc = TreeherderJobCollection(job_type='update') tj = tjc.get_job(pending_jobs) tjc.add(tj) test_utils.post_collection(jm.project, tjc)
def test_send_without_oauth( self, mock_HTTPConnection, mock_time, mock_generate_nonce): """Can send data to the server.""" mock_time.return_value = 1342229050 mock_generate_nonce.return_value = "46810593" host = 'host' req = TreeherderRequest( protocol='http', host=host, project='project', oauth_key=None, oauth_secret=None, ) mock_conn = mock_HTTPConnection.return_value mock_request = mock_conn.request mock_response = mock_conn.getresponse.return_value tjc = TreeherderJobCollection() for job in self.job_data: tjc.add( tjc.get_job(job) ) break response = req.post(tjc) self.assertEqual(mock_HTTPConnection.call_count, 1) self.assertEqual(mock_HTTPConnection.call_args[0][0], host) self.assertEqual(mock_response, response) self.assertEqual(mock_request.call_count, 1) uri = req.get_uri(tjc) method, path, data, header = mock_request.call_args[0] self.assertEqual(method, "POST") deserialized_data = json.loads(data) self.assertEqual( deserialized_data, tjc.get_collection_data() ) self.assertEqual( header['Content-Type'], 'application/json', )
def pending_jobs_stored( jm, pending_jobs, result_set_stored): """ stores a list of buildapi pending jobs into the jobs store using BuildApiTreeHerderAdapter """ pending_jobs.update(result_set_stored[0]) tjc = TreeherderJobCollection(job_type='update') tj = tjc.get_job(pending_jobs) tjc.add(tj) test_utils.post_collection(jm.project, tjc)
def create_job_collection(self, dataset): # reference the page about tjc ttps://github.com/mozilla/treeherder/blob/master/docs/submitting_data.rst tjc = TreeherderJobCollection() for data in dataset: tj = tjc.get_job() tj.add_revision(data['revision']) tj.add_project(data['project']) tj.add_coalesced_guid(data['job']['coalesced']) tj.add_job_guid(data['job']['job_guid']) tj.add_job_name(data['job']['name']) tj.add_job_symbol(data['job']['job_symbol']) tj.add_group_name(data['job']['group_name']) tj.add_group_symbol(data['job']['group_symbol']) tj.add_description(data['job']['desc']) tj.add_product_name(data['job']['product_name']) tj.add_state(data['job']['state']) tj.add_result(data['job']['result']) tj.add_reason(data['job']['reason']) tj.add_who(data['job']['who']) tj.add_tier(data['job']['tier']) tj.add_submit_timestamp(data['job']['submit_timestamp']) tj.add_start_timestamp(data['job']['start_timestamp']) tj.add_end_timestamp(data['job']['end_timestamp']) tj.add_machine(data['job']['machine']) tj.add_build_info( data['job']['build_platform']['os_name'], data['job']['build_platform']['platform'], data['job']['build_platform']['architecture'] ) tj.add_machine_info( data['job']['machine_platform']['os_name'], data['job']['machine_platform']['platform'], data['job']['machine_platform']['architecture'] ) tj.add_option_collection(data['job']['option_collection']) # data['artifact'] is a list of artifacts for artifact_data in data['job']['artifacts']: tj.add_artifact( artifact_data['name'], artifact_data['type'], artifact_data['blob'] ) tjc.add(tj) return tjc
def submit_pending(self, machine, build_url, project, revision, build_type, build_abi, build_platform, build_sdk, builder_type, tests=[]): """Submit tests pending notifications to Treeherder :param machine: machine id :param build_url: url to build being tested. :param project: repository of build. :param revision: Either a URL to the changeset or the revision id. :param tests: Lists of tests to be reported. """ logger = utils.getLogger() logger.debug('AutophoneTreeherder.submit_pending: %s', tests) if not self.url or not revision: logger.debug('AutophoneTreeherder.submit_pending: no url/revision') return tjc = TreeherderJobCollection() for t in tests: logger.debug('AutophoneTreeherder.submit_pending: for %s %s', t.name, project) t.message = None t.submit_timestamp = timestamp_now() t.job_details = [] tj = self._create_job(tjc, machine, build_url, project, revision, build_type, build_abi, build_platform, build_sdk, builder_type, t) tj.add_state(TestState.PENDING) tj.add_submit_timestamp(t.submit_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_start_timestamp(0) tj.add_end_timestamp(0) tjc.add(tj) logger.debug('AutophoneTreeherder.submit_pending: tjc: %s', tjc.to_json()) self.queue_request(machine, project, tjc)
def create_job_collection(dataset): print("[DEBUG] Job Collection:") print(dataset) tjc = TreeherderJobCollection() for data in dataset: tj = tjc.get_job() tj.add_revision(data["revision"]) tj.add_project(data["project"]) tj.add_coalesced_guid(data["job"]["coalesced"]) tj.add_job_guid(data["job"]["job_guid"]) tj.add_job_name(data["job"]["name"]) tj.add_job_symbol(data["job"]["job_symbol"]) tj.add_group_name(data["job"]["group_name"]) tj.add_group_symbol(data["job"]["group_symbol"]) tj.add_description(data["job"]["desc"]) tj.add_product_name(data["job"]["product_name"]) tj.add_state(data["job"]["state"]) tj.add_result(data["job"]["result"]) tj.add_reason(data["job"]["reason"]) tj.add_who(data["job"]["who"]) tj.add_tier(data["job"]["tier"]) tj.add_submit_timestamp(data["job"]["submit_timestamp"]) tj.add_start_timestamp(data["job"]["start_timestamp"]) tj.add_end_timestamp(data["job"]["end_timestamp"]) tj.add_machine(data["job"]["machine"]) tj.add_build_info( data["job"]["build_platform"]["os_name"], data["job"]["build_platform"]["platform"], data["job"]["build_platform"]["architecture"], ) tj.add_machine_info( data["job"]["machine_platform"]["os_name"], data["job"]["machine_platform"]["platform"], data["job"]["machine_platform"]["architecture"], ) tj.add_option_collection(data["job"]["option_collection"]) for artifact_data in data["job"]["artifacts"]: tj.add_artifact(artifact_data["name"], artifact_data["type"], artifact_data["blob"]) tjc.add(tj) return tjc
def serve_forever(self): while not self.shutdown_requested: wait = True job = self.jobs.get_next_treeherder_job() if job: tjc = TreeherderJobCollection() for data in job['job_collection']: tj = TreeherderJob(data) tjc.add(tj) if self.post_request(job['machine'], job['project'], tjc, job['attempts'], job['last_attempt']): self.jobs.treeherder_job_completed(job['id']) wait = False if wait: for i in range(self.retry_wait): if self.shutdown_requested: break time.sleep(1)
def test_send_without_oauth(self, mock_HTTPConnection, mock_time, mock_generate_nonce): """Can send data to the server.""" mock_time.return_value = 1342229050 mock_generate_nonce.return_value = "46810593" host = 'host' req = TreeherderRequest( protocol='http', host=host, project='project', oauth_key=None, oauth_secret=None, ) mock_conn = mock_HTTPConnection.return_value mock_request = mock_conn.request mock_response = mock_conn.getresponse.return_value tjc = TreeherderJobCollection() for job in self.job_data: tjc.add(tjc.get_job(job)) break response = req.post(tjc) self.assertEqual(mock_HTTPConnection.call_count, 1) self.assertEqual(mock_HTTPConnection.call_args[0][0], host) self.assertEqual(mock_response, response) self.assertEqual(mock_request.call_count, 1) uri = req.get_uri(tjc) method, path, data, header = mock_request.call_args[0] self.assertEqual(method, "POST") deserialized_data = json.loads(data) self.assertEqual(deserialized_data, tjc.get_collection_data()) self.assertEqual( header['Content-Type'], 'application/json', )
def submit_running(self, tests=[]): self.worker.loggerdeco.debug('AutophoneTreeherder.submit_running: %s' % tests) if not self.url or not self.worker.build.revision_hash: self.worker.loggerdeco.debug('AutophoneTreeherder.submit_running: no url/revision hash') return tjc = TreeherderJobCollection(job_type='update') if not tests: tests = self.worker.runnable_tests for t in tests: self.worker.loggerdeco.debug('AutophoneTreeherder.submit_running: ' 'for %s %s' % (t.name, t.build.tree)) t.start_timestamp = timestamp_now() tj = tjc.get_job() tj.add_revision_hash(self.worker.build.revision_hash) tj.add_project(self.worker.build.tree) tj.add_job_guid(t.job_guid) tj.add_job_name(t.job_name) tj.add_job_symbol(t.job_symbol) tj.add_group_name(t.group_name) tj.add_group_symbol(t.group_symbol) tj.add_product_name('fennec') tj.add_state(TestState.RUNNING) tj.add_submit_timestamp(t.submit_timestamp) tj.add_start_timestamp(t.start_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_end_timestamp(t.start_timestamp) # tj.add_machine(t.phone.id) tj.add_build_url(self.worker.build.url) tj.add_build_info('android', t.phone.platform, t.phone.architecture) tj.add_machine_info('android',t.phone.platform, t.phone.architecture) tj.add_option_collection({'opt': True}) tj.add_artifact('buildapi', 'json', {'buildername': t.buildername}) tjc.add(tj) self.worker.loggerdeco.debug('AutophoneTreeherder.submit_running: tjc: %s' % tjc.to_json()) self.post_request(tjc)
def create_job_collection(self, dataset): # reference the page about tjc ttps://github.com/mozilla/treeherder/blob/master/docs/submitting_data.rst tjc = TreeherderJobCollection() for data in dataset: tj = tjc.get_job() tj.add_revision(data['revision']) tj.add_project(data['project']) tj.add_coalesced_guid(data['job']['coalesced']) tj.add_job_guid(data['job']['job_guid']) tj.add_job_name(data['job']['name']) tj.add_job_symbol(data['job']['job_symbol']) tj.add_group_name(data['job']['group_name']) tj.add_group_symbol(data['job']['group_symbol']) tj.add_description(data['job']['desc']) tj.add_product_name(data['job']['product_name']) tj.add_state(data['job']['state']) tj.add_result(data['job']['result']) tj.add_reason(data['job']['reason']) tj.add_who(data['job']['who']) tj.add_tier(data['job']['tier']) tj.add_submit_timestamp(data['job']['submit_timestamp']) tj.add_start_timestamp(data['job']['start_timestamp']) tj.add_end_timestamp(data['job']['end_timestamp']) tj.add_machine(data['job']['machine']) tj.add_build_info(data['job']['build_platform']['os_name'], data['job']['build_platform']['platform'], data['job']['build_platform']['architecture']) tj.add_machine_info( data['job']['machine_platform']['os_name'], data['job']['machine_platform']['platform'], data['job']['machine_platform']['architecture']) tj.add_option_collection(data['job']['option_collection']) # data['artifact'] is a list of artifacts for artifact_data in data['job']['artifacts']: tj.add_artifact(artifact_data['name'], artifact_data['type'], artifact_data['blob']) tjc.add(tj) return tjc
def test_objectstore_with_bad_key(job_sample, jm): """ test calling with the wrong project key. extected result are: - return code 403 - return message failed """ tjc = TreeherderJobCollection() tj = tjc.get_job(job_sample) tjc.add(tj) resp = test_utils.post_collection( jm.project, tjc, status=403, consumer_key='wrong-key' ) assert resp.status_int == 403 assert resp.json['response'] == "access_denied" assert resp.json['detail'] == "oauth_consumer_key does not match project, {0}, credentials".format(jm.project)
def test_objectstore_with_bad_key(job_sample, jm): """ test calling with the wrong project key. extected result are: - return code 403 - return message failed """ tjc = TreeherderJobCollection() tj = tjc.get_job(job_sample) tjc.add(tj) resp = test_utils.post_collection( jm.project, tjc, status=403, consumer_key='wrong key' ) assert resp.status_int == 403 assert resp.json['response'] == "access_denied" assert resp.json['message'] == "oauth_consumer_key does not match project, {0}, credentials".format(jm.project)
def test_objectstore_with_bad_secret(job_sample, jm): """ test calling with the wrong project secret. extected result are: - return code 403 - return message authentication failed """ tjc = TreeherderJobCollection() tj = tjc.get_job(job_sample) tjc.add(tj) resp = test_utils.post_collection( jm.project, tjc, status=403, consumer_secret='not so secret' ) assert resp.status_int == 403 assert resp.json['message'] == "Client authentication failed for project, {0}".format(jm.project) assert resp.json['response'] == "invalid_client"
def test_objectstore_with_bad_secret(job_sample, jm): """ test calling with the wrong project secret. extected result are: - return code 403 - return message authentication failed """ tjc = TreeherderJobCollection() tj = tjc.get_job(job_sample) tjc.add(tj) resp = test_utils.post_collection( jm.project, tjc, status=403, consumer_secret='not-so-secret' ) assert resp.status_int == 403 assert resp.json['detail'] == "Client authentication failed for project, {0}".format(jm.project) assert resp.json['response'] == "invalid_client"
def submit_results(self, job): job.add_project(self.project) job.add_revision_hash(self.retrieve_revision_hash()) job.add_submit_timestamp(int(time.time())) job_collection = TreeherderJobCollection() job_collection.add(job) # self.logger.info print('Sending results to Treeherder: %s' % job_collection.to_json()) url = urlparse(self.url) client = TreeherderClient(protocol=url.scheme, host=url.hostname, client_id=self.client_id, secret=self.secret) client.post_collection(self.project, job_collection) # self.logger.info print('Results are available to view at: %s' % ( urljoin(self.url, REVISON_FRAGMENT % (self.project, self.revision))))
def submit(self, job, logs=None): logs = logs or [] # We can only submit job info once, so it has to be done in completed if self._job_details: job.add_artifact('Job Info', 'json', {'job_details': self._job_details}) job_collection = TreeherderJobCollection() job_collection.add(job) print('Sending results to Treeherder: {}'.format(job_collection.to_json())) url = urlparse(self.url) client = TreeherderClient(protocol=url.scheme, host=url.hostname, client_id=self.client_id, secret=self.secret) client.post_collection(self.repository, job_collection) print('Results are available to view at: {}'.format( urljoin(self.url, JOB_FRAGMENT.format(repository=self.repository, revision=self.revision))))
def test_send_job_collection(self, mock_send): """Can add a treeherder collections to a TreeherderRequest.""" tjc = TreeherderJobCollection() for job in self.job_data: tjc.add(tjc.get_job(job)) req = TreeherderRequest( protocol='http', host='host', project='project', oauth_key='key', oauth_secret='secret', ) req.post(tjc) self.assertEqual(mock_send.call_count, 1) self.assertEqual(tjc.to_json(), mock_send.call_args_list[0][1]['data'])
def submit_running(self, machine, build_url, project, revision, build_type, build_abi, build_platform, build_sdk, builder_type, tests=[]): """Submit tests running notifications to Treeherder :param machine: machine id :param build_url: url to build being tested. :param project: repository of build. :param revision: Either a URL to the changeset or the revision id. :param tests: Lists of tests to be reported. """ logger = utils.getLogger() logger.debug('AutophoneTreeherder.submit_running: %s', tests) if not self.url or not revision: logger.debug('AutophoneTreeherder.submit_running: no url/revision') return tjc = TreeherderJobCollection() for t in tests: logger.debug('AutophoneTreeherder.submit_running: for %s %s', t.name, project) t.submit_timestamp = timestamp_now() t.start_timestamp = timestamp_now() tj = self._create_job(tjc, machine, build_url, project, revision, build_type, build_abi, build_platform, build_sdk, builder_type, t) tj.add_state(TestState.RUNNING) tj.add_submit_timestamp(t.submit_timestamp) tj.add_start_timestamp(t.start_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_end_timestamp(0) tjc.add(tj) logger.debug('AutophoneTreeherder.submit_running: tjc: %s', tjc.to_json()) self.queue_request(machine, project, tjc)
def submit(self, job): """Submit the job to treeherder. :param job: Treeherder job instance to use for submission. """ job.add_submit_timestamp(int(time.time())) if self._job_details: job.add_artifact('Job Info', 'json', {'job_details': copy.deepcopy(self._job_details)}) self._job_details = [] job_collection = TreeherderJobCollection() job_collection.add(job) logger.info('Sending results to Treeherder: {}'.format(job_collection.to_json())) self.client.post_collection(self.repository, job_collection) logger.info('Results are available to view at: {}'.format( urljoin('{0}://{1}'.format(self.client.protocol, self.client.host), JOB_FRAGMENT.format(repository=self.repository, revision=self.revision))))
def test_send_job_collection(self, mock_send): """Can add a treeherder collections to a TreeherderRequest.""" tjc = TreeherderJobCollection() for job in self.job_data: tjc.add( tjc.get_job(job) ) req = TreeherderRequest( protocol='http', host='host', project='project', oauth_key='key', oauth_secret='secret', ) req.post(tjc) self.assertEqual(mock_send.call_count, 1) self.assertEqual( tjc.to_json(), mock_send.call_args_list[0][1]['data'] )
def post_to_treeherder(self, tests): version = mozversion.get_version( binary=self.bin, sources=self.sources, dm_type='adb', device_serial=self.device_serial) job_collection = TreeherderJobCollection() job = job_collection.get_job() device = version.get('device_id') device_firmware_version_release = \ version.get('device_firmware_version_release') if not device: self.logger.error('Submitting to Treeherder is currently limited ' 'to devices.') return try: group = DEVICE_GROUP_MAP[device][device_firmware_version_release] job.add_group_name(group['name']) job.add_group_symbol(group['symbol']) job.add_job_name('Gaia Python Integration Test (%s)' % group['symbol']) job.add_job_symbol('Gip') except KeyError: self.logger.error('Unknown device id: %s or device firmware ' 'version: %s. Unable to determine Treeherder ' 'group. Supported devices: %s' % (device, device_firmware_version_release, ['%s: %s' % (k, [fw for fw in v.keys()]) for k, v in DEVICE_GROUP_MAP.iteritems()])) return # Determine revision hash from application revision revision = version['application_changeset'] project = version['application_repository'].split('/')[-1] lookup_url = urljoin( self.treeherder_url, 'api/project/%s/revision-lookup/?revision=%s' % ( project, revision)) self.logger.debug('Getting revision hash from: %s' % lookup_url) response = requests.get(lookup_url) response.raise_for_status() assert response.json(), 'Unable to determine revision hash for %s. ' \ 'Perhaps it has not been ingested by ' \ 'Treeherder?' % revision revision_hash = response.json()[revision]['revision_hash'] job.add_revision_hash(revision_hash) job.add_project(project) job.add_job_guid(str(uuid.uuid4())) job.add_product_name('b2g') job.add_state('completed') # Determine test result if self.failed or self.unexpected_successes: job.add_result('testfailed') else: job.add_result('success') job.add_submit_timestamp(int(self.start_time)) job.add_start_timestamp(int(self.start_time)) job.add_end_timestamp(int(self.end_time)) job.add_machine(socket.gethostname()) job.add_build_info('b2g', 'b2g-device-image', 'x86') job.add_machine_info('b2g', 'b2g-device-image', 'x86') # All B2G device builds are currently opt builds job.add_option_collection({'opt': True}) date_format = '%d %b %Y %H:%M:%S' job_details = [{ 'content_type': 'link', 'title': 'Gaia revision:', 'url': 'https://github.com/mozilla-b2g/gaia/commit/%s' % version.get('gaia_changeset'), 'value': version.get('gaia_changeset'), }, { 'content_type': 'text', 'title': 'Gaia date:', 'value': version.get('gaia_date') and time.strftime( date_format, time.localtime(int(version.get('gaia_date')))), }, { 'content_type': 'text', 'title': 'Device identifier:', 'value': version.get('device_id') }, { 'content_type': 'text', 'title': 'Device firmware (date):', 'value': version.get('device_firmware_date') and time.strftime( date_format, time.localtime(int( version.get('device_firmware_date')))), }, { 'content_type': 'text', 'title': 'Device firmware (incremental):', 'value': version.get('device_firmware_version_incremental') }, { 'content_type': 'text', 'title': 'Device firmware (release):', 'value': version.get('device_firmware_version_release') }] ci_url = os.environ.get('BUILD_URL') if ci_url: job_details.append({ 'url': ci_url, 'value': ci_url, 'content_type': 'link', 'title': 'CI build:'}) # Attach logcat adb_device = ADBDevice(self.device_serial) with tempfile.NamedTemporaryFile(suffix='logcat.txt') as f: f.writelines(adb_device.get_logcat()) self.logger.debug('Logcat stored in: %s' % f.name) try: url = self.upload_to_s3(f.name) job_details.append({ 'url': url, 'value': 'logcat.txt', 'content_type': 'link', 'title': 'Log:'}) except S3UploadError: job_details.append({ 'value': 'Failed to upload logcat.txt', 'content_type': 'text', 'title': 'Error:'}) # Attach log files handlers = [handler for handler in self.logger.handlers if isinstance(handler, StreamHandler) and os.path.exists(handler.stream.name)] for handler in handlers: path = handler.stream.name filename = os.path.split(path)[-1] try: url = self.upload_to_s3(path) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Log:'}) # Add log reference if type(handler.formatter) is TbplFormatter or \ type(handler.formatter) is LogLevelFilter and \ type(handler.formatter.inner) is TbplFormatter: job.add_log_reference(filename, url) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:'}) # Attach reports for report in [self.html_output, self.xml_output]: if report is not None: filename = os.path.split(report)[-1] try: url = self.upload_to_s3(report) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Report:'}) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:'}) if job_details: job.add_artifact('Job Info', 'json', {'job_details': job_details}) job_collection.add(job) # Send the collection to Treeherder url = urlparse(self.treeherder_url) request = TreeherderRequest( protocol=url.scheme, host=url.hostname, project=project, oauth_key=os.environ.get('TREEHERDER_KEY'), oauth_secret=os.environ.get('TREEHERDER_SECRET')) self.logger.debug('Sending results to Treeherder: %s' % job_collection.to_json()) response = request.post(job_collection) self.logger.debug('Response: %s' % response.read()) assert response.status == 200, 'Failed to send results!' self.logger.info('Results are available to view at: %s' % ( urljoin(self.treeherder_url, '/ui/#/jobs?repo=%s&revision=%s' % ( project, revision))))
def post_to_treeherder(self, tests): version = mozversion.get_version(binary=self.bin, sources=self.sources, dm_type='adb', device_serial=self.device_serial) job_collection = TreeherderJobCollection() job = job_collection.get_job() device = version.get('device_id') device_firmware_version_release = \ version.get('device_firmware_version_release') if not device: self.logger.error('Submitting to Treeherder is currently limited ' 'to devices.') return try: group = DEVICE_GROUP_MAP[device][device_firmware_version_release] job.add_group_name(group['name']) job.add_group_symbol(group['symbol']) job.add_job_name('Gaia Python Integration Test (%s)' % group['symbol']) job.add_job_symbol('Gip') except KeyError: self.logger.error('Unknown device id: %s or device firmware ' 'version: %s. Unable to determine Treeherder ' 'group. Supported devices: %s' % (device, device_firmware_version_release, [ '%s: %s' % (k, [fw for fw in v.keys()]) for k, v in DEVICE_GROUP_MAP.iteritems() ])) return # Determine revision hash from application revision revision = version['application_changeset'] project = version['application_repository'].split('/')[-1] lookup_url = urljoin( self.treeherder_url, 'api/project/%s/revision-lookup/?revision=%s' % (project, revision)) self.logger.debug('Getting revision hash from: %s' % lookup_url) response = requests.get(lookup_url) response.raise_for_status() assert response.json(), 'Unable to determine revision hash for %s. ' \ 'Perhaps it has not been ingested by ' \ 'Treeherder?' % revision revision_hash = response.json()[revision]['revision_hash'] job.add_revision_hash(revision_hash) job.add_project(project) job.add_job_guid(str(uuid.uuid4())) job.add_product_name('b2g') job.add_state('completed') # Determine test result if self.failed or self.unexpected_successes: job.add_result('testfailed') else: job.add_result('success') job.add_submit_timestamp(int(self.start_time)) job.add_start_timestamp(int(self.start_time)) job.add_end_timestamp(int(self.end_time)) job.add_machine(socket.gethostname()) job.add_build_info('b2g', 'b2g-device-image', 'x86') job.add_machine_info('b2g', 'b2g-device-image', 'x86') # All B2G device builds are currently opt builds job.add_option_collection({'opt': True}) date_format = '%d %b %Y %H:%M:%S' job_details = [{ 'content_type': 'link', 'title': 'Gaia revision:', 'url': 'https://github.com/mozilla-b2g/gaia/commit/%s' % version.get('gaia_changeset'), 'value': version.get('gaia_changeset'), }, { 'content_type': 'text', 'title': 'Gaia date:', 'value': version.get('gaia_date') and time.strftime( date_format, time.localtime(int(version.get('gaia_date')))), }, { 'content_type': 'text', 'title': 'Device identifier:', 'value': version.get('device_id') }, { 'content_type': 'text', 'title': 'Device firmware (date):', 'value': version.get('device_firmware_date') and time.strftime( date_format, time.localtime(int(version.get('device_firmware_date')))), }, { 'content_type': 'text', 'title': 'Device firmware (incremental):', 'value': version.get('device_firmware_version_incremental') }, { 'content_type': 'text', 'title': 'Device firmware (release):', 'value': version.get('device_firmware_version_release') }] ci_url = os.environ.get('BUILD_URL') if ci_url: job_details.append({ 'url': ci_url, 'value': ci_url, 'content_type': 'link', 'title': 'CI build:' }) # Attach logcat adb_device = ADBDevice(self.device_serial) with tempfile.NamedTemporaryFile(suffix='logcat.txt') as f: f.writelines(adb_device.get_logcat()) self.logger.debug('Logcat stored in: %s' % f.name) try: url = self.upload_to_s3(f.name) job_details.append({ 'url': url, 'value': 'logcat.txt', 'content_type': 'link', 'title': 'Log:' }) except S3UploadError: job_details.append({ 'value': 'Failed to upload logcat.txt', 'content_type': 'text', 'title': 'Error:' }) # Attach log files handlers = [ handler for handler in self.logger.handlers if isinstance(handler, StreamHandler) and os.path.exists(handler.stream.name) ] for handler in handlers: path = handler.stream.name filename = os.path.split(path)[-1] try: url = self.upload_to_s3(path) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Log:' }) # Add log reference if type(handler.formatter) is TbplFormatter or \ type(handler.formatter) is LogLevelFilter and \ type(handler.formatter.inner) is TbplFormatter: job.add_log_reference(filename, url) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) # Attach reports for report in [self.html_output, self.xml_output]: if report is not None: filename = os.path.split(report)[-1] try: url = self.upload_to_s3(report) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Report:' }) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) if job_details: job.add_artifact('Job Info', 'json', {'job_details': job_details}) job_collection.add(job) # Send the collection to Treeherder url = urlparse(self.treeherder_url) request = TreeherderRequest( protocol=url.scheme, host=url.hostname, project=project, oauth_key=os.environ.get('TREEHERDER_KEY'), oauth_secret=os.environ.get('TREEHERDER_SECRET')) self.logger.debug('Sending results to Treeherder: %s' % job_collection.to_json()) response = request.post(job_collection) self.logger.debug('Response: %s' % response.read()) assert response.status == 200, 'Failed to send results!' self.logger.info( 'Results are available to view at: %s' % (urljoin(self.treeherder_url, '/ui/#/jobs?repo=%s&revision=%s' % (project, revision))))
def post_to_treeherder(self, tests): self.logger.info('\nTREEHERDER\n----------') version = mozversion.get_version( binary=self.bin, sources=self.sources, dm_type='adb', device_serial=self.device_serial) job_collection = TreeherderJobCollection() job = job_collection.get_job() device = version.get('device_id') if not device: self.logger.error('Submitting to Treeherder is currently limited ' 'to devices.') return try: group = DEVICE_GROUP_MAP[device] job.add_group_name(group['name']) job.add_group_symbol(group['symbol']) job.add_job_name('Gaia Python Integration Test (%s)' % device) job.add_job_symbol('Gip') except KeyError: self.logger.error('Unknown device id: %s, unable to determine ' 'Treeherder group. Supported device ids: %s' % ( device, DEVICE_GROUP_MAP.keys())) return # Determine revision hash from application revision revision = version['application_changeset'] project = version['application_repository'].split('/')[-1] lookup_url = urljoin( self.treeherder_url, 'api/project/%s/revision-lookup/?revision=%s' % ( project, revision)) self.logger.debug('Getting revision hash from: %s' % lookup_url) response = requests.get(lookup_url) response.raise_for_status() assert response.json(), 'Unable to determine revision hash for %s. ' \ 'Perhaps it has not been ingested by ' \ 'Treeherder?' % revision revision_hash = response.json()[revision]['revision_hash'] job.add_revision_hash(revision_hash) job.add_project(project) job.add_job_guid(str(uuid.uuid4())) job.add_product_name('b2g') job.add_state('completed') # Determine test result if self.failed or self.unexpected_successes: job.add_result('testfailed') else: job.add_result('success') job.add_submit_timestamp(int(self.start_time)) job.add_start_timestamp(int(self.start_time)) job.add_end_timestamp(int(self.end_time)) job.add_machine(socket.gethostname()) job.add_build_info('b2g', 'b2g-device-image', 'x86') job.add_machine_info('b2g', 'b2g-device-image', 'x86') # All B2G device builds are currently opt builds job.add_option_collection({'opt': True}) # TODO: Add log reference # job.add_log_reference() date_format = '%d %b %Y %H:%M:%S' job_details = [{ 'content_type': 'link', 'title': 'Gaia revision:', 'url': 'https://github.com/mozilla-b2g/gaia/commit/%s' % version.get('gaia_changeset'), 'value': version.get('gaia_changeset'), }, { 'content_type': 'text', 'title': 'Gaia date:', 'value': version.get('gaia_date') and time.strftime( date_format, time.localtime(int(version.get('gaia_date')))), }, { 'content_type': 'text', 'title': 'Device identifier:', 'value': version.get('device_id') }, { 'content_type': 'text', 'title': 'Device firmware (date):', 'value': version.get('device_firmware_date') and time.strftime( date_format, time.localtime(int( version.get('device_firmware_date')))), }, { 'content_type': 'text', 'title': 'Device firmware (incremental):', 'value': version.get('device_firmware_version_incremental') }, { 'content_type': 'text', 'title': 'Device firmware (release):', 'value': version.get('device_firmware_version_release') }] if self.ci_url: job_details.append({ 'url': self.ci_url, 'value': self.ci_url, 'content_type': 'link', 'title': 'CI build:'}) if job_details: job.add_artifact('Job Info', 'json', {'job_details': job_details}) # TODO: Add XML/HTML reports as artifacts # job.add_artifact() job_collection.add(job) # Send the collection to Treeherder url = urlparse(self.treeherder_url) request = TreeherderRequest( protocol=url.scheme, host=url.hostname, project=project, oauth_key=self.treeherder_key, oauth_secret=self.treeherder_secret) self.logger.debug('Sending results to Treeherder: %s' % job_collection.to_json()) response = request.post(job_collection) self.logger.debug('Response: %s' % response.read()) assert response.status == 200, 'Failed to send results!' self.logger.info('Results are available to view at: %s' % ( urljoin(self.treeherder_url, '/ui/#/jobs?repo=%s&revision=%s' % ( project, revision))))
def submit_running(self, jobs): """Submit jobs running notifications to Treeherder :param jobs: Lists of jobs to be reported. (TestJob) """ self.logger.debug( type(self).__name__ + '.submit_running: jobs =\n%s' % jobs) if not self.url or not jobs: self.logger.debug( type(self).__name__ + '.submit_running: no url/job') return tjc = TreeherderJobCollection() for j in jobs: project = j.build['repo'] revision = j.build['revision'] revision_hash = self.request_revision_hash(project, revision) if not revision_hash: self.logger.debug( type(self).__name__ + '.submit_running: no revision hash') return self.logger.debug( type(self).__name__ + '.submit_running: ' 'for %s %s' % (j.name, project)) if not j.start_timestamp: j.start_timestamp = timestamp_now() if not j.submit_timestamp: # If a 'pending' submission was never made for this job, # the submit_timestamp may be blank. j.submit_timestamp = timestamp_now() tj = tjc.get_job() tj.add_description(j.description) tj.add_reason(j.reason) tj.add_revision_hash(revision_hash) tj.add_project(project) tj.add_who(j.who) tj.add_job_guid(j.job_guid) tj.add_job_name(j.job_name) tj.add_job_symbol(j.job_symbol) tj.add_group_name(j.group_name) tj.add_group_symbol(j.group_symbol) tj.add_product_name(j.build['product']) tj.add_state(JobState.RUNNING) tj.add_submit_timestamp(j.submit_timestamp) tj.add_start_timestamp(j.start_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_end_timestamp(j.start_timestamp) # tj.add_machine(j.machine['host']) tj.add_build_url(j.build_url) tj.add_build_info(j.build['os_name'], j.build['platform'], j.build['architecture']) tj.add_machine(j.machine['host']) tj.add_machine_info(j.machine['os_name'], j.machine['platform'], j.machine['architecture']) tj.add_option_collection({'opt': True}) tjc.add(tj) self.post_request(project, tjc, j.job_guid)
def submit_running(self, jobs): """Submit jobs running notifications to Treeherder :param jobs: Lists of jobs to be reported. (TestJob) """ self.logger.debug(type(self).__name__ + '.submit_running: jobs =\n%s' % jobs) if not self.url or not jobs: self.logger.debug(type(self).__name__ + '.submit_running: no url/job') return tjc = TreeherderJobCollection() for j in jobs: project = j.build['repo'] revision = j.build['revision'] revision_hash = self.request_revision_hash(project, revision) if not revision_hash: self.logger.debug(type(self).__name__ + '.submit_running: no revision hash') return self.logger.debug(type(self).__name__ + '.submit_running: ' 'for %s %s' % (j.name, project)) if not j.start_timestamp: j.start_timestamp = timestamp_now() if not j.submit_timestamp: # If a 'pending' submission was never made for this job, # the submit_timestamp may be blank. j.submit_timestamp = timestamp_now() tj = tjc.get_job() tj.add_description(j.description) tj.add_reason(j.reason) tj.add_revision_hash(revision_hash) tj.add_project(project) tj.add_who(j.who) tj.add_job_guid(j.job_guid) tj.add_job_name(j.job_name) tj.add_job_symbol(j.job_symbol) tj.add_group_name(j.group_name) tj.add_group_symbol(j.group_symbol) tj.add_product_name(j.build['product']) tj.add_state(JobState.RUNNING) tj.add_submit_timestamp(j.submit_timestamp) tj.add_start_timestamp(j.start_timestamp) # XXX need to send these until Bug 1066346 fixed. tj.add_end_timestamp(j.start_timestamp) # tj.add_machine(j.machine['host']) tj.add_build_info(j.build['os_name'], j.build['platform'], j.build['architecture']) tj.add_machine(j.machine['host']) tj.add_machine_info(j.machine['os_name'], j.machine['platform'], j.machine['architecture']) tj.add_option_collection({'opt': True}) tjc.add(tj) self.post_request(project, tjc, j.job_guid)
def submit_complete(self, jobs): """ Submit results to Treeherder, including uploading logs. All jobs are submitted to the same project in one TreeherderJobCollection. :param jobs: list of jobs (TestJob). """ self.logger.debug(type(self).__name__ + '.submit_complete: jobs =\n%s' % jobs) if not self.url or not jobs: self.logger.debug(type(self).__name__ + '.submit_complete: no url/job') return tjc = TreeherderJobCollection() for j in jobs: project = j.build['repo'] revision = j.build['revision'] revision_hash = self.request_revision_hash(project, revision) if not revision_hash: self.logger.debug(type(self).__name__ + '.submit_complete: no revision hash') return self.logger.debug(type(self).__name__ + '.submit_complete ' 'for %s %s' % (j.name, project)) j.end_timestamp = timestamp_now() # A usercancelled job may not have a start_timestamp # since it may have been cancelled before it started. if not j.start_timestamp: j.start_timestamp = j.end_timestamp # If a 'pending' submission was never made for this job, # the submit_timestamp may be blank. if not j.submit_timestamp: j.submit_timestamp = j.end_timestamp if j.test_result: if j.test_result.failed == 0: failed = '0' else: failed = ('<em class="testfail">%s</em>' % j.test_result.failed) j.job_details.append({ 'value': "%s/%s/%s" % (j.test_result.passed, failed, j.test_result.todo), 'content_type': 'raw_html', 'title': "%s-%s (pass/fail/todo)" % (j.job_name, j.job_symbol) }) tj = tjc.get_job() tj.add_tier(self.tier) tj.add_description(j.description) tj.add_reason(j.reason) tj.add_revision_hash(revision_hash) tj.add_project(project) tj.add_who(j.who) # Note: job_guid should be added before artifacts. tj.add_job_guid(j.job_guid) tj.add_job_name(j.job_name) tj.add_job_symbol(j.job_symbol) tj.add_group_name(j.group_name) tj.add_group_symbol(j.group_symbol) tj.add_product_name(j.build['product']) tj.add_state(JobState.COMPLETED) tj.add_result(j.result) tj.add_submit_timestamp(j.submit_timestamp) tj.add_start_timestamp(j.start_timestamp) tj.add_end_timestamp(j.end_timestamp) tj.add_build_info(j.build['os_name'], j.build['platform'], j.build['architecture']) tj.add_machine(j.machine['host']) tj.add_machine_info(j.machine['os_name'], j.machine['platform'], j.machine['architecture']) tj.add_option_collection({'opt': True}) # Job details and other artifacts # Create/add text_log_summary for each log that should be parsed def build_log_artifacts(log_file, log_url): log_name = os.path.basename(log_file) if (not log_url) or (log_file not in j.parsed_logs): return tj.add_log_reference(log_name, log_url, parse_status='parsed') # NOTE must have started_linenumber < finished_linenumber text_log_summary = parse_log(log_file, log_url, self.logger) tj.add_artifact('text_log_summary', 'json', json.dumps(text_log_summary)) self.logger.debug(type(self).__name__ + '.submit_complete text_log_summary: %s' % pretty(text_log_summary)) # File uploads if self.s3_bucket: prefix = j.unique_s3_prefix filepaths = j.log_files + j.config_files for path in filepaths: url = upload_file(self.s3_bucket, prefix, path, self.logger, j) build_log_artifacts(path, url) if j.upload_dir: for f in glob.glob(os.path.join(j.upload_dir, '*')): url = upload_file(self.s3_bucket, prefix, f, self.logger, j) build_log_artifacts(path, url) tj.add_artifact('Job Info', 'json', {'job_details': j.job_details}) for a in j.artifacts: tj.add_artifact(*a) tjc.add(tj) message = j.message if j.test_result: message += '\nTestResult: %s %s' % (j.test_result.status, j.name) if message: self.logger.info(message) self.post_request(project, tjc, j.job_guid)
def main(): result_revision_hash = create_revision_hash() trsc = TreeherderResultSetCollection() trs = trsc.get_resultset() # self.required_properties = { # 'revision_hash':{ 'len':50, 'cb':self.validate_existence }, # 'revisions':{ 'type':list, 'cb':self.validate_existence }, # 'author':{ 'len':150, 'cb':self.validate_existence } # } trs.add_revision_hash(result_revision_hash) trs.add_author('WebRTC QA Tests') trs.add_push_timestamp(int(time.time())) tr = trs.get_revision() # self.required_properties = { # 'revision':{ 'len':50, 'cb':self.validate_existence }, # 'repository':{ 'cb':self.validate_existence }, # 'files':{ 'type':list, 'cb':self.validate_existence }, # } tr.add_revision(create_revision_hash()[:12]) tr.add_author('Firefox Nightly') tr.add_comment('firefox-33.0a1.en-US') tr.add_files(['firefox-33.0a1.en-US.linux-i686.tar.bz2', 'firefox-33.0a1.en-US.linux-x86_64.tests.zip']) tr.add_repository( 'ftp://ftp.mozilla.org/pub/firefox/nightly/latest-mozilla-central/') trs.add_revision(tr) trsc.add(trs) tjc = TreeherderJobCollection() tj = tjc.get_job() # self.required_properties = { # 'revision_hash':{ 'len':50, 'cb':self.validate_existence }, # 'project':{ 'cb':self.validate_existence }, # 'job':{ 'type':dict, 'cb':self.validate_existence }, # 'job.job_guid':{ 'len':50, 'cb':self.validate_existence } # } tj.add_revision_hash(result_revision_hash) tj.add_project('qa-try') tj.add_job_guid(str(uuid.uuid4())) tj.add_build_info('linux', 'linux64', 'x86_64') tj.add_description('WebRTC Sunny Day') tj.add_machine_info('linux', 'linux64', 'x86_64') tj.add_end_timestamp(int(time.time()) - 5) tj.add_start_timestamp(int(time.time()) - 3600 * 3 - 5) tj.add_submit_timestamp(int(time.time()) - 3600 * 3 - 10) tj.add_state('completed') tj.add_machine('webrtc-server') tj.add_option_collection({'opt': True}) # must not be {}! tj.add_reason('testing') tj.add_result('success') # must be success/testfailed/busted tj.add_who('*****@*****.**') tj.add_group_name('WebRTC QA Tests') tj.add_group_symbol('WebRTC') tj.add_job_symbol('end') tj.add_job_name('Endurance') tj.add_artifact('Job Info', 'json', { "job_details": [ { 'title': 'Iterations:', 'value': '10782', 'content_type': 'text' }, { 'title': 'Errors:', 'value': '5', 'content_type': 'text' }, { 'title': 'Longest Pass Duration:', 'value': '2:58:36.5', 'content_type': 'text' } ], }) tjc.add(tj) key, secret = get_oauth_creds() project, host = get_repo_details() req = TreeherderRequest( protocol='http', host=host, project=project, oauth_key=key, oauth_secret=secret ) print 'trsc = ' + json.dumps(json.loads(trsc.to_json()), sort_keys=True, indent=4, separators=(',', ': ')) print 'tjc = ' + json.dumps(json.loads(tjc.to_json()), sort_keys=True, indent=4, separators=(',', ': ')) # print 'req.oauth_key = ' + req.oauth_key # print 'req.oauth_secret = ' + req.oauth_secret # uri = req.get_uri(trsc) # print 'req.get_uri() = ' + uri # print 'req.oauth_client.get_signed_uri() = ' + # req.oauth_client.get_signed_uri(trsc.to_json(), uri) req.post(trsc) req.post(tjc)
def submit_complete(self, jobs): """ Submit results to Treeherder, including uploading logs. All jobs are submitted to the same project in one TreeherderJobCollection. :param jobs: list of jobs (TestJob). """ self.logger.debug(type(self).__name__ + '.submit_complete: jobs =\n%s' % jobs) if not self.url or not jobs: self.logger.debug(type(self).__name__ + '.submit_complete: no url/job') return tjc = TreeherderJobCollection() for j in jobs: project = j.build['repo'] revision = j.build['revision'] revision_hash = self.request_revision_hash(project, revision) if not revision_hash: self.logger.debug(type(self).__name__ + '.submit_complete: no revision hash') return self.logger.debug(type(self).__name__ + '.submit_complete ' 'for %s %s' % (j.name, project)) j.end_timestamp = timestamp_now() # A usercancelled job may not have a start_timestamp # since it may have been cancelled before it started. if not j.start_timestamp: j.start_timestamp = j.end_timestamp # If a 'pending' submission was never made for this job, # the submit_timestamp may be blank. if not j.submit_timestamp: j.submit_timestamp = j.end_timestamp if j.test_result: if j.test_result.failed == 0: failed = '0' else: failed = ('<em class="testfail">%s</em>' % j.test_result.failed) j.job_details.append({ 'value': "%s/%s/%s" % (j.test_result.passed, failed, j.test_result.todo), 'content_type': 'raw_html', 'title': "%s-%s (pass/fail/todo)" % (j.job_name, j.job_symbol) }) tj = tjc.get_job() tj.add_tier(self.tier) tj.add_description(j.description) tj.add_reason(j.reason) tj.add_revision_hash(revision_hash) tj.add_project(project) tj.add_who(j.who) # Note: job_guid should be added before artifacts. tj.add_job_guid(j.job_guid) tj.add_job_name(j.job_name) tj.add_job_symbol(j.job_symbol) tj.add_group_name(j.group_name) tj.add_group_symbol(j.group_symbol) tj.add_product_name(j.build['product']) tj.add_state(JobState.COMPLETED) tj.add_result(j.result) tj.add_submit_timestamp(j.submit_timestamp) tj.add_start_timestamp(j.start_timestamp) tj.add_end_timestamp(j.end_timestamp) tj.add_build_info(j.build['os_name'], j.build['platform'], j.build['architecture']) tj.add_machine(j.machine['host']) tj.add_machine_info(j.machine['os_name'], j.machine['platform'], j.machine['architecture']) tj.add_option_collection({'opt': True}) # Job details and other artifacts # Add text_log_summary for each parsed log def process_parsed_log(log_file, log_url): log_name = os.path.basename(log_file) if (not log_url) or (log_file not in j.parsed_logs): return error_lines = [{'line': line, 'linenumber': None} for line in j.parsed_logs[log_file]] tj.add_log_reference(log_name, log_url, parse_status='parsed') # NOTE must have started_linenumber < finished_linenumber text_log_summary = { 'step_data': { 'all_errors': error_lines, #'steps': [ # { # 'name': 'step', # 'started_linenumber': None, # 'finished_linenumber': None, # 'duration': j.end_timestamp - j.start_timestamp, # 'finished': '%s' % datetime.datetime.fromtimestamp(j.end_timestamp), # 'errors': error_lines, # 'error_count': len(error_lines), # 'order': 0, # 'result': j.result # }, #], 'errors_truncated': False }, 'logurl': log_url, 'logname': log_name } tj.add_artifact('text_log_summary', 'json', json.dumps(text_log_summary)) self.logger.debug(type(self).__name__ + '.submit_complete text_log_summary: %s' % pretty(text_log_summary)) # File uploads if self.s3_bucket: prefix = j.unique_s3_prefix filepaths = j.log_files + j.config_files for path in filepaths: url = upload_file(self.s3_bucket, prefix, path, self.logger, j) process_parsed_log(path, url) if j.upload_dir: for f in glob.glob(os.path.join(j.upload_dir, '*')): url = upload_file(self.s3_bucket, prefix, f, self.logger, j) process_parsed_log(path, url) tj.add_artifact('Job Info', 'json', {'job_details': j.job_details}) for a in j.artifacts: tj.add_artifact(*a) tjc.add(tj) message = j.message if j.test_result: message += '\nTestResult: %s %s' % (j.test_result.status, j.name) if message: self.logger.info(message) self.post_request(project, tjc, j.job_guid)
def post_to_treeherder(self, tests): self.logger.info('\nTREEHERDER\n----------') version = mozversion.get_version(binary=self.bin, sources=self.sources, dm_type='adb', device_serial=self.device_serial) job_collection = TreeherderJobCollection() job = job_collection.get_job() device = version.get('device_id') if not device: self.logger.error('Submitting to Treeherder is currently limited ' 'to devices.') return try: group = DEVICE_GROUP_MAP[device] job.add_group_name(group['name']) job.add_group_symbol(group['symbol']) job.add_job_name('Gaia Python Integration Test (%s)' % device) job.add_job_symbol('Gip') except KeyError: self.logger.error('Unknown device id: %s, unable to determine ' 'Treeherder group. Supported device ids: %s' % (device, DEVICE_GROUP_MAP.keys())) return # Determine revision hash from application revision revision = version['application_changeset'] project = version['application_repository'].split('/')[-1] lookup_url = urljoin( self.treeherder_url, 'api/project/%s/revision-lookup/?revision=%s' % (project, revision)) self.logger.debug('Getting revision hash from: %s' % lookup_url) response = requests.get(lookup_url) response.raise_for_status() assert response.json(), 'Unable to determine revision hash for %s. ' \ 'Perhaps it has not been ingested by ' \ 'Treeherder?' % revision revision_hash = response.json()[revision]['revision_hash'] job.add_revision_hash(revision_hash) job.add_project(project) job.add_job_guid(str(uuid.uuid4())) job.add_product_name('b2g') job.add_state('completed') # Determine test result if self.failed or self.unexpected_successes: job.add_result('testfailed') else: job.add_result('success') job.add_submit_timestamp(int(self.start_time)) job.add_start_timestamp(int(self.start_time)) job.add_end_timestamp(int(self.end_time)) job.add_machine(socket.gethostname()) job.add_build_info('b2g', 'b2g-device-image', 'x86') job.add_machine_info('b2g', 'b2g-device-image', 'x86') # All B2G device builds are currently opt builds job.add_option_collection({'opt': True}) # TODO: Add log reference # job.add_log_reference() date_format = '%d %b %Y %H:%M:%S' job_details = [{ 'content_type': 'link', 'title': 'Gaia revision:', 'url': 'https://github.com/mozilla-b2g/gaia/commit/%s' % version.get('gaia_changeset'), 'value': version.get('gaia_changeset'), }, { 'content_type': 'text', 'title': 'Gaia date:', 'value': version.get('gaia_date') and time.strftime( date_format, time.localtime(int(version.get('gaia_date')))), }, { 'content_type': 'text', 'title': 'Device identifier:', 'value': version.get('device_id') }, { 'content_type': 'text', 'title': 'Device firmware (date):', 'value': version.get('device_firmware_date') and time.strftime( date_format, time.localtime(int(version.get('device_firmware_date')))), }, { 'content_type': 'text', 'title': 'Device firmware (incremental):', 'value': version.get('device_firmware_version_incremental') }, { 'content_type': 'text', 'title': 'Device firmware (release):', 'value': version.get('device_firmware_version_release') }] if self.ci_url: job_details.append({ 'url': self.ci_url, 'value': self.ci_url, 'content_type': 'link', 'title': 'CI build:' }) if job_details: job.add_artifact('Job Info', 'json', {'job_details': job_details}) # TODO: Add XML/HTML reports as artifacts # job.add_artifact() job_collection.add(job) # Send the collection to Treeherder url = urlparse(self.treeherder_url) request = TreeherderRequest(protocol=url.scheme, host=url.hostname, project=project, oauth_key=self.treeherder_key, oauth_secret=self.treeherder_secret) self.logger.debug('Sending results to Treeherder: %s' % job_collection.to_json()) response = request.post(job_collection) self.logger.debug('Response: %s' % response.read()) assert response.status == 200, 'Failed to send results!' self.logger.info( 'Results are available to view at: %s' % (urljoin(self.treeherder_url, '/ui/#/jobs?repo=%s&revision=%s' % (project, revision))))
def post_to_treeherder(self, script, treeherder_url): job_collection = TreeherderJobCollection() job = job_collection.get_job() job.add_group_name(self.device_properties['name']) job.add_group_symbol(self.device_properties['symbol']) job.add_job_name('Orangutan Monkey Script (%s)' % self.device_properties.get('symbol')) job.add_job_symbol('Om') # Determine revision hash from application revision revision = self.version['application_changeset'] project = self.version['application_repository'].split('/')[-1] lookup_url = urljoin( treeherder_url, 'api/project/%s/revision-lookup/?revision=%s' % (project, revision)) self._logger.debug('Getting revision hash from: %s' % lookup_url) response = requests.get(lookup_url) response.raise_for_status() assert response.json(), 'Unable to determine revision hash for %s. ' \ 'Perhaps it has not been ingested by ' \ 'Treeherder?' % revision revision_hash = response.json()[revision]['revision_hash'] job.add_revision_hash(revision_hash) job.add_project(project) job.add_job_guid(str(uuid.uuid4())) job.add_product_name('b2g') job.add_state('completed') job.add_result(self.runner.crashed and 'testfailed' or 'success') job.add_submit_timestamp(int(self.start_time)) job.add_start_timestamp(int(self.start_time)) job.add_end_timestamp(int(self.end_time)) job.add_machine(socket.gethostname()) job.add_build_info('b2g', 'b2g-device-image', 'x86') job.add_machine_info('b2g', 'b2g-device-image', 'x86') if self.is_debug: job.add_option_collection({'debug': True}) else: job.add_option_collection({'opt': True}) date_format = '%d %b %Y %H:%M:%S' job_details = [{ 'content_type': 'link', 'title': 'Gaia revision:', 'url': 'https://github.com/mozilla-b2g/gaia/commit/%s' % self.version.get('gaia_changeset'), 'value': self.version.get('gaia_changeset'), }, { 'content_type': 'text', 'title': 'Gaia date:', 'value': self.version.get('gaia_date') and time.strftime( date_format, time.localtime(int( self.version.get('gaia_date')))), }, { 'content_type': 'text', 'title': 'Device identifier:', 'value': self.version.get('device_id') }, { 'content_type': 'text', 'title': 'Device firmware (date):', 'value': self.version.get('device_firmware_date') and time.strftime( date_format, time.localtime(int(self.version.get('device_firmware_date')))), }, { 'content_type': 'text', 'title': 'Device firmware (incremental):', 'value': self.version.get('device_firmware_version_incremental') }, { 'content_type': 'text', 'title': 'Device firmware (release):', 'value': self.version.get('device_firmware_version_release') }] ci_url = os.environ.get('BUILD_URL') if ci_url: job_details.append({ 'url': ci_url, 'value': ci_url, 'content_type': 'link', 'title': 'CI build:' }) # Attach log files handlers = [ handler for handler in self._logger.handlers if isinstance(handler, StreamHandler) and os.path.exists(handler.stream.name) ] for handler in handlers: path = handler.stream.name filename = os.path.split(path)[-1] try: url = self.upload_to_s3(path) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Log:' }) # Add log reference if type(handler.formatter) is TbplFormatter or \ type(handler.formatter) is LogLevelFilter and \ type(handler.formatter.inner) is TbplFormatter: job.add_log_reference(filename, url) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) # Attach script filename = os.path.split(script)[-1] try: url = self.upload_to_s3(script) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Script:' }) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) # Attach logcat filename = '%s.log' % self.runner.device.dm._deviceSerial path = os.path.join(self.temp_dir, filename) try: url = self.upload_to_s3(path) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Logcat:' }) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) if job_details: job.add_artifact('Job Info', 'json', {'job_details': job_details}) # Attach crash dumps if self.runner.crashed: crash_dumps = os.listdir(self.crash_dumps_path) for filename in crash_dumps: path = os.path.join(self.crash_dumps_path, filename) try: url = self.upload_to_s3(path) job_details.append({ 'url': url, 'value': filename, 'content_type': 'link', 'title': 'Crash:' }) except S3UploadError: job_details.append({ 'value': 'Failed to upload %s' % filename, 'content_type': 'text', 'title': 'Error:' }) job_collection.add(job) # Send the collection to Treeherder url = urlparse(treeherder_url) request = TreeherderRequest( protocol=url.scheme, host=url.hostname, project=project, oauth_key=os.environ.get('TREEHERDER_KEY'), oauth_secret=os.environ.get('TREEHERDER_SECRET')) self._logger.info('Sending results to Treeherder: %s' % treeherder_url) self._logger.debug('Job collection: %s' % job_collection.to_json()) response = request.post(job_collection) if response.status == 200: self._logger.debug('Response: %s' % response.read()) self._logger.info( 'Results are available to view at: %s' % (urljoin( treeherder_url, '/ui/#/jobs?repo=%s&revision=%s' % (project, revision)))) else: self._logger.error('Failed to send results to Treeherder! ' 'Response: %s' % response.read())
def post_to_treeherder(self, tests): version = mozversion.get_version( binary=self.bin, sources=self.sources, dm_type="adb", device_serial=self.device_serial ) job_collection = TreeherderJobCollection() job = job_collection.get_job() device = version.get("device_id") device_firmware_version_release = version.get("device_firmware_version_release") if not device: self.logger.error("Submitting to Treeherder is currently limited " "to devices.") return try: group = DEVICE_GROUP_MAP[device][device_firmware_version_release] job.add_group_name(group["name"]) job.add_group_symbol(group["symbol"]) job.add_job_name("Gaia Python Integration Test (%s)" % group["symbol"]) job.add_job_symbol("Gip") except KeyError: self.logger.error( "Unknown device id: %s or device firmware " "version: %s. Unable to determine Treeherder " "group. Supported devices: %s" % ( device, device_firmware_version_release, ["%s: %s" % (k, [fw for fw in v.keys()]) for k, v in DEVICE_GROUP_MAP.iteritems()], ) ) return # Determine revision hash from application revision revision = version["application_changeset"] project = version["application_repository"].split("/")[-1] lookup_url = urljoin(self.treeherder_url, "api/project/%s/revision-lookup/?revision=%s" % (project, revision)) self.logger.debug("Getting revision hash from: %s" % lookup_url) response = requests.get(lookup_url) response.raise_for_status() assert response.json(), ( "Unable to determine revision hash for %s. " "Perhaps it has not been ingested by " "Treeherder?" % revision ) revision_hash = response.json()[revision]["revision_hash"] job.add_revision_hash(revision_hash) job.add_project(project) job.add_job_guid(str(uuid.uuid4())) job.add_product_name("b2g") job.add_state("completed") # Determine test result if self.failed or self.unexpected_successes: job.add_result("testfailed") else: job.add_result("success") job.add_submit_timestamp(int(self.start_time)) job.add_start_timestamp(int(self.start_time)) job.add_end_timestamp(int(self.end_time)) job.add_machine(socket.gethostname()) job.add_build_info("b2g", "b2g-device-image", "x86") job.add_machine_info("b2g", "b2g-device-image", "x86") # All B2G device builds are currently opt builds job.add_option_collection({"opt": True}) date_format = "%d %b %Y %H:%M:%S" job_details = [ { "content_type": "link", "title": "Gaia revision:", "url": "https://github.com/mozilla-b2g/gaia/commit/%s" % version.get("gaia_changeset"), "value": version.get("gaia_changeset"), }, { "content_type": "text", "title": "Gaia date:", "value": version.get("gaia_date") and time.strftime(date_format, time.localtime(int(version.get("gaia_date")))), }, {"content_type": "text", "title": "Device identifier:", "value": version.get("device_id")}, { "content_type": "text", "title": "Device firmware (date):", "value": version.get("device_firmware_date") and time.strftime(date_format, time.localtime(int(version.get("device_firmware_date")))), }, { "content_type": "text", "title": "Device firmware (incremental):", "value": version.get("device_firmware_version_incremental"), }, { "content_type": "text", "title": "Device firmware (release):", "value": version.get("device_firmware_version_release"), }, ] ci_url = os.environ.get("BUILD_URL") if ci_url: job_details.append({"url": ci_url, "value": ci_url, "content_type": "link", "title": "CI build:"}) # Attach logcat adb_device = ADBDevice(self.device_serial) with tempfile.NamedTemporaryFile(suffix="logcat.txt") as f: f.writelines(adb_device.get_logcat()) self.logger.debug("Logcat stored in: %s" % f.name) try: url = self.upload_to_s3(f.name) job_details.append({"url": url, "value": "logcat.txt", "content_type": "link", "title": "Log:"}) except S3UploadError: job_details.append({"value": "Failed to upload logcat.txt", "content_type": "text", "title": "Error:"}) # Attach log files handlers = [ handler for handler in self.logger.handlers if isinstance(handler, StreamHandler) and os.path.exists(handler.stream.name) ] for handler in handlers: path = handler.stream.name filename = os.path.split(path)[-1] try: url = self.upload_to_s3(path) job_details.append({"url": url, "value": filename, "content_type": "link", "title": "Log:"}) # Add log reference if ( type(handler.formatter) is TbplFormatter or type(handler.formatter) is LogLevelFilter and type(handler.formatter.inner) is TbplFormatter ): job.add_log_reference(filename, url) except S3UploadError: job_details.append( {"value": "Failed to upload %s" % filename, "content_type": "text", "title": "Error:"} ) # Attach reports for report in [self.html_output]: if report is not None: filename = os.path.split(report)[-1] try: url = self.upload_to_s3(report) job_details.append({"url": url, "value": filename, "content_type": "link", "title": "Report:"}) except S3UploadError: job_details.append( {"value": "Failed to upload %s" % filename, "content_type": "text", "title": "Error:"} ) if job_details: job.add_artifact("Job Info", "json", {"job_details": job_details}) job_collection.add(job) # Send the collection to Treeherder url = urlparse(self.treeherder_url) request = TreeherderRequest( protocol=url.scheme, host=url.hostname, project=project, oauth_key=os.environ.get("TREEHERDER_KEY"), oauth_secret=os.environ.get("TREEHERDER_SECRET"), ) self.logger.debug("Sending results to Treeherder: %s" % job_collection.to_json()) response = request.post(job_collection) self.logger.debug("Response: %s" % response.read()) assert response.status == 200, "Failed to send results!" self.logger.info( "Results are available to view at: %s" % (urljoin(self.treeherder_url, "/ui/#/jobs?repo=%s&revision=%s" % (project, revision))) )