mail.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.utils.mail
  4. ~~~~~~~~~~~~~~~~~
  5. How task error emails are formatted and sent.
  6. :copyright: (c) 2009 - 2012 by Ask Solem.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. from __future__ import absolute_import
  10. import sys
  11. import smtplib
  12. try:
  13. from email.mime.text import MIMEText
  14. except ImportError:
  15. from email.MIMEText import MIMEText # noqa
  16. from .imports import symbol_by_name
  17. supports_timeout = sys.version_info >= (2, 6)
  18. class SendmailWarning(UserWarning):
  19. """Problem happened while sending the email message."""
  20. class Message(object):
  21. def __init__(self, to=None, sender=None, subject=None, body=None,
  22. charset="us-ascii"):
  23. self.to = to
  24. self.sender = sender
  25. self.subject = subject
  26. self.body = body
  27. self.charset = charset
  28. if not isinstance(self.to, (list, tuple)):
  29. self.to = [self.to]
  30. def __repr__(self):
  31. return "<Email: To:%r Subject:%r>" % (self.to, self.subject)
  32. def __str__(self):
  33. msg = MIMEText(self.body, "plain", self.charset)
  34. msg["Subject"] = self.subject
  35. msg["From"] = self.sender
  36. msg["To"] = ", ".join(self.to)
  37. return msg.as_string()
  38. class Mailer(object):
  39. def __init__(self, host="localhost", port=0, user=None, password=None,
  40. timeout=2, use_ssl=False, use_tls=False):
  41. self.host = host
  42. self.port = port
  43. self.user = user
  44. self.password = password
  45. self.timeout = timeout
  46. self.use_ssl = use_ssl
  47. self.use_tls = use_tls
  48. def send(self, message):
  49. if supports_timeout:
  50. self._send(message, timeout=self.timeout)
  51. else:
  52. import socket
  53. old_timeout = socket.getdefaulttimeout()
  54. socket.setdefaulttimeout(self.timeout)
  55. try:
  56. self._send(message)
  57. finally:
  58. socket.setdefaulttimeout(old_timeout)
  59. def _send(self, message, **kwargs):
  60. if (self.use_ssl):
  61. client = smtplib.SMTP_SSL(self.host, self.port, **kwargs)
  62. else:
  63. client = smtplib.SMTP(self.host, self.port, **kwargs)
  64. if self.use_tls:
  65. client.ehlo()
  66. client.starttls()
  67. client.ehlo()
  68. if self.user and self.password:
  69. client.login(self.user, self.password)
  70. client.sendmail(message.sender, message.to, str(message))
  71. client.quit()
  72. class ErrorMail(object):
  73. """Defines how and when task error e-mails should be sent.
  74. :param task: The task instance that raised the error.
  75. :attr:`subject` and :attr:`body` are format strings which
  76. are passed a context containing the following keys:
  77. * name
  78. Name of the task.
  79. * id
  80. UUID of the task.
  81. * exc
  82. String representation of the exception.
  83. * args
  84. Positional arguments.
  85. * kwargs
  86. Keyword arguments.
  87. * traceback
  88. String representation of the traceback.
  89. * hostname
  90. Worker hostname.
  91. """
  92. # pep8.py borks on a inline signature separator and
  93. # says "trailing whitespace" ;)
  94. EMAIL_SIGNATURE_SEP = "-- "
  95. #: Format string used to generate error email subjects.
  96. subject = """\
  97. [celery@%(hostname)s] Error: Task %(name)s (%(id)s): %(exc)s
  98. """
  99. #: Format string used to generate error email content.
  100. body = """
  101. Task %%(name)s with id %%(id)s raised exception:\n%%(exc)r
  102. Task was called with args: %%(args)s kwargs: %%(kwargs)s.
  103. The contents of the full traceback was:
  104. %%(traceback)s
  105. %(EMAIL_SIGNATURE_SEP)s
  106. Just to let you know,
  107. celeryd at %%(hostname)s.
  108. """ % {"EMAIL_SIGNATURE_SEP": EMAIL_SIGNATURE_SEP}
  109. error_whitelist = None
  110. def __init__(self, task, **kwargs):
  111. self.task = task
  112. self.email_subject = kwargs.get("subject", self.subject)
  113. self.email_body = kwargs.get("body", self.body)
  114. self.error_whitelist = getattr(task, "error_whitelist")
  115. def should_send(self, context, exc):
  116. """Returns true or false depending on if a task error mail
  117. should be sent for this type of error."""
  118. allow_classes = tuple(map(symbol_by_name, self.error_whitelist))
  119. return not self.error_whitelist or isinstance(exc, allow_classes)
  120. def format_subject(self, context):
  121. return self.subject.strip() % context
  122. def format_body(self, context):
  123. return self.body.strip() % context
  124. def send(self, context, exc, fail_silently=True):
  125. if self.should_send(context, exc):
  126. self.task.app.mail_admins(self.format_subject(context),
  127. self.format_body(context),
  128. fail_silently=fail_silently)