Release 'fuel2 health *' command
As we deprecate old cli a new 'fuel2 health *' commands with extended functionality were implemented: fuel2 health list fuel2 health start fuel2 health stop fuel2 health restart fuel2 health status list fuel2 health status show DocImpact Change-Id: Ib9ba1912d0e0b391500d6bc257232fee0184620b Closes-Bug: 1619196
This commit is contained in:
parent
d4140d5472
commit
418dd206a4
|
@ -67,6 +67,7 @@ def get_client(resource, version='v1', connection=None):
|
|||
'extension': v1.extension,
|
||||
'fuel-version': v1.fuelversion,
|
||||
'graph': v1.graph,
|
||||
'health': v1.health,
|
||||
'network-configuration': v1.network_configuration,
|
||||
'network-group': v1.network_group,
|
||||
'node': v1.node,
|
||||
|
|
|
@ -116,6 +116,7 @@ def quote_and_join(words):
|
|||
return '"{0}"'.format(words[0])
|
||||
|
||||
|
||||
# TODO(vkulanov): remove when deprecate old cli
|
||||
def print_health_check(env):
|
||||
tests_states = [{"status": "not finished"}]
|
||||
finished_tests = set()
|
||||
|
|
|
@ -162,14 +162,15 @@ class APIClient(object):
|
|||
|
||||
return self._decode_content(resp)
|
||||
|
||||
def put_request(self, api, data, **params):
|
||||
def put_request(self, api, data, ostf=False, **params):
|
||||
"""Make PUT request to specific API with some data.
|
||||
|
||||
:param api: API endpoint (path)
|
||||
:param data: Data send in request, will be serialized to JSON
|
||||
:param ostf: is this a call to OSTF API
|
||||
:param params: Params of query string
|
||||
"""
|
||||
url = self.api_root + api
|
||||
url = (self.ostf_root if ostf else self.api_root) + api
|
||||
data_json = json.dumps(data)
|
||||
resp = self.session.put(url, data=data_json, params=params)
|
||||
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 Vitalii Kulanov
|
||||
#
|
||||
# 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from fuelclient.commands import base
|
||||
from fuelclient.common import data_utils
|
||||
|
||||
|
||||
class HealthMixIn(object):
|
||||
|
||||
entity_name = 'health'
|
||||
|
||||
|
||||
class HealthTestSetsList(HealthMixIn, base.BaseListCommand):
|
||||
"""List of all available test sets for a given environment."""
|
||||
|
||||
columns = ("id",
|
||||
"name")
|
||||
|
||||
filters = {'environment_id': 'env'}
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HealthTestSetsList, self).get_parser(prog_name)
|
||||
parser.add_argument('-e',
|
||||
'--env',
|
||||
type=int,
|
||||
required=True,
|
||||
help='Id of the environment.')
|
||||
return parser
|
||||
|
||||
|
||||
class HealthCheckStart(HealthMixIn, base.BaseListCommand):
|
||||
"""Run specified test sets for a given environment."""
|
||||
|
||||
columns = ("id",
|
||||
"testset",
|
||||
"cluster_id")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HealthCheckStart, self).get_parser(prog_name)
|
||||
parser.add_argument('-e',
|
||||
'--env',
|
||||
type=int,
|
||||
required=True,
|
||||
help='Id of the environment.')
|
||||
parser.add_argument('--force',
|
||||
action='store_true',
|
||||
help='Force run health test sets.')
|
||||
parser.add_argument('-t',
|
||||
'--tests',
|
||||
nargs='+',
|
||||
help='Name of the test sets to run.')
|
||||
parser.add_argument('--ostf-username',
|
||||
default=None,
|
||||
help='OSTF username.')
|
||||
parser.add_argument('--ostf-password',
|
||||
default=None,
|
||||
help='OSTF password.')
|
||||
parser.add_argument('--ostf-tenant-name',
|
||||
default=None,
|
||||
help='OSTF tenant name.')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ostf_credentials = {}
|
||||
if parsed_args.ostf_tenant_name is not None:
|
||||
ostf_credentials['tenant'] = parsed_args.ostf_tenant_name
|
||||
if parsed_args.ostf_username is not None:
|
||||
ostf_credentials['username'] = parsed_args.ostf_username
|
||||
if parsed_args.ostf_password is not None:
|
||||
ostf_credentials['password'] = parsed_args.ostf_password
|
||||
|
||||
if not ostf_credentials:
|
||||
self.app.stdout.write("WARNING: ostf credentials are going to be "
|
||||
"mandatory in the next release.\n")
|
||||
|
||||
data = self.client.start(parsed_args.env,
|
||||
ostf_credentials=ostf_credentials,
|
||||
test_sets=parsed_args.tests,
|
||||
force=parsed_args.force)
|
||||
|
||||
msg = ("\nHealth check tests for environment with id {0} has been "
|
||||
"started:\n".format(parsed_args.env))
|
||||
self.app.stdout.write(msg)
|
||||
data = data_utils.get_display_data_multi(self.columns, data)
|
||||
return self.columns, data
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class HealthCheckBaseAction(HealthMixIn, base.BaseShowCommand):
|
||||
"""Base class for implementing action over a given test set."""
|
||||
|
||||
columns = ("id",
|
||||
"testset",
|
||||
"cluster_id",
|
||||
"status")
|
||||
|
||||
@abc.abstractproperty
|
||||
def action_status(self):
|
||||
"""String with the name of the action."""
|
||||
pass
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
data = self.client.action(parsed_args.id, self.action_status)
|
||||
|
||||
data = data_utils.get_display_data_single(self.columns, data)
|
||||
return self.columns, data
|
||||
|
||||
|
||||
class HealthCheckStop(HealthCheckBaseAction):
|
||||
"""Stop test set with given id."""
|
||||
|
||||
action_status = "stopped"
|
||||
|
||||
|
||||
class HealthCheckRestart(HealthCheckBaseAction):
|
||||
"""Restart test set with given id."""
|
||||
|
||||
action_status = "restarted"
|
||||
|
||||
|
||||
class HealthTestSetsStatusList(HealthMixIn, base.BaseListCommand):
|
||||
"""Show list of statuses of all test sets ever been executed in Fuel."""
|
||||
|
||||
columns = ("id",
|
||||
"testset",
|
||||
"cluster_id",
|
||||
"status",
|
||||
"started_at",
|
||||
"ended_at")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HealthTestSetsStatusList, self).get_parser(prog_name)
|
||||
parser.add_argument('-e',
|
||||
'--env',
|
||||
type=int,
|
||||
help='Id of the environment.')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
data = self.client.get_status_all(parsed_args.env)
|
||||
|
||||
data = data_utils.get_display_data_multi(self.columns, data)
|
||||
return self.columns, data
|
||||
|
||||
|
||||
class HealthTestSetsStatusShow(HealthMixIn, base.BaseShowCommand):
|
||||
"""Show status about a test set with given id."""
|
||||
|
||||
columns = ("id",
|
||||
"testset",
|
||||
"cluster_id",
|
||||
"status",
|
||||
"started_at",
|
||||
"ended_at",
|
||||
"tests")
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
data = self.client.get_status_single(parsed_args.id)
|
||||
|
||||
data = data_utils.get_display_data_single(self.columns, data)
|
||||
return self.columns, data
|
|
@ -19,6 +19,7 @@ functionality from nailgun objects.
|
|||
from fuelclient.objects.base import BaseObject
|
||||
from fuelclient.objects.environment import Environment
|
||||
from fuelclient.objects.extension import Extension
|
||||
from fuelclient.objects.health import Health
|
||||
from fuelclient.objects.node import Node
|
||||
from fuelclient.objects.node import NodeCollection
|
||||
from fuelclient.objects.openstack_config import OpenstackConfig
|
||||
|
|
|
@ -398,6 +398,7 @@ class Environment(BaseObject):
|
|||
"nodes": node_facts
|
||||
}
|
||||
|
||||
# TODO(vkulanov): remove method when deprecate old cli
|
||||
def get_testsets(self):
|
||||
return self.connection.get_request(
|
||||
'testsets/{0}'.format(self.id),
|
||||
|
@ -409,9 +410,11 @@ class Environment(BaseObject):
|
|||
data = self.get_fresh_data()
|
||||
return data["is_customized"]
|
||||
|
||||
# TODO(vkulanov): remove method when deprecate old cli
|
||||
def is_in_running_test_sets(self, test_set):
|
||||
return test_set["testset"] in self._test_sets_to_run
|
||||
|
||||
# TODO(vkulanov): remove method when deprecate old cli
|
||||
def run_test_sets(self, test_sets_to_run, ostf_credentials=None):
|
||||
self._test_sets_to_run = test_sets_to_run
|
||||
|
||||
|
@ -440,6 +443,7 @@ class Environment(BaseObject):
|
|||
self._testruns_ids = [tr['id'] for tr in testruns]
|
||||
return testruns
|
||||
|
||||
# TODO(vkulanov): remove method when deprecate old cli
|
||||
def get_state_of_tests(self):
|
||||
return [
|
||||
self.connection.get_request(
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 Vitalii Kulanov
|
||||
#
|
||||
# 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.objects.base import BaseObject
|
||||
|
||||
|
||||
class Health(BaseObject):
|
||||
|
||||
class_api_path = "testruns/"
|
||||
instance_api_path = "testruns/{0}/"
|
||||
test_sets_api_path = "testsets/{0}/"
|
||||
|
||||
@classmethod
|
||||
def get_test_sets(cls, environment_id):
|
||||
return cls.connection.get_request(
|
||||
cls.test_sets_api_path.format(environment_id),
|
||||
ostf=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_tests_status_all(cls):
|
||||
return cls.connection.get_request(cls.class_api_path, ostf=True)
|
||||
|
||||
def get_tests_status_single(self):
|
||||
return self.connection.get_request(
|
||||
self.instance_api_path.format(self.id),
|
||||
ostf=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_last_tests_status(cls, environment_id):
|
||||
return cls.connection.get_request(
|
||||
'testruns/last/{0}'.format(environment_id),
|
||||
ostf=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def run_test_sets(cls, environment_id, test_sets_to_run,
|
||||
ostf_credentials=None):
|
||||
|
||||
def make_test_set(name):
|
||||
result = {
|
||||
"testset": name,
|
||||
"metadata": {
|
||||
"config": {},
|
||||
"cluster_id": environment_id,
|
||||
}
|
||||
}
|
||||
if ostf_credentials:
|
||||
creds = result['metadata'].setdefault(
|
||||
'ostf_os_access_creds', {})
|
||||
if 'tenant' in ostf_credentials:
|
||||
creds['ostf_os_tenant_name'] = ostf_credentials['tenant']
|
||||
if 'username' in ostf_credentials:
|
||||
creds['ostf_os_username'] = ostf_credentials['username']
|
||||
if 'password' in ostf_credentials:
|
||||
creds['ostf_os_password'] = ostf_credentials['password']
|
||||
return result
|
||||
|
||||
tests_data = [make_test_set(ts) for ts in test_sets_to_run]
|
||||
test_runs = cls.connection.post_request(cls.class_api_path,
|
||||
tests_data,
|
||||
ostf=True)
|
||||
return test_runs
|
||||
|
||||
def action_test(self, action_status):
|
||||
data = [{
|
||||
"id": self.id,
|
||||
"status": action_status
|
||||
}]
|
||||
return self.connection.put_request(
|
||||
'testruns/', data, ostf=True
|
||||
)
|
|
@ -0,0 +1,169 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 Vitalii Kulanov
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from fuelclient.tests.unit.v2.cli import test_engine
|
||||
from fuelclient.tests.utils import fake_health
|
||||
|
||||
|
||||
class TestHealthCommand(test_engine.BaseCLITest):
|
||||
"""Tests for fuel2 health * commands."""
|
||||
|
||||
def test_health_list_for_cluster(self):
|
||||
self.m_client.get_all.return_value = fake_health.get_fake_test_sets(10)
|
||||
cluster_id = 45
|
||||
args = 'health list -e {id}'.format(id=cluster_id)
|
||||
self.exec_command(args)
|
||||
self.m_client.get_all.assert_called_once_with(
|
||||
environment_id=cluster_id)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
@mock.patch('sys.stderr')
|
||||
def test_health_list_for_cluster_fail(self, mocked_stderr):
|
||||
args = 'health list'
|
||||
self.assertRaises(SystemExit, self.exec_command, args)
|
||||
self.assertIn('-e/--env',
|
||||
mocked_stderr.write.call_args_list[-1][0][0])
|
||||
|
||||
def test_health_status_list(self):
|
||||
self.m_client.get_status_all.return_value = [
|
||||
fake_health.get_fake_test_set_item(testset_id=12, cluster_id=30),
|
||||
fake_health.get_fake_test_set_item(testset_id=13, cluster_id=32),
|
||||
fake_health.get_fake_test_set_item(testset_id=14, cluster_id=35)
|
||||
]
|
||||
args = 'health status list'
|
||||
self.exec_command(args)
|
||||
self.m_client.get_status_all.assert_called_once_with(None)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
def test_health_status_list_for_cluster(self):
|
||||
cluster_id = 45
|
||||
self.m_client.get_status_all.return_value = [
|
||||
fake_health.get_fake_test_set_item(testset_id=12,
|
||||
cluster_id=cluster_id),
|
||||
fake_health.get_fake_test_set_item(testset_id=13,
|
||||
cluster_id=cluster_id),
|
||||
fake_health.get_fake_test_set_item(testset_id=14,
|
||||
cluster_id=cluster_id)
|
||||
]
|
||||
args = 'health status list -e {id}'.format(id=cluster_id)
|
||||
self.exec_command(args)
|
||||
self.m_client.get_status_all.assert_called_once_with(cluster_id)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
def test_health_status_show(self):
|
||||
testset_id = 66
|
||||
self.m_client.get_status_single.return_value = \
|
||||
fake_health.get_fake_test_set_item(testset_id=testset_id)
|
||||
args = 'health status show {id}'.format(id=testset_id)
|
||||
self.exec_command(args)
|
||||
self.m_client.get_status_single.assert_called_once_with(testset_id)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
@mock.patch('sys.stderr')
|
||||
def test_health_status_show_fail(self, mocked_stderr):
|
||||
args = 'health status show'
|
||||
self.assertRaises(SystemExit, self.exec_command, args)
|
||||
self.assertIn('id', mocked_stderr.write.call_args_list[0][0][0])
|
||||
|
||||
def test_health_start_force(self):
|
||||
cluster_id = 45
|
||||
testset = ['fake_test_set1', 'fake_test_set2']
|
||||
args = 'health start -e {id} -t {testset} --force'.format(
|
||||
id=cluster_id, testset=' '.join(testset))
|
||||
self.exec_command(args)
|
||||
self.m_client.start.assert_called_once_with(cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=testset,
|
||||
force=True)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
@mock.patch('sys.stderr')
|
||||
def test_health_start_w_wrong_parameters(self, mocked_stderr):
|
||||
args = 'health start'
|
||||
self.assertRaises(SystemExit, self.exec_command, args)
|
||||
self.assertIn('-e/--env',
|
||||
mocked_stderr.write.call_args_list[-1][0][0])
|
||||
|
||||
def test_health_start_wo_force(self):
|
||||
cluster_id = 45
|
||||
testset = ['fake_test_set1', 'fake_test_set2']
|
||||
args = 'health start -e {id} -t {testset}'.format(
|
||||
id=cluster_id, testset=' '.join(testset))
|
||||
self.exec_command(args)
|
||||
self.m_client.start.assert_called_once_with(cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=testset,
|
||||
force=False)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
def test_health_start_all_wo_force(self):
|
||||
cluster_id = 45
|
||||
args = 'health start -e {id}'.format(id=cluster_id)
|
||||
self.exec_command(args)
|
||||
self.m_client.start.assert_called_once_with(cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=None,
|
||||
force=False)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
def test_health_start_force_w_ostf_credentials(self):
|
||||
cluster_id = 45
|
||||
testset = ['fake_test_set1', 'fake_test_set2']
|
||||
ostf_credentials = {'username': 'fake_user',
|
||||
'password': 'fake_password',
|
||||
'tenant': 'fake_tenant_name'}
|
||||
|
||||
args = ('health start -e {id} -t {testset} --force --ostf-username '
|
||||
'fake_user --ostf-password fake_password --ostf-tenant-name '
|
||||
'fake_tenant_name'.format(id=cluster_id,
|
||||
testset=' '.join(testset)))
|
||||
|
||||
self.exec_command(args)
|
||||
self.m_client.start.assert_called_once_with(
|
||||
cluster_id,
|
||||
ostf_credentials=ostf_credentials,
|
||||
test_sets=testset,
|
||||
force=True
|
||||
)
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
def test_health_stop(self):
|
||||
testset_id = 66
|
||||
args = 'health stop {id}'.format(id=testset_id)
|
||||
self.exec_command(args)
|
||||
self.m_client.action.assert_called_once_with(testset_id, 'stopped')
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
@mock.patch('sys.stderr')
|
||||
def test_health_stop_fail(self, mocked_stderr):
|
||||
args = 'health stop'
|
||||
self.assertRaises(SystemExit, self.exec_command, args)
|
||||
self.assertIn('id', mocked_stderr.write.call_args_list[0][0][0])
|
||||
|
||||
def test_health_restart(self):
|
||||
testset_id = 66
|
||||
args = 'health restart {id}'.format(id=testset_id)
|
||||
self.exec_command(args)
|
||||
self.m_client.action.assert_called_once_with(testset_id, 'restarted')
|
||||
self.m_get_client.assert_called_once_with('health', mock.ANY)
|
||||
|
||||
@mock.patch('sys.stderr')
|
||||
def test_health_restart_fail(self, mocked_stderr):
|
||||
args = 'health restart'
|
||||
self.assertRaises(SystemExit, self.exec_command, args)
|
||||
self.assertIn('id', mocked_stderr.write.call_args_list[0][0][0])
|
|
@ -0,0 +1,292 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 Vitalii Kulanov
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
import fuelclient
|
||||
from fuelclient.cli import error
|
||||
from fuelclient.tests.unit.v2.lib import test_api
|
||||
from fuelclient.tests import utils
|
||||
|
||||
|
||||
class TestHealthFacade(test_api.BaseLibTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHealthFacade, self).setUp()
|
||||
|
||||
self.version = 'v1'
|
||||
self.res_uri = '/ostf/'
|
||||
self.fake_test_sets = utils.get_fake_test_sets(10)
|
||||
self.fake_test_sets_items = utils.get_fake_test_set_items(10)
|
||||
|
||||
self.client = fuelclient.get_client('health', self.version)
|
||||
|
||||
def test_health_list_for_cluster(self):
|
||||
cluster_id = 65
|
||||
expected_uri = self.res_uri + 'testsets/{0}/'.format(cluster_id)
|
||||
matcher = self.m_request.get(expected_uri, json=self.fake_test_sets)
|
||||
|
||||
data = self.client.get_all(cluster_id)
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(len(data), 10)
|
||||
|
||||
def test_health_status_list(self):
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
matcher = self.m_request.get(expected_uri,
|
||||
json=self.fake_test_sets_items)
|
||||
|
||||
data = self.client.get_status_all()
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(len(data), 10)
|
||||
|
||||
def test_health_status_list_for_cluster(self):
|
||||
cluster_id = 32
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
fake_test_sets_items = [
|
||||
utils.get_fake_test_set_item(testset_id=12, cluster_id=cluster_id),
|
||||
utils.get_fake_test_set_item(testset_id=13, cluster_id=cluster_id),
|
||||
utils.get_fake_test_set_item(testset_id=14, cluster_id=35)
|
||||
]
|
||||
matcher = self.m_request.get(expected_uri,
|
||||
json=fake_test_sets_items)
|
||||
|
||||
data = self.client.get_status_all(cluster_id)
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(len(data), 2)
|
||||
|
||||
def test_health_status_show(self):
|
||||
testrun_id = 65
|
||||
cluster_id = 32
|
||||
fake_test_set_item = utils.get_fake_test_set_item(
|
||||
testset_id=testrun_id, cluster_id=cluster_id)
|
||||
expected_uri = self.get_object_uri(self.res_uri + 'testruns/',
|
||||
testrun_id)
|
||||
matcher = self.m_request.get(expected_uri,
|
||||
json=fake_test_set_item)
|
||||
|
||||
data = self.client.get_status_single(testrun_id)
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(testrun_id, data["id"])
|
||||
self.assertEqual(cluster_id, data["cluster_id"])
|
||||
|
||||
def test_health_status_show_non_existing_testrun(self):
|
||||
testrun_id = 65
|
||||
expected_uri = self.get_object_uri(self.res_uri + 'testruns/',
|
||||
testrun_id)
|
||||
matcher = self.m_request.get(expected_uri, json={})
|
||||
|
||||
msg = "Test sets with id {id} does not exist".format(id=testrun_id)
|
||||
self.assertRaisesRegexp(error.ActionException,
|
||||
msg,
|
||||
self.client.get_status_single,
|
||||
testrun_id)
|
||||
self.assertTrue(matcher.called)
|
||||
|
||||
@mock.patch('fuelclient.objects.Environment')
|
||||
def test_health_start(self, m_env_obj):
|
||||
cluster_id = 32
|
||||
cluster_state = 'operational'
|
||||
test_sets = ['fake_test_set1', 'fake_test_set2']
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
type(m_env_obj.return_value).status = mock.PropertyMock(
|
||||
return_value=cluster_state)
|
||||
type(m_env_obj.return_value).is_customized = mock.PropertyMock(
|
||||
return_value=False)
|
||||
expected_body = [
|
||||
{'testset': test_sets[0],
|
||||
'metadata': {'cluster_id': cluster_id, 'config': {}}},
|
||||
{'testset': test_sets[1],
|
||||
'metadata': {'cluster_id': cluster_id, 'config': {}}}
|
||||
]
|
||||
matcher = self.m_request.post(expected_uri, json=expected_body)
|
||||
|
||||
data = self.client.start(cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=test_sets,
|
||||
force=False)
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(expected_body, matcher.last_request.json())
|
||||
self.assertEqual(data, matcher.last_request.json())
|
||||
|
||||
@mock.patch('fuelclient.objects.Environment')
|
||||
def test_health_start_fail_not_allowed_env_status(self, m_env_obj):
|
||||
cluster_id = 32
|
||||
cluster_state = 'new'
|
||||
test_sets = ['fake_test_set1', 'fake_test_set2']
|
||||
type(m_env_obj.return_value).status = mock.PropertyMock(
|
||||
return_value=cluster_state)
|
||||
|
||||
msg = ("Environment is not ready to run health check "
|
||||
"because it is in '{0}' state.".format(cluster_state))
|
||||
self.assertRaisesRegexp(error.EnvironmentException,
|
||||
msg,
|
||||
self.client.start,
|
||||
cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=test_sets,
|
||||
force=False)
|
||||
|
||||
@mock.patch('fuelclient.objects.Environment')
|
||||
def test_health_start_not_allowed_env_status_w_force(self, m_env_obj):
|
||||
cluster_id = 32
|
||||
cluster_state = 'new'
|
||||
test_sets = ['fake_test_set1', 'fake_test_set2']
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
type(m_env_obj.return_value).status = mock.PropertyMock(
|
||||
return_value=cluster_state)
|
||||
type(m_env_obj.return_value).is_customized = mock.PropertyMock(
|
||||
return_value=False)
|
||||
expected_body = [
|
||||
{'testset': test_sets[0],
|
||||
'metadata': {'cluster_id': cluster_id, 'config': {}}},
|
||||
{'testset': test_sets[1],
|
||||
'metadata': {'cluster_id': cluster_id, 'config': {}}}
|
||||
]
|
||||
matcher = self.m_request.post(expected_uri, json=expected_body)
|
||||
|
||||
data = self.client.start(cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=test_sets,
|
||||
force=True)
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(expected_body, matcher.last_request.json())
|
||||
self.assertEqual(data, matcher.last_request.json())
|
||||
|
||||
@mock.patch('fuelclient.objects.Environment')
|
||||
def test_health_start_fail_customized_env(self, m_env_obj):
|
||||
cluster_id = 32
|
||||
cluster_state = 'operational'
|
||||
is_customized = True
|
||||
test_sets = ['fake_test_set1', 'fake_test_set2']
|
||||
type(m_env_obj.return_value).status = mock.PropertyMock(
|
||||
return_value=cluster_state)
|
||||
type(m_env_obj.return_value).is_customized = mock.PropertyMock(
|
||||
return_value=is_customized)
|
||||
msg = ("Environment deployment facts were updated. "
|
||||
"Health check is likely to fail because of that.")
|
||||
self.assertRaisesRegexp(error.EnvironmentException,
|
||||
msg,
|
||||
self.client.start,
|
||||
cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=test_sets,
|
||||
force=False)
|
||||
|
||||
@mock.patch('fuelclient.objects.Environment')
|
||||
def test_health_start_customized_env_w_force(self, m_env_obj):
|
||||
cluster_id = 32
|
||||
cluster_state = 'operational'
|
||||
is_customized = True
|
||||
test_sets = ['fake_test_set1', 'fake_test_set2']
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
type(m_env_obj.return_value).status = mock.PropertyMock(
|
||||
return_value=cluster_state)
|
||||
type(m_env_obj.return_value).is_customized = mock.PropertyMock(
|
||||
return_value=is_customized)
|
||||
expected_body = [
|
||||
{'testset': test_sets[0],
|
||||
'metadata': {'cluster_id': cluster_id, 'config': {}}},
|
||||
{'testset': test_sets[1],
|
||||
'metadata': {'cluster_id': cluster_id, 'config': {}}}
|
||||
]
|
||||
matcher = self.m_request.post(expected_uri, json=expected_body)
|
||||
|
||||
data = self.client.start(cluster_id,
|
||||
ostf_credentials={},
|
||||
test_sets=test_sets,
|
||||
force=True)
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(expected_body, matcher.last_request.json())
|
||||
self.assertEqual(data, matcher.last_request.json())
|
||||
|
||||
@mock.patch('fuelclient.objects.Environment')
|
||||
def test_health_start_w_ostf_credentials(self, m_env_obj):
|
||||
cluster_id = 32
|
||||
cluster_state = 'operational'
|
||||
is_customized = False
|
||||
test_sets = ['fake_test_set1', 'fake_test_set2']
|
||||
ostf_credentials = {
|
||||
'username': 'admin',
|
||||
'password': 'admin',
|
||||
'tenant': 'admin'
|
||||
}
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
type(m_env_obj.return_value).status = mock.PropertyMock(
|
||||
return_value=cluster_state)
|
||||
type(m_env_obj.return_value).is_customized = mock.PropertyMock(
|
||||
return_value=is_customized)
|
||||
expected_body = [
|
||||
{'testset': test_sets[0],
|
||||
'metadata': {
|
||||
'ostf_os_access_creds':
|
||||
{'ostf_os_username': 'admin',
|
||||
'ostf_os_tenant_name': 'admin',
|
||||
'ostf_os_password': 'admin'},
|
||||
'cluster_id': cluster_id,
|
||||
'config': {}}},
|
||||
{'testset': test_sets[1],
|
||||
'metadata': {
|
||||
'ostf_os_access_creds':
|
||||
{'ostf_os_username': 'admin',
|
||||
'ostf_os_tenant_name': 'admin',
|
||||
'ostf_os_password': 'admin'},
|
||||
'cluster_id': cluster_id,
|
||||
'config': {}}}
|
||||
]
|
||||
|
||||
matcher = self.m_request.post(expected_uri, json=expected_body)
|
||||
|
||||
data = self.client.start(cluster_id,
|
||||
ostf_credentials=ostf_credentials,
|
||||
test_sets=test_sets,
|
||||
force=False)
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(expected_body, matcher.last_request.json())
|
||||
self.assertEqual(data, matcher.last_request.json())
|
||||
|
||||
def test_health_stop_action(self):
|
||||
testrun_id = 65
|
||||
cluster_id = 32
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
fake_test_set_item = utils.get_fake_test_set_item(
|
||||
testset_id=testrun_id, cluster_id=cluster_id)
|
||||
matcher = self.m_request.put(expected_uri,
|
||||
json=[fake_test_set_item])
|
||||
|
||||
data = self.client.action(testrun_id, action_status='stopped')
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(testrun_id, data["id"])
|
||||
self.assertEqual(cluster_id, data["cluster_id"])
|
||||
|
||||
def test_health_restart_action(self):
|
||||
testrun_id = 65
|
||||
cluster_id = 32
|
||||
expected_uri = self.res_uri + 'testruns/'
|
||||
fake_test_set_item = utils.get_fake_test_set_item(
|
||||
testset_id=testrun_id, cluster_id=cluster_id)
|
||||
matcher = self.m_request.put(expected_uri,
|
||||
json=[fake_test_set_item])
|
||||
|
||||
data = self.client.action(testrun_id, action_status='restarted')
|
||||
|
||||
self.assertTrue(matcher.called)
|
||||
self.assertEqual(testrun_id, data["id"])
|
||||
self.assertEqual(cluster_id, data["cluster_id"])
|
|
@ -36,6 +36,10 @@ from fuelclient.tests.utils.fake_extension import get_fake_env_extensions
|
|||
from fuelclient.tests.utils.fake_extension import get_fake_extension
|
||||
from fuelclient.tests.utils.fake_extension import get_fake_extensions
|
||||
from fuelclient.tests.utils.fake_fuel_version import get_fake_fuel_version
|
||||
from fuelclient.tests.utils.fake_health import get_fake_test_set
|
||||
from fuelclient.tests.utils.fake_health import get_fake_test_sets
|
||||
from fuelclient.tests.utils.fake_health import get_fake_test_set_item
|
||||
from fuelclient.tests.utils.fake_health import get_fake_test_set_items
|
||||
from fuelclient.tests.utils.fake_task import get_fake_task
|
||||
from fuelclient.tests.utils.fake_node_group import get_fake_node_group
|
||||
from fuelclient.tests.utils.fake_node_group import get_fake_node_groups
|
||||
|
@ -59,6 +63,10 @@ __all__ = (get_fake_deployment_history,
|
|||
get_fake_yaml_network_conf,
|
||||
get_fake_env,
|
||||
get_fake_env_network_conf,
|
||||
get_fake_test_set,
|
||||
get_fake_test_sets,
|
||||
get_fake_test_set_item,
|
||||
get_fake_test_set_items,
|
||||
get_fake_release,
|
||||
get_fake_releases,
|
||||
get_fake_attributes_metadata,
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 Vitalii Kulanov
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def get_fake_test_set(testset_id=None, name=None):
|
||||
"""Create a random fake test set for environment."""
|
||||
return {
|
||||
"id": testset_id or "fake_test_set",
|
||||
"name": name or "Fake tests. Duration 30 sec - 2 min"
|
||||
}
|
||||
|
||||
|
||||
def get_fake_test_sets(testsets_count, **kwargs):
|
||||
"""Create a random fake list of test sets for environment."""
|
||||
return [get_fake_test_set(**kwargs)
|
||||
for _ in range(testsets_count)]
|
||||
|
||||
|
||||
def get_fake_test_set_item(testset_id=None, testset=None, cluster_id=None,
|
||||
status=None, tests=None):
|
||||
"""Create a random fake test set item."""
|
||||
return {
|
||||
"id": testset_id or 45,
|
||||
"testset": testset or "fake_test_set",
|
||||
"cluster_id": cluster_id or 65,
|
||||
"status": status or "finished",
|
||||
"started_at": "2016-09-15 09:03:07.697393",
|
||||
"ended_at": "2016-09-15 09:03:19.280296",
|
||||
"tests": tests or [
|
||||
{
|
||||
"status": "failure",
|
||||
"taken": 1.0,
|
||||
"testset": "fake_test_set",
|
||||
"name": "Create fake instance",
|
||||
"duration": "30 s.",
|
||||
"message": "Fake test message",
|
||||
"id": "fuel_health.tests.fake_test",
|
||||
"description": "fake description"
|
||||
},
|
||||
{
|
||||
"status": "stopped",
|
||||
"taken": 0.5,
|
||||
"testset": "fake_test_set",
|
||||
"name": "Check create, update and delete fake instance image",
|
||||
"duration": "70 s.",
|
||||
"message": "Can not set proxy for Health Check.",
|
||||
"id": "fuel_health.tests.fake_test.test_update_fake_images",
|
||||
"description": "fake description"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def get_fake_test_set_items(items_count, **kwargs):
|
||||
"""Create a random fake list of test sets items."""
|
||||
return [get_fake_test_set_item(**kwargs)
|
||||
for _ in range(items_count)]
|
|
@ -19,6 +19,7 @@ from fuelclient.v1 import environment
|
|||
from fuelclient.v1 import extension
|
||||
from fuelclient.v1 import fuelversion
|
||||
from fuelclient.v1 import graph
|
||||
from fuelclient.v1 import health
|
||||
from fuelclient.v1 import network_configuration
|
||||
from fuelclient.v1 import network_group
|
||||
from fuelclient.v1 import node
|
||||
|
@ -39,6 +40,7 @@ __all__ = ('cluster_settings',
|
|||
'extension',
|
||||
'fuelversion',
|
||||
'graph',
|
||||
'health',
|
||||
'network_configuration',
|
||||
'network_group',
|
||||
'node',
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2016 Vitalii Kulanov
|
||||
#
|
||||
# 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.cli import error
|
||||
from fuelclient import objects
|
||||
from fuelclient.v1 import base_v1
|
||||
|
||||
|
||||
class HealthClient(base_v1.BaseV1Client):
|
||||
|
||||
_entity_wrapper = objects.Health
|
||||
_allowed_statuses = (
|
||||
'error',
|
||||
'operational',
|
||||
'update_error',
|
||||
)
|
||||
|
||||
def get_all(self, environment_id):
|
||||
"""Get list of test sets for a given environment.
|
||||
|
||||
:param environment_id: Id of environment
|
||||
:type environment_id: int
|
||||
:return: health test sets as a list of dict
|
||||
:rtype: list
|
||||
"""
|
||||
return self._entity_wrapper.get_test_sets(environment_id)
|
||||
|
||||
def get_status_all(self, environment_id=None):
|
||||
"""Get test sets statuses. If environment_id is None then statuses of
|
||||
all test sets will be retrieved.
|
||||
|
||||
:param environment_id: Id of environment
|
||||
:type environment_id: int
|
||||
:return: health test sets as a list of dict
|
||||
:rtype: list
|
||||
"""
|
||||
data = self._entity_wrapper.get_tests_status_all()
|
||||
# OSTF API doesn't support filtering by cluster, then do it 'manually'
|
||||
if environment_id is not None:
|
||||
data = [i for i in data if i['cluster_id'] == environment_id]
|
||||
return data
|
||||
|
||||
def get_status_single(self, testset_id):
|
||||
testrun_obj = self._entity_wrapper(testset_id)
|
||||
data = testrun_obj.get_tests_status_single()
|
||||
if data:
|
||||
result = []
|
||||
# Retrieve and re-format 'tests' from nested data for clarity
|
||||
for tests in data.get('tests'):
|
||||
result.append("\n* {} - {}, ('{}')".format(tests["status"],
|
||||
tests["name"],
|
||||
tests["message"]))
|
||||
else:
|
||||
msg = "Test sets with id {0} does not exist".format(testset_id)
|
||||
raise error.ActionException(msg)
|
||||
data['tests'] = ' '.join(result)
|
||||
return data
|
||||
|
||||
def get_last_test_status(self, environment_id):
|
||||
return self._entity_wrapper.get_last_tests_status(environment_id)
|
||||
|
||||
def start(self, environment_id, ostf_credentials=None, test_sets=None,
|
||||
force=False):
|
||||
"""Run test sets for a given environment. If test_sets is None then
|
||||
all test sets will be run
|
||||
|
||||
:param environment_id: Id of environment
|
||||
:type environment_id: int
|
||||
:param ostf_credentials: ostf credentials
|
||||
:type ostf_credentials: dict
|
||||
:param test_sets: list of test sets
|
||||
:type test_sets: list
|
||||
:param force:
|
||||
:type force: bool
|
||||
:return: running health test sets as a list of dict
|
||||
:rtype: list
|
||||
"""
|
||||
env_obj = objects.Environment(environment_id)
|
||||
|
||||
if env_obj.status not in self._allowed_statuses and not force:
|
||||
raise error.EnvironmentException(
|
||||
"Environment is not ready to run health check "
|
||||
"because it is in '{0}' state. Health check is likely "
|
||||
"to fail because of this. Use '--force' flag "
|
||||
"to proceed anyway.".format(env_obj.status)
|
||||
)
|
||||
|
||||
if env_obj.is_customized and not force:
|
||||
raise error.EnvironmentException(
|
||||
"Environment deployment facts were updated. "
|
||||
"Health check is likely to fail because of "
|
||||
"that. Use '--force' flag to proceed anyway."
|
||||
)
|
||||
test_sets_to_run = test_sets or set(ts['id'] for ts in
|
||||
self.get_all(environment_id))
|
||||
|
||||
return self._entity_wrapper.run_test_sets(environment_id,
|
||||
test_sets_to_run,
|
||||
ostf_credentials)
|
||||
|
||||
def action(self, testrun_id, action_status):
|
||||
"""Make an action on specific test set.
|
||||
|
||||
:param testrun_id: id of test set
|
||||
:type testrun_id: int
|
||||
:param action_status: the type of action ('stopped', 'restarted')
|
||||
:type action_status: str
|
||||
"""
|
||||
testrun_obj = self._entity_wrapper(obj_id=testrun_id)
|
||||
return testrun_obj.action_test(action_status)[0]
|
||||
|
||||
|
||||
def get_client(connection):
|
||||
return HealthClient(connection)
|
|
@ -66,6 +66,12 @@ fuelclient =
|
|||
graph_execute=fuelclient.commands.graph:GraphExecute
|
||||
graph_list=fuelclient.commands.graph:GraphList
|
||||
graph_upload=fuelclient.commands.graph:GraphUpload
|
||||
health_list=fuelclient.commands.health:HealthTestSetsList
|
||||
health_restart=fuelclient.commands.health:HealthCheckRestart
|
||||
health_start=fuelclient.commands.health:HealthCheckStart
|
||||
health_status_list=fuelclient.commands.health:HealthTestSetsStatusList
|
||||
health_status_show=fuelclient.commands.health:HealthTestSetsStatusShow
|
||||
health_stop=fuelclient.commands.health:HealthCheckStop
|
||||
network-group_create=fuelclient.commands.network_group:NetworkGroupCreate
|
||||
network-group_delete=fuelclient.commands.network_group:NetworkGroupDelete
|
||||
network-group_list=fuelclient.commands.network_group:NetworkGroupList
|
||||
|
|
Loading…
Reference in New Issue