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.

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