Support resource deletion in batches

Support CLI commands like:

usage: mistral workflow-delete [-h] name [name ...]

Change-Id: I467ecfddf88d30e437d00de2671c2a0487557693
Implements: blueprint mistral-batch-deleting-support
This commit is contained in:
LingxianKong 2015-04-24 19:04:13 +08:00 committed by Renat Akhmerov
parent f73760f797
commit 3609a0d4f8
13 changed files with 170 additions and 28 deletions

View File

@ -22,6 +22,7 @@ from cliff import show
from mistralclient.api.v2 import actions
from mistralclient.commands.v2 import base
from mistralclient import utils
LOG = logging.getLogger(__name__)
@ -125,12 +126,18 @@ class Delete(command.Command):
def get_parser(self, prog_name):
parser = super(Delete, self).get_parser(prog_name)
parser.add_argument('name', help='Action name')
parser.add_argument('name', nargs='+', help='Name of action(s).')
return parser
def take_action(self, parsed_args):
actions.ActionManager(self.app.client).delete(parsed_args.name)
action_mgr = actions.ActionManager(self.app.client)
utils.do_action_on_many(
lambda s: action_mgr.delete(s),
parsed_args.name,
"Request to delete action %s has been accepted.",
"Unable to delete the specified action(s)."
)
class Update(base.MistralLister):

View File

@ -22,6 +22,7 @@ from cliff import show
from mistralclient.api.v2 import cron_triggers
from mistralclient.commands.v2 import base
from mistralclient import utils
LOG = logging.getLogger(__name__)
@ -160,11 +161,15 @@ class Delete(command.Command):
def get_parser(self, prog_name):
parser = super(Delete, self).get_parser(prog_name)
parser.add_argument('name', help='Cron trigger name')
parser.add_argument('name', nargs='+', help='Name of cron trigger(s).')
return parser
def take_action(self, parsed_args):
mgr = cron_triggers.CronTriggerManager(self.app.client)
mgr.delete(parsed_args.name)
utils.do_action_on_many(
lambda s: mgr.delete(s),
parsed_args.name,
"Request to delete cron trigger %s has been accepted.",
"Unable to delete the specified cron trigger(s)."
)

View File

@ -22,6 +22,7 @@ import yaml
from mistralclient.api.v2 import environments
from mistralclient.commands.v2 import base
from mistralclient import utils
LOG = logging.getLogger(__name__)
@ -153,13 +154,18 @@ class Delete(command.Command):
def get_parser(self, prog_name):
parser = super(Delete, self).get_parser(prog_name)
parser.add_argument('name', help='Environment name')
parser.add_argument('name', nargs='+', help='Name of environment(s).')
return parser
def take_action(self, parsed_args):
environments.EnvironmentManager(self.app.client).delete(
parsed_args.name)
env_mgr = environments.EnvironmentManager(self.app.client)
utils.do_action_on_many(
lambda s: env_mgr.delete(s),
parsed_args.name,
"Request to delete environment %s has been accepted.",
"Unable to delete the specified environment(s)."
)
class Update(show.ShowOne):

View File

@ -22,6 +22,7 @@ from cliff import show
from mistralclient.api.v2 import executions
from mistralclient.commands.v2 import base
from mistralclient import utils
LOG = logging.getLogger(__name__)
@ -140,12 +141,22 @@ class Delete(command.Command):
def get_parser(self, prog_name):
parser = super(Delete, self).get_parser(prog_name)
parser.add_argument('id', help='Execution identifier')
parser.add_argument(
'id',
nargs='+',
help='Id of execution identifier(s).'
)
return parser
def take_action(self, parsed_args):
executions.ExecutionManager(self.app.client).delete(parsed_args.id)
exe_mgr = executions.ExecutionManager(self.app.client)
utils.do_action_on_many(
lambda s: exe_mgr.delete(s),
parsed_args.id,
"Request to delete execution %s has been accepted.",
"Unable to delete the specified execution(s)."
)
class Update(show.ShowOne):

View File

@ -22,6 +22,7 @@ from cliff import show
from mistralclient.api.v2 import workbooks
from mistralclient.commands.v2 import base
from mistralclient import exceptions as exc
from mistralclient import utils
LOG = logging.getLogger(__name__)
@ -110,12 +111,18 @@ class Delete(command.Command):
def get_parser(self, prog_name):
parser = super(Delete, self).get_parser(prog_name)
parser.add_argument('name', help='Workbook name')
parser.add_argument('name', nargs='+', help='Name of workbook(s).')
return parser
def take_action(self, parsed_args):
workbooks.WorkbookManager(self.app.client).delete(parsed_args.name)
wb_mgr = workbooks.WorkbookManager(self.app.client)
utils.do_action_on_many(
lambda s: wb_mgr.delete(s),
parsed_args.name,
"Request to delete workbook %s has been accepted.",
"Unable to delete the specified workbook(s)."
)
class Update(show.ShowOne):

View File

@ -22,6 +22,7 @@ from cliff import show
from mistralclient.api.v2 import workflows
from mistralclient.commands.v2 import base
from mistralclient import exceptions as exc
from mistralclient import utils
LOG = logging.getLogger(__name__)
@ -119,12 +120,18 @@ class Delete(command.Command):
def get_parser(self, prog_name):
parser = super(Delete, self).get_parser(prog_name)
parser.add_argument('name', help='Workflow name')
parser.add_argument('name', nargs='+', help='Name of workflow(s).')
return parser
def take_action(self, parsed_args):
workflows.WorkflowManager(self.app.client).delete(parsed_args.name)
wf_mgr = workflows.WorkflowManager(self.app.client)
utils.do_action_on_many(
lambda s: wf_mgr.delete(s),
parsed_args.name,
"Request to delete workflow %s has been accepted.",
"Unable to delete the specified workflow(s)."
)
class Update(base.MistralLister):

View File

@ -95,8 +95,20 @@ class TestCLIActionsV2(base.BaseCommandTest):
)
@mock.patch('mistralclient.api.v2.actions.ActionManager.delete')
def test_delete(self, mock):
self.assertIsNone(self.call(action_cmd.Delete, app_args=['name']))
def test_delete(self, del_mock):
self.call(action_cmd.Delete, app_args=['name'])
del_mock.assert_called_once_with('name')
@mock.patch('mistralclient.api.v2.actions.ActionManager.delete')
def test_delete_with_multi_names(self, del_mock):
self.call(action_cmd.Delete, app_args=['name1', 'name2'])
self.assertEqual(2, del_mock.call_count)
self.assertEqual(
[mock.call('name1'), mock.call('name2')],
del_mock.call_args_list
)
@mock.patch('mistralclient.api.v2.actions.'
'ActionManager.get')

View File

