You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2014/07/28 17:41:25 UTC

[08/18] Marvin + test changes from master Signed-off-by: SrikanteswaraRao Talluri

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/cloudstackConnection.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py
index fb03e3b..d006002 100644
--- a/tools/marvin/marvin/cloudstackConnection.py
+++ b/tools/marvin/marvin/cloudstackConnection.py
@@ -21,21 +21,35 @@ import base64
 import hmac
 import hashlib
 import time
-import cloudstackException
 from cloudstackAPI import *
 import jsonHelper
+from marvin.codes import (
+    FAILED,
+    INVALID_RESPONSE,
+    INVALID_INPUT,
+    JOB_FAILED,
+    JOB_INPROGRESS,
+    JOB_CANCELLED,
+    JOB_SUCCEEDED
+)
 from requests import (
     ConnectionError,
     HTTPError,
     Timeout,
     RequestException
-    )
+)
+from marvin.cloudstackException import GetDetailExceptionInfo
 
 
-class cloudConnection(object):
+class CSConnection(object):
+
+    '''
+    @Desc: Connection Class to make API\Command calls to the
+           CloudStack Management Server
+           Sends the GET\POST requests to CS based upon the
+           information provided and retrieves the parsed response.
+    '''
 
-    """ Connections to make API calls to the cloudstack management server
-    """
     def __init__(self, mgmtDet, asyncTimeout=3600, logger=None,
                  path='client/api'):
         self.apiKey = mgmtDet.apiKey
@@ -44,68 +58,93 @@ class cloudConnection(object):
         self.port = mgmtDet.port
         self.user = mgmtDet.user
         self.passwd = mgmtDet.passwd
-        self.certCAPath = mgmtDet.certCAPath
-        self.certPath = mgmtDet.certPath
+        self.certPath = ()
+        if mgmtDet.certCAPath != "NA" and mgmtDet.certPath != "NA":
+            self.certPath = (mgmtDet.certCAPath, mgmtDet.certPath)
         self.logger = logger
         self.path = path
         self.retries = 5
+        self.__lastError = ''
         self.mgtDetails = mgmtDet
-        self.protocol = "http"
         self.asyncTimeout = asyncTimeout
         self.auth = True
         if self.port == 8096 or \
            (self.apiKey is None and self.securityKey is None):
             self.auth = False
-        if mgmtDet.useHttps == "True":
-            self.protocol = "https"
-        self.baseurl = "%s://%s:%d/%s"\
+        self.protocol = "https" if mgmtDet.useHttps == "True" else "http"
+        self.httpsFlag = True if self.protocol == "https" else False
+        self.baseUrl = "%s://%s:%d/%s"\
                        % (self.protocol, self.mgtSvr, self.port, self.path)
 
     def __copy__(self):
-        return cloudConnection(self.mgtDetails,
-                               self.asyncTimeout,
-                               self.logger,
-                               self.path)
-
-    def poll(self, jobid, response):
-        """
-        polls the completion of a given jobid
-        @param jobid:
-        @param response:
-        @return:
-        """
-        cmd = queryAsyncJobResult.queryAsyncJobResultCmd()
-        cmd.jobid = jobid
-        timeout = self.asyncTimeout
-
-        while timeout > 0:
-            asyncResonse = self.marvinRequest(cmd, response_type=response)
+        return CSConnection(self.mgtDetails,
+                            self.asyncTimeout,
+                            self.logger,
+                            self.path)
 
-            if asyncResonse.jobstatus == 2:
-                raise cloudstackException.cloudstackAPIException(
-                    "asyncquery", asyncResonse.jobresult)
-            elif asyncResonse.jobstatus == 1:
-                return asyncResonse
-
-            time.sleep(5)
-            if self.logger is not None:
-                self.logger.debug("job: %s still processing,"
-                                  "will timeout in %ds" % (jobid, timeout))
-            timeout = timeout - 5
+    def __poll(self, jobid, response_cmd):
+        '''
+        @Name : __poll
+        @Desc: polls for the completion of a given jobid
+        @Input 1. jobid: Monitor the Jobid for CS
+               2. response_cmd:response command for request cmd
+        @return: FAILED if jobid is cancelled,failed
+                 Else return async_response
+        '''
+        try:
+            cmd = queryAsyncJobResult.queryAsyncJobResultCmd()
+            cmd.jobid = jobid
+            timeout = self.asyncTimeout
+            start_time = time.time()
+            end_time = time.time()
+            async_response = FAILED
+            self.logger.debug("=== Jobid: %s Started ===" % (str(jobid)))
+            while timeout > 0:
+                async_response = self.\
+                    marvinRequest(cmd, response_type=response_cmd)
+                if async_response != FAILED:
+                    job_status = async_response.jobstatus
+                    if job_status in [JOB_FAILED,
+                                      JOB_CANCELLED,
+                                      JOB_SUCCEEDED]:
+                        break
+                time.sleep(5)
+                timeout -= 5
+                self.logger.debug("=== JobId:%s is Still Processing, "
+                                  "Will TimeOut in:%s ====" % (str(jobid),
+                                                               str(timeout)))
+            end_time = time.time()
+            tot_time = int(start_time - end_time)
+            self.logger.debug(
+                "===Jobid:%s ; StartTime:%s ; EndTime:%s ; "
+                "TotalTime:%s===" %
+                (str(jobid), str(time.ctime(start_time)),
+                 str(time.ctime(end_time)), str(tot_time)))
+            return async_response
+        except Exception as e:
+            self.__lastError = e
+            self.logger.exception("==== __poll: Exception Occurred :%s ====" %
+                                  str(self.__lastError))
+            return FAILED
 
-        raise cloudstackException.cloudstackAPIException(
-            "asyncquery", "Async job timeout %s" % jobid)
+    def getLastError(self):
+        '''
+        @Name : getLastError
+        @Desc : Returns the last error from marvinRequest
+        '''
+        return self.__lastError
 
-    def sign(self, payload):
+    def __sign(self, payload):
         """
-        signs a given request URL when the apiKey and secretKey are known
-
-        @param payload: dict of GET params to be signed
-        @return: the signature of the payload
+        @Name : __sign
+        @Desc:signs a given request URL when the apiKey and
+              secretKey are known
+        @Input: payload: dictionary of params be signed
+        @Output: the signature of the payload
         """
         params = zip(payload.keys(), payload.values())
         params.sort(key=lambda k: str.lower(k[0]))
