Adding first cut(draft) for marvincli

Signed-off-by: Santhosh Edukulla <santhosh.edukulla@gmail.com>
This commit is contained in:
Santhosh Edukulla 2014-09-01 17:55:51 +05:30
parent 10391c9b49
commit 6bd5041ff0
4 changed files with 387 additions and 166 deletions

View File

@ -15,153 +15,362 @@
# specific language governing permissions and limitations
# under the License.
from .tcExecuteEngine import TestCaseExecuteEngine
import sys
import os
import traceback
import time
from argparse import ArgumentParser
from .marvinInit import MarvinInit
from optparse import OptionParser
import cmd
import random
from marvin.marvinInit import MarvinInit
from marvin.deployDataCenter import DeployDataCenters
from marvin.cloudstackException import GetDetailExceptionInfo
from marvin.codegenerator import CodeGenerator
from marvin.codes import (SUCCESS,
FAILED,
EXCEPTION,
UNKNOWN_ERROR
EXCEPTION
)
from marvin.tcExecuteEngine import TestCaseExecuteEngine
parser = None
class MarvinCliHelp(object):
@classmethod
def do_printhelp(cls):
print "\n1. for building marvin from spec file and installing."
cls.help_build_and_install()
print "\n2. for syncing apis and installing marvin."
cls.help_sync_and_install()
print "\n3. for deploying a datacenter"
cls.help_deploydc()
print "\n4. for running test cases"
cls.help_runtest()
print "\n5. for deploying a datacenter (and) running tests"
cls.help_deploydc_and_runtest()
print "\n6. for generating apis from spec file"
cls.help_generateapis_from_apispecfile()
print "\n7. for generating apis from end point"
cls.help_generateapis_from_endpoint()
print "\n8. for printing marvincli version"
cls.help_printversion()
@classmethod
def print_msg(cls, msg):
print ShellColor.BOLD + ShellColor.RED + msg + ShellColor.END
@classmethod
def help_printversion(cls):
cls.print_msg("marvincli -v or --version")
@classmethod
def help_deploydc(cls):
cls.print_msg(
"marvincli [deploydc] \n\t[config-file=<marvin-config-file EX: advanced.cfg file>]")
@classmethod
def help_deploydc_and_runtest(cls, deploy=False):
msg = "marvincli [deploydc_and_runtest] \n\t[config-file=<path_to_marvin_cfg> \n\ttc-path=<test suite or test suite folder path>" \
"\n\tzone=<name of the zone> \n\thyp-type=<hypervisor_type> " \
"\n\trequired_hardware=<true\\false>]"
cls.print_msg(msg)
@classmethod
def help_generateapis_from_apispecfile(cls):
cls.print_msg(
"marvincli [generateapis_from_apispecfile] \n\t[cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tapi-spec-file=<api spec file EX: /etc/cloud/cli/commands.xml>]")
@classmethod
def help_generateapis_from_endpoint(cls):
cls.print_msg(
"marvincli [generateapis_from_endpoint] \n\t[cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tend-point=<CS Endpoint ip EX: localhost>]")
@classmethod
def help_runtest(cls):
cls.print_msg(
"marvincli [runtest] \n\t[config-file=<path_to_marvin_config> \n\ttc-path=test/integration/smoke \n\trequired_hardware=<true\\false> \n\tzone=<name of zone> \n\thyp-type=<xenserver\\kvm\\vmware> etc]")
@classmethod
def help_sync_and_install(cls):
cls.print_msg(
"marvincli [sync_and_install] \n\t[cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tend-point=<CS installed host ip EX: localhost>]")
@classmethod
def help_build_and_install(cls):
cls.print_msg(
"marvincli [build_and_install] \n\t[cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tapi-sync-file<api spec file generated by cs EX: /etc/cloud/cli/commands.xml>]")
def printAndExit():
'''
Prints pretty message for parser and exit
'''
global parser
if parser is not None:
parser.print_usage()
exit(1)
class VerifyAndExit(object):
def __init__(self, msg):
self.msg = msg
def __call__(self, original_func):
def new_function(*args, **kwargs):
exit_check = False
try:
if original_func(*args, **kwargs) == FAILED:
exit_check = True
except Exception as e:
print "---", e
exit_check = True
finally:
if exit_check:
print "==== %s ====" % self.msg
MarvinCliHelp.do_printhelp()
sys.exit(1)
return new_function
def parseAndCheck():
class MarvinCliCommands(object):
cmds_info = {'deploydc': {'options': ['config-file'], 'help': 'config-file=<marvin-config-file EX: advanced.cfg file>'},
'deploydc_and_runtest': {'options': ['config-file', 'tc-path', 'zone', 'hyp-type', 'required_hardware'], 'help': ''},
'generateapis_from_endpoint': {'options': '', 'help': '[cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tend-point=<CS Endpoint ip EX: localhost>]'},
'generateapis_from_apispecfile': {'options': '', 'help': '[cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tapi-spec-file=<api spec file EX: /etc/cloud/cli/commands.xml>]'},
'runtest': {'options': '', 'help': '[config-file=<path_to_marvin_config> \n\ttc-path=test/integration/smoke \n\trequired_hardware=<true\\false> \n\tzone=<name of zone> \n\thyp-type=<xenserver\\kvm\\vmware> etc]'},
'sync_and_install': {'options': ['sync_and_install'], 'help': '[marvincli sync_and_install cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tend-point=<CS installed host ip EX: localhost>]'},
'build_and_install': {'options': ['build_and_install'], 'help': '[marvincli build_and_install cs-folder-path=<cloudstack code root dir EX: /root/cs-4.5/cloudstack/> \n\tapi-sync-file<api spec file generated by cs EX: /etc/cloud/cli/commands.xml>]'},
'version': {'options': '', 'help': ''}
}
class ShellColor(object):
BLUE = '\033[94m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
END = '\033[0m'
ITALICS = '\x1B[3m'
#VERSION = "4.5.0-SNAPSHOT"
class MarvinCli(cmd.Cmd, object):
def __init__(self):
self.__configFile = None
self.__deployFlag = False
self.__zone = None
self.__hypervisorType = None
self.__tcPath = None
self.__testClient = None
self.__tcRunLogger = None
self.__parsedConfig = None
self.__resultStream = None
self.__logFolderPath = None
self.__testRunner = None
self.__requiredHw = False
self.__csFolder = "."
cmd.Cmd.__init__(self)
@VerifyAndExit("Invalid input options, please check")
def parse_input_deploy(self, inputs=None):
'''
Parses,reads the options and verifies for the config file
'''
global parser
parser = ArgumentParser()
parser.add_argument("-d", "--tcpath", dest="tcpath",
help="the test case directory or file path")
parser.add_argument("-c", "--config", action="store",
default="./datacenterCfg", dest="config",
help="the path where the json config file generated,\
by default is ./datacenterCfg")
parser.add_argument("-l", "--load", dest="load", action="store_true",
help="only load config, do not deploy,\
it will only run testcase")
parser.add_argument("-n", "--num", dest="number",
help="how many times you want to run the tests")
options = parser.parse_args()
cfg_file = options.config
tc_path = options.tcpath
load_flag = options.load
num_iter = 1 if options.number is None else int(options.number)
if inputs:
out_dict = {}
args = inputs.strip().split(' ')
for item in args:
(key, value) = item.split('=')
out_dict[key] = value
self.__configFile = out_dict.get('config-file', '')
if not self.__configFile:
return FAILED
print "\n==== Parsing Input Options Successful ===="
return SUCCESS
return FAILED
@VerifyAndExit("Invalid input options, please check")
def parse_input_runtcs(self, inputs):
'''
Check if the config file is None or not and exit accordingly
Parses,reads the options and verifies for the config file
'''
if cfg_file is None:
printAndExit()
return {"cfg_file": cfg_file,
"load_flag": load_flag,
"tc_path": tc_path,
"num_iter": num_iter}
if inputs:
out_dict = {}
args = inputs.strip().split(' ')
for item in args:
(key, value) = item.split('=')
out_dict[key] = value
self.__configFile = out_dict.get('config-file', None)
self.__deployFlag = out_dict.get('deploy', False)
self.__zone = out_dict.get("zone", None)
self.__hypervisorType = out_dict.get("hyp-type", None)
self.__tcPath = out_dict.get("tc-path",)
self.__requiredHw = out_dict.get("required-hardware")
if not all([self.__tcPath, self.__configFile]):
return FAILED
print "\n==== Parsing Input Options Successful ===="
return SUCCESS
return FAILED
def startMarvin(cfg_file, load_flag):
@VerifyAndExit("Marvin initialization failed, please check")
def start_marvin(self):
'''
Initialize the Marvin
'''
try:
obj_marvininit = MarvinInit(cfg_file, load_flag)
if obj_marvininit.init() == SUCCESS:
testClient = obj_marvininit.getTestClient()
tcRunLogger = obj_marvininit.getLogger()
parsedConfig = obj_marvininit.getParsedConfig()
debugStream = obj_marvininit.getDebugFile()
return {"tc_client": testClient,
"tc_runlogger": tcRunLogger,
"tc_parsedcfg": parsedConfig,
"tc_debugstream": debugStream}
else:
print "\nMarvin Initialization Failed"
exit(1)
obj_marvininit = MarvinInit(config_file=self.__configFile,
deploy_dc_flag=self.__deployFlag,
zone=self.__zone,
hypervisor_type=self.__hypervisorType,
user_logfolder_path=None)
if obj_marvininit and obj_marvininit.init() == SUCCESS:
self.__testClient = obj_marvininit.getTestClient()
self.__tcRunLogger = obj_marvininit.getLogger()
self.__parsedConfig = obj_marvininit.getParsedConfig()
self.__resultStream = obj_marvininit.getResultFile()
self.__logFolderPath = obj_marvininit.getLogFolderPath()
return SUCCESS
return FAILED
except Exception as e:
print "\n Exception occurred while starting Marvin %s" % str(e)
exit(1)
print "====Exception Occurred under start_marvin: %s ====" % \
GetDetailExceptionInfo(e)
return FAILED
def runTCs(num_iter, inp1, inp2):
def run_test_suites(self):
print "\n==== Started Running Test Cases ===="
xunit_out_path = "/tmp/marvin_xunit_out" + \
str(random.randrange(1, 10000)) + ".xml"
marvin_tc_run_cmd = "nosetests-2.7 -s --with-marvin --marvin-config=%s --with-xunit --xunit-file=%s %s -a tags=advanced, required_hardware=%s --zone=%s --hypervisor=%s"
if os.path.isfile(self.__tcPath):
marvin_tc_run_cmd = marvin_tc_run_cmd % (self.__configFile,
xunit_out_path, self.__requiredHw, self.__zone, self.__hypervisorType)
if os.path.isdir(self.__tcPath):
marvin_tc_run_cmd = marvin_tc_run_cmd % (self.__configFile,
xunit_out_path, self.__requiredHw, self.__zone, self.__hypervisorType)
os.system(marvin_tc_run_cmd)
'''
Run Test Cases based upon number of iterations
'''
n = 0
while(n < num_iter):
engine = TestCaseExecuteEngine(inp2["tc_client"],
inp2["tc_parsedcfg"],
inp2["tc_runlogger"],
inp2["tc_debugstream"])
if inp1["tc_file"] is not None:
engine.loadTestsFromFile(inp1["tc_file"])
else:
engine.loadTestsFromDir(inp1["tc_dir"])
engine = TestCaseExecuteEngine(self.__testClient,
self.__parsedConfig,
tc_logger=self.__tcRunLogger)
if os.path.isfile(self.__tcPath):
engine.loadTestsFromFile(self.__tcPath)
elif os.path.isdir(self.__tcPath):
engine.loadTestsFromDir(self.__tcPath)
engine.run()
n = n + 1
def checkTCPath(tc_path):
'''
Verifies if the tc_path is a folder or file and its existence
'''
ret = {"tc_file": None, "tc_dir": None}
check = True
if tc_path is None:
printAndExit()
else:
if os.path.isfile(tc_path):
ret["tc_file"] = tc_path
elif os.path.isdir(tc_path):
ret["tc_dir"] = tc_path
else:
check = False
if check is False:
print"\nTC Path is Invalid.So Exiting"
exit(1)
print "\n==== Running Test Cases Successful ===="
return ret
def do_deploy(self, args):
self.__deployFlag = True
self.parse_input_deploy(inputs=args)
self.start_marvin()
def do_deploydc_and_runtest(self, args):
self.do_deploy(inputs=args)
self.parse_input_runtcs()
self.run_test_suites()
def do_generateapis_from_apispecfile(self, args):
api_spec_file = "/etc/cloud/cli/commands.xml"
cs_api_folder = "."
if args:
inp = args.strip().split(' ')
for items in inp:
(key, value) = items.split('=')
if key.lower() == 'api-spec-file':
if os.path.exists(value):
api_spec_file = value
elif not os.path.exists(api_spec_file):
print "=== Mentioned api spec file :%s does not exists ===" % str(api_spec_file)
sys.exit(1)
if key.lower() == 'cs-folder-path':
cs_api_folder = self.create_marvin_api_folder(value)
cg = CodeGenerator(cs_api_folder)
if api_spec_file:
try:
cg.generateCodeFromXML(api_spec_file)
return
except Exception as e:
print "==== Generating apis from api spec file failed: %s ====" % str(e.message())
sys.exit(1)
sys.exit(1)
def create_marvin_api_folder(self, cs_folder_path='.'):
cs_api_folder = cs_folder_path + "/tools/marvin/marvin/cloudstackAPI"
if os.path.exists(cs_api_folder):
os.rmdir(cs_api_folder)
else:
os.makedirs(cs_api_folder)
return cs_api_folder
def do_generateapis_from_endpoint(self, args):
endpoint_url = 'http://%s:8096/client/api?command=listApis&\
response=json'
cs_api_folder = "."
if args:
inp = args.strip().split(' ')
for items in inp:
(key, value) = items.split('=')
if key.lower() == 'endpoint':
cs_end_point = value
if key.lower() == 'cs-folder-path':
cs_api_folder = self.create_marvin_api_folder(value)
cg = CodeGenerator(cs_api_folder)
if cs_end_point:
try:
endpoint_url = endpoint_url % str(cs_end_point)
cg.generateCodeFromJSON(endpoint_url)
return
except Exception as e:
print "==== Generating apis from end point failed: %s ====" % str(e.message())
sys.exit(1)
sys.exit(1)
def do_runtest(self, args):
self.parse_input_runtcs(args)
self.start_marvin()
self.run_test_suites()
def install_marvin(self):
if self.__csFolder:
marvin_setup_file_path = self.__csFolder + "/tools/marvin/setup.py"
# step2: Build and install the Marvin
try:
os.system("python %s install" % str(marvin_setup_file_path))
except Exception as e:
print "==== Marvin Installation Failed ===="
print "==== Marvin Installed Successfully ===="
def do_build_and_install(self, args):
# step1: Generate the apis from spec file first
self.do_generateapis_from_apispecfile(args)
self.install_marvin()
def do_sync_and_install(self, args):
# step1: Generate the apis from spec file first
self.do_generateapis_from_endpoint(args)
self.install_marvin()
class MarvinCliParser(OptionParser):
def format_help(self, formatter=None):
if formatter is None:
formatter = self.formatter
print MarvinCliHelp.print_msg("Usage: marvincli [cmd] [options].See, the below cmds for more information \n\n")
print MarvinCliHelp.do_printhelp()
return "\n===========================================================================\n"
def main():
parser = MarvinCliParser()
parser.add_option("-v", "--version",
action="store_true", dest="version", default=False,
help="prints marvin cli version information")
(options, args) = parser.parse_args()
if options.version:
MarvinCliHelp.help_printversion()
sys.exit(0)
if len(sys.argv) > 1:
if sys.argv[1].lower() in ["deploydc", "deploydc_and_runtest", "generateapis_from_endpoint",
"generateapis_from_apispecfile", "runtest", "sync_and_install", "build_and_install"]:
MarvinCli().onecmd(' '.join(args))
else:
print "\n==== Invalid Command ===="
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
'''
1. Parse and Check
'''
out1 = parseAndCheck()
print "\nStep1 :Parsing Options And Check Went Fine"
'''
2. Start Marvin
'''
out2 = startMarvin(out1["cfg_file"], out1["load_flag"])
print "\nStep2: Marvin Initialization Went Fine"
'''
3. Check TC folder or Module and Path existence
'''
out3 = checkTCPath(out1["tc_path"])
print "\nStep3: TC Path Check Went Fine"
'''
4. Run TCs
'''
runTCs(out1["num_iter"], out3, out2)
print "\nStep4: TC Running Finished"
main()

