mirror of
https://github.com/eduardogsilva/routerfleet.git
synced 2025-06-21 01:25:41 +02:00
Merge pull request #61 from petrunetworking/main
update status.html page update view.py dashboard and urls.py
This commit is contained in:
commit
3fd2d9fadf
5 changed files with 564 additions and 147 deletions
|
@ -1,9 +1,10 @@
|
|||
from django.http import HttpResponseBadRequest, JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from backup.models import BackupProfile
|
||||
from backup_data.models import RouterBackup
|
||||
from router_manager.models import Router, RouterGroup, RouterStatus, BackupSchedule, SSHKey
|
||||
from integration_manager.models import ExternalIntegration
|
||||
from user_manager.models import User
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
@ -11,23 +12,55 @@ from datetime import timedelta
|
|||
|
||||
import os
|
||||
import shutil
|
||||
ALLOWED_DAYS = [3,5,7,10, 15, 30] # Define allowed values
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from django.conf import settings
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from django.conf import settings
|
||||
|
||||
def human_readable_size(size):
|
||||
"""Convert bytes to a human-readable format (KB, MB, GB)."""
|
||||
for unit in ['B', 'KB', 'MB', 'GB']:
|
||||
if size < 1024.0:
|
||||
return f"{size:.2f} {unit}"
|
||||
size /= 1024.0
|
||||
return f"{size:.2f} TB" # For sizes larger than GB
|
||||
|
||||
def get_directory_statistics(directory_path):
|
||||
# Get total disk usage
|
||||
total, used, free = shutil.disk_usage(directory_path)
|
||||
# Convert to GB
|
||||
total = total // (2 ** 30)
|
||||
used = used // (2 ** 30)
|
||||
free = free // (2 ** 30)
|
||||
usage_percentage = (used / total) * 100
|
||||
|
||||
# Calculate the size of the directory
|
||||
directory_used = sum(os.path.getsize(os.path.join(root, file)) for root, dirs, files in os.walk(directory_path) for file in files)
|
||||
|
||||
# Get sizes in a human-readable format
|
||||
total_human = human_readable_size(total)
|
||||
used_human = human_readable_size(directory_used)
|
||||
free_human = human_readable_size(free)
|
||||
|
||||
# Calculate usage percentage
|
||||
usage_percentage = (directory_used / total) * 100 if total > 0 else 0
|
||||
|
||||
# Determine storage warning
|
||||
storage_warning = ""
|
||||
if usage_percentage > 90:
|
||||
storage_warning = "Warning: Storage usage is above 90%!"
|
||||
elif usage_percentage > 75:
|
||||
storage_warning = "Caution: Storage usage is above 75%."
|
||||
|
||||
return {
|
||||
"total_gb": total,
|
||||
"used_gb": used,
|
||||
"free_gb": free,
|
||||
"usage_percentage": round(usage_percentage, 2)
|
||||
"total": total_human,
|
||||
"used": used_human,
|
||||
"free": free_human,
|
||||
"usage_percentage": round(usage_percentage, 2) if total > 0 else 0,
|
||||
"storage_warning": storage_warning
|
||||
}
|
||||
|
||||
|
||||
@login_required
|
||||
def view_dashboard(request):
|
||||
context = {'page_title': 'Welcome to routerfleet'}
|
||||
|
@ -43,7 +76,9 @@ def view_status(request):
|
|||
'queue': RouterBackup.objects.filter(success=False, error=False).count(),
|
||||
'success_backup_last_24h': RouterBackup.objects.filter(success=True, created__gte=timezone.now() - timedelta(days=1)).count(),
|
||||
'error_backup_last_24h': RouterBackup.objects.filter(error=True, created__gte=timezone.now() - timedelta(days=1)).count(),
|
||||
'router_count': Router.objects.filter(enabled=True).count(),
|
||||
'total_router_count':Router.objects.all().count(),
|
||||
'router_enabled_count': Router.objects.filter(enabled=True).count(),
|
||||
'router_disabled_count': Router.objects.filter(enabled=False).count(),
|
||||
'router_online_count': RouterStatus.objects.filter(status_online=True, router__monitoring=True).count(),
|
||||
'router_offline_count': RouterStatus.objects.filter(status_online=False, router__monitoring=True).count(),
|
||||
'router_not_monitored_count': Router.objects.filter(enabled=True, monitoring=False).count(),
|
||||
|
@ -51,3 +86,87 @@ def view_status(request):
|
|||
}
|
||||
|
||||
return render(request, 'dashboard/status.html', context=context)
|
||||
@login_required
|
||||
def backup_statistics_data(request):
|
||||
try:
|
||||
days = int(request.GET.get('days', 7))
|
||||
except ValueError:
|
||||
return HttpResponseBadRequest("Invalid 'days' parameter")
|
||||
|
||||
if days not in ALLOWED_DAYS:
|
||||
return HttpResponseBadRequest("Invalid 'days' parameter")
|
||||
today = timezone.now()
|
||||
start_date = today - timedelta(days=days)
|
||||
|
||||
dates = [start_date + timedelta(days=i) for i in range(days + 1)]
|
||||
success_data = []
|
||||
error_data = []
|
||||
|
||||
for i in range(days):
|
||||
day_start = dates[i]
|
||||
day_end = dates[i + 1]
|
||||
success_count = RouterBackup.objects.filter(created__gte=day_start, created__lt=day_end, success=True).count()
|
||||
error_count = RouterBackup.objects.filter(created__gte=day_start, created__lt=day_end, error=True).count()
|
||||
success_data.append(success_count)
|
||||
error_data.append(error_count)
|
||||
|
||||
data = {
|
||||
'dates': [date.strftime('%Y-%m-%d') for date in dates[:-1]],
|
||||
'success_data': success_data,
|
||||
'error_data': error_data,
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
@login_required
|
||||
def router_status_data(request):
|
||||
try:
|
||||
days = int(request.GET.get('days', '7'))
|
||||
|
||||
except ValueError:
|
||||
return HttpResponseBadRequest(f"Invalid 'days' {days} parameter")
|
||||
|
||||
if days not in ALLOWED_DAYS:
|
||||
return HttpResponseBadRequest("Invalid 'days' parameter. Must be one of: " + ', '.join(map(str, ALLOWED_DAYS)))
|
||||
|
||||
today = timezone.now()
|
||||
start_date = today - timedelta(days=days)
|
||||
|
||||
# Create a list of dates for the period
|
||||
dates = [start_date + timedelta(days=i) for i in range(days + 1)]
|
||||
router_statuses = RouterStatus.objects.filter(router__enabled=True)
|
||||
|
||||
online_data = []
|
||||
offline_data = []
|
||||
|
||||
for i in range(days):
|
||||
day_start = dates[i]
|
||||
day_end = dates[i + 1]
|
||||
|
||||
# Get statuses that changed within the current day
|
||||
daily_statuses = router_statuses.filter(last_status_change__gte=day_start, last_status_change__lt=day_end)
|
||||
|
||||
online_count = daily_statuses.filter(status_online=True).count()
|
||||
offline_count = daily_statuses.filter(status_online=False).count()
|
||||
|
||||
# Get routers that have not changed status on the current day
|
||||
unchanged_routers = router_statuses.exclude(last_status_change__gte=day_start, last_status_change__lt=day_end)
|
||||
|
||||
for router_status in unchanged_routers:
|
||||
last_change = router_status.last_status_change
|
||||
# Only perform the comparison if last_change is not None
|
||||
if last_change and last_change < day_start:
|
||||
if router_status.status_online:
|
||||
online_count += 1
|
||||
else:
|
||||
offline_count += 1
|
||||
|
||||
online_data.append(online_count)
|
||||
offline_data.append(offline_count)
|
||||
|
||||
data = {
|
||||
'dates': [date.strftime('%Y-%m-%d') for date in dates[:-1]],
|
||||
'online_data': online_data,
|
||||
'offline_data': offline_data,
|
||||
}
|
||||
|
||||
return JsonResponse(data)
|
|
@ -1,5 +1,6 @@
|
|||
from django.contrib import messages
|
||||
from django.db.models import Sum
|
||||
from django.http import JsonResponse
|
||||
from django.utils import timezone
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
@ -227,3 +228,44 @@ def view_create_instant_backup_task(request):
|
|||
messages.success(request, 'Backup task created successfully')
|
||||
return redirect(router_details_url)
|
||||
|
||||
@login_required
|
||||
def view_create_instant_backup_multiple_routers(request):
|
||||
if request.method == 'POST':
|
||||
if not UserAcl.objects.filter(user=request.user, user_level__gte=20).exists():
|
||||
return JsonResponse({'error': 'Permission denied.'}, status=403)
|
||||
|
||||
uuids = request.POST.getlist('routers[]') # Ajustat pentru a primi lista corect
|
||||
|
||||
if not uuids:
|
||||
return JsonResponse({'error': 'No routers selected.'}, status=400)
|
||||
|
||||
results = []
|
||||
for uuid in uuids:
|
||||
router = get_object_or_404(Router, uuid=uuid)
|
||||
|
||||
if RouterBackup.objects.filter(router=router, success=False, error=False).exists():
|
||||
results.append({'router': router.name, 'status': 'active backup task exists'})
|
||||
continue
|
||||
|
||||
if router.routerstatus.backup_lock:
|
||||
results.append({'router': router.name, 'status': 'backup locked'})
|
||||
continue
|
||||
|
||||
if not router.backup_profile:
|
||||
results.append({'router': router.name, 'status': 'no backup profile'})
|
||||
continue
|
||||
|
||||
router_backup = RouterBackup.objects.create(
|
||||
router=router,
|
||||
schedule_time=timezone.now(),
|
||||
schedule_type='instant'
|
||||
)
|
||||
|
||||
router.routerstatus.backup_lock = router_backup.schedule_time
|
||||
router.routerstatus.save()
|
||||
|
||||
results.append({'router': router.name, 'status': 'backup started'})
|
||||
|
||||
return JsonResponse({'results': results})
|
||||
|
||||
return JsonResponse({'error': 'Invalid request method.'}, status=405)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from dashboard.views import view_dashboard, view_status
|
||||
from dashboard.views import view_dashboard, view_status,backup_statistics_data,router_status_data
|
||||
|
||||
from integration_manager.views import view_wireguard_webadmin_launcher, view_manage_wireguard_integration, view_launch_wireguard_webadmin
|
||||
from user_manager.views import view_manage_user, view_user_list
|
||||
from accounts.views import view_login, view_logout, view_create_first_user
|
||||
from router_manager.views import view_router_list, view_manage_router, view_router_group_list, view_ssh_key_list, view_manage_router_group, view_manage_sshkey, view_router_details, view_create_instant_backup_task, view_router_availability
|
||||
from router_manager.views import view_create_instant_backup_multiple_routers, view_router_list, view_manage_router, view_router_group_list, view_ssh_key_list, view_manage_router_group, view_manage_sshkey, view_router_details, view_create_instant_backup_task, view_router_availability
|
||||
from backup.views import view_backup_profile_list, view_manage_backup_profile, view_backup_list, view_backup_details, view_debug_run_backups, view_compare_backups, view_backup_download, view_backup_delete
|
||||
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
|
||||
|
@ -19,6 +21,8 @@ urlpatterns = [
|
|||
path('debug/test_messages/', view_debug_test_messages, name='debug_test_messages'),
|
||||
path('', view_dashboard, name='dashboard'),
|
||||
path('status/', view_status, name='status'),
|
||||
path('router_status_data/', router_status_data, name='router_status_data'),
|
||||
path('backup_statistics_data/', backup_statistics_data, name='backup_statistics_data'),
|
||||
path('user/list/', view_user_list, name='user_list'),
|
||||
path('user/manage/', view_manage_user, name='manage_user'),
|
||||
path('accounts/create_first_user/', view_create_first_user, name='create_first_user'),
|
||||
|
@ -33,6 +37,7 @@ urlpatterns = [
|
|||
path('router/manage_group/', view_manage_router_group, name='manage_router_group'),
|
||||
path('router/manage_sshkey/', view_manage_sshkey, name='manage_sshkey'),
|
||||
path('router/create_instant_backup/', view_create_instant_backup_task, name='create_instant_backup_task'),
|
||||
path('router/create_instant_backup/multiple/', view_create_instant_backup_multiple_routers, name='create_instant_backup_multiple'),
|
||||
path('router/import_tool/', view_import_tool_list, name='import_tool_list'),
|
||||
path('router/import_tool/csv/', view_import_csv_file, name='import_csv_file'),
|
||||
path('router/import_tool/details/', view_import_details, name='import_details'),
|
||||
|
|
|
@ -1,123 +1,321 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class='row'>
|
||||
<div class='{% if form_size %}{{ form_size }}{% else %}col-lg-6{% endif %}'>
|
||||
<div class="card card-primary card-outline">
|
||||
{% if page_title %}
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ page_title }}</h3>
|
||||
<style>
|
||||
.info-box {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.info-box:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="row">
|
||||
<!-- Left Column for General Information -->
|
||||
<div class="col-lg-12">
|
||||
<!-- System Stats Card -->
|
||||
<div class="card card-primary card-outline">
|
||||
{% if page_title %}
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-tachometer-alt"></i> {{ page_title }}
|
||||
</h3>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<div class="row mb-4">
|
||||
<!-- Used Storage -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-info">
|
||||
<span class="info-box-icon"><i class="fas fa-hdd"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Used Storage (binary and rsc backups)</span>
|
||||
<span class="info-box-number">{{ media_root_stats.used }} used</span>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: {{ media_root_stats.usage_percentage }}%"></div>
|
||||
</div>
|
||||
<span class="progress-description">
|
||||
{{ media_root_stats.usage_percentage }}% of {{ media_root_stats.total }}
|
||||
{% if media_root_stats.storage_warning %}
|
||||
<span class="text-danger">{{ media_root_stats.storage_warning }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body row">
|
||||
<div class="col-lg-12">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Used storage (binary backup)
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ media_root_stats.usage_percentage }}%
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Backup Queue
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ queue }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Successful backups (24h)
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ success_backup_last_24h }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Failed backups (24h)
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ error_backup_last_24h }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Routers
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ router_count }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Online Routers
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ router_online_count }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Offline Routers
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ router_offline_count }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Not monitored Routers
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ router_not_monitored_count }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
Router Fleet Version
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
{{ webadmin_version.current_version }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- Backup Queue -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-warning">
|
||||
<span class="info-box-icon"><i class="fas fa-tasks"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Backup Queue</span>
|
||||
<span class="info-box-number">{{ queue }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Successful Backups -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-success">
|
||||
<span class="info-box-icon"><i class="fas fa-check-circle"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Successful Backups (24h)</span>
|
||||
<span class="info-box-number">{{ success_backup_last_24h }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Failed Backups -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-danger">
|
||||
<span class="info-box-icon"><i class="fas fa-times-circle"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Failed Backups (24h)</span>
|
||||
<span class="info-box-number">{{ error_backup_last_24h }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
|
||||
<!-- Total Routers -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-primary">
|
||||
<span class="info-box-icon"><i class="fas fa-network-wired"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Total Routers</span>
|
||||
<span class="info-box-number">{{ total_router_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Online Routers -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-success">
|
||||
<span class="info-box-icon"><i class="fas fa-plug"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Online Routers</span>
|
||||
<span class="info-box-number">{{ router_online_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Offline Routers -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-danger">
|
||||
<span class="info-box-icon"><i class="fas fa-plug"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Offline Routers</span>
|
||||
<span class="info-box-number">{{ router_offline_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Disabled Routers -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-danger">
|
||||
<span class="info-box-icon"><i class="fas fa-times"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Disabled Routers</span>
|
||||
<span class="info-box-number">{{ router_disabled_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<!-- Not Monitored Routers -->
|
||||
<div class="col-md-3">
|
||||
<div class="info-box bg-secondary">
|
||||
<span class="info-box-icon"><i class="fas fa-question-circle"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Not Monitored Routers</span>
|
||||
<span class="info-box-number">{{ router_not_monitored_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /.card-body -->
|
||||
</div> <!-- /.card -->
|
||||
</div> <!-- /.col-lg-12 -->
|
||||
</div> <!-- /.row -->
|
||||
|
||||
|
||||
<!-- Right Column for Charts -->
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<!-- Backup Status Chart -->
|
||||
<div class="card card-primary card-outline">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-chart-bar"></i> Backup Status Statistics</h3>
|
||||
<div class="card-tools">
|
||||
<select id="days-select-backup" class="form-control">
|
||||
<option value="7">Last 7 days</option>
|
||||
<option value="3">Last 3 days</option>
|
||||
<option value="5">Last 5 days</option>
|
||||
<option value="10">Last 10 days</option>
|
||||
<option value="15">Last 15 days</option>
|
||||
<option value="30">Last 30 days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="backupChart"></canvas>
|
||||
</div>
|
||||
<p class="small">This status page is just a placeholder, it will be improved in next versions</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
<div class="col-lg-6">
|
||||
<!-- Router Status Chart -->
|
||||
<div class="card card-primary card-outline">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-signal"></i> Router Status Statistics</h3>
|
||||
<div class="card-tools">
|
||||
<select id="days-select-status" class="form-control">
|
||||
<option value="7">Last 7 days</option>
|
||||
<option value="3">Last 3 days</option>
|
||||
<option value="5">Last 5 days</option>
|
||||
<option value="10">Last 10 days</option>
|
||||
<option value="15">Last 15 days</option>
|
||||
<option value="30">Last 30 days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="routerStatusChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
var backupChart;
|
||||
|
||||
function createBackupChart(data) {
|
||||
var ctx = document.getElementById('backupChart').getContext('2d');
|
||||
if (backupChart) {
|
||||
backupChart.destroy();
|
||||
}
|
||||
backupChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.dates,
|
||||
datasets: [{
|
||||
label: 'Successful Backups',
|
||||
data: data.success_data,
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 2,
|
||||
fill: true, // Fill the area under the line
|
||||
},
|
||||
{
|
||||
label: 'Failed Backups',
|
||||
data: data.error_data,
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
borderWidth: 2,
|
||||
fill: true, // Fill the area under the line
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function updateBackupStatistics(days) {
|
||||
fetch(`/backup_statistics_data?days=${days}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Invalid days parameter');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
createBackupChart(data);
|
||||
})
|
||||
.catch(error => {
|
||||
alert(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const daysSelectBackup = document.getElementById('days-select-backup');
|
||||
daysSelectBackup.addEventListener('change', (event) => {
|
||||
updateBackupStatistics(event.target.value);
|
||||
});
|
||||
|
||||
updateBackupStatistics(7); // Initial load for the last 7 days
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
var routerStatusChart;
|
||||
function createRouterStatusChart(data) {
|
||||
var ctx2 = document.getElementById('routerStatusChart').getContext('2d');
|
||||
if (routerStatusChart) {
|
||||
routerStatusChart.destroy();
|
||||
}
|
||||
routerStatusChart = new Chart(ctx2, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.dates,
|
||||
datasets: [{
|
||||
label: 'Online Routers',
|
||||
data: data.online_data,
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 2,
|
||||
fill: true, // Fill the area under the line
|
||||
},
|
||||
{
|
||||
label: 'Offline Routers',
|
||||
data: data.offline_data,
|
||||
backgroundColor: 'rgba(153, 102, 255, 0.2)',
|
||||
borderColor: 'rgba(153, 102, 255, 1)',
|
||||
borderWidth: 2,
|
||||
fill: true, // Fill the area under the line
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateRouterStatus(days) {
|
||||
fetch(`/router_status_data?days=${days}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Invalid days parameter'+ " " + days);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
createRouterStatusChart(data);
|
||||
})
|
||||
.catch(error => {
|
||||
alert(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const daysSelectStatus = document.getElementById('days-select-status');
|
||||
daysSelectStatus.addEventListener('change', (event) => {
|
||||
updateRouterStatus(event.target.value);
|
||||
});
|
||||
|
||||
updateRouterStatus(7); // Initial load for the last 7 days
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
|
@ -17,6 +17,7 @@
|
|||
<table class="table table-hover datatables-no-export">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Select</th>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Address</th>
|
||||
|
@ -26,17 +27,14 @@
|
|||
<th>Auth</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for router in router_list %}
|
||||
<tr {% if not router.enabled %}style="text-decoration: line-through;"{% endif %}>
|
||||
<td>
|
||||
<a href="/router/details/?uuid={{ router.uuid }}">{{ router.name }}</a>
|
||||
</td>
|
||||
<td><input type="checkbox" class="router-checkbox" data-uuid="{{ router.uuid }}"></td>
|
||||
<td><a href="/router/details/?uuid={{ router.uuid }}">{{ router.name }}</a></td>
|
||||
<td>{{ router.get_router_type_display }}</td>
|
||||
<td>{{ router.address }}</td>
|
||||
|
||||
<td id="status-{{ router.uuid }}">
|
||||
{% if router.monitoring %}
|
||||
{% if router.routerstatus.status_online %}
|
||||
|
@ -50,16 +48,14 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if router.router_type != 'monitoring' %}
|
||||
{% if router.backup_profile %}
|
||||
{{ router.backup_profile }} {% if router.routerstatus.last_backup_failed %}<i class="fas fa-exclamation-triangle text-danger" title="Last backup failed to complete"></i>{% endif %}
|
||||
{% else %}
|
||||
<i class="fas fa-exclamation-triangle text-warning" title="No backup profile selected"></i>
|
||||
{% endif %}
|
||||
{% if router.backup_profile %}
|
||||
{{ router.backup_profile }} {% if router.routerstatus.last_backup_failed %}<i class="fas fa-exclamation-triangle text-danger" title="Last backup failed to complete"></i>{% endif %}
|
||||
{% else %}
|
||||
<i class="fas fa-exclamation-triangle text-warning" title="No backup profile selected"></i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ router.routergroup_set.count }}
|
||||
</td>
|
||||
<td>{{ router.routergroup_set.count }}</td>
|
||||
<td class="min-width">
|
||||
{% if router.router_type != 'monitoring' %}
|
||||
{% if router.ssh_key %}
|
||||
|
@ -78,17 +74,20 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<a href="/router/manage/" class="btn btn-primary">Add Router</a>
|
||||
<a href="/router/import_tool/" class="btn btn-outline-primary">Import tool</a>
|
||||
<div class="row mb-3">
|
||||
<div class="col-lg-12 text-right">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/router/manage/" class="btn btn-primary">Add Router</a>
|
||||
<a href="/router/import_tool/" class="btn btn-warning">Import Tool</a>
|
||||
<button id="select-all" class="btn btn-outline-success"><i class="fas fa-check-square"></i> Select All</button>
|
||||
<button id="select-none" class="btn btn-outline-danger"><i class="fas fa-square"></i> Select None</button>
|
||||
<button id="create-backup" class="btn btn-warning" style="display: none;">Create Backup Task</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -160,4 +159,58 @@
|
|||
checkStatusChange();
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.router-checkbox').prop('checked', false);
|
||||
|
||||
$('#select-all').click(function() {
|
||||
$('.router-checkbox').prop('checked', true).trigger('change');
|
||||
});
|
||||
|
||||
$('#select-none').click(function() {
|
||||
$('.router-checkbox').prop('checked', false).trigger('change');
|
||||
});
|
||||
|
||||
$('.router-checkbox').change(function() {
|
||||
var checkedCount = $('.router-checkbox:checked').length;
|
||||
$('#create-backup').toggle(checkedCount >= 1);
|
||||
});
|
||||
|
||||
$('#create-backup').click(function() {
|
||||
var selectedRouters = $('.router-checkbox:checked').map(function() {
|
||||
return $(this).data('uuid');
|
||||
}).get();
|
||||
|
||||
$.ajax({
|
||||
url: '/router/create_instant_backup/multiple/',
|
||||
method: 'POST',
|
||||
data: {
|
||||
routers: selectedRouters,
|
||||
csrfmiddlewaretoken: '{{ csrf_token }}'
|
||||
},
|
||||
success: function(response) {
|
||||
let messages = response.results.map(item => `${item.router}: ${item.status}`).join('\n');
|
||||
// Create success toast
|
||||
$(document).Toasts('create', {
|
||||
class: 'bg-success',
|
||||
title: 'Backup Status',
|
||||
body: 'The following routers were backed up: ' + messages,
|
||||
delay: 10000,
|
||||
autohide: true
|
||||
});
|
||||
},
|
||||
error: function(xhr) {
|
||||
// Create error toast
|
||||
$(document).Toasts('create', {
|
||||
class: 'bg-danger',
|
||||
title: 'Error',
|
||||
body: 'Error: ' + xhr.responseJSON.error,
|
||||
delay: 10000,
|
||||
autohide: true
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue