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.

101 lines
3.0 KiB

  1. import time
  2. from threading import Thread
  3. from django.core.management.base import BaseCommand
  4. from django.utils import timezone
  5. from hc.api.models import Check
  6. def notify(check_id, stdout):
  7. check = Check.objects.get(id=check_id)
  8. tmpl = "Sending alert, status=%s, code=%s\n"
  9. stdout.write(tmpl % (check.status, check.code))
  10. errors = check.send_alert()
  11. for ch, error in errors:
  12. stdout.write("ERROR: %s %s %s\n" % (ch.kind, ch.value, error))
  13. def notify_on_thread(check_id, stdout):
  14. t = Thread(target=notify, args=(check_id, stdout))
  15. t.start()
  16. class Command(BaseCommand):
  17. help = 'Sends UP/DOWN email alerts'
  18. owned = Check.objects.filter(user__isnull=False)
  19. def add_arguments(self, parser):
  20. parser.add_argument(
  21. '--no-loop',
  22. action='store_false',
  23. dest='loop',
  24. default=True,
  25. help='Do not keep running indefinitely in a 2 second wait loop',
  26. )
  27. parser.add_argument(
  28. '--no-threads',
  29. action='store_false',
  30. dest='use_threads',
  31. default=False,
  32. help='Send alerts synchronously, without using threads',
  33. )
  34. def handle_one(self, use_threads=True):
  35. """ Process a single check. """
  36. now = timezone.now()
  37. # Look for checks that are going down
  38. q = self.owned.filter(alert_after__lt=now, status="up")
  39. check = q.first()
  40. # If none found, look for checks that are going up
  41. if not check:
  42. q = self.owned.filter(alert_after__gt=now, status="down")
  43. check = q.first()
  44. if check is None:
  45. return False
  46. q = Check.objects.filter(id=check.id, status=check.status)
  47. current_status = check.get_status()
  48. if check.status == current_status:
  49. # Stored status is already up-to-date. Update alert_after
  50. # as needed but don't send notifications
  51. q.update(alert_after=check.get_alert_after())
  52. return True
  53. else:
  54. # Atomically update status to the opposite
  55. num_updated = q.update(status=current_status)
  56. if num_updated == 1:
  57. # Send notifications only if status update succeeded
  58. # (no other sendalerts process got there first)
  59. if use_threads:
  60. notify_on_thread(check.id, self.stdout)
  61. else:
  62. notify(check.id, self.stdout)
  63. return True
  64. return False
  65. def handle(self, use_threads=True, loop=True, *args, **options):
  66. self.stdout.write("sendalerts is now running\n")
  67. i, sent = 0, 0
  68. while True:
  69. while self.handle_one(use_threads):
  70. sent += 1
  71. if not loop:
  72. break
  73. time.sleep(2)
  74. i += 1
  75. if i % 60 == 0:
  76. timestamp = timezone.now().isoformat()
  77. self.stdout.write("-- MARK %s --\n" % timestamp)
  78. return "Sent %d alert(s)" % sent