diff --git a/tools/marvin/marvin/deployAndRun.py b/tools/marvin/marvin/deployAndRun.py index 611e16792ca..5955bc98fde 100644 --- a/tools/marvin/marvin/deployAndRun.py +++ b/tools/marvin/marvin/deployAndRun.py @@ -20,6 +20,7 @@ import os from optparse import OptionParser import cmd import random +from collections import OrderedDict from marvin.marvinInit import MarvinInit from marvin.deployDataCenter import DeployDataCenters from marvin.cloudstackException import GetDetailExceptionInfo @@ -31,73 +32,6 @@ from marvin.codes import (SUCCESS, from marvin.tcExecuteEngine import TestCaseExecuteEngine -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=]") - - @classmethod - def help_deploydc_and_runtest(cls, deploy=False): - msg = "marvincli [deploydc_and_runtest] \n\t[config-file= \n\ttc-path=" \ - "\n\tzone= \n\thyp-type= " \ - "\n\trequired_hardware=]" - cls.print_msg(msg) - - @classmethod - def help_generateapis_from_apispecfile(cls): - cls.print_msg( - "marvincli [generateapis_from_apispecfile] \n\t[cs-folder-path= \n\tapi-spec-file=]") - - @classmethod - def help_generateapis_from_endpoint(cls): - cls.print_msg( - "marvincli [generateapis_from_endpoint] \n\t[cs-folder-path= \n\tend-point=]") - - @classmethod - def help_runtest(cls): - cls.print_msg( - "marvincli [runtest] \n\t[config-file= \n\ttc-path=test/integration/smoke \n\trequired_hardware= \n\tzone= \n\thyp-type= etc]") - - @classmethod - def help_sync_and_install(cls): - cls.print_msg( - "marvincli [sync_and_install] \n\t[cs-folder-path= \n\tend-point=]") - - @classmethod - def help_build_and_install(cls): - cls.print_msg( - "marvincli [build_and_install] \n\t[cs-folder-path= \n\tapi-sync-file]") - - class VerifyAndExit(object): def __init__(self, msg): @@ -110,26 +44,113 @@ class VerifyAndExit(object): if original_func(*args, **kwargs) == FAILED: exit_check = True except Exception as e: - print "---", e + print "===Exception.Please Check:===", e exit_check = True finally: if exit_check: print "==== %s ====" % self.msg - MarvinCliHelp.do_printhelp() + MarvinCliHelp.print_cmds_help() sys.exit(1) return new_function class MarvinCliCommands(object): - cmds_info = {'deploydc': {'options': ['config-file'], 'help': 'config-file='}, - 'deploydc_and_runtest': {'options': ['config-file', 'tc-path', 'zone', 'hyp-type', 'required_hardware'], 'help': ''}, - 'generateapis_from_endpoint': {'options': '', 'help': '[cs-folder-path= \n\tend-point=]'}, - 'generateapis_from_apispecfile': {'options': '', 'help': '[cs-folder-path= \n\tapi-spec-file=]'}, - 'runtest': {'options': '', 'help': '[config-file= \n\ttc-path=test/integration/smoke \n\trequired_hardware= \n\tzone= \n\thyp-type= etc]'}, - 'sync_and_install': {'options': ['sync_and_install'], 'help': '[marvincli sync_and_install cs-folder-path= \n\tend-point=]'}, - 'build_and_install': {'options': ['build_and_install'], 'help': '[marvincli build_and_install cs-folder-path= \n\tapi-sync-file]'}, - 'version': {'options': '', 'help': ''} - } + cmds_info = OrderedDict({ + 'deploydc': + { + 'summary': 'for deploying a datacenter', + 'options': ['*config-file'], + 'help': 'marvincli deploydc config-file=', + 'desc': 'deploys a data center using the config file provided' + }, + 'deploydc_and_runtest': + { + 'summary': 'for deploying a datacenter (and) running tests, either test suite (or) directory of test suites', + 'options': ['*config-file', '*tc-path', 'zone', 'hyp-type', 'required_hardware'], + 'help': 'marvincli deploydc_and_runtest config-file=' + 'tc-path=' + 'zone= hyp-type= required_hardware=', + 'desc': 'deploys a data center using the config file provided, and runs test cases using the test suite or directory of test suites provided. ' + 'If zone to run against is not provided, then default zone mentioned in config file is provided ' + 'If hyp-type information is not provided, first hypervisor from config file is taken. ' + 'If required_hardware option is not provided, then it is set to false' + }, + 'generateapis_from_endpoint': + { + 'summary': 'for generating apis from cs end point', + 'options': ['*cs-folder-path', 'end-point'], + 'help': 'marvincli generateapis_from_endpoint cs-folder-path=' + 'end-point=', + 'desc': 'generates cloudstackAPI directory with CS apis information from cloudstack endpoint. ' + 'If end-point information is not provided, localhost is considered as default' + + }, + 'generateapis_from_apispecfile': + { + 'summary': 'for generating apis from api spec file', + 'options': ['*cs-folder-path', 'api-spec-file'], + 'help': 'marvincli generateapis_from_apispecfile cs-folder-path=' + 'api-spec-file=', + 'desc': 'generates cloudstackAPI directory with CS apis information from cloudstack api spec file. ' + 'If spec file information is not provided, /etc/cloud/cli/commands.xml is considered as default' + }, + 'runtest': + { + 'summary': 'for running test cases, either test suite (or) directory of test suites', + 'options': ['*config-file', '*tc-path', 'required_hardware', 'zone', 'hyp-type'], + 'help': 'marvincli runtest config-file= tc-path=test/integration/smoke' + 'required_hardware= zone= hyp-type= etc', + 'desc': 'runs marvin integration tests against CS using config file, test suite path or directory of test suites are provided as input for running tests', + }, + 'sync_and_install': + { + 'summary': 'for syncing apis and installing marvin using cs endpoint', + 'options': ['*cs-folder-path', 'end-point'], + 'help': 'marvincli sync_and_install cs-folder-path = ' + 'end-point = ', + 'desc': 'generates cloudstackAPI directory with CS apis information from cloudstack end-point (and) installs new marvin.' + 'If end-point information is not provided, localhost is considered as default' + }, + 'build_and_install': + { + 'summary': 'for building and installing marvin using spec file', + 'options': ['*cs-folder-path', 'api-sync-file'], + 'help': 'marvincli build_and_install cs-folder-path = ' + 'api-sync-file = ', + 'desc': 'generates cloudstackAPI directory with CS apis information from cloudstack api-spec-file (and) installs new marvin.' + 'If api spec file information is not provided, /etc/cloud/cli/commands.xml is considered as default' + }, + 'version': + { + 'summary': 'for printing marvincli version', + 'options': ['-v (or) --version'], + 'help': 'marvincli -v (or) marvincli --version', + 'desc': 'prints the version of marvincli' + } + }) + + +class MarvinCliHelp(object): + + @classmethod + def print_cmds_help(cls): + msg = '' + for cmd_name, cmd_txt in MarvinCliCommands.cmds_info.items(): + msg = msg + \ + '\n----------------------------------------------------\n' + cmd_info = ShellColor.BOLD + ShellColor.RED + \ + 'cmd_name:%s' % str(cmd_name) + ShellColor.END + for key, value in cmd_txt.iteritems(): + cmd_info = cmd_info + '\n' + \ + str(key) + ' : ' + str(value).strip('\n') + msg = msg + cmd_info + # return ShellColor.BOLD + ShellColor.RED + msg + ShellColor.END + return msg + + @classmethod + def print_msg(cls, msg): + if msg: + return ShellColor.BOLD + ShellColor.RED + msg + ShellColor.END class ShellColor(object): @@ -163,7 +184,8 @@ class MarvinCli(cmd.Cmd, object): self.__csFolder = "." cmd.Cmd.__init__(self) - @VerifyAndExit("Invalid input options, please check") + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def parse_input_deploy(self, inputs=None): ''' Parses,reads the options and verifies for the config file @@ -181,7 +203,8 @@ class MarvinCli(cmd.Cmd, object): return SUCCESS return FAILED - @VerifyAndExit("Invalid input options, please check") + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def parse_input_runtcs(self, inputs): ''' Parses,reads the options and verifies for the config file @@ -193,7 +216,6 @@ class MarvinCli(cmd.Cmd, object): (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",) @@ -225,13 +247,13 @@ class MarvinCli(cmd.Cmd, object): return FAILED except Exception as e: print "====Exception Occurred under start_marvin: %s ====" % \ - GetDetailExceptionInfo(e) + 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" + 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, @@ -252,16 +274,32 @@ class MarvinCli(cmd.Cmd, object): ''' print "\n==== Running Test Cases Successful ====" - def do_deploy(self, args): - self.__deployFlag = True - self.parse_input_deploy(inputs=args) - self.start_marvin() + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") + def do_deploydc(self, args): + try: + self.__deployFlag = True + self.parse_input_deploy(inputs=args) + self.start_marvin() + return SUCCESS + except Exception as e: + print "==== deploy cmd failed :%s ==== " % str(e) + return FAILED + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def do_deploydc_and_runtest(self, args): - self.do_deploy(inputs=args) - self.parse_input_runtcs() - self.run_test_suites() + try: + self.do_deploy(inputs=args) + self.parse_input_runtcs() + self.run_test_suites() + return SUCCESS + except Exception as e: + print "==== deploydc cmd failed:%s ==== " % str(e) + return FAILED + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def do_generateapis_from_apispecfile(self, args): api_spec_file = "/etc/cloud/cli/commands.xml" cs_api_folder = "." @@ -281,11 +319,11 @@ class MarvinCli(cmd.Cmd, object): if api_spec_file: try: cg.generateCodeFromXML(api_spec_file) - return + return SUCCESS except Exception as e: print "==== Generating apis from api spec file failed: %s ====" % str(e.message()) - sys.exit(1) - sys.exit(1) + return FAILED + return FAILED def create_marvin_api_folder(self, cs_folder_path='.'): cs_api_folder = cs_folder_path + "/tools/marvin/marvin/cloudstackAPI" @@ -295,6 +333,8 @@ class MarvinCli(cmd.Cmd, object): os.makedirs(cs_api_folder) return cs_api_folder + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def do_generateapis_from_endpoint(self, args): endpoint_url = 'http://%s:8096/client/api?command=listApis&\ response=json' @@ -312,36 +352,54 @@ response=json' try: endpoint_url = endpoint_url % str(cs_end_point) cg.generateCodeFromJSON(endpoint_url) - return + return SUCCESS except Exception as e: print "==== Generating apis from end point failed: %s ====" % str(e.message()) - sys.exit(1) - sys.exit(1) + return FAILED + return FAILED + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def do_runtest(self, args): - self.parse_input_runtcs(args) - self.start_marvin() - self.run_test_suites() + try: + self.parse_input_runtcs(args) + self.start_marvin() + self.run_test_suites() + return SUCCESS + except Exception as e: + print "==== run test failed: %s ====" % str(e.message()) + return FAILED 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)) + print "==== Marvin Installed Successfully ====" except Exception as e: print "==== Marvin Installation Failed ====" - print "==== Marvin Installed Successfully ====" + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def do_build_and_install(self, args): - # step1: Generate the apis from spec file first - self.do_generateapis_from_apispecfile(args) - self.install_marvin() + try: + self.do_generateapis_from_apispecfile(args) + self.install_marvin() + return SUCCESS + except Exception as e: + print "==== build from end point and install marvin failed: %s ====" % str(e) + return FAILED + @VerifyAndExit( + "cmd failed, may be invalid input options, please check help") def do_sync_and_install(self, args): - # step1: Generate the apis from spec file first - self.do_generateapis_from_endpoint(args) - self.install_marvin() + try: + self.do_generateapis_from_endpoint(args) + self.install_marvin() + return SUCCESS + except Exception as e: + print "==== sync from spec file and install marvin failed: %s ====" % str(e) + return FAILED class MarvinCliParser(OptionParser): @@ -349,9 +407,14 @@ 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" + result = [] + if self.usage: + result.append(MarvinCliHelp.print_msg("\nUsage: marvincli [cmd] [options]. See, the below cmds for more information." + "(*) signifies mandatory fields \n\n")) + self.description = MarvinCliHelp.print_cmds_help() + if self.description: + result.append(self.format_description(formatter) + "\n") + return "".join(result) def main(): @@ -364,12 +427,14 @@ def main(): 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: + if sys.argv[1].lower() not in MarvinCliCommands.cmds_info.keys(): print "\n==== Invalid Command ====" sys.exit(1) + args = ' '.join(args) + if '-h' in args or '--help' in args: + print MarvinCliCommands.cmds_info[sys.argv[0]] + else: + MarvinCli().onecmd(args) sys.exit(0) if __name__ == "__main__": diff --git a/tools/marvin/marvin/marvinInit.py b/tools/marvin/marvin/marvinInit.py index e888484a6cb..c6ee1a2c7ad 100644 --- a/tools/marvin/marvin/marvinInit.py +++ b/tools/marvin/marvin/marvinInit.py @@ -111,10 +111,12 @@ class MarvinInit: if not self.__hypervisorType: 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 + for pod in zone.pods: + if pod is not None: + for cluster in pod.clusters: + if cluster is not None and cluster.hypervisor 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: