From 743d2dd0a4c848eeef2c579c3e54ac57d0fec096 Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 19 Sep 2017 15:37:11 +0800 Subject: [PATCH] Add force parameter for cluster and node deletion Change-Id: I9329dcce4cb1727206b8786c15cf6ba7837e385c Depends-On: I0c0fe77eafea29dbd75306690ec3f860939655bc --- senlinclient/plugin.py | 2 +- senlinclient/tests/unit/v1/test_cluster.py | 29 ++++++++++++++++------ senlinclient/tests/unit/v1/test_node.py | 29 ++++++++++++++++------ senlinclient/tests/unit/v1/test_shell.py | 25 ++++++++++++++++--- senlinclient/v1/cluster.py | 12 ++++++--- senlinclient/v1/node.py | 12 ++++++--- senlinclient/v1/shell.py | 8 ++++-- 7 files changed, 88 insertions(+), 29 deletions(-) diff --git a/senlinclient/plugin.py b/senlinclient/plugin.py index 0f60b2c2..2546d01c 100644 --- a/senlinclient/plugin.py +++ b/senlinclient/plugin.py @@ -23,7 +23,7 @@ LOG = logging.getLogger(__name__) DEFAULT_CLUSTERING_API_VERSION = '1' API_VERSION_OPTION = 'os_clustering_api_version' API_NAME = 'clustering' -CURRENT_API_VERSION = '1.7' +CURRENT_API_VERSION = '1.8' def make_client(instance): diff --git a/senlinclient/tests/unit/v1/test_cluster.py b/senlinclient/tests/unit/v1/test_cluster.py index 25d48649..35153a47 100644 --- a/senlinclient/tests/unit/v1/test_cluster.py +++ b/senlinclient/tests/unit/v1/test_cluster.py @@ -343,8 +343,9 @@ class TestClusterDelete(TestCluster): parsed_args = self.check_parser(self.cmd, arglist, []) self.cmd.take_action(parsed_args) self.mock_client.delete_cluster.assert_has_calls( - [mock.call('cluster1', False), mock.call('cluster2', False), - mock.call('cluster3', False)] + [mock.call('cluster1', False, False), + mock.call('cluster2', False, False), + mock.call('cluster3', False, False)] ) def test_cluster_delete_force(self): @@ -352,8 +353,19 @@ class TestClusterDelete(TestCluster): parsed_args = self.check_parser(self.cmd, arglist, []) self.cmd.take_action(parsed_args) self.mock_client.delete_cluster.assert_has_calls( - [mock.call('cluster1', False), mock.call('cluster2', False), - mock.call('cluster3', False)] + [mock.call('cluster1', False, False), + mock.call('cluster2', False, False), + mock.call('cluster3', False, False)] + ) + + def test_cluster_delete_force_delete(self): + arglist = ['cluster1', 'cluster2', 'cluster3', '--force-delete'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + self.mock_client.delete_cluster.assert_has_calls( + [mock.call('cluster1', True, False), + mock.call('cluster2', True, False), + mock.call('cluster3', True, False)] ) def test_cluster_delete_not_found(self): @@ -364,7 +376,7 @@ class TestClusterDelete(TestCluster): self.cmd.take_action(parsed_args) self.mock_client.delete_cluster.assert_has_calls( - [mock.call('my_cluster', False)] + [mock.call('my_cluster', False, False)] ) def test_cluster_delete_one_found_one_not_found(self): @@ -377,7 +389,8 @@ class TestClusterDelete(TestCluster): self.cmd.take_action(parsed_args) self.mock_client.delete_cluster.assert_has_calls( - [mock.call('cluster1', False), mock.call('cluster2', False)] + [mock.call('cluster1', False, False), + mock.call('cluster2', False, False)] ) @mock.patch('sys.stdin', spec=six.StringIO) @@ -390,8 +403,8 @@ class TestClusterDelete(TestCluster): self.cmd.take_action(parsed_args) mock_stdin.readline.assert_called_with() - self.mock_client.delete_cluster.assert_called_with('my_cluster', - False) + self.mock_client.delete_cluster.assert_called_with( + 'my_cluster', False, False) @mock.patch('sys.stdin', spec=six.StringIO) def test_cluster_delete_prompt_no(self, mock_stdin): diff --git a/senlinclient/tests/unit/v1/test_node.py b/senlinclient/tests/unit/v1/test_node.py index ed014eec..ed036728 100644 --- a/senlinclient/tests/unit/v1/test_node.py +++ b/senlinclient/tests/unit/v1/test_node.py @@ -339,8 +339,9 @@ class TestNodeDelete(TestNode): parsed_args = self.check_parser(self.cmd, arglist, []) self.cmd.take_action(parsed_args) self.mock_client.delete_node.assert_has_calls( - [mock.call('node1', False), mock.call('node2', False), - mock.call('node3', False)] + [mock.call('node1', False, False), + mock.call('node2', False, False), + mock.call('node3', False, False)] ) def test_node_delete_force(self): @@ -348,8 +349,19 @@ class TestNodeDelete(TestNode): parsed_args = self.check_parser(self.cmd, arglist, []) self.cmd.take_action(parsed_args) self.mock_client.delete_node.assert_has_calls( - [mock.call('node1', False), mock.call('node2', False), - mock.call('node3', False)] + [mock.call('node1', False, False), + mock.call('node2', False, False), + mock.call('node3', False, False)] + ) + + def test_node_delete_force_delete(self): + arglist = ['node1', 'node2', 'node3', '--force-delete'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + self.mock_client.delete_node.assert_has_calls( + [mock.call('node1', True, False), + mock.call('node2', True, False), + mock.call('node3', True, False)] ) def test_node_delete_not_found(self): @@ -360,7 +372,7 @@ class TestNodeDelete(TestNode): self.cmd.take_action(parsed_args) self.mock_client.delete_node.assert_has_calls( - [mock.call('my_node', False)] + [mock.call('my_node', False, False)] ) def test_node_delete_one_found_one_not_found(self): @@ -373,7 +385,8 @@ class TestNodeDelete(TestNode): self.cmd.take_action(parsed_args) self.mock_client.delete_node.assert_has_calls( - [mock.call('node1', False), mock.call('node2', False)] + [mock.call('node1', False, False), + mock.call('node2', False, False)] ) @mock.patch('sys.stdin', spec=six.StringIO) @@ -386,8 +399,8 @@ class TestNodeDelete(TestNode): self.cmd.take_action(parsed_args) mock_stdin.readline.assert_called_with() - self.mock_client.delete_node.assert_called_with('my_node', - False) + self.mock_client.delete_node.assert_called_with( + 'my_node', False, False) @mock.patch('sys.stdin', spec=six.StringIO) def test_node_delete_prompt_no(self, mock_stdin): diff --git a/senlinclient/tests/unit/v1/test_shell.py b/senlinclient/tests/unit/v1/test_shell.py index 6b79c7b4..e9824986 100644 --- a/senlinclient/tests/unit/v1/test_shell.py +++ b/senlinclient/tests/unit/v1/test_shell.py @@ -834,11 +834,19 @@ class ShellTest(testtools.TestCase): def test_do_cluster_delete(self): service = mock.Mock() - args = {'id': ['CID']} + args = {'id': ['CID'], 'force_delete': False} args = self._make_args(args) service.delete_cluster = mock.Mock() sh.do_cluster_delete(service, args) - service.delete_cluster.assert_called_once_with('CID', False) + service.delete_cluster.assert_called_once_with('CID', False, False) + + def test_do_cluster_delete_force(self): + service = mock.Mock() + args = {'id': ['CID'], 'force_delete': True} + args = self._make_args(args) + service.delete_cluster = mock.Mock() + sh.do_cluster_delete(service, args) + service.delete_cluster.assert_called_once_with('CID', True, False) @mock.patch('subprocess.Popen') def test__run_script(self, mock_proc): @@ -1669,12 +1677,21 @@ class ShellTest(testtools.TestCase): def test_do_node_delete(self): service = mock.Mock() - args = self._make_args({'id': ['node1']}) + args = self._make_args({'id': ['node1'], 'force_delete': False}) service.delete_node = mock.Mock() sh.do_node_delete(service, args) - service.delete_node.assert_called_once_with('node1', False) + service.delete_node.assert_called_once_with('node1', False, False) + + def test_do_node_delete_force(self): + service = mock.Mock() + args = self._make_args({'id': ['node1'], 'force_delete': True}) + service.delete_node = mock.Mock() + + sh.do_node_delete(service, args) + + service.delete_node.assert_called_once_with('node1', True, False) def test_do_node_check(self): service = mock.Mock() diff --git a/senlinclient/v1/cluster.py b/senlinclient/v1/cluster.py index 5b81e65e..4eb33a59 100644 --- a/senlinclient/v1/cluster.py +++ b/senlinclient/v1/cluster.py @@ -322,12 +322,17 @@ class DeleteCluster(command.Command): 'cluster', metavar='', nargs='+', - help=_('Name or ID of cluster(s) to delete') + help=_('Name or ID of cluster(s) to delete.') + ) + parser.add_argument( + '--force-delete', + action='store_true', + help=_('Force to delete cluster(s).') ) parser.add_argument( '--force', action='store_true', - help=_('Skip yes/no prompt (assume yes)') + help=_('Skip yes/no prompt (assume yes).') ) return parser @@ -353,7 +358,8 @@ class DeleteCluster(command.Command): result = {} for cid in parsed_args.cluster: try: - cluster = senlin_client.delete_cluster(cid, False) + cluster = senlin_client.delete_cluster( + cid, parsed_args.force_delete, False) result[cid] = ('OK', cluster.location.split('/')[-1]) except Exception as ex: result[cid] = ('ERROR', six.text_type(ex)) diff --git a/senlinclient/v1/node.py b/senlinclient/v1/node.py index 04d95b39..772f0620 100644 --- a/senlinclient/v1/node.py +++ b/senlinclient/v1/node.py @@ -293,12 +293,17 @@ class DeleteNode(command.Command): 'node', metavar='', nargs='+', - help=_('Name or ID of node(s) to delete') + help=_('Name or ID of node(s) to delete.') + ) + parser.add_argument( + '--force-delete', + action='store_true', + help=_('Force to delete node(s).') ) parser.add_argument( '--force', action='store_true', - help=_('Skip yes/no prompt (assume yes)') + help=_('Skip yes/no prompt (assume yes).') ) return parser @@ -324,7 +329,8 @@ class DeleteNode(command.Command): result = {} for nid in parsed_args.node: try: - node = senlin_client.delete_node(nid, False) + node = senlin_client.delete_node( + nid, parsed_args.force_delete, False) result[nid] = ('OK', node.location.split('/')[-1]) except Exception as ex: result[nid] = ('ERROR', six.text_type(ex)) diff --git a/senlinclient/v1/shell.py b/senlinclient/v1/shell.py index 0b5b5ca8..2c0c80df 100644 --- a/senlinclient/v1/shell.py +++ b/senlinclient/v1/shell.py @@ -631,6 +631,8 @@ def do_cluster_collect(service, args): @utils.arg('id', metavar='', nargs='+', help=_('Name or ID of cluster(s) to delete.')) +@utils.arg('-f', '--force-delete', default=False, action="store_true", + help=_('Force to delete cluster(s).')) def do_cluster_delete(service, args): """Delete the cluster(s).""" show_deprecated('senlin cluster-delete', 'openstack cluster delete') @@ -638,7 +640,7 @@ def do_cluster_delete(service, args): result = {} for cid in args.id: try: - cluster = service.delete_cluster(cid, False) + cluster = service.delete_cluster(cid, args.force_delete, False) result[cid] = ('OK', cluster.location.split('/')[-1]) except Exception as ex: result[cid] = ('ERROR', six.text_type(ex)) @@ -1406,6 +1408,8 @@ def do_node_show(service, args): @utils.arg('id', metavar='', nargs='+', help=_('Name or ID of node(s) to delete.')) +@utils.arg('-f', '--force-delete', default=False, action="store_true", + help=_('Force to delete the node(s).')) def do_node_delete(service, args): """Delete the node(s).""" show_deprecated('senlin node-delete', 'openstack cluster node delete') @@ -1413,7 +1417,7 @@ def do_node_delete(service, args): result = {} for nid in args.id: try: - node = service.delete_node(nid, False) + node = service.delete_node(nid, args.force_delete, False) result[nid] = ('OK', node.location.split('/')[-1]) except Exception as ex: result[nid] = ('ERROR', six.text_type(ex))