diff --git a/atdb/atdb/settings/base.py b/atdb/atdb/settings/base.py index 79d03f49dd38ad97a54c444e99d91a0098e04ea4..52dcb06fd829093025ad6f689324e43f02ceaa72 100644 --- a/atdb/atdb/settings/base.py +++ b/atdb/atdb/settings/base.py @@ -198,4 +198,6 @@ ACTIVE_STATUSSES = ['staging','staged','processing','processed','validated','sto STATUSSES_WITH_DATA = ['staged','fetching','fetched','processing','processed','validated','storing','stored','scrubbing','scrubbed','archiving','archived'] AGGREGATES = ['failed','active','total'] -QUERY_LIMIT_MULTI_CHANGE = 1000 \ No newline at end of file +QUERY_LIMIT_MULTI_CHANGE = 1000 +MAX_MONITORING_HISTORY_HOURS = 7 * 24 +SERVICES_LATE_WARNING_SECONDS = 1800 \ No newline at end of file diff --git a/atdb/atdb/static/taskdatabase/style.css b/atdb/atdb/static/taskdatabase/style.css index abaa979356a70d6a40c5ffac12ca958ef50d26c4..86e9ac908dc232210c22807bcabfd36f7bd2efe7 100644 --- a/atdb/atdb/static/taskdatabase/style.css +++ b/atdb/atdb/static/taskdatabase/style.css @@ -34,7 +34,7 @@ TD { } -.error,.failed,.staging_failed,.staged_failed,.processed_failed,.scrubbed_failed,.stored_failed,.archived_failed { +.error,.failed,.staging_failed,.staged_failed,.processed_failed,.scrubbed_failed,.stored_failed,.archived_failed,.on_hold { color: red; font-weight: bold; } diff --git a/atdb/taskdatabase/models.py b/atdb/taskdatabase/models.py index 3a770aa02f4adf7425479d30d119969050e06e71..60ede446eadfeb294007b3cdbd8f417fcb457fc1 100644 --- a/atdb/taskdatabase/models.py +++ b/atdb/taskdatabase/models.py @@ -1,6 +1,8 @@ from django.db import models from django.urls import reverse -from django.utils.timezone import datetime +from django.utils import timezone +from django.utils.timezone import datetime, timedelta +from django.conf import settings # constants datetime_format_string = '%Y-%m-%dT%H:%M:%SZ' @@ -226,11 +228,31 @@ class LatestMonitor(models.Model): status = models.CharField(max_length=50, default="ok", null=True) metadata = models.JSONField(null=True, blank=True) + @property + def enabled(self): + try: + enabled = self.metadata['enabled'] + return enabled + except: + # only when metadata['enabled']=False' this will be register as false. + # to make sure that services are enabled by default + return "True" + # the representation of the value in the REST API def __str__(self): return str(self.name) + ' - ('+ self.hostname+') - '+str(self.timestamp) + ' - ' + self.status +def purge_old_records(): + current_time = timezone.now() # change this + try: + time_threshold = current_time - timedelta(hours=settings.MAX_MONITORING_HISTORY_HOURS) + records_to_delete = Monitor.objects.filter(timestamp__lt=time_threshold).delete() + except Exception as e: + # if MAX_MONITORING_HISTORY_HOURS is not set, then do nothing and continue + pass + + class Monitor(models.Model): name = models.CharField(max_length=50, default="unknown") type = models.CharField(max_length=20, default="ldv-service", null=True, blank=True) @@ -246,11 +268,20 @@ class Monitor(models.Model): # in the LatestMonitor, and update if it is newer. try: latestMonitor = LatestMonitor.objects.get(name=self.name,hostname=self.hostname) + # carry over the metadata, if possible + latest_metadata = latestMonitor.metadata latestMonitor.delete() except: pass # this combination of name and hostname didn't yet exist, create it. + metadata = self.metadata + try: + if latest_metadata: + metadata = latest_metadata + except: + pass + latestMonitor = LatestMonitor( name=self.name, type=self.type, @@ -259,13 +290,16 @@ class Monitor(models.Model): process_id = self.process_id, description = self.description, status = self.status, - metadata = self.metadata + metadata = metadata ) latestMonitor.save() # finally save the Monitor info itself also super(Monitor, self).save(*args, **kwargs) + # and purge the monitoring table to its max + purge_old_records() + # the representation of the value in the REST API def __str__(self): return str(self.name) + ' - ('+ self.hostname+') - '+str(self.timestamp) + ' - ' + self.status diff --git a/atdb/taskdatabase/services/algorithms.py b/atdb/taskdatabase/services/algorithms.py index 0fa1fce7ed1fe033353a16efb482646959a2845c..62fe1b7ba12196b4e00d31f296496ebbf1805db9 100644 --- a/atdb/taskdatabase/services/algorithms.py +++ b/atdb/taskdatabase/services/algorithms.py @@ -9,10 +9,6 @@ from django.db.models import Q, Sum import logging from .common import timeit -from urllib.request import urlopen -from django.core.files import File -from django.core.files.temp import NamedTemporaryFile - from ..models import Task, LogEntry, Workflow, Configuration from django.conf import settings @@ -288,19 +284,31 @@ def convert_monitor_to_html(request, monitor_data): line += "<td><b>" + link_to_service_history + "</b></td>" line += "<td>" + str(record.hostname) + "</td>" + # only provide the hold/resume buttons for superusers, otherwise just show the state + if request.user.is_superuser: + if record.enabled=="True": + service_enabled = str(record.enabled) + ' <a href="service_hold_resume/' + str(record.pk) + '/False"' + 'class="btn btn-warning btn-sm" role="button"><i class="fas fa-pause"></i> Hold</a>' + else: + service_enabled = str(record.enabled) + ' <a href="service_hold_resume/' + str(record.pk) + '/True"' + 'class="btn btn-success btn-sm" role="button"><i class="fas fa-play"></i> Resume</a>' + else: + service_enabled = str(record.enabled) + # if the heartbeat is 30 minutes late, show '(late)' in red - if delta.seconds > 1800: + if delta.seconds > settings.SERVICES_LATE_WARNING_SECONDS: + line += "<td>" + service_enabled + "</td>" line += "<td><i>unknown</i></td>" line += '<td class="error">' + str(record.timestamp.strftime(TIME_FORMAT)) + " - (late)</td>" else: + line += "<td>" + service_enabled + "</td>" line += '<td class="' + record.status + '" >' + str(record.status) + "</td>" line += '<td>' + str(record.timestamp.strftime(TIME_FORMAT)) + "</td>" + line += "<td>" + str(record.process_id) + "</td>" line += "<td>" + str(record.description) + "</td>" line += "</tr>" results = results + line - except: + except Exception as e: results = "<tr><td>no data</td></tr>" return results diff --git a/atdb/taskdatabase/static/taskdatabase/style.css b/atdb/taskdatabase/static/taskdatabase/style.css index abaa979356a70d6a40c5ffac12ca958ef50d26c4..86e9ac908dc232210c22807bcabfd36f7bd2efe7 100644 --- a/atdb/taskdatabase/static/taskdatabase/style.css +++ b/atdb/taskdatabase/static/taskdatabase/style.css @@ -34,7 +34,7 @@ TD { } -.error,.failed,.staging_failed,.staged_failed,.processed_failed,.scrubbed_failed,.stored_failed,.archived_failed { +.error,.failed,.staging_failed,.staged_failed,.processed_failed,.scrubbed_failed,.stored_failed,.archived_failed,.on_hold { color: red; font-weight: bold; } diff --git a/atdb/taskdatabase/templates/taskdatabase/index.html b/atdb/taskdatabase/templates/taskdatabase/index.html index 36cbd2dd4c658906d7831f7b8a455375c496df71..ea9b66c7a793f2c60bdadef8bacaf840acc6383d 100644 --- a/atdb/taskdatabase/templates/taskdatabase/index.html +++ b/atdb/taskdatabase/templates/taskdatabase/index.html @@ -34,7 +34,7 @@ {% include 'taskdatabase/pagination.html' %} </div> </div> - <p class="footer"> Version 1.0.0 (14 apr 2021 - 13:00) + <p class="footer"> Version 1.0.0 (21 apr 2021 - 13:00) </div> diff --git a/atdb/taskdatabase/templates/taskdatabase/monitoring_page.html b/atdb/taskdatabase/templates/taskdatabase/monitoring_page.html index 6b85c675e86ef472209e1402525f456b39e13d76..0679ca228746b005a9a786f5c9139dcf07052ba3 100644 --- a/atdb/taskdatabase/templates/taskdatabase/monitoring_page.html +++ b/atdb/taskdatabase/templates/taskdatabase/monitoring_page.html @@ -22,7 +22,7 @@ </p> {% endif %} <table class="table table-striped"> - <th>LDV-Service</th><th>Host</th><th>Status</th><th>Timestamp</th><th>Process id</th><th>Description</th> + <th>LDV-Service</th><th>Host</th><th>Enabled</th><th>Last Status</th><th>Timestamp</th><th>Process id</th><th>Description</th> <tbody> {{ monitor_results | safe }} </tbody> diff --git a/atdb/taskdatabase/urls.py b/atdb/taskdatabase/urls.py index d950d86cf0e76e08b1188b68f26d42bc2d5bd722..e55c0c0335c99b9232cddff67cd81f3287ef90ad 100644 --- a/atdb/taskdatabase/urls.py +++ b/atdb/taskdatabase/urls.py @@ -72,7 +72,7 @@ urlpatterns = [ path('monitor/<int:pk>/', views.MonitorDetailsViewAPI.as_view(),name='monitor-detail-view-api'), path('latest_monitor/', views.LatestMonitorListViewAPI.as_view(),name='latest-monitor-detail-view-api'), path('monitor/clear_inactive_services/', views.ClearInactiveServices, name='clear-inactive-services'), - + path('monitoring/service_hold_resume/<int:pk>/<enabled>', views.ServiceHoldResume, name='service-hold-resume'), # --- custom requests --- # /atdb/get_size?status__in=defined,staged @@ -108,5 +108,7 @@ urlpatterns = [ path('tasks/<int:pk>/hold/<hold_it>', views.Hold, name='task-hold-resume'), path('tasks/<int:pk>/query-hold/<hold_it>/<query_params>', views.HoldQuery, name='query-hold-resume'), + path('tasks/<int:pk>/hold/<hold_it>/<page>', views.Hold, name='service-hold-resume'), + path('hello/', views.HelloView.as_view(), name='hello'), ] diff --git a/atdb/taskdatabase/views.py b/atdb/taskdatabase/views.py index 654b8cd028f96c4d344d4015ddba8e2e1a01f3b0..f6775feb55ce896ef2825ff784c385ea40897c7e 100644 --- a/atdb/taskdatabase/views.py +++ b/atdb/taskdatabase/views.py @@ -6,7 +6,6 @@ from django.contrib.auth.decorators import login_required from django.views.generic import ListView from django.contrib import messages -from django.http import QueryDict from rest_framework import generics, pagination from rest_framework.response import Response @@ -41,7 +40,6 @@ from .serializers import \ PostProcessingRuleSerializer, \ MonitorSerializer, LatestMonitorSerializer - from .services import algorithms logger = logging.getLogger(__name__) @@ -156,6 +154,15 @@ class MonitorFilter(filters.FilterSet): 'timestamp': ['icontains'], } +class LatestMonitorFilter(filters.FilterSet): + class Meta: + model = LatestMonitor + + fields = { + 'name': ['exact', 'icontains', 'in'], + 'hostname': ['exact', 'icontains', 'in'], + } + # ---------- Tables2 Views (experimental) ----------- class QueryView(SingleTableMixin, FilterView): table_class = TaskTable @@ -903,7 +910,7 @@ class LatestMonitorListViewAPI(generics.ListCreateAPIView): serializer_class = LatestMonitorSerializer filter_backends = (filters.DjangoFilterBackend,) - filter_class = MonitorFilter + filter_class = LatestMonitorFilter @login_required @@ -945,6 +952,19 @@ def HoldQuery(request, pk, hold_it, query_params): current_query_params = convert_query_params_to_url(query_params) return redirect('/atdb/query/?' + current_query_params) +@login_required +def ServiceHoldResume(request, pk, enabled): + model = LatestMonitor + service = LatestMonitor.objects.get(pk=pk) + metadata = service.metadata + if not metadata: + metadata = {} + + metadata['enabled'] = enabled + service.metadata = metadata + service.save() + return redirect('/atdb/monitoring') + @login_required def TaskSetStatus(request, pk, new_status, page=0):