Message notification and sending

This commit is contained in:
Eduardo Silva 2024-04-16 16:07:48 -03:00
parent dfb4285ab3
commit 034bc7057b
9 changed files with 182 additions and 3 deletions

View file

@ -7,6 +7,7 @@ from django.utils import timezone
from backup.models import BackupProfile from backup.models import BackupProfile
from backup_data.models import RouterBackup from backup_data.models import RouterBackup
from message_center.functions import notify_backup_fail
from router_manager.models import Router, BackupSchedule, RouterStatus from router_manager.models import Router, BackupSchedule, RouterStatus
from routerlib.backup_functions import perform_backup from routerlib.backup_functions import perform_backup
@ -249,6 +250,7 @@ def view_housekeeping(requests):
backup.save() backup.save()
backup.router.routerstatus.last_backup_failed = timezone.now() backup.router.routerstatus.last_backup_failed = timezone.now()
backup.router.routerstatus.save() backup.router.routerstatus.save()
notify_backup_fail(backup)
if not RouterBackup.objects.filter(router=backup.router, success=False, error=False).exists(): if not RouterBackup.objects.filter(router=backup.router, success=False, error=False).exists():
backup.router.routerstatus.backup_lock = None backup.router.routerstatus.backup_lock = None

105
message_center/functions.py Normal file
View file

@ -0,0 +1,105 @@
import datetime
from router_manager.models import Router
from .models import MessageChannel, Notification, MessageSettings, Message
from backup_data.models import RouterBackup
import requests
from django.utils import timezone
def send_notification_message(message: Message):
message_settings, _ = MessageSettings.objects.get_or_create(name='message_settings')
if message.status != 'pending':
return
if message.retry_count > message_settings.max_retry:
message.status = 'failed'
message.completed = timezone.now()
message.save()
return
message_response = {'status': 'pending', 'error_message': '', 'error_status_code': 0}
if message.channel.channel_type == 'callmebot':
url = f'https://api.callmebot.com/whatsapp.php?phone={message.channel.destination}&text={message.message}&apikey={message.channel.token}'
elif message.channel.channel_type == 'telegram':
url = f'https://api.telegram.org/bot{message.channel.token}/sendMessage?chat_id={message.channel.destination}&text={message.message}'
else:
message_response['status'] = 'failed'
message_response['error_message'] = 'Failed to send message: Invalid channel type'
message_response['error_status_code'] = 0
if message_response['status'] == 'pending':
try:
response = requests.get(url)
if response.status_code == 200:
message_response['status'] = 'sent'
else:
message_response['status'] = 'failed'
message_response['error_message'] = response.text
message_response['error_status_code'] = response.status_code
except:
message_response['status'] = 'failed'
message_response['error_message'] = 'Failed to send message: Request exception'
message_response['error_status_code'] = 0
if message_response['status'] == 'sent':
message.status = 'sent'
message.completed = timezone.now()
message.save()
else:
message.retry_count += 1
message.error_message = message_response['error_message']
message.error_status_code = message_response['error_status_code']
message.next_retry = timezone.now() + datetime.timedelta(seconds=message_settings.retry_interval)
message.save()
return
def notify_router_status_update(router: Router):
message_channel_list = MessageChannel.objects.filter(enabled=True)
message_settings, _ = MessageSettings.objects.get_or_create(name='message_settings')
if message_settings.concatenate_status_change:
if router.routerstatus.status_online:
if message_channel_list.filter(status_change_online=True):
Notification.objects.create(notification_type='status_online', router=router)
else:
if message_channel_list.filter(status_change_offline=True):
Notification.objects.create(notification_type='status_offline', router=router)
else:
if router.routerstatus.status_online:
for message_channel in message_channel_list.filter(status_change_online=True):
Message.objects.create(
channel=message_channel,
subject='Router status change: Online',
message=f'Router {router.name} ({router.address}) is now online'
)
else:
for message_channel in message_channel_list.filter(status_change_offline=True):
Message.objects.create(
channel=message_channel,
subject='Router status change: Offline',
message=f'Router {router.name} ({router.address}) is now offline'
)
return
def notify_backup_fail(router_backup: RouterBackup):
message_settings, _ = MessageSettings.objects.get_or_create(name='message_settings')
message_channel_list = MessageChannel.objects.filter(enabled=True, backup_fail=True)
if not message_channel_list:
return
if message_settings.concatenate_backup_fails:
Notification.objects.create(notification_type='backup_fail', router=router_backup.router, router_backup=router_backup)
else:
error_message = f'Backup {router_backup.id} failed for router {router_backup.router.name} ({router_backup.router.address})'
if router_backup.error_message:
error_message += f'\n\nError message: {router_backup.error_message}'
for message_channel in message_channel_list:
Message.objects.create(
channel=message_channel,
subject=f'Backup failed: {router_backup.id}',
message=error_message
)
return

