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 # specific language governing permissions and limitations
# under the License. # under the License.
from .tcExecuteEngine import TestCaseExecuteEngine
import sys import sys
import os import os
import traceback from optparse import OptionParser
import time import cmd
from argparse import ArgumentParser import random
from .marvinInit import MarvinInit 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, from marvin.codes import (SUCCESS,
FAILED, FAILED,
EXCEPTION, EXCEPTION
UNKNOWN_ERROR
) )
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(): class VerifyAndExit(object):
'''
Prints pretty message for parser and exit def __init__(self, msg):
''' self.msg = msg
global parser
if parser is not None: def __call__(self, original_func):
parser.print_usage() def new_function(*args, **kwargs):
exit(1) 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>'},
Parses,reads the options and verifies for the config 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>]'},
global parser '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>]'},
parser = ArgumentParser() '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>]'},
parser.add_argument("-d", "--tcpath", dest="tcpath", '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>]'},
help="the test case directory or file path") 'version': {'options': '', 'help': ''}
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)
'''
Check if the config file is None or not and exit accordingly
'''
if cfg_file is None:
printAndExit()
return {"cfg_file": cfg_file,
"load_flag": load_flag,
"tc_path": tc_path,
"num_iter": num_iter}
def startMarvin(cfg_file, load_flag): class ShellColor(object):
''' BLUE = '\033[94m'
Initialize the Marvin GREEN = '\033[92m'
''' YELLOW = '\033[93m'
try: RED = '\033[91m'
obj_marvininit = MarvinInit(cfg_file, load_flag) BOLD = '\033[1m'
if obj_marvininit.init() == SUCCESS: UNDERLINE = '\033[4m'
testClient = obj_marvininit.getTestClient() END = '\033[0m'
tcRunLogger = obj_marvininit.getLogger() ITALICS = '\x1B[3m'
parsedConfig = obj_marvininit.getParsedConfig()
debugStream = obj_marvininit.getDebugFile() #VERSION = "4.5.0-SNAPSHOT"
return {"tc_client": testClient,
"tc_runlogger": tcRunLogger,
"tc_parsedcfg": parsedConfig,
"tc_debugstream": debugStream}
else:
print "\nMarvin Initialization Failed"
exit(1)
except Exception as e:
print "\n Exception occurred while starting Marvin %s" % str(e)
exit(1)
def runTCs(num_iter, inp1, inp2): class MarvinCli(cmd.Cmd, object):
'''
Run Test Cases based upon number of iterations def __init__(self):
''' self.__configFile = None
n = 0 self.__deployFlag = False
while(n < num_iter): self.__zone = None
engine = TestCaseExecuteEngine(inp2["tc_client"], self.__hypervisorType = None
inp2["tc_parsedcfg"], self.__tcPath = None
inp2["tc_runlogger"], self.__testClient = None
inp2["tc_debugstream"]) self.__tcRunLogger = None
if inp1["tc_file"] is not None: self.__parsedConfig = None
engine.loadTestsFromFile(inp1["tc_file"]) self.__resultStream = None
else: self.__logFolderPath = None
engine.loadTestsFromDir(inp1["tc_dir"]) 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
'''
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):
'''
Parses,reads the options and verifies for the config file
'''
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
@VerifyAndExit("Marvin initialization failed, please check")
def start_marvin(self):
'''
Initialize the Marvin
'''
try:
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 "====Exception Occurred under start_marvin: %s ====" % \
GetDetailExceptionInfo(e)
return FAILED
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)
'''
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() engine.run()
n = n + 1 '''
print "\n==== Running Test Cases Successful ===="
def do_deploy(self, args):
self.__deployFlag = True
self.parse_input_deploy(inputs=args)
self.start_marvin()
def checkTCPath(tc_path): def do_deploydc_and_runtest(self, args):
''' self.do_deploy(inputs=args)
Verifies if the tc_path is a folder or file and its existence self.parse_input_runtcs()
''' self.run_test_suites()
ret = {"tc_file": None, "tc_dir": None}
check = True def do_generateapis_from_apispecfile(self, args):
if tc_path is None: api_spec_file = "/etc/cloud/cli/commands.xml"
printAndExit() cs_api_folder = "."
else: if args:
if os.path.isfile(tc_path): inp = args.strip().split(' ')
ret["tc_file"] = tc_path for items in inp:
elif os.path.isdir(tc_path): (key, value) = items.split('=')
ret["tc_dir"] = tc_path 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: else:
check = False os.makedirs(cs_api_folder)
if check is False: return cs_api_folder
print"\nTC Path is Invalid.So Exiting"
exit(1)
return ret 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__": if __name__ == "__main__":
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"

View File

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

View File

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