Add support for dry-run deployment for deploy changes commands

This commit adds an additional option for fuel client to support
dry-run mode of deployment which is really useful for
debugging and day-to day operations and life cycle management of
clusters

Partial-bug: #1569839

(squash of cherry-pick of 7597187095 and
19007f1d78)

Change-Id: I77d0a8257a97a9f40e3cfd1a630c95b7800fe1e6
This commit is contained in:
Vladimir Kuklin 2016-04-14 18:31:09 +03:00
parent fa053d0768
commit e12872552b
8 changed files with 182 additions and 29 deletions

View File

@ -28,6 +28,7 @@ class ChangesAction(Action):
super(ChangesAction, self).__init__()
self.args = (
Args.get_env_arg(required=True),
Args.get_dry_run_deployment_arg(),
)
self.flag_func_map = (
(None, self.deploy_changes),
@ -38,7 +39,10 @@ class ChangesAction(Action):
fuel --env 1 {action_name}
"""
env = Environment(params.env)
deploy_task = getattr(env, self.actions_func_map[self.action_name])()
deploy_task = getattr(
env, self.actions_func_map[self.action_name])(
dry_run=params.dry_run)
self.serializer.print_to_output(
deploy_task.data,
deploy_task,

View File

@ -258,6 +258,14 @@ def get_sync_deployment_tasks_arg():
help="Update tasks for each release.")
def get_dry_run_deployment_arg():
return get_boolean_arg(
"dry-run",
dest='dry_run',
help="Specifies to dry-run a deployment by configuring task executor"
"to dump the deployment graph to a dot file.")
def get_file_pattern_arg():
return get_str_arg(
"filepattern",

View File

@ -203,10 +203,22 @@ class EnvDeploy(EnvMixIn, base.BaseCommand):
type=int,
help='Id of the environment to be deployed.')
dry_run_help_string = 'Specifies to dry-run a deployment by' \
'configuring task executor to dump the' \
'deployment graph to a dot file.' \
'Store cluster settings and serialized ' \
'data in the db and ask the task executor ' \
'to dump the resulting graph into a dot file'
parser.add_argument(
'-d', '--dry-run', dest="dry_run",
action='store_true', help=dry_run_help_string)
return parser
def take_action(self, parsed_args):
task_id = self.client.deploy_changes(parsed_args.id)
task_id = self.client.deploy_changes(parsed_args.id,
dry_run=parsed_args.dry_run)
msg = 'Deployment task with id {t} for the environment {e} '\
'has been started.\n'.format(t=task_id, e=parsed_args.id)
@ -214,20 +226,12 @@ class EnvDeploy(EnvMixIn, base.BaseCommand):
self.app.stdout.write(msg)
class EnvRedeploy(EnvMixIn, base.BaseCommand):
class EnvRedeploy(EnvDeploy):
"""Redeploys changes on the specified environment."""
def get_parser(self, prog_name):
parser = super(EnvRedeploy, self).get_parser(prog_name)
parser.add_argument('id',
type=int,
help='Id of the environment to be redeployed.')
return parser
def take_action(self, parsed_args):
task_id = self.client.redeploy_changes(parsed_args.id)
task_id = self.client.redeploy_changes(parsed_args.id,
dry_run=parsed_args.dry_run)
msg = 'Deployment task with id {t} for the environment {e} '\
'has been started.\n'.format(t=task_id, e=parsed_args.id)

View File

@ -93,17 +93,17 @@ class Environment(BaseObject):
[{"id": n.id} for n in nodes]
)
def deploy_changes(self):
def deploy_changes(self, dry_run=False):
deploy_data = self.connection.put_request(
"clusters/{0}/changes".format(self.id),
{}
{}, dry_run=int(dry_run)
)
return DeployTask.init_with_data(deploy_data)
def redeploy_changes(self):
def redeploy_changes(self, dry_run=False):
deploy_data = self.connection.put_request(
"clusters/{0}/changes/redeploy".format(self.id),
{}
{}, dry_run=int(dry_run)
)
return DeployTask.init_with_data(deploy_data)

View File

@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
import requests_mock as rm
from six import moves
@ -60,6 +59,59 @@ class TestEnvironment(base.UnitTestCase):
"deprecated since 7.0 release.",
m_stderr.getvalue())
@mock.patch('fuelclient.objects.task.DeployTask.init_with_data')
def test_deploy_changes(self, task_data):
dry_run = False
mdeploy = self.m_request.put('/api/v1/clusters/1/changes'
'?dry_run={0}'.format(
int(dry_run)), json={})
cmd = ['fuel', 'deploy-changes', '--env', '1']
self.execute(cmd)
self.check_deploy_redeploy_changes(dry_run, mdeploy)
@mock.patch('fuelclient.objects.task.DeployTask.init_with_data')
def test_deploy_changes_dry_run(self, task_data):
dry_run = True
mdeploy = self.m_request.put('/api/v1/clusters/1/changes'
'?dry_run={0}'.format(
int(dry_run)), json={})
cmd = ['fuel', 'deploy-changes', '--env', '1']
cmd.append('--dry-run')
self.execute(cmd)
self.check_deploy_redeploy_changes(dry_run, mdeploy)
@mock.patch('fuelclient.objects.task.DeployTask.init_with_data')
def test_redeploy_changes(self, task_data):
dry_run = False
mdeploy = self.m_request.put('/api/v1/clusters/1/changes/redeploy'
'?dry_run={0}'.format(
int(dry_run)), json={})
cmd = ['fuel', 'redeploy-changes', '--env', '1']
self.execute(cmd)
self.check_deploy_redeploy_changes(dry_run, mdeploy)
@mock.patch('fuelclient.objects.task.DeployTask.init_with_data')
def test_redeploy_changes_dry_run(self, task_data):
dry_run = True
mdeploy = self.m_request.put('/api/v1/clusters/1/changes/redeploy'
'?dry_run={0}'.format(
int(dry_run)), json={})
cmd = ['fuel', 'redeploy-changes', '--env', '1']
cmd.append('--dry-run')
self.execute(cmd)
self.check_deploy_redeploy_changes(dry_run, mdeploy)
def check_deploy_redeploy_changes(self, dry_run, mdeploy):
self.assertEqual(mdeploy.last_request.qs['dry_run'][0],
str(int(dry_run)))
class TestEnvironmentOstf(base.UnitTestCase):

View File

@ -89,18 +89,64 @@ class TestEnvCommand(test_engine.BaseCLITest):
self.assertIn('--force', m_stdout.getvalue())
def test_env_deploy(self):
args = 'env deploy 42'
dry_run = False
args = 'env deploy'
args += ' 42'
self.exec_command(args)
self.m_get_client.assert_called_once_with('environment', mock.ANY)
self.m_client.deploy_changes.assert_called_once_with(42)
calls = list()
calls.append(mock.call.deploy_changes(42,
dry_run=dry_run))
self.m_get_client.assert_called_with('environment', mock.ANY)
self.m_client.assert_has_calls(calls)
def test_env_deploy_dry_run(self):
dry_run = True
args = 'env deploy -d'
args += ' 42'
self.exec_command(args)
calls = list()
calls.append(mock.call.deploy_changes(42,
dry_run=dry_run))
self.m_get_client.assert_called_with('environment', mock.ANY)
self.m_client.assert_has_calls(calls)
def test_env_redeploy(self):
args = 'env redeploy 42'
dry_run = False
args = 'env redeploy'
args += ' 42'
self.exec_command(args)
self.m_get_client.assert_called_once_with('environment', mock.ANY)
self.m_client.redeploy_changes.assert_called_once_with(42)
calls = list()
calls.append(mock.call.redeploy_changes(42,
dry_run=dry_run))
self.m_get_client.assert_called_with('environment', mock.ANY)
self.m_client.assert_has_calls(calls)
def test_env_redeploy_dry_run(self):
dry_run = True
args = 'env redeploy -d'
args += ' 42'
self.exec_command(args)
calls = list()
calls.append(mock.call.redeploy_changes(42,
dry_run=dry_run))
self.m_get_client.assert_called_with('environment', mock.ANY)
self.m_client.assert_has_calls(calls)
def test_env_add_nodes(self):
args = 'env add nodes -e 42 -n 24 25 -r compute cinder'

View File

@ -116,10 +116,49 @@ class TestEnvFacade(test_api.BaseLibTest):
env_id = 42
expected_uri = self.get_object_uri(self.res_uri, env_id, '/changes')
matcher = self.m_request.put(expected_uri, json={})
dry_run = False
self.client.deploy_changes(env_id)
self.client.deploy_changes(env_id, dry_run=dry_run)
self.check_deploy_redeploy_changes(dry_run, matcher)
def check_deploy_redeploy_changes(self, dry_run, matcher):
self.assertTrue(matcher.called)
self.assertEqual(matcher.last_request.qs['dry_run'][0],
str(int(dry_run)))
@mock.patch.object(task_object.DeployTask, 'init_with_data')
def test_env_deploy_dry_run(self, m_init):
env_id = 42
expected_uri = self.get_object_uri(self.res_uri, env_id, '/changes')
matcher = self.m_request.put(expected_uri, json={})
dry_run = True
self.client.deploy_changes(env_id, dry_run=dry_run)
self.check_deploy_redeploy_changes(dry_run, matcher)
@mock.patch.object(task_object.DeployTask, 'init_with_data')
def test_env_redeploy(self, m_init):
env_id = 42
expected_uri = self.get_object_uri(self.res_uri, env_id,
'/changes/redeploy')
matcher = self.m_request.put(expected_uri, json={})
dry_run = False
self.client.redeploy_changes(env_id, dry_run=dry_run)
self.check_deploy_redeploy_changes(dry_run, matcher)
@mock.patch.object(task_object.DeployTask, 'init_with_data')
def test_env_redeploy_dry_run(self, m_init):
env_id = 42
expected_uri = self.get_object_uri(self.res_uri, env_id,
'/changes/redeploy')
matcher = self.m_request.put(expected_uri, json={})
dry_run = True
self.client.redeploy_changes(env_id, dry_run=dry_run)
self.check_deploy_redeploy_changes(dry_run, matcher)
@mock.patch.object(base_object.BaseObject, 'init_with_data')
def test_env_update(self, m_init):

View File

@ -65,16 +65,16 @@ class EnvironmentClient(base_v1.BaseV1Client):
env.assign(nodes, roles)
def deploy_changes(self, environment_id):
def deploy_changes(self, environment_id, dry_run=False):
env = self._entity_wrapper(obj_id=environment_id)
deploy_task = env.deploy_changes()
deploy_task = env.deploy_changes(dry_run=dry_run)
return deploy_task.id
def redeploy_changes(self, environment_id):
def redeploy_changes(self, environment_id, dry_run=False):
env = self._entity_wrapper(obj_id=environment_id)
redeploy_task = env.redeploy_changes()
redeploy_task = env.redeploy_changes(dry_run=dry_run)
return redeploy_task.id
def spawn_vms(self, environment_id):