aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilo Casagrande <milo.casagrande@linaro.org>2015-02-06 14:56:25 +0100
committerMilo Casagrande <milo.casagrande@linaro.org>2015-02-06 14:56:25 +0100
commita5036cf9dcc2a66780cd3d7199677a98e61473ae (patch)
tree9ea512b224dcf247f1ba2f628af41f0294ea5338
parent31f836c85523495dc8b4b60e2998e63e4c769bb8 (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.py269
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