mail.py 4.5 KB

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