diff --git a/SAS/TMSS/backend/services/CMakeLists.txt b/SAS/TMSS/backend/services/CMakeLists.txt index d28add1121af1b33ff814fac4c6f5ba9c5a2e9c1..fcf450a17b3faff9e242cc8c0d0160b362129b36 100644 --- a/SAS/TMSS/backend/services/CMakeLists.txt +++ b/SAS/TMSS/backend/services/CMakeLists.txt @@ -10,4 +10,5 @@ lofar_add_package(TMSSSchedulingService scheduling) lofar_add_package(TMSSSlackWebhookService slack_webhook) lofar_add_package(TMSSWebSocketService websocket) lofar_add_package(TMSSWorkflowService workflow_service) +lofar_add_package(TMSSReportRefreshService report_refresh) diff --git a/SAS/TMSS/backend/services/report_refresh/CMakeLists.txt b/SAS/TMSS/backend/services/report_refresh/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..de02703279e29aa6db07e43737780a3a9a5525e3 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/CMakeLists.txt @@ -0,0 +1,14 @@ +lofar_package(TMSSWebSocketService 0.1 DEPENDS TMSSClient PyCommon pyparameterset PyMessaging) # also depends on TMSSBackend, but that dependency is added implicitely because this is a child package + +lofar_find_package(PythonInterp 3.6 REQUIRED) + +IF(NOT SKIP_TMSS_BUILD) + include(FindPythonModule) + find_python_module(SimpleWebSocketServer REQUIRED) # sudo pip3 install SimpleWebSocketServer + + add_subdirectory(lib) + add_subdirectory(test) +ENDIF(NOT SKIP_TMSS_BUILD) + +add_subdirectory(bin) + diff --git a/SAS/TMSS/backend/services/report_refresh/bin/CMakeLists.txt b/SAS/TMSS/backend/services/report_refresh/bin/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c61137bc0b7a662ae864ff6faa37f35d9d8eafca --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/bin/CMakeLists.txt @@ -0,0 +1,2 @@ +lofar_add_bin_scripts(tmss_report_refresh_service) + diff --git a/SAS/TMSS/backend/services/report_refresh/bin/tmss_report_refresh_service b/SAS/TMSS/backend/services/report_refresh/bin/tmss_report_refresh_service new file mode 100755 index 0000000000000000000000000000000000000000..e7e317c64f6b2e073a66b17ec916d589a7453a93 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/bin/tmss_report_refresh_service @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +# Copyright (C) 2012-2015 ASTRON (Netherlands Institute for Radio Astronomy) +# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. + + +from lofar.sas.tmss.services.report_refresh import main + +if __name__ == "__main__": + main() diff --git a/SAS/TMSS/backend/services/report_refresh/lib/CMakeLists.txt b/SAS/TMSS/backend/services/report_refresh/lib/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6d5d8984446c293661930b5f1e819db34a4c0c46 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/lib/CMakeLists.txt @@ -0,0 +1,10 @@ +lofar_find_package(PythonInterp 3.4 REQUIRED) +include(PythonInstall) + +set(_py_files + report_refresh.py + ) + +python_install(${_py_files} + DESTINATION lofar/sas/tmss/services) + diff --git a/SAS/TMSS/backend/services/report_refresh/lib/report_refresh.py b/SAS/TMSS/backend/services/report_refresh/lib/report_refresh.py new file mode 100644 index 0000000000000000000000000000000000000000..4202ca24ff3071be4e5abc8984430bea71033461 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/lib/report_refresh.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +# subtask_scheduling.py +# +# Copyright (C) 2015 +# ASTRON (Netherlands Institute for Radio Astronomy) +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +from datetime import timedelta +import logging +import os +from optparse import OptionParser, OptionGroup + +logger = logging.getLogger(__name__) + +from lofar.sas.tmss.client.tmssbuslistener import * +from lofar.sas.tmss.client.tmss_http_rest_client import TMSSsession + +class TMSSEventMessageHandlerForReportRefresh(TMSSEventMessageHandler): + + def __init__(self, tmss_client_credentials_id: str="TMSSClient"): + super().__init__() + self.tmss_client = TMSSsession.create_from_dbcreds_for_ldap(tmss_client_credentials_id) + + def start_handling(self): + self.tmss_client.open() + + def stop_handling(self): + self.tmss_client.close() + + def onSubTaskStatusChanged(self, id: int, status: str): + + # if end state has been reached, request refresh of cycle report + if status in ['finished', 'cancelled', 'error']: + logger.info('Subtask id=%s has reached end state=%s' % (id, status)) + from lofar.sas.tmss.tmss.tmssapp.models import Subtask + subtask = Subtask.objects.get(id=id) + for cycle in subtask.project.cycles.all(): + logger.info('Requesting recalculation of report for cycle=%s because related subtask=%s has reached its end state=%s' % (cycle.pk, id, status)) + self.tmss_client.get_path_as_json_object('/cycle/%s/report/?refresh=true' % cycle.pk) + + +def create_service(exchange: str=DEFAULT_BUSNAME, broker: str=DEFAULT_BROKER, tmss_client_credentials_id: str="TMSSClient"): + return TMSSBusListener(handler_type=TMSSEventMessageHandlerForReportRefresh, + handler_kwargs={'tmss_client_credentials_id': tmss_client_credentials_id}, + exchange=exchange, broker=broker) + + +def main(): + # make sure we run in UTC timezone + os.environ['TZ'] = 'UTC' + + logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) + + # Check the invocation arguments + parser = OptionParser('%prog [options]', + description='run the tmss_report_refresh_service which listens for TMSS event messages on the messagebus, and triggers a refresh of the cycle report on relevant changes.') + + group = OptionGroup(parser, 'Messaging options') + group.add_option('-b', '--broker', dest='broker', type='string', default=DEFAULT_BROKER, + help='Address of the message broker, default: %default') + group.add_option('-e', "--exchange", dest="exchange", type="string", default=DEFAULT_BUSNAME, + help="exchange where the TMSS event messages are published. [default: %default]") + parser.add_option_group(group) + + group = OptionGroup(parser, 'Django options') + parser.add_option_group(group) + group.add_option('-C', '--credentials', dest='dbcredentials', type='string', default=os.environ.get('TMSS_DBCREDENTIALS', 'TMSSClient'), help='django dbcredentials name, default: %default') + + (options, args) = parser.parse_args() + + from lofar.sas.tmss.tmss import setup_and_check_tmss_django_database_connection_and_exit_on_error + setup_and_check_tmss_django_database_connection_and_exit_on_error(options.dbcredentials) + + with create_service(options.exchange, options.broker, options.dbcredentials): + waitForInterrupt() + +if __name__ == '__main__': + main() diff --git a/SAS/TMSS/backend/services/report_refresh/requirements.txt b/SAS/TMSS/backend/services/report_refresh/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..601bc79fa354b84d437e1e17971621db27f43089 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/requirements.txt @@ -0,0 +1,3 @@ +SimpleWebSocketServer # MIT +Django # BSD3 +djangorestframework # BSD \ No newline at end of file diff --git a/SAS/TMSS/backend/services/report_refresh/test/CMakeLists.txt b/SAS/TMSS/backend/services/report_refresh/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7de2bd757aec594a18182e59a2d89674d70dc655 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/test/CMakeLists.txt @@ -0,0 +1,9 @@ +# $Id: CMakeLists.txt 32679 2015-10-26 09:31:56Z schaap $ + +if(BUILD_TESTING) + include(FindPythonModule) + + include(LofarCTest) + + lofar_add_test(t_report_refresh_service) +endif() diff --git a/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.py b/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.py new file mode 100755 index 0000000000000000000000000000000000000000..c28354cefb0a259f4f0c377c408218d7802ddb2e --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2012-2015 ASTRON (Netherlands Institute for Radio Astronomy) +# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This file is part of the LOFAR software suite. +# The LOFAR software suite is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# The LOFAR software suite is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. +import time +import unittest +import uuid +from datetime import datetime + +import logging +logger = logging.getLogger('lofar.' + __name__) +logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO) + +from lofar.messaging.messagebus import TemporaryExchange, BusListenerJanitor + +# create temporary queue +TEST_UUID = uuid.uuid1() +tmp_exchange = TemporaryExchange("%s_%s" % (__name__, TEST_UUID)) +tmp_exchange.open() + +# override DEFAULT_BUSNAME (before test env start) +import lofar +lofar.messaging.config.DEFAULT_BUSNAME = tmp_exchange.address + +# start test env +from lofar.sas.tmss.test.test_environment import TMSSTestEnvironment +tmss_test_env = TMSSTestEnvironment(exchange=tmp_exchange.address, populate_test_data=False, populate_schemas=False, start_websocket=False, start_postgres_listener=True, enable_viewflow=False) +tmss_test_env.start() + +# import what's needed for the test (after test env start) +from lofar.sas.tmss.services.report_refresh import create_service, TMSSEventMessageHandlerForReportRefresh +from lofar.common.test_utils import integration_test +from lofar.sas.tmss.test.tmss_test_data_django_models import * +from lofar.sas.tmss.tmss.tmssapp import models + +def tearDownModule(): + tmss_test_env.stop() + tmp_exchange.close() + +@integration_test +class TestReportRefreshService(unittest.TestCase): + ''' + Tests for the ReportRefreshService + ''' + + def test_refort_refresh_on_ending_subtask(self): + ''' + This test starts a report refresh service. Creates and cancels a subtasl to check if this triggers the creation + of a cycle report in the database. + ''' + + # test data setup + project = models.Project.objects.create(**Project_test_data(name=str(uuid.uuid4()))) + cycle = models.Cycle.objects.create(**Cycle_test_data(name=str(uuid.uuid4()))) + project.cycles.set([cycle]) + project.save() + scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data(project=project)) + scheduling_unit_template = models.SchedulingUnitTemplate.get_latest(name='scheduling unit') + scheduling_unit_draft = models.SchedulingUnitDraft.objects.create(**SchedulingUnitDraft_test_data(name=str(uuid.uuid4()), template=scheduling_unit_template, scheduling_set=scheduling_set)) + scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data(name=str(uuid.uuid4()), draft=scheduling_unit_draft)) + task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), scheduling_unit_blueprint=scheduling_unit_blueprint)) + subtask_obs = models.Subtask.objects.create(**Subtask_test_data(state=models.SubtaskState.objects.get(value="defining"), + subtask_template=models.SubtaskTemplate.get_latest(name='observation control'), + task_blueprint=task_blueprint)) + subtask_obs.state = models.SubtaskState.objects.get(value="defined") + subtask_obs.save() + + # create and start the service (the object under test) + service = create_service(exchange=tmp_exchange.address, tmss_client_credentials_id=tmss_test_env.client_credentials.dbcreds_id) + with BusListenerJanitor(service): + # assert we did not have anything in the cache for the test cycle + self.assertEqual(models.CycleReport.objects.filter(cycle=cycle).count(), 0) + + # trigger the service with a subtask status update + subtask_obs.state = models.SubtaskState.objects.get(value="error") + subtask_obs.save() + + # wait for the service to do its thing + timeout = 30 + start = datetime.utcnow() + while models.CycleReport.objects.filter(cycle=cycle).count() == 0: + if datetime.utcnow() - start > timedelta(seconds=timeout): + raise TimeoutError("timeout while waiting for cycle report refresh to get triggered.") + logger.info('waiting for cycle report refresh to get triggered.') + time.sleep(0.5) + while 'weekly_efficiency' not in models.CycleReport.objects.filter(cycle=cycle).first().data: + if datetime.utcnow() - start > timedelta(seconds=timeout): + raise TimeoutError("timeout while waiting for refreshed cycle report to get created.") + logger.info('waiting for refreshed cycle report to get created.') + time.sleep(0.5) + + # assert we have a cache entry, so the refresh request worked + self.assertEqual(models.CycleReport.objects.filter(cycle=cycle).count(), 1) + + # assert we have a valid report and not just a placeholder, so the actual cycle report generation worked + self.assertIn('weekly_efficiency', models.CycleReport.objects.filter(cycle=cycle).first().data) + + +if __name__ == '__main__': + #run the unit tests + unittest.main() diff --git a/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.run b/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.run new file mode 100755 index 0000000000000000000000000000000000000000..1850968465f90b2db304a8cf12eb63afa73bbbf3 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.run @@ -0,0 +1,6 @@ +#!/bin/bash + +# Run the unit test +source python-coverage.sh +python_coverage_test "*tmss*" t_report_refresh_service.py + diff --git a/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.sh b/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.sh new file mode 100755 index 0000000000000000000000000000000000000000..945fe16351f1507be81e11f57c27c908e1028288 --- /dev/null +++ b/SAS/TMSS/backend/services/report_refresh/test/t_report_refresh_service.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./runctest.sh t_report_refresh_service \ No newline at end of file diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0049_cyclereport.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0049_cyclereport.py new file mode 100644 index 0000000000000000000000000000000000000000..eac1f1a73aabbb9d0e7595febc1ddd8800b6bae5 --- /dev/null +++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0049_cyclereport.py @@ -0,0 +1,32 @@ +# Generated by Django 3.0.9 on 2023-09-19 11:20 + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tmssapp', '0048_indices'), + ] + + operations = [ + migrations.CreateModel( + name='CycleReport', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cycle', models.OneToOneField(help_text='Cycle to which these quota apply.', on_delete=django.db.models.deletion.PROTECT, related_name='report', to='tmssapp.Cycle')), + ('data', django.contrib.postgres.fields.jsonb.JSONField(help_text='The report data')), + ('start', models.DateTimeField(help_text='Moment at which the reporting period starts.')), + ('stop', models.DateTimeField(help_text='Moment at which the reporting period ends.')), + ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)), + ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')), + ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')), + ], + ), + migrations.AddConstraint( + model_name='cyclereport', + constraint=models.UniqueConstraint(fields=('cycle', 'start', 'stop'), + name='cyclereport_unique_cycle_start_stop')) + ] diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index 68b06075bc53eb1c6916acd096e2ccff6119e6fe..11b25f6fad982d1b84fec49b08b0748bdad254d6 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -33,6 +33,7 @@ from lofar.common.util import subdict_of_pointer_items from .permissions import TMSSUser as User import typing import re +import collections, time, threading # # I/O @@ -858,6 +859,50 @@ class Cycle(RefreshFromDbInvalidatesCachedPropertiesMixin, NamedCommonPK): super().save(force_insert, force_update, using, update_fields) +class CycleReport(BasicCommon): + '''This table caches cycle report data, which are costly to compute and easily time out. + We create these on demand and keep them in the database, since async requests are not supported by DRF.''' + cycle = OneToOneField('Cycle', related_name="report", on_delete=PROTECT, help_text='Cycle to which these quota apply.') + start = DateTimeField(help_text='Moment at which the reporting period starts.') + stop = DateTimeField(help_text='Moment at which the reporting period ends.') + data = JSONField(help_text='The report data') + + class Meta: + constraints = [UniqueConstraint(fields=['cycle', 'start', 'stop'], name='%(class)s_unique_cycle_start_stop')] + + @staticmethod + def __process_cycle_queue(cycle_queue: collections.deque): + '''background thread method to process cycles on the given queue into CycleReport entries''' + from lofar.sas.tmss.tmss.tmssapp.adapters.reports import create_cycle_report + while True: + try: + cycle, start, stop = cycle_queue.popleft() + report = CycleReport.objects.get(cycle=cycle, start=start, stop=stop) + report.data=create_cycle_report(cycle, start, stop) + report.save() + logger.info("CycleReport.process_cycle_queue: created CycleReport for cycle pk=%s", cycle.pk) + except IndexError: + # wait for work... + time.sleep(10) + except Exception as e: + logger.error("CycleReport.process_cycle_queue: %s", e) + + # a queue, processing thread and lock for processing submitted cycles in the background + __cycle_queue = collections.deque() + __cycle_processor_thread = threading.Thread(target=__process_cycle_queue, daemon=True, + kwargs={'cycle_queue': __cycle_queue}) + __cycle_processor_thread.start() + __lock = threading.Lock() + + @classmethod + def submit_cycle_for_report_creation(cls, cycle, start, stop): + '''submit a cycle for which we want to create a report, which will be generated in a background thread''' + with cls.__lock: + if [cycle, start, stop] not in cls.__cycle_queue: + logger.info("submitting cycle id=%s to queue for background report creation...", cycle.pk) + cls.__cycle_queue.append([cycle, start, stop]) + + class CycleQuota(Model): cycle = ForeignKey('Cycle', related_name="quota", on_delete=PROTECT, help_text='Cycle to which these quota apply.') value = FloatField(help_text='Resource Quota value') diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py index 8c8b80cc842de549c63a536eb4a4c204cec4358b..0ab0a32c5c3e56e367890b56eb92f1602213f9ad 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py @@ -57,6 +57,7 @@ import dateutil from django.core.exceptions import ObjectDoesNotExist from django import forms + logger = logging.getLogger(__name__) @@ -393,6 +394,7 @@ class CycleViewSet(LOFARViewSet): cycle = get_object_or_404(models.Cycle, pk=pk) # Check start and stop times start, stop = request.query_params.get('start', None), request.query_params.get('stop', None) + refresh = request.query_params.get('refresh', None) if start: try: start = dateutil.parser.parse(start, ignoretz=True) @@ -409,9 +411,15 @@ class CycleViewSet(LOFARViewSet): if not stop or stop > cycle.stop: stop = cycle.stop - result = create_cycle_report(cycle, start, stop) - return Response(result, status=status.HTTP_200_OK) - + if refresh: + # wipe all reports for this cycle (not just request period), so we have no outdated info in the cache. + logger.info('Refresh requested, wiping all cached reports for cycle=%s' % cycle.pk) + models.CycleReport.objects.filter(cycle=cycle).delete() + report = models.CycleReport.objects.filter(cycle=cycle, start=start, stop=stop).first() + if not report: + report = models.CycleReport.objects.create(cycle=cycle, start=start, stop=stop, data="Your report is getting prepared. Please check again in a few minutes") + models.CycleReport.submit_cycle_for_report_creation(cycle, start, stop) + return Response(report.data, status=status.HTTP_200_OK) class CycleQuotaViewSet(LOFARViewSet): queryset = models.CycleQuota.objects.all() diff --git a/SAS/TMSS/backend/test/t_reports.py b/SAS/TMSS/backend/test/t_reports.py index 6288d1eb16cfa35127d2eef5e079714c3f1bce3d..072072df825e9344479a2faae6d2402a8e43f74d 100755 --- a/SAS/TMSS/backend/test/t_reports.py +++ b/SAS/TMSS/backend/test/t_reports.py @@ -18,6 +18,7 @@ # with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. import os +import time import unittest import requests @@ -384,7 +385,14 @@ class ReportTest(unittest.TestCase): Test create_cycle_report extra action. """ with tmss_test_env.create_tmss_client() as client: - result = client.get_path_as_json_object('/cycle/%s/report' % self.cycle.pk) + for trial in range(10): + result = client.get_path_as_json_object('/cycle/%s/report' % self.cycle.pk) + if "report is getting prepared" not in result: + break + logger.info('waiting for report generation to complete...') + time.sleep(2) + + self.assertNotIn("report is getting prepared", result) # Assertions diff --git a/SAS/TMSS/backend/test/test_environment.py b/SAS/TMSS/backend/test/test_environment.py index 7a37aa1bb8cd0587b2d08f39cbbf1a5643639c58..148078e9ef16a408891d5f9cb0bd765dab8044b6 100644 --- a/SAS/TMSS/backend/test/test_environment.py +++ b/SAS/TMSS/backend/test/test_environment.py @@ -234,6 +234,7 @@ class TMSSTestEnvironment: start_feedback_service: bool=False, start_workflow_service: bool=False, enable_viewflow: bool=False, start_precalculations_service: bool=False, + start_report_refresh_service: bool=False, ldap_dbcreds_id: str=None, db_dbcreds_id: str=None, client_dbcreds_id: str=None): self._exchange = exchange self._broker = broker @@ -288,6 +289,9 @@ class TMSSTestEnvironment: self._start_precalculations_service = start_precalculations_service self.precalculations_service = None + self._start_report_refresh_service = start_report_refresh_service + self.report_refresh_service = None + # Check for correct Django version, should be at least 3.0 if django.VERSION[0] < 3: print("\nWARNING: YOU ARE USING DJANGO VERSION '%s', WHICH WILL NOT SUPPORT ALL FEATURES IN TMSS!\n" % @@ -387,6 +391,12 @@ class TMSSTestEnvironment: except Exception as e: logger.exception(e) + if self._start_report_refresh_service: + from lofar.sas.tmss.services.report_refresh import create_service as create_report_refresh_service + self.report_refresh_service = create_report_refresh_service(exchange=self._exchange, broker=self._broker) + service_threads.append(threading.Thread(target=self.report_refresh_service.start_listening())) + service_threads[-1].start() + # wait for all services to be fully started in their background threads for thread in service_threads: thread.join() @@ -691,6 +701,7 @@ def main_test_environment(): group.add_option('-w', '--websockets', dest='websockets', action='store_true', help='Enable json updates pushed via websockets') group.add_option('-f', '--feedbackservice', dest='feedbackservice', action='store_true', help='Enable feedbackservice to handle feedback from observations/pipelines which comes in via the (old qpid) otdb messagebus.') group.add_option('-C', '--precalculations_service', dest='precalculations_service', action='store_true', help='Enable the PreCalculations service') + group.add_option('-r', '--report_refresh_service', dest='report_refresh_service', action='store_true', help='Enable the ReportRefresh service') group.add_option('--all', dest='all', action='store_true', help='Enable/Start all the services, upload schemas') group.add_option('--simulate', dest='simulate', action='store_true', help='Simulate a run of the for each scheduled scheduling_unit (implies --eventmessages)') @@ -736,6 +747,7 @@ def main_test_environment(): enable_viewflow=options.viewflow_app or options.viewflow_service or options.all, start_workflow_service=options.viewflow_service or options.all, start_precalculations_service=options.precalculations_service or options.all, + start_report_refresh_service=options.report_refresh_service or options.all, ldap_dbcreds_id=options.LDAP_ID, db_dbcreds_id=options.DB_ID, client_dbcreds_id=options.REST_CLIENT_ID) as tmss_test_env: # print some nice info for the user to use the test servers... diff --git a/SAS/TMSS/deploy/docker-compose.yml b/SAS/TMSS/deploy/docker-compose.yml index 8548e66686889364e0aafef0968d2d4f53fa5c39..833fc28248106f918a4572f4cb3cc80dbceb3b1d 100644 --- a/SAS/TMSS/deploy/docker-compose.yml +++ b/SAS/TMSS/deploy/docker-compose.yml @@ -301,3 +301,26 @@ services: driver: journald options: tag: tmss_lobster + report_refresh: + container_name: tmss_report_refresh + image: tmss_report_refresh + build: + context: ./app + dockerfile: Dockerfile + args: + SOURCE_IMAGE: ${SOURCE_IMAGE} + HOME: "/localhome/lofarsys" + restart: unless-stopped + env_file: + - env + environment: + - USER=lofarsys + - HOME=/localhome/lofarsys + command: /bin/bash -c 'source /opt/lofar/lofarinit.sh; exec tmss_report_refresh_service' + depends_on: + db_migrate: + condition: service_completed_successfully + logging: + driver: journald + options: + tag: tmss_report_refresh \ No newline at end of file