Merge "Add health check feature to Compass" into dev/experimental
This commit is contained in:
commit
a5f72f2086
@ -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…
Reference in New Issue
Block a user