From a8b54b225871145d4e31f591735648aa83d30780 Mon Sep 17 00:00:00 2001 From: xiaodongwang Date: Fri, 24 Oct 2014 14:11:40 -0700 Subject: [PATCH] delete cluster and set cluster_id in chef server env Change-Id: I33b7d978a0f2cfabd1a6e3b0177dfec059dc9666 --- bin/client.py | 7 +- bin/delete_clusters.py | 55 +++-- bin/poll_switch.py | 2 +- bin/progress_update.py | 3 +- compass/actions/clean_deployment.py | 47 ---- compass/actions/delete.py | 129 ++++++++++ compass/actions/deploy.py | 225 ++--------------- compass/actions/util.py | 227 +++++++++++++++++- compass/api/api.py | 8 +- compass/db/api/cluster.py | 83 ++++++- compass/db/api/host.py | 32 ++- compass/db/api/machine.py | 7 + compass/db/api/network.py | 14 ++ compass/db/api/utils.py | 30 +-- compass/db/models.py | 88 ++++++- compass/deployment/deploy_manager.py | 4 +- .../chef_installer/chef_installer.py | 26 +- compass/tasks/tasks.py | 77 +++++- compass/tests/api/test_api.py | 8 +- compass/tests/db/api/test_cluster.py | 33 +-- compass/tests/db/api/test_host.py | 18 +- .../environments/allinone.tmpl | 5 + .../environments/multinodes.tmpl | 5 + 23 files changed, 776 insertions(+), 357 deletions(-) delete mode 100644 compass/actions/clean_deployment.py create mode 100644 compass/actions/delete.py diff --git a/bin/client.py b/bin/client.py index fa6a3c92..53101b04 100755 --- a/bin/client.py +++ b/bin/client.py @@ -16,11 +16,8 @@ """binary to deploy a cluster by compass client api.""" import logging -import netaddr import os import re -import requests -import simplejson as json import socket import sys import time @@ -32,6 +29,10 @@ sys.path.append(current_dir) import switch_virtualenv +import netaddr +import requests +import simplejson as json + from compass.apiclient.restful import Client from compass.utils import flags from compass.utils import logsetting diff --git a/bin/delete_clusters.py b/bin/delete_clusters.py index faa846c5..277daed9 100755 --- a/bin/delete_clusters.py +++ b/bin/delete_clusters.py @@ -28,17 +28,19 @@ sys.path.append(current_dir) import switch_virtualenv +from compass.actions import delete from compass.db.api import cluster as cluster_api from compass.db.api import database -from compass.db.api import host as host_api from compass.db.api import user as user_api -from compass.db.api import utils -from compass.db import models +from compass.tasks.client import celery from compass.utils import flags from compass.utils import logsetting from compass.utils import setting_wrapper as setting +flags.add_bool('async', + help='run in async mode', + default=True) flags.add('clusternames', help='comma seperated cluster names', default='') @@ -47,27 +49,46 @@ flags.add_bool('delete_hosts', default=False) -if __name__ == '__main__': - flags.init() - logsetting.init() - database.init() +def delete_clusters(): clusternames = [ clustername for clustername in flags.OPTIONS.clusternames.split(',') if clustername ] - logging.info('delete clusters %s', clusternames) user = user_api.get_user_object(setting.COMPASS_ADMIN_EMAIL) clusters = cluster_api.list_clusters( user, name=clusternames ) - if flags.OPTIONS.delete_hosts: - for cluster in clusters: - hosts = cluster_api.list_cluster_hosts( - user, cluster['id']) - for host in hosts: - logging.info('delete host %s', host['hostname']) - host_api.del_host(user, host['id']) + delete_underlying_host = flags.OPTIONS.delete_hosts for cluster in clusters: - logging.info('delete cluster %s', cluster['name']) - cluster_api.del_cluster(user, cluster['id']) + cluster_id = cluster['id'] + hosts = cluster_api.list_cluster_hosts(user, cluster_id) + host_id_list = [host['id'] for host in hosts] + if flags.OPTIONS.async: + celery.send_task( + 'compass.tasks.delete_cluster', + ( + setting.COMPASS_ADMIN_EMAIL, + cluster_id, + host_id_list, + delete_underlying_host + ) + ) + else: + try: + delete.delete_cluster( + cluster_id, + host_id_list, + setting.COMPASS_ADMIN_EMAIL, + delete_underlying_host + ) + except Exception as error: + logging.error('failed to delete cluster %s', cluster) + logging.exception(error) + + +if __name__ == '__main__': + flags.init() + logsetting.init() + database.init() + delete_clusters() diff --git a/bin/poll_switch.py b/bin/poll_switch.py index ab051e19..779fa1b8 100755 --- a/bin/poll_switch.py +++ b/bin/poll_switch.py @@ -16,7 +16,6 @@ """main script to poll machines which is connected to the switches.""" import functools -import lockfile import logging import os import sys @@ -28,6 +27,7 @@ sys.path.append(current_dir) import switch_virtualenv +import lockfile from multiprocessing import Pool from compass.actions import poll_switch diff --git a/bin/progress_update.py b/bin/progress_update.py index 7a968882..cc8c12bf 100755 --- a/bin/progress_update.py +++ b/bin/progress_update.py @@ -16,7 +16,6 @@ """main script to run as service to update hosts installing progress.""" import functools -import lockfile import logging import os import sys @@ -28,6 +27,8 @@ sys.path.append(current_dir) import switch_virtualenv +import lockfile + from compass.actions import update_progress from compass.db.api import database from compass.tasks.client import celery diff --git a/compass/actions/clean_deployment.py b/compass/actions/clean_deployment.py deleted file mode 100644 index 7b1be985..00000000 --- a/compass/actions/clean_deployment.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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. - -"""Module to clean deployment of a given cluster - - .. moduleauthor:: Xiaodong Wang -""" -import logging - -from compass.actions import util -from compass.config_management.utils.config_manager import ConfigManager -from compass.db.api import database - - -def clean_deployment(cluster_hosts): - """Clean deployment of clusters. - - :param cluster_hosts: clusters and hosts in each cluster to clean. - :type cluster_hosts: dict of int or str to list of int or str - - .. note:: - The function should be called out of database session. - """ - with util.lock('serialized_action') as lock: - if not lock: - raise Exception( - 'failed to acquire lock to clean deployment') - - logging.debug('clean cluster_hosts: %s', cluster_hosts) - with database.session(): - cluster_hosts, os_versions, target_systems = ( - util.update_cluster_hosts(cluster_hosts)) - manager = ConfigManager() - manager.clean_cluster_and_hosts( - cluster_hosts, os_versions, target_systems) - manager.sync() diff --git a/compass/actions/delete.py b/compass/actions/delete.py new file mode 100644 index 00000000..fdd31685 --- /dev/null +++ b/compass/actions/delete.py @@ -0,0 +1,129 @@ +# 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. + +"""Module to delete a given cluster +""" +import logging + +from compass.actions import util +from compass.db.api import user as user_db +from compass.deployment.deploy_manager import DeployManager +from compass.deployment.utils import constants as const + + +def delete_cluster( + cluster_id, host_id_list, + username=None, delete_underlying_host=False +): + """Delete cluster. + + :param cluster_id: id of the cluster. + :type cluster_id: int + + .. note:: + The function should be called out of database session. + """ + with util.lock('serialized_action', timeout=100) as lock: + if not lock: + raise Exception('failed to acquire lock to delete cluster') + + 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) + hosts_info = util.ActionHelper.get_hosts_info( + cluster_id, host_id_list, user) + + deploy_manager = DeployManager(adapter_info, cluster_info, hosts_info) + logging.debug('Created deploy manager with %s %s %s' + % (adapter_info, cluster_info, hosts_info)) + + deploy_manager.remove_hosts( + package_only=not delete_underlying_host, + delete_cluster=True + ) + util.ActionHelper.delete_cluster( + cluster_id, host_id_list, user, + delete_underlying_host + ) + + +def delete_cluster_host( + cluster_id, host_id, + username=None, delete_underlying_host=False +): + with util.lock('serialized_action', timeout=100) as lock: + if not lock: + raise Exception('failed to acquire lock to delete clusterhost') + + 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) + hosts_info = util.ActionHelper.get_hosts_info( + cluster_id, [host_id], user) + + deploy_manager = DeployManager(adapter_info, cluster_info, hosts_info) + logging.debug('Created deploy manager with %s %s %s' + % (adapter_info, cluster_info, hosts_info)) + + deploy_manager.remove_hosts( + package_only=not delete_underlying_host, + delete_cluster=False + ) + util.ActionHelper.delete_cluster_host( + cluster_id, host_id, user, + delete_underlying_host + ) + + +def delete_host( + host_id, cluster_id_list, username=None +): + with util.lock('serialized_action', timeout=100) as lock: + if not lock: + raise Exception('failed to acquire lock to delete host') + + user = user_db.get_user_object(username) + for cluster_id in cluster_id_list: + 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) + hosts_info = util.ActionHelper.get_hosts_info( + cluster_id, [host_id], user) + + deploy_manager = DeployManager( + adapter_info, cluster_info, hosts_info) + logging.debug('Created deploy manager with %s %s %s' + % (adapter_info, cluster_info, hosts_info)) + + deploy_manager.remove_hosts( + package_only=True, + delete_cluster=False + ) + + util.ActionHelper.delete_host( + host_id, user + ) + + +ActionHelper = util.ActionHelper diff --git a/compass/actions/deploy.py b/compass/actions/deploy.py index 26c3c086..dfc87f32 100644 --- a/compass/actions/deploy.py +++ b/compass/actions/deploy.py @@ -14,15 +14,12 @@ """Module to deploy a given cluster """ +import logging from compass.actions import util -from compass.db.api import adapter_holder as adapter_db -from compass.db.api import cluster as cluster_db -from compass.db.api import machine as machine_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 -import logging def deploy(cluster_id, hosts_id_list, username=None): @@ -40,13 +37,13 @@ def deploy(cluster_id, hosts_id_list, username=None): user = user_db.get_user_object(username) - cluster_info = ActionHelper.get_cluster_info(cluster_id, user) + cluster_info = util.ActionHelper.get_cluster_info(cluster_id, user) adapter_id = cluster_info[const.ADAPTER_ID] - adapter_info = ActionHelper.get_adapter_info(adapter_id, cluster_id, - user) - hosts_info = ActionHelper.get_hosts_info(cluster_id, hosts_id_list, - user) + adapter_info = util.ActionHelper.get_adapter_info( + adapter_id, cluster_id, user) + hosts_info = util.ActionHelper.get_hosts_info( + cluster_id, hosts_id_list, user) deploy_manager = DeployManager(adapter_info, cluster_info, hosts_info) #deploy_manager.prepare_for_deploy() @@ -54,8 +51,8 @@ def deploy(cluster_id, hosts_id_list, username=None): % (adapter_info, cluster_info, hosts_info)) deployed_config = deploy_manager.deploy() - ActionHelper.save_deployed_config(deployed_config, user) - ActionHelper.update_state(cluster_id, hosts_id_list, user) + util.ActionHelper.save_deployed_config(deployed_config, user) + util.ActionHelper.update_state(cluster_id, hosts_id_list, user) def redeploy(cluster_id, hosts_id_list, username=None): @@ -69,20 +66,21 @@ def redeploy(cluster_id, hosts_id_list, username=None): raise Exception('failed to acquire lock to deploy') user = user_db.get_user_object(username) - cluster_info = ActionHelper.get_cluster_info(cluster_id, user) + cluster_info = util.ActionHelper.get_cluster_info(cluster_id, user) adapter_id = cluster_info[const.ADAPTER_ID] - adapter_info = ActionHelper.get_adapter_info(adapter_id, - cluster_id, - user) - hosts_info = ActionHelper.get_hosts_info(cluster_id, - hosts_id_list, - user) + adapter_info = util.ActionHelper.get_adapter_info( + adapter_id, cluster_id, user) + hosts_info = util.ActionHelper.get_hosts_info( + cluster_id, hosts_id_list, user) deploy_manager = DeployManager(adapter_info, cluster_info, hosts_info) # deploy_manager.prepare_for_deploy() deploy_manager.redeploy() - ActionHelper.update_state(cluster_id, hosts_id_list, user) + util.ActionHelper.update_state(cluster_id, hosts_id_list, user) + + +ActionHelper = util.ActionHelper class ServerPowerMgmt(object): @@ -117,192 +115,3 @@ class HostPowerMgmt(object): @staticmethod def reset(host_id, user): pass - - -class ActionHelper(object): - - @staticmethod - def get_adapter_info(adapter_id, cluster_id, user): - """Get adapter information. Return a dictionary as below, - { - "id": 1, - "name": "xxx", - "flavors": [ - { - "flavor_name": "xxx", - "roles": ['xxx', 'yyy', ...], - "template": "xxx.tmpl" - }, - ... - ], - "metadata": { - "os_config": { - ... - }, - "package_config": { - ... - } - }, - "os_installer": { - "name": "cobbler", - "settings": {....} - }, - "pk_installer": { - "name": "chef", - "settings": {....} - }, - ... - } - To view a complete output, please refer to backend doc. - """ - adapter_info = adapter_db.get_adapter(user, adapter_id) - metadata = cluster_db.get_cluster_metadata(user, cluster_id) - adapter_info.update({const.METADATA: metadata}) - - for flavor_info in adapter_info[const.FLAVORS]: - roles = flavor_info[const.ROLES] - flavor_info[const.ROLES] = ActionHelper._get_role_names(roles) - - return adapter_info - - @staticmethod - def _get_role_names(roles): - return [role[const.NAME] for role in roles] - - @staticmethod - def get_cluster_info(cluster_id, user): - """Get cluster information.Return a dictionary as below, - { - "id": 1, - "adapter_id": 1, - "os_version": "CentOS-6.5-x86_64", - "name": "cluster_01", - "flavor": { - "flavor_name": "zzz", - "template": "xx.tmpl", - "roles": [...] - } - "os_config": {..}, - "package_config": {...}, - "deployed_os_config": {}, - "deployed_package_config": {}, - "owner": "xxx" - } - """ - cluster_info = cluster_db.get_cluster(user, cluster_id) - - # convert roles retrieved from db into a list of role names - roles_info = cluster_info.setdefault( - const.FLAVOR, {}).setdefault(const.ROLES, []) - cluster_info[const.FLAVOR][const.ROLES] = \ - ActionHelper._get_role_names(roles_info) - - # get cluster config info - cluster_config = cluster_db.get_cluster_config(user, cluster_id) - cluster_info.update(cluster_config) - - deploy_config = cluster_db.get_cluster_deployed_config(user, - cluster_id) - cluster_info.update(deploy_config) - - return cluster_info - - @staticmethod - def get_hosts_info(cluster_id, hosts_id_list, user): - """Get hosts information. Return a dictionary as below, - { - "hosts": { - 1($host_id): { - "reinstall_os": True, - "mac": "xxx", - "name": "xxx", - "roles": [xxx, yyy] - }, - "networks": { - "eth0": { - "ip": "192.168.1.1", - "netmask": "255.255.255.0", - "is_mgmt": True, - "is_promiscuous": False, - "subnet": "192.168.1.0/24" - }, - "eth1": {...} - }, - "os_config": {}, - "package_config": {}, - "deployed_os_config": {}, - "deployed_package_config": {} - }, - 2: {...}, - .... - } - } - """ - hosts_info = {} - for host_id in hosts_id_list: - info = cluster_db.get_cluster_host(user, cluster_id, host_id) - logging.debug("checking on info %r %r" % (host_id, info)) - - info[const.ROLES] = ActionHelper._get_role_names(info[const.ROLES]) - - # TODO(grace): Is following line necessary?? - info.setdefault(const.ROLES, []) - - config = cluster_db.get_cluster_host_config(user, - cluster_id, - host_id) - info.update(config) - - networks = info[const.NETWORKS] - networks_dict = {} - # Convert networks from list to dictionary format - for entry in networks: - nic_info = {} - nic_info = { - entry[const.NIC]: { - const.IP_ADDR: entry[const.IP_ADDR], - const.NETMASK: entry[const.NETMASK], - const.MGMT_NIC_FLAG: entry[const.MGMT_NIC_FLAG], - const.PROMISCUOUS_FLAG: entry[const.PROMISCUOUS_FLAG], - const.SUBNET: entry[const.SUBNET] - } - } - networks_dict.update(nic_info) - - info[const.NETWORKS] = networks_dict - - hosts_info[host_id] = info - - return hosts_info - - @staticmethod - def save_deployed_config(deployed_config, user): - cluster_config = deployed_config[const.CLUSTER] - cluster_id = cluster_config[const.ID] - del cluster_config[const.ID] - - cluster_db.update_cluster_deployed_config(user, cluster_id, - **cluster_config) - - hosts_id_list = deployed_config[const.HOSTS].keys() - for host_id in hosts_id_list: - config = deployed_config[const.HOSTS][host_id] - cluster_db.update_cluster_host_deployed_config(user, - cluster_id, - host_id, - **config) - - @staticmethod - def update_state(cluster_id, host_id_list, user): - # update all clusterhosts state - for host_id in host_id_list: - cluster_db.update_cluster_host_state(user, cluster_id, host_id, - state='INSTALLING') - - # update cluster state - cluster_db.update_cluster_state(user, cluster_id, state='INSTALLING') - - @staticmethod - def get_machine_IPMI(machine_id, user): - machine_info = machine_db.get_machine(user, machine_id) - return machine_info[const.IPMI_CREDS] diff --git a/compass/actions/util.py b/compass/actions/util.py index de68423e..f8783119 100644 --- a/compass/actions/util.py +++ b/compass/actions/util.py @@ -21,8 +21,11 @@ import redis from contextlib import contextmanager -from compass.db.api import database -from compass.db import models +from compass.db.api import adapter_holder as adapter_db +from compass.db.api import cluster as cluster_db +from compass.db.api import host as host_db +from compass.db.api import machine as machine_db +from compass.deployment.utils import constants as const @contextmanager @@ -53,3 +56,223 @@ def lock(lock_name, blocking=True, timeout=10): logging.debug('released lock %s', lock_name) else: logging.debug('nothing to release %s', lock_name) + + +class ActionHelper(object): + + @staticmethod + def get_adapter_info(adapter_id, cluster_id, user): + """Get adapter information. Return a dictionary as below, + { + "id": 1, + "name": "xxx", + "flavors": [ + { + "flavor_name": "xxx", + "roles": ['xxx', 'yyy', ...], + "template": "xxx.tmpl" + }, + ... + ], + "metadata": { + "os_config": { + ... + }, + "package_config": { + ... + } + }, + "os_installer": { + "name": "cobbler", + "settings": {....} + }, + "pk_installer": { + "name": "chef", + "settings": {....} + }, + ... + } + To view a complete output, please refer to backend doc. + """ + adapter_info = adapter_db.get_adapter(user, adapter_id) + metadata = cluster_db.get_cluster_metadata(user, cluster_id) + adapter_info.update({const.METADATA: metadata}) + + for flavor_info in adapter_info[const.FLAVORS]: + roles = flavor_info[const.ROLES] + flavor_info[const.ROLES] = ActionHelper._get_role_names(roles) + + return adapter_info + + @staticmethod + def _get_role_names(roles): + return [role[const.NAME] for role in roles] + + @staticmethod + def get_cluster_info(cluster_id, user): + """Get cluster information.Return a dictionary as below, + { + "id": 1, + "adapter_id": 1, + "os_version": "CentOS-6.5-x86_64", + "name": "cluster_01", + "flavor": { + "flavor_name": "zzz", + "template": "xx.tmpl", + "roles": [...] + } + "os_config": {..}, + "package_config": {...}, + "deployed_os_config": {}, + "deployed_package_config": {}, + "owner": "xxx" + } + """ + cluster_info = cluster_db.get_cluster(user, cluster_id) + + # convert roles retrieved from db into a list of role names + roles_info = cluster_info.setdefault( + const.FLAVOR, {}).setdefault(const.ROLES, []) + cluster_info[const.FLAVOR][const.ROLES] = \ + ActionHelper._get_role_names(roles_info) + + # get cluster config info + cluster_config = cluster_db.get_cluster_config(user, cluster_id) + cluster_info.update(cluster_config) + + deploy_config = cluster_db.get_cluster_deployed_config(user, + cluster_id) + cluster_info.update(deploy_config) + + return cluster_info + + @staticmethod + def get_hosts_info(cluster_id, hosts_id_list, user): + """Get hosts information. Return a dictionary as below, + { + "hosts": { + 1($host_id): { + "reinstall_os": True, + "mac": "xxx", + "name": "xxx", + "roles": [xxx, yyy] + }, + "networks": { + "eth0": { + "ip": "192.168.1.1", + "netmask": "255.255.255.0", + "is_mgmt": True, + "is_promiscuous": False, + "subnet": "192.168.1.0/24" + }, + "eth1": {...} + }, + "os_config": {}, + "package_config": {}, + "deployed_os_config": {}, + "deployed_package_config": {} + }, + 2: {...}, + .... + } + } + """ + hosts_info = {} + for host_id in hosts_id_list: + info = cluster_db.get_cluster_host(user, cluster_id, host_id) + logging.debug("checking on info %r %r" % (host_id, info)) + + info[const.ROLES] = ActionHelper._get_role_names(info[const.ROLES]) + + # TODO(grace): Is following line necessary?? + info.setdefault(const.ROLES, []) + + config = cluster_db.get_cluster_host_config(user, + cluster_id, + host_id) + info.update(config) + + networks = info[const.NETWORKS] + networks_dict = {} + # Convert networks from list to dictionary format + for entry in networks: + nic_info = {} + nic_info = { + entry[const.NIC]: { + const.IP_ADDR: entry[const.IP_ADDR], + const.NETMASK: entry[const.NETMASK], + const.MGMT_NIC_FLAG: entry[const.MGMT_NIC_FLAG], + const.PROMISCUOUS_FLAG: entry[const.PROMISCUOUS_FLAG], + const.SUBNET: entry[const.SUBNET] + } + } + networks_dict.update(nic_info) + + info[const.NETWORKS] = networks_dict + + hosts_info[host_id] = info + + return hosts_info + + @staticmethod + def save_deployed_config(deployed_config, user): + cluster_config = deployed_config[const.CLUSTER] + cluster_id = cluster_config[const.ID] + del cluster_config[const.ID] + + cluster_db.update_cluster_deployed_config(user, cluster_id, + **cluster_config) + + hosts_id_list = deployed_config[const.HOSTS].keys() + for host_id in hosts_id_list: + config = deployed_config[const.HOSTS][host_id] + cluster_db.update_cluster_host_deployed_config(user, + cluster_id, + host_id, + **config) + + @staticmethod + def update_state(cluster_id, host_id_list, user): + # update all clusterhosts state + for host_id in host_id_list: + cluster_db.update_cluster_host_state(user, cluster_id, host_id, + state='INSTALLING') + + # update cluster state + cluster_db.update_cluster_state(user, cluster_id, state='INSTALLING') + + @staticmethod + def delete_cluster( + cluster_id, host_id_list, user, delete_underlying_host=False + ): + if delete_underlying_host: + for host_id in host_id_list: + host_db.del_host_from_database( + user, host_id + ) + cluster_db.del_cluster_from_database( + user, cluster_id + ) + + @staticmethod + def delete_cluster_host( + cluster_id, host_id, user, delete_underlying_host=False + ): + if delete_underlying_host: + host_db.del_host_from_database( + user, host_id + ) + cluster_db.del_cluster_host_from_database( + user, cluster_id, host_id + ) + + @staticmethod + def delete_host(host_id, user): + host_db.del_host_from_database( + user, host_id + ) + + @staticmethod + def get_machine_IPMI(machine_id, user): + machine_info = machine_db.get_machine(user, machine_id) + return machine_info[const.IPMI_CREDS] diff --git a/compass/api/api.py b/compass/api/api.py index f24f886e..098be35a 100644 --- a/compass/api/api.py +++ b/compass/api/api.py @@ -1346,7 +1346,7 @@ def delete_cluster(cluster_id): """Delete cluster.""" data = _get_request_data() return utils.make_json_response( - 200, + 202, cluster_api.del_cluster( current_user, cluster_id, **data ) @@ -1613,7 +1613,7 @@ def delete_cluster_host(cluster_id, host_id): """Delete cluster host.""" data = _get_request_data() return utils.make_json_response( - 200, + 202, cluster_api.del_cluster_host( current_user, cluster_id, host_id, **data ) @@ -1630,7 +1630,7 @@ def delete_clusterhost(clusterhost_id): """Delete cluster host.""" data = _get_request_data() return utils.make_json_response( - 200, + 202, cluster_api.del_clusterhost( current_user, clusterhost_id, **data ) @@ -1918,7 +1918,7 @@ def delete_host(host_id): """Delete host.""" data = _get_request_data() return utils.make_json_response( - 200, + 202, host_api.del_host( current_user, host_id, **data ) diff --git a/compass/db/api/cluster.py b/compass/db/api/cluster.py index 75cb1a20..1528601b 100644 --- a/compass/db/api/cluster.py +++ b/compass/db/api/cluster.py @@ -292,9 +292,14 @@ def update_cluster(session, updater, cluster_id, **kwargs): @user_api.check_user_permission_in_session( permission.PERMISSION_DEL_CLUSTER ) -@utils.wrap_to_dict(RESP_FIELDS) +@utils.wrap_to_dict( + ['status', 'cluster', 'hosts'], + cluster=RESP_FIELDS, + hosts=RESP_CLUSTERHOST_FIELDS +) def del_cluster(session, deleter, cluster_id, **kwargs): """Delete a cluster.""" + from compass.tasks import client as celery_client cluster = utils.get_db_object( session, models.Cluster, id=cluster_id ) @@ -302,6 +307,32 @@ def del_cluster(session, deleter, cluster_id, **kwargs): session, cluster, deleter, reinstall_distributed_system_set=True ) + clusterhosts = [] + for clusterhost in cluster.clusterhosts: + clusterhosts.append(clusterhost) + + celery_client.celery.send_task( + 'compass.tasks.delete_cluster', + ( + deleter.email, cluster_id, + [clusterhost.host_id for clusterhost in clusterhosts] + ) + ) + return { + 'status': 'delete action sent', + 'cluster': cluster, + 'hosts': clusterhosts + } + + +@database.run_in_session() +@user_api.check_user_permission_in_session( + permission.PERMISSION_DEL_CLUSTER +) +def del_cluster_from_database(session, deleter, cluster_id): + cluster = utils.get_db_object( + session, models.Cluster, id=cluster_id + ) return utils.del_db_object(session, cluster) @@ -802,9 +833,10 @@ def patch_clusterhost( @user_api.check_user_permission_in_session( permission.PERMISSION_DEL_CLUSTER_HOST ) -@utils.wrap_to_dict(RESP_CLUSTERHOST_FIELDS) +@utils.wrap_to_dict(['status', 'host'], host=RESP_CLUSTERHOST_FIELDS) def del_cluster_host(session, deleter, cluster_id, host_id, **kwargs): """Delete cluster host.""" + from compass.tasks import client as celery_client clusterhost = utils.get_db_object( session, models.ClusterHost, cluster_id=cluster_id, host_id=host_id @@ -813,9 +845,27 @@ def del_cluster_host(session, deleter, cluster_id, host_id, **kwargs): session, clusterhost.cluster, deleter, reinstall_distributed_system_set=True ) - return utils.del_db_object( - session, clusterhost + celery_client.celery.send_task( + 'compass.tasks.delete_cluster_host', + ( + deleter.email, cluster_id, host_id + ) ) + return { + 'status': 'delete action sent', + 'host': clusterhost, + } + + +@database.run_in_session() +@user_api.check_user_permission_in_session( + permission.PERMISSION_DEL_CLUSTER_HOST +) +def del_cluster_host_from_database(session, deleter, cluster_id, host_id): + clusterhost = utils.get_db_object( + session, models.ClusterHost, id=cluster_id, host_id=host_id + ) + return utils.del_db_object(session, clusterhost) @utils.supported_filters([]) @@ -823,9 +873,10 @@ def del_cluster_host(session, deleter, cluster_id, host_id, **kwargs): @user_api.check_user_permission_in_session( permission.PERMISSION_DEL_CLUSTER_HOST ) -@utils.wrap_to_dict(RESP_CLUSTERHOST_FIELDS) +@utils.wrap_to_dict(['status', 'host'], host=RESP_CLUSTERHOST_FIELDS) def del_clusterhost(session, deleter, clusterhost_id, **kwargs): """Delete cluster host.""" + from compass.tasks import client as celery_client clusterhost = utils.get_db_object( session, models.ClusterHost, clusterhost_id=clusterhost_id @@ -834,9 +885,27 @@ def del_clusterhost(session, deleter, clusterhost_id, **kwargs): session, clusterhost.cluster, deleter, reinstall_distributed_system_set=True ) - return utils.del_db_object( - session, clusterhost + celery_client.celery.send_task( + 'compass.tasks.delete_cluster_host', + ( + deleter.email, clusterhost.cluster_id, clusterhost.host_id + ) ) + return { + 'status': 'delete action sent', + 'host': clusterhost, + } + + +@database.run_in_session() +@user_api.check_user_permission_in_session( + permission.PERMISSION_DEL_CLUSTER_HOST +) +def del_clusterhost_from_database(session, deleter, clusterhost_id): + clusterhost = utils.get_db_object( + session, models.ClusterHost, clusterhost_id=clusterhost_id + ) + return utils.del_db_object(session, clusterhost) @utils.supported_filters([]) diff --git a/compass/db/api/host.py b/compass/db/api/host.py index a039e424..cfd4f48b 100644 --- a/compass/db/api/host.py +++ b/compass/db/api/host.py @@ -317,9 +317,11 @@ def update_hosts(session, updater, data=[]): @user_api.check_user_permission_in_session( permission.PERMISSION_DEL_HOST ) -@utils.wrap_to_dict(RESP_FIELDS) +@utils.wrap_to_dict(['status', 'host'], host=RESP_FIELDS) def del_host(session, deleter, host_id, **kwargs): """Delete a host.""" + from compass.db.api import cluster as cluster_api + from compass.tasks import client as celery_client host = utils.get_db_object( session, models.Host, id=host_id ) @@ -327,6 +329,34 @@ def del_host(session, deleter, host_id, **kwargs): session, host, deleter, reinstall_os_set=True ) + cluster_ids = [] + for clusterhost in host.clusterhosts: + cluster_api.is_cluster_editable( + session, clusterhost.cluster, deleter, + reinstall_distributed_system_set=True + ) + cluster_ids.append(clusterhost.cluster_id) + + celery_client.celery.send_task( + 'compass.tasks.delete_host', + ( + deleter.email, host_id, cluster_ids + ) + ) + return { + 'status': 'delete action sent', + 'host': host, + } + + +@database.run_in_session() +@user_api.check_user_permission_in_session( + permission.PERMISSION_DEL_HOST +) +def del_host_from_database(session, deleter, host_id): + host = utils.get_db_object( + session, models.Host, id=host_id + ) return utils.del_db_object(session, host) diff --git a/compass/db/api/machine.py b/compass/db/api/machine.py index b94c63f4..8a168344 100644 --- a/compass/db/api/machine.py +++ b/compass/db/api/machine.py @@ -128,6 +128,13 @@ def patch_machine(session, updater, machine_id, **kwargs): def del_machine(session, deleter, machine_id, **kwargs): """Delete a machine.""" machine = utils.get_db_object(session, models.Machine, id=machine_id) + if machine.host: + host = machine.host + raise exception.NotAcceptable( + 'machine %s has host %s on it' % ( + machine.mac, host.name + ) + ) return utils.del_db_object(session, machine) diff --git a/compass/db/api/network.py b/compass/db/api/network.py index 3eedb26f..94f71987 100644 --- a/compass/db/api/network.py +++ b/compass/db/api/network.py @@ -125,4 +125,18 @@ def del_subnet(session, deleter, subnet_id, **kwargs): subnet = utils.get_db_object( session, models.Subnet, id=subnet_id ) + if subnet.host_networks: + host_networks = [ + '%s:%s=%s' % ( + host_network.host.name, host_network.interface, + host_network.ip + ) + for host_network in subnet.host_networks + ] + raise exception.NotAcceptable( + 'subnet %s contains host networks %s' % ( + subnet.subnet, host_networks + ) + ) + return utils.del_db_object(session, subnet) diff --git a/compass/db/api/utils.py b/compass/db/api/utils.py index d20f09df..a0304261 100644 --- a/compass/db/api/utils.py +++ b/compass/db/api/utils.py @@ -462,12 +462,12 @@ def get_db_object(session, table, exception_when_missing=True, **kwargs): with session.begin(subtransactions=True): logging.debug( 'session %s get db object %s from table %s', - session, kwargs, table.__name__) + id(session), kwargs, table.__name__) db_object = model_filter( model_query(session, table), table, **kwargs ).first() logging.debug( - 'session %s got db object %s', session, db_object + 'session %s got db object %s', id(session), db_object ) if db_object: return db_object @@ -488,7 +488,7 @@ def add_db_object(session, table, exception_when_existing=True, with session.begin(subtransactions=True): logging.debug( 'session %s add object %s atributes %s to table %s', - session, args, kwargs, table.__name__) + id(session), args, kwargs, table.__name__) argspec = inspect.getargspec(table.__init__) arg_names = argspec.args[1:] arg_defaults = argspec.defaults @@ -526,7 +526,7 @@ def add_db_object(session, table, exception_when_existing=True, db_object.initialize() db_object.validate() logging.debug( - 'session %s db object %s added', session, db_object + 'session %s db object %s added', id(session), db_object ) return db_object @@ -536,7 +536,7 @@ def list_db_objects(session, table, order_by=[], **filters): with session.begin(subtransactions=True): logging.debug( 'session %s list db objects by filters %s in table %s', - session, filters, table.__name__ + id(session), filters, table.__name__ ) db_objects = model_order_by( model_filter( @@ -549,7 +549,7 @@ def list_db_objects(session, table, order_by=[], **filters): ).all() logging.debug( 'session %s got listed db objects: %s', - session, db_objects + id(session), db_objects ) return db_objects @@ -559,7 +559,7 @@ def del_db_objects(session, table, **filters): with session.begin(subtransactions=True): logging.debug( 'session %s delete db objects by filters %s in table %s', - session, filters, table.__name__ + id(session), filters, table.__name__ ) query = model_filter( model_query(session, table), table, **filters @@ -567,7 +567,7 @@ def del_db_objects(session, table, **filters): db_objects = query.all() query.delete(synchronize_session=False) logging.debug( - 'session %s db objects %s deleted', session, db_objects + 'session %s db objects %s deleted', id(session), db_objects ) return db_objects @@ -577,7 +577,7 @@ def update_db_objects(session, table, **filters): with session.begin(subtransactions=True): logging.debug( 'session %s update db objects by filters %s in table %s', - session, filters, table.__name__) + id(session), filters, table.__name__) db_objects = model_filter( model_query(session, table), table, **filters ).all() @@ -587,7 +587,8 @@ def update_db_objects(session, table, **filters): db_object.update() db_object.validate() logging.debug( - 'session %s db objects %s updated', session, db_objects + 'session %s db objects %s updated', + id(session), db_objects ) return db_objects @@ -597,7 +598,7 @@ def update_db_object(session, db_object, **kwargs): with session.begin(subtransactions=True): logging.debug( 'session %s update db object %s by value %s', - session, db_object, kwargs + id(session), db_object, kwargs ) for key, value in kwargs.items(): setattr(db_object, key, value) @@ -605,7 +606,8 @@ def update_db_object(session, db_object, **kwargs): db_object.update() db_object.validate() logging.debug( - 'session %s db object %s updated', session, db_object + 'session %s db object %s updated', + id(session), db_object ) return db_object @@ -615,12 +617,12 @@ def del_db_object(session, db_object): with session.begin(subtransactions=True): logging.debug( 'session %s delete db object %s', - session, db_object + id(session), db_object ) session.delete(db_object) logging.debug( 'session %s db object %s deleted', - session, db_object + id(session), db_object ) return db_object diff --git a/compass/db/models.py b/compass/db/models.py index da1c4155..a539729e 100644 --- a/compass/db/models.py +++ b/compass/db/models.py @@ -486,6 +486,9 @@ class HostNetwork(BASE, TimestampMixin, HelperMixin): self.interface = interface super(HostNetwork, self).__init__(**kwargs) + def __str__(self): + return 'HostNetwork[%s=%s]' % (self.interface, self.ip) + @property def ip(self): return str(netaddr.IPAddress(self.ip_int)) @@ -558,6 +561,11 @@ class ClusterHostLogHistory(BASE, LogHistoryMixin): self.filename = filename super(ClusterHostLogHistory, self).__init__(**kwargs) + def __str__(self): + return 'ClusterHostLogHistory[%s:%s]' % ( + self.clusterhost_id, self.filename + ) + def initialize(self): self.cluster_id = self.clusterhost.cluster_id self.host_id = self.clusterhost.host_id @@ -580,6 +588,9 @@ class HostLogHistory(BASE, LogHistoryMixin): self.filename = filename super(HostLogHistory, self).__init__(**kwargs) + def __str__(self): + return 'HostLogHistory[%s:%s]' % (self.id, self.filename) + class ClusterHostState(BASE, StateMixin): """ClusterHost state table.""" @@ -594,6 +605,11 @@ class ClusterHostState(BASE, StateMixin): primary_key=True ) + def __str__(self): + return 'ClusterHostState[%s state %s percentage %s]' % ( + self.id, self.state, self.percentage + ) + def update(self): super(ClusterHostState, self).update() host_state = self.clusterhost.host.state @@ -655,6 +671,9 @@ class ClusterHost(BASE, TimestampMixin, HelperMixin): self.state = ClusterHostState() super(ClusterHost, self).__init__(**kwargs) + def __str__(self): + return 'ClusterHost[%s:%s]' % (self.clusterhost_id, self.name) + def update(self): if self.host.reinstall_os: if self.state in ['SUCCESSFUL', 'ERROR']: @@ -662,7 +681,8 @@ class ClusterHost(BASE, TimestampMixin, HelperMixin): self.state.state = 'INITIALIZED' else: self.state.state = 'UNINITIALIZED' - self.state.update() + self.state.update() + super(ClusterHost, self).update() @property def name(self): @@ -866,6 +886,11 @@ class HostState(BASE, StateMixin): primary_key=True ) + def __str__(self): + return 'HostState[%s state %s percentage %s]' % ( + self.id, self.state, self.percentage + ) + def update(self): super(HostState, self).update() host = self.host @@ -944,6 +969,9 @@ class Host(BASE, TimestampMixin, HelperMixin): backref=backref('host') ) + def __str__(self): + return 'Host[%s:%s]' % (self.id, self.name) + @hybrid_property def mac(self): machine = self.machine @@ -1004,6 +1032,7 @@ class Host(BASE, TimestampMixin, HelperMixin): self.os_name = os.name else: self.os_name = None + self.state.update() super(Host, self).update() def validate(self): @@ -1089,6 +1118,11 @@ class ClusterState(BASE, StateMixin): default=0 ) + def __str__(self): + return 'ClusterState[%s state %s percentage %s]' % ( + self.id, self.state, self.percentage + ) + def to_dict(self): dict_info = super(ClusterState, self).to_dict() dict_info['status'] = { @@ -1199,8 +1233,8 @@ class Cluster(BASE, TimestampMixin, HelperMixin): self.state = ClusterState() super(Cluster, self).__init__(**kwargs) - def initialize(self): - super(Cluster, self).initialize() + def __str__(self): + return 'Cluster[%s:%s]' % (self.id, self.name) def update(self): creator = self.creator @@ -1233,6 +1267,7 @@ class Cluster(BASE, TimestampMixin, HelperMixin): self.flavor_name = flavor.name else: self.flavor_name = None + self.state.update() super(Cluster, self).update() def validate(self): @@ -1375,6 +1410,9 @@ class UserPermission(BASE, HelperMixin, TimestampMixin): self.user_id = user_id self.permission_id = permission_id + def __str__(self): + return 'UserPermission[%s:%s]' % (self.id, self.name) + @hybrid_property def name(self): return self.permission.name @@ -1404,6 +1442,9 @@ class Permission(BASE, HelperMixin, TimestampMixin): self.name = name super(Permission, self).__init__(**kwargs) + def __str__(self): + return 'Permission[%s:%s]' % (self.id, self.name) + class UserToken(BASE, HelperMixin): """user token table.""" @@ -1497,6 +1538,9 @@ class User(BASE, HelperMixin, TimestampMixin): self.email = email super(User, self).__init__(**kwargs) + def __str__(self): + return 'User[%s]' % self.email + def validate(self): super(User, self).validate() if not self.crypted_password: @@ -1528,12 +1572,6 @@ class User(BASE, HelperMixin, TimestampMixin): ] return dict_info - def __str__(self): - return '%s[email:%s,is_admin:%s,active:%s]' % ( - self.__class__.__name__, - self.email, self.is_admin, self.active - ) - class SwitchMachine(BASE, HelperMixin, TimestampMixin): """Switch Machine table.""" @@ -1560,6 +1598,11 @@ class SwitchMachine(BASE, HelperMixin, TimestampMixin): self.machine_id = machine_id super(SwitchMachine, self).__init__(**kwargs) + def __str__(self): + return 'SwitchMachine[%s port %s]' % ( + self.switch_machine_id, self.port + ) + def validate(self): super(SwitchMachine, self).validate() if not self.switch: @@ -1709,6 +1752,9 @@ class Machine(BASE, HelperMixin, TimestampMixin): self.mac = mac super(Machine, self).__init__(**kwargs) + def __str__(self): + return 'Machine[%s:%s]' % (self.id, self.mac) + def validate(self): super(Machine, self).validate() try: @@ -1789,6 +1835,9 @@ class Switch(BASE, HelperMixin, TimestampMixin): backref=backref('switch') ) + def __str__(self): + return 'Switch[%s:%s]' % (self.id, self.ip) + @classmethod def parse_filters(cls, filters): if isinstance(filters, basestring): @@ -2094,6 +2143,9 @@ class OperatingSystem(BASE, HelperMixin): self.name = name super(OperatingSystem, self).__init__() + def __str__(self): + return 'OperatingSystem[%s:%s]' % (self.id, self.name) + @property def root_metadatas(self): return [ @@ -2199,6 +2251,9 @@ class AdapterFlavor(BASE, HelperMixin): UniqueConstraint('name', 'adapter_id', name='constraint'), ) + def __str__(self): + return 'AdapterFlavor[%s:%s]' % (self.id, self.name) + @property def ordered_flavor_roles(self): flavor_roles = dict([ @@ -2285,6 +2340,9 @@ class AdapterRole(BASE, HelperMixin): self.adapter_id = adapter_id super(AdapterRole, self).__init__(**kwargs) + def __str__(self): + return 'AdapterRole[%s:%s]' % (self.id, self.name) + def initialize(self): if not self.description: self.description = self.name @@ -2592,6 +2650,9 @@ class DistributedSystem(BASE, HelperMixin): self.name = name super(DistributedSystem, self).__init__() + def __str__(self): + return 'DistributedSystem[%s:%s]' % (self.id, self.name) + class OSInstaller(BASE, InstallerMixin): """OS installer table.""" @@ -2612,6 +2673,9 @@ class OSInstaller(BASE, InstallerMixin): self.alias = alias super(OSInstaller, self).__init__(**kwargs) + def __str__(self): + return 'OSInstaller[%s:%s]' % (self.id, self.alias) + class PackageInstaller(BASE, InstallerMixin): """package installer table.""" @@ -2628,6 +2692,9 @@ class PackageInstaller(BASE, InstallerMixin): self.alias = alias super(PackageInstaller, self).__init__(**kwargs) + def __str__(self): + return 'PackageInstaller[%s:%s]' % (self.id, self.alias) + class Subnet(BASE, TimestampMixin, HelperMixin): """network table.""" @@ -2648,6 +2715,9 @@ class Subnet(BASE, TimestampMixin, HelperMixin): self.subnet = subnet super(Subnet, self).__init__(**kwargs) + def __str__(self): + return 'Subnet[%s:%s]' % (self.id, self.subnet) + def to_dict(self): dict_info = super(Subnet, self).to_dict() if not self.name: diff --git a/compass/deployment/deploy_manager.py b/compass/deployment/deploy_manager.py index 63afd0d0..8d52ff7d 100644 --- a/compass/deployment/deploy_manager.py +++ b/compass/deployment/deploy_manager.py @@ -140,13 +140,13 @@ class DeployManager(object): self.redeploy_os() self.redeploy_target_system() - def remove_hosts(self, package_only=False): + def remove_hosts(self, package_only=False, delete_cluster=False): """Remove hosts from both OS and/or package installlers server side.""" if self.os_installer and not package_only: self.os_installer.delete_hosts() if self.pk_installer: - self.pk_installer.delete_hosts() + self.pk_installer.delete_hosts(delete_cluster=delete_cluster) def _get_hosts_for_os_installation(self, hosts_info): """Get info of hosts which need to install/reinstall OS.""" diff --git a/compass/deployment/installers/pk_installers/chef_installer/chef_installer.py b/compass/deployment/installers/pk_installers/chef_installer/chef_installer.py index 534dc2bb..c9cd372e 100644 --- a/compass/deployment/installers/pk_installers/chef_installer/chef_installer.py +++ b/compass/deployment/installers/pk_installers/chef_installer/chef_installer.py @@ -135,10 +135,20 @@ class ChefInstaller(PKInstaller): return node - def delete_hosts(self): + def delete_hosts(self, delete_cluster=False): hosts_id_list = self.config_manager.get_host_id_list() for host_id in hosts_id_list: self.delete_node(host_id) + if delete_cluster: + self.delete_environment() + + def delete_environment(self): + adapter_name = self.config_manager.get_adapter_name() + cluster_name = self.config_manager.get_clustername() + env_name = self.get_env_name(adapter_name, cluster_name) + env = self.get_create_environment(env_name) + if env: + self._delete_environment(env) def delete_node(self, host_id): fullname = self.config_manager.get_host_fullname(host_id) @@ -146,6 +156,20 @@ class ChefInstaller(PKInstaller): if node: self._delete_node(node) + def _delete_environment(self, env): + """clean env attributes about arget system.""" + import chef + if env is None: + raise Exception("env is None, cannot delete a bnone env.") + env_name = env.name + try: + env.delete() + except Exception as error: + logging.debug( + 'failed to delete env %s, error: %s', + env_name, error + ) + def _delete_node(self, node): """clean node attributes about target system.""" import chef diff --git a/compass/tasks/tasks.py b/compass/tasks/tasks.py index fdd10227..eec1204d 100644 --- a/compass/tasks/tasks.py +++ b/compass/tasks/tasks.py @@ -21,6 +21,7 @@ import logging from celery.signals import celeryd_init from celery.signals import setup_logging +from compass.actions import delete from compass.actions import deploy from compass.actions import poll_switch from compass.actions import update_progress @@ -82,8 +83,10 @@ def pollswitch( def deploy_cluster(deployer_email, cluster_id, clusterhost_ids): """Deploy the given cluster. - :param cluster_hosts: the cluster and hosts of each cluster to deploy. - :type cluster_hosts: dict of int to list of int + :param cluster_id: id of the cluster + :type cluster_id: int + :param clusterhost_ids: the id of the hosts in the cluster + :type clusterhost_ids: list of int """ try: deploy.deploy(cluster_id, clusterhost_ids, deployer_email) @@ -95,10 +98,74 @@ def deploy_cluster(deployer_email, cluster_id, clusterhost_ids): def reinstall_cluster(installer_email, cluster_id, clusterhost_ids): """reinstall the given cluster. - :param cluster_hosts: the cluster and hosts of each cluster to reinstall. - :type cluster_hosts: dict of int to list of int + :param cluster_id: id of the cluster + :type cluster_id: int + :param clusterhost_ids: the id of the hosts in the cluster + :type clusterhost_ids: list of int """ - pass + try: + deploy.redeploy(cluster_id, clusterhost_ids, installer_email) + except Exception as error: + logging.exception(error) + + +@celery.task(name='compass.tasks.delete_cluster') +def delete_cluster( + deleter_email, cluster_id, clusterhost_ids, + delete_underlying_host=False +): + """Delete the given cluster. + + :param cluster_id: id of the cluster + :type cluster_id: int + :param clusterhost_ids: the id of the hosts in the cluster + :type clusterhost_ids: list of int + """ + try: + delete.delete_cluster( + cluster_id, clusterhost_ids, deleter_email, + delete_underlying_host=delete_underlying_host + ) + except Exception as error: + logging.exception(error) + + +@celery.task(name='compass.tasks.delete_cluster_host') +def delete_cluster_host( + deleter_email, cluster_id, host_id, + delete_underlying_host=False +): + """Delte the given cluster host. + + :param cluster_id: id of the cluster + :type cluster_id: int + :param host_id: id of the host + :type host_id: int + """ + try: + delete.delete_cluster_host( + cluster_id, host_id, deleter_email, + delete_underlying_host=delete_underlying_host + ) + except Exception as error: + logging.exception(error) + + +@celery.task(name='compass.tasks.delete_host') +def delete_host(deleter_email, host_id, cluster_ids): + """Delete the given host. + + :param host_id: id of the host + :type host_id: int + :param cluster_ids: list of cluster id + :type cluster_ids: list of int + """ + try: + delete.delete_host( + host_id, deleter_email, cluster_ids + ) + except Exception as error: + logging.exception(error) @celery.task(name='compass.tasks.poweron_host') diff --git a/compass/tests/api/test_api.py b/compass/tests/api/test_api.py index 706741a6..491ffacf 100644 --- a/compass/tests/api/test_api.py +++ b/compass/tests/api/test_api.py @@ -72,6 +72,8 @@ class ApiTestCase(unittest2.TestCase): self.test_client = application.test_client() celery.current_app.send_task = mock.Mock() + from compass.tasks import client as celery_client + celery_client.celery.send_task = mock.Mock() url = '/users/token' data = self.USER_CREDENTIALS request_data = json.dumps(data) @@ -385,7 +387,7 @@ class TestClusterAPI(ApiTestCase): # delete a cluster sucessfully url = '/clusters/1' return_value = self.delete(url) - self.assertEqual(return_value.status_code, 200) + self.assertEqual(return_value.status_code, 202) def test_list_cluster_hosts(self): # list cluster_hosts successfully @@ -451,7 +453,7 @@ class TestClusterAPI(ApiTestCase): # delete a cluster_host successfully url = '/clusters/1/hosts/1' return_value = self.delete(url) - self.assertEqual(return_value.status_code, 200) + self.assertEqual(return_value.status_code, 202) # give a non-existed cluster_id url = '/clusters/99/hosts/1' @@ -862,7 +864,7 @@ class TestHostAPI(ApiTestCase): # delete a host successfully url = '/hosts/2' return_value = self.delete(url) - self.assertEqual(return_value.status_code, 200) + self.assertEqual(return_value.status_code, 202) # give a non-existed id url = '/hosts/99' diff --git a/compass/tests/db/api/test_cluster.py b/compass/tests/db/api/test_cluster.py index 2a46e14c..733fc949 100644 --- a/compass/tests/db/api/test_cluster.py +++ b/compass/tests/db/api/test_cluster.py @@ -383,15 +383,13 @@ class TestDelCluster(ClusterTestCase): super(TestDelCluster, self).setUp() def test_del_cluster(self): - cluster.del_cluster( + from compass.tasks import client as celery_client + celery_client.celery.send_task = mock.Mock() + del_cluster = cluster.del_cluster( self.user_object, self.cluster_id ) - del_clusters = cluster.list_clusters(self.user_object) - cluster_ids = [] - for del_cluster in del_clusters: - cluster_ids.append(del_cluster['id']) - self.assertNotIn(self.cluster_id, cluster_ids) + self.assertIsNotNone(del_cluster['status']) def test_is_cluster_editable(self): #state is INSTALLING @@ -907,19 +905,14 @@ class TestDelClusterHost(ClusterTestCase): super(TestDelClusterHost, self).tearDown() def test_del_cluster_host(self): - cluster.del_cluster_host( + from compass.tasks import client as celery_client + celery_client.celery.send_task = mock.Mock() + del_clusterhost = cluster.del_cluster_host( self.user_object, self.cluster_id, self.host_id[0] ) - del_cluster_host = cluster.list_cluster_hosts( - self.user_object, - self.cluster_id - ) - result = [] - for item in del_cluster_host: - result.append(item['hostname']) - self.assertNotIn('newname1', result) + self.assertIsNotNone(del_clusterhost) def test_is_cluster_editable(self): cluster.update_cluster_state( @@ -946,15 +939,13 @@ class TestDelClusterhost(ClusterTestCase): super(TestDelClusterhost, self).tearDown() def test_del_clusterhost(self): - cluster.del_clusterhost( + from compass.tasks import client as celery_client + celery_client.celery.send_task = mock.Mock() + del_clusterhost = cluster.del_clusterhost( self.user_object, self.clusterhost_id[0] ) - del_clusterhost = cluster.list_clusterhosts(self.user_object) - result = [] - for item in del_clusterhost: - result.append(item['hostname']) - self.assertNotIn('newname1', result) + self.assertIsNotNone(del_clusterhost) def test_is_cluster_editable(self): cluster.update_cluster_state( diff --git a/compass/tests/db/api/test_host.py b/compass/tests/db/api/test_host.py index e13c6b6a..df230734 100644 --- a/compass/tests/db/api/test_host.py +++ b/compass/tests/db/api/test_host.py @@ -267,11 +267,11 @@ class TestListMachinesOrHosts(HostTestCase): self.assertIn(item, ['newname1', 'newname2']) def test_list_machines(self): - host.del_host( + host.del_host_from_database( self.user_object, self.host_ids[0] ) - host.del_host( + host.del_host_from_database( self.user_object, self.host_ids[1] ) @@ -327,7 +327,7 @@ class TestGetMachineOrHost(HostTestCase): self.assertEqual(get_host['mac'], '28:6e:d4:46:c4:25') def test_get_machine(self): - host.del_host( + host.del_host_from_database( self.user_object, self.host_ids[0] ) @@ -448,17 +448,13 @@ class TestDelHost(HostTestCase): super(TestDelHost, self).tearDown() def test_del_host(self): - host.del_host( + from compass.tasks import client as celery_client + celery_client.celery.send_task = mock.Mock() + del_host = host.del_host( self.user_object, self.host_ids[0] ) - del_host = host.list_hosts( - self.user_object - ) - ids = [] - for item in del_host: - ids.append(item['id']) - self.assertNotIn(self.host_ids[0], ids) + self.assertIsNotNone(del_host['status']) def test_is_host_editable(self): host.update_host_state( diff --git a/conf/templates/chef_installer/openstack_icehouse/environments/allinone.tmpl b/conf/templates/chef_installer/openstack_icehouse/environments/allinone.tmpl index 3cae474e..911bedde 100644 --- a/conf/templates/chef_installer/openstack_icehouse/environments/allinone.tmpl +++ b/conf/templates/chef_installer/openstack_icehouse/environments/allinone.tmpl @@ -11,6 +11,11 @@ }, "json_class": "Chef::Environment", "chef_type": "environment", + "override_attributes": { + "compass": { + "cluster_id": "$id" + } + }, "default_attributes": { "local_repo": "", "mysql": { diff --git a/conf/templates/chef_installer/openstack_icehouse/environments/multinodes.tmpl b/conf/templates/chef_installer/openstack_icehouse/environments/multinodes.tmpl index 562f8a33..24120439 100644 --- a/conf/templates/chef_installer/openstack_icehouse/environments/multinodes.tmpl +++ b/conf/templates/chef_installer/openstack_icehouse/environments/multinodes.tmpl @@ -73,6 +73,11 @@ }, "json_class": "Chef::Environment", "chef_type": "environment", + "override_attributes": { + "compass": { + "cluster_id": "$id" + } + }, "default_attributes": { "local_repo": "", "mysql": {