Add 'execution-get-report' command
Implements blueprint: workflow-error-analysis Depends-On: Id3e17821e04b7a1b84dfea5126d223d90ad8e3c2 Change-Id: I52b1d87ada2ead4f47a402b8aa683a0fa1629e70
This commit is contained in:
parent
b99cebafd4
commit
8cbf2fa1b9
|
@ -31,12 +31,11 @@ class ExecutionManager(base.ResourceManager):
|
|||
def create(self, workflow_identifier='', namespace='',
|
||||
workflow_input=None, description='', source_execution_id=None,
|
||||
**params):
|
||||
ident = workflow_identifier or source_execution_id
|
||||
self._ensure_not_empty(workflow_identifier=ident)
|
||||
self._ensure_not_empty(
|
||||
workflow_identifier=workflow_identifier or source_execution_id
|
||||
)
|
||||
|
||||
data = {
|
||||
'description': description,
|
||||
}
|
||||
data = {'description': description}
|
||||
|
||||
if uuidutils.is_uuid_like(source_execution_id):
|
||||
data.update({'source_execution_id': source_execution_id})
|
||||
|
@ -101,10 +100,31 @@ class ExecutionManager(base.ResourceManager):
|
|||
|
||||
def delete(self, id, force=None):
|
||||
self._ensure_not_empty(id=id)
|
||||
qparams = {}
|
||||
if force:
|
||||
qparams['force'] = True
|
||||
|
||||
query_string = self._build_query_params(filters=qparams)
|
||||
query_params = {}
|
||||
|
||||
if force:
|
||||
query_params['force'] = True
|
||||
|
||||
query_string = self._build_query_params(filters=query_params)
|
||||
|
||||
self._delete('/executions/%s%s' % (id, query_string))
|
||||
|
||||
def get_report(self, id, errors_only=True, max_depth=None):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
query_params = {}
|
||||
|
||||
if errors_only:
|
||||
query_params['errors_only'] = True
|
||||
|
||||
if max_depth is not None:
|
||||
query_params['max_depth'] = max_depth
|
||||
|
||||
query_string = self._build_query_params(filters=query_params)
|
||||
|
||||
resp = self.http_client.get(
|
||||
'/executions/%s/report%s' % (id, query_string)
|
||||
)
|
||||
|
||||
return resp.json()
|
||||
|
|
|
@ -215,7 +215,9 @@ class Delete(command.Command):
|
|||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
force = parsed_args.force
|
||||
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.executions.delete(s, force=force),
|
||||
parsed_args.execution,
|
||||
|
@ -290,6 +292,7 @@ class GetInput(command.Command):
|
|||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
ex_input = mistral_client.executions.get(parsed_args.id).input
|
||||
|
||||
try:
|
||||
|
@ -313,6 +316,7 @@ class GetOutput(command.Command):
|
|||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
output = mistral_client.executions.get(parsed_args.id).output
|
||||
|
||||
try:
|
||||
|
@ -322,3 +326,139 @@ class GetOutput(command.Command):
|
|||
LOG.debug("Execution output is not JSON.")
|
||||
|
||||
self.app.stdout.write(output or "\n")
|
||||
|
||||
|
||||
REPORT_ENTRY_INDENT = 4
|
||||
|
||||
|
||||
class GetReport(command.Command):
|
||||
"""Print execution report."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetReport, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('id', help='Execution ID')
|
||||
parser.add_argument(
|
||||
'--errors-only',
|
||||
dest='errors_only',
|
||||
action='store_true',
|
||||
help='Only error paths will be included.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-errors-only',
|
||||
dest='errors_only',
|
||||
action='store_false',
|
||||
help='Not only error paths will be included.'
|
||||
)
|
||||
parser.set_defaults(errors_only=True)
|
||||
|
||||
parser.add_argument(
|
||||
'--max-depth',
|
||||
dest='max_depth',
|
||||
nargs='?',
|
||||
type=int,
|
||||
default=-1,
|
||||
help='Maximum depth of the workflow execution tree. '
|
||||
'If 0, only the root workflow execution and its '
|
||||
'tasks will be included'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def print_line(self, line, level=0):
|
||||
self.app.stdout.write(
|
||||
"%s%s\n" % (' ' * (level * REPORT_ENTRY_INDENT), line)
|
||||
)
|
||||
|
||||
def print_workflow_execution_entry(self, wf_ex, level):
|
||||
self.print_line(
|
||||
"workflow '%s' [%s] %s" %
|
||||
(wf_ex['name'], wf_ex['state'], wf_ex['id']),
|
||||
level
|
||||
)
|
||||
|
||||
if 'task_executions' in wf_ex:
|
||||
for t_ex in wf_ex['task_executions']:
|
||||
self.print_task_execution_entry(t_ex, level + 1)
|
||||
|
||||
def print_task_execution_entry(self, t_ex, level):
|
||||
self.print_line(
|
||||
"task '%s' [%s] %s" %
|
||||
(t_ex['name'], t_ex['state'], t_ex['id']),
|
||||
level
|
||||
)
|
||||
|
||||
if t_ex['state'] == 'ERROR':
|
||||
state_info = t_ex['state_info']
|
||||
state_info = state_info[0:200] + '...'
|
||||
|
||||
self.print_line('(error info: %s)' % state_info, level)
|
||||
|
||||
if 'action_executions' in t_ex:
|
||||
for a_ex in t_ex['action_executions']:
|
||||
self.print_action_execution_entry(a_ex, level + 1)
|
||||
|
||||
if 'workflow_executions' in t_ex:
|
||||
for wf_ex in t_ex['workflow_executions']:
|
||||
self.print_workflow_execution_entry(wf_ex, level + 1)
|
||||
|
||||
def print_action_execution_entry(self, a_ex, level):
|
||||
self.print_line(
|
||||
"action '%s' [%s] %s" %
|
||||
(a_ex['name'], a_ex['state'], a_ex['id']),
|
||||
level
|
||||
)
|
||||
|
||||
def print_statistics(self, stat):
|
||||
self.print_line(
|
||||
'Number of tasks in SUCCESS state: %s' %
|
||||
stat['success_tasks_count']
|
||||
)
|
||||
self.print_line(
|
||||
'Number of tasks in ERROR state: %s' % stat['error_tasks_count']
|
||||
)
|
||||
self.print_line(
|
||||
'Number of tasks in RUNNING state: %s' %
|
||||
stat['running_tasks_count']
|
||||
)
|
||||
self.print_line(
|
||||
'Number of tasks in IDLE state: %s' % stat['idle_tasks_count']
|
||||
)
|
||||
self.print_line(
|
||||
'Number of tasks in PAUSED state: %s\n' %
|
||||
stat['paused_tasks_count']
|
||||
)
|
||||
|
||||
def print_report(self, report_json):
|
||||
self.print_line(
|
||||
"\nTo get more details on a task failure "
|
||||
"run: mistral task-get <id> -c 'State info'\n"
|
||||
)
|
||||
|
||||
frame_line = '=' * 30
|
||||
|
||||
self.print_line(
|
||||
'%s General Statistics %s\n' %
|
||||
(frame_line, frame_line)
|
||||
)
|
||||
self.print_statistics(report_json['statistics'])
|
||||
|
||||
self.print_line(
|
||||
'%s Workflow Execution Tree %s\n' %
|
||||
(frame_line, frame_line)
|
||||
)
|
||||
self.print_workflow_execution_entry(
|
||||
report_json['root_workflow_execution'],
|
||||
0
|
||||
)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
report_json = mistral_client.executions.get_report(
|
||||
parsed_args.id,
|
||||
errors_only=parsed_args.errors_only,
|
||||
max_depth=parsed_args.max_depth
|
||||
)
|
||||
|
||||
self.print_report(report_json)
|
||||
|
|
|
@ -728,6 +728,8 @@ class MistralShell(app.App):
|
|||
mistralclient.commands.v2.executions.GetInput,
|
||||
'execution-get-output':
|
||||
mistralclient.commands.v2.executions.GetOutput,
|
||||
'execution-get-report':
|
||||
mistralclient.commands.v2.executions.GetReport,
|
||||
'task-list': mistralclient.commands.v2.tasks.List,
|
||||
'task-get': mistralclient.commands.v2.tasks.Get,
|
||||
'task-get-published': mistralclient.commands.v2.tasks.GetPublished,
|
||||
|
|
|
@ -265,3 +265,17 @@ class TestExecutionsV2(base.BaseClientV2Test):
|
|||
self.requests_mock.delete(url, status_code=204)
|
||||
|
||||
self.executions.delete(EXEC['id'])
|
||||
|
||||
def test_report(self):
|
||||
url = self.TEST_URL + URL_TEMPLATE_ID % EXEC['id'] + '/report'
|
||||
|
||||
expected_json = {
|
||||
'root_workflow_execution': {},
|
||||
'statistics': {}
|
||||
}
|
||||
|
||||
self.requests_mock.get(url, json=expected_json)
|
||||
|
||||
report = self.executions.get_report(EXEC['id'])
|
||||
|
||||
self.assertDictEqual(expected_json, report)
|
||||
|
|
Loading…
Reference in New Issue