diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py index b65af8cfb5788f271f41c34e096d3fc550f439b0..37db5cfda230c217a46bab7c1c328033582abe84 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py @@ -18,8 +18,7 @@ from django.http import ( Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect, ) - - +from distutils.util import strtobool from rest_framework.decorators import permission_classes from rest_framework.permissions import IsAuthenticated from rest_framework.decorators import action @@ -1276,6 +1275,32 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet): return RestResponse(serializer.data) + @swagger_auto_schema(responses={200: 'The schedule was reset', + 403: 'forbidden'}, + operation_description="reset the schedule: marking the unschedulable units as schedulable, and then move all schedulable to a 'parking'-timestamp") + @action(methods=['post'], detail=False, url_name="reset_schedule") + def reset_schedule(self, request, pk=None): + from lofar.sas.tmss.tmss.tmssapp.subtasks import update_subtasks_start_times_for_scheduling_unit, mark_independent_subtasks_in_task_blueprint_as_schedulable + for unit in SchedulingUnitBlueprint.objects.filter(status__value='unschedulable', obsolete_since__isnull=True).all(): + mark_independent_subtasks_in_task_blueprint_as_schedulable(unit) + + if strtobool(request.query_params.get('scheduled', 'False')): + for unit in SchedulingUnitBlueprint.objects.filter(status__value='scheduled', obsolete_since__isnull=True).all(): + unschedule_subtasks_in_scheduling_unit_blueprint(unit) + + # default 'parking'-timestamp is at noon seven days from now. + timestamp = (datetime.utcnow() + timedelta(days=7)).replace(hour=12, minute=0, second=0, microsecond=0) + timestamp_str = request.query_params.get('timestamp', None) + if timestamp_str: + timestamp = dateutil.parser.parse(timestamp_str) + + for unit in SchedulingUnitBlueprint.objects.filter(status__value='schedulable', obsolete_since__isnull=True).all(): + update_subtasks_start_times_for_scheduling_unit(unit, start_time=timestamp, placed=False) + + # just return a success + return HttpResponse(status=200) + + class SchedulingUnitBlueprintExtendedViewSet(SchedulingUnitBlueprintViewSet): serializer_class = serializers.SchedulingUnitBlueprintExtendedSerializer diff --git a/SAS/TMSS/client/bin/CMakeLists.txt b/SAS/TMSS/client/bin/CMakeLists.txt index 1f7836c2d90bf1098542b6a2e197d6347912f8be..c88db45ce2add38274e87bcc6190d5c35417b88b 100644 --- a/SAS/TMSS/client/bin/CMakeLists.txt +++ b/SAS/TMSS/client/bin/CMakeLists.txt @@ -18,5 +18,4 @@ lofar_add_bin_scripts(tmss_mark_scheduling_unit_dynamically_scheduled) lofar_add_bin_scripts(tmss_mark_scheduling_unit_fixed_time_scheduled_at_scheduled_starttime) lofar_add_bin_scripts(tmss_schedule_scheduling_unit_at_given_starttime) lofar_add_bin_scripts(tmss_unschedule_scheduling_unit) - - +lofar_add_bin_scripts(tmss_reset_schedule) \ No newline at end of file diff --git a/SAS/TMSS/client/bin/tmss_reset_schedule b/SAS/TMSS/client/bin/tmss_reset_schedule new file mode 100755 index 0000000000000000000000000000000000000000..a3c2e3c9e15822f42b25eb7a6c56191dd2a369d2 --- /dev/null +++ b/SAS/TMSS/client/bin/tmss_reset_schedule @@ -0,0 +1,23 @@ +#!/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.client.mains import main_reset_schedule + +if __name__ == "__main__": + main_reset_schedule() diff --git a/SAS/TMSS/client/lib/mains.py b/SAS/TMSS/client/lib/mains.py index 4a750826e1ac6904b496ce13066cd52c1e5170bc..ce40aa29fee6c84eba52b2f9d615684b106b2976 100644 --- a/SAS/TMSS/client/lib/mains.py +++ b/SAS/TMSS/client/lib/mains.py @@ -367,4 +367,23 @@ def main_mark_scheduling_unit_dynamically_scheduled(): exit(1) +def main_reset_schedule(): + # default 'parking'-timestamp is at noon seven days from now. + timestamp = (datetime.datetime.utcnow() + datetime.timedelta(days=7)).replace(hour=12, minute=0, second=0, microsecond=0) + + parser = argparse.ArgumentParser(description="Reset the status of all unschedulable units to schedulable, and then position all schedulable units at the given start_time. Typical usage: reset the schedule using this tool and let the scheduler place all units.") + parser.add_argument('-t', '--timestamp', type=str, default=timestamp.strftime("%Y-%m-%d %H:%M"), help="Position/park all schedulable units at this timestamp expressed as \"YYYY-MM-DD HH:MM\"") + parser.add_argument('-s', '--scheduled', action='store_true', help='also unschedule all scheduled units') + parser.add_argument('-R', '--rest_api_credentials', type=str, default='TMSSClient', help='TMSS django REST API credentials name, default: TMSSClient') + args = parser.parse_args() + + try: + with TMSSsession.create_from_dbcreds_for_ldap(dbcreds_name=args.rest_api_credentials) as client: + client.post_to_path_and_get_result_as_json_object('scheduling_unit_blueprint/reset_schedule', + params={'scheduled': args.scheduled, + 'timestamp': args.timestamp}) + except Exception as e: + print(e) + exit(1) + diff --git a/SAS/TMSS/client/lib/tmss_http_rest_client.py b/SAS/TMSS/client/lib/tmss_http_rest_client.py index 7c6c1579805338a05f3f2a011f364465b8d97bc7..9e75c50e870e9289af2e5bb7e600d4c8db147088 100644 --- a/SAS/TMSS/client/lib/tmss_http_rest_client.py +++ b/SAS/TMSS/client/lib/tmss_http_rest_client.py @@ -359,19 +359,19 @@ class TMSSsession(object): return result_object return result - def post_to_url_and_get_result_as_string(self, full_url: str, json_data:dict=None, retry_count: int=DEFAULT_RETRY_COUNT) -> str: + def post_to_url_and_get_result_as_string(self, full_url: str, json_data:dict=None, params: dict=None, retry_count: int=DEFAULT_RETRY_COUNT) -> str: '''post to the given full_url including http://<base_url>, and return the response as plain text ''' - return self.do_request_and_get_result_as_string('POST', full_url, json_data=json_data, retry_count=retry_count) + return self.do_request_and_get_result_as_string('POST', full_url, json_data=json_data, params=params, retry_count=retry_count) - def post_to_url_and_get_result_as_json_object(self, full_url: str, json_data:dict=None, retry_count: int=DEFAULT_RETRY_COUNT) -> object: + def post_to_url_and_get_result_as_json_object(self, full_url: str, json_data:dict=None, params: dict=None, retry_count: int=DEFAULT_RETRY_COUNT) -> object: '''post to the given full_url (including http://<base_url>), and return the response as native object (usually a dict or a list of dicts)''' - result = self.post_to_url_and_get_result_as_string(full_url, json_data=json_data, retry_count=retry_count) + result = self.post_to_url_and_get_result_as_string(full_url, json_data=json_data, params=params, retry_count=retry_count) return json.loads(result) - def post_to_path_and_get_result_as_json_object(self, path: str, json_data:dict=None, retry_count: int=DEFAULT_RETRY_COUNT) -> object: + def post_to_path_and_get_result_as_json_object(self, path: str, json_data:dict=None, params: dict=None, retry_count: int=DEFAULT_RETRY_COUNT) -> object: '''post to the given path, and return the response as native object (usually a dict or a list of dicts)''' - return self.post_to_url_and_get_result_as_json_object(self.get_full_url_for_path(path=path), json_data=json_data, retry_count=retry_count) + return self.post_to_url_and_get_result_as_json_object(self.get_full_url_for_path(path=path), json_data=json_data, params=params, retry_count=retry_count) def _log_response(self, response: requests.Response): logger.log(level=logging.INFO if response.status_code >= 200 and response.status_code < 300 else logging.WARNING,