Introduced command 'env redeploy' in v2 version of fuelclient

Example: `fuel2 env redeploy <env_id>`

Fixed deployment tests.
Fuel-web repo has been changed from 'stackforge' to 'openstack'.

Partial-Bug: 1540558
Change-Id: I6bc18ff672bc119c523841033fde4bbacb9290fd
Depends-On: Ibc89fdbfbd0a36a890412cd8e861d35bcf930690
This commit is contained in:
Vitalii Myhal
2016-03-12 14:26:45 -06:00
parent 7f4ccf4509
commit 820c7c488f
9 changed files with 213 additions and 30 deletions

View File

@@ -201,14 +201,35 @@ class EnvDeploy(EnvMixIn, base.BaseCommand):
parser.add_argument('id', parser.add_argument('id',
type=int, type=int,
help='Id of the nailgun entity to be processed.') help='Id of the environment to be deployed.')
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
task_id = self.client.deploy_changes(parsed_args.id) task_id = self.client.deploy_changes(parsed_args.id)
msg = 'Deploy task with id {t} for the environment {e} '\ msg = 'Deployment task with id {t} for the environment {e} '\
'has been started.\n'.format(t=task_id, e=parsed_args.id)
self.app.stdout.write(msg)
class EnvRedeploy(EnvMixIn, base.BaseCommand):
"""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)
msg = 'Deployment task with id {t} for the environment {e} '\
'has been started.\n'.format(t=task_id, e=parsed_args.id) 'has been started.\n'.format(t=task_id, e=parsed_args.id)
self.app.stdout.write(msg) self.app.stdout.write(msg)

View File

@@ -14,4 +14,21 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from collections import namedtuple
def Enum(*values, **kwargs):
names = kwargs.get('names')
if names:
return namedtuple('Enum', names)(*values)
return namedtuple('Enum', values)(*values)
SERIALIZATION_FORMAT_FLAG = 'serialization_format' SERIALIZATION_FORMAT_FLAG = 'serialization_format'
TASK_STATUSES = Enum(
'error',
'pending',
'ready',
'running'
)

View File

@@ -12,24 +12,27 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import json
import logging import logging
import os import os
import re
import shutil import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import time
from oslotest import base as oslo_base from fuelclient import consts
from fuelclient.objects import Release from fuelclient.objects import Release
from oslotest import base as oslo_base
logging.basicConfig(stream=sys.stderr) logging.basicConfig(stream=sys.stderr)
log = logging.getLogger("CliTest.ExecutionLog") log = logging.getLogger("CliTest.ExecutionLog")
log.setLevel(logging.DEBUG) log.setLevel(logging.DEBUG)
class CliExectutionResult(object): class CliExecutionResult(object):
def __init__(self, process_handle, out, err): def __init__(self, process_handle, out, err):
self.return_code = process_handle.returncode self.return_code = process_handle.returncode
self.stdout = out self.stdout = out
@@ -45,6 +48,8 @@ class CliExectutionResult(object):
class BaseTestCase(oslo_base.BaseTestCase): class BaseTestCase(oslo_base.BaseTestCase):
handler = ''
nailgun_root = os.environ.get('NAILGUN_ROOT', '/tmp/fuel_web/nailgun') nailgun_root = os.environ.get('NAILGUN_ROOT', '/tmp/fuel_web/nailgun')
def setUp(self): def setUp(self):
@@ -91,7 +96,7 @@ class BaseTestCase(oslo_base.BaseTestCase):
def run_cli_command(self, command_line, def run_cli_command(self, command_line,
check_errors=True, env=os.environ.copy()): check_errors=True, env=os.environ.copy()):
command_args = [" ".join(('fuel', command_line))] command_args = [" ".join((self.handler, command_line))]
process_handle = subprocess.Popen( process_handle = subprocess.Popen(
command_args, command_args,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@@ -100,7 +105,7 @@ class BaseTestCase(oslo_base.BaseTestCase):
env=env env=env
) )
out, err = process_handle.communicate() out, err = process_handle.communicate()
result = CliExectutionResult(process_handle, out, err) result = CliExecutionResult(process_handle, out, err)
log.debug("command_args: '%s',stdout: '%s', stderr: '%s'", log.debug("command_args: '%s',stdout: '%s', stderr: '%s'",
command_args[0], out, err) command_args[0], out, err)
if check_errors: if check_errors:
@@ -129,6 +134,12 @@ class BaseTestCase(oslo_base.BaseTestCase):
call = self.run_cli_command(command, check_errors=check_errors) call = self.run_cli_command(command, check_errors=check_errors)
self.assertEqual(call.stdout, msg) self.assertEqual(call.stdout, msg)
def check_for_stdout_by_regexp(self, command, pattern, check_errors=True):
call = self.run_cli_command(command, check_errors=check_errors)
result = re.search(pattern, call.stdout)
self.assertIsNotNone(result)
return result
def check_for_stderr(self, command, msg, check_errors=True): def check_for_stderr(self, command, msg, check_errors=True):
call = self.run_cli_command(command, check_errors=check_errors) call = self.run_cli_command(command, check_errors=check_errors)
self.assertIn(msg, call.stderr) self.assertIn(msg, call.stderr)
@@ -147,3 +158,59 @@ class BaseTestCase(oslo_base.BaseTestCase):
def check_number_of_rows_in_table(self, command, number_of_rows): def check_number_of_rows_in_table(self, command, number_of_rows):
output = self.run_cli_command(command) output = self.run_cli_command(command)
self.assertEqual(len(output.stdout.split("\n")), number_of_rows + 3) self.assertEqual(len(output.stdout.split("\n")), number_of_rows + 3)
def _get_task_info(self, task_id):
"""Get info about task with given ID.
:param task_id: Task ID
:type task_id: str or int
:return: Task info
:rtype: dict
"""
return {}
def wait_task_ready(self, task_id, timeout=60, interval=3):
"""Wait for changing task status to 'ready'.
:param task_id: Task ID
:type task_id: str or int
:param timeout: Max time of waiting, in seconds
:type timeout: int
:param interval: Interval of getting task info, in seconds
:type interval: int
"""
wait_until_in_statuses = (consts.TASK_STATUSES.running,
consts.TASK_STATUSES.pending)
timer = time.time()
while True:
task = self._get_task_info(task_id)
status = task.get('status', '')
if status not in wait_until_in_statuses:
self.assertEqual(status, consts.TASK_STATUSES.ready)
break
if time.time() - timer > timeout:
raise Exception(
"Task '{0}' seems to be hanged".format(task['name'])
)
time.sleep(interval)
class CLIv1TestCase(BaseTestCase):
handler = 'fuel'
def _get_task_info(self, task_id):
command = "task --task {0} --json".format(str(task_id))
call = self.run_cli_command(command)
return json.loads(call.stdout)[0]
class CLIv2TestCase(BaseTestCase):
handler = 'fuel2'
def _get_task_info(self, task_id):
command = "task show -f json {0}".format(str(task_id))
call = self.run_cli_command(command)
return json.loads(call.stdout)

View File

@@ -20,7 +20,7 @@ import tempfile
from fuelclient.tests.functional import base from fuelclient.tests.functional import base
class TestHandlers(base.BaseTestCase): class TestHandlers(base.CLIv1TestCase):
def test_env_action(self): def test_env_action(self):
# check env help # check env help
@@ -289,7 +289,7 @@ class TestHandlers(base.BaseTestCase):
) )
class TestCharset(base.BaseTestCase): class TestCharset(base.CLIv1TestCase):
def test_charset_problem(self): def test_charset_problem(self):
self.load_data_to_nailgun_server() self.load_data_to_nailgun_server()
@@ -301,7 +301,7 @@ class TestCharset(base.BaseTestCase):
)) ))
class TestFiles(base.BaseTestCase): class TestFiles(base.CLIv1TestCase):
def test_file_creation(self): def test_file_creation(self):
self.load_data_to_nailgun_server() self.load_data_to_nailgun_server()
@@ -393,7 +393,7 @@ class TestFiles(base.BaseTestCase):
)) ))
class TestDownloadUploadNodeAttributes(base.BaseTestCase): class TestDownloadUploadNodeAttributes(base.CLIv1TestCase):
def test_upload_download_interfaces(self): def test_upload_download_interfaces(self):
self.load_data_to_nailgun_server() self.load_data_to_nailgun_server()
@@ -415,35 +415,43 @@ class TestDownloadUploadNodeAttributes(base.BaseTestCase):
self.upload_command(cmd))) self.upload_command(cmd)))
class TestDeployChanges(base.BaseTestCase): class TestDeployChanges(base.CLIv1TestCase):
create_env = "env create --name=test --release={0}" cmd_create_env = "env create --name=test --release={0}"
add_node = "--env-id=1 node set --node 1 --role=controller" cmd_add_node = "--env-id=1 node set --node 1 --role=controller"
deploy_changes = "deploy-changes --env 1" cmd_deploy_changes = "deploy-changes --env 1"
redeploy_changes = "redeploy-changes --env 1" cmd_redeploy_changes = "redeploy-changes --env 1"
messages_success = [
"Deploying changes to environment with id=1\n",
"Finished deployment!\n"
]
message_error = "(No changes to deploy)\n"
def setUp(self): def setUp(self):
super(TestDeployChanges, self).setUp() super(TestDeployChanges, self).setUp()
self.load_data_to_nailgun_server() self.load_data_to_nailgun_server()
release_id = self.get_first_deployable_release_id() release_id = self.get_first_deployable_release_id()
self.create_env = self.create_env.format(release_id) self.cmd_create_env = self.cmd_create_env.format(release_id)
self.run_cli_commands((self.create_env, self.add_node)) self.run_cli_commands((
self.cmd_create_env,
self.cmd_add_node
))
def test_deploy_changes(self): def test_deploy_changes(self):
self.run_cli_commands((self.deploy_changes,)) self.check_all_in_msg(self.cmd_deploy_changes,
self.messages_success)
def test_no_changes_to_deploy(self):
self.run_cli_commands((self.deploy_changes,))
self.check_for_stderr(self.deploy_changes,
"(No changes to deploy)\n",
check_errors=False)
def test_redeploy_changes(self): def test_redeploy_changes(self):
self.run_cli_commands((self.deploy_changes, self.run_cli_command(self.cmd_deploy_changes)
self.redeploy_changes)) self.check_for_stderr(self.cmd_deploy_changes,
self.message_error,
check_errors=False)
self.check_all_in_msg(self.cmd_redeploy_changes,
self.messages_success)
class TestDirectoryDoesntExistErrorMessages(base.BaseTestCase): class TestDirectoryDoesntExistErrorMessages(base.CLIv1TestCase):
def test_settings_upload(self): def test_settings_upload(self):
self.check_for_stderr( self.check_for_stderr(
@@ -520,7 +528,7 @@ class TestDirectoryDoesntExistErrorMessages(base.BaseTestCase):
) )
class TestUploadSettings(base.BaseTestCase): class TestUploadSettings(base.CLIv1TestCase):
create_env = "env create --name=test --release={0}" create_env = "env create --name=test --release={0}"
add_node = "--env-id=1 node set --node 1 --role=controller" add_node = "--env-id=1 node set --node 1 --role=controller"

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
#
# Copyright 2013-2014 Mirantis, Inc.
#
# 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.
from fuelclient.tests.functional import base
class TestDeployChanges(base.CLIv2TestCase):
cmd_create_env = "env create -r {0} cluster-test"
cmd_add_node = "env add nodes -e 1 -n 1 -r controller"
cmd_deploy_changes = "env deploy 1"
cmd_redeploy_changes = "env redeploy 1"
pattern_success = (r"^Deployment task with id (\d{1,}) "
r"for the environment 1 has been started.\n$")
message_error = "(No changes to deploy)\n"
def setUp(self):
super(TestDeployChanges, self).setUp()
self.load_data_to_nailgun_server()
release_id = self.get_first_deployable_release_id()
self.cmd_create_env = self.cmd_create_env.format(release_id)
self.run_cli_commands((
self.cmd_create_env,
self.cmd_add_node
))
def test_deploy_changes(self):
self.check_for_stdout_by_regexp(self.cmd_deploy_changes,
self.pattern_success)
def test_redeploy_changes(self):
result = self.check_for_stdout_by_regexp(self.cmd_deploy_changes,
self.pattern_success)
task_id = result.group(1)
self.wait_task_ready(task_id)
self.check_for_stderr(self.cmd_deploy_changes,
self.message_error,
check_errors=False)
self.check_for_stdout_by_regexp(self.cmd_redeploy_changes,
self.pattern_success)

View File

@@ -95,6 +95,13 @@ class TestEnvCommand(test_engine.BaseCLITest):
self.m_get_client.assert_called_once_with('environment', mock.ANY) self.m_get_client.assert_called_once_with('environment', mock.ANY)
self.m_client.deploy_changes.assert_called_once_with(42) self.m_client.deploy_changes.assert_called_once_with(42)
def test_env_redeploy(self):
args = 'env redeploy 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)
def test_env_add_nodes(self): def test_env_add_nodes(self):
args = 'env add nodes -e 42 -n 24 25 -r compute cinder' args = 'env add nodes -e 42 -n 24 25 -r compute cinder'
self.exec_command(args) self.exec_command(args)

View File

@@ -71,6 +71,12 @@ class EnvironmentClient(base_v1.BaseV1Client):
return deploy_task.id return deploy_task.id
def redeploy_changes(self, environment_id):
env = self._entity_wrapper(obj_id=environment_id)
redeploy_task = env.redeploy_changes()
return redeploy_task.id
def spawn_vms(self, environment_id): def spawn_vms(self, environment_id):
env = self._entity_wrapper(obj_id=environment_id) env = self._entity_wrapper(obj_id=environment_id)
return env.spawn_vms() return env.spawn_vms()

View File

@@ -34,6 +34,7 @@ fuelclient =
env_delete=fuelclient.commands.environment:EnvDelete env_delete=fuelclient.commands.environment:EnvDelete
env_deploy=fuelclient.commands.environment:EnvDeploy env_deploy=fuelclient.commands.environment:EnvDeploy
env_list=fuelclient.commands.environment:EnvList env_list=fuelclient.commands.environment:EnvList
env_redeploy=fuelclient.commands.environment:EnvRedeploy
env_show=fuelclient.commands.environment:EnvShow env_show=fuelclient.commands.environment:EnvShow
env_spawn-vms=fuelclient.commands.environment:EnvSpawnVms env_spawn-vms=fuelclient.commands.environment:EnvSpawnVms
env_update=fuelclient.commands.environment:EnvUpdate env_update=fuelclient.commands.environment:EnvUpdate

View File

@@ -16,7 +16,7 @@ setenv = VIRTUAL_ENV={envdir}
# Functional env settings # Functional env settings
FUEL_WEB_CLONE={env:FUEL_WEB_CLONE:yes} FUEL_WEB_CLONE={env:FUEL_WEB_CLONE:yes}
FUEL_WEB_REPO={env:FUEL_WEB_REPO:https://github.com/stackforge/fuel-web.git} FUEL_WEB_REPO={env:FUEL_WEB_REPO:https://github.com/openstack/fuel-web.git}
FUEL_WEB_ROOT={env:FUEL_WEB_ROOT:/tmp/fuel_web} FUEL_WEB_ROOT={env:FUEL_WEB_ROOT:/tmp/fuel_web}
FETCH_REPO={env:FETCH_REPO:} FETCH_REPO={env:FETCH_REPO:}
FETCH_REFSPEC={env:FETCH_REFSPEC:} FETCH_REFSPEC={env:FETCH_REFSPEC:}