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:
@@ -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)
|
||||||
|
|||||||
@@ -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'
|
||||||
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
56
fuelclient/tests/functional/v2/test_client.py
Normal file
56
fuelclient/tests/functional/v2/test_client.py
Normal 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)
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
2
tox.ini
2
tox.ini
@@ -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:}
|
||||||
|
|||||||
Reference in New Issue
Block a user