def delay_jobs(ticket=None, delay=None, back=True): """ Delay pending jobs for given `abuse.models.Ticket` :param `abuse.models.Ticket` ticket: The Cerberus ticket :param int delay: Postpone duration :param bool back: In case of unpause, reschedule jobs with effectively elapsed time """ if not delay: Logger.error(unicode('Missing delay. Skipping...')) return if not isinstance(ticket, Ticket): try: ticket = Ticket.objects.get(id=ticket) except (AttributeError, ObjectDoesNotExist, TypeError, ValueError): Logger.error(unicode('Ticket %d cannot be found in DB. Skipping...' % (ticket))) return # a job is here a tuple (Job instance, datetime instance) pending_jobs = {job[0].id: job for job in utils.scheduler.get_jobs(until=timedelta(days=7), with_times=True)} for job in ticket.jobs.all(): if pending_jobs.get(job.asynchronousJobId): current_date = pending_jobs[job.asynchronousJobId][1] new_date = current_date - delay if back else current_date + delay utils.scheduler.change_execution_time( pending_jobs[job.asynchronousJobId][0], new_date )
def _generates_onclose_kpi(ticket): """ Kpi on ticket close """ try: ImplementationFactory.instance.get_singleton_of('KPIServiceBase').close_ticket(ticket) except KPIServiceException as ex: Logger.error(unicode('Error while pushing KPI - %s' % (ex)))
def __cancel_by_status(ticket): """ Action cancelled because of ticket status """ current_job = get_current_job() Logger.error(unicode('Ticket %d is %s, Skipping...' % (ticket.id, ticket.status))) ServiceActionJob.objects.filter( asynchronousJobId=current_job.id ).update( status='cancelled', comment='ticket is %s' % (ticket.status) )
def __index_report_to_searchservice(parsed_email, filename, reports_id): """ Index a report to the SearchService """ try: Logger.debug(unicode('Pushing email %s document to SearchService' % (filename))) ImplementationFactory.instance.get_singleton_of('SearchServiceBase').index_email( parsed_email, filename, reports_id ) except SearchServiceException as ex: # Not fatal => don't stop current routine Logger.error(unicode('Unable to index mail %s in SearchService -> %s' % (filename, ex)))
def _get_ip_for_action(ticket): """ Extract and check IP address """ # Get ticket IP(s) reports = ticket.reportTicket.all() ips_on_ticket = [itm.ip for rep in reports for itm in rep.reportItemRelatedReport.filter(~Q(ip=None), itemType='IP')] ips_on_ticket.extend([itm.fqdnResolved for rep in reports for itm in rep.reportItemRelatedReport.filter(~Q(fqdnResolved=None), itemType__in=['FQDN', 'URL'])]) ips_on_ticket = list(set(ips_on_ticket)) if len(ips_on_ticket) != 1: Logger.error(unicode('Multiple or no IP on this ticket')) return return ips_on_ticket[0]
def _genereates_oncreate_kpi(ticket): """ Kpi on ticket creation """ Logger.debug( unicode('new ticket %d' % (ticket.id)), extra={ 'ticket': ticket.id, 'action': 'new ticket', 'public_id': ticket.publicId } ) try: ImplementationFactory.instance.get_singleton_of('KPIServiceBase').new_ticket(ticket) except KPIServiceException as ex: Logger.error(unicode('Error while pushing KPI - %s' % (ex)))
def log_new_report(report): """ Log report creation """ Logger.debug( unicode('New report %d' % (report.id)), extra={ 'from': report.provider.email, 'action': 'new report', 'report': report.id, } ) if ImplementationFactory.instance.is_implemented('KPIServiceBase'): try: ImplementationFactory.instance.get_singleton_of('KPIServiceBase').new_report(report) except KPIServiceException as ex: Logger.error(unicode('Error while pushing KPI - %s' % (ex)))
def close_because_all_down(report=None, denied_by=None): """ Create and close a ticket when all report's items are down :param `abuse.models.Report` report: A Cerberus `abuse.models.Report` instance :param int denied_by: The id of the `abuse.models.User` who takes the decision to close the ticket """ if not isinstance(report, Report): try: report = Report.objects.get(id=report) except (AttributeError, ObjectDoesNotExist, TypeError, ValueError): Logger.error(unicode('Report %d cannot be found in DB. Skipping...' % (report))) return if not report.ticket: report.ticket = common.create_ticket(report, denied_by) report.save() # Add temp proof(s) for mail content temp_proofs = [] if not report.ticket.proof.count(): temp_proofs = common.get_temp_proofs(report.ticket) # Send email to Provider try: validate_email(report.provider.email.strip()) Logger.info(unicode('Sending email to provider')) __send_email(report.ticket, report.provider.email, settings.CODENAMES['no_more_content']) report.ticket.save() Logger.info(unicode('Mail sent to provider')) ImplementationFactory.instance.get_singleton_of('MailerServiceBase').close_thread(report.ticket) # Delete temp proof(s) for proof in temp_proofs: Proof.objects.filter(id=proof.id).delete() except (AttributeError, TypeError, ValueError, ValidationError): pass # Closing ticket and add tags common.close_ticket(report, resolution_codename=settings.CODENAMES['no_more_content']) report.ticket.tags.remove(Tag.objects.get(name=settings.TAGS['phishing_autoreopen'])) report.ticket.tags.add(Tag.objects.get(name=settings.TAGS['phishing_autoclosed'])) Logger.info(unicode('Ticket %d and report %d closed' % (report.ticket.id, report.id)))
def mass_contact(ip_address=None, category=None, campaign_name=None, email_subject=None, email_body=None, user_id=None): """ Try to identify customer based on `ip_address`, creates Cerberus ticket then send email to customer and finally close ticket. The use case is: a trusted provider sent you a list of vulnerable DNS servers (DrDOS amp) for example. To prevent abuse on your network, you notify customer of this vulnerability. :param str ip_address: The IP address :param str category: The category of the abuse :param str campaign_name: The name if the "mass-conctact" campaign :param str email_subject: The subject of the email to send to defendant :param str email_body: The body of the email to send to defendant :param int user_id: The id of the Cerberus `abuse.models.User` who created the campaign """ # Check params _, _, _, values = inspect.getargvalues(inspect.currentframe()) if not all(values.values()): Logger.error(unicode('invalid parameters submitted %s' % str(values))) return try: validate_ipv46_address(ip_address) except (TypeError, ValidationError): Logger.error(unicode('invalid ip addresses submitted')) return # Get Django model objects try: category = Category.objects.get(name=category) user = User.objects.get(id=user_id) except (AttributeError, ObjectDoesNotExist, TypeError): Logger.error(unicode('invalid user or category')) return # Identify service for ip_address try: services = ImplementationFactory.instance.get_singleton_of('CustomerDaoBase').get_services_from_items(ips=[ip_address]) schema.valid_adapter_response('CustomerDaoBase', 'get_services_from_items', services) except CustomerDaoException as ex: Logger.error(unicode('Exception while identifying defendants for ip %s -> %s ' % (ip_address, str(ex)))) raise CustomerDaoException(ex) # Create report/ticket if services: Logger.debug(unicode('creating report/ticket for ip address %s' % (ip_address))) with pglocks.advisory_lock('cerberus_lock'): __create_contact_tickets(services, campaign_name, ip_address, category, email_subject, email_body, user) return True else: Logger.debug(unicode('no service found for ip address %s' % (ip_address))) return False
def reparse_validated(report_id=None, user_id=None): """ Reparse now validated `abuse.models.Report` :param int report_id: A Cerberus `abuse.models.Report` id :param int user_id: A Cerberus `abuse.models.User` id """ try: report = Report.objects.get(id=report_id) user = User.objects.get(id=user_id) except (ObjectDoesNotExist, ValueError): Logger.error(unicode('Report %d cannot be found in DB. Skipping...' % (report_id))) return if not report.defendant or not report.service: _create_closed_ticket(report, user) else: _reinject_validated(report, user) Logger.error(unicode('Report %d successfully processed' % (report_id)))
def cancel_rq_scheduler_jobs(ticket_id=None, status='answered'): """ Cancel all rq scheduler jobs for given `abuse.models.Ticket` :param int ticket_id: The id of the `abuse.models.Ticket` :param str status: The `abuse.models.Ticket.TICKET_STATUS' reason of the cancel """ try: ticket = Ticket.objects.get(id=ticket_id) except (AttributeError, ObjectDoesNotExist, TypeError, ValueError): Logger.error(unicode('Ticket %d cannot be found in DB. Skipping...' % (ticket))) return for job in utils.scheduler.get_jobs(): if job.func_name in ASYNC_JOB_TO_CANCEL and job.kwargs['ticket_id'] == ticket.id: utils.scheduler.cancel(job.id) for job in ticket.jobs.all(): if job.asynchronousJobId in utils.scheduler: utils.scheduler.cancel(job.asynchronousJobId) job.status = 'cancelled by %s' % status job.save()
def timeout(ticket_id=None): """ If ticket timeout , apply action on service (if defendant not internal/VIP) and ticket is not assigned :param int ticket_id: The id of the Cerberus `abuse.models.Ticket` """ try: ticket = Ticket.objects.get(id=ticket_id) except (AttributeError, ObjectDoesNotExist, ValueError): Logger.error(unicode('Ticket %d cannot be found in DB. Skipping...' % (ticket_id))) return if not _check_timeout_ticket_conformance(ticket): return action = ImplementationFactory.instance.get_singleton_of('ActionServiceBase').get_action_for_timeout(ticket) if not action: Logger.error(unicode('Ticket %d service %s: action not found, exiting ...' % (ticket_id, ticket.service.componentType))) return # Maybe customer fixed, closing ticket if ticket.category.name.lower() == 'phishing' and phishing.is_all_down_for_ticket(ticket): Logger.info(unicode('All items are down for ticket %d, closing ticket' % (ticket_id))) close_ticket(ticket, reason=settings.CODENAMES['fixed_customer'], service_blocked=False) return # Getting ip for action ip_addr = _get_ip_for_action(ticket) if not ip_addr: Logger.error(unicode('Error while getting IP for action, exiting')) ticket.status = ticket.previousStatus ticket.status = 'ActionError' database.log_action_on_ticket( ticket=ticket, action='change_status', previous_value=ticket.previousStatus, new_value=ticket.status ) comment = Comment.objects.create(user=BOT_USER, comment='None or multiple ip addresses for this ticket') TicketComment.objects.create(ticket=ticket, comment=comment) database.log_action_on_ticket( ticket=ticket, action='add_comment' ) ticket.save() return # Apply action service_action_job = _apply_timeout_action(ticket, ip_addr, action) if not service_action_job.result: Logger.debug(unicode('Error while executing service action, exiting')) return Logger.info(unicode('All done, sending close notification to provider(s)')) ticket = Ticket.objects.get(id=ticket.id) # Closing ticket close_ticket(ticket, reason=settings.CODENAMES['fixed'], service_blocked=True)
def _check_timeout_ticket_conformance(ticket): if not ticket.defendant or not ticket.service: Logger.error(unicode('Ticket %d is invalid (no defendant/service), skipping...' % (ticket.id))) return False if ticket.status.lower() in ['closed', 'answered']: Logger.error(unicode('Ticket %d is invalid (no defendant/service or not Alarm), Skipping...' % (ticket.id))) return False if ticket.category.name.lower() not in ['phishing', 'copyright']: Logger.error(unicode('Ticket %d is in wrong category (%s, Skipping...' % (ticket.id, ticket.category.name))) return False if ticket.treatedBy: Logger.error(unicode('Ticket is %d assigned, skipping' % (ticket.id))) return False if ticket.jobs.count(): Logger.error(unicode('Ticket %d has existing jobs, exiting ...' % (ticket.id))) return False return True
def apply_then_close(ticket_id=None, action_id=None, ip_addr=None, resolution_id=None, user_id=None): """ Action on service then close :param int ticket_id: The id of the Cerberus `Ticket` :param int action_id: The id of the Cerberus `ServiceAction` :param str ip_addr: The ip address :param int resolution_id: The id of the Cerberus `Resolution` :param int user_id: The id of the Cerberus `User` """ # Checking conformance if not all((ticket_id, action_id, resolution_id, user_id)): msg = 'Invalid parameters submitted [ticket_id=%d, action_id=%s, resolution_id=%s, user_id=%s]' Logger.error(unicode(msg % (ticket_id, action_id, resolution_id, user_id))) return # Apply action applied = apply_action(ticket_id, action_id, ip_addr, user_id) if not applied: return # Closing ticket and updating ticket info ticket = Ticket.objects.get(id=ticket_id) user = User.objects.get(id=user_id) __close_ticket(ticket, resolution_id) database.log_action_on_ticket( ticket=ticket, action='change_status', user=user, previous_value=ticket.previousStatus, new_value=ticket.status, close_reason=ticket.resolution.codename ) ticket.resolution_id = resolution_id ticket.save() Logger.info(unicode('Ticket %d processed. Next !' % (ticket_id)))
def check_mass_contact_result(result_campaign_id=None, jobs=None): """ Check "mass-contact" campaign jobs's result :param int result_campaign_id: The id of the `abuse.models.MassContactResult` :param list jobs: The list of associated Python-Rq jobs id """ # Check params _, _, _, values = inspect.getargvalues(inspect.currentframe()) if not all(values.values()) or not isinstance(jobs, list): Logger.error(unicode('invalid parameters submitted %s' % str(values))) return if not isinstance(result_campaign_id, MassContactResult): try: campaign_result = MassContactResult.objects.get(id=result_campaign_id) except (AttributeError, ObjectDoesNotExist, TypeError, ValueError): Logger.error(unicode('MassContactResult %d cannot be found in DB. Skipping...' % (result_campaign_id))) return result = [] for job_id in jobs: job = utils.default_queue.fetch_job(job_id) if not job: continue while job.status.lower() == 'queued': sleep(0.5) result.append(job.result) count = Counter(result) campaign_result.state = 'Done' campaign_result.matchingCount = count[True] campaign_result.notMatchingCount = count[False] campaign_result.failedCount = count[None] campaign_result.save() Logger.info(unicode('MassContact campaign %d finished' % (campaign_result.campaign.id)))
def apply_if_no_reply(ticket_id=None, action_id=None, ip_addr=None, resolution_id=None, user_id=None, close=False): """ Action if no reply from customer :param int ticket_id: The id of the Cerberus `Ticket` :param int action_id: The id of the Cerberus `ServiceAction` :param str ip_addr: The ip address :param int resolution_id: The id of the Cerberus `Resolution` :param int user_id: The id of the Cerberus `User` :param bool close: If the ticket has to be closed after action """ # Checking conformance if not all((ticket_id, action_id, user_id)): Logger.error(unicode( 'Invalid parameters [ticket_id=%s, action_id=%s, user_id=%s]' % (ticket_id, action_id, user_id) )) return if close and not resolution_id: Logger.error(unicode('Close requested but no resolution submitted')) return if resolution_id and not Resolution.objects.filter(id=resolution_id).exists(): Logger.error(unicode('Ticket resolution %d not found, Skipping...' % (resolution_id))) return # Apply action applied = apply_action(ticket_id, action_id, ip_addr, user_id) if not applied: return # Updating ticket info ticket = Ticket.objects.get(id=ticket_id) user = User.objects.get(id=user_id) ticket.previousStatus = ticket.status ticket.snoozeDuration = None ticket.snoozeStart = None close_reason = None if close and resolution_id: __close_ticket(ticket, resolution_id) close_reason = ticket.resolution.codename else: ticket.status = 'Alarm' ticket.save() database.log_action_on_ticket( ticket=ticket, action='change_status', user=user, previous_value=ticket.previousStatus, new_value=ticket.status, close_reason=close_reason ) Logger.info(unicode('Ticket %d processed. Next !' % (ticket_id)))
def block_url_and_mail(ticket_id=None, report_id=None): """ Block url with PhishingService and send mail to defendant :param int ticket_id: The id of the Cerberus `abuse.models.Ticket` :param int report_id: The id of the Cerberus `abuse.models.Report` """ if not isinstance(ticket_id, Ticket): try: ticket = Ticket.objects.get(id=ticket_id) if not ticket.defendant or not ticket.service: Logger.error(unicode('Ticket %d has no defendant/service' % (ticket_id))) return except (ObjectDoesNotExist, ValueError): Logger.error(unicode('Ticket %d cannot be found in DB. Skipping...' % (ticket_id))) return else: ticket = ticket_id if not isinstance(report_id, Report): try: report = Report.objects.get(id=report_id) except (ObjectDoesNotExist, ValueError): Logger.error(unicode('Report %d cannot be found in DB. Skipping...' % (report_id))) return else: report = report_id for item in report.reportItemRelatedReport.filter(itemType='URL'): ImplementationFactory.instance.get_singleton_of('PhishingServiceBase').block_url(item.rawItem, item.report) database.add_phishing_blocked_tag(report) __send_email(ticket, report.defendant.details.email, settings.CODENAMES['phishing_blocked'], report.defendant.details.lang) ticket = Ticket.objects.get(id=ticket.id) ticket_snooze = settings.GENERAL_CONFIG['phishing']['wait'] if not ticket.status == 'WaitingAnswer' and not ticket.snoozeDuration and not ticket.snoozeStart: ticket.previousStatus = ticket.status ticket.status = 'WaitingAnswer' ticket.snoozeDuration = ticket_snooze ticket.snoozeStart = datetime.now() ticket.save() Logger.info(unicode('Ticket %d now with status WaitingAnswer for %d' % (ticket.id, ticket_snooze)))
def check_if_all_down(report=None, last=5): """ Check if all urls items for a report (phishing for example) are 'down'. :param `abuse.models.Report` report: A Cerberus `abuse.models.Report` instance to ping :param int last: Look for the n last record in db :return: the result :rtype: bool """ if not isinstance(report, Report): try: report = Report.objects.get(id=report) except (AttributeError, ObjectDoesNotExist, TypeError, ValueError): Logger.error(unicode('Report %d cannot be found in DB. Skipping...' % (report))) return items = report.reportItemRelatedReport.all() items = list(set([item for item in items if item.itemType == 'URL'])) if not items: return False country = report.defendant.details.country if report.defendant else 'FR' for item in items: __update_item_status(item, country) items = report.reportItemRelatedReport.all() items = list(set([item for item in items if item.itemType == 'URL'])) scoring = {item.id: 0 for item in items} for item in items: status_score = database.get_item_status_score(item.id, last=last) for score in status_score: scoring[item.id] += score if all(v >= settings.GENERAL_CONFIG['phishing']['down_threshold'] for v in scoring.itervalues()): Logger.error(unicode('All urls are down for report %d' % (report.id))) return True Logger.error(unicode('Some url are still up for report %d' % (report.id))) return False
def archive_if_timeout(report_id=None): """ Archived report if not attached :param int report_id: The report id """ if not report_id: Logger.error(unicode('Invalid parameters submitted [report_id=%d]' % (report_id))) return try: report = Report.objects.get(id=report_id) except (ObjectDoesNotExist, ValueError): Logger.error(unicode('Report %d cannot be found in DB. Skipping...' % (report_id))) return if report.status != 'New': Logger.error(unicode('Report %d not New, status : %s , Skipping ...' % (report_id, report.status))) return report.ticket = None report.status = 'Archived' report.save() Logger.info(unicode('Report %d successfully archived' % (report_id)))
def create_ticket_from_phishtocheck(report=None, user=None): """ Create/attach report to ticket + block_url + mail to defendant + email to provider :param int report: The id of the `abuse.models.Report` :param int user: The id of the `abuse.models.User` """ if not isinstance(report, Report): try: report = Report.objects.get(id=report) except (AttributeError, ObjectDoesNotExist, TypeError, ValueError): Logger.error(unicode('Report %d cannot be found in DB. Skipping...' % (report))) return if not isinstance(user, User): try: user = User.objects.get(id=user) except (AttributeError, ObjectDoesNotExist, TypeError, ValueError): Logger.error(unicode('User %d cannot be found in DB. Skipping...' % (user))) return # Create/attach to ticket ticket = database.search_ticket(report.defendant, report.category, report.service) new_ticket = False if not ticket: ticket = database.create_ticket(report.defendant, report.category, report.service, priority=report.provider.priority) new_ticket = True utils.scheduler.enqueue_in( timedelta(seconds=settings.GENERAL_CONFIG['phishing']['wait']), 'ticket.timeout', ticket_id=ticket.id, timeout=3600, ) common.get_temp_proofs(ticket, only_urls=True) report.ticket = ticket report.status = 'Attached' report.save() database.log_action_on_ticket( ticket=ticket, action='attach_report', report=report, new_ticket=new_ticket ) database.log_action_on_ticket( ticket=ticket, action='validate_phishtocheck', user=user, report=report ) # Sending email to provider if settings.TAGS['no_autoack'] not in report.provider.tags.all().values_list('name', flat=True): common.send_email( ticket, [report.provider.email], settings.CODENAMES['ack_received'], acknowledged_report_id=report.id, ) utils.default_queue.enqueue('phishing.block_url_and_mail', ticket_id=ticket.id, report_id=report.id) return ticket
def create_from_email(email_content=None, filename=None, lang='EN', send_ack=False): """ Create Cerberus report(s) based on email content If send_ack is True and report is attached to a ticket, then an acknowledgement is sent to the email provider. :param str email_content: The raw email content :param str filename: The name of the raw email file :param str lang: Langage to use if send_ack is True :param bool send_ack: If an acknowledgment have to be sent to provider :raises CustomerDaoException: if exception while identifying defendants from items :raises MailerServiceException: if exception while updating ticket's emails :raises StorageServiceException: if exception while accessing storage """ # This function use a lock/commit_on_succes on db when creating reports # # Huge blocks of code are under transaction because it's important to # rollback if ANYTHING goes wrong in the report creation workflow. # # Concurrent transactions (with multiple workers), on defendant/service creation # can result in unconsistent data, So a pg_lock is used. # # `abuse.models.Defendant` and `abuse.models.Service` HAVE to be unique. if not email_content: Logger.error(unicode('Missing email content')) return if not filename: # Worker have to push email to Storage Service filename = hashlib.sha256(email_content).hexdigest() __save_email(filename, email_content) # Parse email content abuse_report = Parser.parse(email_content) Logger.debug(unicode('New email from %s' % (abuse_report.provider)), extra={'from': abuse_report.provider, 'action': 'new email'}) # Check if provider is not blacklisted if abuse_report.provider in settings.PARSING['providers_to_ignore']: Logger.error(unicode('Provider %s is blacklisted, skipping ...' % (abuse_report.provider))) return # Check if it's an answer to a ticket(s) tickets = ImplementationFactory.instance.get_singleton_of('MailerServiceBase').is_email_ticket_answer(abuse_report) if tickets: for ticket, category, recipient in tickets: if all((ticket, category, recipient)) and not ticket.locked: # OK it's an anwser, updating ticket and exiting _update_ticket_if_answer(ticket, category, recipient, abuse_report, filename) return # Check if items are linked to customer and get corresponding services try: services = ImplementationFactory.instance.get_singleton_of('CustomerDaoBase').get_services_from_items( urls=abuse_report.urls, ips=abuse_report.ips, fqdn=abuse_report.fqdn ) schema.valid_adapter_response('CustomerDaoBase', 'get_services_from_items', services) except CustomerDaoException as ex: Logger.error(unicode('Exception while identifying defendants from items for mail %s -> %s ' % (filename, str(ex)))) raise CustomerDaoException(ex) # Create report(s) with identified services if not services: created_reports = [__create_without_services(abuse_report, filename)] else: with pglocks.advisory_lock('cerberus_lock'): __create_defendants_and_services(services) created_reports = __create_with_services(abuse_report, filename, services) # Upload attachments if abuse_report.attachments: _save_attachments(filename, abuse_report.attachments, reports=created_reports) # Send acknowledgement to provider (only if send_ack = True and report is attached to a ticket) for report in created_reports: if send_ack and report.ticket: try: __send_ack(report, lang=lang) except MailerServiceException as ex: raise MailerServiceException(ex) # Index to SearchService if ImplementationFactory.instance.is_implemented('SearchServiceBase'): __index_report_to_searchservice(abuse_report, filename, [rep.id for rep in created_reports]) Logger.info(unicode('All done successfully for email %s' % (filename)))
def apply_action(ticket_id=None, action_id=None, ip_addr=None, user_id=None): """ Apply given action on customer service :param int ticket_id: The id of the Cerberus `Ticket` :param int action_id: The id of the Cerberus `ServiceAction` :param int user_id: The id of the Cerberus `User` :rtype: bool :returns: if action has been applied """ current_job = get_current_job() # Checking conformance if not all((ticket_id, action_id, user_id)): msg = 'Invalid parameters submitted [ticket_id=%d, action_id=%s, user_id=%s]' Logger.error(unicode(msg % (ticket_id, action_id, user_id))) return False # Fetching Django model object Logger.info(unicode('Starting process ticket %d with params [%d]' % (ticket_id, action_id))) try: ticket = Ticket.objects.get(id=ticket_id) user = User.objects.get(id=user_id) except (ObjectDoesNotExist, ValueError): Logger.error(unicode('Ticket %d or user %d cannot be found in DB. Skipping...' % (ticket_id, user_id))) return False if ticket.status in ['Closed', 'Answered']: __cancel_by_status(ticket) ticket.previousStatus = ticket.status ticket.status = 'ActionError' ticket.save() database.log_action_on_ticket( ticket=ticket, action='change_status', user=user, previous_value=ticket.previousStatus, new_value=ticket.status, ) return False # Call action service try: result = ImplementationFactory.instance.get_singleton_of( 'ActionServiceBase' ).apply_action_on_service( ticket_id, action_id, ip_addr, user.id ) _update_job(current_job.id, todo_id=result.todo_id, status=result.status, comment=result.comment) return True except ActionServiceException as ex: Logger.info(unicode('Service Action not apply for ticket %d' % (ticket_id))) _update_job(current_job.id, status='actionError', comment=str(ex)) ticket.previousStatus = ticket.status ticket.status = 'ActionError' ticket.save() database.log_action_on_ticket( ticket=ticket, action='change_status', user=user, previous_value=ticket.previousStatus, new_value=ticket.status, ) return False