View File

@ -40,7 +40,7 @@ import os
class MarvinInit:
def __init__(self, config_file,
deploy_dc_flag=None,
deploy_dc_flag=False,
test_mod_name="deploydc",
zone=None,
hypervisor_type=None,
@ -109,12 +109,19 @@ class MarvinInit:
'''
try:
if not self.__hypervisorType:
self.__hypervisorType = XEN_SERVER
if self.__parsedConfig and self.__parsedConfig.zones is not None:
for zone in self.__parsedConfig.zones:
for pod in zone.pods and pod is not None:
for cluster in pod.clusters and cluster is not None:
self.__hypervisorType = cluster.hypervisor
break
if not self.__zoneForTests:
if self.__parsedConfig and self.__parsedConfig.zones is not None:
for zone in self.__parsedConfig.zones:
self.__zoneForTests = zone.name
break
if not self.__hypervisorType:
self.__hypervisorType = XEN_SERVER
return SUCCESS
except Exception as e:
print "\n Exception Occurred Under init " \
@ -133,13 +140,16 @@ class MarvinInit:
@Output : SUCCESS or FAILED
'''
try:
print "\n==== Marvin Init Started ===="
if ((self.__parseConfig() != FAILED) and
(self.__setHypervisorAndZoneInfo())and
(self.__setTestDataPath() != FAILED) and
(self.__initLogging() != FAILED) and
(self.__createTestClient() != FAILED) and
(self.__deployDC() != FAILED)):
print "\n==== Marvin Init Successful ===="
return SUCCESS
print "\n==== Marvin Init Failed ===="
return FAILED
except Exception as e:
print "\n Exception Occurred Under init " \
@ -232,10 +242,8 @@ class MarvinInit:
self.__parsedConfig,
self.__tcRunLogger)
ret = deploy_obj.deploy()
if ret == SUCCESS:
print "Deploy DC Successful"
else:
print "Deploy DC Failed"
if ret != SUCCESS:
print "==== Deploy DC Failed ===="
return ret
except Exception as e:
print "\n Exception Occurred Under __deployDC : %s" % \

