Add support of Session Manager for Rest API

1. Add support of Session Manager for Rest API;
2. Add Rest API for getting periodical report;
3. Add Rest API for getting console logs;
4. Fix KB-PROXY placement bug when topology file is provided;
5. Fix the logic for consolidating samples;
6. Refine Swagger definition files for Rest API;
7. Set the -c parameter in kb_gen_chart.py to required;

Known Issue / TODO:
1. The Pecan server logs will be dumped to clients;
2. The RestAPI "log" need remember the offset and only gets the
incremental

Change-Id: If912fbfcabc73a04caa0122ffe942405a667608d
This commit is contained in:
Yichen Wang
2015-08-25 18:15:35 -07:00
parent 8aaa12d2b0
commit 0d30c860fc
13 changed files with 375 additions and 84 deletions

View File

@@ -12,36 +12,57 @@
# License for the specific language governing permissions and limitations
# under the License.
import hashlib
import json
import os
import sys
import threading
import traceback
kb_main_path = os.path.split(os.path.abspath(__file__))[0] + "/../../.."
sys.path.append(kb_main_path)
from credentials import Credentials
from kb_session import KBSession
from kb_session import KBSessionManager
import log as logging
from configure import Configuration
from kb_config import KBConfig
from pecan import expose
from pecan import response
lock = threading.Lock()
kb_config = KBConfig()
class ConfigController(object):
@expose(generic=True)
def running_config(self):
def default_config(self):
kb_config = KBConfig()
# @TODO(Bug in Python???)
# return json.dumps(dict(kb_config.config_scale))
return str(kb_config.config_scale)
@expose(generic=True)
def running_config(self, *args):
if len(args):
session_id = args[0]
else:
response.status = 400
response.text = u"Please specify the session_id."
return response.text
if KBSessionManager.has(session_id):
kb_config_obj = KBSessionManager.get(session_id).kb_config
config_scale = kb_config_obj.config_scale
config_scale['server'] = kb_config_obj.server_cfg
config_scale['client'] = kb_config_obj.client_cfg
config_scale = dict(config_scale)
# @TODO(Bug in Python???)
# return json.dumps(config_scale)
return str(config_scale)
else:
response.status = 404
response.text = u"Session ID is not found or invalid."
return response.text
@running_config.when(method='POST')
def running_config_POST(self, arg):
if not lock.acquire(False):
response.status = 403
response.text = u"An instance of KloudBuster is running, you cannot change"\
"the config until the run is finished!"
return response.text
try:
# Expectation:
# {
@@ -51,7 +72,7 @@ class ConfigController(object):
# 'topo_cfg': {<TOPOLOGY_CONFIGS>}
# 'tenants_cfg': {<TENANT_AND_USER_LISTS_FOR_REUSING>}
# }
user_config = eval(arg)
user_config = json.loads(arg)
# Parsing credentials from application input
cred_config = user_config['credentials']
@@ -65,6 +86,13 @@ class ConfigController(object):
# Use the same openrc file for both cases
cred_testing = cred_tested
session_id = hashlib.md5(str(cred_config)).hexdigest()
kb_config = KBConfig()
if KBSessionManager.has(session_id):
response.status = 403
response.text = u"Session is already existed."
return response.text
# Parsing server and client configs from application input
# Save the public key into a temporary file
if 'public_key' in user_config['kb_cfg']:
@@ -74,6 +102,9 @@ class ConfigController(object):
f.close()
kb_config.config_scale['public_key_file'] = pubkey_filename
if 'prompt_before_run' in user_config['kb_cfg']:
kb_config.config_scale['prompt_before_run'] = False
if user_config['kb_cfg']:
alt_config = Configuration.from_string(user_config['kb_cfg']).configure()
kb_config.config_scale = kb_config.config_scale.merge(alt_config)
@@ -89,16 +120,58 @@ class ConfigController(object):
tenants_list = Configuration.from_string(user_config['tenants_list']).configure()
else:
tenants_list = None
except Exception as e:
response.status = 403
response.text = u"Error while parsing configurations: %s" % (e.message)
lock.release()
except Exception:
response.status = 400
response.text = u"Error while parsing configurations: \n%s" % (traceback.format_exc)
return response.text
logging.setup("kloudbuster", logfile="/tmp/kb_log_%s" % session_id)
kb_config.init_with_rest_api(cred_tested=cred_tested,
cred_testing=cred_testing,
topo_cfg=topo_cfg,
tenants_list=tenants_list)
lock.release()
return "OK!"
kb_session = KBSession()
kb_session.kb_config = kb_config
KBSessionManager.add(session_id, kb_session)
return str(session_id)
@running_config.when(method='PUT')
def running_config_PUT(self, *args):
# @TODO(Not completed! ENOTSUP)
if len(args):
session_id = args[0]
else:
response.status = 400
response.text = u"Please specify the session_id."
return response.text
if KBSessionManager.has(session_id):
# kb_session = KBSessionManager.get(session_id)
#
#
#
return "OK!"
else:
response.status = 404
response.text = u"Session ID is not found or invalid."
return response.text
@running_config.when(method='DELETE')
def running_config_DELETE(self, *args):
if len(args):
session_id = args[0]
else:
response.status = 400
response.text = u"Please specify the session_id."
return response.text
if KBSessionManager.has(session_id):
kb_session = KBSessionManager.get(session_id)
if kb_session.kloudbuster:
kb_session.kloudbuster.dispose()
KBSessionManager.delete(session_id)
return "OK!"
else:
response.status = 404
response.text = u"Session ID is not found or invalid."
return response.text

