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:
grace.yu 2015-01-06 14:38:33 -08:00
parent 98c97e5d6f
commit 899be8691c
17 changed files with 655 additions and 8 deletions

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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'

View 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."
}

View File

@ -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
]

View File

@ -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
)

View File

@ -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()

View File

@ -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, {})

View File

@ -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
)

View File

@ -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'

View File

@ -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.

View File

@ -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)

View 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)

View File

@ -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):

View File

@ -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()

View File

@ -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'