# Copyright 2015 Cisco Systems, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import hashlib import json import os import sys import traceback kb_main_path = os.path.split(os.path.abspath(__file__))[0] + "/../../../kloudbuster" sys.path.append(kb_main_path) from attrdict import AttrDict from credentials import Credentials from kb_session import KBSession from kb_session import KBSessionManager import log as logging from kb_config import KBConfig from kloudbuster import KloudBuster from pecan import expose from pecan import response LOG = logging.getLogger("kloudbuster") class ConfigController(object): # Decorator to check for missing or invalid session ID def check_session_id(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): if not len(args): response.status = 404 response.text = u"Please specify the session_id." return response.text if not KBSessionManager.has(args[0]): response.status = 404 response.text = u"Session ID is not found or invalid." return response.text return func(self, *args, **kwargs) return wrapper def fix_config(self, kb_config, user_config): # Parsing server and client configs from application input # Save the public key into a temporary file if 'public_key' in user_config['kb_cfg']: pubkey_filename = '/tmp/kb_public_key.pub' with open(pubkey_filename, 'w') as f: f.write(user_config['kb_cfg']['public_key_file']) kb_config.config_scale['public_key_file'] = pubkey_filename kb_config.config_scale.client['prompt_before_run'] = False kb_config.config_scale['cleanup_resources'] = False # Parsing the KloudBuster/topology/tenants configs from application input alt_config = AttrDict(user_config['kb_cfg']) if 'kb_cfg' in user_config else None topo_cfg = AttrDict(user_config['topo_cfg']) if 'topo_cfg' in user_config else None tenants_list = AttrDict(user_config['tenants_list'])\ if 'tenants_list' in user_config else None # Synchronize the polling interval with report interval try: alt_config['kb_cfg']['client']['polling_interval'] = \ alt_config['kb_cfg']['client']['http_tool_config']['report_interval'] except Exception: pass key = ['alt_cfg', 'topo_cfg', 'tenants_list'] val = [alt_config, topo_cfg, tenants_list] kwargs = dict([(k, v) for k, v in zip(key, val) if v]) kb_config.update_with_rest_api(**kwargs) @expose(generic=True) def default_config(self): kb_config = KBConfig() return json.dumps(dict(kb_config.config_scale)) @expose(generic=True) @check_session_id def hypervisor_list(self, *args): session_id = args[0] kb_session = KBSessionManager.get(session_id) kloudbuster = kb_session.kloudbuster ret_dict = {} ret_dict['server'] = kloudbuster.get_hypervisor_list(kloudbuster.server_cred) if not kloudbuster.single_cloud: ret_dict['client'] = kloudbuster.get_hypervisor_list(kloudbuster.client_cred) return json.dumps(ret_dict) @expose(generic=True) @check_session_id def az_list(self, *args): session_id = args[0] kb_session = KBSessionManager.get(session_id) kloudbuster = kb_session.kloudbuster ret_dict = {} ret_dict['server'] = kloudbuster.get_az_list(kloudbuster.server_cred) if not kloudbuster.single_cloud: ret_dict['client'] = kloudbuster.get_az_list(kloudbuster.client_cred) return json.dumps(ret_dict) @expose(generic=True) @check_session_id def topology_config(self, *args): session_id = args[0] kb_config_obj = KBSessionManager.get(session_id).kb_config return json.dumps(kb_config_obj.topo_cfg) @expose(generic=True) @check_session_id def running_config(self, *args): session_id = args[0] kb_config_obj = KBSessionManager.get(session_id).kb_config config_scale = dict(kb_config_obj.config_scale) return json.dumps(config_scale) @running_config.when(method='POST') def running_config_POST(self, arg): try: # Expectation: # { # 'credentials': {'tested-rc': '', 'tested-passwd': '', # 'testing-rc': '', 'testing-passwd': ''}, # 'kb_cfg': {}, # 'topo_cfg': {}, # 'tenants_cfg': {}, # 'storage_mode': True/False # } user_config = json.loads(arg) # Parsing credentials from application input cred_config = user_config['credentials'] cred_tested = Credentials(openrc_contents=cred_config['tested-rc'], pwd=cred_config['tested-passwd']) if ('testing-rc' in cred_config and cred_config['testing-rc'] != cred_config['tested-rc']): cred_testing = Credentials(openrc_contents=cred_config['testing-rc'], pwd=cred_config['testing-passwd']) else: # Use the same openrc file for both cases cred_testing = cred_tested kb_config = KBConfig() kb_config.storage_mode = user_config.get('storage_mode', False) session_id = hashlib.md5(str(cred_config)).hexdigest() if KBSessionManager.has(session_id): response.status = 200 return str(session_id) except Exception: response.status = 400 response.text = u"Error while parsing configurations: \n%s" % (traceback.format_exc()) return response.text logfile_name = "/tmp/kb_log_%s" % session_id logging.setup("kloudbuster", logfile=logfile_name) kb_config.init_with_rest_api(cred_tested=cred_tested, cred_testing=cred_testing) self.fix_config(kb_config, user_config) kb_session = KBSession() kb_session.kb_config = kb_config try: kb_session.kloudbuster = KloudBuster( kb_config.cred_tested, kb_config.cred_testing, kb_config.server_cfg, kb_config.client_cfg, kb_config.topo_cfg, kb_config.tenants_list, storage_mode=kb_config.storage_mode) kb_session.kloudbuster.fp_logfile = open(logfile_name) except Exception: LOG.warning(traceback.format_exc()) kb_session.kb_status = 'ERROR' response.status = 400 response.text = u"Cannot initialize KloudBuster instance." return response.text KBSessionManager.add(session_id, kb_session) response.status = 201 return str(session_id) @running_config.when(method='PUT') @check_session_id def running_config_PUT(self, *args, **kwargs): session_id = args[0] status = KBSessionManager.get(session_id).kb_status try: user_config = json.loads(kwargs['arg']) allowed_status = ['READY'] except Exception as e: response.status = 400 response.text = u"Invalid JSON: \n%s" % (e.message) return response.text # http_tool_configs for client VMs is allowed to changed under "STAGED" status if ('kb_cfg' in user_config and len(user_config['kb_cfg']) == 1) and \ ('client' in user_config['kb_cfg'] and len(user_config['kb_cfg']['client']) == 1) and \ ('http_tool_configs' in user_config['kb_cfg']['client']): allowed_status.append('STAGED') if status in allowed_status: # Expectation: # { # 'kb_cfg': {}, # 'topo_cfg': {} # 'tenants_cfg': {} # } try: kb_config = KBSessionManager.get(session_id).kb_config self.fix_config(kb_config, user_config) except Exception: response.status = 400 response.text = u"Error while parsing configurations: \n%s" %\ (traceback.format_exc()) return response.text else: response.status = 403 response.text = u"Cannot update configuration if KloudBuster is not at READY." return response.text return "OK!" @running_config.when(method='DELETE') @check_session_id def running_config_DELETE(self, *args): session_id = args[0] kb_session = KBSessionManager.get(session_id) status = kb_session.kb_status if status != "READY": response.status = 403 response.text = u"Session can be destroyed only if it is at READY." return response.text if kb_session.kloudbuster: kb_session.kloudbuster.dispose() KBSessionManager.delete(session_id) return "OK!"