View file

@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-04-16 18:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('message_center', '0002_alter_messagechannel_channel_type'),
]
operations = [
migrations.AddField(
model_name='message',
name='error_status_code',
field=models.IntegerField(blank=True, null=True),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-04-16 18:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('message_center', '0003_message_error_status_code'),
]
operations = [
migrations.AddField(
model_name='message',
name='next_retry',
field=models.DateTimeField(blank=True, null=True),
),
]

View file

@ -35,6 +35,9 @@ class MessageChannel(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False) uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
def __str__(self):
return self.name + ' (' + self.channel_type + ')'
class Message(models.Model): class Message(models.Model):
channel = models.ForeignKey(MessageChannel, on_delete=models.CASCADE) channel = models.ForeignKey(MessageChannel, on_delete=models.CASCADE)
@ -44,7 +47,9 @@ class Message(models.Model):
('pending', 'Pending'), ('sent', 'Sent'), ('failed', 'Failed'), ('pending', 'Pending'), ('sent', 'Sent'), ('failed', 'Failed'),
), default='pending') ), default='pending')
retry_count = models.IntegerField(default=0) retry_count = models.IntegerField(default=0)
next_retry = models.DateTimeField(blank=True, null=True)
error_message = models.TextField(blank=True, null=True) error_message = models.TextField(blank=True, null=True)
error_status_code = models.IntegerField(blank=True, null=True)
completed = models.DateTimeField(blank=True, null=True) completed = models.DateTimeField(blank=True, null=True)
updated = models.DateTimeField(auto_now=True) updated = models.DateTimeField(auto_now=True)

View file

@ -1,9 +1,34 @@
from django.shortcuts import render, redirect from django.http import JsonResponse, Http404
from django.shortcuts import render, redirect, Http404, get_object_or_404
from django.contrib import messages from django.contrib import messages
from router_manager.models import Router
from user_manager.models import UserAcl from user_manager.models import UserAcl
from .forms import MessageSettingsForm, MessageChannelForm from .forms import MessageSettingsForm, MessageChannelForm
from .models import Notification, MessageChannel, Message, MessageSettings from .models import Notification, MessageChannel, Message, MessageSettings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.conf import settings
from .functions import notify_router_status_update, notify_backup_fail, send_notification_message
from backup_data.models import RouterBackup
def view_debug_test_messages(request):
if not settings.DEBUG:
raise Http404
data = {'status': 'success'}
router = None
router_backup = None
if request.GET.get('router_uuid'):
router = get_object_or_404(Router, uuid=request.GET.get('router_uuid'))
print(f'Creating test message for router {router.name}')
notify_router_status_update(router)
elif request.GET.get('backup_id'):
router_backup = get_object_or_404(RouterBackup, id=request.GET.get('backup_id'))
notify_backup_fail(router_backup)
else:
for message in Message.objects.filter(status='pending'):
send_notification_message(message)
return JsonResponse(data)
@login_required() @login_required()

View file

