You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by sa...@apache.org on 2014/09/01 14:30:41 UTC

git commit: updated refs/heads/master to 6bd5041

Repository: cloudstack
Updated Branches:
  refs/heads/master 10391c9b4 -> 6bd5041ff


Adding first cut(draft) for marvincli

Signed-off-by: Santhosh Edukulla <sa...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/6bd5041f
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/6bd5041f
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/6bd5041f

Branch: refs/heads/master
Commit: 6bd5041ff01bb58c31ce3e76b1ea4a01b87998a8
Parents: 10391c9
Author: Santhosh Edukulla <sa...@gmail.com>
Authored: Mon Sep 1 17:55:51 2014 +0530
Committer: Santhosh Edukulla <sa...@gmail.com>
Committed: Mon Sep 1 18:00:20 2014 +0530

----------------------------------------------------------------------
 tools/marvin/marvin/deployAndRun.py    | 485 ++++++++++++++++++++--------
 tools/marvin/marvin/marvinInit.py      |  20 +-
 tools/marvin/marvin/tcExecuteEngine.py |  65 ++--
 tools/marvin/setup.py                  |   3 +-
 4 files changed, 397 insertions(+), 176 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6bd5041f/tools/marvin/marvin/deployAndRun.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/deployAndRun.py b/tools/marvin/marvin/deployAndRun.py
index 9f392e5..611e167 100644
--- a/tools/marvin/marvin/deployAndRun.py
+++ b/tools/marvin/marvin/deployAndRun.py
@@ -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
-
-
-def printAndExit():
-    '''
-    Prints pretty message for parser and exit
-    '''
-    global parser
-    if parser is not None:
-        parser.print_usage()
-        exit(1)
-
-
-def parseAndCheck():
-    '''
-    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)
-
-    '''
-    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):
-    '''
-    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)
-    except Exception as e:
-        print "\n Exception occurred while starting Marvin %s" % str(e)
-        exit(1)
-
-
-def runTCs(num_iter, inp1, inp2):
-    '''
-    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"])
+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>]")
+
+
+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
+
+
+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
+        '''
+        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()
-        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
+        '''
+        print "\n==== Running Test Cases Successful ===="
+
+    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:
-            check = False
-    if check is False:
-        print"\nTC Path is Invalid.So Exiting"
-        exit(1)
+            os.makedirs(cs_api_folder)
+        return cs_api_folder
 
-    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)
 
-if __name__ == "__main__":
+    def do_runtest(self, args):
+        self.parse_input_runtcs(args)
+        self.start_marvin()
+        self.run_test_suites()
 
-    '''
-    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"
+    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__":
+    main()

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6bd5041f/tools/marvin/marvin/marvinInit.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/marvinInit.py b/tools/marvin/marvin/marvinInit.py
index 56ea509..e888484 100644
--- a/tools/marvin/marvin/marvinInit.py
+++ b/tools/marvin/marvin/marvinInit.py
@@ -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" % \

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6bd5041f/tools/marvin/marvin/tcExecuteEngine.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/tcExecuteEngine.py b/tools/marvin/marvin/tcExecuteEngine.py
index e2f4d11..11b5d09 100644
--- a/tools/marvin/marvin/tcExecuteEngine.py
+++ b/tools/marvin/marvin/tcExecuteEngine.py
@@ -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),
-                                              os.path.basename(file_name))
-            self.injectTestCase(self.suite)
+            self.__suite = self.__loader.discover(os.path.dirname(file_name),
+                                                  os.path.basename(file_name))
+            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,
-                                         test.DomainName,
-                                         test.AcctType)
+                    # 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))

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6bd5041f/tools/marvin/setup.py
----------------------------------------------------------------------
diff --git a/tools/marvin/setup.py b/tools/marvin/setup.py
index 03e53c9..2a44a16 100644
--- a/tools/marvin/setup.py
+++ b/tools/marvin/setup.py
@@ -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']
       },
       )