-        hashStr = "&".join(
+        hash_str = "&".join(
             ["=".join(
                 [str.lower(r[0]),
                  str.lower(
@@ -114,168 +153,219 @@ class cloudConnection(object):
             ) for r in params]
         )
         signature = base64.encodestring(hmac.new(
-            self.securityKey, hashStr, hashlib.sha1).digest()).strip()
-        self.logger.debug("Computed Signature by Marvin: %s" % signature)
+            self.securityKey, hash_str, hashlib.sha1).digest()).strip()
         return signature
 
-    def request(self, command, auth=True, payload={}, method='GET'):
+    def __sendPostReqToCS(self, url, payload):
+        '''
+        @Name : __sendPostReqToCS
+        @Desc : Sends the POST Request to CS
+        @Input : url: URL to send post req
+                 payload:Payload information as part of request
+        @Output: Returns response from POST output
+                 else FAILED
+        '''
+        try:
+            response = requests.post(url,
+                                     params=payload,
+                                     cert=self.certPath,
+                                     verify=self.httpsFlag)
+            return response
+        except Exception as e:
+            self.__lastError = e
+            self.logger.\
+                exception("__sendPostReqToCS : Exception "
+                          "Occurred: %s" % str(self.__lastError))
+            return FAILED
+
+    def __sendGetReqToCS(self, url, payload):
+        '''
+        @Name : __sendGetReqToCS
+        @Desc : Sends the GET Request to CS
+        @Input : url: URL to send post req
+                 payload:Payload information as part of request
+        @Output: Returns response from GET output
+                 else FAILED
+        '''
+        try:
+            response = requests.get(url,
+                                    params=payload,
+                                    cert=self.certPath,
+                                    verify=self.httpsFlag)
+            return response
+        except Exception as e:
+            self.__lastError = e
+            self.logger.exception("__sendGetReqToCS : Exception Occurred: %s" %
+                                  str(self.__lastError))
+            return FAILED
+
+    def __sendCmdToCS(self, command, auth=True, payload={}, method='GET'):
         """
-        Makes requests using auth or over integration port
-        @param command: cloudstack API command name
+        @Name : __sendCmdToCS
+        @Desc : Makes requests to CS using the Inputs provided
+        @Input: command: cloudstack API command name
                     eg: deployVirtualMachineCommand
-        @param auth: Authentication (apikey,secretKey) => True
+                auth: Authentication (apikey,secretKey) => True
                      else False for integration.api.port
-        @param payload: request data composed as a dictionary
-        @param method: GET/POST via HTTP
-        @return:
+                payload: request data composed as a dictionary
+                method: GET/POST via HTTP
+        @output: FAILED or else response from CS
         """
-        payload["command"] = command
-        payload["response"] = "json"
-
-        if auth:
-            payload["apiKey"] = self.apiKey
-            signature = self.sign(payload)
-            payload["signature"] = signature
-
         try:
-            #https_flag : Signifies whether to verify connection over \
-            #http or https, \
-            #initialized to False, will be set to true if user provided https
-            #connection
-            https_flag = False
-            cert_path = ()
-            if self.protocol == "https":
-                https_flag = True
-                if self.certCAPath != "NA" and self.certPath != "NA":
-                    cert_path = (self.certCAPath, self.certPath)
+            payload["command"] = command
+            payload["response"] = "json"
 
-            #Verify whether protocol is "http", then call the request over http
-            if self.protocol == "http":
+            if auth:
+                payload["apiKey"] = self.apiKey
+                payload["signature"] = self.__sign(payload)
+
+            # Verify whether protocol is "http" or "https", then send the
+            # request
+            if self.protocol in ["http", "https"]:
+                self.logger.debug("Payload: %s" % str(payload))
                 if method == 'POST':
-                    response = requests.post(self.baseurl, params=payload,
-                                             verify=https_flag)
-                else:
-                    response = requests.get(self.baseurl, params=payload,
-                                            verify=https_flag)
+                    self.logger.debug("=======Sending POST Cmd : %s======="
+                                      % str(command))
+                    return self.__sendPostReqToCS(self.baseUrl, payload)
+                if method == "GET":
+                    self.logger.debug("========Sending GET Cmd : %s======="
+                                      % str(command))
+                    return self.__sendGetReqToCS(self.baseUrl, payload)
             else:
-                '''
-                If protocol is https, then create the  connection url with \
-                user provided certificates \
-                provided as part of cert
-                '''
-                try:
-                    if method == 'POST':
-                        response = requests.post(self.baseurl,
-                                                 params=payload,
-                                                 cert=cert_path,
-                                                 verify=https_flag)
-                    else:
-                        response = requests.get(self.baseurl, params=payload,
-                                                cert=cert_path,
-                                                verify=https_flag)
-                except Exception, e:
-                    '''
-                    If an exception occurs with user provided CA certs, \
-                    then try with default certs, \
-                    we dont need to mention here the cert path
-                    '''
-                    self.logger.debug("Creating CS connection over https \
-                                        didnt worked with user provided certs \
-                                            , so trying with no certs %s" % e)
-                    if method == 'POST':
-                        response = requests.post(self.baseurl,
-                                                 params=payload,
-                                                 verify=https_flag)
-                    else:
-                        response = requests.get(self.baseurl,
-                                                params=payload,
-                                                verify=https_flag)
-        except ConnectionError, c:
-            self.logger.debug("Connection refused. Reason: %s : %s" %
-                              (self.baseurl, c))
-            raise c
-        except HTTPError, h:
-            self.logger.debug("Http Error.Server returned error code: %s" % h)
-            raise h
-        except Timeout, t:
-            self.logger.debug("Connection timed out with %s" % t)
-            raise t
-        except RequestException, r:
-            self.logger.debug("RequestException from server %s" % r)
-            raise r
-        except Exception, e:
-            self.logger.debug("Error returned by server %s" % r)
-            raise e
-        else:
-            return response
+                self.logger.exception("__sendCmdToCS: Invalid Protocol")
+                return FAILED
+        except Exception as e:
+            self.logger.exception("__sendCmdToCS: Exception:%s" %
+                                  GetDetailExceptionInfo(e))
+            return FAILED
 
-    def sanitizeCommand(self, cmd):
+    def __sanitizeCmd(self, cmd):
         """
-        Removes None values, Validates all required params are present
-        @param cmd: Cmd object eg: createPhysicalNetwork
-        @return:
+        @Name : __sanitizeCmd
+        @Desc : Removes None values, Validates all required params are present
+        @Input: cmd: Cmd object eg: createPhysicalNetwork
+        @Output: Returns command name, asynchronous or not,request payload
+                 FAILED for failed cases
         """
-        requests = {}
-        required = []
-        for attribute in dir(cmd):
-            if not attribute.startswith('__'):
-                if attribute == "isAsync":
-                    isAsync = getattr(cmd, attribute)
-                elif attribute == "required":
-                    required = getattr(cmd, attribute)
-                else:
-                    requests[attribute] = getattr(cmd, attribute)
-
-        cmdname = cmd.__class__.__name__.replace("Cmd", "")
-        for requiredPara in required:
-            if requests[requiredPara] is None:
-                raise cloudstackException.cloudstackAPIException(
-                    cmdname, "%s is required" % requiredPara)
-        for param, value in requests.items():
-            if value is None:
-                requests.pop(param)
-            elif isinstance(value, list):
-                if len(value) == 0:
-                    requests.pop(param)
-                else:
-                    if not isinstance(value[0], dict):
-                        requests[param] = ",".join(value)
+        try:
+            cmd_name = ''
+            payload = {}
+            required = []
+            isAsync = "false"
+            for attribute in dir(cmd):
+                if not attribute.startswith('__'):
+                    if attribute == "isAsync":
+                        isAsync = getattr(cmd, attribute)
+                    elif attribute == "required":
+                        required = getattr(cmd, attribute)
                     else:
-                        requests.pop(param)
-                        i = 0
-                        for val in value:
-                            for k, v in val.iteritems():
-                                requests["%s[%d].%s" % (param, i, k)] = v
-                            i = i + 1
-        return cmdname.strip(), isAsync, requests
+                        payload[attribute] = getattr(cmd, attribute)
+            cmd_name = cmd.__class__.__name__.replace("Cmd", "")
+            for required_param in required:
+                if payload[required_param] is None:
+                    self.logger.debug("CmdName: %s Parameter : %s is Required"
+                                      % (cmd_name, required_param))
+                    return FAILED
+            for param, value in payload.items():
+                if value is None:
+                    payload.pop(param)
+                elif isinstance(value, list):
+                    if len(value) == 0:
+                        payload.pop(param)
+                    else:
+                        if not isinstance(value[0], dict):
+                            payload[param] = ",".join(value)
+                        else:
+                            payload.pop(param)
+                            i = 0
+                            for val in value:
+                                for k, v in val.iteritems():
+                                    payload["%s[%d].%s" % (param, i, k)] = v
+                                i += 1
+            return cmd_name.strip(), isAsync, payload
+        except Exception as e:
+            self.logger.\
+                exception("__sanitizeCmd: CmdName : "
+                          "%s : Exception:%s" % (cmd_name,
+                                                 GetDetailExceptionInfo(e)))
+            return FAILED
+
+    def __parseAndGetResponse(self, cmd_response, response_cls, is_async):
+        '''
+        @Name : __parseAndGetResponse
+        @Desc : Verifies the  Response(from CS) and returns an
+                appropriate json parsed Response
+        @Input: cmd_response: Command Response from cs
+                response_cls : Mapping class for this Response
+                is_async: Whether the cmd is async or not.
+        @Output:Response output from CS
+        '''
+        try:
+            ret = jsonHelper.getResultObj(cmd_response.json(), response_cls)
+        except TypeError:
+            ret = jsonHelper.getResultObj(cmd_response.json, response_cls)
+
+        '''
+        If the response is asynchronous, poll and return response
+        else return response as it is
+        '''
+        if is_async == "false":
+            self.logger.debug("Response : %s" % str(ret))
+            return ret
+        else:
+            response = self.__poll(ret.jobid, response_cls)
+            self.logger.debug("Response : %s" % str(response))
+            return response.jobresult if response != FAILED else FAILED
 
     def marvinRequest(self, cmd, response_type=None, method='GET', data=''):
         """
-        Requester for marvin command objects
-        @param cmd: marvin's command from cloudstackAPI
-        @param response_type: response type of the command in cmd
-        @param method: HTTP GET/POST, defaults to GET
-        @return:
+        @Name : marvinRequest
+        @Desc: Handles Marvin Requests
+        @Input  cmd: marvin's command from cloudstackAPI
+                response_type: response type of the command in cmd
+                method: HTTP GET/POST, defaults to GET
+        @Output: Response received from CS
+                 FAILED In case of Error\Exception
         """
-        cmdname, isAsync, payload = self.sanitizeCommand(cmd)
-        self.logger.debug("sending %s request: %s %s" % (method, cmdname,
-                                                         str(payload)))
-        response = self.request(cmdname,
-                                self.auth,
-                                payload=payload,
-                                method=method)
-        if response is None:
-            return None
-        self.logger.debug("Request: %s Response: %s" % (response.url,
-                                                        response.text))
         try:
-            response = jsonHelper.getResultObj(response.json(), response_type)
-        except TypeError:
-            response = jsonHelper.getResultObj(response.json, response_type)
+            '''
+            1. Verify the Inputs Provided
+            '''
+            if (cmd is None or cmd == '')or \
+                    (response_type is None or response_type == ''):
+                self.logger.exception("marvinRequest : Invalid Command Input")
+                return FAILED
 
-        if isAsync == "false":
-            return response
-        else:
-            asyncJobId = response.jobid
-            response = self.poll(asyncJobId, response_type)
-            return response.jobresult
+            '''
+            2. Sanitize the Command
+            '''
+            sanitize_cmd_out = self.__sanitizeCmd(cmd)
+
+            if sanitize_cmd_out == FAILED:
+                return FAILED
+
+            cmd_name, is_async, payload = sanitize_cmd_out
+            '''
+            3. Send Command to CS
+            '''
+            cmd_response = self.__sendCmdToCS(cmd_name,
+                                              self.auth,
+                                              payload=payload,
+                                              method=method)
+            if cmd_response == FAILED:
+                raise self.__lastError
+
+            '''
+            4. Check if the Command Response received above is valid or Not.
+               If not return Invalid Response
+            '''
+            ret = self.__parseAndGetResponse(cmd_response,
+                                             response_type,
+                                             is_async)
+            if ret == FAILED:
+                raise self.__lastError
+            return ret
+        except Exception as e:
+            self.logger.exception("marvinRequest : CmdName: %s Exception: %s" %
+                                  (str(cmd), GetDetailExceptionInfo(e)))
+            return FAILED

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/cloudstackException.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/cloudstackException.py b/tools/marvin/marvin/cloudstackException.py
index 6200003..c2eb57a 100644
--- a/tools/marvin/marvin/cloudstackException.py
+++ b/tools/marvin/marvin/cloudstackException.py
@@ -15,8 +15,13 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import sys
+import traceback
+from marvin.codes import (INVALID_INPUT, EXCEPTION_OCCURRED)
+
+
+class CloudstackAPIException(Exception):
 
-class cloudstackAPIException(Exception):
     def __init__(self, cmd="", result=""):
         self.errorMsg = "Execute cmd: %s failed, due to: %s" % (cmd, result)
 
@@ -25,6 +30,7 @@ class cloudstackAPIException(Exception):
 
 
 class InvalidParameterException(Exception):
+
     def __init__(self, msg=''):
         self.errorMsg = msg
 
@@ -33,6 +39,7 @@ class InvalidParameterException(Exception):
 
 
 class dbException(Exception):
+
     def __init__(self, msg=''):
         self.errorMsg = msg
 
@@ -41,8 +48,18 @@ class dbException(Exception):
 
 
 class internalError(Exception):
+
     def __init__(self, msg=''):
         self.errorMsg = msg
 
     def __str__(self):
         return self.errorMsg
+
+
+def GetDetailExceptionInfo(e):
+    if e is not None:
+        exc_type, exc_value, exc_traceback = sys.exc_info()
+        return str(repr(traceback.format_exception(
+            exc_type, exc_value, exc_traceback)))
+    else:
+        return EXCEPTION_OCCURRED

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/cloudstackTestCase.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/cloudstackTestCase.py b/tools/marvin/marvin/cloudstackTestCase.py
index 6456bb1..5cb4a10 100644
--- a/tools/marvin/marvin/cloudstackTestCase.py
+++ b/tools/marvin/marvin/cloudstackTestCase.py
@@ -16,7 +16,7 @@
 # under the License.
 
 import unittest
-from marvin.integration.lib.utils import verifyElementInList
+from marvin.lib.utils import verifyElementInList
 from marvin.codes import PASS
 
 
@@ -37,7 +37,7 @@ def user(Name, DomainName, AcctType):
 class cloudstackTestCase(unittest.case.TestCase):
     clstestclient = None
 
-    def assertElementInList(inp, toverify, responsevar=None,  pos=0,
+    def assertElementInList(inp, toverify, responsevar=None, pos=0,
                             assertmsg="TC Failed for reason"):
         '''
         @Name: assertElementInList
@@ -46,7 +46,7 @@ class cloudstackTestCase(unittest.case.TestCase):
         Takes one additional argument of what message to assert with
         when failed
         '''
-        out = verifyElementInList(inp, toverify, responsevar,  pos)
+        out = verifyElementInList(inp, toverify, responsevar, pos)
         unittest.TestCase.assertEquals(out[0], PASS, "msg:%s" % out[1])
 
     @classmethod

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/cloudstackTestClient.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/cloudstackTestClient.py b/tools/marvin/marvin/cloudstackTestClient.py
index 4ac510b..53d48ab 100644
--- a/tools/marvin/marvin/cloudstackTestClient.py
+++ b/tools/marvin/marvin/cloudstackTestClient.py
@@ -15,201 +15,433 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import cloudstackConnection
-import asyncJobMgr
-import dbConnection
-from cloudstackAPI import *
-import random
-import string
-import hashlib
-from configGenerator import ConfigManager
-from marvin.integration.lib.utils import random_gen
-
-'''
-@Desc  : CloudStackTestClient is encapsulated class for getting various \
-         clients viz., apiclient,dbconnection etc
-@Input : mgmtDetails : Management Server Details
-         dbSvrDetails: Database Server details of Management \
+from marvin.cloudstackConnection import CSConnection
+from marvin.asyncJobMgr import asyncJobMgr
+from marvin.dbConnection import DbConnection
+from marvin.cloudstackAPI import *
+from marvin.codes import (FAILED, PASS, ADMIN, DOMAIN_ADMIN,
+                          USER, SUCCESS, XEN_SERVER)
+from marvin.configGenerator import ConfigManager
+from marvin.cloudstackException import GetDetailExceptionInfo
+from marvin.lib.utils import (random_gen, validateList)
+from marvin.cloudstackAPI.cloudstackAPIClient import CloudStackAPIClient
+
+
+class CSTestClient(object):
+
+    '''
+    @Desc  : CloudStackTestClient is encapsulated entity for creating and
+         getting various clients viz., apiclient,
+         user api client, dbconnection, test Data parsed
+         information etc
+    @Input :
+         mgmt_details : Management Server Details
+         dbsvr_details: Database Server details of Management \
                        Server. Retrieved from configuration file.
-         asyncTimeout :
-         defaultWorkerThreads :
-         logger : provides logging facilities for this library 
-'''
-
-
-class cloudstackTestClient(object):
-    def __init__(self, mgmtDetails,
-                 dbSvrDetails, asyncTimeout=3600,
-                 defaultWorkerThreads=10,
-                 logger=None):
-        self.mgmtDetails = mgmtDetails
-        self.connection = \
-            cloudstackConnection.cloudConnection(self.mgmtDetails,
-                                                 asyncTimeout,
-                                                 logger)
-        self.apiClient =\
-            cloudstackAPIClient.CloudStackAPIClient(self.connection)
-        self.dbConnection = None
-        if dbSvrDetails is not None:
-                self.createDbConnection(dbSvrDetails.dbSvr, dbSvrDetails.port,
-                                        dbSvrDetails.user,
-                                        dbSvrDetails.passwd, dbSvrDetails.db)
-        '''
-        Provides the Configuration Object to users through getConfigParser
-        The purpose of this object is to parse the config
-        and provide dictionary of the config so users can
-        use that configuration.Users can later call getConfig
-        on this object and it will return the default parsed
-        config dictionary from default configuration file,
-        they can overwrite it with providing their own
-        configuration file as well.
-        '''
-        self.configObj = ConfigManager()
-        self.asyncJobMgr = None
-        self.id = None
-        self.defaultWorkerThreads = defaultWorkerThreads
+         async_timeout : Timeout for Async queries
+         default_worker_threads : Number of worker threads
+         logger : provides logging facilities for this library
+         zone : The zone on which test suites using this test client will run
+    '''
+
+    def __init__(self, mgmt_details,
+                 dbsvr_details,
+                 async_timeout=3600,
+                 logger=None,
+                 test_data_filepath=None,
+                 zone=None,
+                 hypervisor_type=None):
+        self.__mgmtDetails = mgmt_details
+        self.__dbSvrDetails = dbsvr_details
+        self.__csConnection = None
+        self.__dbConnection = None
+        self.__testClient = None
+        self.__asyncTimeOut = async_timeout
+        self.__logger = logger
+        self.__apiClient = None
+        self.__userApiClient = None
+        self.__asyncJobMgr = None
+        self.__id = None
+        self.__hypervisor = hypervisor_type
+        self.__testDataFilePath = test_data_filepath
+        self.__parsedTestDataConfig = None
+        self.__zone = zone
+        self.__setHypervisorInfo()
 
     @property
     def identifier(self):
-        return self.id
+        return self.__id
 
     @identifier.setter
     def identifier(self, id):
-        self.id = id
+        self.__id = id
+
+    def getParsedTestDataConfig(self):
+        '''
+        @Name : getParsedTestDataConfig
+        @Desc : Provides the TestData Config needed for
+                Tests are to Run
+        @Output : Returns the Parsed Test Data Dictionary
+        '''
+        return self.__parsedTestDataConfig
+
+    def getZoneForTests(self):
+        '''
+        @Name : getZoneForTests
+        @Desc : Provides the Zone against which Tests are to run
+                If zone name provided to marvin plugin is none
+                it will get it from Test Data Config File
+                Even, if  it is not available, return None
+        @Output : Returns the Zone Name
+        '''
+        return self.__zone
+
+    def getHypervisorInfo(self):
+        '''
+        @Name : getHypervisorInfo
+        @Desc : Provides the hypervisor Information to test users
+        @Output : Return Hypervisor Information
+        '''
+        return self.__hypervisor
+
+    def __setHypervisorInfo(self):
+        '''
+        @Name : __setHypervisorInfo
+        @Desc:  Set the HyperVisor details;
+                default to XenServer
+        '''
+        try:
+            if not self.__hypervisor:
+                self.__hypervisor = XEN_SERVER
+            return SUCCESS
+        except Exception as e:
+            print "\n Exception Occurred Under __setHypervisorInfo " \
+                  "%s" % GetDetailExceptionInfo(e)
+            return FAILED
+
+    def __createApiClient(self):
+        try:
+            '''
+            Step1 : Create a CS Connection Object
+            '''
+            self.__csConnection = CSConnection(self.__mgmtDetails,
+                                               self.__asyncTimeOut,
+                                               self.__logger)
 
-    def createDbConnection(self, host="localhost", port=3306, user='cloud',
-                           passwd='cloud', db='cloud'):
-        self.dbConnection = dbConnection.dbConnection(host, port, user,
-                                                      passwd, db)
+            '''
+            Step2 : Create API Client with earlier created connection object
+            '''
+            self.__apiClient = CloudStackAPIClient(self.__csConnection)
+
+            '''
+            Step3:  If API Key is not provided as part of Management Details,
+                    then verify and register
+            '''
+            if self.__mgmtDetails.apiKey is None:
+                list_user = listUsers.listUsersCmd()
+                list_user.account = "admin"
+                list_user_res = self.__apiClient.listUsers(list_user)
+                if list_user_res is None or\
+                        (validateList(list_user_res)[0] != PASS):
+                    self.__logger.error("__createApiClient: API "
+                                        "Client Creation Failed")
+                    return FAILED
+                user_id = list_user_res[0].id
+                api_key = list_user_res[0].apikey
+                security_key = list_user_res[0].secretkey
+                if api_key is None:
+                    ret = self.__getKeys(user_id)
+                    if ret != FAILED:
+                        self.__mgmtDetails.port = 8080
+                        self.__mgmtDetails.apiKey = ret[0]
+                        self.__mgmtDetails.securityKey = ret[1]
+                    else:
+                        self.__logger.error("__createApiClient: API Client "
+                                            "Creation Failed while "
+                                            "Registering User")
+                        return FAILED
+                else:
+                    self.__mgmtDetails.port = 8080
+                    self.__mgmtDetails.apiKey = api_key
+                    self.__mgmtDetails.securityKey = security_key
+                '''
+                Now Create the Connection objects and Api Client using
+                new details
+                '''
+                self.__csConnection = CSConnection(self.__mgmtDetails,
+                                                   self.__asyncTimeOut,
+                                                   self.__logger)
+                self.__apiClient = CloudStackAPIClient(self.__csConnection)
+            return SUCCESS
+        except Exception as e:
+            self.__logger.exception(" Exception Occurred Under "
+                                    "__createApiClient: %s" %
+                                    GetDetailExceptionInfo(e))
+            return FAILED
+
+    def __createDbConnection(self):
+        '''
+        @Name : ___createDbConnection
+        @Desc : Creates the CloudStack DB Connection
+        '''
+        host = "localhost" if self.__dbSvrDetails.dbSvr is None \
+            else self.__dbSvrDetails.dbSvr
+        port = 3306 if self.__dbSvrDetails.port is None \
+            else self.__dbSvrDetails.port
+        user = "cloud" if self.__dbSvrDetails.user is None \
+            else self.__dbSvrDetails.user
+        passwd = 'cloud' if self.__dbSvrDetails.passd is None \
+            else self.__dbSvrDetails.passd
+        db = 'cloud' if self.__dbSvrDetails.db is None \
+            else self.__dbSvrDetails.db
+        self.__dbConnection = DbConnection(host, port, user, passwd, db)
+
+    def __getKeys(self, userid):
+        '''
+        @Name : ___getKeys
+        @Desc : Retrieves the API and Secret Key for the provided Userid
+        @Input: userid: Userid to register
+        @Output: FAILED or tuple with apikey and secretkey
+        '''
+        try:
+            register_user = registerUserKeys.registerUserKeysCmd()
+            register_user.id = userid
+            register_user_res = \
+                self.__apiClient.registerUserKeys(register_user)
+            if not register_user_res:
+                return FAILED
+            return (register_user_res.apikey, register_user_res.secretkey)
+        except Exception as e:
+            self.__logger.exception("Exception Occurred Under __geKeys : "
+                                    "%s" % GetDetailExceptionInfo(e))
+            return FAILED
+
+    def createTestClient(self):
+        '''
+        @Name : createTestClient
+        @Desc : Creates the Test Client.
+                The test Client is used by test suites
+                Here we create ParsedTestData Config.
+                Creates a DB Connection.
+                Creates an API Client
+        @Output : FAILED In case of an issue\Failure
+                  SUCCESS in case of Success of this function
+        '''
+        try:
+            '''
+            1. Create Config Object
+               Provides the Configuration Object to test suites through
+               getConfigParser. The purpose of this config object is to
+               parse the default config and provide dictionary of the
+               config so users can use that configuration.
+               Users can later call getConfig on this object and it will
+               return the default parsed config dictionary from default
+               configuration file. They can overwrite it with
+               providing their own configuration file as well.
+            '''
+            '''
+            1. Check Config,Zone,Hypervisor Information
+            '''
+            self.__configObj = ConfigManager(self.__testDataFilePath)
+
+            if not self.__configObj or not self.__hypervisor:
+                self.__logger.error("createTestClient : "
+                                    "Either Hypervisor is None or "
+                                    "Not able to create "
+                                    "ConfigManager Object")
+                return FAILED
+
+            self.__parsedTestDataConfig = self.__configObj.getConfig()
+            self.__logger.debug("Parsing Test data successful")
+
+            '''
+            2. Create DB Connection
+            '''
+            self.__createDbConnection()
+            '''
+            3. Creates API Client
+            '''
+            ret = self.__createApiClient()
+            if ret == FAILED:
+                self.__logger.\
+                    error("==== Test Client Creation Failed ====")
+            else:
+                self.__logger.\
+                    debug("==== Test Client Creation Successful ====")
+            return ret
+        except Exception as e:
+            self.__logger.exception("Exception Occurred "
+                                    "Under createTestClient "
+                                    ": %s" % GetDetailExceptionInfo(e))
+            return FAILED
 
     def isAdminContext(self):
         """
-        A user is a regular user if he fails to listDomains;
+        @Name : isAdminContext
+        @Desc:A user is a regular user if he fails to listDomains;
         if he is a domain-admin, he can list only domains that are non-ROOT;
         if he is an admin, he can list the ROOT domain successfully
         """
         try:
             listdom = listDomains.listDomainsCmd()
             listdom.name = 'ROOT'
-            listdomres = self.apiClient.listDomains(listdom)
-            rootdom = listdomres[0].name
-            if rootdom == 'ROOT':
-                return 1  # admin
-            else:
-                return 2  # domain-admin
+            listdomres = self.__apiClient.listDomains(listdom)
+            if listdomres != FAILED:
+                rootdom = listdomres[0].name
+                if rootdom == 'ROOT':
+                    return ADMIN
+                else:
+                    return DOMAIN_ADMIN
+            return USER
         except:
-            return 0  # user
+            return USER
 
-    def createUserApiClient(self, UserName, DomainName, acctType=0):
-        if not self.isAdminContext():
-            return self.apiClient
-
-        listDomain = listDomains.listDomainsCmd()
-        listDomain.listall = True
-        listDomain.name = DomainName
-        try:
-            domains = self.apiClient.listDomains(listDomain)
-            domId = domains[0].id
-        except:
-            cdomain = createDomain.createDomainCmd()
-            cdomain.name = DomainName
-            domain = self.apiClient.createDomain(cdomain)
-            domId = domain.id
-
-        cmd = listAccounts.listAccountsCmd()
-        cmd.name = UserName
-        cmd.domainid = domId
+    def __createUserApiClient(self, UserName, DomainName, acctType=0):
+        '''
+        @Name : ___createUserApiClient
+        @Desc : Creates a User API Client with given
+                UserName\DomainName Parameters
+        @Input: UserName: Username to be created in cloudstack
+                DomainName: Domain under which the above account be created
+                accType: Type of Account EX: Root,Non Root etc
+        @Output: Return the API client for the user
+        '''
         try:
-            accounts = self.apiClient.listAccounts(cmd)
-            acctId = accounts[0].id
-        except:
-            createAcctCmd = createAccount.createAccountCmd()
-            createAcctCmd.accounttype = acctType
-            createAcctCmd.domainid = domId
-            createAcctCmd.email = "test-" + random_gen()\
-                + "@cloudstack.org"
-            createAcctCmd.firstname = UserName
-            createAcctCmd.lastname = UserName
-            createAcctCmd.password = 'password'
-            createAcctCmd.username = UserName
-            acct = self.apiClient.createAccount(createAcctCmd)
-            acctId = acct.id
-
-        listuser = listUsers.listUsersCmd()
-        listuser.username = UserName
-
-        listuserRes = self.apiClient.listUsers(listuser)
-        userId = listuserRes[0].id
-        apiKey = listuserRes[0].apikey
-        securityKey = listuserRes[0].secretkey
-
-        if apiKey is None:
-            registerUser = registerUserKeys.registerUserKeysCmd()
-            registerUser.id = userId
-            registerUserRes = self.apiClient.registerUserKeys(registerUser)
-            apiKey = registerUserRes.apikey
-            securityKey = registerUserRes.secretkey
-
-        mgtDetails = self.mgmtDetails
-        mgtDetails.apiKey = apiKey
-        mgtDetails.securityKey = securityKey
-
-        newUserConnection =\
-            cloudstackConnection.cloudConnection(mgtDetails,
-                                                 self.connection.asyncTimeout,
-                                                 self.connection.logger)
-        self.userApiClient =\
-            cloudstackAPIClient.CloudStackAPIClient(newUserConnection)
-        self.userApiClient.connection = newUserConnection
-        self.userApiClient.hypervisor = self.apiClient.hypervisor
-        return self.userApiClient
+            if not self.isAdminContext():
+                return self.__apiClient
+
+            listDomain = listDomains.listDomainsCmd()
+            listDomain.listall = True
+            listDomain.name = DomainName
+            try:
+                domains = self.__apiClient.listDomains(listDomain)
+                domId = domains[0].id
+            except:
+                cdomain = createDomain.createDomainCmd()
+                cdomain.name = DomainName
+                domain = self.__apiClient.createDomain(cdomain)
+                domId = domain.id
+
+            cmd = listAccounts.listAccountsCmd()
+            cmd.name = UserName
+            cmd.domainid = domId
+            try:
+                accounts = self.__apiClient.listAccounts(cmd)
+                acctId = accounts[0].id
+            except:
+                createAcctCmd = createAccount.createAccountCmd()
+                createAcctCmd.accounttype = acctType
+                createAcctCmd.domainid = domId
+                createAcctCmd.email = "test-" + random_gen()\
+                    + "@cloudstack.org"
+                createAcctCmd.firstname = UserName
+                createAcctCmd.lastname = UserName
+                createAcctCmd.password = 'password'
+                createAcctCmd.username = UserName
+                acct = self.__apiClient.createAccount(createAcctCmd)
+                acctId = acct.id
+
+            listuser = listUsers.listUsersCmd()
+            listuser.username = UserName
+
+            listuserRes = self.__apiClient.listUsers(listuser)
+            userId = listuserRes[0].id
+            apiKey = listuserRes[0].apikey
+            securityKey = listuserRes[0].secretkey
+
+            if apiKey is None:
+                ret = self.__getKeys(userId)
+                if ret != FAILED:
+                    mgtDetails = self.__mgmtDetails
+                    mgtDetails.apiKey = ret[0]
+                    mgtDetails.securityKey = ret[1]
+                else:
+                    self.__logger.error("__createUserApiClient: "
+                                        "User API Client Creation."
+                                        " While Registering User Failed")
+                    return FAILED
+            else:
+                mgtDetails = self.__mgmtDetails
+                mgtDetails.apiKey = apiKey
+                mgtDetails.securityKey = securityKey
+
+            newUserConnection =\
+                CSConnection(mgtDetails,
+                             self.__csConnection.asyncTimeout,
+                             self.__csConnection.logger)
+            self.__userApiClient = CloudStackAPIClient(newUserConnection)
+            self.__userApiClient.connection = newUserConnection
+            self.__userApiClient.hypervisor = self.__hypervisor
+            return self.__userApiClient
+        except Exception as e:
+            self.__logger.exception("Exception Occurred "
+                                    "Under getUserApiClient : %s" %
+                                    GetDetailExceptionInfo(e))
+            return FAILED
 
     def close(self):
-        if self.connection is not None:
-            self.connection.close()
+        if self.__csConnection is not None:
+            self.__csConnection.close()
 
     def getDbConnection(self):
-        return self.dbConnection
+        '''
+        @Name : getDbConnection
+        @Desc : Retrieves the DB Connection Handle
+        '''
+        return self.__dbConnection
 
     def getConfigParser(self):
-        return self.configObj
+        '''
+        @Name : getConfigParser
+        @Desc : Provides the ConfigManager Interface to TestClients
+        '''
+        return self.__configObj
 
     def getApiClient(self):
-        self.apiClient.id = self.identifier
-        return self.apiClient
+        if self.__apiClient:
+            self.__apiClient.id = self.identifier
+            return self.__apiClient
+        return None
 
-    def getUserApiClient(self, account, domain, type=0):
+    def getUserApiClient(self, UserName=None, DomainName=None, type=0):
         """
-        0 - user
-        1 - admin
-        2 - domain admin
+        @Name : getUserApiClient
+        @Desc : Provides the User API Client to test Users
+        0 - user ; 1 - admin;2 - domain admin
+        @OutPut : FAILED In case of an issue
+                  else User API Client
         """
-        self.createUserApiClient(account, domain, type)
-        if hasattr(self, "userApiClient"):
-            return self.userApiClient
-        return None
+        if UserName is None or DomainName is None:
+            return FAILED
+        return self.__createUserApiClient(UserName, DomainName, type)
 
     def submitCmdsAndWait(self, cmds, workers=1):
-        '''FixME, httplib has issue if more than one thread submitted'''
-        if self.asyncJobMgr is None:
-            self.asyncJobMgr = asyncJobMgr.asyncJobMgr(self.apiClient,
-                                                       self.dbConnection)
-        return self.asyncJobMgr.submitCmdsAndWait(cmds, workers)
+        '''
+        @Desc : FixME, httplib has issue if more than one thread submitted
+        '''
+        if self.__asyncJobMgr is None:
+            self.__asyncJobMgr = asyncJobMgr(self.__apiClient,
+                                             self.__dbConnection)
+        return self.__asyncJobMgr.submitCmdsAndWait(cmds, workers)
 
     def submitJob(self, job, ntimes=1, nums_threads=10, interval=1):
         '''
-        submit one job and execute the same job ntimes, with nums_threads
-        of threads
+        @Desc : submit one job and execute the same job
+                ntimes, with nums_threads of threads
         '''
-        if self.asyncJobMgr is None:
-            self.asyncJobMgr = asyncJobMgr.asyncJobMgr(self.apiClient,
-                                                       self.dbConnection)
-        self.asyncJobMgr.submitJobExecuteNtimes(job, ntimes, nums_threads,
-                                                interval)
+        if self.__asyncJobMgr is None:
+            self.__asyncJobMgr = asyncJobMgr(self.__apiClient,
+                                             self.__dbConnection)
+        self.__asyncJobMgr.submitJobExecuteNtimes(job, ntimes,
+                                                  nums_threads,
+                                                  interval)
 
     def submitJobs(self, jobs, nums_threads=10, interval=1):
-        '''submit n jobs, execute them with nums_threads of threads'''
-        if self.asyncJobMgr is None:
-            self.asyncJobMgr = asyncJobMgr.asyncJobMgr(self.apiClient,
-                                                       self.dbConnection)
-        self.asyncJobMgr.submitJobs(jobs, nums_threads, interval)
+        '''
+        @Desc :submit n jobs, execute them with nums_threads
+               of threads
+        '''
+        if self.__asyncJobMgr is None:
+            self.__asyncJobMgr = asyncJobMgr(self.__apiClient,
+                                             self.__dbConnection)
+        self.__asyncJobMgr.submitJobs(jobs, nums_threads, interval)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/codegenerator.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/codegenerator.py b/tools/marvin/marvin/codegenerator.py
index e0f056f..1e02ddf 100644
--- a/tools/marvin/marvin/codegenerator.py
+++ b/tools/marvin/marvin/codegenerator.py
@@ -25,6 +25,7 @@ import urllib2
 
 
 class cmdParameterProperty(object):
+
     def __init__(self):
         self.name = None
         self.required = False
@@ -34,6 +35,7 @@ class cmdParameterProperty(object):
 
 
 class cloudStackCmd(object):
+
     def __init__(self):
         self.name = ""
         self.desc = ""
@@ -42,7 +44,8 @@ class cloudStackCmd(object):
         self.response = []
 
 
-class codeGenerator(object):
+class CodeGenerator(object):
+
     """
     Apache CloudStack- marvin python classes can be generated from the json
     returned by API discovery or from the xml spec of commands generated by
@@ -208,12 +211,12 @@ class codeGenerator(object):
 
         body += self.space + '@property' + self.newline
         body += self.space + 'def id(self):' + self.newline
-        body += self.space*2 + 'return self._id' + self.newline
+        body += self.space * 2 + 'return self._id' + self.newline
         body += self.newline
 
         body += self.space + '@id.setter' + self.newline
         body += self.space + 'def id(self, identifier):' + self.newline
-        body += self.space*2 + 'self._id = identifier' + self.newline
+        body += self.space * 2 + 'self._id = identifier' + self.newline
         body += self.newline
 
         for cmdName in self.cmdsName:
@@ -340,7 +343,7 @@ class codeGenerator(object):
             paramProperty.desc = response['description']
         if 'type' in response:
             if response['type'] in ['list', 'map', 'set']:
-            #Here list becomes a subproperty
+            # Here list becomes a subproperty
                 if 'response' in response:
                     for innerResponse in response['response']:
                         subProperty =\
@@ -394,7 +397,7 @@ class codeGenerator(object):
                 csCmd.request.append(paramProperty)
 
             for response in cmd['response']:
-            #FIXME: ExtractImage related APIs return empty dicts in response
+            # FIXME: ExtractImage related APIs return empty dicts in response
                 if len(response) > 0:
                     paramProperty = self.constructResponseFromJSON(response)
                     csCmd.response.append(paramProperty)
@@ -454,7 +457,7 @@ if __name__ == "__main__":
             print parser.print_help()
             exit(1)
 
-    cg = codeGenerator(folder)
+    cg = CodeGenerator(folder)
     if options.spec is not None:
         cg.generateCodeFromXML(apiSpecFile)
     elif options.endpoint is not None:

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/codes.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/codes.py b/tools/marvin/marvin/codes.py
index 92d6cf9..28e907c 100644
--- a/tools/marvin/marvin/codes.py
+++ b/tools/marvin/marvin/codes.py
@@ -47,13 +47,32 @@ YES = "yes"
 FAILED = "FAILED"
 UNKNOWN_ERROR = "Unknown Error"
 EXCEPTION = "EXCEPTION"
+INVALID_RESPONSE = "Invalid Response"
+'''
+Async Job Related Codes
+'''
+JOB_INPROGRESS = 0
+JOB_SUCCEEDED = 1
+JOB_FAILED = 2
+JOB_CANCELLED = 3
+'''
+User Related Codes
+'''
 BASIC_ZONE = "basic"
 ISOLATED_NETWORK = "ISOLATED"
 SHARED_NETWORK = "SHARED"
 VPC_NETWORK = "VPC"
-ERROR_NO_HOST_FOR_MIGRATION = "Could not find suitable host for migration, please ensure setup has required no. of hosts"
+ERROR_NO_HOST_FOR_MIGRATION = \
+    "Could not find suitable host for migration, " \
+    "please ensure setup has required no. of hosts"
 NAT_RULE = "nat rule"
 STATIC_NAT_RULE = "static nat rule"
 UNKNOWN = "UNKNOWN"
 FAULT = "FAULT"
 MASTER = "MASTER"
+ADMIN = 1
+DOMAIN_ADMIN = 2
+USER = 0
+XEN_SERVER = "XenServer"
+ADMIN_ACCOUNT='ADMIN_ACCOUNT'
+USER_ACCOUNT='USER_ACCOUNT'

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/config/__init__.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/config/__init__.py b/tools/marvin/marvin/config/__init__.py
new file mode 100644
index 0000000..13a8339
--- /dev/null
+++ b/tools/marvin/marvin/config/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/config/config.cfg
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/config/config.cfg b/tools/marvin/marvin/config/config.cfg
index a278068..a4bbe9d 100644
--- a/tools/marvin/marvin/config/config.cfg
+++ b/tools/marvin/marvin/config/config.cfg
@@ -24,7 +24,7 @@
                     }
                   ,
         "project":
-        {
+                  {
                     "name": "Project",
                     "displaytext": "Test project"
                    },
@@ -35,6 +35,15 @@
                     "username": "test-account",
                     "password": "password"
                     },
+        "user": {
+                    "email": "user@test.com",
+                    "firstname": "User",
+                    "lastname": "User",
+                    "username": "User",
+                    # Random characters are appended for unique
+                    # username
+                    "password": "fr3sca",
+                },
         "service_offering": {
                              "name": "Tiny Instance",
                              "displaytext": "Tiny Instance",
@@ -74,6 +83,18 @@
                             "publicport": 22,
                             "protocol": "TCP"
                             },
+        "template": {
+                        "displaytext": "Public Template",
+                        "name": "Public template",
+                        "ostype": 'CentOS 5.3 (64-bit)',
+                        "url": "",
+                        "hypervisor": '',
+                        "format": '',
+                        "isfeatured": True,
+                        "ispublic": True,
+                        "isextractable": True,
+                        "templatefilter": "self"
+                    },
         "shared_network": {
                            "name": "Test Shared Network",
                            "displaytext": "Test Shared Network",
@@ -126,17 +147,28 @@
                                     "name": "SharedNwOffering",
                                     "displaytext": "SharedNwOffering",
                                     "guestiptype": "Shared",
-                                    "supportedservices": "Dhcp,Dns,UserData",
+                                    "supportedservices": "Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat",
                                     "specifyVlan" : "False",
                                     "specifyIpRanges" : "False",
                                     "traffictype": "GUEST",
                                     "serviceProviderList" : {
                                             "Dhcp": "VirtualRouter",
                                             "Dns": "VirtualRouter",
-                                            "UserData": "VirtualRouter"
+                                            "SourceNat": "VirtualRouter",
+                                            "PortForwarding": "VirtualRouter",
+                                            "Vpn": "VirtualRouter",
+                                            "Firewall": "VirtualRouter",
+                                            "Lb": "VirtualRouter",
+                                            "UserData": "VirtualRouter",
+                                            "StaticNat": "VirtualRouter"
                                         }
                                 },
         "security_group" : { "name": "custom_Sec_Grp" },
+         "natrule": {
+                        "publicport": 22,
+                        "privateport": 22,
+                        "protocol": 'TCP',
+                    },
         "ingress_rule": {
                          "protocol": "TCP",
                          "startport": "22",

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/798a6aa2/tools/marvin/marvin/config/test_data.cfg
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/config/test_data.cfg b/tools/marvin/marvin/config/test_data.cfg
new file mode 100644
index 0000000..5a3d8aa
--- /dev/null
+++ b/tools/marvin/marvin/config/test_data.cfg
@@ -0,0 +1,427 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+{
+        "region": {
+                "regionid": "2",
+                "regionname": "Region2",
+                "regionendpoint": "http://region2:8080/client"
+        },
+        "zone": "NA",
+        
+        "domain": { "name": "domain" },
+        
+        "project": {
+            "name": "Project",
+            "displaytext": "Test project"
+        },
+        "account": {
+            "email": "test-account@test.com",
+            "firstname": "test",
+            "lastname": "test",
+            "username": "test-account",
+            "password": "password"
+        }, 
+        "small": {
+            "displayname": "testserver",
+            "username": "root", 
+            "password": "password",
+            "ssh_port": 22,
+            "hypervisor": "XenServer",
+            "privateport": 22,
+            "publicport": 22,
+            "protocol": 'TCP',
+        },
+        "medium": {
+            "displayname": "testserver",
+            "username": "root",
+            "password": "password",
+            "ssh_port": 22,
+            "hypervisor": 'XenServer',
+            "privateport": 22,
+            "publicport": 22,
+            "protocol": 'TCP',
+        },
+        "service_offerings": {
+            "name": "Tiny Instance",
+            "displaytext": "Tiny Instance",
+            "cpunumber": 1,
+            "cpuspeed": 100,
+            "memory": 128,
+            
+            "tiny": {
+                "name": "Tiny Instance",
+                "displaytext": "Tiny Instance",
+                "cpunumber": 1,
+                "cpuspeed": 100, 
+                "memory": 128, 
+            },   
+            "small": {
+                "name": "Small Instance",
+                "displaytext": "Small Instance",
+                "cpunumber": 1,
+                "cpuspeed": 100,
+                "memory": 256 
+            },
+            "medium": {
+                "name": "Medium Instance",
+                "displaytext": "Medium Instance",
+                "cpunumber": 1,
+                "cpuspeed": 100,
+                "memory": 256,
+            },
+            "big": {
+                "name": "BigInstance",
+                "displaytext": "BigInstance",
+                "cpunumber": 1,
+                "cpuspeed": 100,
+                "memory": 512,
+            }
+        },
+        "disk_offering": {
+            "name": "Disk offering",
+            "displaytext": "Disk offering",
+            "disksize": 1  
+        },
+        'resized_disk_offering': {
+            "displaytext": "Resized",
+            "name": "Resized",
+            "disksize": 3
+        },
+        "network": {
+            "name": "Test Network",
+            "displaytext": "Test Network",
+            "acltype": "Account",
+        },
+        "network2": {
+            "name": "Test Network Shared",
+            "displaytext": "Test Network Shared",
+            "vlan" :1201,
+            "gateway" :"172.16.15.1",
+            "netmask" :"255.255.255.0",
+            "startip" :"172.16.15.21",
+            "endip" :"172.16.15.41",
+            "acltype": "Account",
+        },
+        "network_offering": {
+            "name": 'Test Network offering',
+            "displaytext": 'Test Network offering',
+            "guestiptype": 'Isolated',
+            "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding',
+            "traffictype": 'GUEST',
+            "availability": 'Optional',
+            "serviceProviderList" : {
+                "Dhcp": 'VirtualRouter',
+                "Dns": 'VirtualRouter',
+                "SourceNat": 'VirtualRouter',
+                "PortForwarding": 'VirtualRouter',
+            },
+        },
+        "isolated_network_offering": {
+            "name": "Network offering-DA services",
+            "displaytext": "Network offering-DA services",
+            "guestiptype": "Isolated",
+            "supportedservices": "Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat",
+            "traffictype": "GUEST",
+            "availability": "Optional'",
+            "serviceProviderList": {
+                "Dhcp": "VirtualRouter",
+                "Dns": "VirtualRouter",
+                "SourceNat": "VirtualRouter",
+                "PortForwarding": "VirtualRouter",
+                "Vpn": "VirtualRouter",
+                "Firewall": "VirtualRouter",
+                "Lb": "VirtualRouter",
+                "UserData": "VirtualRouter",
+                "StaticNat": "VirtualRouter"
+            }
+        },
+        "isolated_network": {
+            "name": "Isolated Network",
+            "displaytext": "Isolated Network"
+        },
+        "virtual_machine": {
+            "displayname": "Test VM",
+            "username": "root",
+            "password": "password",
+            "ssh_port": 22,
+            "privateport": 22,
+            "publicport": 22,
+            "protocol": "TCP",
+            "affinity": {
+                "name": "webvms",
+                "type": "host anti-affinity",
+            },
+        },
+        "server_without_disk": {
+            "displayname": "Test VM-No Disk",
+            "username": "root",
+            "password": "password",
+            "ssh_port": 22,
+            "hypervisor": 'XenServer',
+            "privateport": 22,
+            "publicport": 22,
+            "protocol": 'TCP',
+        },
+        "shared_network": {
+            "name": "MySharedNetwork - Test",
+            "displaytext": "MySharedNetwork",
+            "vlan" : "",
+            "gateway" :"",
+            "netmask" :"",
+            "startip" :"",
+            "endip" :"",
+            "acltype" : "Domain",
+            "scope":"all"
+        },
+        "shared_network_offering_sg": {
+            "name": "MySharedOffering-sg",
+            "displaytext": "MySharedOffering-sg",
+            "guestiptype": "Shared",
+            "supportedservices": "Dhcp,Dns,UserData,SecurityGroup",
+            "specifyVlan" : "False",
+            "specifyIpRanges" : "False",
+            "traffictype": "GUEST",
+            "serviceProviderList" : {
+                "Dhcp": "VirtualRouter",
+                "Dns": "VirtualRouter",
+                "UserData": "VirtualRouter",
+                "SecurityGroup": "SecurityGroupProvider"
+            }
+        },
+        "shared_network_sg": {
+            "name": "Shared-Network-SG-Test",
+            "displaytext": "Shared-Network_SG-Test",
+            "networkofferingid":"1",
+            "vlan" : "",
+            "gateway" :"",
+            "netmask" :"255.255.255.0",
+            "startip" :"",
+            "endip" :"",
+            "acltype" : "Domain",
+            "scope":"all"
+        },
+        "vpc_offering": {
+            "name": "VPC off",
+            "displaytext": "VPC off",
+            "supportedservices": "Dhcp,Dns,SourceNat,PortForwarding,Vpn,Lb,UserData,StaticNat,NetworkACL"
+        },
+        "vpc": {
+            "name": "TestVPC",
+            "displaytext": "TestVPC",
+            "cidr": "10.0.0.1/24"
+        },
+        "clusters": {
+            0: {
+                "clustername": "Xen Cluster",
+                "clustertype": "CloudManaged",
+                "hypervisor": "XenServer",
+            },
+            1: {
+                "clustername": "KVM Cluster",
+                "clustertype": "CloudManaged",
+                "hypervisor": "KVM",
+            },
+            2: {
+                "hypervisor": 'VMware',
+                "clustertype": 'ExternalManaged',
+                "username": 'administrator',
+                "password": 'fr3sca',
+                "url": 'http://192.168.100.17/CloudStack-Clogeny-Pune/Pune-1',
+                "clustername": 'VMWare Cluster',
+            },
+        },
+        "hosts": {
+            "xenserver": {
+                "hypervisor": 'XenServer',
+                "clustertype": 'CloudManaged',
+                "url": 'http://192.168.100.211',
+                "username": "root",
+                "password": "fr3sca",
+            },
+            "kvm": {
+                "hypervisor": 'KVM',
+                "clustertype": 'CloudManaged',
+                "url": 'http://192.168.100.212',
+                "username": "root",
+                "password": "fr3sca",
+            },
+            "vmware": {
+                "hypervisor": 'VMware',
+                "clustertype": 'ExternalManaged',
+                "url": 'http://192.168.100.203',
+                "username": "administrator",
+                "password": "fr3sca",
+            },
+        },
+        "network_offering_shared": {
+            "name": 'Test Network offering shared',
+            "displaytext": 'Test Network offering Shared',
+            "guestiptype": 'Shared',
+            "supportedservices": 'Dhcp,Dns,UserData',
+            "traffictype": 'GUEST',
+            "specifyVlan" : "True",
+            "specifyIpRanges" : "True",
+            "serviceProviderList" : {
+                "Dhcp": 'VirtualRouter',
+                "Dns": 'VirtualRouter',
+                "UserData": 'VirtualRouter',
+            },
+        },        
+        "network_offering_internal_lb": {
+            "name": "Network offering for internal lb service",
+            "displaytext": "Network offering for internal lb service",
+            "guestiptype": "Isolated",
+            "traffictype": "Guest",
+            "supportedservices": "Vpn,Dhcp,Dns,Lb,UserData,SourceNat,StaticNat,PortForwarding,NetworkACL",
+            "serviceProviderList": {
+                "Dhcp": "VpcVirtualRouter",
+                "Dns": "VpcVirtualRouter",
+                "Vpn": "VpcVirtualRouter",
+                "UserData": "VpcVirtualRouter",
+                "Lb": "InternalLbVM",
+                "SourceNat": "VpcVirtualRouter",
+                "StaticNat": "VpcVirtualRouter",
+                "PortForwarding": "VpcVirtualRouter",
+                "NetworkACL": "VpcVirtualRouter",
+            },
+            "serviceCapabilityList": {
+                "SourceNat": {"SupportedSourceNatTypes": "peraccount"},
+                "Lb": {"lbSchemes": "internal", "SupportedLbIsolation": "dedicated"}
+            }
+        },
+
+        "natrule": {
+            "privateport": 22,
+            "publicport": 2222,
+            "protocol": "TCP"
+        },
+        "lbrule": {
+            "name": "SSH",
+            "alg": "roundrobin",
+            "privateport": 22,
+            "publicport": 2222,
+            "protocol": 'TCP'
+        },
+
+        "iso1": {
+            "displaytext": "Test ISO 1",
+            "name": "ISO 1",
+            "url": "http://people.apache.org/~tsp/dummy.iso",
+            "isextractable": True,
+            "isfeatured": True,
+            "ispublic": True,
+            "ostype": "CentOS 5.3 (64-bit)",
+        },
+        "iso2": {
+            "displaytext": "Test ISO 2",
+            "name": "ISO 2",
+            "url": "http://people.apache.org/~tsp/dummy.iso",
+            "isextractable": True,
+            "isfeatured": True,
+            "ispublic": True,
+            "ostype": "CentOS 5.3 (64-bit)",
+            "mode": 'HTTP_DOWNLOAD',
+        },
+        "isfeatured": True,
+        "ispublic": True,
+        "isextractable": True,
+        "bootable": True, 
+        "passwordenabled": True,
+        
+        "template": {
+            "displaytext": "xs",
+            "name": "xs",
+            "passwordenabled": False,
+        },
+        "template_2": {
+            "displaytext": "Public Template",
+            "name": "Public template",
+            "ostype": "CentOS 5.3 (64-bit)",
+            "isfeatured": True,
+            "ispublic": True,
+            "isextractable": True,
+            "mode": "HTTP_DOWNLOAD",
+        },
+        "templatefilter": 'self',
+
+        "security_group" : { "name": "custom_Sec_Grp" },
+        "ingress_rule": {
+            "protocol": "TCP",
+            "startport": "22",
+            "endport": "22",
+            "cidrlist": "0.0.0.0/0"
+        },
+        "ostype": "CentOS 5.3 (64-bit)",
+        "sleep": 90,
+        "timeout": 10,
+        "advanced_sg": {
+            "zone": {
+                "name": "",
+                "dns1": "8.8.8.8",
+                "internaldns1": "192.168.100.1",
+                "networktype": "Advanced",
+                "securitygroupenabled": "true"
+            },
+            "securitygroupenabled": "true"
+        },
+        "vlan": {
+            "part": ["4090-4091", "4092-4095"],
+            "full": "4090-4095",
+        },
+        "nfs": {
+            "url": "nfs://10.147.28.7/export/home/talluri/testprimary",
+            "name": "Primary XEN"
+        },
+        "iscsi": {
+            "url": "iscsi://192.168.100.21/iqn.2012-01.localdomain.clo-cstack-cos6:iser/1",
+            "name": "Primary iSCSI"
+        },
+        "volume": {"diskname": "Test Volume"},
+        "volume_offerings": {
+            0: {"diskname": "TestDiskServ"},
+        },
+        "diskdevice": ['/dev/vdc',  '/dev/vdb', '/dev/hdb', '/dev/hdc', '/dev/xvdd', '/dev/cdrom', '/dev/sr0',  '/dev/cdrom1' ],
+        
+        #test_vpc_vpn.py
+        "vpn_user": {
+            "username": "test",
+            "password": "password",
+        },
+        "vpc": {
+            "name": "vpc_vpn",
+            "displaytext": "vpc-vpn",
+            "cidr": "10.1.1.0/24"
+        },
+        "ntwk": {
+            "name": "tier1",
+            "displaytext": "vpc-tier1",
+            "gateway" : "10.1.1.1",
+            "netmask" : "255.255.255.192"
+        },
+        "vpc2": {
+            "name": "vpc2_vpn",
+            "displaytext": "vpc2-vpn",
+            "cidr": "10.2.1.0/24"
+        },
+        "ntwk2": {
+            "name": "tier2",
+            "displaytext": "vpc-tier2",
+            "gateway" : "10.2.1.1",
+            "netmask" : "255.255.255.192"
+        }
+}