You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bf...@apache.org on 2013/11/08 19:08:21 UTC

[05/76] [abbrv] CLOUSTACK-5099: Utils.py-has-wrong-reference, cleaned it. As well added Uniform naming convention Signed-off-by: SrikanteswaraRao Talluri

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/cloudstackAPI/updateZone.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/cloudstackAPI/updateZone.py b/tools/marvin/build/lib/marvin/cloudstackAPI/updateZone.py
new file mode 100644
index 0000000..b1b4360
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/cloudstackAPI/updateZone.py
@@ -0,0 +1,154 @@
+# 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.
+
+
+"""Updates a Zone."""
+from baseCmd import *
+from baseResponse import *
+class updateZoneCmd (baseCmd):
+    def __init__(self):
+        self.isAsync = "false"
+        """the ID of the Zone"""
+        """Required"""
+        self.id = None
+        """Allocation state of this cluster for allocation of new resources"""
+        self.allocationstate = None
+        """the details for the Zone"""
+        self.details = []
+        """the dhcp Provider for the Zone"""
+        self.dhcpprovider = None
+        """the first DNS for the Zone"""
+        self.dns1 = None
+        """the second DNS for the Zone"""
+        self.dns2 = None
+        """the dns search order list"""
+        self.dnssearchorder = []
+        """Network domain name for the networks in the zone; empty string will update domain with NULL value"""
+        self.domain = None
+        """the guest CIDR address for the Zone"""
+        self.guestcidraddress = None
+        """the first internal DNS for the Zone"""
+        self.internaldns1 = None
+        """the second internal DNS for the Zone"""
+        self.internaldns2 = None
+        """the first DNS for IPv6 network in the Zone"""
+        self.ip6dns1 = None
+        """the second DNS for IPv6 network in the Zone"""
+        self.ip6dns2 = None
+        """updates a private zone to public if set, but not vice-versa"""
+        self.ispublic = None
+        """true if local storage offering enabled, false otherwise"""
+        self.localstorageenabled = None
+        """the name of the Zone"""
+        self.name = None
+        self.required = ["id",]
+
+class updateZoneResponse (baseResponse):
+    def __init__(self):
+        """Zone id"""
+        self.id = None
+        """the allocation state of the cluster"""
+        self.allocationstate = None
+        """Zone description"""
+        self.description = None
+        """the dhcp Provider for the Zone"""
+        self.dhcpprovider = None
+        """the display text of the zone"""
+        self.displaytext = None
+        """the first DNS for the Zone"""
+        self.dns1 = None
+        """the second DNS for the Zone"""
+        self.dns2 = None
+        """Network domain name for the networks in the zone"""
+        self.domain = None
+        """the UUID of the containing domain, null for public zones"""
+        self.domainid = None
+        """the name of the containing domain, null for public zones"""
+        self.domainname = None
+        """the guest CIDR address for the Zone"""
+        self.guestcidraddress = None
+        """the first internal DNS for the Zone"""
+        self.internaldns1 = None
+        """the second internal DNS for the Zone"""
+        self.internaldns2 = None
+        """the first IPv6 DNS for the Zone"""
+        self.ip6dns1 = None
+        """the second IPv6 DNS for the Zone"""
+        self.ip6dns2 = None
+        """true if local storage offering enabled, false otherwise"""
+        self.localstorageenabled = None
+        """Zone name"""
+        self.name = None
+        """the network type of the zone; can be Basic or Advanced"""
+        self.networktype = None
+        """true if security groups support is enabled, false otherwise"""
+        self.securitygroupsenabled = None
+        """the vlan range of the zone"""
+        self.vlan = None
+        """Zone Token"""
+        self.zonetoken = None
+        """the capacity of the Zone"""
+        self.capacity = []
+        """the list of resource tags associated with zone."""
+        self.tags = []
+
+class capacity:
+    def __init__(self):
+        """"the total capacity available"""
+        self.capacitytotal = None
+        """"the capacity currently in use"""
+        self.capacityused = None
+        """"the Cluster ID"""
+        self.clusterid = None
+        """"the Cluster name"""
+        self.clustername = None
+        """"the percentage of capacity currently in use"""
+        self.percentused = None
+        """"the Pod ID"""
+        self.podid = None
+        """"the Pod name"""
+        self.podname = None
+        """"the capacity type"""
+        self.type = None
+        """"the Zone ID"""
+        self.zoneid = None
+        """"the Zone name"""
+        self.zonename = None
+
+class tags:
+    def __init__(self):
+        """"the account associated with the tag"""
+        self.account = None
+        """"customer associated with the tag"""
+        self.customer = None
+        """"the domain associated with the tag"""
+        self.domain = None
+        """"the ID of the domain associated with the tag"""
+        self.domainid = None
+        """"tag key name"""
+        self.key = None
+        """"the project name where tag belongs to"""
+        self.project = None
+        """"the project id the tag belongs to"""
+        self.projectid = None
+        """"id of the resource"""
+        self.resourceid = None
+        """"resource type"""
+        self.resourcetype = None
+        """"tag value"""
+        self.value = None
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/cloudstackAPI/uploadCustomCertificate.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/cloudstackAPI/uploadCustomCertificate.py b/tools/marvin/build/lib/marvin/cloudstackAPI/uploadCustomCertificate.py
new file mode 100644
index 0000000..d84f448
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/cloudstackAPI/uploadCustomCertificate.py
@@ -0,0 +1,43 @@
+# 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.
+
+
+"""Uploads a custom certificate for the console proxy VMs to use for SSL. Can be used to upload a single certificate signed by a known CA. Can also be used, through multiple calls, to upload a chain of certificates from CA to the custom certificate itself."""
+from baseCmd import *
+from baseResponse import *
+class uploadCustomCertificateCmd (baseCmd):
+    def __init__(self):
+        self.isAsync = "true"
+        """The certificate to be uploaded."""
+        """Required"""
+        self.certificate = None
+        """DNS domain suffix that the certificate is granted for."""
+        """Required"""
+        self.domainsuffix = None
+        """An integer providing the location in a chain that the certificate will hold. Usually, this can be left empty. When creating a chain, the top level certificate should have an ID of 1, with each step in the chain incrementing by one. Example, CA with id = 1, Intermediate CA with id = 2, Site certificate with ID = 3"""
+        self.id = None
+        """A name / alias for the certificate."""
+        self.name = None
+        """The private key for the attached certificate."""
+        self.privatekey = None
+        self.required = ["certificate","domainsuffix",]
+
+class uploadCustomCertificateResponse (baseResponse):
+    def __init__(self):
+        """message of the certificate upload operation"""
+        self.message = None
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/cloudstackAPI/uploadVolume.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/cloudstackAPI/uploadVolume.py b/tools/marvin/build/lib/marvin/cloudstackAPI/uploadVolume.py
new file mode 100644
index 0000000..acd4a0e
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/cloudstackAPI/uploadVolume.py
@@ -0,0 +1,162 @@
+# 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.
+
+
+"""Uploads a data disk."""
+from baseCmd import *
+from baseResponse import *
+class uploadVolumeCmd (baseCmd):
+    def __init__(self):
+        self.isAsync = "true"
+        """the format for the volume. Possible values include QCOW2, OVA, and VHD."""
+        """Required"""
+        self.format = None
+        """the name of the volume"""
+        """Required"""
+        self.name = None
+        """the URL of where the volume is hosted. Possible URL include http:// and https://"""
+        """Required"""
+        self.url = None
+        """the ID of the zone the volume is to be hosted on"""
+        """Required"""
+        self.zoneid = None
+        """an optional accountName. Must be used with domainId."""
+        self.account = None
+        """the MD5 checksum value of this volume"""
+        self.checksum = None
+        """an optional domainId. If the account parameter is used, domainId must also be used."""
+        self.domainid = None
+        """Image store uuid"""
+        self.imagestoreuuid = None
+        """Upload volume for the project"""
+        self.projectid = None
+        self.required = ["format","name","url","zoneid",]
+
+class uploadVolumeResponse (baseResponse):
+    def __init__(self):
+        """ID of the disk volume"""
+        self.id = None
+        """the account associated with the disk volume"""
+        self.account = None
+        """the date the volume was attached to a VM instance"""
+        self.attached = None
+        """the date the disk volume was created"""
+        self.created = None
+        """the boolean state of whether the volume is destroyed or not"""
+        self.destroyed = None
+        """the ID of the device on user vm the volume is attahed to. This tag is not returned when the volume is detached."""
+        self.deviceid = None
+        """bytes read rate of the disk volume"""
+        self.diskBytesReadRate = None
+        """bytes write rate of the disk volume"""
+        self.diskBytesWriteRate = None
+        """io requests read rate of the disk volume"""
+        self.diskIopsReadRate = None
+        """io requests write rate of the disk volume"""
+        self.diskIopsWriteRate = None
+        """the display text of the disk offering"""
+        self.diskofferingdisplaytext = None
+        """ID of the disk offering"""
+        self.diskofferingid = None
+        """name of the disk offering"""
+        self.diskofferingname = None
+        """an optional field whether to the display the volume to the end user or not."""
+        self.displayvolume = None
+        """the domain associated with the disk volume"""
+        self.domain = None
+        """the ID of the domain associated with the disk volume"""
+        self.domainid = None
+        """Hypervisor the volume belongs to"""
+        self.hypervisor = None
+        """true if the volume is extractable, false otherwise"""
+        self.isextractable = None
+        """max iops of the disk volume"""
+        self.maxiops = None
+        """min iops of the disk volume"""
+        self.miniops = None
+        """name of the disk volume"""
+        self.name = None
+        """The path of the volume"""
+        self.path = None
+        """the project name of the vpn"""
+        self.project = None
+        """the project id of the vpn"""
+        self.projectid = None
+        """the display text of the service offering for root disk"""
+        self.serviceofferingdisplaytext = None
+        """ID of the service offering for root disk"""
+        self.serviceofferingid = None
+        """name of the service offering for root disk"""
+        self.serviceofferingname = None
+        """size of the disk volume"""
+        self.size = None
+        """ID of the snapshot from which this volume was created"""
+        self.snapshotid = None
+        """the state of the disk volume"""
+        self.state = None
+        """the status of the volume"""
+        self.status = None
+        """name of the primary storage hosting the disk volume"""
+        self.storage = None
+        """id of the primary storage hosting the disk volume; returned to admin user only"""
+        self.storageid = None
+        """shared or local storage"""
+        self.storagetype = None
+        """type of the disk volume (ROOT or DATADISK)"""
+        self.type = None
+        """id of the virtual machine"""
+        self.virtualmachineid = None
+        """display name of the virtual machine"""
+        self.vmdisplayname = None
+        """name of the virtual machine"""
+        self.vmname = None
+        """state of the virtual machine"""
+        self.vmstate = None
+        """ID of the availability zone"""
+        self.zoneid = None
+        """name of the availability zone"""
+        self.zonename = None
+        """the list of resource tags associated with volume"""
+        self.tags = []
+        """the ID of the latest async job acting on this object"""
+        self.jobid = None
+        """the current status of the latest async job acting on this object"""
+        self.jobstatus = None
+
+class tags:
+    def __init__(self):
+        """"the account associated with the tag"""
+        self.account = None
+        """"customer associated with the tag"""
+        self.customer = None
+        """"the domain associated with the tag"""
+        self.domain = None
+        """"the ID of the domain associated with the tag"""
+        self.domainid = None
+        """"tag key name"""
+        self.key = None
+        """"the project name where tag belongs to"""
+        self.project = None
+        """"the project id the tag belongs to"""
+        self.projectid = None
+        """"id of the resource"""
+        self.resourceid = None
+        """"resource type"""
+        self.resourcetype = None
+        """"tag value"""
+        self.value = None
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/cloudstackConnection.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/cloudstackConnection.py b/tools/marvin/build/lib/marvin/cloudstackConnection.py
new file mode 100644
index 0000000..686c533
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/cloudstackConnection.py
@@ -0,0 +1,235 @@
+# 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.
+
+import requests
+import urllib
+import base64
+import hmac
+import hashlib
+import logging
+import time
+import cloudstackException
+from cloudstackAPI import *
+import jsonHelper
+from requests import ConnectionError
+from requests import HTTPError
+from requests import Timeout
+from requests import RequestException
+
+
+class cloudConnection(object):
+
+    """ Connections to make API calls to the cloudstack management server
+    """
+
+    def __init__(self, mgtSvr, port=8096, user=None, passwd=None,
+                 apiKey=None, securityKey=None,
+                 asyncTimeout=3600, logging=None, scheme='http',
+                 path='client/api'):
+        self.loglevel()  # Turn off requests logs
+        self.apiKey = apiKey
+        self.securityKey = securityKey
+        self.mgtSvr = mgtSvr
+        self.port = port
+        self.user = user
+        self.passwd = passwd
+        self.logging = logging
+        self.path = path
+        self.retries = 5
+        self.asyncTimeout = asyncTimeout
+        self.auth = True
+        if port == 8096 or \
+           (self.apiKey is None and self.securityKey is None):
+            self.auth = False
+        if scheme not in ['http', 'https']:
+                raise RequestException("Protocol must be HTTP")
+        self.protocol = scheme
+        self.baseurl = "%s://%s:%d/%s"\
+                       % (self.protocol, self.mgtSvr, self.port, self.path)
+
+    def __copy__(self):
+        return cloudConnection(self.mgtSvr, self.port, self.user, self.passwd,
+                               self.apiKey, self.securityKey,
+                               self.asyncTimeout, self.logging, self.protocol,
+                               self.path)
+
+    def loglevel(self, lvl=logging.WARNING):
+        """
+        Turns off the INFO/DEBUG logs from `requests`
+        """
+        requests_log = logging.getLogger("requests")
+        requests_log.setLevel(lvl)
+
+    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.marvin_request(cmd, response_type=response)
+
+            if asyncResonse.jobstatus == 2:
+                raise cloudstackException.cloudstackAPIException(
+                    "asyncquery", asyncResonse.jobresult)
+            elif asyncResonse.jobstatus == 1:
+                return asyncResonse
+
+            time.sleep(5)
+            if self.logging is not None:
+                self.logging.debug("job: %s still processing,"
+                                   " will timeout in %ds" % (jobid, timeout))
+            timeout = timeout - 5
+
+        raise cloudstackException.cloudstackAPIException(
+            "asyncquery", "Async job timeout %s" % jobid)
+
+    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
+        """
+        params = zip(payload.keys(), payload.values())
+        params.sort(key=lambda k: str.lower(k[0]))
+        hashStr = "&".join(
+            ["=".join(
+                [str.lower(r[0]),
+                 str.lower(
+                     urllib.quote_plus(str(r[1]))
+                 ).replace("+", "%20")]
+            ) for r in params]
+        )
+        signature = base64.encodestring(hmac.new(
+            self.securityKey, hashStr, hashlib.sha1).digest()).strip()
+        self.logging.debug("Computed Signature by Marvin: %s" % signature)
+        return signature
+
+    def request(self, command, auth=True, payload={}, method='GET'):
+        """
+        Makes requests using auth or over integration port
+        @param command: cloudstack API command name
+                    eg: deployVirtualMachineCommand
+        @param 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["command"] = command
+        payload["response"] = "json"
+
+        if auth:
+            payload["apiKey"] = self.apiKey
+            signature = self.sign(payload)
+            payload["signature"] = signature
+
+        try:
+            if method == 'POST':
+                response = requests.post(
+                    self.baseurl, params=payload, verify=False)
+            else:
+                response = requests.get(
+                    self.baseurl, params=payload, verify=False)
+        except ConnectionError, c:
+            self.logging.debug("Connection refused. Reason: %s : %s" %
+                               (self.baseurl, c))
+            raise c
+        except HTTPError, h:
+            self.logging.debug("Server returned error code: %s" % h)
+            raise h
+        except Timeout, t:
+            self.logging.debug("Connection timed out with %s" % t)
+            raise t
+        except RequestException, r:
+            self.logging.debug("Error returned by server %s" % r)
+            raise r
+        else:
+            return response
+
+    def sanitize_command(self, cmd):
+        """
+        Removes None values, Validates all required params are present
+        @param cmd: Cmd object eg: createPhysicalNetwork
+        @return:
+        """
+        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)
+                    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, isAsync, requests
+
+    def marvin_request(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:
+        """
+        cmdname, isAsync, payload = self.sanitize_command(cmd)
+        self.logging.debug("sending %s request: %s %s" % (method, cmdname,
+                                                          str(payload)))
+        response = self.request(
+            cmdname, self.auth, payload=payload, method=method)
+        self.logging.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)
+
+        if isAsync == "false":
+            return response
+        else:
+            asyncJobId = response.jobid
+            response = self.poll(asyncJobId, response_type)
+            return response.jobresult

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/cloudstackException.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/cloudstackException.py b/tools/marvin/build/lib/marvin/cloudstackException.py
new file mode 100644
index 0000000..6200003
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/cloudstackException.py
@@ -0,0 +1,48 @@
+# 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.
+
+
+class cloudstackAPIException(Exception):
+    def __init__(self, cmd="", result=""):
+        self.errorMsg = "Execute cmd: %s failed, due to: %s" % (cmd, result)
+
+    def __str__(self):
+        return self.errorMsg
+
+
+class InvalidParameterException(Exception):
+    def __init__(self, msg=''):
+        self.errorMsg = msg
+
+    def __str__(self):
+        return self.errorMsg
+
+
+class dbException(Exception):
+    def __init__(self, msg=''):
+        self.errorMsg = msg
+
+    def __str__(self):
+        return self.errorMsg
+
+
+class internalError(Exception):
+    def __init__(self, msg=''):
+        self.errorMsg = msg
+
+    def __str__(self):
+        return self.errorMsg

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/cloudstackTestCase.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/cloudstackTestCase.py b/tools/marvin/build/lib/marvin/cloudstackTestCase.py
new file mode 100644
index 0000000..85ef542
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/cloudstackTestCase.py
@@ -0,0 +1,40 @@
+# 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.
+
+import unittest
+
+
+def user(Name, DomainName, AcctType):
+    def wrapper(cls):
+        orig_init = cls.__init__
+
+        def __init__(self, *args, **kws):
+            cls.UserName = Name
+            cls.DomainName = DomainName
+            cls.AcctType = AcctType
+            orig_init(self, *args, **kws)
+        cls.__init__ = __init__
+        return cls
+    return wrapper
+
+
+class cloudstackTestCase(unittest.case.TestCase):
+    clstestclient = None
+
+    @classmethod
+    def getClsTestClient(cls):
+        return cls.clstestclient

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/cloudstackTestClient.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/cloudstackTestClient.py b/tools/marvin/build/lib/marvin/cloudstackTestClient.py
new file mode 100644
index 0000000..36f7f8d
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/cloudstackTestClient.py
@@ -0,0 +1,201 @@
+# 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.
+
+import cloudstackConnection
+import asyncJobMgr
+import dbConnection
+from cloudstackAPI import *
+import random
+import string
+import hashlib
+
+
+class cloudstackTestClient(object):
+    def __init__(self, mgtSvr=None, port=8096, user=None, passwd=None,
+                 apiKey=None, securityKey=None, asyncTimeout=3600,
+                 defaultWorkerThreads=10, logging=None):
+        self.connection = \
+            cloudstackConnection.cloudConnection(mgtSvr, port, user, passwd,
+                                                 apiKey, securityKey,
+                                                 asyncTimeout, logging)
+        self.apiClient =\
+            cloudstackAPIClient.CloudStackAPIClient(self.connection)
+        self.dbConnection = None
+        self.asyncJobMgr = None
+        self.ssh = None
+        self.id = None
+        self.defaultWorkerThreads = defaultWorkerThreads
+
+    @property
+    def identifier(self):
+        return self.id
+
+    @identifier.setter
+    def identifier(self, id):
+        self.id = id
+
+    def dbConfigure(self, host="localhost", port=3306, user='cloud',
+                    passwd='cloud', db='cloud'):
+        self.dbConnection = dbConnection.dbConnection(host, port, user, passwd,
+                                                      db)
+
+    def isAdminContext(self):
+        """
+        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
+        except:
+            return 0  # user
+
+    def random_gen(self, size=6, chars=string.ascii_uppercase + string.digits):
+        """Generate Random Strings of variable length"""
+        randomstr = ''.join(random.choice(chars) for x in range(size))
+        if self.identifier:
+            return ''.join([self.identifier, '-', randomstr])
+        return randomstr
+
+    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
+        try:
+            accounts = self.apiClient.listAccounts(cmd)
+            acctId = accounts[0].id
+        except:
+            createAcctCmd = createAccount.createAccountCmd()
+            createAcctCmd.accounttype = acctType
+            createAcctCmd.domainid = domId
+            createAcctCmd.email = "test-" + self.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
+
+        newUserConnection =\
+            cloudstackConnection.cloudConnection(self.connection.mgtSvr,
+                                                 self.connection.port,
+                                                 self.connection.user,
+                                                 self.connection.passwd,
+                                                 apiKey, securityKey,
+                                                 self.connection.asyncTimeout,
+                                                 self.connection.logging)
+        self.userApiClient =\
+            cloudstackAPIClient.CloudStackAPIClient(newUserConnection)
+        self.userApiClient.connection = newUserConnection
+        self.userApiClient.hypervisor = self.apiClient.hypervisor
+        return self.userApiClient
+
+    def close(self):
+        if self.connection is not None:
+            self.connection.close()
+
+    def getDbConnection(self):
+        return self.dbConnection
+
+    def executeSql(self, sql=None):
+        if sql is None or self.dbConnection is None:
+            return None
+
+        return self.dbConnection.execute()
+
+    def executeSqlFromFile(self, sqlFile=None):
+        if sqlFile is None or self.dbConnection is None:
+            return None
+        return self.dbConnection.executeSqlFromFile(sqlFile)
+
+    def getApiClient(self):
+        self.apiClient.id = self.identifier
+        return self.apiClient
+
+    def getUserApiClient(self, account, domain, type=0):
+        """
+        0 - user
+        1 - admin
+        2 - domain admin
+        """
+        self.createUserApiClient(account, domain, type)
+        if hasattr(self, "userApiClient"):
+            return self.userApiClient
+        return None
+
+    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)
+
+    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
+        '''
+        if self.asyncJobMgr is None:
+            self.asyncJobMgr = 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)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/codegenerator.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/codegenerator.py b/tools/marvin/build/lib/marvin/codegenerator.py
new file mode 100644
index 0000000..96729f6
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/codegenerator.py
@@ -0,0 +1,463 @@
+# 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.
+
+import xml.dom.minidom
+import json
+from optparse import OptionParser
+from textwrap import dedent
+import os
+import sys
+import urllib2
+
+
+class cmdParameterProperty(object):
+    def __init__(self):
+        self.name = None
+        self.required = False
+        self.desc = ""
+        self.type = "planObject"
+        self.subProperties = []
+
+
+class cloudStackCmd(object):
+    def __init__(self):
+        self.name = ""
+        self.desc = ""
+        self.async = "false"
+        self.request = []
+        self.response = []
+
+
+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
+    the ApiDocWriter. This class provides helper methods for these uses.
+    """
+    space = '    '
+    newline = '\n'
+    cmdsName = []
+
+    def __init__(self, outputFolder):
+        self.cmd = None
+        self.code = ""
+        self.required = []
+        self.subclass = []
+        self.outputFolder = outputFolder
+        lic = """\
+          # 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.
+
+          """
+        self.license = dedent(lic)
+
+    def addAttribute(self, attr, pro):
+        value = pro.value
+        if pro.required:
+            self.required.append(attr)
+        desc = pro.desc
+        if desc is not None:
+            self.code += self.space
+            self.code += "''' " + pro.desc + " '''"
+            self.code += self.newline
+
+        self.code += self.space
+        self.code += attr + " = " + str(value)
+        self.code += self.newline
+
+    def generateSubClass(self, name, properties):
+        '''generate code for sub list'''
+        subclass = 'class %s:\n' % name
+        subclass += self.space + "def __init__(self):\n"
+        for pro in properties:
+            if pro.desc is not None:
+                subclass += self.space + self.space + '""""%s"""\n' % pro.desc
+            if len(pro.subProperties) > 0:
+                subclass += self.space + self.space
+                subclass += 'self.%s = []\n' % pro.name
+                self.generateSubClass(pro.name, pro.subProperties)
+            else:
+                subclass += self.space + self.space
+                subclass += 'self.%s = None\n' % pro.name
+
+        self.subclass.append(subclass)
+
+    def generate(self, cmd):
+
+        self.cmd = cmd
+        self.cmdsName.append(self.cmd.name)
+        self.code = self.license
+        self.code += self.newline
+        self.code += '"""%s"""\n' % self.cmd.desc
+        self.code += 'from baseCmd import *\n'
+        self.code += 'from baseResponse import *\n'
+        self.code += "class %sCmd (baseCmd):\n" % self.cmd.name
+        self.code += self.space + "def __init__(self):\n"
+
+        self.code += self.space + self.space
+        self.code += 'self.isAsync = "%s"\n' % str(self.cmd.async).lower()
+
+        for req in self.cmd.request:
+            if req.desc is not None:
+                self.code += self.space + self.space + '"""%s"""\n' % req.desc
+            if req.required == "true":
+                self.code += self.space + self.space + '"""Required"""\n'
+
+            value = "None"
+            if req.type == "list" or req.type == "map":
+                value = "[]"
+
+            self.code += self.space + self.space
+            self.code += 'self.%s = %s\n' % (req.name, value)
+            if req.required == "true":
+                self.required.append(req.name)
+
+        self.code += self.space + self.space + "self.required = ["
+        for require in self.required:
+            self.code += '"' + require + '",'
+        self.code += "]\n"
+        self.required = []
+
+        """generate response code"""
+        subItems = {}
+        self.code += self.newline
+        self.code += 'class %sResponse (baseResponse):\n' % self.cmd.name
+        self.code += self.space + "def __init__(self):\n"
+        if len(self.cmd.response) == 0:
+            self.code += self.space + self.space + "pass"
+        else:
+            for res in self.cmd.response:
+                if res.desc is not None:
+                    self.code += self.space + self.space
+                    self.code += '"""%s"""\n' % res.desc
+
+                if len(res.subProperties) > 0:
+                    self.code += self.space + self.space
+                    self.code += 'self.%s = []\n' % res.name
+                    self.generateSubClass(res.name, res.subProperties)
+                else:
+                    self.code += self.space + self.space
+                    self.code += 'self.%s = None\n' % res.name
+        self.code += self.newline
+
+        for subclass in self.subclass:
+            self.code += subclass + "\n"
+
+        fp = open(self.outputFolder + "/cloudstackAPI/%s.py" % self.cmd.name,
+                  "w")
+        fp.write(self.code)
+        fp.close()
+        self.code = ""
+        self.subclass = []
+
+    def finalize(self):
+        '''generate an api call'''
+
+        header = '"""Test Client for CloudStack API"""\n'
+        imports = "import copy\n"
+        initCmdsList = '__all__ = ['
+        body = ''
+        body += "class CloudStackAPIClient(object):\n"
+        body += self.space + 'def __init__(self, connection):\n'
+        body += self.space + self.space + 'self.connection = connection\n'
+        body += self.space + self.space + 'self._id = None\n'
+        body += self.newline
+
+        body += self.space + 'def __copy__(self):\n'
+        body += self.space + self.space
+        body += 'return CloudStackAPIClient(copy.copy(self.connection))\n'
+        body += self.newline
+
+        # The `id` property will be used to link the test with the cloud
+        # resource being created
+        #            @property
+        #            def id(self):
+        #                return self._id
+        #
+        #            @id.setter
+        #            def id(self, identifier):
+        #                self._id = identifier
+
+        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.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.newline
+
+        for cmdName in self.cmdsName:
+            body += self.space
+            body += 'def %s(self, command, method="GET"):\n' % cmdName
+            body += self.space + self.space
+            body += 'response = %sResponse()\n' % cmdName
+            body += self.space + self.space
+            body += 'response = self.connection.marvin_request(command,'
+            body += ' response_type=response, method=method)\n'
+            body += self.space + self.space + 'return response\n'
+            body += self.newline
+
+            imports += 'from %s import %sResponse\n' % (cmdName, cmdName)
+            initCmdsList += '"%s",' % cmdName
+
+        fp = open(self.outputFolder + '/cloudstackAPI/cloudstackAPIClient.py',
+                  'w')
+        fp.write(self.license)
+        for item in [header, imports, body]:
+            fp.write(item)
+        fp.close()
+
+        '''generate __init__.py'''
+        initCmdsList = self.license + initCmdsList + '"cloudstackAPIClient"]'
+        fp = open(self.outputFolder + '/cloudstackAPI/__init__.py', 'w')
+        fp.write(initCmdsList)
+        fp.close()
+
+        fp = open(self.outputFolder + '/cloudstackAPI/baseCmd.py', 'w')
+        basecmd = self.license
+        basecmd += '"""Base Command"""\n'
+        basecmd += 'class baseCmd(object):\n'
+        basecmd += self.space + 'pass\n'
+        fp.write(basecmd)
+        fp.close()
+
+        fp = open(self.outputFolder + '/cloudstackAPI/baseResponse.py', 'w')
+        basecmd = self.license
+        basecmd += '"""Base class for response"""\n'
+        basecmd += 'class baseResponse(object):\n'
+        basecmd += self.space + 'pass\n'
+        fp.write(basecmd)
+        fp.close()
+
+    def constructResponseFromXML(self, response):
+        paramProperty = cmdParameterProperty()
+        paramProperty.name = getText(response.getElementsByTagName('name'))
+        paramProperty.desc = getText(response.
+                                     getElementsByTagName('description'))
+        if paramProperty.name.find('(*)') != -1:
+            '''This is a list'''
+            paramProperty.name = paramProperty.name.split('(*)')[0]
+            argList = response.getElementsByTagName('arguments')[0].\
+                getElementsByTagName('arg')
+            for subresponse in argList:
+                subProperty = self.constructResponseFromXML(subresponse)
+                paramProperty.subProperties.append(subProperty)
+        return paramProperty
+
+    def loadCmdFromXML(self, dom):
+        cmds = []
+        for cmd in dom.getElementsByTagName("command"):
+            csCmd = cloudStackCmd()
+            csCmd.name = getText(cmd.getElementsByTagName('name'))
+            assert csCmd.name
+
+            desc = getText(cmd.getElementsByTagName('description'))
+            if desc:
+                csCmd.desc = desc
+
+            async = getText(cmd.getElementsByTagName('isAsync'))
+            if async:
+                csCmd.async = async
+
+            argList = cmd.getElementsByTagName("request")[0].\
+                getElementsByTagName("arg")
+            for param in argList:
+                paramProperty = cmdParameterProperty()
+
+                paramProperty.name =\
+                    getText(param.getElementsByTagName('name'))
+                assert paramProperty.name
+
+                required = param.getElementsByTagName('required')
+                if required:
+                    paramProperty.required = getText(required)
+
+                requestDescription = param.getElementsByTagName('description')
+                if requestDescription:
+                    paramProperty.desc = getText(requestDescription)
+
+                type = param.getElementsByTagName("type")
+                if type:
+                    paramProperty.type = getText(type)
+
+                csCmd.request.append(paramProperty)
+
+            responseEle = cmd.getElementsByTagName("response")[0]
+            for response in responseEle.getElementsByTagName("arg"):
+                if response.parentNode != responseEle:
+                    continue
+
+                paramProperty = self.constructResponseFromXML(response)
+                csCmd.response.append(paramProperty)
+
+            cmds.append(csCmd)
+        return cmds
+
+    def generateCodeFromXML(self, apiSpecFile):
+        dom = xml.dom.minidom.parse(apiSpecFile)
+        cmds = self.loadCmdFromXML(dom)
+        for cmd in cmds:
+            self.generate(cmd)
+        self.finalize()
+
+    def constructResponseFromJSON(self, response):
+        paramProperty = cmdParameterProperty()
+        if 'name' in response:
+            paramProperty.name = response['name']
+        assert paramProperty.name, "%s has no property name" % response
+
+        if 'description' in response:
+            paramProperty.desc = response['description']
+        if 'type' in response:
+            if response['type'] in ['list', 'map', 'set']:
+            #Here list becomes a subproperty
+                if 'response' in response:
+                    for innerResponse in response['response']:
+                        subProperty =\
+                            self.constructResponseFromJSON(innerResponse)
+                        paramProperty.subProperties.append(subProperty)
+            paramProperty.type = response['type']
+        return paramProperty
+
+    def loadCmdFromJSON(self, apiStream):
+        if apiStream is None:
+            raise Exception("No APIs found through discovery")
+
+        jsonOut = apiStream.readlines()
+        assert len(jsonOut) > 0
+        apiDict = json.loads(jsonOut[0])
+        if not 'listapisresponse' in apiDict:
+            raise Exception("API discovery plugin response failed")
+        if not 'count' in apiDict['listapisresponse']:
+            raise Exception("Malformed api response")
+
+        apilist = apiDict['listapisresponse']['api']
+        cmds = []
+        for cmd in apilist:
+            csCmd = cloudStackCmd()
+            if 'name' in cmd:
+                csCmd.name = cmd['name']
+            assert csCmd.name
+
+            if 'description' in cmd:
+                csCmd.desc = cmd['description']
+
+            if 'isasync' in cmd:
+                csCmd.async = cmd['isasync']
+
+            for param in cmd['params']:
+                paramProperty = cmdParameterProperty()
+
+                if 'name' in param:
+                    paramProperty.name = param['name']
+                assert paramProperty.name
+
+                if 'required' in param:
+                    paramProperty.required = param['required']
+
+                if 'description' in param:
+                    paramProperty.desc = param['description']
+
+                if 'type' in param:
+                    paramProperty.type = param['type']
+
+                csCmd.request.append(paramProperty)
+
+            for response in cmd['response']:
+            #FIXME: ExtractImage related APIs return empty dicts in response
+                if len(response) > 0:
+                    paramProperty = self.constructResponseFromJSON(response)
+                    csCmd.response.append(paramProperty)
+
+            cmds.append(csCmd)
+        return cmds
+
+    def generateCodeFromJSON(self, endpointUrl):
+        """
+        Api Discovery plugin returns the supported APIs of a CloudStack
+        endpoint.
+        @return: The classes in cloudstackAPI/ formed from api discovery json
+        """
+        if endpointUrl.find('response=json') >= 0:
+                apiStream = urllib2.urlopen(endpointUrl)
+                cmds = self.loadCmdFromJSON(apiStream)
+                for cmd in cmds:
+                    self.generate(cmd)
+                self.finalize()
+
+
+def getText(elements):
+    return elements[0].childNodes[0].nodeValue.strip()
+
+if __name__ == "__main__":
+    parser = OptionParser()
+    parser.add_option("-o", "--output", dest="output",
+                      help="The path to the generated code entities, default\
+ is .")
+    parser.add_option("-s", "--specfile", dest="spec",
+                      help="The path and name of the api spec xml file,\
+ default is /etc/cloud/cli/commands.xml")
+    parser.add_option("-e", "--endpoint", dest="endpoint",
+                      help="The endpoint mgmt server (with open 8096) where\
+ apis are discovered, default is localhost")
+
+    (options, args) = parser.parse_args()
+
+    folder = "."
+    if options.output is not None:
+        folder = options.output
+    apiModule = folder + "/cloudstackAPI"
+    if not os.path.exists(apiModule):
+        try:
+            os.mkdir(apiModule)
+        except:
+            print "Failed to create folder %s, due to %s" % (apiModule,
+                                                             sys.exc_info())
+            print parser.print_help()
+            exit(2)
+
+    apiSpecFile = "/etc/cloud/cli/commands.xml"
+    if options.spec is not None:
+        apiSpecFile = options.spec
+        if not os.path.exists(apiSpecFile):
+            print "the spec file %s does not exists" % apiSpecFile
+            print parser.print_help()
+            exit(1)
+
+    cg = codeGenerator(folder)
+    if options.spec is not None:
+        cg.generateCodeFromXML(apiSpecFile)
+    elif options.endpoint is not None:
+        endpointUrl = 'http://%s:8096/client/api?command=listApis&\
+response=json' % options.endpoint
+        cg.generateCodeFromJSON(endpointUrl)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/configGenerator.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/configGenerator.py b/tools/marvin/build/lib/marvin/configGenerator.py
new file mode 100644
index 0000000..a966ae0
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/configGenerator.py
@@ -0,0 +1,870 @@
+# 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.
+
+import json
+import os
+from optparse import OptionParser
+import jsonHelper
+
+
+class managementServer(object):
+    def __init__(self):
+        self.mgtSvrIp = None
+        self.port = 8096
+        self.apiKey = None
+        self.securityKey = None
+
+
+class dbServer(object):
+    def __init__(self):
+        self.dbSvr = None
+        self.port = 3306
+        self.user = "cloud"
+        self.passwd = "cloud"
+        self.db = "cloud"
+
+
+class configuration(object):
+    def __init__(self):
+        self.name = None
+        self.value = None
+
+
+class logger(object):
+    def __init__(self):
+        '''TestCase/TestClient'''
+        self.name = None
+        self.file = None
+
+
+class cloudstackConfiguration(object):
+    def __init__(self):
+        self.zones = []
+        self.mgtSvr = []
+        self.dbSvr = None
+        self.globalConfig = []
+        self.logger = []
+
+
+class zone(object):
+    def __init__(self):
+        self.dns1 = None
+        self.internaldns1 = None
+        self.name = None
+        '''Basic or Advanced'''
+        self.networktype = None
+        self.dns2 = None
+        self.internaldns2 = None
+        self.securitygroupenabled = None
+        self.localstorageenabled = None
+        '''default public network, in advanced mode'''
+        self.ipranges = []
+        self.physical_networks = []
+        self.pods = []
+        self.secondaryStorages = []
+        self.cacheStorages = []
+
+
+class traffictype(object):
+    def __init__(self, typ, labeldict=None):
+        self.typ = typ  # Guest/Management/Public
+        if labeldict:
+            self.xen = labeldict['xen'] if 'xen' in labeldict.keys() else None
+            self.kvm = labeldict['kvm'] if 'kvm' in labeldict.keys() else None
+            self.vmware = labeldict['vmware']\
+                if 'vmware' in labeldict.keys() else None
+            self.simulator = labeldict['simulator']\
+                if 'simulator' in labeldict.keys() else None
+        #{
+        #    'xen' : 'cloud-xen',
+        #    'kvm' : 'cloud-kvm',
+        #    'vmware' : 'cloud-vmware'
+        #}
+
+
+class pod(object):
+    def __init__(self):
+        self.gateway = None
+        self.name = None
+        self.netmask = None
+        self.startip = None
+        self.endip = None
+        self.zoneid = None
+        self.clusters = []
+        self.vmwaredc = []
+        '''Used in basic network mode'''
+        self.guestIpRanges = []
+
+
+class VmwareDc(object):
+    def __init__(self):
+        self.zoneid = None
+        self.name = None
+        self.vcenter = None
+        self.username = None
+        self.password = None
+
+
+class cluster(object):
+    def __init__(self):
+        self.clustername = None
+        self.clustertype = None
+        self.hypervisor = None
+        self.zoneid = None
+        self.podid = None
+        self.password = None
+        self.url = None
+        self.username = None
+        self.hosts = []
+        self.primaryStorages = []
+
+
+class host(object):
+    def __init__(self):
+        self.hypervisor = None
+        self.password = None
+        self.url = None
+        self.username = None
+        self.zoneid = None
+        self.podid = None
+        self.clusterid = None
+        self.clustername = None
+        self.cpunumber = None
+        self.cpuspeed = None
+        self.hostmac = None
+        self.hosttags = None
+        self.memory = None
+
+
+class physical_network(object):
+    def __init__(self):
+        self.name = None
+        self.tags = []
+        self.traffictypes = []
+        self.broadcastdomainrange = 'Zone'
+        self.vlan = None
+        self.isolationmethods = []
+        '''enable default virtual router provider'''
+        vrouter = provider()
+        vrouter.name = 'VirtualRouter'
+        self.providers = [vrouter]
+
+
+class provider(object):
+    def __init__(self, name=None):
+        self.name = name
+        self.state = None
+        self.broadcastdomainrange = 'ZONE'
+        self.zoneid = None
+        self.servicelist = []
+        self.devices = []
+
+
+class network(object):
+    def __init__(self):
+        self.displaytext = None
+        self.name = None
+        self.zoneid = None
+        self.acltype = None
+        self.domainid = None
+        self.networkdomain = None
+        self.networkofferingid = None
+        self.ipranges = []
+
+
+class iprange(object):
+    def __init__(self):
+        '''tagged/untagged'''
+        self.gateway = None
+        self.netmask = None
+        self.startip = None
+        self.endip = None
+        self.vlan = None
+        '''for account specific '''
+        self.account = None
+        self.domain = None
+
+
+class primaryStorage(object):
+    def __init__(self):
+        self.name = None
+        self.url = None
+
+
+class secondaryStorage(object):
+    def __init__(self):
+        self.url = None
+        self.provider = None
+        self.details = None
+
+
+class cacheStorage(object):
+    def __init__(self):
+        self.url = None
+        self.provider = None
+        self.details = None
+
+
+class s3(object):
+    def __init__(self):
+        self.accesskey = None
+        self.secretkey = None
+        self.bucket = None
+        self.endpoint = None
+        self.sockettimeout = None
+        self.connectiontimeout = None
+        self.maxerrorrety = None
+        self.usehttps = None
+
+
+class netscaler(object):
+    def __init__(self, hostname=None, username='nsroot', password='nsroot'):
+        self.hostname = hostname
+        self.username = username
+        self.password = password
+        self.networkdevicetype = 'NetscalerVPXLoadBalancer'
+        self.publicinterface = '1/1'
+        self.privateinterface = '1/1'
+        self.numretries = '2'
+        self.lbdevicecapacity = '50'
+        self.lbdevicededicated = 'false'
+
+    def getUrl(self):
+        return repr(self)
+
+    def __repr__(self):
+        req = zip(self.__dict__.keys(), self.__dict__.values())
+        return self.hostname+"?" + "&".join(["=".join([r[0], r[1]])
+                                             for r in req])
+
+
+class srx(object):
+    def __init__(self, hostname=None, username='root', password='admin'):
+        self.hostname = hostname
+        self.username = username
+        self.password = password
+        self.networkdevicetype = 'JuniperSRXFirewall'
+        self.publicinterface = '1/1'
+        self.privateinterface = '1/1'
+        self.numretries = '2'
+        self.fwdevicededicated = 'false'
+        self.timeout = '300'
+        self.publicnetwork = 'untrusted'
+        self.privatenetwork = 'trusted'
+
+    def getUrl(self):
+        return repr(self)
+
+    def __repr__(self):
+        req = zip(self.__dict__.keys(), self.__dict__.values())
+        return self.hostname+"?" + "&".join(["=".join([r[0], r[1]])
+                                             for r in req])
+
+
+class bigip(object):
+    def __init__(self, hostname=None, username='root', password='default'):
+        self.hostname = hostname
+        self.username = username
+        self.password = password
+        self.networkdevicetype = 'F5BigIpLoadBalancer'
+        self.publicinterface = '1/1'
+        self.privateinterface = '1/1'
+        self.numretries = '2'
+        self.lbdevicededicated = 'false'
+        self.lbdevicecapacity = '50'
+
+    def getUrl(self):
+        return repr(self)
+
+    def __repr__(self):
+        req = zip(self.__dict__.keys(), self.__dict__.values())
+        return self.hostname+"?" + "&".join(["=".join([r[0], r[1]])
+                                             for r in req])
+
+
+def getDeviceUrl(obj):
+    req = zip(obj.__dict__.keys(), obj.__dict__.values())
+    if obj.hostname:
+        return "http://" + obj.hostname+"?" + "&".join(["=".join([r[0],
+                                                                  r[1]])
+                                                        for r in req])
+    else:
+        return None
+
+
+def describe_setup_in_basic_mode():
+    '''sample code to generate setup configuration file'''
+    zs = cloudstackConfiguration()
+
+    for l in range(1):
+        z = zone()
+        z.dns1 = "8.8.8.8"
+        z.dns2 = "8.8.4.4"
+        z.internaldns1 = "192.168.110.254"
+        z.internaldns2 = "192.168.110.253"
+        z.name = "test"+str(l)
+        z.networktype = 'Basic'
+        z.securitygroupenabled = 'True'
+
+        #If security groups are reqd
+        sgprovider = provider()
+        sgprovider.broadcastdomainrange = 'Pod'
+        sgprovider.name = 'SecurityGroupProvider'
+
+        pn = physical_network()
+        pn.name = "test-network"
+        pn.traffictypes = [traffictype("Guest"), traffictype("Management")]
+        pn.providers.append(sgprovider)
+
+        z.physical_networks.append(pn)
+
+        '''create 10 pods'''
+        for i in range(2):
+            p = pod()
+            p.name = "test" + str(l) + str(i)
+            p.gateway = "192.168.%d.1" % i
+            p.netmask = "255.255.255.0"
+            p.startip = "192.168.%d.150" % i
+            p.endip = "192.168.%d.220" % i
+
+            '''add two pod guest ip ranges'''
+            for j in range(2):
+                ip = iprange()
+                ip.gateway = p.gateway
+                ip.netmask = p.netmask
+                ip.startip = "192.168.%d.%d" % (i, j*20)
+                ip.endip = "192.168.%d.%d" % (i, j*20+10)
+
+                p.guestIpRanges.append(ip)
+
+            '''add 10 clusters'''
+            for j in range(2):
+                c = cluster()
+                c.clustername = "test"+str(l)+str(i) + str(j)
+                c.clustertype = "CloudManaged"
+                c.hypervisor = "Simulator"
+
+                '''add 10 hosts'''
+                for k in range(2):
+                    h = host()
+                    h.username = "root"
+                    h.password = "password"
+                    memory = 8*1024*1024*1024
+                    localstorage = 1*1024*1024*1024*1024
+                    h.url = "http://sim/%d%d%d%d" % (l, i, j, k)
+                    c.hosts.append(h)
+
+                '''add 2 primary storages'''
+                for m in range(2):
+                    primary = primaryStorage()
+                    primary.name = "primary"+str(l) + str(i) + str(j) + str(m)
+                    primary.url = "nfs://localhost/path%s" % (str(l) + str(i) +
+                                                              str(j) + str(m))
+                    c.primaryStorages.append(primary)
+
+                p.clusters.append(c)
+
+            z.pods.append(p)
+
+        '''add two secondary'''
+        for i in range(5):
+            secondary = secondaryStorage()
+            secondary.url = "nfs://localhost/path"+str(l) + str(i)
+            z.secondaryStorages.append(secondary)
+
+        zs.zones.append(z)
+
+    '''Add one mgt server'''
+    mgt = managementServer()
+    mgt.mgtSvrIp = "localhost"
+    zs.mgtSvr.append(mgt)
+
+    '''Add a database'''
+    db = dbServer()
+    db.dbSvr = "localhost"
+
+    zs.dbSvr = db
+
+    '''add global configuration'''
+    global_settings = {'expunge.delay': '60',
+                       'expunge.interval': '60',
+                       'expunge.workers': '3',
+                       }
+    for k, v in global_settings.iteritems():
+        cfg = configuration()
+        cfg.name = k
+        cfg.value = v
+        zs.globalConfig.append(cfg)
+
+    ''''add loggers'''
+    testClientLogger = logger()
+    testClientLogger.name = "TestClient"
+    testClientLogger.file = "/tmp/testclient.log"
+
+    testCaseLogger = logger()
+    testCaseLogger.name = "TestCase"
+    testCaseLogger.file = "/tmp/testcase.log"
+
+    zs.logger.append(testClientLogger)
+    zs.logger.append(testCaseLogger)
+
+    return zs
+
+
+def describe_setup_in_eip_mode():
+    """
+    Setting up an EIP/ELB enabled zone with netscaler provider
+    """
+    zs = cloudstackConfiguration()
+
+    for l in range(1):
+        z = zone()
+        z.dns1 = "8.8.8.8"
+        z.dns2 = "8.8.4.4"
+        z.internaldns1 = "192.168.110.254"
+        z.internaldns2 = "192.168.110.253"
+        z.name = "test"+str(l)
+        z.networktype = 'Basic'
+
+        ips = iprange()
+        ips.vlan = "49"
+        ips.startip = "10.147.49.200"
+        ips.endip = "10.147.49.250"
+        ips.gateway = "10.147.49.1"
+        ips.netmask = "255.255.255.0"
+        z.ipranges.append(ips)
+
+        #If security groups are reqd
+        sgprovider = provider()
+        sgprovider.broadcastdomainrange = 'Pod'
+        sgprovider.name = 'SecurityGroupProvider'
+
+        nsprovider = provider()
+        nsprovider.name = 'Netscaler'
+        ns = netscaler()
+        ns.hostname = '10.147.40.100'
+        nsprovider.devices.append(ns)
+
+        pn = physical_network()
+        pn.name = "test-network"
+        pn.traffictypes = [traffictype("Guest",
+                                       {"xen": "cloud-guest"}),
+                           traffictype("Management"),
+                           traffictype("Public", {"xen": "cloud-public"})]
+        pn.providers.extend([sgprovider, nsprovider])
+        z.physical_networks.append(pn)
+
+        '''create 10 pods'''
+        for i in range(2):
+            p = pod()
+            p.name = "test" + str(l) + str(i)
+            p.gateway = "192.168.%d.1" % i
+            p.netmask = "255.255.255.0"
+            p.startip = "192.168.%d.150" % i
+            p.endip = "192.168.%d.220" % i
+
+            '''add two pod guest ip ranges'''
+            for j in range(2):
+                ip = iprange()
+                ip.gateway = p.gateway
+                ip.netmask = p.netmask
+                ip.startip = "192.168.%d.%d" % (i, j*20)
+                ip.endip = "192.168.%d.%d" % (i, j*20+10)
+
+                p.guestIpRanges.append(ip)
+
+            '''add 10 clusters'''
+            for j in range(2):
+                c = cluster()
+                c.clustername = "test"+str(l)+str(i) + str(j)
+                c.clustertype = "CloudManaged"
+                c.hypervisor = "Simulator"
+
+                '''add 10 hosts'''
+                for k in range(2):
+                    h = host()
+                    h.username = "root"
+                    h.password = "password"
+                    h.url = "http://Sim/%d%d%d%d" % (l, i, j, k)
+                    c.hosts.append(h)
+
+                '''add 2 primary storages'''
+                for m in range(2):
+                    primary = primaryStorage()
+                    primary.name = "primary"+str(l) + str(i) + str(j) + str(m)
+                    primary.url = "nfs://localhost/path%s" % (str(l) + str(i)
+                                                              + str(j)
+                                                              + str(m))
+                    c.primaryStorages.append(primary)
+
+                p.clusters.append(c)
+
+            z.pods.append(p)
+
+        '''add two secondary'''
+        for i in range(5):
+            secondary = secondaryStorage()
+            secondary.url = "nfs://localhost/path"+str(l) + str(i)
+            z.secondaryStorages.append(secondary)
+
+        zs.zones.append(z)
+
+    '''Add one mgt server'''
+    mgt = managementServer()
+    mgt.mgtSvrIp = "localhost"
+    zs.mgtSvr.append(mgt)
+
+    '''Add a database'''
+    db = dbServer()
+    db.dbSvr = "localhost"
+
+    zs.dbSvr = db
+
+    '''add global configuration'''
+    global_settings = {'expunge.delay': '60',
+                       'expunge.interval': '60',
+                       'expunge.workers': '3',
+                       }
+    for k, v in global_settings.iteritems():
+        cfg = configuration()
+        cfg.name = k
+        cfg.value = v
+        zs.globalConfig.append(cfg)
+
+    ''''add loggers'''
+    testClientLogger = logger()
+    testClientLogger.name = "TestClient"
+    testClientLogger.file = "/tmp/testclient.log"
+
+    testCaseLogger = logger()
+    testCaseLogger.name = "TestCase"
+    testCaseLogger.file = "/tmp/testcase.log"
+
+    zs.logger.append(testClientLogger)
+    zs.logger.append(testCaseLogger)
+
+    return zs
+
+
+def describe_setup_in_advanced_mode():
+    '''sample code to generate setup configuration file'''
+    zs = cloudstackConfiguration()
+
+    for l in range(1):
+        z = zone()
+        z.dns1 = "8.8.8.8"
+        z.dns2 = "8.8.4.4"
+        z.internaldns1 = "192.168.110.254"
+        z.internaldns2 = "192.168.110.253"
+        z.name = "test"+str(l)
+        z.networktype = 'Advanced'
+        z.guestcidraddress = "10.1.1.0/24"
+        z.vlan = "100-2000"
+
+        pn = physical_network()
+        pn.name = "test-network"
+        pn.traffictypes = [traffictype("Guest"), traffictype("Management"),
+                           traffictype("Public")]
+
+        vpcprovider = provider('VpcVirtualRouter')
+
+        nsprovider = provider('Netscaler')
+        nsprovider.devices.append(netscaler(hostname='10.147.40.100'))
+
+        srxprovider = provider('JuniperSRX')
+        srxprovider.devices.append(srx(hostname='10.147.40.3'))
+
+        f5provider = provider('F5BigIp')
+        f5provider.devices.append(bigip(hostname='10.147.40.3'))
+
+        pn.providers.extend([vpcprovider, nsprovider, srxprovider, f5provider])
+        z.physical_networks.append(pn)
+
+        '''create 10 pods'''
+        for i in range(2):
+            p = pod()
+            p.name = "test" + str(l) + str(i)
+            p.gateway = "192.168.%d.1" % i
+            p.netmask = "255.255.255.0"
+            p.startip = "192.168.%d.200" % i
+            p.endip = "192.168.%d.220" % i
+
+            '''add 10 clusters'''
+            for j in range(2):
+                c = cluster()
+                c.clustername = "test"+str(l)+str(i) + str(j)
+                c.clustertype = "CloudManaged"
+                c.hypervisor = "Simulator"
+
+                '''add 10 hosts'''
+                for k in range(2):
+                    h = host()
+                    h.username = "root"
+                    h.password = "password"
+                    memory = 8 * 1024 * 1024 * 1024
+                    localstorage = 1 * 1024 * 1024 * 1024 * 1024
+                    #h.url = "http://sim/%d%d%d%d/cpucore=1&cpuspeed=8000&\
+                    #    memory=%d&localstorage=%d"%(l, i, j, k, memory,
+                    #                                localstorage)
+                    h.url = "http://sim/%d%d%d%d" % (l, i, j, k)
+                    c.hosts.append(h)
+
+                '''add 2 primary storages'''
+                for m in range(2):
+                    primary = primaryStorage()
+                    primary.name = "primary"+str(l) + str(i) + str(j) + str(m)
+                    #primary.url = "nfs://localhost/path%s/size=%d" %
+                    #    (str(l) + str(i) + str(j) + str(m), size)
+                    primary.url = "nfs://localhost/path%s" % (str(l) + str(i)
+                                                              + str(j)
+                                                              + str(m))
+                    c.primaryStorages.append(primary)
+
+                p.clusters.append(c)
+
+            z.pods.append(p)
+
+        '''add two secondary'''
+        for i in range(5):
+            secondary = secondaryStorage()
+            secondary.url = "nfs://localhost/path"+str(l) + str(i)
+            z.secondaryStorages.append(secondary)
+
+        '''add default public network'''
+        ips = iprange()
+        ips.vlan = "26"
+        ips.startip = "172.16.26.2"
+        ips.endip = "172.16.26.100"
+        ips.gateway = "172.16.26.1"
+        ips.netmask = "255.255.255.0"
+        z.ipranges.append(ips)
+
+        zs.zones.append(z)
+
+    '''Add one mgt server'''
+    mgt = managementServer()
+    mgt.mgtSvrIp = "localhost"
+    zs.mgtSvr.append(mgt)
+
+    '''Add a database'''
+    db = dbServer()
+    db.dbSvr = "localhost"
+
+    zs.dbSvr = db
+
+    '''add global configuration'''
+    global_settings = {'expunge.delay': '60',
+                       'expunge.interval': '60',
+                       'expunge.workers': '3',
+                       }
+    for k, v in global_settings.iteritems():
+        cfg = configuration()
+        cfg.name = k
+        cfg.value = v
+        zs.globalConfig.append(cfg)
+
+    ''''add loggers'''
+    testClientLogger = logger()
+    testClientLogger.name = "TestClient"
+    testClientLogger.file = "/tmp/testclient.log"
+
+    testCaseLogger = logger()
+    testCaseLogger.name = "TestCase"
+    testCaseLogger.file = "/tmp/testcase.log"
+
+    zs.logger.append(testClientLogger)
+    zs.logger.append(testCaseLogger)
+
+    return zs
+
+'''sample code to generate setup configuration file'''
+
+
+def describe_setup_in_advancedsg_mode():
+    zs = cloudstackConfiguration()
+
+    for l in range(1):
+        z = zone()
+        z.dns1 = "8.8.8.8"
+        z.dns2 = "8.8.4.4"
+        z.internaldns1 = "192.168.110.254"
+        z.internaldns2 = "192.168.110.253"
+        z.name = "test"+str(l)
+        z.networktype = 'Advanced'
+        z.vlan = "100-2000"
+        z.securitygroupenabled = "true"
+
+        pn = physical_network()
+        pn.name = "test-network"
+        pn.traffictypes = [traffictype("Guest"), traffictype("Management")]
+
+        #If security groups are reqd
+        sgprovider = provider()
+        sgprovider.broadcastdomainrange = 'ZONE'
+        sgprovider.name = 'SecurityGroupProvider'
+
+        pn.providers.append(sgprovider)
+        z.physical_networks.append(pn)
+
+        '''create 10 pods'''
+        for i in range(2):
+            p = pod()
+            p.name = "test" + str(l) + str(i)
+            p.gateway = "192.168.%d.1" % i
+            p.netmask = "255.255.255.0"
+            p.startip = "192.168.%d.200" % i
+            p.endip = "192.168.%d.220" % i
+
+            '''add 10 clusters'''
+            for j in range(2):
+                c = cluster()
+                c.clustername = "test"+str(l)+str(i) + str(j)
+                c.clustertype = "CloudManaged"
+                c.hypervisor = "Simulator"
+
+                '''add 10 hosts'''
+                for k in range(2):
+                    h = host()
+                    h.username = "root"
+                    h.password = "password"
+                    memory = 8 * 1024 * 1024 * 1024
+                    localstorage = 1 * 1024 * 1024 * 1024 * 1024
+                    #h.url = "http://sim/%d%d%d%d/cpucore=1&cpuspeed=8000&\
+                        #memory=%d&localstorage=%d" % (l, i, j, k, memory,
+                        #localstorage)
+                    h.url = "http://sim/%d%d%d%d" % (l, i, j, k)
+                    c.hosts.append(h)
+
+                '''add 2 primary storages'''
+                for m in range(2):
+                    primary = primaryStorage()
+                    primary.name = "primary"+str(l) + str(i) + str(j) + str(m)
+                    #primary.url = "nfs://localhost/path%s/size=%d" % \
+                        #(str(l) + str(i) + str(j) + str(m), size)
+                    primary.url = "nfs://localhost/path%s" % \
+                        (str(l) + str(i) + str(j) + str(m))
+                    c.primaryStorages.append(primary)
+
+                p.clusters.append(c)
+
+            z.pods.append(p)
+
+        '''add two secondary'''
+        for i in range(5):
+            secondary = secondaryStorage()
+            secondary.url = "nfs://localhost/path"+str(l) + str(i)
+            z.secondaryStorages.append(secondary)
+
+        '''add default guest network'''
+        ips = iprange()
+        ips.vlan = "26"
+        ips.startip = "172.16.26.2"
+        ips.endip = "172.16.26.100"
+        ips.gateway = "172.16.26.1"
+        ips.netmask = "255.255.255.0"
+        z.ipranges.append(ips)
+
+        zs.zones.append(z)
+
+    '''Add one mgt server'''
+    mgt = managementServer()
+    mgt.mgtSvrIp = "localhost"
+    zs.mgtSvr.append(mgt)
+
+    '''Add a database'''
+    db = dbServer()
+    db.dbSvr = "localhost"
+
+    zs.dbSvr = db
+
+    '''add global configuration'''
+    global_settings = {'expunge.delay': '60',
+                       'expunge.interval': '60',
+                       'expunge.workers': '3',
+                       }
+    for k, v in global_settings.iteritems():
+        cfg = configuration()
+        cfg.name = k
+        cfg.value = v
+        zs.globalConfig.append(cfg)
+
+    ''''add loggers'''
+    testClientLogger = logger()
+    testClientLogger.name = "TestClient"
+    testClientLogger.file = "/tmp/testclient.log"
+
+    testCaseLogger = logger()
+    testCaseLogger.name = "TestCase"
+    testCaseLogger.file = "/tmp/testcase.log"
+
+    zs.logger.append(testClientLogger)
+    zs.logger.append(testCaseLogger)
+
+    return zs
+
+
+def generate_setup_config(config, file=None):
+    describe = config
+    if file is None:
+        return json.dumps(jsonHelper.jsonDump.dump(describe))
+    else:
+        fp = open(file, 'w')
+        json.dump(jsonHelper.jsonDump.dump(describe), fp, indent=4)
+        fp.close()
+
+
+def get_setup_config(file):
+    if not os.path.exists(file):
+        raise IOError("config file %s not found. \
+please specify a valid config file" % file)
+    config = cloudstackConfiguration()
+    configLines = []
+    with open(file, 'r') as fp:
+        for line in fp:
+            ws = line.strip()
+            if not ws.startswith("#"):
+                configLines.append(ws)
+    config = json.loads("\n".join(configLines))
+    return jsonHelper.jsonLoader(config)
+
+if __name__ == "__main__":
+    parser = OptionParser()
+
+    parser.add_option("-i", "--input", action="store", default=None,
+                      dest="inputfile", help="input file")
+    parser.add_option("-a", "--advanced", action="store_true", default=False,
+                      dest="advanced", help="use advanced networking")
+    parser.add_option("-s", "--advancedsg", action="store_true", default=False,
+                      dest="advancedsg", help="use advanced networking \
+with security groups")
+    parser.add_option("-o", "--output", action="store",
+                      default="./datacenterCfg", dest="output",
+                      help="the path where the json config file generated, \
+by default is ./datacenterCfg")
+
+    (options, args) = parser.parse_args()
+
+    if options.inputfile:
+        config = get_setup_config(options.inputfile)
+    if options.advanced:
+        config = describe_setup_in_advanced_mode()
+    elif options.advancedsg:
+        config = describe_setup_in_advancedsg_mode()
+    else:
+        config = describe_setup_in_basic_mode()
+
+    generate_setup_config(config, options.output)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ec00a6fb/tools/marvin/build/lib/marvin/dbConnection.py
----------------------------------------------------------------------
diff --git a/tools/marvin/build/lib/marvin/dbConnection.py b/tools/marvin/build/lib/marvin/dbConnection.py
new file mode 100644
index 0000000..99014ab
--- /dev/null
+++ b/tools/marvin/build/lib/marvin/dbConnection.py
@@ -0,0 +1,86 @@
+# 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.
+
+import mysql
+import contextlib
+from mysql import connector
+from mysql.connector import errors
+from contextlib import closing
+import cloudstackException
+import sys
+import os
+
+
+class dbConnection(object):
+    def __init__(self, host="localhost", port=3306, user='cloud',
+                 passwd='cloud', db='cloud'):
+        self.host = host
+        self.port = port
+        self.user = str(user)  # Workaround: http://bugs.mysql.com/?id=67306
+        self.passwd = passwd
+        self.database = db
+
+    def execute(self, sql=None, params=None):
+        if sql is None:
+            return None
+
+        resultRow = []
+        with contextlib.\
+            closing(mysql.connector.connect(host=str(self.host),
+                                            port=int(self.port),
+                                            user=str(self.user),
+                                            password=str(self.passwd),
+                                            db=str(self.database))) as conn:
+            conn.autocommit = True
+            with contextlib.closing(conn.cursor(buffered=True)) as cursor:
+                cursor.execute(sql, params)
+                try:
+                    resultRow = cursor.fetchall()
+                except errors.InterfaceError:
+                    #Raised on empty result - DML
+                    resultRow = []
+        return resultRow
+
+    def executeSqlFromFile(self, fileName=None):
+        if fileName is None:
+            raise cloudstackException.\
+                InvalidParameterException("file can't not none")
+
+        if not os.path.exists(fileName):
+            raise cloudstackException.\
+                InvalidParameterException("%s not exists" % fileName)
+
+        sqls = open(fileName, "r").read()
+        return self.execute(sqls)
+
+if __name__ == "__main__":
+    db = dbConnection()
+    '''
+    try:
+
+        result = db.executeSqlFromFile("/tmp/server-setup.sql")
+        if result is not None:
+            for r in result:
+                print r[0], r[1]
+    except cloudstackException.dbException, e:
+        print e
+    '''
+    print db.execute("update vm_template set name='fjkd' where id=200")
+    for i in range(10):
+        result = db.execute("select job_status, created, \
+last_updated from async_job where id=%d" % i)
+        print result