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 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 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 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 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 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 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 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 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 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))) )
def main(): submit_time, start_time, end_time = argv[1:4] config = get_config() app_revision, app_repository = get_app_information(config) files = get_files(config) build_version = get_build_version(os.path.basename(files[0])) push_time = int(os.stat(files[0]).st_ctime) results = steepleparse.parse(config['system']['logfile']) result_set_hash = create_revision_hash() trsc = TreeherderResultSetCollection() trs = trsc.get_resultset() trs.add_revision_hash(result_set_hash) trs.add_author('Firefox Nightly') trs.add_push_timestamp(push_time) tr = trs.get_revision() tr.add_revision(app_revision) tr.add_author('Firefox Nightly') tr.add_comment(build_version) tr.add_files([os.path.basename(f) for f in files]) tr.add_repository(app_repository) trs.add_revision(tr) trsc.add(trs) tjc = TreeherderJobCollection() tj = tjc.get_job() tj.add_revision_hash(result_set_hash) tj.add_project(config['repo']['project']) tj.add_job_guid(str(uuid.uuid4())) tj.add_group_name('WebRTC QA Tests') tj.add_group_symbol('WebRTC') tj.add_job_name('Endurance') tj.add_job_symbol('end') tj.add_build_info('linux', 'linux64', 'x86_64') tj.add_machine_info('linux', 'linux64', 'x86_64') tj.add_description('WebRTC Sunny Day') tj.add_option_collection({'opt': True}) # must not be {}! tj.add_reason('testing') tj.add_who('Mozilla Platform QA') tj.add_submit_timestamp(submit_time) tj.add_start_timestamp(start_time) tj.add_end_timestamp(end_time) tj.add_state('completed') tj.add_machine(socket.gethostname()) result_string = get_result_string(results) tj.add_result(result_string) if result_string != 'busted': summary = get_result_summary(results) tj.add_artifact('Job Info', 'json', summary) tj.add_artifact('Results', 'json', results) tjc.add(tj) 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=(',', ': ')) req = TreeherderRequest( protocol='http', host=config['repo']['host'], project=config['repo']['project'], oauth_key=config['credentials']['key'], oauth_secret=config['credentials']['secret'] ) req.post(trsc) req.post(tjc)
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 submit_complete(self, test=None, test_status=None, test_message=None): """Submit test results for the worker's current job to Treeherder. submit_complete operates in two modes: * To report an infrastructure error which has prevented any of the tests from running. In this case, the test argument is None and the test_status and test_message will be used to report the error for each of the tests defined for the worker using PhoneTest.add_failure. * To report the status of an individual test. In this case, the test argument references a test object and both test_status and test_message are required to be None. The Treeherder test_status is determined by whether there were any failures reported. :param test: test to be reported. :param test_status: global test status to be reported. :param test_message: global test message to be reported. """ self.worker.loggerdeco.debug('AutophoneTreeherder.submit_complete: %s' % test) assert((test is None and test_status and test_message and test_status != PhoneTestResult.SUCCESS) or (test is not None and test_status is None and test_message is None)) if not self.url or not self.worker.build.revision_hash: self.worker.loggerdeco.debug('AutophoneTreeherder.submit_complete: no url/revision hash') return tjc = TreeherderJobCollection() if test: tests = [test] else: tests = self.worker.runnable_tests for t in tests: t.test_result.add_failure(t.name, test_status, test_message) for t in tests: self.worker.loggerdeco.debug('AutophoneTreeherder.submit_complete ' 'for %s %s' % (t.name, t.build.tree)) t.end_timestamp = timestamp_now() # A usercancelled job may not have a start_timestamp # since it may have been cancelled before it started. if not t.start_timestamp: t.start_timestamp = t.end_timestamp if t.test_result.failed == 0: test_status = PhoneTestResult.SUCCESS failed = '0' else: if not test_status: test_status = PhoneTestResult.TESTFAILED failed = '<em class="testfail">%s</em>' % t.test_result.failed t.job_details.append({ 'value': "%s/%s/%s" % (t.test_result.passed, failed, t.test_result.todo), 'content_type': 'raw_html', 'title': "%s-%s" % (t.job_name, t.job_symbol) }) bug_suggestions = self.get_bug_suggestions(t.test_result.failures) if hasattr(t, 'phonedash_url'): t.job_details.append({ 'url': t.phonedash_url, 'value': 'graph', 'content_type': 'link', 'title': 'phonedash:' }) tj = tjc.get_job() # Attach logs if self.worker.s3_bucket: # We must make certain that S3 keys for uploaded files # are unique. We can create a unique log_identifier as # follows: For Unittests, t._log's basename contains a # unique name based on the actual Unittest name, chunk # and phone id. For Non-Unittests, the test classname, # chunk and phone id can be used. if t._log: log_identifier = os.path.splitext(os.path.basename(t._log))[0] else: log_identifier = "%s-%s-%s" % (t.name, t.chunk, t.phone.id) key_prefix = os.path.dirname( urlparse.urlparse(self.worker.build.url).path) key_prefix = re.sub('/tmp$', '', key_prefix) # Logcat fname = '%s-logcat.log' % log_identifier lname = 'logcat' with tempfile.NamedTemporaryFile(suffix='logcat.txt') as f: for line in t.logcat.get(full=True): f.write('%s\n' % line) try: url = self.worker.s3_bucket.upload(f.name, "%s/%s" % ( key_prefix, fname)) t.job_details.append({ 'url': url, 'value': lname, 'content_type': 'link', 'title': 'artifact uploaded:'}) except S3Error: self.worker.loggerdeco.exception('Error uploading logcat %s' % fname) t.job_details.append({ 'value': 'Failed to upload %s' % fname, 'content_type': 'text', 'title': 'Error:'}) # UnitTest Log if t._log: logfile = os.path.basename(t._log) try: url = self.worker.s3_bucket.upload(t._log, "%s/%s" % ( key_prefix, logfile)) t.job_details.append({ 'url': url, 'value': logfile, 'content_type': 'link', 'title': 'artifact uploaded:'}) # don't add log reference since we don't # use treeherder's log parsing. #tj.add_log_reference(logfile, url) except S3Error: self.worker.loggerdeco.exception('Error uploading log %s' % logfile) t.job_details.append({ 'value': 'Failed to upload log %s' % logfile, 'content_type': 'text', 'title': 'Error:'}) # Upload directory containing ANRs, tombstones and other items # to be uploaded. if t.upload_dir: for f in glob.glob(os.path.join(t.upload_dir, '*')): try: lname = os.path.basename(f) fname = '%s-%s' % (log_identifier, lname) url = self.worker.s3_bucket.upload(f, "%s/%s" % ( key_prefix, fname)) t.job_details.append({ 'url': url, 'value': lname, 'content_type': 'link', 'title': 'artifact uploaded:'}) except S3Error: self.worker.loggerdeco.exception('Error uploading artifact %s' % fname) t.job_details.append({ 'value': 'Failed to upload artifact %s' % fname, 'content_type': 'text', 'title': 'Error:'}) # Since we are submitting results to Treeherder, we flush # the worker's log before uploading the log to # Treeherder. When we upload the log, it will contain # results for a single test run with possibly an error # message from the previous test if the previous log # upload failed. self.worker.filehandler.flush() logfile = self.worker.logfile fname = 'autophone-%s.log' % log_identifier lname = 'Autophone Log' try: url = self.worker.s3_bucket.upload( logfile, "%s/%s" % (key_prefix, fname)) t.job_details.append({ 'url': url, 'value': lname, 'content_type': 'link', 'title': 'artifact uploaded:'}) except S3Error: self.worker.loggerdeco.exception('Error uploading %s' % fname) t.job_details.append({ 'value': 'Failed to upload Autophone log', 'content_type': 'text', 'title': 'Error:'}) 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.COMPLETED) tj.add_result(test_status) tj.add_submit_timestamp(t.submit_timestamp) tj.add_start_timestamp(t.start_timestamp) tj.add_end_timestamp(t.end_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('Job Info', 'json', {'job_details': t.job_details}) if bug_suggestions: tj.add_artifact('Bug suggestions', 'json', bug_suggestions) tj.add_artifact('buildapi', 'json', {'buildername': t.buildername}) tjc.add(tj) message = '%s %s %s TestResult: %s' % (self.worker.build.tree, self.worker.build.id, t.name, test_status) if t.message: message += ', %s' % t.message self.worker.loggerdeco.info(message) self.worker.loggerdeco.debug('AutophoneTreeherder.submit_completed: tjc: %s' % tjc.to_json()) self.post_request(tjc)
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:'}) artifacts = [self.html_output, self.xml_output] if any(artifacts): required_envs = ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY'] upload_artifacts = all([os.environ.get(v) for v in required_envs]) if upload_artifacts: conn = boto.connect_s3() bucket = conn.create_bucket( os.environ.get('S3_UPLOAD_BUCKET', 'gaiatest')) bucket.set_acl('public-read') for artifact in artifacts: if artifact and os.path.exists(artifact): h = hashlib.sha512() with open(artifact, 'rb') as f: for chunk in iter(lambda: f.read(1024 ** 2), b''): h.update(chunk) _key = h.hexdigest() key = bucket.get_key(_key) if not key: key = bucket.new_key(_key) key.set_contents_from_filename(artifact) key.set_acl('public-read') blob_url = key.generate_url(expires_in=0, query_auth=False) job_details.append({ 'url': blob_url, 'value': artifact, 'content_type': 'link', 'title': 'Artifact:'}) self.logger.info('Artifact %s uploaded to: %s' % ( artifact, blob_url)) else: self.logger.info( 'Artifacts will not be included with the report. Please ' 'set the following environment variables to enable ' 'uploading of artifacts: %s' % ', '.join([ v for v in required_envs if not os.environ.get(v)])) 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=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 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_pending(self, machine, build_url, project, revision_hash, 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_hash: Treeherder revision hash of build. :param tests: Lists of tests to be reported. """ logger.debug('AutophoneTreeherder.submit_pending: %s' % tests) if not self.url or not revision_hash: logger.debug('AutophoneTreeherder.submit_pending: no url/revision hash') return tjc = TreeherderJobCollection(job_type='update') for t in tests: t.message = None t.submit_timestamp = timestamp_now() t.job_details = [] logger.info('creating Treeherder job %s for %s %s, ' 'revision_hash: %s' % ( t.job_guid, t.name, project, revision_hash)) logger.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(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.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(machine) tj.add_build_url(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.get_buildername(project)}) # Create a 'privatebuild' artifact for storing information # regarding the build. tj.add_artifact('privatebuild', 'json', { 'build_url': build_url, 'config_file': t.config_file, 'chunk': t.chunk}) tjc.add(tj) logger.debug('AutophoneTreeherder.submit_pending: tjc: %s' % ( tjc.to_json())) self.post_request(machine, project, tjc)
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))))