diff options
author | Milo Casagrande <milo.casagrande@linaro.org> | 2015-02-06 14:56:25 +0100 |
---|---|---|
committer | Milo Casagrande <milo.casagrande@linaro.org> | 2015-02-06 14:56:25 +0100 |
commit | a5036cf9dcc2a66780cd3d7199677a98e61473ae (patch) | |
tree | 9ea512b224dcf247f1ba2f628af41f0294ea5338 | |
parent | 31f836c85523495dc8b4b60e2998e63e4c769bb8 (diff) |
Complete the send handler.
* Add support for the build report to be sent.
* Refactor the send handler.
* Add maximum delay of 3 hours.
Change-Id: Icaf2664a0e7fa1e0a025bdd8d45acc13d7fa805b
-rw-r--r-- | app/handlers/send.py | 269 |
1 files changed, 251 insertions, 18 deletions
diff --git a/app/handlers/send.py b/app/handlers/send.py index d7cf4b2..7f308a6 100644 --- a/app/handlers/send.py +++ b/app/handlers/send.py @@ -15,6 +15,7 @@ import bson import datetime +import types import handlers.base as hbase import handlers.common as hcommon @@ -22,6 +23,9 @@ import handlers.response as hresponse import models import taskqueue.tasks as taskq +# Max delay in sending email report set to 3hrs. +MAX_DELAY = 10800 + # pylint: disable=too-many-public-methods class SendHandler(hbase.BaseHandler): @@ -34,46 +38,177 @@ class SendHandler(hbase.BaseHandler): def _valid_keys(method): return hcommon.SEND_VALID_KEYS.get(method, None) + # pylint: disable=too-many-locals def _post(self, *args, **kwargs): response = hresponse.HandlerResponse(202) json_obj = kwargs["json_obj"] - db_options = kwargs["db_options"] - mail_options = self.settings["mailoptions"] + + j_get = json_obj.get + job = j_get(models.JOB_KEY) + kernel = j_get(models.KERNEL_KEY) + lab_name = j_get(models.LAB_NAME_KEY, None) self.log.info( "Email trigger received from IP '%s' for '%s-%s' at %s", self.request.remote_ip, - json_obj.get("job", None), - json_obj.get("kernel", None), + job, + kernel, datetime.datetime.utcnow() ) - countdown = json_obj.get(models.DELAY_KEY, self.settings["senddelay"]) + countdown = j_get(models.DELAY_KEY, self.settings["senddelay"]) if countdown is None: countdown = self.settings["senddelay"] try: - countdown = abs(int(countdown)) - when = ( - datetime.datetime.now(tz=bson.tz_util.utc) + - datetime.timedelta(seconds=countdown) - ) - response.reason = ( - "Email report scheduled to be sent at '%s' UTC" % - when.isoformat() - ) + send_boot = bool(j_get(models.SEND_BOOT_REPORT_KEY, False)) + send_build = bool(j_get(models.SEND_BUILD_REPORT_KEY, False)) + + boot_errors = False + build_errors = False + + if any([send_boot, send_build]): + countdown = int(countdown) + if countdown < 0: + countdown = abs(countdown) + response.errrors = ( + "Negative value specified for the '%s' key, " + "its positive value will be used instead (%ds)" % + (models.DELAY_KEY, countdown) + ) + + if countdown > MAX_DELAY: + response.errors = ( + "Delay value specified out of range (%ds), " + "maximum delay permitted (%ds) will be used instead" % + (countdown, MAX_DELAY) + ) + countdown = MAX_DELAY + + when = ( + datetime.datetime.now(tz=bson.tz_util.utc) + + datetime.timedelta(seconds=countdown) + ) + + schedule_data = { + "countdown": countdown, + "boot_emails": j_get(models.BOOT_REPORT_SEND_TO_KEY, None), + "build_emails": j_get( + models.BUILD_REPORT_SEND_TO_KEY, None), + "generic_emails": j_get(models.REPORT_SEND_TO_KEY, None), + "db_options": kwargs["db_options"], + "mail_options": self.settings["mailoptions"] + } + + if send_boot: + boot_errors, response.errors = self._schedule_boot_report( + job, kernel, lab_name, schedule_data) + + if send_build: + build_errors, response.errors = \ + self._schedule_build_report(job, kernel, schedule_data) - taskq.schedule_boot_report.apply_async( - [json_obj, db_options, mail_options, countdown]) + response.reason, response.status_code = _check_status( + send_boot, send_build, boot_errors, build_errors, when + ) + else: + response.status_code = 400 + response.reason = ( + "Don't know which report to send: either specify " + " '%s' or '%s'." % + (models.SEND_BOOT_REPORT_KEY, models.SEND_BUILD_REPORT_KEY) + ) except (TypeError, ValueError): response.status_code = 400 response.reason = ( - "Wrong value specified for 'delay': %s" % countdown - ) + "Wrong value specified for 'delay': %s" % countdown) return response + def _schedule_boot_report(self, job, kernel, lab_name, schedule_data): + """Schedule the boot report performing some checks on the emails. + + :param job: The name of the job. + :type job: string + :param kernel: The name of the kernel. + :type kernel: string + :param lab_name: The name of the lab. + :type lab_name: string + :param schedule_data: The data necessary for scheduling a report. + :type schedule_data: dictionary + :return A tuple with as first parameter a bool indicating if the + scheduling had success, as second argument the error string in case + of error or None. + """ + has_errors = False + error_string = None + + to_addrs = _get_email_addresses( + schedule_data["boot_emails"], schedule_data["generic_emails"]) + + if to_addrs: + taskq.send_boot_report.apply_async( + [ + job, + kernel, + lab_name, + to_addrs, + schedule_data["db_options"], + schedule_data["mail_options"] + ], + countdown=schedule_data["countdown"] + ) + else: + has_errors = True + error_string = "No email address provided to send boot report" + self.log.error( + "No email addresses to send boot report for '%s-%s'", + job, kernel + ) + + return has_errors, error_string + + def _schedule_build_report(self, job, kernel, schedule_data): + """Schedule the build report performing some checks on the emails. + + :param job: The name of the job. + :type job: string + :param kernel: The name of the kernel. + :type kernel: string + :param schedule_data: The data necessary for scheduling a report. + :type schedule_data: dictionary + :return A tuple with as first parameter a bool indicating if the + scheduling had success, as second argument the error string in case + of error or None. + """ + has_errors = False + error_string = None + + to_addrs = _get_email_addresses( + schedule_data["build_emails"], schedule_data["generic_emails"]) + + if to_addrs: + taskq.send_build_report.apply_async( + [ + job, + kernel, + to_addrs, + schedule_data["db_options"], + schedule_data["mail_options"] + ], + countdown=schedule_data["countdown"] + ) + else: + has_errors = True + error_string = "No email address provided to send build report" + self.log.error( + "No email addresses to send build report for '%s-%s'", + job, kernel + ) + + return has_errors, error_string + def execute_delete(self, *args, **kwargs): """Perform DELETE pre-operations. @@ -83,6 +218,7 @@ class SendHandler(hbase.BaseHandler): if self.validate_req_token("DELETE"): response = hresponse.HandlerResponse(501) + response.reason = hcommon.METHOD_NOT_IMPLEMENTED else: response = hresponse.HandlerResponse(403) response.reason = hcommon.NOT_VALID_TOKEN @@ -98,8 +234,105 @@ class SendHandler(hbase.BaseHandler): if self.validate_req_token("GET"): response = hresponse.HandlerResponse(501) + response.reason = hcommon.METHOD_NOT_IMPLEMENTED else: response = hresponse.HandlerResponse(403) response.reason = hcommon.NOT_VALID_TOKEN return response + + +def _check_status(send_boot, send_build, boot_errors, build_errors, when): + """Check the status of the boot/build report schedule. + + :param send_boot: If a boot report should have been sent. + :type send_boot: bool + :param send_build: If a build report should have been sent. + :type send_build: bool + :param boot_errors: If there have been errors in scheduling the boot + report. + :type boot_errors: bool + :param build_errors: If there have been errors in scheduling the build + report. + :type build_errors: bool + :param when: A datetime object when the report should have been scheduled. + :type when: datetime.datetime + :return A tuple with the reason message and the status code. + """ + status_code = 202 + reason = None + + if all([send_boot, send_build, boot_errors, build_errors]): + reason = "No email reports scheduled to be sent" + status_code = 400 + elif all([ + send_boot, send_build, boot_errors, not build_errors]): + reason = ( + "Build email report scheduled to be sent at '%s' UTC" % + when.isoformat() + ) + elif all([ + send_boot, send_build, not boot_errors, build_errors]): + reason = ( + "Boot email report scheduled to be sent at '%s' UTC" % + when.isoformat() + ) + elif all([ + send_boot, + send_build, not boot_errors, not build_errors]): + reason = ( + "Email reports scheduled to be sent at '%s' UTC" % + when.isoformat() + ) + elif all([ + not send_boot, + send_build, not boot_errors, build_errors]): + reason = ( + "Build email report not scheduled to be sent") + status_code = 400 + elif all([ + not send_boot, + send_build, not boot_errors, not build_errors]): + reason = ( + "Build email report scheduled to be send at '%s' UTC" % + when) + elif all([ + send_boot, + not send_build, boot_errors, not build_errors]): + reason = ( + "Boot email report not scheduled to be sent") + status_code = 400 + elif all([ + send_boot, + not send_build, not boot_errors, not build_errors]): + reason = ( + "Boot email report scheduled to be sent at '%s' UTC" % + when) + + return reason, status_code + + +def _get_email_addresses(report_emails, generic_emails): + """Return a list of email address from the ones provided. + + :param report_emails: The emails to analyze. + :type report_emails: string or list + :param generic_emails: The generic emails to analyze. + :type generic_emails: string or list + :return A list of email addresses or an empty list. + """ + to_addrs = [] + + if report_emails: + if isinstance(report_emails, types.ListType): + to_addrs.extend(report_emails) + elif isinstance(report_emails, types.StringTypes): + to_addrs.append(report_emails) + + if generic_emails: + if isinstance(generic_emails, types.ListType): + to_addrs.extend(generic_emails) + elif isinstance(generic_emails, types.StringTypes): + to_addrs.append(generic_emails) + + return to_addrs |