@ -1,5 +1,6 @@
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from message_center.functions import notify_router_status_update
from monitoring.models import RouterDownTime from monitoring.models import RouterDownTime
from router_manager.models import Router, RouterStatus, RouterGroup from router_manager.models import Router, RouterStatus, RouterGroup
from django.http import JsonResponse from django.http import JsonResponse
@ -78,7 +79,6 @@ def view_update_router_status(request):
router.routerstatus.save() router.routerstatus.save()
if current_status != new_status: if current_status != new_status:
if new_status == 'online': if new_status == 'online':
router.routerstatus.status_online = True router.routerstatus.status_online = True
downtime = RouterDownTime.objects.filter(router=router, end_time=None).first() downtime = RouterDownTime.objects.filter(router=router, end_time=None).first()
@ -92,6 +92,7 @@ def view_update_router_status(request):
router.routerstatus.save() router.routerstatus.save()
if downtime: if downtime:
RouterDownTime.objects.filter(router=router, end_time=None).exclude(uuid=downtime.uuid).delete() RouterDownTime.objects.filter(router=router, end_time=None).exclude(uuid=downtime.uuid).delete()
notify_router_status_update(router)
webadmin_settings, _ = WebadminSettings.objects.get_or_create(name='webadmin_settings') webadmin_settings, _ = WebadminSettings.objects.get_or_create(name='webadmin_settings')
webadmin_settings.monitoring_last_run = timezone.now() webadmin_settings.monitoring_last_run = timezone.now()

View file

@ -9,12 +9,13 @@ from backup.views import view_backup_profile_list, view_manage_backup_profile, v
from monitoring.views import view_export_router_list, view_update_router_status, view_router_config_timestamp, view_router_last_status_change from monitoring.views import view_export_router_list, view_update_router_status, view_router_config_timestamp, view_router_last_status_change
from backup_data.views import view_generate_backup_schedule, view_create_backup_tasks, view_perform_backup_tasks, view_housekeeping from backup_data.views import view_generate_backup_schedule, view_create_backup_tasks, view_perform_backup_tasks, view_housekeeping
from routerfleet_tools.views import cron_check_updates from routerfleet_tools.views import cron_check_updates
from message_center.views import view_message_channel_list, view_manage_message_settings, view_manage_message_channel from message_center.views import view_message_channel_list, view_manage_message_settings, view_manage_message_channel, view_debug_test_messages
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('debug/run_backups/', view_debug_run_backups, name='debug_run_backups'), path('debug/run_backups/', view_debug_run_backups, name='debug_run_backups'),
path('debug/test_messages/', view_debug_test_messages, name='debug_test_messages'),
path('', view_dashboard, name='dashboard'), path('', view_dashboard, name='dashboard'),
path('status/', view_status, name='status'), path('status/', view_status, name='status'),
path('user/list/', view_user_list, name='user_list'), path('user/list/', view_user_list, name='user_list'),

View file

@ -4,6 +4,8 @@ from backup_data.models import RouterBackup
import os import os
from scp import SCPClient from scp import SCPClient
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from message_center.functions import notify_backup_fail
from routerlib.functions import gen_backup_name, connect_to_ssh, get_router_backup_file_extension from routerlib.functions import gen_backup_name, connect_to_ssh, get_router_backup_file_extension
@ -18,12 +20,14 @@ def perform_backup(router_backup: RouterBackup):
router_backup.save() router_backup.save()
router_backup.router.routerstatus.backup_lock = None router_backup.router.routerstatus.backup_lock = None
router_backup.router.routerstatus.save() router_backup.router.routerstatus.save()
notify_backup_fail(router_backup)
return return
if router_backup.retry_count > router_backup.router.backup_profile.max_retry: if router_backup.retry_count > router_backup.router.backup_profile.max_retry:
router_backup.error = True router_backup.error = True
router_backup.save() router_backup.save()
router_backup.router.routerstatus.backup_lock = None router_backup.router.routerstatus.backup_lock = None
router_backup.router.routerstatus.save() router_backup.router.routerstatus.save()
notify_backup_fail(router_backup)
return return
if router_backup.backup_pending_retrieval: if router_backup.backup_pending_retrieval: