You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

78 lines
2.5 KiB

  1. import logging
  2. import time
  3. from concurrent.futures import ThreadPoolExecutor
  4. from django.core.management.base import BaseCommand
  5. from django.db import connection
  6. from django.utils import timezone
  7. from hc.api.models import Check
  8. executor = ThreadPoolExecutor(max_workers=10)
  9. logger = logging.getLogger(__name__)
  10. class Command(BaseCommand):
  11. help = 'Sends UP/DOWN email alerts'
  12. def handle_many(self):
  13. """ Send alerts for many checks simultaneously. """
  14. query = Check.objects.filter(user__isnull=False)
  15. now = timezone.now()
  16. going_down = query.filter(alert_after__lt=now, status="up")
  17. going_up = query.filter(alert_after__gt=now, status="down")
  18. # Don't combine this in one query so Postgres can query using index:
  19. checks = list(going_down.iterator()) + list(going_up.iterator())
  20. if not checks:
  21. return False
  22. futures = [executor.submit(self.handle_one, check) for check in checks]
  23. for future in futures:
  24. future.result()
  25. return True
  26. def handle_one(self, check):
  27. """ Send an alert for a single check.
  28. Return True if an appropriate check was selected and processed.
  29. Return False if no checks need to be processed.
  30. """
  31. check.status = check.get_status()
  32. tmpl = "\nSending alert, status=%s, code=%s\n"
  33. self.stdout.write(tmpl % (check.status, check.code))
  34. try:
  35. check.send_alert()
  36. except:
  37. # Catch EVERYTHING. If we crash here, what can happen is:
  38. # - the sendalerts command will crash
  39. # - supervisor will respawn sendalerts command
  40. # - sendalerts will try same thing again, resulting in
  41. # infinite loop
  42. # So instead we catch and log all exceptions, and mark
  43. # the checks as paused so they are not retried.
  44. logger.error("Could not alert %s" % check.code, exc_info=True)
  45. check.status = "paused"
  46. finally:
  47. check.save()
  48. connection.close()
  49. return True
  50. def handle(self, *args, **options):
  51. self.stdout.write("sendalerts starts up")
  52. ticks = 0
  53. while True:
  54. if self.handle_many():
  55. ticks = 0
  56. else:
  57. ticks += 1
  58. time.sleep(1)
  59. if ticks % 60 == 0:
  60. formatted = timezone.now().isoformat()
  61. self.stdout.write("-- MARK %s --" % formatted)