@ -80,7 +80,17 @@ class TestCLIWorkbooksV2(base.BaseCommandTest):
)
@mock.patch('mistralclient.api.v2.cron_triggers.CronTriggerManager.delete')
def test_delete(self, mock):
self.assertIsNone(
self.call(cron_triggers_cmd.Delete, app_args=['name'])
def test_delete(self, del_mock):
self.call(cron_triggers_cmd.Delete, app_args=['name'])
del_mock.assert_called_once_with('name')
@mock.patch('mistralclient.api.v2.cron_triggers.CronTriggerManager.delete')
def test_delete_with_multi_names(self, del_mock):
self.call(cron_triggers_cmd.Delete, app_args=['name1', 'name2'])
self.assertEqual(2, del_mock.call_count)
self.assertEqual(
[mock.call('name1'), mock.call('name2')],
del_mock.call_args_list
)

View File

@ -115,5 +115,17 @@ class TestCLIEnvironmentsV2(base.BaseCommandTest):
self.assertEqual(EXPECTED_RESULT, result[1])
@mock.patch('mistralclient.api.v2.environments.EnvironmentManager.delete')
def test_delete(self, mock):
self.assertIsNone(self.call(environment_cmd.Delete, app_args=['name']))
def test_delete(self, del_mock):
self.call(environment_cmd.Delete, app_args=['name'])
del_mock.assert_called_once_with('name')
@mock.patch('mistralclient.api.v2.environments.EnvironmentManager.delete')
def test_delete_with_multi_names(self, del_mock):
self.call(environment_cmd.Delete, app_args=['name1', 'name2'])
self.assertEqual(2, del_mock.call_count)
self.assertEqual(
[mock.call('name1'), mock.call('name2')],
del_mock.call_args_list
)

View File

@ -82,7 +82,17 @@ class TestCLIExecutionsV2(base.BaseCommandTest):
'1', '1'), result[1])
@mock.patch('mistralclient.api.v2.executions.ExecutionManager.delete')
def test_delete(self, mock):
result = self.call(execution_cmd.Delete, app_args=['id'])
def test_delete(self, del_mock):
self.call(execution_cmd.Delete, app_args=['id'])
self.assertIsNone(result)
del_mock.assert_called_once_with('id')
@mock.patch('mistralclient.api.v2.executions.ExecutionManager.delete')
def test_delete_with_multi_names(self, del_mock):
self.call(execution_cmd.Delete, app_args=['id1', 'id2'])
self.assertEqual(2, del_mock.call_count)
self.assertEqual(
[mock.call('id1'), mock.call('id2')],
del_mock.call_args_list
)

View File

@ -86,8 +86,20 @@ class TestCLIWorkbooksV2(base.BaseCommandTest):
self.assertEqual(('a', 'a, b', '1', '1'), result[1])
@mock.patch('mistralclient.api.v2.workbooks.WorkbookManager.delete')
def test_delete(self, mock):
self.assertIsNone(self.call(workbook_cmd.Delete, app_args=['name']))
def test_delete(self, del_mock):
self.call(workbook_cmd.Delete, app_args=['name'])
del_mock.assert_called_once_with('name')
@mock.patch('mistralclient.api.v2.workbooks.WorkbookManager.delete')
def test_delete_with_multi_names(self, del_mock):
self.call(workbook_cmd.Delete, app_args=['name1', 'name2'])
self.assertEqual(2, del_mock.call_count)
self.assertEqual(
[mock.call('name1'), mock.call('name2')],
del_mock.call_args_list
)
@mock.patch('mistralclient.api.v2.workbooks.WorkbookManager.get')
def test_get_definition(self, mock):

View File

@ -80,8 +80,20 @@ class TestCLIWorkflowsV2(base.BaseCommandTest):
self.assertEqual(('a', 'a, b', 'param', '1', '1'), result[1])
@mock.patch('mistralclient.api.v2.workflows.WorkflowManager.delete')
def test_delete(self, mock):
self.assertIsNone(self.call(workflow_cmd.Delete, app_args=['name']))
def test_delete(self, del_mock):
self.call(workflow_cmd.Delete, app_args=['name'])
del_mock.assert_called_once_with('name')
@mock.patch('mistralclient.api.v2.workflows.WorkflowManager.delete')
def test_delete_with_multi_names(self, del_mock):
self.call(workflow_cmd.Delete, app_args=['name1', 'name2'])
self.assertEqual(2, del_mock.call_count)
self.assertEqual(
[mock.call('name1'), mock.call('name2')],
del_mock.call_args_list
)
@mock.patch('mistralclient.api.v2.workflows.'
'WorkflowManager.get')

31
mistralclient/utils.py Normal file
View File

@ -0,0 +1,31 @@
# Copyright 2015 - Huawei Technologies Co. Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import exceptions
def do_action_on_many(action, resources, success_msg, error_msg):
"""Helper to run an action on many resources."""
failure_flag = False
for resource in resources:
try:
action(resource)
print(success_msg % resource)
except Exception as e:
failure_flag = True
print(e)
if failure_flag:
raise exceptions.MistralClientException(error_msg)