Adds --wait argument for cluster CLI interactions
Add option to wait for CLI command to complete for the following cluster commands: create, update, delete, resize, scale in, scale out, policy attach, policy detach, node add, node delete, node replace, cluster check, recover and cluster operation. Change-Id: I5663ca7286c55da4491644f979d5ab44f0cfc915
This commit is contained in:
@@ -38,6 +38,10 @@ class FileFormatError(BaseException):
|
||||
"""Illegal file format detected."""
|
||||
|
||||
|
||||
class PollingExceededError(BaseException):
|
||||
"""Desired resource state not achived within polling period."""
|
||||
|
||||
|
||||
class HTTPException(BaseException):
|
||||
"""Base exception for all HTTP-derived exceptions."""
|
||||
code = 'N/A'
|
||||
|
@@ -12,15 +12,21 @@
|
||||
|
||||
|
||||
from heatclient.common import template_utils
|
||||
import logging
|
||||
from openstack import exceptions as sdk_exc
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import importutils
|
||||
import prettytable
|
||||
import time
|
||||
import yaml
|
||||
|
||||
from senlinclient.common import exc
|
||||
from senlinclient.common.i18n import _
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def import_versioned_module(version, submodule=None):
|
||||
module = 'senlinclient.v%s' % version
|
||||
if submodule:
|
||||
@@ -153,3 +159,78 @@ def process_stack_spec(spec):
|
||||
}
|
||||
|
||||
return new_spec
|
||||
|
||||
|
||||
def await_action(senlin_client, action_id,
|
||||
poll_count_max=10, poll_interval=5):
|
||||
|
||||
def check_action():
|
||||
try:
|
||||
action = senlin_client.get_action(action_id)
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_('Action not found: %s')
|
||||
% action_id)
|
||||
action_states = ['succeeded', 'failed', 'cancelled']
|
||||
if action.status.lower() in action_states:
|
||||
log.info("Action %s completed with status %s."
|
||||
% (action.id, action.status))
|
||||
return True
|
||||
log.info("Awaiting action %s completion status (current: %s)."
|
||||
% (action.id, action.status))
|
||||
return False
|
||||
|
||||
_check(check_action, poll_count_max, poll_interval)
|
||||
|
||||
|
||||
def await_cluster_status(senlin_client, cluster_id, statuses=None,
|
||||
poll_count_max=10, poll_interval=5):
|
||||
|
||||
if not statuses or len(statuses) <= 0:
|
||||
statuses = ['ACTIVE', 'ERROR', 'WARNING']
|
||||
|
||||
def check_status():
|
||||
try:
|
||||
cluster = senlin_client.get_cluster(cluster_id)
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_('Cluster not found: %s') % cluster_id)
|
||||
|
||||
if cluster.status.lower() in [fs.lower() for fs in statuses]:
|
||||
return True
|
||||
log.info("Awaiting cluster status (desired: %s - current: %s)." %
|
||||
(', '.join(statuses), cluster.status))
|
||||
return False
|
||||
|
||||
_check(check_status, poll_count_max, poll_interval)
|
||||
|
||||
|
||||
def await_cluster_delete(senlin_client, cluster_id,
|
||||
poll_count_max=10, poll_interval=5):
|
||||
|
||||
def check_deleted():
|
||||
try:
|
||||
senlin_client.get_cluster(cluster_id)
|
||||
except sdk_exc.ResourceNotFound:
|
||||
log.info("Successfully deleted cluster %s." % cluster_id)
|
||||
return True
|
||||
log.info("Awaiting cluster deletion for %s." % cluster_id)
|
||||
return False
|
||||
|
||||
_check(check_deleted, poll_count_max, poll_interval)
|
||||
|
||||
|
||||
def _check(check_func, poll_count_max=10, poll_interval=5):
|
||||
# a negative poll_count_max is considered indefinite
|
||||
|
||||
poll_increment = 1
|
||||
if poll_count_max < 0:
|
||||
poll_count_max = 1
|
||||
poll_increment = 0
|
||||
|
||||
poll_count = 0
|
||||
while poll_count < poll_count_max:
|
||||
if check_func():
|
||||
return
|
||||
|
||||
time.sleep(poll_interval)
|
||||
poll_count += poll_increment
|
||||
raise exc.PollingExceededError()
|
||||
|
@@ -14,6 +14,7 @@ from heatclient.common import template_utils
|
||||
from unittest import mock
|
||||
|
||||
import testtools
|
||||
import time
|
||||
|
||||
from senlinclient.common import exc
|
||||
from senlinclient.common.i18n import _
|
||||
@@ -97,3 +98,40 @@ class UtilTest(testtools.TestCase):
|
||||
def test_list_formatter_with_empty_list(self):
|
||||
params = []
|
||||
self.assertEqual('', utils.list_formatter(params))
|
||||
|
||||
@mock.patch.object(utils, '_check')
|
||||
def test_await_cluster_action(self, mock_check):
|
||||
utils.await_action('fake-client', 'test-action-id')
|
||||
mock_check.assert_called_once()
|
||||
|
||||
@mock.patch.object(utils, '_check')
|
||||
def test_await_cluster_status(self, mock_check):
|
||||
utils.await_cluster_status('fake-client', 'ACTIVE')
|
||||
mock_check.assert_called_once()
|
||||
|
||||
@mock.patch.object(utils, '_check')
|
||||
def test_await_cluster_delete(self, mock_check):
|
||||
utils.await_cluster_delete('fake-client', 'test-cluster-id')
|
||||
mock_check.assert_called_once()
|
||||
|
||||
def test_check(self):
|
||||
check_func = mock.Mock(return_value=True)
|
||||
|
||||
try:
|
||||
utils._check(check_func)
|
||||
except Exception:
|
||||
self.fail("_check() unexpectedly raised an exception")
|
||||
|
||||
check_func.assert_called()
|
||||
|
||||
@mock.patch.object(time, 'sleep')
|
||||
def test_check_raises(self, mock_sleep):
|
||||
mock_check_func = mock.Mock(return_value=False)
|
||||
|
||||
poll_count = 2
|
||||
poll_interval = 1
|
||||
|
||||
self.assertRaises(exc.PollingExceededError, utils._check,
|
||||
mock_check_func, poll_count, poll_interval)
|
||||
mock_check_func.assert_called()
|
||||
mock_sleep.assert_called()
|
||||
|
@@ -18,6 +18,7 @@ from unittest import mock
|
||||
from openstack import exceptions as sdk_exc
|
||||
from osc_lib import exceptions as exc
|
||||
|
||||
from senlinclient.common import utils as senlin_utils
|
||||
from senlinclient.tests.unit.v1 import fakes
|
||||
from senlinclient.v1 import cluster as osc_cluster
|
||||
|
||||
@@ -202,13 +203,14 @@ class TestClusterCreate(TestCluster):
|
||||
def setUp(self):
|
||||
super(TestClusterCreate, self).setUp()
|
||||
self.cmd = osc_cluster.CreateCluster(self.app, None)
|
||||
self.cluster_id = '7d85f602-a948-4a30-afd4-e84f47471c15'
|
||||
fake_cluster = mock.Mock(
|
||||
config={},
|
||||
created_at="2015-02-11T15:13:20",
|
||||
data={},
|
||||
desired_capacity=0,
|
||||
domain_id=None,
|
||||
id="7d85f602-a948-4a30-afd4-e84f47471c15",
|
||||
id=self.cluster_id,
|
||||
init_time="2015-02-10T14:26:11",
|
||||
max_size=-1,
|
||||
metadata={},
|
||||
@@ -265,6 +267,24 @@ class TestClusterCreate(TestCluster):
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.mock_client.create_cluster.assert_called_with(**kwargs)
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_create_with_wait(self, mock_await):
|
||||
arglist = ['test_cluster', '--profile', 'mystack',
|
||||
'--min-size', '1', '--max-size', '10',
|
||||
'--desired-capacity', '2', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await.assert_called_once_with(self.mock_client, self.cluster_id)
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_create_without_wait(self, mock_await):
|
||||
arglist = ['test_cluster', '--profile', 'mystack',
|
||||
'--min-size', '1', '--max-size', '10',
|
||||
'--desired-capacity', '2']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterUpdate(TestCluster):
|
||||
|
||||
@@ -334,6 +354,24 @@ class TestClusterUpdate(TestCluster):
|
||||
parsed_args)
|
||||
self.assertIn('Cluster not found: c6b8b252', str(error))
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_update_with_wait(self, mock_await):
|
||||
arglist = ['--name', 'new_cluster', '--metadata', 'nk1=nv1;nk2=nv2',
|
||||
'--profile', 'new_profile', '--timeout', '30', '45edadcb',
|
||||
'--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await.assert_called_once_with(self.mock_client,
|
||||
self.fake_cluster.id)
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_update_without_wait(self, mock_await):
|
||||
arglist = ['--name', 'new_cluster', '--metadata', 'nk1=nv1;nk2=nv2',
|
||||
'--profile', 'new_profile', '--timeout', '30', '45edadcb']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterDelete(TestCluster):
|
||||
def setUp(self):
|
||||
@@ -422,6 +460,45 @@ class TestClusterDelete(TestCluster):
|
||||
mock_stdin.readline.assert_called_with()
|
||||
self.mock_client.delete_cluster.assert_not_called()
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_delete')
|
||||
def test_cluster_delete_with_wait(self, mock_await_cluster,
|
||||
mock_await_action):
|
||||
fake_action = {'id': 'fake-action-id'}
|
||||
self.mock_client.delete_cluster = mock.Mock(return_value=fake_action)
|
||||
arglist = ['my_cluster', '--force', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
fake_action['id'])
|
||||
mock_await_cluster.assert_called_once_with(self.mock_client,
|
||||
'my_cluster')
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_delete')
|
||||
def test_cluster_delete_without_wait(self, mock_await_cluster,
|
||||
mock_await_action):
|
||||
fake_action = {'id': 'fake-action-id'}
|
||||
self.mock_client.delete_cluster = mock.Mock(return_value=fake_action)
|
||||
arglist = ['my_cluster', '--force']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_cluster.assert_not_called()
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_delete')
|
||||
def test_cluster_delete_with_wait_bad_action(self, mock_await_cluster,
|
||||
mock_await_action):
|
||||
self.mock_client.delete_cluster.side_effect = (
|
||||
Exception('test exception')
|
||||
)
|
||||
arglist = ['my_cluster', '--force', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_cluster.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterResize(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -565,6 +642,46 @@ class TestClusterResize(TestCluster):
|
||||
self.assertEqual('Max size cannot be less than the specified '
|
||||
'capacity.', str(error))
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_resize_with_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--capacity', '2', 'my_cluster', "--wait"]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_await_status.assert_called_once_with(self.mock_client,
|
||||
'my_cluster')
|
||||
mock_show.assert_called_once()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_resize_without_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--capacity', '2', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_resize_with_wait_no_action(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
error = 'test error'
|
||||
self.mock_client.resize_cluster = mock.Mock(return_value=error)
|
||||
arglist = ['--capacity', '2', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterScaleIn(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -582,6 +699,48 @@ class TestClusterScaleIn(TestCluster):
|
||||
self.mock_client.scale_in_cluster.assert_called_with('my_cluster',
|
||||
'2')
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_scale_in_with_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--count', '2', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_await_status.assert_called_once_with(self.mock_client,
|
||||
'my_cluster')
|
||||
mock_show.assert_called_once()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_scale_in_without_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--count', '2', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_scale_in_with_wait_no_action(self, mock_await_status,
|
||||
mock_await_action,
|
||||
mock_show):
|
||||
arglist = ['--count', '2', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.scale_in_cluster = mock.Mock(return_value=error)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterScaleOut(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -599,6 +758,48 @@ class TestClusterScaleOut(TestCluster):
|
||||
self.mock_client.scale_out_cluster.assert_called_with('my_cluster',
|
||||
'2')
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_scale_out_with_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--count', '2', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_await_status.assert_called_once_with(self.mock_client,
|
||||
'my_cluster')
|
||||
mock_show.assert_called_once()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_scale_out_without_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--count', '2', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_scale_out_with_wait_no_action(self, mock_await_status,
|
||||
mock_await_action,
|
||||
mock_show):
|
||||
arglist = ['--count', '2', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.scale_out_cluster = mock.Mock(return_value=error)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterPolicyAttach(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -618,6 +819,32 @@ class TestClusterPolicyAttach(TestCluster):
|
||||
'my_policy',
|
||||
enabled=True)
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_policy_attach_with_wait(self, mock_await_action):
|
||||
arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_policy_attach_without_wait(self, mock_await_action):
|
||||
arglist = ['--policy', 'my_policy', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_policy_attach_with_wait_no_action(self,
|
||||
mock_await_action):
|
||||
arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.attach_policy_to_cluster = \
|
||||
mock.Mock(return_value=error)
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterPolicyDetach(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -636,6 +863,32 @@ class TestClusterPolicyDetach(TestCluster):
|
||||
'my_cluster',
|
||||
'my_policy')
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_policy_dettach_with_wait(self, mock_await_action):
|
||||
arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_policy_dettach_without_wait(self, mock_await_action):
|
||||
arglist = ['--policy', 'my_policy', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_policy_dettach_with_wait_no_action(self,
|
||||
mock_await_action):
|
||||
arglist = ['--policy', 'my_policy', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.detach_policy_from_cluster = \
|
||||
mock.Mock(return_value=error)
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterNodeList(TestCluster):
|
||||
columns = ['id', 'name', 'index', 'status', 'physical_id', 'created_at']
|
||||
@@ -734,6 +987,40 @@ class TestClusterNodeAdd(TestCluster):
|
||||
'my_cluster',
|
||||
['node1', 'node2'])
|
||||
|
||||
@mock.patch.object(osc_cluster, "_show_cluster")
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_node_add_with_wait(self, mock_await_action, mock_show):
|
||||
arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_show.assert_called_once_with(self.mock_client, 'my_cluster')
|
||||
|
||||
@mock.patch.object(osc_cluster, "_show_cluster")
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_node_add_without_wait(self, mock_await_action, mock_show):
|
||||
arglist = ['--nodes', 'node1', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, "_show_cluster")
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_node_add_with_wait_no_action(self, mock_await_action,
|
||||
mock_show):
|
||||
arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.add_nodes_to_cluster = mock.Mock(return_value=error)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterNodeDel(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -771,6 +1058,43 @@ class TestClusterNodeDel(TestCluster):
|
||||
['node1', 'node2'],
|
||||
destroy_after_deletion=False)
|
||||
|
||||
@mock.patch.object(osc_cluster, "_show_cluster")
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_node_delete_with_wait(self, mock_await_action, mock_show):
|
||||
arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_show.assert_called_once_with(self.mock_client, 'my_cluster')
|
||||
|
||||
@mock.patch.object(osc_cluster, "_show_cluster")
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_node_delete_without_wait(self, mock_await_action,
|
||||
mock_show):
|
||||
arglist = ['--nodes', 'node1', 'my_cluster']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, "_show_cluster")
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_node_delete_with_wait_no_action(self, mock_await_action,
|
||||
mock_show):
|
||||
arglist = ['--nodes', 'node1', 'my_cluster', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.remove_nodes_from_cluster = \
|
||||
mock.Mock(return_value=error)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterCheck(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -798,6 +1122,48 @@ class TestClusterCheck(TestCluster):
|
||||
parsed_args)
|
||||
self.assertIn('Cluster not found: cluster1', str(error))
|
||||
|
||||
@mock.patch.object(osc_cluster, "_list_cluster_summaries")
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_check_with_wait(self, mock_await_action,
|
||||
mock_await_status, mock_list):
|
||||
arglist = ['cluster1', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_called_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_await_status.assert_called_with(self.mock_client, 'cluster1')
|
||||
mock_list.assert_called_with(self.mock_client, {'cluster1'})
|
||||
|
||||
@mock.patch.object(osc_cluster, "_list_cluster_summaries")
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_check_without_wait(self, mock_await_action,
|
||||
mock_await_status, mock_list):
|
||||
arglist = ['cluster1']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_list.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, "_list_cluster_summaries")
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_check_with_wait_no_action(self, mock_await_action,
|
||||
mock_await_status, mock_list):
|
||||
arglist = ['cluster1', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.check_cluster = mock.Mock(return_value=error)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_list.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterRecover(TestCluster):
|
||||
response = {"action": "8bb476c3-0f4c-44ee-9f64-c7b0260814de"}
|
||||
@@ -826,6 +1192,48 @@ class TestClusterRecover(TestCluster):
|
||||
parsed_args)
|
||||
self.assertIn('Cluster not found: cluster1', str(error))
|
||||
|
||||
@mock.patch.object(osc_cluster, "_list_cluster_summaries")
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_recover_with_wait(self, mock_await_action,
|
||||
mock_await_status, mock_list):
|
||||
arglist = ['cluster1', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_called_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_await_status.assert_called_with(self.mock_client, 'cluster1')
|
||||
mock_list.assert_called_with(self.mock_client, {'cluster1'})
|
||||
|
||||
@mock.patch.object(osc_cluster, "_list_cluster_summaries")
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_recover_without_wait(self, mock_await_action,
|
||||
mock_await_status, mock_list):
|
||||
arglist = ['cluster1']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_list.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, "_list_cluster_summaries")
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
def test_cluster_recover_with_wait_no_action(self, mock_await_action,
|
||||
mock_await_status, mock_list):
|
||||
arglist = ['cluster1', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.recover_cluster = mock.Mock(return_value=error)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_list.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterOp(TestCluster):
|
||||
|
||||
@@ -856,6 +1264,48 @@ class TestClusterOp(TestCluster):
|
||||
parsed_args)
|
||||
self.assertIn('Cluster not found: cluster1', str(error))
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_op_with_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--operation', 'dance', 'cluster1', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_called_once_with(self.mock_client,
|
||||
self.response['action'])
|
||||
mock_await_status.assert_called_once_with(self.mock_client,
|
||||
'cluster1')
|
||||
mock_show.assert_called_once()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_op_without_wait(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--operation', 'dance', 'cluster1']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
@mock.patch.object(osc_cluster, '_show_cluster')
|
||||
@mock.patch.object(senlin_utils, 'await_action')
|
||||
@mock.patch.object(senlin_utils, 'await_cluster_status')
|
||||
def test_cluster_op_with_wait_no_action(self, mock_await_status,
|
||||
mock_await_action, mock_show):
|
||||
arglist = ['--operation', 'dance', 'cluster1', '--wait']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
error = {'error': 'test-error'}
|
||||
self.mock_client.perform_operation_on_cluster = \
|
||||
mock.Mock(return_value=error)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_await_action.assert_not_called()
|
||||
mock_await_status.assert_not_called()
|
||||
mock_show.assert_not_called()
|
||||
|
||||
|
||||
class TestClusterCollect(TestCluster):
|
||||
response = [
|
||||
|
@@ -210,6 +210,11 @@ class CreateCluster(command.ShowOne):
|
||||
metavar='<cluster-name>',
|
||||
help=_('Name of the cluster to create')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster creation to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -230,6 +235,8 @@ class CreateCluster(command.ShowOne):
|
||||
}
|
||||
|
||||
cluster = senlin_client.create_cluster(**attrs)
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_cluster_status(senlin_client, cluster.id)
|
||||
return _show_cluster(senlin_client, cluster.id)
|
||||
|
||||
|
||||
@@ -262,7 +269,6 @@ class UpdateCluster(command.ShowOne):
|
||||
"If false, it will be applied to all existing nodes. "
|
||||
"If true, any newly created nodes will use the new profile,"
|
||||
"but existing nodes will not be changed. Default is False.")
|
||||
|
||||
)
|
||||
parser.add_argument(
|
||||
'--timeout',
|
||||
@@ -288,6 +294,11 @@ class UpdateCluster(command.ShowOne):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to be updated')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster update to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -310,6 +321,12 @@ class UpdateCluster(command.ShowOne):
|
||||
}
|
||||
|
||||
senlin_client.update_cluster(cluster, **attrs)
|
||||
if parsed_args.wait:
|
||||
# PATCH operations do not currently return an action to await.
|
||||
# introducing a delay to allow the cluster to transition state
|
||||
# out of ACTIVE before inspection.
|
||||
time.sleep(1)
|
||||
senlin_utils.await_cluster_status(senlin_client, cluster.id)
|
||||
return _show_cluster(senlin_client, cluster.id)
|
||||
|
||||
|
||||
@@ -336,6 +353,11 @@ class DeleteCluster(command.Command):
|
||||
action='store_true',
|
||||
help=_('Skip yes/no prompt (assume yes).')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster delete to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -361,17 +383,21 @@ class DeleteCluster(command.Command):
|
||||
result = {}
|
||||
for cid in parsed_args.cluster:
|
||||
try:
|
||||
cluster_delete_action = senlin_client.delete_cluster(
|
||||
action = senlin_client.delete_cluster(
|
||||
cid, False, parsed_args.force_delete)
|
||||
result[cid] = ('OK', cluster_delete_action['id'])
|
||||
result[cid] = ('OK', action['id'])
|
||||
except Exception as ex:
|
||||
result[cid] = ('ERROR', str(ex))
|
||||
|
||||
for rid, res in result.items():
|
||||
senlin_utils.print_action_result(rid, res)
|
||||
for cid, a in result.items():
|
||||
senlin_utils.print_action_result(cid, a)
|
||||
if parsed_args.wait:
|
||||
if a[0] == 'OK':
|
||||
senlin_utils.await_action(senlin_client, a[1])
|
||||
senlin_utils.await_cluster_delete(senlin_client, cid)
|
||||
|
||||
|
||||
class ResizeCluster(command.Command):
|
||||
class ResizeCluster(command.ShowOne):
|
||||
"""Resize a cluster."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ResizeCluster")
|
||||
@@ -432,6 +458,11 @@ class ResizeCluster(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster resize to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -446,6 +477,7 @@ class ResizeCluster(command.Command):
|
||||
min_size = parsed_args.min_size
|
||||
max_size = parsed_args.max_size
|
||||
min_step = parsed_args.min_step
|
||||
wait = parsed_args.wait
|
||||
|
||||
if sum(v is not None for v in (capacity, adjustment, percentage,
|
||||
min_size, max_size)) == 0:
|
||||
@@ -507,13 +539,21 @@ class ResizeCluster(command.Command):
|
||||
action_args['strict'] = parsed_args.strict
|
||||
|
||||
resp = senlin_client.resize_cluster(parsed_args.cluster, **action_args)
|
||||
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
senlin_utils.await_cluster_status(senlin_client,
|
||||
parsed_args.cluster)
|
||||
return _show_cluster(senlin_client, parsed_args.cluster)
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
return '', ''
|
||||
|
||||
class ScaleInCluster(command.Command):
|
||||
|
||||
class ScaleInCluster(command.ShowOne):
|
||||
"""Scale in a cluster by the specified number of nodes."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ScaleInCluster")
|
||||
@@ -530,6 +570,11 @@ class ScaleInCluster(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster scale-in to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -544,11 +589,18 @@ class ScaleInCluster(command.Command):
|
||||
'Unable to scale in cluster: %s') % resp['error']['message'])
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
senlin_utils.await_cluster_status(senlin_client,
|
||||
parsed_args.cluster)
|
||||
return _show_cluster(senlin_client, parsed_args.cluster)
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
return '', ''
|
||||
|
||||
class ScaleOutCluster(command.Command):
|
||||
|
||||
class ScaleOutCluster(command.ShowOne):
|
||||
"""Scale out a cluster by the specified number of nodes."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ScaleOutCluster")
|
||||
@@ -565,6 +617,11 @@ class ScaleOutCluster(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster scale-out to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -579,9 +636,16 @@ class ScaleOutCluster(command.Command):
|
||||
'Unable to scale out cluster: %s') % resp['error']['message'])
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
senlin_utils.await_cluster_status(senlin_client,
|
||||
parsed_args.cluster)
|
||||
return _show_cluster(senlin_client, parsed_args.cluster)
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
return '', ''
|
||||
|
||||
|
||||
class ClusterPolicyAttach(command.Command):
|
||||
"""Attach policy to cluster."""
|
||||
@@ -608,6 +672,11 @@ class ClusterPolicyAttach(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster policy-attach to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -624,6 +693,8 @@ class ClusterPolicyAttach(command.Command):
|
||||
**kwargs)
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
@@ -646,6 +717,11 @@ class ClusterPolicyDetach(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster policy-detach to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -655,6 +731,8 @@ class ClusterPolicyDetach(command.Command):
|
||||
parsed_args.policy)
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
@@ -737,7 +815,7 @@ class ClusterNodeList(command.Lister):
|
||||
)
|
||||
|
||||
|
||||
class ClusterNodeAdd(command.Command):
|
||||
class ClusterNodeAdd(command.ShowOne):
|
||||
"""Add specified nodes to cluster."""
|
||||
log = logging.getLogger(__name__ + ".ClusterNodeAdd")
|
||||
|
||||
@@ -755,6 +833,11 @@ class ClusterNodeAdd(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster members add to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -765,11 +848,16 @@ class ClusterNodeAdd(command.Command):
|
||||
node_ids)
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
return _show_cluster(senlin_client, parsed_args.cluster)
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
return '', ''
|
||||
|
||||
class ClusterNodeDel(command.Command):
|
||||
|
||||
class ClusterNodeDel(command.ShowOne):
|
||||
"""Delete specified nodes from cluster."""
|
||||
log = logging.getLogger(__name__ + ".ClusterNodeDel")
|
||||
|
||||
@@ -795,6 +883,11 @@ class ClusterNodeDel(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster members delete to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -808,11 +901,16 @@ class ClusterNodeDel(command.Command):
|
||||
parsed_args.cluster, node_ids, **kwargs)
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
return _show_cluster(senlin_client, parsed_args.cluster)
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
return '', ''
|
||||
|
||||
class ClusterNodeReplace(command.Command):
|
||||
|
||||
class ClusterNodeReplace(command.ShowOne):
|
||||
"""Replace the nodes in a cluster with specified nodes."""
|
||||
log = logging.getLogger(__name__ + ".ClusterNodeReplace")
|
||||
|
||||
@@ -833,6 +931,11 @@ class ClusterNodeReplace(command.Command):
|
||||
metavar='<cluster>',
|
||||
help=_('Name or ID of cluster to operate on')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster members replace to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -847,11 +950,16 @@ class ClusterNodeReplace(command.Command):
|
||||
nodepairs)
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
return _show_cluster(senlin_client, parsed_args.cluster)
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
return '', ''
|
||||
|
||||
class CheckCluster(command.Command):
|
||||
|
||||
class CheckCluster(command.Lister):
|
||||
"""Check the cluster(s)."""
|
||||
log = logging.getLogger(__name__ + ".CheckCluster")
|
||||
|
||||
@@ -863,11 +971,19 @@ class CheckCluster(command.Command):
|
||||
nargs='+',
|
||||
help=_('ID or name of cluster(s) to operate on.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster check to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
senlin_client = self.app.client_manager.clustering
|
||||
|
||||
cluster_actions = {}
|
||||
|
||||
for cid in parsed_args.cluster:
|
||||
try:
|
||||
resp = senlin_client.check_cluster(cid)
|
||||
@@ -877,11 +993,39 @@ class CheckCluster(command.Command):
|
||||
print('Cluster check request on cluster %(cid)s is '
|
||||
'accepted by action %(action)s.'
|
||||
% {'cid': cid, 'action': resp['action']})
|
||||
cluster_actions[cid] = resp['action']
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
# generate the output after all actions have been accepted/rejected
|
||||
if parsed_args.wait and len(cluster_actions) > 0:
|
||||
for cid, action in cluster_actions.items():
|
||||
senlin_utils.await_action(senlin_client, action)
|
||||
senlin_utils.await_cluster_status(senlin_client, cid)
|
||||
return _list_cluster_summaries(senlin_client,
|
||||
cluster_actions.keys())
|
||||
|
||||
class RecoverCluster(command.Command):
|
||||
return '', ''
|
||||
|
||||
|
||||
def _list_cluster_summaries(senlin_client, cluster_ids):
|
||||
clusters = []
|
||||
for cluster_id in cluster_ids:
|
||||
try:
|
||||
cluster = senlin_client.get_cluster(cluster_id)
|
||||
except sdk_exc.ResourceNotFound:
|
||||
raise exc.CommandError(_('Cluster not found: %s') % cluster_id)
|
||||
|
||||
clusters.append(cluster)
|
||||
|
||||
columns = ['ID', 'Name', 'Status', 'Status Reason']
|
||||
formatters = {}
|
||||
props = (utils.get_item_properties(c, columns, formatters=formatters)
|
||||
for c in clusters)
|
||||
return columns, props
|
||||
|
||||
|
||||
class RecoverCluster(command.Lister):
|
||||
"""Recover the cluster(s)."""
|
||||
log = logging.getLogger(__name__ + ".RecoverCluster")
|
||||
|
||||
@@ -893,7 +1037,6 @@ class RecoverCluster(command.Command):
|
||||
nargs='+',
|
||||
help=_('ID or name of cluster(s) to operate on.')
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--check',
|
||||
metavar='<boolean>',
|
||||
@@ -901,6 +1044,11 @@ class RecoverCluster(command.Command):
|
||||
help=_("Whether the cluster should check it's nodes status before "
|
||||
"doing cluster recover. Default is false")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster recover to complete')
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
@@ -912,6 +1060,7 @@ class RecoverCluster(command.Command):
|
||||
'check': strutils.bool_from_string(parsed_args.check, strict=True)
|
||||
}
|
||||
|
||||
cluster_actions = {}
|
||||
for cid in parsed_args.cluster:
|
||||
try:
|
||||
resp = senlin_client.recover_cluster(cid, **params)
|
||||
@@ -921,9 +1070,20 @@ class RecoverCluster(command.Command):
|
||||
print('Cluster recover request on cluster %(cid)s is '
|
||||
'accepted by action %(action)s.'
|
||||
% {'cid': cid, 'action': resp['action']})
|
||||
cluster_actions[cid] = resp['action']
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
# generate the output after all actions have been accepted/rejected
|
||||
if parsed_args.wait and len(cluster_actions) > 0:
|
||||
for cid, action in cluster_actions.items():
|
||||
senlin_utils.await_action(senlin_client, action)
|
||||
senlin_utils.await_cluster_status(senlin_client, cid)
|
||||
return _list_cluster_summaries(senlin_client,
|
||||
cluster_actions.keys())
|
||||
|
||||
return '', ''
|
||||
|
||||
|
||||
class ClusterCollect(command.Lister):
|
||||
"""Collect attributes across a cluster."""
|
||||
@@ -966,7 +1126,7 @@ class ClusterCollect(command.Lister):
|
||||
for a in attrs))
|
||||
|
||||
|
||||
class ClusterOp(command.Lister):
|
||||
class ClusterOp(command.ShowOne):
|
||||
"""Perform an operation on all nodes across a cluster."""
|
||||
log = logging.getLogger(__name__ + ".ClusterOp")
|
||||
|
||||
@@ -991,6 +1151,11 @@ class ClusterOp(command.Lister):
|
||||
metavar='<cluster>',
|
||||
help=_('ID or name of cluster to operate on.')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait for cluster operation to complete')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -1009,9 +1174,15 @@ class ClusterOp(command.Lister):
|
||||
raise exc.CommandError(_('Cluster not found: %s') % cid)
|
||||
if 'action' in resp:
|
||||
print('Request accepted by action: %s' % resp['action'])
|
||||
if parsed_args.wait:
|
||||
senlin_utils.await_action(senlin_client, resp['action'])
|
||||
senlin_utils.await_cluster_status(senlin_client, cid)
|
||||
return _show_cluster(senlin_client, cid)
|
||||
else:
|
||||
print('Request error: %s' % resp)
|
||||
|
||||
return '', ''
|
||||
|
||||
|
||||
class ClusterRun(command.Command):
|
||||
"""Run scripts on cluster."""
|
||||
|
Reference in New Issue
Block a user