View File

@ -24,50 +24,53 @@ from functools import partial
class TestCaseExecuteEngine(object):
def __init__(self, testclient, config, tc_logger=None, debug_stream=None):
def __init__(self, testclient, config, tc_logger=None,
debug_stream=sys.stdout):
"""
Initialize the testcase execution engine, just the basics here
@var testcaseLogFile: client log file
@var testResultLogFile: summary report file
Initialize the testcase execution engine
"""
self.testclient = testclient
self.config = config
self.tcRunLogger = tc_logger
self.debugStream = debug_stream
self.loader = unittest.loader.TestLoader()
self.suite = None
self.__testClient = testclient
self.__parsedConfig = config
self.__tcRunLogger = tc_logger
self.__debugStream = debug_stream
self.__loader = unittest.loader.TestLoader()
self.__suite = None
def loadTestsFromDir(self, testDirectory):
def loadTestsFromDir(self, test_directory):
""" Load the test suites from a package with multiple test files """
self.suite = self.loader.discover(testDirectory)
self.injectTestCase(self.suite)
self.__suite = self.__loader.discover(test_directory)
self.injectTestCase(self.__suite)
def loadTestsFromFile(self, file_name):
""" Load the tests from a single script/module """
if os.path.isfile(file_name):
self.suite = self.loader.discover(os.path.dirname(file_name),
self.__suite = self.__loader.discover(os.path.dirname(file_name),
os.path.basename(file_name))
self.injectTestCase(self.suite)
self.injectTestCase(self.__suite)
def injectTestCase(self, testSuites):
for test in testSuites:
def injectTestCase(self, test_suites):
for test in test_suites:
if isinstance(test, unittest.BaseTestSuite):
self.injectTestCase(test)
else:
# inject testclient and logger into each unittest
self.tcRunLogger.name = test.__str__()
setattr(test, "testClient", self.testclient)
setattr(test, "config", self.config)
setattr(test, "debug", self.tcRunLogger.debug)
setattr(test.__class__, "clstestclient", self.testclient)
setattr(test, "debug", self.__tcRunLogger.debug)
setattr(test, "info", self.__tcRunLogger.info)
setattr(test, "warn", self.__tcRunLogger.warning)
setattr(test, "error", self.__tcRunLogger.error)
setattr(test, "clstestclient", self.__testClient)
setattr(test, "testClient", self.__testClient)
setattr(test, "config", self.__parsedConfig)
if hasattr(test, "user"):
# attribute when test is entirely executed as user
self.testclient.\
getUserApiClient(test.UserName,
# when the class-level attr applied. all test runs as
# 'user'
self.__testClient.getUserApiClient(test.UserName,
test.DomainName,
test.AcctType)
def run(self):
if self.suite:
unittest.TextTestRunner(stream=self.debugStream,
verbosity=2).run(self.suite)
if self.__suite:
print "\n==== Test Suite :%s Started ====" % (str(self.__suite))
unittest.TextTestRunner(stream=self.__debugStream,
verbosity=2).run(self.__suite)
print "\n==== Test Suite :%s Finished ====" % (str(self.__suite))

View File

@ -55,6 +55,7 @@ setup(name="Marvin",
py_modules=['marvin.marvinPlugin'],
zip_safe=False,
entry_points={
'nose.plugins': ['marvinPlugin = marvin.marvinPlugin:MarvinPlugin']
'nose.plugins': ['marvinPlugin = marvin.marvinPlugin:MarvinPlugin'],
'console_scripts': ['marvincli = marvin.deployAndRun:main']
},
)