diff --git a/zoe_api/rest_api/execution.py b/zoe_api/rest_api/execution.py index 97a9dbf21a2dd93e29ade2216ad4f96819099684..1ae94cbf1fff39cb451b28a8bddffc74534c1591 100644 --- a/zoe_api/rest_api/execution.py +++ b/zoe_api/rest_api/execution.py @@ -126,8 +126,21 @@ class ExecutionCollectionAPI(RequestHandler): def get(self): """ Returns a list of all active executions. - - Return a list of all executions which have status equal to ..." + + The list can be filtered by passing a non-empty JSON dictionary. Any combination of the following filters is supported: + + * status: one of submitted, scheduled, starting, error, running, cleaning up, terminated + * name: execution mane + * user_id: user_id owning the execution (admin only) + * limit: limit the number of returned entries + * earlier_than_submit: all execution that where submitted earlier than this timestamp + * earlier_than_start: all execution that started earlier than this timestamp + * earlier_than_end: all execution that ended earlier than this timestamp + * later_than_submit: all execution that where submitted later than this timestamp + * later_than_start: all execution that started later than this timestamp + * later_than_end: all execution that started later than this timestamp + + All timestamps should be passed as number of seconds since the epoch (UTC timezone). example: curl -u 'username:password' -X GET -H "Content-Type: application/json" -d '{"status":"terminated"}' http://bf5:8080/api/0.6/execution @@ -137,16 +150,26 @@ class ExecutionCollectionAPI(RequestHandler): filt_dict = {} - try: - if self.request.body: - filt_dict = tornado.escape.json_decode(self.request.body) - except ValueError: - raise zoe_api.exceptions.ZoeRestAPIException('Error decoding JSON data') - - if 'status' in filt_dict: - execs = self.api_endpoint.execution_list(uid, role, status=filt_dict['status']) - else: - execs = self.api_endpoint.execution_list(uid, role) + filters = [ + ('status', str), + ('name', str), + ('user_id', str), + ('limit', int), + ('earlier_than_submit', int), + ('earlier_than_start', int), + ('earlier_than_end', int), + ('later_than_submit', int), + ('later_than_start', int), + ('later_than_end', int) + ] + for f in filters: + if f[0] in self.request.arguments: + if f[1] == str: + filt_dict[f[0]] = self.request.arguments[f[0]][0].decode('utf-8') + else: + filt_dict[f[0]] = f[1](self.request.arguments[f[0]][0]) + + execs = self.api_endpoint.execution_list(uid, role, **filt_dict) self.write(dict([(e.id, e.serialize()) for e in execs])) diff --git a/zoe_cmd/entrypoint.py b/zoe_cmd/entrypoint.py index a676c73ddb7b263ea82c41c57dbc9b23f4cfdb92..6363742f63b1f39641d617f350a1fd57ca2b58fe 100644 --- a/zoe_cmd/entrypoint.py +++ b/zoe_cmd/entrypoint.py @@ -66,10 +66,26 @@ def app_get_cmd(args): json.dump(execution['description'], sys.stdout, sort_keys=True, indent=4) -def exec_list_cmd(args_): +def exec_list_cmd(args): """List executions""" exec_api = ZoeExecutionsAPI(utils.zoe_url(), utils.zoe_user(), utils.zoe_pass()) - data = exec_api.list() + filter_names = [ + 'status', + 'name', + 'user_id', + 'limit', + 'earlier_than_submit', + 'earlier_than_start', + 'earlier_than_end', + 'later_than_submit', + 'later_than_start', + 'later_than_end' + ] + filters = {} + for k, v in vars(args).items(): + if k in filter_names: + filters[k] = v + data = exec_api.list(**filters) for e in sorted(data.values(), key=lambda x: x['id']): print('Execution {} (User: {}, ID: {}): {}'.format(e['name'], e['user_id'], e['id'], e['status'])) @@ -181,6 +197,16 @@ def process_arguments() -> Tuple[ArgumentParser, Namespace]: argparser_exec_start.set_defaults(func=exec_start_cmd) argparser_app_list = subparser.add_parser('exec-ls', help="List all executions for the calling user") + argparser_app_list.add_argument('--limit', type=int, help='Limit the number of executions') + argparser_app_list.add_argument('--name', help='Show only executions with this name') + argparser_app_list.add_argument('--user', help='Show only executions belonging to this user') + argparser_app_list.add_argument('--status', choices=["submitted", "scheduled", "starting", "error", "running", "cleaning up", "terminated"], help='Show only executions with this status') + argparser_app_list.add_argument('--earlier-than-submit', help='Show only executions submitted earlier than this timestamp (seconds since UTC epoch)') + argparser_app_list.add_argument('--earlier-than-start', help='Show only executions submitted earlier than this timestamp (seconds since UTC epoch)') + argparser_app_list.add_argument('--earlier-than-end', help='Show only executions submitted earlier than this timestamp (seconds since UTC epoch)') + argparser_app_list.add_argument('--later-than-submit', help='Show only executions submitted earlier than this timestamp (seconds since UTC epoch)') + argparser_app_list.add_argument('--later-than-start', help='Show only executions submitted earlier than this timestamp (seconds since UTC epoch)') + argparser_app_list.add_argument('--later-than-end', help='Show only executions submitted earlier than this timestamp (seconds since UTC epoch)') argparser_app_list.set_defaults(func=exec_list_cmd) argparser_execution_get = subparser.add_parser('exec-get', help="Get execution status") @@ -195,9 +221,9 @@ def process_arguments() -> Tuple[ArgumentParser, Namespace]: argparser_execution_kill.add_argument('id', type=int, help="Execution id") argparser_execution_kill.set_defaults(func=exec_kill_cmd) - argparser_execution_kill = subparser.add_parser('exec-rm', help="Deletes an execution") - argparser_execution_kill.add_argument('id', type=int, help="Execution id") - argparser_execution_kill.set_defaults(func=exec_rm_cmd) + argparser_execution_rm = subparser.add_parser('exec-rm', help="Deletes an execution") + argparser_execution_rm.add_argument('id', type=int, help="Execution id") + argparser_execution_rm.set_defaults(func=exec_rm_cmd) argparser_stats = subparser.add_parser('stats', help="Prints all available statistics") argparser_stats.set_defaults(func=stats_cmd) diff --git a/zoe_lib/api_base.py b/zoe_lib/api_base.py index 207d5eb3e83f22d7fd559995590d7cd655ec62e6..41beb7ea5e446693a4e902a246116bce532242a4 100644 --- a/zoe_lib/api_base.py +++ b/zoe_lib/api_base.py @@ -79,14 +79,15 @@ class ZoeAPIBase: return req, req.status_code @retry(ZoeAPIException) - def _rest_get(self, path): + def _rest_get(self, path, payload=None): """ :type path: str + :type payload: dict :rtype: (dict, int) """ url = self.url + '/api/' + ZOE_API_VERSION + path try: - req = requests.get(url, auth=(self.user, self.password)) + req = requests.get(url, auth=(self.user, self.password), params=payload) except requests.exceptions.Timeout: raise ZoeAPIException('HTTP connection timeout') except requests.exceptions.HTTPError: diff --git a/zoe_lib/executions.py b/zoe_lib/executions.py index 2e1ab08f6ef57ac61d77db6cfb317727a7ed2db1..0751ba72276b88b5b53b7579ae74d098dd61fe73 100644 --- a/zoe_lib/executions.py +++ b/zoe_lib/executions.py @@ -61,13 +61,26 @@ class ZoeExecutionsAPI(ZoeAPIBase): else: raise ZoeAPIException(data['message']) - def list(self): + def list(self, **kwargs): """ Returns a list of all executions for the calling user, all of them if the user is admin. + + The list can be filtered by passing arguments. Any combination of the following filters is supported: + + * status: one of submitted, scheduled, starting, error, running, cleaning up, terminated + * name: execution mane + * user_id: user_id owning the execution (admin only) + * limit: limit the number of returned entries + * earlier_than_submit: all execution that where submitted earlier than this timestamp + * earlier_than_start: all execution that started earlier than this timestamp + * earlier_than_end: all execution that ended earlier than this timestamp + * later_than_submit: all execution that where submitted later than this timestamp + * later_than_start: all execution that started later than this timestamp + * later_than_end: all execution that started later than this timestamp :return: """ - data, status_code = self._rest_get('/execution') + data, status_code = self._rest_get('/execution', kwargs) if status_code == 200: return data else: diff --git a/zoe_lib/state/sql_manager.py b/zoe_lib/state/sql_manager.py index 65551aacab2ff0b572a463b6ab17744a698bda74..e6a5b76393507191178e507f3ea1f68f0884401d 100644 --- a/zoe_lib/state/sql_manager.py +++ b/zoe_lib/state/sql_manager.py @@ -59,12 +59,14 @@ class SQLManager: cur.execute('SET search_path TO {},public'.format(self.schema)) return cur - def execution_list(self, only_one=False, **kwargs): + def execution_list(self, only_one=False, limit=-1, **kwargs): """ Return a list of executions. :param only_one: only one result is expected :type only_one: bool + :param limit: limit the result to this number of entries + :type limit: int :param kwargs: filter executions based on their fields/columns :return: one or more executions """ @@ -75,11 +77,28 @@ class SQLManager: filter_list = [] args_list = [] for key, value in kwargs.items(): - filter_list.append('{} = %s'.format(key)) + if key == 'earlier_than_submit': + filter_list.append('"time_submit" <= to_timestamp(%s)') + elif key == 'earlier_than_start': + filter_list.append('"time_start" <= to_timestamp(%s)') + elif key == 'earlier_than_end': + filter_list.append('"time_end" <= to_timestamp(%s)') + elif key == 'later_than_submit': + filter_list.append('"time_submit" >= to_timestamp(%s)') + elif key == 'later_than_start': + filter_list.append('"time_start" >= to_timestamp(%s)') + elif key == 'later_than_end': + filter_list.append('"time_end" >= to_timestamp(%s)') + else: + filter_list.append('{} = %s'.format(key)) args_list.append(value) q += ' AND '.join(filter_list) + if limit > 0: + q += ' LIMIT {}'.format(limit) query = cur.mogrify(q, args_list) else: + if limit > 0: + q_base += ' LIMIT {}'.format(limit) query = cur.mogrify(q_base) cur.execute(query)