Add health check feature to Compass
Implement health check feature for Compass by Rally. User is able to check health by sending request after the cluster is deployed successfully. blueprint rally-acceptance-test Change-Id: If445ad3a97156bde3b623552d77bf186931b556b
This commit is contained in:
parent
98c97e5d6f
commit
899be8691c
@ -17,6 +17,7 @@
|
||||
import logging
|
||||
|
||||
from compass.actions import util
|
||||
from compass.db.api import health_check_report as health_check_db
|
||||
from compass.db.api import user as user_db
|
||||
from compass.deployment.deploy_manager import DeployManager
|
||||
from compass.deployment.utils import constants as const
|
||||
@ -46,7 +47,7 @@ def deploy(cluster_id, hosts_id_list, username=None):
|
||||
cluster_id, hosts_id_list, user)
|
||||
|
||||
deploy_manager = DeployManager(adapter_info, cluster_info, hosts_info)
|
||||
#deploy_manager.prepare_for_deploy()
|
||||
# deploy_manager.prepare_for_deploy()
|
||||
logging.debug('Created deploy manager with %s %s %s'
|
||||
% (adapter_info, cluster_info, hosts_info))
|
||||
|
||||
@ -80,7 +81,35 @@ def redeploy(cluster_id, hosts_id_list, username=None):
|
||||
util.ActionHelper.update_state(cluster_id, hosts_id_list, user)
|
||||
|
||||
|
||||
ActionHelper = util.ActionHelper
|
||||
def health_check(cluster_id, report_uri, username):
|
||||
with util.lock('cluster_health_check') as lock:
|
||||
if not lock:
|
||||
raise Exception('failed to acquire lock to check health')
|
||||
|
||||
user = user_db.get_user_object(username)
|
||||
cluster_info = util.ActionHelper.get_cluster_info(cluster_id, user)
|
||||
adapter_id = cluster_info[const.ADAPTER_ID]
|
||||
|
||||
adapter_info = util.ActionHelper.get_adapter_info(
|
||||
adapter_id, cluster_id, user
|
||||
)
|
||||
|
||||
deploy_manager = DeployManager(adapter_info, cluster_info, None)
|
||||
try:
|
||||
deploy_manager.check_cluster_health(report_uri)
|
||||
except Exception as exc:
|
||||
logging.error("health_check exception: ============= %s" % exc)
|
||||
data = {'state': 'error', 'error_message': str(exc), 'report': {}}
|
||||
reports = health_check_db.list_health_reports(user, cluster_id)
|
||||
if not reports:
|
||||
# Exception before executing command remotely for health check.
|
||||
# No reports names sending back yet. Create a report
|
||||
name = 'pre_remote_health_check'
|
||||
health_check_db.add_report_record(
|
||||
cluster_id, name=name, **data
|
||||
)
|
||||
|
||||
health_check_db.update_multi_reports(cluster_id, **data)
|
||||
|
||||
|
||||
class ServerPowerMgmt(object):
|
||||
|
@ -35,6 +35,7 @@ from compass.api import utils
|
||||
from compass.db.api import adapter_holder as adapter_api
|
||||
from compass.db.api import cluster as cluster_api
|
||||
from compass.db.api import database
|
||||
from compass.db.api import health_check_report as health_report_api
|
||||
from compass.db.api import host as host_api
|
||||
from compass.db.api import machine as machine_api
|
||||
from compass.db.api import metadata_holder as metadata_api
|
||||
@ -1506,6 +1507,8 @@ def delete_cluster_config(cluster_id):
|
||||
def take_cluster_action(cluster_id):
|
||||
"""take cluster action."""
|
||||
data = _get_request_data()
|
||||
url_root = request.url_root
|
||||
|
||||
update_cluster_hosts_func = _wrap_response(
|
||||
functools.partial(
|
||||
cluster_api.update_cluster_hosts, current_user, cluster_id
|
||||
@ -1524,13 +1527,22 @@ def take_cluster_action(cluster_id):
|
||||
),
|
||||
202
|
||||
)
|
||||
check_cluster_health_func = _wrap_response(
|
||||
functools.partial(
|
||||
health_report_api.start_check_cluster_health,
|
||||
current_user, cluster_id,
|
||||
'%s/clusters/%s/healthreports' % (url_root, cluster_id)
|
||||
),
|
||||
202
|
||||
)
|
||||
return _group_data_action(
|
||||
data,
|
||||
add_hosts=update_cluster_hosts_func,
|
||||
set_hosts=update_cluster_hosts_func,
|
||||
remove_hosts=update_cluster_hosts_func,
|
||||
review=review_cluster_func,
|
||||
deploy=deploy_cluster_func
|
||||
deploy=deploy_cluster_func,
|
||||
check_health=check_cluster_health_func
|
||||
)
|
||||
|
||||
|
||||
@ -1549,6 +1561,73 @@ def get_cluster_state(cluster_id):
|
||||
)
|
||||
|
||||
|
||||
@app.route("/clusters/<int:cluster_id>/healthreports", methods=['POST'])
|
||||
def create_health_reports(cluster_id):
|
||||
"""Create a health check report."""
|
||||
data = _get_request_data()
|
||||
output = []
|
||||
if 'report_list' in data:
|
||||
for report in data['report_list']:
|
||||
try:
|
||||
output.append(
|
||||
health_report_api.add_report_record(cluster_id, **report)
|
||||
)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
else:
|
||||
output = health_report_api.add_report_record(cluster_id, **data)
|
||||
|
||||
return utils.make_json_response(
|
||||
200,
|
||||
output
|
||||
)
|
||||
|
||||
|
||||
@app.route("/clusters/<int:cluster_id>/healthreports", methods=['PUT'])
|
||||
def bulk_update_reports(cluster_id):
|
||||
"""Bulk update reports."""
|
||||
data = _get_request_data()
|
||||
return utils.make_json_response(
|
||||
200,
|
||||
health_report_api.update_multi_reports(cluster_id, **data)
|
||||
)
|
||||
|
||||
|
||||
@app.route("/clusters/<int:cluster_id>/healthreports", methods=['GET'])
|
||||
@log_user_action
|
||||
@login_required
|
||||
@update_user_token
|
||||
def list_health_reports(cluster_id):
|
||||
return utils.make_json_response(
|
||||
200,
|
||||
health_report_api.list_health_reports(current_user, cluster_id)
|
||||
)
|
||||
|
||||
|
||||
@app.route("/clusters/<int:cluster_id>/healthreports/<name>", methods=['PUT'])
|
||||
def update_health_report(cluster_id, name):
|
||||
data = _get_request_data()
|
||||
if 'error_message' not in data:
|
||||
data['error_message'] = ""
|
||||
|
||||
return utils.make_json_response(
|
||||
200,
|
||||
health_report_api.update_report(cluster_id, name, **data)
|
||||
)
|
||||
|
||||
|
||||
@app.route("/clusters/<int:cluster_id>/healthreports/<name>", methods=['GET'])
|
||||
@log_user_action
|
||||
@login_required
|
||||
@update_user_token
|
||||
def get_health_report(cluster_id, name):
|
||||
return utils.make_json_response(
|
||||
200,
|
||||
health_report_api.get_health_report(current_user, cluster_id, name)
|
||||
)
|
||||
|
||||
|
||||
@app.route("/clusters/<int:cluster_id>/hosts", methods=['GET'])
|
||||
@log_user_action
|
||||
@login_required
|
||||
|
@ -98,7 +98,8 @@ def add_adapters_internal(session, exception_when_existing=True):
|
||||
distributed_system=distributed_system,
|
||||
os_installer=os_installer,
|
||||
package_installer=package_installer,
|
||||
deployable=config.get('DEPLOYABLE', False)
|
||||
deployable=config.get('DEPLOYABLE', False),
|
||||
health_check_cmd=config.get('HEALTH_CHECK_COMMAND', None)
|
||||
)
|
||||
supported_os_patterns = [
|
||||
re.compile(supported_os_pattern)
|
||||
|
@ -32,7 +32,7 @@ RESP_FIELDS = [
|
||||
'os_installer', 'package_installer',
|
||||
'distributed_system_id',
|
||||
'distributed_system_name',
|
||||
'supported_oses', 'display_name'
|
||||
'supported_oses', 'display_name', 'health_check_cmd'
|
||||
]
|
||||
RESP_OS_FIELDS = [
|
||||
'id', 'os_id', 'name'
|
||||
|
177
compass/db/api/health_check_report.py
Normal file
177
compass/db/api/health_check_report.py
Normal file
@ -0,0 +1,177 @@
|
||||
# Copyright 2014 Huawei Technologies Co. Ltd
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Cluster health check report."""
|
||||
import logging
|
||||
|
||||
from compass.db.api import database
|
||||
from compass.db.api import permission
|
||||
from compass.db.api import user as user_api
|
||||
from compass.db.api import utils
|
||||
from compass.db import exception
|
||||
from compass.db import models
|
||||
|
||||
|
||||
REQUIRED_INSERT_FIELDS = ['name']
|
||||
OPTIONAL_INSERT_FIELDS = [
|
||||
'display_name', 'report', 'category', 'state', 'error_message'
|
||||
]
|
||||
UPDATE_FIELDS = ['report', 'state', 'error_message']
|
||||
RESP_FIELDS = [
|
||||
'cluster_id', 'name', 'display_name', 'report',
|
||||
'category', 'state', 'error_message'
|
||||
]
|
||||
RESP_ACTION_FIELDS = ['cluster_id', 'status']
|
||||
|
||||
|
||||
@utils.supported_filters(REQUIRED_INSERT_FIELDS, OPTIONAL_INSERT_FIELDS)
|
||||
@database.run_in_session()
|
||||
@utils.wrap_to_dict(RESP_FIELDS)
|
||||
def add_report_record(cluster_id, name, report={},
|
||||
state='verifying', session=None, **kwargs):
|
||||
"""Create a health check report record."""
|
||||
# Replace any white space into '-'
|
||||
words = name.split()
|
||||
name = '-'.join(words)
|
||||
|
||||
return utils.add_db_object(
|
||||
session, models.HealthCheckReport, True, cluster_id, name,
|
||||
report=report, state=state, **kwargs
|
||||
)
|
||||
|
||||
|
||||
@utils.supported_filters(UPDATE_FIELDS)
|
||||
@database.run_in_session()
|
||||
@utils.wrap_to_dict(RESP_FIELDS)
|
||||
def update_report(cluster_id, name, session=None, **kwargs):
|
||||
"""Update health check report."""
|
||||
report = utils.get_db_object(
|
||||
session, models.HealthCheckReport, cluster_id=cluster_id, name=name
|
||||
)
|
||||
if report.state == 'finished':
|
||||
err_msg = 'Report cannot be updated if state is in "finished"'
|
||||
raise exception.Forbidden(err_msg)
|
||||
|
||||
return utils.update_db_object(session, report, **kwargs)
|
||||
|
||||
|
||||
@utils.supported_filters(UPDATE_FIELDS)
|
||||
@database.run_in_session()
|
||||
@utils.wrap_to_dict(RESP_FIELDS)
|
||||
def update_multi_reports(cluster_id, session=None, **kwargs):
|
||||
"""Bulk update reports."""
|
||||
return set_error(cluster_id, session=session, **kwargs)
|
||||
|
||||
|
||||
def set_error(cluster_id, report={}, session=None,
|
||||
state='error', error_message=None):
|
||||
with session.begin(subtransactions=True):
|
||||
logging.debug(
|
||||
"session %s updates all reports as %s in cluster %s",
|
||||
id(session), state, cluster_id
|
||||
)
|
||||
session.query(
|
||||
models.HealthCheckReport
|
||||
).filter_by(cluster_id=cluster_id).update(
|
||||
{"report": {}, 'state': 'error', 'error_message': error_message}
|
||||
)
|
||||
|
||||
reports = session.query(
|
||||
models.HealthCheckReport
|
||||
).filter_by(cluster_id=cluster_id).all()
|
||||
|
||||
return reports
|
||||
|
||||
|
||||
@database.run_in_session()
|
||||
@user_api.check_user_permission_in_session(
|
||||
permission.PERMISSION_LIST_HEALTH_REPORT
|
||||
)
|
||||
@utils.wrap_to_dict(RESP_FIELDS)
|
||||
def list_health_reports(user, cluster_id, session=None):
|
||||
"""List all reports in the specified cluster."""
|
||||
return utils.list_db_objects(
|
||||
session, models.HealthCheckReport, cluster_id=cluster_id
|
||||
)
|
||||
|
||||
|
||||
@database.run_in_session()
|
||||
@user_api.check_user_permission_in_session(
|
||||
permission.PERMISSION_GET_HEALTH_REPORT
|
||||
)
|
||||
@utils.wrap_to_dict(RESP_FIELDS)
|
||||
def get_health_report(user, cluster_id, name, session=None):
|
||||
return utils.get_db_object(
|
||||
session, models.HealthCheckReport, cluster_id=cluster_id, name=name
|
||||
)
|
||||
|
||||
|
||||
@database.run_in_session()
|
||||
@user_api.check_user_permission_in_session(
|
||||
permission.PERMISSION_DELETE_REPORT
|
||||
)
|
||||
@utils.wrap_to_dict(RESP_FIELDS)
|
||||
def delete_reports(user, cluster_id, name=None, session=None):
|
||||
if not name:
|
||||
report = utils.get_db_object(
|
||||
session, models.HealthCheckReport, cluster_id=cluster_id, name=name
|
||||
)
|
||||
return utils.del_db_object(session, report)
|
||||
|
||||
return utils.del_db_objects(
|
||||
session, models.HealthCheckReport, cluster_id=cluster_id
|
||||
)
|
||||
|
||||
|
||||
@utils.supported_filters(optional_support_keys=['check_health'])
|
||||
@database.run_in_session()
|
||||
@user_api.check_user_permission_in_session(
|
||||
permission.PERMISSION_CHECK_CLUSTER_HEALTH
|
||||
)
|
||||
@utils.wrap_to_dict(RESP_ACTION_FIELDS)
|
||||
def start_check_cluster_health(user, cluster_id, send_report_url,
|
||||
session=None, check_health={}):
|
||||
"""Start to check cluster health."""
|
||||
cluster_state = utils.get_db_object(
|
||||
session, models.Cluster, True, id=cluster_id
|
||||
).state_dict()
|
||||
|
||||
if cluster_state['state'] != 'SUCCESSFUL':
|
||||
logging.debug("state is %s" % cluster_state['state'])
|
||||
err_msg = "Healthcheck starts only after cluster finished deployment!"
|
||||
raise exception.Forbidden(err_msg)
|
||||
|
||||
reports = utils.list_db_objects(
|
||||
session, models.HealthCheckReport,
|
||||
cluster_id=cluster_id, state='verifying'
|
||||
)
|
||||
if reports:
|
||||
err_msg = 'Healthcheck in progress, please wait for it to complete!'
|
||||
raise exception.Forbidden(err_msg)
|
||||
|
||||
# Clear all preivous report
|
||||
utils.del_db_objects(
|
||||
session, models.HealthCheckReport, cluster_id=cluster_id
|
||||
)
|
||||
|
||||
from compass.tasks import client as celery_client
|
||||
celery_client.celery.send_task(
|
||||
'compass.tasks.cluster_health',
|
||||
(cluster_id, send_report_url, user.email)
|
||||
)
|
||||
return {
|
||||
"cluster_id": cluster_id,
|
||||
"status": "start to check cluster health."
|
||||
}
|
@ -208,6 +208,31 @@ PERMISSION_UPDATE_CLUSTERHOST_STATE = PermissionWrapper(
|
||||
'update clusterhost state',
|
||||
'update clusterhost state'
|
||||
)
|
||||
PERMISSION_LIST_HEALTH_REPORT = PermissionWrapper(
|
||||
'list_health_reports',
|
||||
'list health check report',
|
||||
'list health check report'
|
||||
)
|
||||
PERMISSION_GET_HEALTH_REPORT = PermissionWrapper(
|
||||
'get_health_report',
|
||||
'get health report',
|
||||
'get health report'
|
||||
)
|
||||
PERMISSION_CHECK_CLUSTER_HEALTH = PermissionWrapper(
|
||||
'start_check_cluster_health',
|
||||
'start check cluster health',
|
||||
'start check cluster health'
|
||||
)
|
||||
PERMISSION_SET_HEALTH_CHECK_ERROR = PermissionWrapper(
|
||||
'set_error_state',
|
||||
'set health check into error state',
|
||||
'set health check into error state'
|
||||
)
|
||||
PERMISSION_DELETE_REPORT = PermissionWrapper(
|
||||
'delete_reports',
|
||||
'delete health reports',
|
||||
'delete health reports'
|
||||
)
|
||||
PERMISSIONS = [
|
||||
PERMISSION_LIST_PERMISSIONS,
|
||||
PERMISSION_LIST_SWITCHES,
|
||||
@ -258,6 +283,11 @@ PERMISSIONS = [
|
||||
PERMISSION_DEL_CLUSTERHOST_CONFIG,
|
||||
PERMISSION_GET_CLUSTERHOST_STATE,
|
||||
PERMISSION_UPDATE_CLUSTERHOST_STATE,
|
||||
PERMISSION_LIST_HEALTH_REPORT,
|
||||
PERMISSION_GET_HEALTH_REPORT,
|
||||
PERMISSION_CHECK_CLUSTER_HEALTH,
|
||||
PERMISSION_SET_HEALTH_CHECK_ERROR,
|
||||
PERMISSION_DELETE_REPORT
|
||||
]
|
||||
|
||||
|
||||
|
@ -2466,6 +2466,8 @@ class Adapter(BASE, HelperMixin):
|
||||
Boolean, default=False
|
||||
)
|
||||
|
||||
health_check_cmd = Column(String(80))
|
||||
|
||||
supported_oses = relationship(
|
||||
AdapterOS,
|
||||
passive_deletes=True, passive_updates=True,
|
||||
@ -2732,3 +2734,41 @@ class Subnet(BASE, TimestampMixin, HelperMixin):
|
||||
if not self.name:
|
||||
dict_info['name'] = self.subnet
|
||||
return dict_info
|
||||
|
||||
|
||||
HEALTH_REPORT_STATES = ('verifying', 'success', 'finished', 'error')
|
||||
|
||||
|
||||
class HealthCheckReport(BASE, HelperMixin):
|
||||
"""Health check report table."""
|
||||
__tablename__ = 'health_check_report'
|
||||
|
||||
cluster_id = Column(
|
||||
Integer,
|
||||
ForeignKey('cluster.id', onupdate='CASCADE', ondelete='CASCADE'),
|
||||
primary_key=True
|
||||
)
|
||||
name = Column(String(80), nullable=False, primary_key=True)
|
||||
display_name = Column(String(100))
|
||||
report = Column(JSONEncoded, default={})
|
||||
category = Column(String(80), default='')
|
||||
state = Column(
|
||||
Enum(*HEALTH_REPORT_STATES, name='report_state'),
|
||||
ColumnDefault('verifying'),
|
||||
nullable=False
|
||||
)
|
||||
error_message = Column(Text, default='')
|
||||
|
||||
def __init__(self, cluster_id, name, **kwargs):
|
||||
self.cluster_id = cluster_id
|
||||
self.name = name
|
||||
if 'state' in kwargs and kwargs['state'] not in HEALTH_REPORT_STATES:
|
||||
err_msg = 'State value %s is not accepted.' % kwargs['state']
|
||||
raise exception.InvalidParameter(err_msg)
|
||||
|
||||
super(HealthCheckReport, self).__init__(**kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return 'HealthCheckReport[cluster_id: %s, name: %s]' % (
|
||||
self.cluster_id, self.name
|
||||
)
|
||||
|
@ -69,6 +69,10 @@ class DeployManager(object):
|
||||
|
||||
return deployed_config
|
||||
|
||||
def check_cluster_health(self, callback_url):
|
||||
logging.info("DeployManager check_cluster_health...........")
|
||||
self.pk_installer.check_cluster_health(callback_url)
|
||||
|
||||
def clean_progress(self):
|
||||
"""Clean previous installation log and progress."""
|
||||
self.clean_os_installtion_progress()
|
||||
|
@ -86,6 +86,16 @@ class BaseConfigManager(object):
|
||||
def get_cluster_os_config(self):
|
||||
return deepcopy(self.__get_cluster_item(const.OS_CONFIG, {}))
|
||||
|
||||
def get_server_credentials(self):
|
||||
cluster_os_config = self.get_cluster_os_config()
|
||||
if not cluster_os_config:
|
||||
logging.info("cluster os_config is None!")
|
||||
return ()
|
||||
|
||||
username = cluster_os_config[const.SERVER_CREDS][const.USERNAME]
|
||||
password = cluster_os_config[const.SERVER_CREDS][const.PASSWORD]
|
||||
return (username, password)
|
||||
|
||||
def get_cluster_package_config(self):
|
||||
return deepcopy(self.__get_cluster_item(const.PK_CONFIG, {}))
|
||||
|
||||
@ -309,6 +319,9 @@ class BaseConfigManager(object):
|
||||
def get_dist_system_name(self):
|
||||
return self.__get_adapter_item(const.NAME, None)
|
||||
|
||||
def get_adapter_health_check_cmd(self):
|
||||
return self.__get_adapter_item(const.HEALTH_CHECK_CMD)
|
||||
|
||||
def get_os_installer_settings(self):
|
||||
installer_info = self.__get_adapter_item(const.OS_INSTALLER, {})
|
||||
return installer_info.setdefault(const.INSTALLER_SETTINGS, {})
|
||||
|
@ -627,3 +627,35 @@ class ChefInstaller(PKInstaller):
|
||||
settings = self.config_manager.get_pk_installer_settings()
|
||||
|
||||
return settings.setdefault(self.CHEFSERVER_DNS, None)
|
||||
|
||||
def check_cluster_health(self, callback_url):
|
||||
import chef
|
||||
|
||||
cluster_name = self.config_manager.get_clustername()
|
||||
nodes = chef.Search(
|
||||
'node',
|
||||
'tags:rally_node AND name:*%s' % cluster_name,
|
||||
api=self.chef_api
|
||||
)
|
||||
if not nodes:
|
||||
err_msg = "Cannot find Rally node!"
|
||||
logging.info(err_msg)
|
||||
raise Exception(err_msg)
|
||||
|
||||
rally_node_name = None
|
||||
for node in nodes:
|
||||
rally_node_name = node.object.name
|
||||
break
|
||||
|
||||
rally_node = chef.Node(rally_node_name, api=self.chef_api)
|
||||
rally_node_ip = rally_node['ipaddress']
|
||||
|
||||
command = self.config_manager.get_adapter_health_check_cmd()
|
||||
option = '--url %s --clustername %s' % (callback_url, cluster_name)
|
||||
command = ' '.join((command, option))
|
||||
|
||||
username, pwd = self.config_manager.get_server_credentials()
|
||||
util.execute_cli_by_ssh(
|
||||
command, rally_node_ip, username=username,
|
||||
password=pwd, nowait=True
|
||||
)
|
||||
|
@ -34,6 +34,7 @@ DIST_SYS_NAME = 'distributed_system_name'
|
||||
FLAVOR = 'flavor'
|
||||
FLAVORS = 'flavors'
|
||||
FLAVOR_NAME = 'flavor_name'
|
||||
HEALTH_CHECK_CMD = 'health_check_cmd'
|
||||
TMPL = 'template'
|
||||
INSTALLER_SETTINGS = 'settings'
|
||||
METADATA = 'metadata'
|
||||
@ -77,4 +78,5 @@ OS_CONFIG_GENERAL = 'general'
|
||||
PK_CONFIG = 'package_config'
|
||||
ROLES = 'roles'
|
||||
ROLES_MAPPING = 'roles_mapping'
|
||||
SERVER_CREDS = 'server_credentials'
|
||||
TMPL_VARS_DICT = 'vars_dict'
|
||||
|
@ -80,6 +80,19 @@ def pollswitch(
|
||||
logging.exception(error)
|
||||
|
||||
|
||||
@celery.task(name='compass.tasks.cluster_health')
|
||||
def health_check(cluster_id, send_report_url, useremail):
|
||||
"""Verify the deployed cluster functionally works.
|
||||
|
||||
:param cluster_id: ID of the cluster
|
||||
:param send_report_url: The URL which reports should send back
|
||||
"""
|
||||
try:
|
||||
deploy.health_check(cluster_id, send_report_url, useremail)
|
||||
except Exception as error:
|
||||
logging.exception(error)
|
||||
|
||||
|
||||
@celery.task(name='compass.tasks.deploy_cluster')
|
||||
def deploy_cluster(deployer_email, cluster_id, clusterhost_ids):
|
||||
"""Deploy the given cluster.
|
||||
|
@ -25,6 +25,7 @@ os.environ['COMPASS_IGNORE_SETTING'] = 'true'
|
||||
|
||||
|
||||
from compass.actions import deploy
|
||||
from compass.actions import util
|
||||
from compass.utils import setting_wrapper as setting
|
||||
reload(setting)
|
||||
|
||||
@ -101,7 +102,7 @@ class TestDeployAction(unittest2.TestCase):
|
||||
"package_config": {}
|
||||
}
|
||||
}
|
||||
output = deploy.ActionHelper.get_adapter_info(1, 1, None)
|
||||
output = util.ActionHelper.get_adapter_info(1, 1, None)
|
||||
self.maxDiff = None
|
||||
self.assertDictEqual(expected_output, output)
|
||||
|
||||
@ -165,6 +166,6 @@ class TestDeployAction(unittest2.TestCase):
|
||||
"deployed_package_config": {}
|
||||
}
|
||||
}
|
||||
output = deploy.ActionHelper.get_hosts_info(1, [1], None)
|
||||
output = util.ActionHelper.get_hosts_info(1, [1], None)
|
||||
self.maxDiff = None
|
||||
self.assertDictEqual(expected_output, output)
|
||||
|
160
compass/tests/api/test_health_check_api.py
Normal file
160
compass/tests/api/test_health_check_api.py
Normal file
@ -0,0 +1,160 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 Huawei Technologies Co. Ltd
|
||||
#
|
||||
# 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.
|
||||
"""Test health check api."""
|
||||
|
||||
import os
|
||||
import simplejson as json
|
||||
|
||||
|
||||
os.environ['COMPASS_IGNORE_SETTING'] = 'true'
|
||||
from compass.utils import setting_wrapper as setting
|
||||
reload(setting)
|
||||
|
||||
|
||||
from compass.db.api import cluster as cluster_db
|
||||
from compass.db.api import health_check_report as health_check_db
|
||||
from compass.db import models
|
||||
from compass.tests.api.test_api import ApiTestCase
|
||||
|
||||
|
||||
report_sample = {
|
||||
"report": {
|
||||
"actions": {
|
||||
"neutron.create_network": {
|
||||
"duration": {
|
||||
"data": [1.105, 0.973],
|
||||
"summary": {
|
||||
"errors": 0,
|
||||
"success": "100.0%",
|
||||
"min (sec)": 0.973,
|
||||
"avg (sec)": 1.04,
|
||||
"max (sec)": 1.105,
|
||||
"total": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"neutron.delete_network": {
|
||||
"duration": {
|
||||
"data": [1.038, 0.842],
|
||||
"summary": {
|
||||
"errors": 0,
|
||||
"success": "100.0%",
|
||||
"min (sec)": 0.842,
|
||||
"avg (sec)": 0.940,
|
||||
"max (sec)": 1.038,
|
||||
"total": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors_info": []
|
||||
},
|
||||
"raw_output": {}
|
||||
}
|
||||
|
||||
|
||||
api_resp_tpml = {
|
||||
"cluster_id": 1,
|
||||
"name": "sample_name",
|
||||
"report": {},
|
||||
"state": "verifying",
|
||||
"errors_message": ""
|
||||
}
|
||||
|
||||
|
||||
class TestHealthCheckAPI(ApiTestCase):
|
||||
"""Test health check api."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestHealthCheckAPI, self).setUp()
|
||||
self.cluster_id = 1
|
||||
self.url = '/clusters/%s/healthreports' % self.cluster_id
|
||||
|
||||
def tearDown(self):
|
||||
super(TestHealthCheckAPI, self).tearDown()
|
||||
|
||||
def test_add_and_list_reports(self):
|
||||
# Create multiple reports
|
||||
reports_list = [
|
||||
{'name': 'rp1', 'category': 'c1'},
|
||||
{'name': 'rp2', 'category': 'c2'},
|
||||
{'name': 'rp3', 'category': 'c3'}
|
||||
]
|
||||
request_data = json.dumps({"report_list": reports_list})
|
||||
return_value = self.test_client.post(self.url, data=request_data)
|
||||
resp = json.loads(return_value.get_data())
|
||||
|
||||
self.assertEqual(200, return_value.status_code)
|
||||
self.assertEqual(3, len(resp))
|
||||
|
||||
# Create one report
|
||||
request_data = json.dumps({'name': 'rp4 test'})
|
||||
return_value = self.test_client.post(self.url, data=request_data)
|
||||
resp = json.loads(return_value.get_data())
|
||||
|
||||
self.assertEqual(200, return_value.status_code)
|
||||
self.assertEqual('rp4-test', resp['name'])
|
||||
|
||||
# Create duplicate report
|
||||
return_value = self.test_client.post(self.url, data=request_data)
|
||||
self.assertEqual(409, return_value.status_code)
|
||||
|
||||
# List all reports
|
||||
return_value = self.test_client.get(self.url)
|
||||
resp = json.loads(return_value.get_data())
|
||||
|
||||
self.assertEqual(200, return_value.status_code)
|
||||
self.assertEqual(4, len(resp))
|
||||
|
||||
def test_update_and_get_health_report(self):
|
||||
report_name = 'test-report'
|
||||
health_check_db.add_report_record(self.cluster_id, name=report_name)
|
||||
|
||||
url = '/'.join((self.url, report_name))
|
||||
request_data = json.dumps(
|
||||
{"report": report_sample, "state": "finished"}
|
||||
)
|
||||
return_value = self.test_client.put(url, data=request_data)
|
||||
resp = json.loads(return_value.get_data())
|
||||
self.maxDiff = None
|
||||
|
||||
self.assertEqual(200, return_value.status_code)
|
||||
self.assertDictEqual(report_sample, resp['report'])
|
||||
|
||||
return_value = self.test_client.put(url, data=request_data)
|
||||
self.assertEqual(403, return_value.status_code)
|
||||
|
||||
# Get report
|
||||
return_value = self.test_client.get(url)
|
||||
|
||||
self.assertEqual(200, return_value.status_code)
|
||||
self.assertDictEqual(report_sample, resp['report'])
|
||||
|
||||
def test_action_start_check_health(self):
|
||||
url = '/clusters/%s/action' % self.cluster_id
|
||||
request_data = json.dumps({'check_health': None})
|
||||
|
||||
# Cluster's state is not 'SUCCESSFUL' yet.
|
||||
return_value = self.test_client.post(url, data=request_data)
|
||||
self.assertEqual(403, return_value.status_code)
|
||||
|
||||
# Cluster has been deployed successfully.
|
||||
user = models.User.query.filter_by(email='admin@huawei.com').first()
|
||||
cluster_db.update_cluster_state(
|
||||
user, self.cluster_id, state='SUCCESSFUL'
|
||||
)
|
||||
return_value = self.test_client.post(url, data=request_data)
|
||||
self.assertEqual(202, return_value.status_code)
|
@ -46,7 +46,7 @@ class TestListPermissions(BaseTest):
|
||||
def test_list_permissions(self):
|
||||
permissions = permission.list_permissions(self.user_object)
|
||||
self.assertIsNotNone(permissions)
|
||||
self.assertEqual(49, len(permissions))
|
||||
self.assertEqual(54, len(permissions))
|
||||
|
||||
|
||||
class TestGetPermission(BaseTest):
|
||||
|
@ -258,3 +258,68 @@ def get_switch_machines_from_file(filename):
|
||||
})
|
||||
|
||||
return (switches, switch_machines)
|
||||
|
||||
|
||||
def execute_cli_by_ssh(cmd, host, username, password=None,
|
||||
keyfile='/root/.ssh/id_rsa', nowait=False):
|
||||
"""SSH to execute script on remote machine
|
||||
:param host: ip of the remote machine
|
||||
:param username: username to access the remote machine
|
||||
:param password: password to access the remote machine
|
||||
:param cmd: command to execute
|
||||
"""
|
||||
if not cmd:
|
||||
logging.error("No command found!")
|
||||
raise Exception('No command found!')
|
||||
|
||||
if nowait:
|
||||
cmd = "nohup %s >/dev/null 2>&1 &" % cmd
|
||||
|
||||
stdin = None
|
||||
stdout = None
|
||||
stderr = None
|
||||
try:
|
||||
import paramiko
|
||||
from paramiko import ssh_exception
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
if password:
|
||||
client.connect(host, username=username, password=password)
|
||||
else:
|
||||
client.load_system_host_keys()
|
||||
client.connect(
|
||||
host, username=username,
|
||||
key_filename=keyfile, look_for_keys=True
|
||||
)
|
||||
stdin, stdout, stderr = client.exec_command(cmd)
|
||||
result = stdout.readlines()
|
||||
logging.info("result of command '%s' is '%s'!" % (cmd, result))
|
||||
return result
|
||||
|
||||
except ImportError:
|
||||
err_msg = "Cannot find Paramiko package!"
|
||||
logging.error(err_msg)
|
||||
raise ImportError(err_msg)
|
||||
|
||||
except (ssh_exception.BadHostKeyException,
|
||||
ssh_exception.AuthenticationException,
|
||||
ssh_exception.SSHException):
|
||||
|
||||
err_msg = 'SSH connection error or command execution failed!'
|
||||
logging.error(err_msg)
|
||||
raise Exception(err_msg)
|
||||
|
||||
except Exception as exc:
|
||||
logging.error(
|
||||
'Failed to execute command "%s", exception is %s' % (cmd, exc)
|
||||
)
|
||||
raise Exception(exc)
|
||||
|
||||
finally:
|
||||
for resource in [stdin, stdout, stderr]:
|
||||
if resource:
|
||||
resource.close()
|
||||
|
||||
client.close()
|
||||
|
@ -5,3 +5,4 @@ PACKAGE_INSTALLER = 'chef_installer'
|
||||
OS_INSTALLER = 'cobbler'
|
||||
SUPPORTED_OS_PATTERNS = ['(?i)centos.*', '(?i)ubuntu.*']
|
||||
DEPLOYABLE = True
|
||||
HEALTH_CHECK_COMMAND = 'python /opt/compass/health_check.py'
|
||||
|
Loading…
x
Reference in New Issue
Block a user