View File

@@ -12,14 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import sys
import traceback
import threading
kb_main_path = os.path.split(os.path.abspath(__file__))[0] + "/../../.."
sys.path.append(kb_main_path)
from api_cfg import kb_config as kb_config
from api_cfg import lock as kb_config_lock
from kb_session import KBSessionManager
from kloudbuster import __version__ as kb_version
from kloudbuster import KloudBuster
from pecan import expose
@@ -28,37 +29,109 @@ from pecan import response
class KBController(object):
def __init__(self):
self.kb_status = 'READY'
@expose(generic=True)
def status(self):
return self.kb_status
@expose(generic=True)
def run(self):
if (not kb_config.cred_tested) or (not kb_config.cred_testing):
response.status = 403
response.text = u"Credentials to the cloud are missing."\
"(Forgot to provide the config?)"
return response.text
if not kb_config_lock.acquire(False):
response.status = 403
response.text = u"An instance of KloudBuster is running, you cannot "\
"change the config until the run is finished!"
return response.text
self.kb_thread = None
def kb_thread_handler(self, session_id):
kb_session = KBSessionManager.get(session_id)
kb_session.kb_status = 'RUNNING'
kb_config = kb_session.kb_config
try:
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)
kb_session.kloudbuster = kloudbuster
if kloudbuster.check_and_upload_images():
kloudbuster.run()
kb_session.kb_status = 'READY'
except Exception:
response.status = 403
response.text = u"Error while running KloudBuster:\n%s" % traceback.format_exc()
kb_config_lock.release()
kb_session.kb_status = 'ERROR'
@expose(generic=True)
def status(self, *args):
if len(args):
session_id = args[0]
else:
response.status = 400
response.text = u"Please specify the session_id."
return response.text
kb_config_lock.release()
if KBSessionManager.has(session_id):
status = KBSessionManager.get(session_id).kb_status
return status
else:
response.status = 404
response.text = u"Session ID is not found or invalid."
return response.text
@expose(generic=True)
def log(self, *args):
if len(args):
session_id = args[0]
else:
response.status = 400
response.text = u"Please specify the session_id."
return response.text
if KBSessionManager.has(session_id):
kb_session = KBSessionManager.get(session_id)
plog = kb_session.kloudbuster.dump_logs(offset=0)\
if kb_session.kloudbuster else ""
return json.dumps(plog)
else:
response.status = 404
response.text = u"Session ID is not found or invalid."
return response.text
@expose(generic=True)
def report(self, *args):
if len(args):
session_id = args[0]
else:
response.status = 400
response.text = u"Please specify the session_id."
return response.text
if KBSessionManager.has(session_id):
kb_session = KBSessionManager.get(session_id)
preport = kb_session.kloudbuster.kb_runner.report\
if kb_session.kloudbuster and kb_session.kloudbuster.kb_runner else ""
return json.dumps(preport)
else:
response.status = 404
response.text = u"Session ID is not found or invalid."
return response.text
@expose(generic=True)
def version(self):
return kb_version
@expose(generic=True)
def run(self, *args):
response.status = 400
response.text = u"Please POST to this resource."
return response.text
@run.when(method='POST')
def run_POST(self, *args):
if len(args):
session_id = args[0]
else:
response.status = 400
response.text = u"Please specify the session_id."
return response.text
if not KBSessionManager.has(session_id):
response.status = 404
response.text = u"Session ID is not found or invalid."
return response.text
if KBSessionManager.get(session_id).kb_status == 'RUNNING':
response.status = 403
response.text = u"An instance of KloudBuster is already running."
return response.text
self.kb_thread = threading.Thread(target=self.kb_thread_handler, args=[session_id])
self.kb_thread.daemon = True
self.kb_thread.start()
return "OK!"

View File

@@ -0,0 +1,53 @@
# 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 threading
KB_SESSIONS = {}
KB_SESSIONS_LOCK = threading.Lock()
class KBSessionManager(object):
@staticmethod
def has(session_id):
global KB_SESSIONS
return True if session_id in KB_SESSIONS else False
@staticmethod
def get(session_id):
global KB_SESSIONS
return KB_SESSIONS[session_id] if KBSessionManager.has(session_id) else None
@staticmethod
def add(session_id, new_session):
global KB_SESSIONS
global KB_SESSIONS_LOCK
KB_SESSIONS_LOCK.acquire()
KB_SESSIONS[session_id] = new_session
KB_SESSIONS_LOCK.release()
@staticmethod
def delete(session_id):
global KB_SESSIONS
global KB_SESSIONS_LOCK
KB_SESSIONS_LOCK.acquire()
KB_SESSIONS.pop(session_id)
KB_SESSIONS_LOCK.release()
class KBSession(object):
def __init__(self):
self.kb_status = 'READY'
self.kb_config = None
self.kloudbuster = None