def _find_first_component_from_list(self, possible_components): ret = None for compname in possible_components: try: component = TicketComponent(self.env, compname) ret = component.name break except ResourceNotFound: # No such component exists pass return ret
def _sub_owner_keyword(self, owner, ticket): """Substitute keywords from the default_owner field. < default > -> component owner """ if owner in ('< default >', '<default>'): default_owner = '' if ticket['component']: try: component = TicketComponent(self.env, ticket['component']) except ResourceNotFound: pass # No such component exists else: default_owner = component.owner # May be empty return default_owner return owner
def __call__(self, context, hack_name): hack_type = context.data.get('type', 'plugin') page_name = get_page_name(hack_name, hack_type) if WikiPage(self.env, page_name).exists: raise ValidationError('Page already exists.') repos = self.env.get_repository() path = '/%s' % page_name.lower() if repos.has_node(path): raise ValidationError( 'Resulting path "%s" already exists in repository.' % path ) try: TicketComponent(self.env, page_name) except ResourceNotFound, e: pass
def copy_component(source_env, dest_env, name, dest_db=None): # In case a string gets passed in if not isinstance(source_env, Environment): source_env = _open_environment(source_env) if not isinstance(dest_env, Environment): dest_env = _open_environment(dest_env) # Log message source_env.log.info( 'DatamoverPlugin: Moving component %s to the environment at %s', name, dest_env.path) dest_env.log.info( 'DatamoverPlugin: Moving component %s from the environment at %s', name, source_env.path) # Open databases source_db = source_env.get_db_cnx() source_cursor = source_db.cursor() handle_commit = True if not dest_db: dest_db, handle_commit = dest_env.get_db_cnx(), False dest_cursor = dest_db.cursor() # Remove the component from the destination try: dest_comp = TicketComponent(dest_env, name, db=dest_db) dest_comp.delete(db=dest_db) except TracError: pass # Copy each entry in the component table source_cursor.execute('SELECT * FROM component WHERE name=%s', (name, )) for row in source_cursor: comp_data = dict(zip([d[0] for d in source_cursor.description], row)) q = make_query(comp_data, 'component') dest_cursor.execute(*q) if handle_commit: dest_db.commit()
def get_ticket_changes(self, req, ticket, action): this_action = self.actions[action] # Enforce permissions if not self._is_action_allowed(req, this_action, ticket.resource): # The user does not have any of the listed permissions, so we won't # do anything. return {} updated = {} # Status changes status = this_action['newstate'] if status != '*': updated['status'] = status for operation in this_action['operations']: if operation == 'del_owner': updated['owner'] = '' elif operation in ('set_owner', 'may_set_owner'): set_owner = this_action.get('set_owner') newowner = req.args.get('action_%s_reassign_owner' % action, set_owner[0] if set_owner else '') # If there was already an owner, we get a list, [new, old], # but if there wasn't we just get new. if type(newowner) == list: newowner = newowner[0] updated['owner'] = self._sub_owner_keyword(newowner, ticket) elif operation == 'set_owner_to_self': updated['owner'] = get_reporter_id(req, 'author') elif operation == 'del_resolution': updated['resolution'] = '' elif operation == 'set_resolution': set_resolution = this_action.get('set_resolution') newresolution = req.args.get('action_%s_resolve_resolution' % action, set_resolution[0] if set_resolution else '') updated['resolution'] = newresolution # reset_workflow is just a no-op here, so we don't look for it. # leave_status is just a no-op here, so we don't look for it. # Set owner to component owner for 'new' ticket if: # - ticket doesn't exist and owner is < default > # - component is changed # - owner isn't explicitly changed # - ticket owner is equal to owner of previous component # - new component has an owner if not ticket.exists and 'owner' not in updated: updated['owner'] = self._sub_owner_keyword(ticket['owner'], ticket) elif ticket['status'] == 'new' and \ 'component' in ticket.values and \ 'component' in ticket._old and \ 'owner' not in updated: try: old_comp = TicketComponent(self.env, ticket._old['component']) except ResourceNotFound: # If the old component has been removed from the database # we just leave the owner as is. pass else: old_owner = old_comp.owner or '' current_owner = ticket['owner'] or '' if old_owner == current_owner: new_comp = TicketComponent(self.env, ticket['component']) if new_comp.owner: updated['owner'] = new_comp.owner return updated
def create_hack(self, req, data, vars): import fcntl messages = [] created = False have_lock = False lockfile = open(self.lockfile, "w") try: rv = fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) if rv: raise TracError('Failed to acquire lock, error: %i' % rv) have_lock = True except IOError: messages.append( 'A hack is currently being created by another user. ' 'Please wait a few seconds, then click the "Create hack" ' 'button again.' ) if have_lock: steps_done = [] try: # Step 1: create repository paths from os import popen svn_path = 'file://%s' % \ self.env.config.get('trac', 'repository_dir') svn_path = svn_path.rstrip('/') page_name = vars['WIKINAME'] hack_path = vars['LCNAME'] paths = [ '%s/%s' % (svn_path, hack_path) ] selected_releases = data['selected_releases'] if isinstance(selected_releases, (basestring, unicode)): selected_releases = [ selected_releases, ] for release in selected_releases: if release == 'anyrelease': continue paths.append("%s/%s/%s" % \ (svn_path, hack_path, release)) cmd = '/usr/bin/op create-hack %s ' % req.authname cmd += '"New hack %s, created by %s" ' % \ (page_name, req.authname) cmd += '%s 2>&1' % ' '.join(paths) output = popen(cmd).readlines() if output: raise Exception( "Failed to create Subversion paths:\n%s" % \ '\n'.join(output) ) steps_done.append('repository') # Step 2: Add permissions from svnauthz.model import User, Path, PathAcl authz_file = self.env.config.get('trac', 'authz_file') authz = AuthzFileReader().read(authz_file) svn_path_acl = PathAcl(User(req.authname), r=True, w=True) authz.add_path(Path("/%s" % hack_path, acls = [svn_path_acl,])) AuthzFileWriter().write(authz_file, authz) steps_done.append('permissions') # Step 3: Add component component = TicketComponent(self.env) component.name = page_name component.owner = req.authname component.insert() steps_done.append('component') # Step 4: Create wiki page template_page = WikiPage(self.env, self.template) page = WikiPage(self.env, page_name) page.text = Template(template_page.text).substitute(vars) page.save(req.authname, 'New hack %s, created by %s' % \ (page_name, req.authname), '0.0.0.0') steps_done.append('wiki') # Step 5: Tag the new wiki page res = Resource('wiki', page_name) tags = data['tags'].split() + selected_releases + [data['type']] TagSystem(self.env).set_tags(req, res, tags) steps_done.append('tags') rv = fcntl.flock(lockfile, fcntl.LOCK_UN) created = True except Exception, e: try: if 'tags' in steps_done: res = Resource('wiki', page_name) tags = data['tags'].split() + selected_releases TagSystem(self.env).delete_tags(req, res, tags) if 'wiki' in steps_done: WikiPage(self.env, page_name).delete() if 'component' in steps_done: TicketComponent(self.env, page_name).delete() if 'permissions' in steps_done: authz_file = self.env.config.get('trac', 'authz_file') authz = AuthzFileReader().read(authz_file) authz.del_path(Path("/%s" % hack_path)) AuthzFileWriter().write(authz_file, authz) # TODO: rollback subversion path creation rv = fcntl.flock(lockfile, fcntl.LOCK_UN) except: self.env.log.error("Rollback failed") rv = fcntl.flock(lockfile, fcntl.LOCK_UN) self.env.log.error(e, exc_info=True) raise TracError(str(e))
def process_request_submit(self, req): if req.method != "POST": return self._error_response(req, status=HTTPMethodNotAllowed.code, body='Method %s not allowed' % req.method) manual_upload = req.args.as_int('manual_upload', 0) if manual_upload == 0: user_agent_full = req.get_header('User-Agent') if user_agent_full is None: return self._error_response(req, status=HTTPForbidden.code, body='No user-agent specified.') if '/' in user_agent_full: user_agent, agent_ver = user_agent_full.split('/', 1) else: user_agent = user_agent_full if user_agent != 'terra3d-crashuploader': return self._error_response(req, status=HTTPForbidden.code, body='User-agent %s not allowed' % user_agent_full) headers = {} headers['Max-Upload-Size'] = self.max_upload_size headers['Upload-Disabled'] = '1' if self.upload_disabled else '0' if self.upload_disabled: return self._error_response(req, status=HTTPInternalServerError.code, body='Crashdump upload has been disabled by the administrator.', headers=headers) id_str = req.args.get('id') if not manual_upload: if not id_str or not CrashDump.uuid_is_valid(id_str): return self._error_response(req, status=HTTPInternalServerError.code, body='Invalid crash identifier %s specified.' % id_str) total_upload_size = self._get_total_upload_size(req) if self.max_upload_size > 0 and total_upload_size > self.max_upload_size: self.log.debug('total_upload_size %i > max_upload_size %i' % (total_upload_size, self.max_upload_size) ) return self._error_response(req, status=HTTPInternalServerError.code, body='Upload size %i bytes exceed the upload limit of %i bytes' % (total_upload_size, self.max_upload_size), headers=headers) else: self.log.debug('total_upload_size %i <= max_upload_size %i' % (total_upload_size, self.max_upload_size) ) if manual_upload: self.log.debug('manual_upload') files = req.args.getlist('files') if len(files) == 0: return self._error_response(req, status=HTTPInternalServerError.code, body='No files uploaded.') import re id_str = None minidump = None minidumpreportxml = None for file in files: if isinstance(file, cgi.FieldStorage): filename = os.path.basename(file.filename) self.log.debug('got file %s' % filename) match = re.match(r'^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\.([0-9a-zA-Z\.]+)$', filename) if match: new_id_str = match.groups()[0] ext = match.groups()[1] self.log.debug('got file match %s' % new_id_str) if id_str is None: id_str = new_id_str elif id_str == new_id_str: pass else: return self._error_response(req, status=HTTPInternalServerError.code, body='At the moment uploading multiples crashes is not supported.') if ext == 'dmp': minidump = file elif ext == 'dmp.xml': minidumpreportxml = file self.log.debug('got id %s, ext %s' % (id_str, ext)) else: self.log.debug('skip file field %s-%s' % (type(file), file) ) if not id_str: return self._manual_upload_result(req, error='Cannot determine crash identifier from file upload. The files uploaded must have a UUID in its name and the extentsion must either be .dmp or .dmp.xml.') elif minidump is None and minidumpreportxml is None: return self._manual_upload_result(req, error='Uploaded files do not contain a valid crash dump information.') self.log.debug('got crashid %s' % id_str) if minidump is not None: req.args['minidump'] = minidump if minidumpreportxml is not None: req.args['minidumpreportxml'] = minidumpreportxml uuid = UUID(id_str) crashid = None crashobj = CrashDump.find_by_uuid(self.env, uuid) if not crashobj: crashobj = CrashDump(uuid=uuid, env=self.env, must_exist=False) else: crashid = crashobj.id force_str = req.args.get('force') or 'false' force = True if force_str.lower() == 'true' else False if crashid is not None and not force and not manual_upload: headers = {} headers['Crash-URL'] = req.abs_href('crash', str(uuid)) headers['CrashId'] = str(crashid) self.log.debug('crash %s already uploaded %s' % (uuid, headers['Crash-URL']) ) return self._error_response(req, status=HTTPInternalServerError.code, body='Crash identifier %s already uploaded.' % id_str, headers=headers) ticket_str = req.args.get('ticket') or 'no' linked_tickets = set() ticketobjs = [] new_ticket = None if ticket_str == 'no': pass elif '#' in ticket_str: ticket_ids = [] for t in ticket_str.split(','): if t[0] == '#': ticket_ids.append(int(t[1:])) ticketobjs = [] for tkt_id in ticket_ids: try: ticketobjs.append(Ticket(env=self.env, tkt_id=tkt_id)) except ResourceNotFound: return self._error_response(req, status=HTTPNotFound.code, body='Ticket %i not found. Cannot link crash %s to the requested ticket.' % (tkt_id, str(uuid))) elif ticket_str == 'auto': if crashid is None: new_ticket = Ticket(env=self.env) ticketobjs = [ new_ticket ] else: for tkt_id in crashobj.linked_tickets: try: ticketobjs.append( Ticket(env=self.env, tkt_id=tkt_id) ) break except ResourceNotFound: pass if len(ticketobjs) == 0: new_ticket = Ticket(env=self.env) ticketobjs = [ new_ticket ] elif ticket_str == 'new': new_ticket = Ticket(env=self.env) ticketobjs = [ new_ticket ] else: return self._error_response(req, status=HTTPInternalServerError.code, body='Unrecognized ticket string %s for crash %s.' % (ticket_str, str(uuid))) #print('ticket_str=%s' % ticket_str) #print('ticketobjs=%s' % str(ticketobjs)) # we require at least one crash dump file (either minidump or coredump) # and any number of report files failure_message = None result = False ok, new_minidumpfile, errmsg = self._store_dump_file(uuid, req, 'minidump', force) if ok: result = True elif failure_message is None: failure_message = errmsg ok, new_minidumpreporttextfile, errmsg = self._store_dump_file(uuid, req, 'minidumpreport', force) ok, new_minidumpreportxmlfile, errmsg = self._store_dump_file(uuid, req, 'minidumpreportxml', force) # accept XML crash upload only for manual uploads if manual_upload and ok: result = True elif failure_message is None: failure_message = errmsg ok, new_minidumpreporthtmlfile, errmsg = self._store_dump_file(uuid, req, 'minidumpreporthtml', force) ok, new_coredumpfile, errmsg = self._store_dump_file(uuid, req, 'coredump', force) if ok: result = True elif failure_message is None: failure_message = errmsg ok, new_coredumpreporttextfile, errmsg = self._store_dump_file(uuid, req, 'coredumpreport', force) ok, new_coredumpreportxmlfile, errmsg = self._store_dump_file(uuid, req, 'coredumpreportxml', force) ok, new_coredumpreporthtmlfile, errmsg = self._store_dump_file(uuid, req, 'coredumpreporthtml', force) self.log.debug('new_minidumpfile \'%s\'' % new_minidumpfile) self.log.debug('new_minidumpreportxmlfile \'%s\'' % new_minidumpreportxmlfile) self.log.debug('before crashobj[minidumpfile] \'%s\'' % crashobj['minidumpfile']) self.log.debug('before crashobj[minidumpreportxmlfile] \'%s\'' % crashobj['minidumpreportxmlfile']) if manual_upload: if not crashobj['minidumpfile'] or force: crashobj['minidumpfile'] = new_minidumpfile if not crashobj['minidumpreporttextfile'] or force: crashobj['minidumpreporttextfile'] = new_minidumpreporttextfile if not crashobj['minidumpreportxmlfile'] or force: crashobj['minidumpreportxmlfile'] = new_minidumpreportxmlfile if not crashobj['minidumpreporthtmlfile'] or force: crashobj['minidumpreporthtmlfile'] = new_minidumpreporthtmlfile if not crashobj['coredumpfile'] or force: crashobj['coredumpfile'] = new_coredumpfile if not crashobj['coredumpreporttextfile'] or force: crashobj['coredumpreporttextfile'] = new_coredumpreporttextfile if not crashobj['coredumpreportxmlfile'] or force: crashobj['coredumpreportxmlfile'] = new_coredumpreportxmlfile if not crashobj['coredumpreporthtmlfile'] or force: crashobj['coredumpreporthtmlfile'] = new_coredumpreporthtmlfile else: crashobj['minidumpfile'] = new_minidumpfile crashobj['minidumpreporttextfile'] = new_minidumpreporttextfile crashobj['minidumpreportxmlfile'] = new_minidumpreportxmlfile crashobj['minidumpreporthtmlfile'] = new_minidumpreporthtmlfile crashobj['coredumpfile'] = new_coredumpfile crashobj['coredumpreporttextfile'] = new_coredumpreporttextfile crashobj['coredumpreportxmlfile'] = new_coredumpreportxmlfile crashobj['coredumpreporthtmlfile'] = new_coredumpreporthtmlfile self.log.debug('after crashobj[minidumpfile] \'%s\'' % crashobj['minidumpfile']) self.log.debug('after crashobj[minidumpreportxmlfile] \'%s\'' % crashobj['minidumpreportxmlfile']) new_applicationfile = req.args.get('applicationfile') if not crashobj['applicationfile']: crashobj['applicationfile'] = new_applicationfile self.log.debug('crashtimestamp from http form \'%s\'' % req.args.get('crashtimestamp')) self.log.debug('reporttimestamp from http form \'%s\'' % req.args.get('reporttimestamp')) try: crashtimestamp = parse_date(req.args.get('crashtimestamp', ''), hint='iso8601' ) except TracError: crashtimestamp = None self.log.warn('invalid crash timestamp \'%s\'' % (req.args.get('crashtimestamp'))) try: reporttimestamp = parse_date(req.args.get('reporttimestamp', ''), hint='iso8601' ) except TracError: reporttimestamp = None self.log.warn('invalid crash report timestamp \'%s\'' % (req.args.get('reporttimestamp'))) crashobj['crashtime'] = crashtimestamp if crashtimestamp else None crashobj['reporttime'] = reporttimestamp if reporttimestamp else None crashobj['uploadtime'] = datetime.datetime.now(utc) self.log.debug('crashtimestamp %s' % (crashobj['crashtime'])) self.log.debug('reporttimestamp %s' % (crashobj['reporttime'])) self.log.debug('uploadtime %s' % (crashobj['uploadtime'])) if not manual_upload: crashobj['productname'] = req.args.get('productname') crashobj['productcodename'] = req.args.get('productcodename') crashobj['productversion'] = req.args.get('productversion') crashobj['producttargetversion'] = req.args.get('producttargetversion') crashobj['uploadhostname'] = req.args.get('fqdn') crashobj['uploadusername'] = req.args.get('username') crashobj['crashhostname'] = req.args.get('crashfqdn') crashobj['crashusername'] = req.args.get('crashusername') crashobj['buildtype'] = req.args.get('buildtype') crashobj['buildpostfix'] = req.args.get('buildpostfix') crashobj['machinetype'] = req.args.get('machinetype') crashobj['systemname'] = req.args.get('systemname') crashobj['osversion'] = req.args.get('osversion') crashobj['osrelease'] = req.args.get('osrelease') crashobj['osmachine'] = req.args.get('osmachine') if result: xmlreport = None try: if crashobj['minidumpreportxmlfile']: xmlfile = self._get_dump_filename(crashobj, 'minidumpreportxmlfile') xmlreport = XMLReport(xmlfile) elif crashobj['coredumpreportxmlfile']: xmlfile = self._get_dump_filename(crashobj, 'coredumpreportxmlfile') xmlreport = XMLReport(xmlfile) except XMLReport.XMLReportException as e: return self._error_response(req, status=HTTPInternalServerError.code, body='Failed to process crash dump %s: %s' % (uuid, str(e))) if xmlreport and manual_upload: if xmlreport.crash_info: crashobj['crashtime'] = xmlreport.crash_info.crash_timestamp crashobj['reporttime'] = xmlreport.crash_info.report_time crashobj['uploadhostname'] = req.remote_addr crashobj['uploadusername'] = req.remote_user crashobj['applicationfile'] = xmlreport.crash_info.application if xmlreport.fast_protect_version_info: crashobj['productname'] = xmlreport.fast_protect_version_info.product_name crashobj['productcodename'] = xmlreport.fast_protect_version_info.product_code_name crashobj['productversion'] = xmlreport.fast_protect_version_info.product_version crashobj['producttargetversion'] = xmlreport.fast_protect_version_info.product_target_version crashobj['buildtype'] = xmlreport.fast_protect_version_info.product_build_type crashobj['buildpostfix'] = xmlreport.fast_protect_version_info.product_build_postfix if xmlreport.fast_protect_system_info: crashobj['crashhostname'] = xmlreport.fast_protect_system_info.fqdn crashobj['crashusername'] = xmlreport.fast_protect_system_info.username crashobj['machinetype'] = xmlreport.fast_protect_system_info.machine_type if xmlreport.system_info: crashobj['systemname'] = xmlreport.system_info.platform_type crashobj['osversion'] = xmlreport.system_info.os_version crashobj['osrelease'] = xmlreport.system_info.os_build_number crashobj['osmachine'] = xmlreport.system_info.cpu_type # get the application name from the application file if crashobj['applicationfile']: appfile = crashobj['applicationfile'] if '/' in appfile: appbase = appfile.split('/')[-1] elif '\\' in appfile: appbase = appfile.split('\\')[-1] else: appbase = os.path.basename(appfile) (appbase, ext) = os.path.splitext(appbase) if crashobj['buildpostfix'] and appbase.endswith(crashobj['buildpostfix']): appbase = appbase[:-len(crashobj['buildpostfix'])] crashobj['applicationname'] = appbase new_crash = True if crashid is None else False if new_crash: crashobj['status'] = 'new' crashobj['type'] = 'crash' crashobj['priority'] = self.default_priority if self.default_milestone == '< default >': crashobj['milestone'] = self._find_milestone(crashobj['productversion'], crashobj['producttargetversion']) else: crashobj['milestone'] = self.default_milestone if self.default_version == '< default >': crashobj['version'] = self._find_version(crashobj['productversion'], crashobj['producttargetversion']) else: crashobj['version'] = self.default_version if self.default_component == '< default >': if xmlreport is not None and xmlreport.exception is not None and xmlreport.exception.involved_modules: crashobj['component'] = self._find_component_from_involved_modules(xmlreport.exception.involved_modules, crashobj['buildpostfix']) if not crashobj['component']: crashobj['component'] = self._find_component_for_application(crashobj['applicationname']) else: crashobj['component'] = self.default_component crashobj['severity'] = self.default_severity crashobj['summary'] = self.default_summary crashobj['description'] = self.default_description crashobj['keywords'] = self.default_keywords if self.default_owner == '< default >': default_to_owner = '' if crashobj['component']: try: component = TicketComponent(self.env, crashobj['component']) default_to_owner = component.owner # even if it's empty except ResourceNotFound: # No such component exists pass if default_to_owner: crashobj['owner'] = default_to_owner else: # If the current owner is "< default >", we need to set it to # _something_ else, even if that something else is blank. crashobj['owner'] = crashobj['crashusername'] else: crashobj['owner'] = self.default_owner if self.default_reporter == '< default >': crashobj['reporter'] = crashobj['crashusername'] else: crashobj['reporter'] = self.default_reporter # apply replacements on usernames in owner and reporter field crashobj['owner'] = self._apply_username_replacements(crashobj['owner']) crashobj['reporter'] = self._apply_username_replacements(crashobj['reporter']) crashid = crashobj.insert() result = True if crashid else False if result: if xmlreport is not None and xmlreport.exception is not None: ex_thread = xmlreport.exception.thread else: ex_thread = None if ex_thread is not None: threadid = ex_thread.id stackdump = ex_thread.simplified_stackdump if ex_thread.simplified_stackdump is not None else ex_thread.stackdump if stackdump: for frameno, frm in enumerate(stackdump.callstack): frameobj = CrashDumpStackFrame(crashid, threadid,frameno, env=self.env) frameobj['module'] = frm.module frameobj['function'] = frm.function frameobj['funcoff'] = frm.funcoff frameobj['source'] = frm.source frameobj['line'] = frm.line frameobj['lineoff'] = frm.lineoff frameobj.insert() else: #print('update crash %s' % crashobj) result = crashobj.save_changes(author=crashobj['crashusername']) if result: values = crashobj.values values['crashtimestamp'] = crashtimestamp values['reporttimestamp'] = reporttimestamp values['crashid'] = crashid values['uuid'] = crashobj.uuid values['app'] = crashobj['applicationname'] if crashobj['applicationname'] else crashobj['applicationfile'] # Update all already linked tickets for tkt_id in crashobj.linked_tickets: try: new_linked_ticketobj = Ticket(env=self.env, tkt_id=tkt_id) comment = """The crash [[/crash/%(uuid)s|CrashId#%(crashid)s - %(uuid)s]] has been updated by **%(uploadusername)s** from **%(uploadhostname)s** is already linked to this ticket. """ % values new_linked_ticketobj.save_changes(author=crashobj['reporter'], comment=comment) # Only add valid tickets to the linked_tickets set linked_tickets.add(tkt_id) except ResourceNotFound: pass if new_ticket is not None: new_ticket['type'] = self.default_ticket_type new_ticket['summary'] = "Crash %(uuid)s in %(app)s" % values comment = """The crash [[/crash/%(uuid)s|CrashId#%(crashid)s - %(uuid)s]] has been uploaded by **%(uploadusername)s** from **%(uploadhostname)s** and this ticket has been automatically created to track the progress in finding and resolving the cause of the crash. """ % values new_ticket['description'] = comment # copy over some fields from the crash itself for field in ['status', 'priority', 'milestone', 'component', 'severity', 'keywords']: new_ticket[field] = crashobj[field] # apply replacements on usernames in owner and reporter field new_ticket['owner'] = self._apply_username_replacements(crashobj['owner']) new_ticket['reporter'] = self._apply_username_replacements(crashobj['reporter']) new_ticket['linked_crash'] = str(crashid) new_ticket.insert() # Now add the newly linked tickets as well for tkt_obj in ticketobjs: if tkt_obj.id not in crashobj.linked_tickets: ticket_values = self.escape_ticket_values(values) #self.log.debug('ticket_values=%s' % str(ticket_values)) comment = """The crash [[/crash/%(uuid)s|CrashId#%(crashid)s - %(uuid)s]] has been uploaded by **%(uploadusername)s** from **%(uploadhostname)s** and linked to this ticket. The crash occured at //%(crashtimestamp)s UTC// on **%(crashhostname)s** with user **%(crashusername)s** while running `%(applicationfile)s`. The application was running as part of %(productname)s (%(productcodename)s) version %(productversion)s (%(producttargetversion)s, %(buildtype)s) on a %(systemname)s/%(machinetype)s with %(osversion)s (%(osrelease)s/%(osmachine)s). """ % ticket_values linked_crashes = tkt_obj['linked_crash'] if tkt_obj['linked_crash'] else '' linked_crashes = set([int(x.strip()) for x in linked_crashes.split(',') if x.strip()]) #print('crashid=%s' % crashid) #print('linked_crashes=%s' % linked_crashes) linked_crashes.add(crashid) #print('linked_crashes=%s' % linked_crashes) tkt_obj['linked_crash'] = ', '.join(str(x) for x in sorted(linked_crashes)) tkt_obj.save_changes(author=crashobj['reporter'], comment=comment) linked_tickets.add(tkt_obj.id) with self.env.db_transaction as db: links = CrashDumpTicketLinks(self.env, tkt=tkt_obj, db=db) links.crashes.add(crashid) links.save(author=crashobj['reporter'], db=db) if result: if manual_upload: req.redirect(req.abs_href('crash', str(uuid))) else: headers = {} linked_ticket_header = [] for tkt_id in linked_tickets: linked_ticket_header.append('#%i:%s' % (tkt_id, req.abs_href.ticket(tkt_id))) if linked_ticket_header: headers['Linked-Tickets'] = ';'.join(linked_ticket_header) headers['Crash-URL'] = req.abs_href('crash', str(uuid)) headers['CrashId'] = str(crashid) return self._success_response(req, body='Crash dump %s uploaded successfully.' % uuid, headers=headers) elif new_crash: return self._error_response(req, status=HTTPInternalServerError.code, body='Failed to add crash dump %s to database' % uuid) else: headers = {} headers['Crash-URL'] = req.abs_href('crash', str(uuid)) headers['CrashId'] = str(crashid) return self._error_response(req, status=HTTPInternalServerError.code, body='Failed to update crash dump %s to database' % uuid, headers=headers) else: if failure_message is None: body = 'Failed to process crash dump %s' % uuid else: body = 'The following occured while processing the crash dump %s: %s' % (uuid, failure_message) return self._error_response(req, status=HTTPInternalServerError.code, body=body)