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"
+ }
+}