|
12 | 12 | from flask_restful import Resource, Api, inputs |
13 | 13 | from werkzeug.exceptions import BadRequest |
14 | 14 | from agaveflask.utils import RequestParser, ok |
| 15 | +from parse import parse |
15 | 16 |
|
16 | 17 | from auth import check_permissions, get_tas_data, tenant_can_use_tas, get_uid_gid_homedir, get_token_default |
17 | 18 | from channels import ActorMsgChannel, CommandChannel, ExecutionResultsChannel, WorkerChannel |
@@ -65,6 +66,68 @@ def get(self, search_type): |
65 | 66 | return ok(result=result, msg="Search completed successfully.") |
66 | 67 |
|
67 | 68 |
|
| 69 | +class CronResource(Resource): |
| 70 | + def get(self): |
| 71 | + logger.debug("HERE I AM IN GET /cron") |
| 72 | + actor_ids = [actor['db_id'] for actor in actors_store.items()] |
| 73 | + logger.debug(f"actor ids are {actor_ids}") |
| 74 | + # Loop through all actor ids to check for cron schedules |
| 75 | + for actor_id in actor_ids: |
| 76 | + # Create actor based on the actor_id |
| 77 | + actor = actors_store[actor_id] |
| 78 | + logger.debug(f"cron_on equals {actor.get('cron_on')} for actor {actor_id}") |
| 79 | + try: |
| 80 | + # Check if next execution == UTC current time |
| 81 | + if self.cron_execution_datetime(actor): |
| 82 | + # Check if cron switch is on |
| 83 | + if actor.get('cron_on'): |
| 84 | + d = {} |
| 85 | + logger.debug("the current time is the same as the next cron scheduled, adding execution") |
| 86 | + # Execute actor |
| 87 | + before_exc_time = timeit.default_timer() |
| 88 | + exc = Execution.add_execution(actor_id, {'cpu': 0, |
| 89 | + 'io': 0, |
| 90 | + 'runtime': 0, |
| 91 | + 'status': codes.SUBMITTED, |
| 92 | + 'executor': 'cron'}) |
| 93 | + logger.debug("execution has been added, now making message") |
| 94 | + # Create & add message to the queue |
| 95 | + d['Time_msg_queued'] = before_exc_time |
| 96 | + d['_abaco_execution_id'] = exc |
| 97 | + d['_abaco_Content_Type'] = 'str' |
| 98 | + ch = ActorMsgChannel(actor_id=actor_id) |
| 99 | + ch.put_msg(message="This is your cron execution", d=d) |
| 100 | + ch.close() |
| 101 | + logger.debug("Message added to actor inbox. id: {}.".format(actor_id)) |
| 102 | + # Update the actor's next execution |
| 103 | + actors_store[actor_id, 'cron_next_ex'] = Actor.set_next_ex(actor, actor_id) |
| 104 | + else: |
| 105 | + logger.debug("Actor's cron is not activated, but next execution will be incremented") |
| 106 | + actors_store[actor_id, 'cron_next_ex'] = Actor.set_next_ex(actor, actor_id) |
| 107 | + else: |
| 108 | + logger.debug("now is not the time") |
| 109 | + except: |
| 110 | + logger.debug("Actor has no cron setup") |
| 111 | + |
| 112 | + def cron_execution_datetime(self, actor): |
| 113 | + logger.debug("inside cron_execution_datetime method") |
| 114 | + now = get_current_utc_time() |
| 115 | + now = datetime.datetime(now.year, now.month, now.day, now.hour) |
| 116 | + logger.debug(f"the current utc time is {now}") |
| 117 | + # Get cron execution datetime |
| 118 | + cron = actor['cron_next_ex'] |
| 119 | + logger.debug(f"cron_next_ex is {cron}") |
| 120 | + # Parse the next execution into a list of the form: [year,month,day,hour] |
| 121 | + cron_datetime = parse("{}-{}-{} {}", cron) |
| 122 | + logger.debug(f"cron datetime is {cron_datetime}") |
| 123 | + # Create a datetime out of cron_datetime |
| 124 | + cron_execution = datetime.datetime(int(cron_datetime[0]), int(cron_datetime[1]), int(cron_datetime[2]), int(cron_datetime[3])) |
| 125 | + logger.debug(f"cron execution is {cron_execution}") |
| 126 | + # Return true/false comparing now with the next cron execution |
| 127 | + logger.debug(f"does cron == now? {cron_execution == now}") |
| 128 | + return cron_execution == now |
| 129 | + |
| 130 | + |
68 | 131 | class MetricsResource(Resource): |
69 | 132 | def get(self): |
70 | 133 | enable_autoscaling = Config.get('workers', 'autoscaling') |
@@ -769,6 +832,36 @@ def post(self): |
769 | 832 | else: |
770 | 833 | token = get_token_default() |
771 | 834 | args['token'] = token |
| 835 | + # adding check for 'log_ex' |
| 836 | + if 'logEx' in args and args.get('logEx') is not None: |
| 837 | + log_ex = int(args.get('logEx')) |
| 838 | + args['log_ex'] = log_ex |
| 839 | + # cron attribute |
| 840 | + cron = None |
| 841 | + if Config.get('web', 'case') == 'camel': |
| 842 | + logger.debug("Case is camel") |
| 843 | + if 'cronSchedule' in args and args.get('cronSchedule') is not None: |
| 844 | + cron = args.get('cronSchedule') |
| 845 | + else: |
| 846 | + if 'cron_schedule' in args and args.get('cron_schedule') is not None: |
| 847 | + logger.debug("Case is snake") |
| 848 | + cron = args.get('cron_schedule') |
| 849 | + if cron is not None: |
| 850 | + logger.debug("Cron has been posted") |
| 851 | + # set_cron checks for the 'now' alias |
| 852 | + # It also checks that the cron schedule is greater than or equal to the current UTC time |
| 853 | + r = Actor.set_cron(cron) |
| 854 | + logger.debug(f"r is {r}") |
| 855 | + if r.fixed[2] in ['hours', 'hour', 'days', 'day', 'weeks', 'week', 'months', 'month']: |
| 856 | + args['cron_schedule'] = cron |
| 857 | + logger.debug(f"setting cron_next_ex to {r.fixed[0]}") |
| 858 | + args['cron_next_ex'] = r.fixed[0] |
| 859 | + args['cron_on'] = True |
| 860 | + else: |
| 861 | + raise BadRequest(f'{r.fixed[2]} is an invalid unit of time') |
| 862 | + args['cron_on'] = False |
| 863 | + else: |
| 864 | + logger.debug("Cron schedule was not sent in") |
772 | 865 | if Config.get('web', 'case') == 'camel': |
773 | 866 | max_workers = args.get('maxWorkers') |
774 | 867 | args['max_workers'] = max_workers |
@@ -891,6 +984,36 @@ def put(self, actor_id): |
891 | 984 | args = self.validate_put(actor) |
892 | 985 | logger.debug("PUT args validated successfully.") |
893 | 986 | args['tenant'] = g.tenant |
| 987 | + cron = None |
| 988 | + if 'logEx' in args and args.get('logEx') is not None: |
| 989 | + log_ex = int(args.get('logEx')) |
| 990 | + logger.debug(f"log_ex in args; using: {log_ex}") |
| 991 | + args['log_ex'] = log_ex |
| 992 | + # Check for both camel and snake case |
| 993 | + if Config.get('web', 'case') == 'camel': |
| 994 | + if 'cronSchedule' in args and args.get('cronSchedule') is not None: |
| 995 | + cron = args.get('cronSchedule') |
| 996 | + if 'cronOn' in args and args.get('cronOn') is not None: |
| 997 | + actor['cron_on'] = args.get('cronOn') |
| 998 | + else: |
| 999 | + if 'cron_schedule' in args and args.get('cron_schedule') is not None: |
| 1000 | + cron = args.get('cron_schedule') |
| 1001 | + if 'cron_on' in args and args.get('cron_on') is not None: |
| 1002 | + actor['cron_on'] = args.get('cron_on') |
| 1003 | + if cron is not None: |
| 1004 | + # set_cron checks for the 'now' alias |
| 1005 | + # It also checks that the cron schedule is greater than or equal to the current UTC time |
| 1006 | + # Check for proper unit of time |
| 1007 | + r = Actor.set_cron(cron) |
| 1008 | + if r.fixed[2] in ['hours', 'hour', 'days', 'day', 'weeks', 'week', 'months', 'month']: |
| 1009 | + args['cron_schedule'] = cron |
| 1010 | + logger.debug(f"setting cron_next_ex to {r.fixed[0]}") |
| 1011 | + args['cron_next_ex'] = r.fixed[0] |
| 1012 | + else: |
| 1013 | + raise BadRequest(f'{r.fixed[2]} is an invalid unit of time') |
| 1014 | + args['cron_on'] = False |
| 1015 | + else: |
| 1016 | + logger.debug("No cron schedule has been sent") |
894 | 1017 | if args['queue']: |
895 | 1018 | queues_list = Config.get('spawner', 'host_queues').replace(' ', '') |
896 | 1019 | valid_queues = queues_list.split(',') |
@@ -970,6 +1093,7 @@ def validate_put(self, actor): |
970 | 1093 | actor.pop('max_workers') |
971 | 1094 | actor.pop('mem_limit') |
972 | 1095 | actor.pop('max_cpus') |
| 1096 | + actor.pop('log_ex') |
973 | 1097 |
|
974 | 1098 | # this update overrides all required and optional attributes |
975 | 1099 | try: |
|
0 commit comments