Provide consistent options and return latest execution entries
This patchset updates the *-list commands to have consistent definitions of the sort_keys/sort_dirs/limit/filter/marker options. It also modified the execution-list, action-execution-list and task-list commands to return the MOST RECENT entries by default, when no other sort_key, sort_dir or marker options are provided, rather than the oldest entries. There is a new --oldest option for these three commands to allow the user to access the oldest entries instead of the newest. A release note has also been created. Change-Id: I002edd1b10ab281072cfa7501cfa763073a7781c
This commit is contained in:
parent
7a1c8cc240
commit
d53da3629f
@ -14,9 +14,12 @@
|
||||
|
||||
import copy
|
||||
import json
|
||||
import six
|
||||
|
||||
from keystoneauth1 import exceptions
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Resource(object):
|
||||
resource_name = 'Something'
|
||||
@ -71,7 +74,41 @@ class ResourceManager(object):
|
||||
self.http_client = http_client
|
||||
|
||||
def find(self, **kwargs):
|
||||
return [i for i in self.list() if _check_items(i, kwargs.items())]
|
||||
return [i for i in self._list() if _check_items(i, kwargs.items())]
|
||||
|
||||
@staticmethod
|
||||
def _build_query_params(marker=None, limit=None, sort_keys=None,
|
||||
sort_dirs=None, fields=None, filters=None,
|
||||
scope=None, namespace=None):
|
||||
qparams = {}
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit and limit > 0:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
if fields:
|
||||
qparams['fields'] = ",".join(fields)
|
||||
|
||||
if filters:
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
if scope:
|
||||
qparams['scope'] = scope
|
||||
|
||||
if namespace:
|
||||
qparams['namespace'] = namespace
|
||||
|
||||
return ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
|
||||
def _ensure_not_empty(self, **kwargs):
|
||||
for name, value in kwargs.items():
|
||||
|
@ -13,12 +13,9 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import six
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class ActionExecution(base.Resource):
|
||||
resource_name = 'ActionExecution'
|
||||
@ -62,7 +59,8 @@ class ActionExecutionManager(base.ResourceManager):
|
||||
|
||||
return self._update('/action_executions/%s' % id, data)
|
||||
|
||||
def list(self, task_execution_id=None, limit=None):
|
||||
def list(self, task_execution_id=None, limit=None, marker='', fields=None,
|
||||
sort_keys='', sort_dirs='', **filters):
|
||||
url = '/action_executions'
|
||||
|
||||
if task_execution_id:
|
||||
@ -70,13 +68,14 @@ class ActionExecutionManager(base.ResourceManager):
|
||||
|
||||
url += "%s"
|
||||
|
||||
qparams = {}
|
||||
|
||||
if limit and limit > 0:
|
||||
qparams['limit'] = limit
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list(url % query_string, response_key='action_executions')
|
||||
|
||||
|
@ -12,14 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import six
|
||||
|
||||
from keystoneauth1 import exceptions
|
||||
from mistralclient.api import base
|
||||
from mistralclient import utils
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Action(base.Resource):
|
||||
resource_name = 'Action'
|
||||
@ -75,26 +71,16 @@ class ActionManager(base.ResourceManager):
|
||||
for resource_data in base.extract_json(resp, 'actions')]
|
||||
|
||||
def list(self, marker='', limit=None, sort_keys='', sort_dirs='',
|
||||
**filters):
|
||||
qparams = {}
|
||||
fields='', **filters):
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit and limit > 0:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list(
|
||||
'/actions%s' % query_string,
|
||||
|
@ -54,8 +54,19 @@ class CronTriggerManager(base.ResourceManager):
|
||||
|
||||
return self._create('/cron_triggers', data)
|
||||
|
||||
def list(self):
|
||||
return self._list('/cron_triggers', response_key='cron_triggers')
|
||||
def list(self, marker='', limit=None, sort_keys='', fields='',
|
||||
sort_dirs='', **filters):
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list('/cron_triggers%s' % query_string,
|
||||
response_key='cron_triggers')
|
||||
|
||||
def get(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
@ -13,7 +13,6 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
import six
|
||||
|
||||
from mistralclient.api import base
|
||||
@ -71,8 +70,19 @@ class EnvironmentManager(base.ResourceManager):
|
||||
|
||||
return self._update('/environments', kwargs)
|
||||
|
||||
def list(self):
|
||||
return self._list('/environments', response_key='environments')
|
||||
def list(self, marker='', limit=None, sort_keys='', sort_dirs='',
|
||||
fields='', **filters):
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list('/environments%s' % query_string,
|
||||
response_key='environments')
|
||||
|
||||
def get(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
@ -47,8 +47,19 @@ class EventTriggerManager(base.ResourceManager):
|
||||
|
||||
return self._create('/event_triggers', data)
|
||||
|
||||
def list(self):
|
||||
return self._list('/event_triggers', response_key='event_triggers')
|
||||
def list(self, marker='', limit=None, sort_keys='', sort_dirs='',
|
||||
fields='', **filters):
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list('/event_triggers%s' % query_string,
|
||||
response_key='event_triggers')
|
||||
|
||||
def get(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
@ -14,16 +14,13 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Execution(base.Resource):
|
||||
resource_name = 'Execution'
|
||||
|
||||
@ -79,29 +76,18 @@ class ExecutionManager(base.ResourceManager):
|
||||
return self._update('/executions/%s' % id, data)
|
||||
|
||||
def list(self, task=None, marker='', limit=None, sort_keys='',
|
||||
sort_dirs='', **filters):
|
||||
qparams = {}
|
||||
|
||||
sort_dirs='', fields='', **filters):
|
||||
if task:
|
||||
qparams['task_execution_id'] = task
|
||||
filters['task_execution_id'] = task
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit and limit > 0:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list(
|
||||
'/executions%s' % query_string,
|
||||
@ -116,11 +102,9 @@ class ExecutionManager(base.ResourceManager):
|
||||
def delete(self, id, force=None):
|
||||
self._ensure_not_empty(id=id)
|
||||
qparams = {}
|
||||
|
||||
if force:
|
||||
qparams['force'] = True
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
query_string = self._build_query_params(filters=qparams)
|
||||
|
||||
self._delete('/executions/%s%s' % (id, query_string))
|
||||
|
@ -14,12 +14,9 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import six
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Task(base.Resource):
|
||||
resource_name = 'Task'
|
||||
@ -29,7 +26,7 @@ class TaskManager(base.ResourceManager):
|
||||
resource_class = Task
|
||||
|
||||
def list(self, workflow_execution_id=None, marker='', limit=None,
|
||||
sort_keys='', sort_dirs='', fields=[], **filters):
|
||||
sort_keys='', sort_dirs='', fields=None, **filters):
|
||||
url = '/tasks'
|
||||
|
||||
if workflow_execution_id:
|
||||
@ -37,28 +34,14 @@ class TaskManager(base.ResourceManager):
|
||||
|
||||
url += '%s'
|
||||
|
||||
qparams = {}
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit and limit > 0:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
if fields:
|
||||
qparams['fields'] = ",".join(fields)
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list(url % query_string, response_key='tasks')
|
||||
|
||||
|
@ -82,12 +82,20 @@ class WorkbookManager(base.ResourceManager):
|
||||
|
||||
return self.resource_class(self, base.extract_json(resp, None))
|
||||
|
||||
def list(self, namespace=''):
|
||||
return self._list(
|
||||
self._get_workbooks_url(None, namespace),
|
||||
response_key='workbooks'
|
||||
def list(self, namespace='', marker='', limit=None, sort_keys='',
|
||||
sort_dirs='', fields='', **filters):
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
filters=filters,
|
||||
namespace=namespace
|
||||
)
|
||||
|
||||
return self._list('/workbooks{}'.format(query_string),
|
||||
response_key='workbooks')
|
||||
|
||||
def get(self, name, namespace=''):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
|
@ -13,16 +13,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import six
|
||||
|
||||
from keystoneauth1 import exceptions
|
||||
from mistralclient.api import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Workflow(base.Resource):
|
||||
resource_name = 'Workflow'
|
||||
|
||||
@ -80,29 +75,18 @@ class WorkflowManager(base.ResourceManager):
|
||||
for resource_data in base.extract_json(resp, 'workflows')]
|
||||
|
||||
def list(self, namespace='', marker='', limit=None, sort_keys='',
|
||||
sort_dirs='', **filters):
|
||||
qparams = {}
|
||||
|
||||
sort_dirs='', fields='', **filters):
|
||||
if namespace:
|
||||
qparams['namespace'] = namespace
|
||||
filters['namespace'] = namespace
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit and limit > 0:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
query_string = self._build_query_params(
|
||||
marker=marker,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
fields=fields,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
return self._list(
|
||||
'/workflows%s' % query_string,
|
||||
|
@ -25,72 +25,54 @@ from mistralclient import utils
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def format_list(action_ex=None):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Workflow name',
|
||||
'Workflow namespace',
|
||||
'Task name',
|
||||
'Task ID',
|
||||
'State',
|
||||
'Accepted',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
class ActionExecutionFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('id', 'ID'),
|
||||
('name', 'Name'),
|
||||
('workflow_name', 'Workflow name'),
|
||||
('workflow_namespace', 'Workflow namespace'),
|
||||
('task_name', 'Task name'),
|
||||
('task_execution_id', 'Task ID'),
|
||||
('state', 'State'),
|
||||
('state_info', 'State info'),
|
||||
('accepted', 'Accepted'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at'),
|
||||
]
|
||||
|
||||
if action_ex:
|
||||
data = (
|
||||
action_ex.id,
|
||||
action_ex.name,
|
||||
action_ex.workflow_name,
|
||||
action_ex.workflow_namespace,
|
||||
action_ex.task_name if hasattr(action_ex, 'task_name') else None,
|
||||
action_ex.task_execution_id,
|
||||
action_ex.state,
|
||||
action_ex.accepted,
|
||||
action_ex.created_at,
|
||||
action_ex.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
LIST_COLUMN_FIELD_NAMES = [c[0] for c in COLUMNS if c[0] != 'state_info']
|
||||
LIST_COLUMN_HEADING_NAMES = [c[1] for c in COLUMNS if c[0] != 'state_info']
|
||||
|
||||
return columns, data
|
||||
@staticmethod
|
||||
def format(action_ex=None, lister=False):
|
||||
if lister:
|
||||
columns = ActionExecutionFormatter.LIST_COLUMN_HEADING_NAMES
|
||||
else:
|
||||
columns = ActionExecutionFormatter.headings()
|
||||
if action_ex:
|
||||
if hasattr(action_ex, 'task_name'):
|
||||
task_name = action_ex.task_name
|
||||
else:
|
||||
task_name = None
|
||||
data = (
|
||||
action_ex.id,
|
||||
action_ex.name,
|
||||
action_ex.workflow_name,
|
||||
action_ex.workflow_namespace,
|
||||
task_name,
|
||||
action_ex.task_execution_id,
|
||||
action_ex.state,)
|
||||
if not lister:
|
||||
data += (action_ex.state_info,)
|
||||
data += (
|
||||
action_ex.accepted,
|
||||
action_ex.created_at,
|
||||
action_ex.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
|
||||
|
||||
def format(action_ex=None):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Workflow name',
|
||||
'Workflow namespace',
|
||||
'Task name',
|
||||
'Task ID',
|
||||
'State',
|
||||
'State info',
|
||||
'Accepted',
|
||||
'Created at',
|
||||
'Updated at',
|
||||
)
|
||||
|
||||
if action_ex:
|
||||
data = (
|
||||
action_ex.id,
|
||||
action_ex.name,
|
||||
action_ex.workflow_name,
|
||||
action_ex.workflow_namespace,
|
||||
action_ex.task_name if hasattr(action_ex, 'task_name') else None,
|
||||
action_ex.task_execution_id,
|
||||
action_ex.state,
|
||||
action_ex.state_info,
|
||||
action_ex.accepted,
|
||||
action_ex.created_at,
|
||||
action_ex.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
return columns, data
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
@ -166,18 +148,18 @@ class Create(command.ShowOne):
|
||||
)
|
||||
|
||||
if not parsed_args.run_sync and parsed_args.save_result:
|
||||
return format(action_ex)
|
||||
return ActionExecutionFormatter.format(action_ex)
|
||||
else:
|
||||
self.app.stdout.write("%s\n" % action_ex.output)
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
class List(base.MistralExecutionLister):
|
||||
"""List all Action executions."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return ActionExecutionFormatter.format_list
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(List, self).get_parser(prog_name)
|
||||
@ -187,33 +169,21 @@ class List(base.MistralLister):
|
||||
nargs='?',
|
||||
help='Task execution ID.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
help='Maximum number of action-executions to return in a single '
|
||||
'result. limit is set to %s by default. Use --limit -1 to '
|
||||
'fetch the full result set.' % base.DEFAULT_LIMIT,
|
||||
nargs='?'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
if parsed_args.limit is None:
|
||||
parsed_args.limit = base.DEFAULT_LIMIT
|
||||
|
||||
LOG.info(
|
||||
"limit is set to %s by default. Set "
|
||||
"the limit explicitly using \'--limit\', if required. "
|
||||
"Use \'--limit\' -1 to fetch the full result set.",
|
||||
base.DEFAULT_LIMIT
|
||||
)
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.action_executions.list(
|
||||
parsed_args.task_execution_id,
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
# TODO(bobh) - Uncomment when the fix for bug 1800322 merges
|
||||
# fields=ActionExecutionFormatter.LIST_COLUMN_FIELD_NAMES,
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
@ -234,7 +204,7 @@ class Get(command.ShowOne):
|
||||
parsed_args.action_execution
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
return ActionExecutionFormatter.format(execution)
|
||||
|
||||
|
||||
class Update(command.ShowOne):
|
||||
@ -270,7 +240,7 @@ class Update(command.ShowOne):
|
||||
output
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
return ActionExecutionFormatter.format(execution)
|
||||
|
||||
|
||||
class GetOutput(command.Command):
|
||||
|
@ -22,70 +22,66 @@ from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(action=None):
|
||||
return format(action, lister=True)
|
||||
class ActionFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('id', 'ID'),
|
||||
('name', 'Name'),
|
||||
('is_system', 'Is system'),
|
||||
('input', 'Input'),
|
||||
('description', 'Description'),
|
||||
('tags', 'Tags'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at')
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def format(action=None, lister=False):
|
||||
if action:
|
||||
tags = getattr(action, 'tags', None) or []
|
||||
input_ = action.input if not lister else base.cut(action.input)
|
||||
desc = (action.description if not lister
|
||||
else base.cut(action.description))
|
||||
|
||||
def format(action=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Is system',
|
||||
'Input',
|
||||
'Description',
|
||||
'Tags',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
data = (
|
||||
action.id,
|
||||
action.name,
|
||||
action.is_system,
|
||||
input_,
|
||||
desc,
|
||||
base.wrap(', '.join(tags)) or '<none>',
|
||||
action.created_at,
|
||||
)
|
||||
if hasattr(action, 'updated_at'):
|
||||
data += (action.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
|
||||
if action:
|
||||
tags = getattr(action, 'tags', None) or []
|
||||
input = action.input if not lister else base.cut(action.input)
|
||||
desc = (action.description if not lister
|
||||
else base.cut(action.description))
|
||||
|
||||
data = (
|
||||
action.id,
|
||||
action.name,
|
||||
action.is_system,
|
||||
input,
|
||||
desc,
|
||||
base.wrap(', '.join(tags)) or '<none>',
|
||||
action.created_at,
|
||||
)
|
||||
|
||||
if hasattr(action, 'updated_at'):
|
||||
data += (action.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
data = (tuple('' for _ in range(len(ActionFormatter.COLUMNS))),)
|
||||
|
||||
return columns, data
|
||||
return ActionFormatter.headings(), data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all actions."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return ActionFormatter.format_list
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(List, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.actions.list(
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=ActionFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
@ -104,7 +100,7 @@ class Get(command.ShowOne):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
action = mistral_client.actions.get(parsed_args.action)
|
||||
|
||||
return format(action)
|
||||
return ActionFormatter.format(action)
|
||||
|
||||
|
||||
class Create(base.MistralLister):
|
||||
@ -131,7 +127,7 @@ class Create(base.MistralLister):
|
||||
raise RuntimeError("Provide action definition file.")
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return ActionFormatter.format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
scope = 'public' if parsed_args.public else 'private'
|
||||
@ -190,7 +186,7 @@ class Update(base.MistralLister):
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return ActionFormatter.format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
scope = 'public' if parsed_args.public else 'private'
|
||||
@ -223,8 +219,8 @@ class GetDefinition(command.Command):
|
||||
|
||||
class Validate(command.ShowOne):
|
||||
"""Validate action."""
|
||||
|
||||
def _format(self, result=None):
|
||||
@staticmethod
|
||||
def _format(result=None):
|
||||
columns = ('Valid', 'Error')
|
||||
|
||||
if result:
|
||||
|
@ -24,12 +24,75 @@ import six
|
||||
DEFAULT_LIMIT = 100
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class MistralFormatter(object):
|
||||
COLUMNS = []
|
||||
|
||||
@classmethod
|
||||
def fields(cls):
|
||||
return [c[0] for c in cls.COLUMNS]
|
||||
|
||||
@classmethod
|
||||
def headings(cls):
|
||||
return [c[1] for c in cls.COLUMNS]
|
||||
|
||||
@classmethod
|
||||
def format_list(cls, instance=None):
|
||||
return cls.format(instance, lister=True)
|
||||
|
||||
@staticmethod
|
||||
def format(instance=None, lister=False):
|
||||
pass
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class MistralLister(command.Lister):
|
||||
@abc.abstractmethod
|
||||
def _get_format_function(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_parser(self, parsed_args):
|
||||
parser = super(MistralLister, self).get_parser(parsed_args)
|
||||
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
type=str,
|
||||
help='The last execution uuid of the previous page, displays list '
|
||||
'of executions after "marker".',
|
||||
default='',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
help='Maximum number of entries to return in a single result. ',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort_keys',
|
||||
help='Comma-separated list of sort keys to sort results by. '
|
||||
'Default: created_at. '
|
||||
'Example: mistral execution-list --sort_keys=id,description',
|
||||
default='created_at',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort_dirs',
|
||||
help='Comma-separated list of sort directions. Default: asc. '
|
||||
'Example: mistral execution-list --sort_keys=id,description '
|
||||
'--sort_dirs=asc,desc',
|
||||
default='asc',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_resources(self, parsed_args):
|
||||
"""Gets a list of API resources (e.g. using client)."""
|
||||
@ -56,6 +119,48 @@ class MistralLister(command.Lister):
|
||||
return f()
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class MistralExecutionLister(MistralLister):
|
||||
def get_parser(self, parsed_args):
|
||||
parser = super(MistralExecutionLister, self).get_parser(parsed_args)
|
||||
parser.set_defaults(limit=DEFAULT_LIMIT)
|
||||
parser.add_argument(
|
||||
'--oldest',
|
||||
help='Display the executions starting from the oldest entries '
|
||||
'instead of the newest',
|
||||
default=False,
|
||||
action='store_true'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self._validate_parsed_args(parsed_args)
|
||||
|
||||
f = self._get_format_function()
|
||||
|
||||
reverse_results = False
|
||||
if (parsed_args.marker == '' and parsed_args.sort_dirs == 'asc' and
|
||||
parsed_args.sort_keys == 'created_at' and
|
||||
not parsed_args.oldest):
|
||||
reverse_results = True
|
||||
parsed_args.sort_dirs = 'desc'
|
||||
|
||||
ret = self._get_resources(parsed_args)
|
||||
if not isinstance(ret, list):
|
||||
ret = [ret]
|
||||
|
||||
if reverse_results:
|
||||
ret.reverse()
|
||||
|
||||
data = [f(r)[1] for r in ret]
|
||||
|
||||
if data:
|
||||
return f()[0], data
|
||||
else:
|
||||
return f()
|
||||
|
||||
|
||||
def cut(string, length=25):
|
||||
if string and len(string) > length:
|
||||
return "%s..." % string[:length]
|
||||
|
@ -22,60 +22,67 @@ from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(trigger=None):
|
||||
return format(trigger, lister=True)
|
||||
|
||||
|
||||
def format(trigger=None, lister=False):
|
||||
columns = (
|
||||
'Name',
|
||||
'Workflow',
|
||||
'Params',
|
||||
'Pattern',
|
||||
class CronTriggerFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('name', 'Name'),
|
||||
('workflow_name', 'Workflow'),
|
||||
('workflow_params', 'Params'),
|
||||
('pattern', 'Pattern'),
|
||||
# TODO(rakhmerov): Uncomment when passwords are handled properly.
|
||||
# TODO(rakhmerov): Add 'Workflow input' column.
|
||||
'Next execution time',
|
||||
'Remaining executions',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
('next_execution_time', 'Next execution time'),
|
||||
('remaining_executions', 'Remaining executions'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at')
|
||||
]
|
||||
|
||||
if trigger:
|
||||
# TODO(rakhmerov): Add following here:
|
||||
# TODO(rakhmerov): wf_input = trigger.workflow_input if not lister
|
||||
# TODO(rakhmerov:): else base.cut(trigger.workflow_input)
|
||||
@staticmethod
|
||||
def format(trigger=None, lister=False):
|
||||
if trigger:
|
||||
# TODO(rakhmerov): Add following here:
|
||||
# TODO(rakhmerov): wf_input = trigger.workflow_input if not lister
|
||||
# TODO(rakhmerov:): else base.cut(trigger.workflow_input)
|
||||
|
||||
data = (
|
||||
trigger.name,
|
||||
trigger.workflow_name,
|
||||
trigger.workflow_params,
|
||||
trigger.pattern,
|
||||
# TODO(rakhmerov): Uncomment when passwords are handled properly.
|
||||
# TODo(rakhmerov): Add 'wf_input' here.
|
||||
trigger.next_execution_time,
|
||||
trigger.remaining_executions,
|
||||
trigger.created_at,
|
||||
)
|
||||
data = (
|
||||
trigger.name,
|
||||
trigger.workflow_name,
|
||||
trigger.workflow_params,
|
||||
trigger.pattern,
|
||||
# TODO(rakhmerov): Uncomment when passwords are handled
|
||||
# properly.
|
||||
# TODo(rakhmerov): Add 'wf_input' here.
|
||||
trigger.next_execution_time,
|
||||
trigger.remaining_executions,
|
||||
trigger.created_at,
|
||||
)
|
||||
|
||||
if hasattr(trigger, 'updated_at'):
|
||||
data += (trigger.updated_at,)
|
||||
if hasattr(trigger, 'updated_at'):
|
||||
data += (trigger.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
data = (tuple('' for _ in
|
||||
range(len(CronTriggerFormatter.COLUMNS))),)
|
||||
|
||||
return columns, data
|
||||
return CronTriggerFormatter.headings(), data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all cron triggers."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return CronTriggerFormatter.format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.cron_triggers.list()
|
||||
return mistral_client.cron_triggers.list(
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=CronTriggerFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
@ -91,7 +98,7 @@ class Get(command.ShowOne):
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return format(mistral_client.cron_triggers.get(
|
||||
return CronTriggerFormatter.format(mistral_client.cron_triggers.get(
|
||||
parsed_args.cron_trigger
|
||||
))
|
||||
|
||||
@ -189,7 +196,7 @@ class Create(command.ShowOne):
|
||||
parsed_args.count
|
||||
)
|
||||
|
||||
return format(trigger)
|
||||
return CronTriggerFormatter.format(trigger)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
|
@ -21,78 +21,63 @@ from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(environment=None):
|
||||
columns = (
|
||||
'Name',
|
||||
'Description',
|
||||
'Scope',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
class EnvironmentFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('name', 'Name'),
|
||||
('description', 'Description'),
|
||||
('variables', 'Variables'),
|
||||
('scope', 'Scope'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at'),
|
||||
]
|
||||
LIST_COLUMN_FIELD_NAMES = [c[0] for c in COLUMNS if c[0] != 'variables']
|
||||
LIST_COLUMN_HEADING_NAMES = [c[1] for c in COLUMNS if c[0] != 'variables']
|
||||
|
||||
if environment:
|
||||
data = (
|
||||
environment.name,
|
||||
environment.description,
|
||||
environment.scope,
|
||||
environment.created_at,
|
||||
)
|
||||
|
||||
if hasattr(environment, 'updated_at'):
|
||||
data += (environment.updated_at or '<none>',)
|
||||
@staticmethod
|
||||
def format(environment=None, lister=False):
|
||||
if lister:
|
||||
columns = EnvironmentFormatter.LIST_COLUMN_HEADING_NAMES
|
||||
else:
|
||||
data += (None,)
|
||||
columns = EnvironmentFormatter.headings()
|
||||
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def format(environment=None):
|
||||
columns = (
|
||||
'Name',
|
||||
'Description',
|
||||
'Variables',
|
||||
'Scope',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if environment:
|
||||
data = (environment.name,)
|
||||
|
||||
if hasattr(environment, 'description'):
|
||||
data += (environment.description or '<none>',)
|
||||
if environment:
|
||||
data = (
|
||||
environment.name,)
|
||||
if hasattr(environment, 'description'):
|
||||
data += (environment.description or '<none>',)
|
||||
else:
|
||||
data += (None,)
|
||||
if not lister:
|
||||
data += (json.dumps(environment.variables, indent=4),)
|
||||
data += (
|
||||
environment.scope,
|
||||
environment.created_at,)
|
||||
if hasattr(environment, 'updated_at'):
|
||||
data += (environment.updated_at or '<none>',)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data += (None,)
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
|
||||
data += (
|
||||
json.dumps(environment.variables, indent=4),
|
||||
environment.scope,
|
||||
environment.created_at,
|
||||
)
|
||||
|
||||
if hasattr(environment, 'updated_at'):
|
||||
data += (environment.updated_at or '<none>',)
|
||||
else:
|
||||
data += (None,)
|
||||
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all environments."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return EnvironmentFormatter.format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.environments.list()
|
||||
return mistral_client.environments.list(
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=EnvironmentFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
@ -132,7 +117,7 @@ class Get(command.ShowOne):
|
||||
|
||||
return columns, data
|
||||
|
||||
return format(environment)
|
||||
return EnvironmentFormatter.format(environment)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
@ -155,7 +140,7 @@ class Create(command.ShowOne):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
environment = mistral_client.environments.create(**data)
|
||||
|
||||
return format(environment)
|
||||
return EnvironmentFormatter.format(environment)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
@ -203,4 +188,4 @@ class Update(command.ShowOne):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
environment = mistral_client.environments.update(**data)
|
||||
|
||||
return format(environment)
|
||||
return EnvironmentFormatter.format(environment)
|
||||
|
@ -19,54 +19,60 @@ from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(trigger=None):
|
||||
return format(trigger, lister=True)
|
||||
class EventTriggerFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('id', 'ID'),
|
||||
('name', 'Name'),
|
||||
('workflow_id', 'Workflow ID'),
|
||||
('workflow_params', 'Params'),
|
||||
('exchange', 'Exchange'),
|
||||
('topic', 'Topic'),
|
||||
('event', 'Event'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at')
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def format(trigger=None, lister=False):
|
||||
if trigger:
|
||||
data = (
|
||||
trigger.id,
|
||||
trigger.name,
|
||||
trigger.workflow_id,
|
||||
trigger.workflow_params,
|
||||
trigger.exchange,
|
||||
trigger.topic,
|
||||
trigger.event,
|
||||
trigger.created_at,
|
||||
)
|
||||
|
||||
def format(trigger=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Workflow ID',
|
||||
'Params',
|
||||
'Exchange',
|
||||
'Topic',
|
||||
'Event',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if trigger:
|
||||
data = (
|
||||
trigger.id,
|
||||
trigger.name,
|
||||
trigger.workflow_id,
|
||||
trigger.workflow_params,
|
||||
trigger.exchange,
|
||||
trigger.topic,
|
||||
trigger.event,
|
||||
trigger.created_at,
|
||||
)
|
||||
|
||||
if hasattr(trigger, 'updated_at'):
|
||||
data += (trigger.updated_at,)
|
||||
if hasattr(trigger, 'updated_at'):
|
||||
data += (trigger.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
data = (tuple('' for _ in
|
||||
range(len(EventTriggerFormatter.COLUMNS))),)
|
||||
|
||||
return columns, data
|
||||
return EventTriggerFormatter.headings(), data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all event triggers."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return EventTriggerFormatter.format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.event_triggers.list()
|
||||
return mistral_client.event_triggers.list(
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=EventTriggerFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
@ -82,9 +88,8 @@ class Get(command.ShowOne):
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return format(mistral_client.event_triggers.get(
|
||||
parsed_args.event_trigger
|
||||
))
|
||||
return EventTriggerFormatter.format(mistral_client.event_triggers.get(
|
||||
parsed_args.event_trigger))
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
@ -140,7 +145,7 @@ class Create(command.ShowOne):
|
||||
wf_params,
|
||||
)
|
||||
|
||||
return format(trigger)
|
||||
return EventTriggerFormatter.format(trigger)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
|
@ -29,54 +29,54 @@ from mistralclient import utils
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def format_list(execution=None):
|
||||
return format(execution, lister=True)
|
||||
class ExecutionFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('id', 'ID'),
|
||||
('workflow_id', 'Workflow ID'),
|
||||
('workflow_name', 'Workflow name'),
|
||||
('workflow_namespace', 'Workflow namespace'),
|
||||
('description', 'Description'),
|
||||
('task_execution_id', 'Task Execution ID'),
|
||||
('root_execution_id', 'Root Execution ID'),
|
||||
('state', 'State'),
|
||||
('state_info', 'State info'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at'),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def format(execution=None, lister=False):
|
||||
# TODO(nmakhotkin) Add parent task id when it's implemented in API.
|
||||
|
||||
if execution:
|
||||
state_info = (execution.state_info if not lister
|
||||
else base.cut(execution.state_info))
|
||||
|
||||
data = (
|
||||
execution.id,
|
||||
execution.workflow_id,
|
||||
execution.workflow_name,
|
||||
execution.workflow_namespace,
|
||||
execution.description,
|
||||
execution.task_execution_id or '<none>',
|
||||
execution.root_execution_id or '<none>',
|
||||
execution.state,
|
||||
state_info,
|
||||
execution.created_at,
|
||||
execution.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('' for _ in
|
||||
range(len(ExecutionFormatter.COLUMNS))),)
|
||||
|
||||
return ExecutionFormatter.headings(), data
|
||||
|
||||
|
||||
def format(execution=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Workflow ID',
|
||||
'Workflow name',
|
||||
'Workflow namespace',
|
||||
'Description',
|
||||
'Task Execution ID',
|
||||
'Root Execution ID',
|
||||
'State',
|
||||
'State info',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
# TODO(nmakhotkin) Add parent task id when it's implemented in API.
|
||||
|
||||
if execution:
|
||||
state_info = (execution.state_info if not lister
|
||||
else base.cut(execution.state_info))
|
||||
|
||||
data = (
|
||||
execution.id,
|
||||
execution.workflow_id,
|
||||
execution.workflow_name,
|
||||
execution.workflow_namespace,
|
||||
execution.description,
|
||||
execution.task_execution_id or '<none>',
|
||||
execution.root_execution_id or '<none>',
|
||||
execution.state,
|
||||
state_info,
|
||||
execution.created_at,
|
||||
execution.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
class List(base.MistralExecutionLister):
|
||||
"""List all executions."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return ExecutionFormatter.format_list
|
||||
|
||||
def get_parser(self, parsed_args):
|
||||
parser = super(List, self).get_parser(parsed_args)
|
||||
@ -86,58 +86,10 @@ class List(base.MistralLister):
|
||||
help="Parent task execution ID associated with workflow "
|
||||
"execution list.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
type=str,
|
||||
help='The last execution uuid of the previous page, displays list '
|
||||
'of executions after "marker".',
|
||||
default='',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
help='Maximum number of executions to return in a single result. '
|
||||
'limit is set to %s by default. Use --limit -1 to fetch the '
|
||||
'full result set.' % base.DEFAULT_LIMIT,
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort_keys',
|
||||
help='Comma-separated list of sort keys to sort results by. '
|
||||
'Default: created_at. '
|
||||
'Example: mistral execution-list --sort_keys=id,description',
|
||||
default='created_at',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort_dirs',
|
||||
help='Comma-separated list of sort directions. Default: asc. '
|
||||
'Example: mistral execution-list --sort_keys=id,description '
|
||||
'--sort_dirs=asc,desc',
|
||||
default='asc',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
if parsed_args.limit is None:
|
||||
parsed_args.limit = base.DEFAULT_LIMIT
|
||||
|
||||
LOG.info(
|
||||
"limit is set to %s by default. Set "
|
||||
"the limit explicitly using \'--limit\', if required. "
|
||||
"Use \'--limit\' -1 to fetch the full result set.",
|
||||
base.DEFAULT_LIMIT
|
||||
)
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.executions.list(
|
||||
@ -146,6 +98,7 @@ class List(base.MistralLister):
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=ExecutionFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
@ -164,7 +117,7 @@ class Get(command.ShowOne):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
execution = mistral_client.executions.get(parsed_args.execution)
|
||||
|
||||
return format(execution)
|
||||
return ExecutionFormatter.format(execution)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
@ -235,7 +188,7 @@ class Create(command.ShowOne):
|
||||
**params
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
return ExecutionFormatter.format(execution)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
@ -322,7 +275,7 @@ class Update(command.ShowOne):
|
||||
env=env
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
return ExecutionFormatter.format(execution)
|
||||
|
||||
|
||||
class GetInput(command.Command):
|
||||
|
@ -19,46 +19,44 @@ from mistralclient.commands.v2 import base
|
||||
from mistralclient import exceptions
|
||||
|
||||
|
||||
def format_list(member=None):
|
||||
return format(member, lister=True)
|
||||
class MemberFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('resource_id', 'Resource ID'),
|
||||
('resource_type', 'Resource Type'),
|
||||
('project_id', 'Resource Owner'),
|
||||
('member_id', 'Member ID'),
|
||||
('status', 'Status'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at')
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def format(member=None, lister=False):
|
||||
if member:
|
||||
data = (
|
||||
member.resource_id,
|
||||
member.resource_type,
|
||||
member.project_id,
|
||||
member.member_id,
|
||||
member.status,
|
||||
member.created_at,
|
||||
)
|
||||
|
||||
def format(member=None, lister=False):
|
||||
columns = (
|
||||
'Resource ID',
|
||||
'Resource Type',
|
||||
'Resource Owner',
|
||||
'Member ID',
|
||||
'Status',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if member:
|
||||
data = (
|
||||
member.resource_id,
|
||||
member.resource_type,
|
||||
member.project_id,
|
||||
member.member_id,
|
||||
member.status,
|
||||
member.created_at,
|
||||
)
|
||||
|
||||
if hasattr(member, 'updated_at'):
|
||||
data += (member.updated_at,)
|
||||
if hasattr(member, 'updated_at'):
|
||||
data += (member.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
data = (tuple('' for _ in range(len(MemberFormatter.COLUMNS))),)
|
||||
|
||||
return columns, data
|
||||
return MemberFormatter.headings(), data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all members."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return MemberFormatter.format_list
|
||||
|
||||
def get_parser(self, parsed_args):
|
||||
parser = super(List, self).get_parser(parsed_args)
|
||||
@ -114,7 +112,7 @@ class Get(command.ShowOne):
|
||||
parsed_args.member_id,
|
||||
)
|
||||
|
||||
return format(member)
|
||||
return MemberFormatter.format(member)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
@ -146,7 +144,7 @@ class Create(command.ShowOne):
|
||||
parsed_args.member_id,
|
||||
)
|
||||
|
||||
return format(member)
|
||||
return MemberFormatter.format(member)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
@ -232,4 +230,4 @@ class Update(command.ShowOne):
|
||||
status=parsed_args.status
|
||||
)
|
||||
|
||||
return format(member)
|
||||
return MemberFormatter.format(member)
|
||||
|
@ -27,7 +27,7 @@ from mistralclient import utils
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TaskFormatter(object):
|
||||
class TaskFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('id', 'ID'),
|
||||
('name', 'Name'),
|
||||
@ -40,13 +40,6 @@ class TaskFormatter(object):
|
||||
('updated_at', 'Updated at'),
|
||||
]
|
||||
|
||||
COLUMN_FIELD_NAMES = list(zip(*COLUMNS))[0]
|
||||
COLUMN_HEADING_NAMES = list(zip(*COLUMNS))[1]
|
||||
|
||||
@staticmethod
|
||||
def format_list(task=None):
|
||||
return TaskFormatter.format(task, lister=True)
|
||||
|
||||
@staticmethod
|
||||
def format(task=None, lister=False):
|
||||
if task:
|
||||
@ -67,10 +60,10 @@ class TaskFormatter(object):
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(TaskFormatter.COLUMNS))),)
|
||||
|
||||
return TaskFormatter.COLUMN_HEADING_NAMES, data
|
||||
return TaskFormatter.headings(), data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
class List(base.MistralExecutionLister):
|
||||
"""List all tasks."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
@ -81,43 +74,21 @@ class List(base.MistralLister):
|
||||
nargs='?',
|
||||
help='Workflow execution ID associated with list of Tasks.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
help='Maximum number of tasks to return in a single result. '
|
||||
'limit is set to %s by default. Use --limit -1 to fetch the '
|
||||
'full result set.' % base.DEFAULT_LIMIT,
|
||||
nargs='?'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return TaskFormatter.format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
if parsed_args.limit is None:
|
||||
parsed_args.limit = base.DEFAULT_LIMIT
|
||||
|
||||
LOG.info(
|
||||
"limit is set to %s by default. Set "
|
||||
"the limit explicitly using \'--limit\', if required. "
|
||||
"Use \'--limit\' -1 to fetch the full result set.",
|
||||
base.DEFAULT_LIMIT
|
||||
)
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.tasks.list(
|
||||
parsed_args.workflow_execution,
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
fields=TaskFormatter.COLUMN_FIELD_NAMES,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=TaskFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
@ -21,45 +21,54 @@ from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format(workbook=None):
|
||||
columns = (
|
||||
'Name',
|
||||
'Namespace',
|
||||
'Tags',
|
||||
'Scope',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
class WorkbookFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('name', 'Name'),
|
||||
('namespace', 'Namespace'),
|
||||
('tags', 'Tags'),
|
||||
('scope', 'Scope'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at')
|
||||
]
|
||||
|
||||
if workbook:
|
||||
data = (
|
||||
workbook.name,
|
||||
workbook.namespace,
|
||||
base.wrap(', '.join(workbook.tags or '')) or '<none>',
|
||||
workbook.scope,
|
||||
workbook.created_at,
|
||||
)
|
||||
@staticmethod
|
||||
def format(workbook=None, lister=False):
|
||||
if workbook:
|
||||
data = (
|
||||
workbook.name,
|
||||
workbook.namespace,
|
||||
base.wrap(', '.join(workbook.tags or '')) or '<none>',
|
||||
workbook.scope,
|
||||
workbook.created_at,
|
||||
)
|
||||
|
||||
if hasattr(workbook, 'updated_at'):
|
||||
data += (workbook.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
|
||||
if hasattr(workbook, 'updated_at'):
|
||||
data += (workbook.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
data = (tuple('' for _ in range(len(WorkbookFormatter.COLUMNS))),)
|
||||
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
return WorkbookFormatter.headings(), data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all workbooks."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format
|
||||
return WorkbookFormatter.format
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.workbooks.list()
|
||||
return mistral_client.workbooks.list(
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=WorkbookFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
@ -86,7 +95,7 @@ class Get(command.ShowOne):
|
||||
parsed_args.namespace
|
||||
)
|
||||
|
||||
return format(workbook)
|
||||
return WorkbookFormatter.format(workbook)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
@ -125,7 +134,7 @@ class Create(command.ShowOne):
|
||||
scope=scope
|
||||
)
|
||||
|
||||
return format(workbook)
|
||||
return WorkbookFormatter.format(workbook)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
@ -190,7 +199,7 @@ class Update(command.ShowOne):
|
||||
scope=scope
|
||||
)
|
||||
|
||||
return format(workbook)
|
||||
return WorkbookFormatter.format(workbook)
|
||||
|
||||
|
||||
class GetDefinition(command.Command):
|
||||
@ -212,8 +221,8 @@ class GetDefinition(command.Command):
|
||||
|
||||
class Validate(command.ShowOne):
|
||||
"""Validate workbook."""
|
||||
|
||||
def _format(self, result=None):
|
||||
@staticmethod
|
||||
def _format(result=None):
|
||||
columns = ('Valid', 'Error')
|
||||
|
||||
if result:
|
||||
|
@ -22,69 +22,65 @@ from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(workflow=None):
|
||||
return format(workflow, lister=True)
|
||||
class WorkflowFormatter(base.MistralFormatter):
|
||||
COLUMNS = [
|
||||
('id', 'ID'),
|
||||
('name', 'Name'),
|
||||
('namespace', 'Namespace'),
|
||||
('project_id', 'Project ID'),
|
||||
('tags', 'Tags'),
|
||||
('input', 'Input'),
|
||||
('scope', 'Scope'),
|
||||
('created_at', 'Created at'),
|
||||
('updated_at', 'Updated at')
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def format(workflow=None, lister=False):
|
||||
if workflow:
|
||||
tags = getattr(workflow, 'tags', None) or []
|
||||
|
||||
def format(workflow=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Namespace',
|
||||
'Project ID',
|
||||
'Tags',
|
||||
'Input',
|
||||
'Scope',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
data = (
|
||||
workflow.id,
|
||||
workflow.name,
|
||||
workflow.namespace,
|
||||
workflow.project_id,
|
||||
base.wrap(', '.join(tags)) or '<none>',
|
||||
workflow.input if not lister else base.cut(workflow.input),
|
||||
workflow.scope,
|
||||
workflow.created_at
|
||||
)
|
||||
|
||||
if workflow:
|
||||
tags = getattr(workflow, 'tags', None) or []
|
||||
|
||||
data = (
|
||||
workflow.id,
|
||||
workflow.name,
|
||||
workflow.namespace,
|
||||
workflow.project_id,
|
||||
base.wrap(', '.join(tags)) or '<none>',
|
||||
workflow.input if not lister else base.cut(workflow.input),
|
||||
workflow.scope,
|
||||
workflow.created_at
|
||||
)
|
||||
|
||||
if hasattr(workflow, 'updated_at'):
|
||||
data += (workflow.updated_at,)
|
||||
if hasattr(workflow, 'updated_at'):
|
||||
data += (workflow.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('' for _ in range(len(columns))),)
|
||||
data = (tuple('' for _ in range(len(WorkflowFormatter.COLUMNS))),)
|
||||
|
||||
return columns, data
|
||||
return WorkflowFormatter.headings(), data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all workflows."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return WorkflowFormatter.format_list
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(List, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.workflows.list(
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
fields=WorkflowFormatter.fields(),
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
@ -113,7 +109,7 @@ class Get(show.ShowOne):
|
||||
parsed_args.namespace
|
||||
)
|
||||
|
||||
return format(wf)
|
||||
return WorkflowFormatter.format(wf)
|
||||
|
||||
|
||||
class Create(base.MistralLister):
|
||||
@ -142,7 +138,7 @@ class Create(base.MistralLister):
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return WorkflowFormatter.format_list
|
||||
|
||||
def _validate_parsed_args(self, parsed_args):
|
||||
if not parsed_args.definition:
|
||||
@ -223,7 +219,7 @@ class Update(base.MistralLister):
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
return WorkflowFormatter.format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
scope = 'public' if parsed_args.public else 'private'
|
||||
@ -267,8 +263,8 @@ class GetDefinition(command.Command):
|
||||
|
||||
class Validate(show.ShowOne):
|
||||
"""Validate workflow."""
|
||||
|
||||
def _format(self, result=None):
|
||||
@staticmethod
|
||||
def _format(result=None):
|
||||
columns = ('Valid', 'Error')
|
||||
|
||||
if result:
|
||||
|
@ -225,7 +225,7 @@ class TestCLIExecutionsV2(base.BaseCommandTest):
|
||||
)
|
||||
|
||||
def test_list(self):
|
||||
self.client.executions.list.return_value = [EXEC, SUB_WF_EXEC]
|
||||
self.client.executions.list.return_value = [SUB_WF_EXEC, EXEC]
|
||||
|
||||
result = self.call(execution_cmd.List)
|
||||
|
||||
@ -239,28 +239,46 @@ class TestCLIExecutionsV2(base.BaseCommandTest):
|
||||
|
||||
self.call(execution_cmd.List)
|
||||
self.client.executions.list.assert_called_once_with(
|
||||
fields=execution_cmd.ExecutionFormatter.fields(),
|
||||
limit=100,
|
||||
marker='',
|
||||
sort_dirs='asc',
|
||||
sort_dirs='desc',
|
||||
sort_keys='created_at',
|
||||
task=None
|
||||
)
|
||||
|
||||
self.call(
|
||||
execution_cmd.List,
|
||||
app_args=[
|
||||
'--oldest'
|
||||
]
|
||||
)
|
||||
|
||||
self.client.executions.list.assert_called_with(
|
||||
fields=execution_cmd.ExecutionFormatter.fields(),
|
||||
limit=100,
|
||||
marker='',
|
||||
sort_keys='created_at',
|
||||
sort_dirs='asc',
|
||||
task=None
|
||||
)
|
||||
|
||||
self.call(
|
||||
execution_cmd.List,
|
||||
app_args=[
|
||||
'--limit', '5',
|
||||
'--sort_dirs', 'id, Workflow',
|
||||
'--sort_keys', 'desc',
|
||||
'--sort_keys', 'id, Workflow',
|
||||
'--sort_dirs', 'desc',
|
||||
'--marker', 'abc'
|
||||
]
|
||||
)
|
||||
|
||||
self.client.executions.list.assert_called_with(
|
||||
fields=execution_cmd.ExecutionFormatter.fields(),
|
||||
limit=5,
|
||||
marker='abc',
|
||||
sort_dirs='id, Workflow',
|
||||
sort_keys='desc',
|
||||
sort_keys='id, Workflow',
|
||||
sort_dirs='desc',
|
||||
task=None
|
||||
)
|
||||
|
||||
|
@ -61,7 +61,7 @@ class TestCLITasksV2(base.BaseCommandTest):
|
||||
self.assertEqual([EXPECTED_TASK_RESULT], result[1])
|
||||
self.assertEqual(
|
||||
self.client.tasks.list.call_args[1]["fields"],
|
||||
task_cmd.TaskFormatter.COLUMN_FIELD_NAMES
|
||||
task_cmd.TaskFormatter.fields()
|
||||
)
|
||||
|
||||
def test_list_with_workflow_execution(self):
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
critical:
|
||||
- |
|
||||
The default behavior of the action-execution-list, execution-list and
|
||||
task-list commands has been changed. Instead of returning the oldest
|
||||
N records (default 100 or --limit specified value) by default,
|
||||
they now return the most recent N records, when no other sort_dir,
|
||||
sort_key or marker values are provided. If the user specifies --oldest
|
||||
or any of the --marker, --sort_key or --sort_dir options, the new
|
||||
behavior is disabled and the commands will work according to the
|
||||
user-supplied options.
|
Loading…
Reference in New Issue
Block a user