You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ed...@apache.org on 2014/09/23 03:10:27 UTC

[3/7] add more libs

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/serviceoffering.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/serviceoffering.py b/tools/marvin/marvin/lib/cloudstack/serviceoffering.py
new file mode 100644
index 0000000..5191134
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/serviceoffering.py
@@ -0,0 +1,99 @@
+# 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.
+
+from marvin.cloudstackAPI import createServiceOffering,deleteServiceOffering,listServiceOfferings
+class ServiceOffering:
+
+    """Manage service offerings cycle"""
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, services, domainid=None, **kwargs):
+        """Create Service offering"""
+        cmd = createServiceOffering.createServiceOfferingCmd()
+        cmd.cpunumber = services["cpunumber"]
+        cmd.cpuspeed = services["cpuspeed"]
+        cmd.displaytext = services["displaytext"]
+        cmd.memory = services["memory"]
+        cmd.name = services["name"]
+        if "storagetype" in services:
+            cmd.storagetype = services["storagetype"]
+
+        if "systemvmtype" in services:
+            cmd.systemvmtype = services['systemvmtype']
+
+        if "issystem" in services:
+            cmd.issystem = services['issystem']
+
+        if "tags" in services:
+            cmd.tags = services["tags"]
+
+        if "hosttags" in services:
+            cmd.hosttags = services["hosttags"]
+
+        if "deploymentplanner" in services:
+            cmd.deploymentplanner = services["deploymentplanner"]
+
+        if "serviceofferingdetails" in services:
+            count = 1
+            for i in services["serviceofferingdetails"]:
+                for key, value in i.items():
+                    setattr(cmd, "serviceofferingdetails[%d].key" % count, key)
+                    setattr(cmd, "serviceofferingdetails[%d].value" % count, value)
+                count = count + 1
+
+        if "isvolatile" in services:
+            cmd.isvolatile = services["isvolatile"]
+
+        if "miniops" in services:
+            cmd.miniops = services["miniops"]
+
+        if "maxiops" in services:
+            cmd.maxiops = services["maxiops"]
+
+        if "customizediops" in services:
+            cmd.customizediops = services["customizediops"]
+
+        if "offerha" in services:
+            cmd.offerha = services["offerha"]
+
+        # Service Offering private to that domain
+        if domainid:
+            cmd.domainid = domainid
+
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return ServiceOffering(apiclient.createServiceOffering(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete Service offering"""
+        cmd = deleteServiceOffering.deleteServiceOfferingCmd()
+        cmd.id = self.id
+        apiclient.deleteServiceOffering(cmd)
+        return
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """Lists all available service offerings."""
+
+        cmd = listServiceOfferings.listServiceOfferingsCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listServiceOfferings(cmd))
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/snapshot.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/snapshot.py b/tools/marvin/marvin/lib/cloudstack/snapshot.py
new file mode 100644
index 0000000..b3f10b9
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/snapshot.py
@@ -0,0 +1,125 @@
+# 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 time
+
+from marvin.cloudstackAPI import createSnapshot,deleteSnapshot,listSnapshots,listSnapshotPolicies,createSnapshotPolicy,deleteSnapshotPolicies
+from marvin.lib.cloudstack.utils import  validateList
+from marvin.codes import BACKED_UP,BACKING_UP,FAIL,PASS
+
+
+class Snapshot:
+    """Manage Snapshot Lifecycle
+    """
+    '''Class level variables'''
+    # Variables denoting possible Snapshot states - start
+    BACKED_UP = BACKED_UP
+    BACKING_UP = BACKING_UP
+    # Variables denoting possible Snapshot states - end
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, volume_id, account=None,
+               domainid=None, projectid=None):
+        """Create Snapshot"""
+        cmd = createSnapshot.createSnapshotCmd()
+        cmd.volumeid = volume_id
+        if account:
+            cmd.account = account
+        if domainid:
+            cmd.domainid = domainid
+        if projectid:
+            cmd.projectid = projectid
+        return Snapshot(apiclient.createSnapshot(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete Snapshot"""
+        cmd = deleteSnapshot.deleteSnapshotCmd()
+        cmd.id = self.id
+        apiclient.deleteSnapshot(cmd)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """List all snapshots matching criteria"""
+
+        cmd = listSnapshots.listSnapshotsCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listSnapshots(cmd))
+
+    def validateState(self, apiclient, snapshotstate, timeout=600):
+        """Check if snapshot is in required state
+           returnValue: List[Result, Reason]
+                 @Result: PASS if snapshot is in required state,
+                          else FAIL
+                 @Reason: Reason for failure in case Result is FAIL
+        """
+        isSnapshotInRequiredState = False
+        try:
+            while timeout >= 0:
+                snapshots = Snapshot.list(apiclient, id=self.id)
+                assert validateList(snapshots)[0] == PASS, "snapshots list\
+                        validation failed"
+                if str(snapshots[0].state).lower() == snapshotstate:
+                    isSnapshotInRequiredState = True
+                    break
+                timeout -= 60
+                time.sleep(60)
+            #end while
+            if isSnapshotInRequiredState:
+                return[PASS, None]
+            else:
+                raise Exception("Snapshot not in required state")
+        except Exception as e:
+            return [FAIL, e]
+
+class SnapshotPolicy:
+    """Manage snapshot policies"""
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, volumeid, services):
+        """Create Snapshot policy"""
+        cmd = createSnapshotPolicy.createSnapshotPolicyCmd()
+        cmd.intervaltype = services["intervaltype"]
+        cmd.maxsnaps = services["maxsnaps"]
+        cmd.schedule = services["schedule"]
+        cmd.timezone = services["timezone"]
+        cmd.volumeid = volumeid
+        return SnapshotPolicy(apiclient.createSnapshotPolicy(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete Snapshot policy"""
+        cmd = deleteSnapshotPolicies.deleteSnapshotPoliciesCmd()
+        cmd.id = self.id
+        apiclient.deleteSnapshotPolicies(cmd)
+        return
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """Lists snapshot policies."""
+
+        cmd = listSnapshotPolicies.listSnapshotPoliciesCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listSnapshotPolicies(cmd))

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/storagepool.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/storagepool.py b/tools/marvin/marvin/lib/cloudstack/storagepool.py
new file mode 100644
index 0000000..1836c26
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/storagepool.py
@@ -0,0 +1,122 @@
+# 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.
+
+from marvin.cloudstackAPI import createStoragePool,enableStorageMaintenance,deleteStoragePool,listStoragePools,findStoragePoolsForMigration
+import time
+class StoragePool:
+    """Manage Storage pools (Primary Storage)"""
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, services, scope=None, clusterid=None,
+               zoneid=None, podid=None, provider=None, tags=None,
+               capacityiops=None, capacitybytes=None, hypervisor=None):
+        """Create Storage pool (Primary Storage)"""
+
+        cmd = createStoragePool.createStoragePoolCmd()
+        cmd.name = services["name"]
+
+        if podid:
+            cmd.podid = podid
+        elif "podid" in services:
+            cmd.podid = services["podid"]
+
+        cmd.url = services["url"]
+        if clusterid:
+            cmd.clusterid = clusterid
+        elif "clusterid" in services:
+            cmd.clusterid = services["clusterid"]
+
+        if zoneid:
+            cmd.zoneid = zoneid
+        else:
+            cmd.zoneid = services["zoneid"]
+
+        if scope:
+            cmd.scope = scope
+        elif "scope" in services:
+            cmd.scope = services["scope"]
+
+        if provider:
+            cmd.provider = provider
+        elif "provider" in services:
+            cmd.provider = services["provider"]
+
+        if tags:
+            cmd.tags = tags
+        elif "tags" in services:
+            cmd.tags = services["tags"]
+
+        if capacityiops:
+            cmd.capacityiops = capacityiops
+        elif "capacityiops" in services:
+            cmd.capacityiops = services["capacityiops"]
+
+        if capacitybytes:
+            cmd.capacitybytes = capacitybytes
+        elif "capacitybytes" in services:
+            cmd.capacitybytes = services["capacitybytes"]
+
+        if hypervisor:
+            cmd.hypervisor = hypervisor
+        elif "hypervisor" in services:
+            cmd.hypervisor = services["hypervisor"]
+
+        return StoragePool(apiclient.createStoragePool(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete Storage pool (Primary Storage)"""
+
+        # Storage pool must be in maintenance mode before deletion
+        cmd = enableStorageMaintenance.enableStorageMaintenanceCmd()
+        cmd.id = self.id
+        apiclient.enableStorageMaintenance(cmd)
+        time.sleep(30)
+        cmd = deleteStoragePool.deleteStoragePoolCmd()
+        cmd.id = self.id
+        apiclient.deleteStoragePool(cmd)
+        return
+
+    def enableMaintenance(self, apiclient):
+        """enables maintenance mode Storage pool"""
+
+        cmd = enableStorageMaintenance.enableStorageMaintenanceCmd()
+        cmd.id = self.id
+        return apiclient.enableStorageMaintenance(cmd)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """List all storage pools matching criteria"""
+
+        cmd = listStoragePools.listStoragePoolsCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listStoragePools(cmd))
+
+    @classmethod
+    def listForMigration(cls, apiclient, **kwargs):
+        """List all storage pools for migration matching criteria"""
+
+        cmd = findStoragePoolsForMigration.findStoragePoolsForMigrationCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.findStoragePoolsForMigration(cmd))
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/template.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/template.py b/tools/marvin/marvin/lib/cloudstack/template.py
new file mode 100644
index 0000000..ca5a228
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/template.py
@@ -0,0 +1,277 @@
+# 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 time
+
+from marvin.cloudstackAPI import createTemplate,listOsTypes,registerTemplate,extractTemplate,deleteTemplate,updateTemplatePermissions,copyTemplate,updateTemplate,listTemplates
+from marvin.lib.cloudstack.utils import random_gen
+
+
+class Template:
+    """Manage template life cycle"""
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, services, volumeid=None,
+               account=None, domainid=None, projectid=None):
+        """Create template from Volume"""
+        # Create template from Virtual machine and Volume ID
+        cmd = createTemplate.createTemplateCmd()
+        cmd.displaytext = services["displaytext"]
+        cmd.name = "-".join([services["name"], random_gen()])
+        if "ostypeid" in services:
+            cmd.ostypeid = services["ostypeid"]
+        elif "ostype" in services:
+            # Find OSTypeId from Os type
+            sub_cmd = listOsTypes.listOsTypesCmd()
+            sub_cmd.description = services["ostype"]
+            ostypes = apiclient.listOsTypes(sub_cmd)
+
+            if not isinstance(ostypes, list):
+                raise Exception(
+                    "Unable to find Ostype id with desc: %s" %
+                    services["ostype"])
+            cmd.ostypeid = ostypes[0].id
+        else:
+            raise Exception(
+                "Unable to find Ostype is required for creating template")
+
+        cmd.isfeatured = services[
+            "isfeatured"] if "isfeatured" in services else False
+        cmd.ispublic = services[
+            "ispublic"] if "ispublic" in services else False
+        cmd.isextractable = services[
+            "isextractable"] if "isextractable" in services else False
+        cmd.passwordenabled = services[
+            "passwordenabled"] if "passwordenabled" in services else False
+
+        if volumeid:
+            cmd.volumeid = volumeid
+
+        if account:
+            cmd.account = account
+
+        if domainid:
+            cmd.domainid = domainid
+
+        if projectid:
+            cmd.projectid = projectid
+        return Template(apiclient.createTemplate(cmd).__dict__)
+
+    @classmethod
+    def register(cls, apiclient, services, zoneid=None,
+                 account=None, domainid=None, hypervisor=None,
+                 projectid=None):
+        """Create template from URL"""
+
+        # Create template from Virtual machine and Volume ID
+        cmd = registerTemplate.registerTemplateCmd()
+        cmd.displaytext = services["displaytext"]
+        cmd.name = "-".join([services["name"], random_gen()])
+        cmd.format = services["format"]
+        if hypervisor:
+            cmd.hypervisor = hypervisor
+        elif "hypervisor" in services:
+            cmd.hypervisor = services["hypervisor"]
+
+        if "ostypeid" in services:
+            cmd.ostypeid = services["ostypeid"]
+        elif "ostype" in services:
+            # Find OSTypeId from Os type
+            sub_cmd = listOsTypes.listOsTypesCmd()
+            sub_cmd.description = services["ostype"]
+            ostypes = apiclient.listOsTypes(sub_cmd)
+
+            if not isinstance(ostypes, list):
+                raise Exception(
+                    "Unable to find Ostype id with desc: %s" %
+                    services["ostype"])
+            cmd.ostypeid = ostypes[0].id
+        else:
+            raise Exception(
+                "Unable to find Ostype is required for registering template")
+
+        cmd.url = services["url"]
+
+        if zoneid:
+            cmd.zoneid = zoneid
+        else:
+            cmd.zoneid = services["zoneid"]
+
+        cmd.isfeatured = services[
+            "isfeatured"] if "isfeatured" in services else False
+        cmd.ispublic = services[
+            "ispublic"] if "ispublic" in services else False
+        cmd.isextractable = services[
+            "isextractable"] if "isextractable" in services else False
+        cmd.passwordenabled = services[
+            "passwordenabled"] if "passwordenabled" in services else False
+
+        if account:
+            cmd.account = account
+
+        if domainid:
+            cmd.domainid = domainid
+
+        if projectid:
+            cmd.projectid = projectid
+        elif "projectid" in services:
+            cmd.projectid = services["projectid"]
+
+        # Register Template
+        template = apiclient.registerTemplate(cmd)
+
+        if isinstance(template, list):
+            return Template(template[0].__dict__)
+
+    @classmethod
+    def extract(cls, apiclient, id, mode, zoneid=None):
+        "Extract template "
+
+        cmd = extractTemplate.extractTemplateCmd()
+        cmd.id = id
+        cmd.mode = mode
+        cmd.zoneid = zoneid
+
+        return apiclient.extractTemplate(cmd)
+
+    @classmethod
+    def create_from_snapshot(cls, apiclient, snapshot, services,
+                             random_name=True):
+        """Create Template from snapshot"""
+        # Create template from Virtual machine and Snapshot ID
+        cmd = createTemplate.createTemplateCmd()
+        cmd.displaytext = services["displaytext"]
+        cmd.name = "-".join([
+            services["name"],
+            random_gen()
+        ]) if random_name else services["name"]
+
+        if "ostypeid" in services:
+            cmd.ostypeid = services["ostypeid"]
+        elif "ostype" in services:
+            # Find OSTypeId from Os type
+            sub_cmd = listOsTypes.listOsTypesCmd()
+            sub_cmd.description = services["ostype"]
+            ostypes = apiclient.listOsTypes(sub_cmd)
+
+            if not isinstance(ostypes, list):
+                raise Exception(
+                    "Unable to find Ostype id with desc: %s" %
+                    services["ostype"])
+            cmd.ostypeid = ostypes[0].id
+        else:
+            raise Exception(
+                "Unable to find Ostype is required for creating template")
+
+        cmd.snapshotid = snapshot.id
+        return Template(apiclient.createTemplate(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete Template"""
+
+        cmd = deleteTemplate.deleteTemplateCmd()
+        cmd.id = self.id
+        apiclient.deleteTemplate(cmd)
+
+    def download(self, apiclient, timeout=5, interval=60):
+        """Download Template"""
+        # Sleep to ensure template is in proper state before download
+        time.sleep(interval)
+
+        while True:
+            template_response = Template.list(
+                apiclient,
+                id=self.id,
+                zoneid=self.zoneid,
+                templatefilter='self'
+            )
+            if isinstance(template_response, list):
+
+                template = template_response[0]
+                # If template is ready,
+                # template.status = Download Complete
+                # Downloading - x% Downloaded
+                # Error - Any other string
+                if template.status == 'Download Complete':
+                    break
+
+                elif 'Downloaded' in template.status:
+                    time.sleep(interval)
+
+                elif 'Installing' not in template.status:
+                    raise Exception(
+                        "Error in downloading template: status - %s" %
+                        template.status)
+
+            elif timeout == 0:
+                break
+
+            else:
+                time.sleep(interval)
+                timeout = timeout - 1
+        return
+
+    def updatePermissions(self, apiclient, **kwargs):
+        """Updates the template permissions"""
+
+        cmd = updateTemplatePermissions.updateTemplatePermissionsCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return(apiclient.updateTemplatePermissions(cmd))
+
+    def update(self, apiclient, **kwargs):
+        """Updates the template details"""
+
+        cmd = updateTemplate.updateTemplateCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return(apiclient.updateTemplate(cmd))
+
+    @classmethod
+    def copy(cls, apiclient, id, sourcezoneid, destzoneid):
+        "Copy Template from source Zone to Destination Zone"
+
+        cmd = copyTemplate.copyTemplateCmd()
+        cmd.id = id
+        cmd.sourcezoneid = sourcezoneid
+        cmd.destzoneid = destzoneid
+
+        return apiclient.copyTemplate(cmd)
+
+    def copy(self, apiclient, sourcezoneid, destzoneid):
+        "Copy Template from source Zone to Destination Zone"
+
+        cmd = copyTemplate.copyTemplateCmd()
+        cmd.id = self.id
+        cmd.sourcezoneid = sourcezoneid
+        cmd.destzoneid = destzoneid
+
+        return apiclient.copyTemplate(cmd)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """List all templates matching criteria"""
+
+        cmd = listTemplates.listTemplatesCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listTemplates(cmd))
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/user.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/user.py b/tools/marvin/marvin/lib/cloudstack/user.py
new file mode 100644
index 0000000..7ae54c3
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/user.py
@@ -0,0 +1,95 @@
+# 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.
+from marvin.cloudstackAPI import createUser,deleteUser,listUsers,registerUserKeys,updateUser,login
+from marvin.lib.cloudstack.utils import random_gen
+class User:
+    """ User Life Cycle """
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, services, account, domainid):
+        cmd = createUser.createUserCmd()
+        """Creates an user"""
+
+        cmd.account = account
+        cmd.domainid = domainid
+        cmd.email = services["email"]
+        cmd.firstname = services["firstname"]
+        cmd.lastname = services["lastname"]
+
+        if "userUUID" in services:
+            cmd.userid = "-".join([services["userUUID"], random_gen()])
+
+        cmd.password = services["password"]
+        cmd.username = "-".join([services["username"], random_gen()])
+        user = apiclient.createUser(cmd)
+
+        return User(user.__dict__)
+
+    def delete(self, apiclient):
+        """Delete an account"""
+        cmd = deleteUser.deleteUserCmd()
+        cmd.id = self.id
+        apiclient.deleteUser(cmd)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """Lists users and provides detailed account information for
+        listed users"""
+
+        cmd = listUsers.listUsersCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listUsers(cmd))
+
+    @classmethod
+    def registerUserKeys(cls, apiclient, userid):
+        cmd = registerUserKeys.registerUserKeysCmd()
+        cmd.id = userid
+        return apiclient.registerUserKeys(cmd)
+
+    def update(self, apiclient, **kwargs):
+        """Updates the user details"""
+
+        cmd = updateUser.updateUserCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return (apiclient.updateUser(cmd))
+
+    @classmethod
+    def update(cls, apiclient, id, **kwargs):
+        """Updates the user details (class method)"""
+
+        cmd = updateUser.updateUserCmd()
+        cmd.id = id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return (apiclient.updateUser(cmd))
+
+    @classmethod
+    def login(cls, apiclient, username, password, domain=None, domainid=None):
+        """Logins to the CloudStack"""
+
+        cmd = login.loginCmd()
+        cmd.username = username
+        cmd.password = password
+        if domain:
+            cmd.domain = domain
+        if domainid:
+            cmd.domainId = domainid
+        return apiclient.login(cmd)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/utils.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/utils.py b/tools/marvin/marvin/lib/cloudstack/utils.py
new file mode 100644
index 0000000..c52e277
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/utils.py
@@ -0,0 +1,504 @@
+# 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.
+"""Utilities functions
+"""
+
+import marvin
+import os
+import time
+import logging
+import string
+import random
+import imaplib
+import email
+import socket
+import urlparse
+import datetime
+from marvin.cloudstackAPI import cloudstackAPIClient, listHosts, listRouters
+from platform import system
+from marvin.cloudstackException import GetDetailExceptionInfo
+from marvin.sshClient import SshClient
+from marvin.codes import (
+                          SUCCESS,
+                          FAIL,
+                          PASS,
+                          MATCH_NOT_FOUND,
+                          INVALID_INPUT,
+                          EMPTY_LIST,
+                          FAILED)
+
+def restart_mgmt_server(server):
+    """Restarts the management server"""
+
+    try:
+        # Get the SSH client
+        ssh = is_server_ssh_ready(
+            server["ipaddress"],
+            server["port"],
+            server["username"],
+            server["password"],
+        )
+        result = ssh.execute("/etc/init.d/cloud-management restart")
+        res = str(result)
+        # Server Stop - OK
+        # Server Start - OK
+        if res.count("OK") != 2:
+            raise ("ErrorInReboot!")
+    except Exception as e:
+        raise e
+    return
+
+
+def fetch_latest_mail(services, from_mail):
+    """Fetch mail"""
+
+    # Login to mail server to verify email
+    mail = imaplib.IMAP4_SSL(services["server"])
+    mail.login(
+        services["email"],
+        services["password"]
+    )
+    mail.list()
+    mail.select(services["folder"])
+    date = (datetime.date.today() - datetime.timedelta(1)).strftime("%d-%b-%Y")
+
+    result, data = mail.uid(
+        'search',
+        None,
+        '(SENTSINCE {date} HEADER FROM "{mail}")'.format(
+            date=date,
+            mail=from_mail
+        )
+    )
+    # Return False if email is not present
+    if data == []:
+        return False
+
+    latest_email_uid = data[0].split()[-1]
+    result, data = mail.uid('fetch', latest_email_uid, '(RFC822)')
+    raw_email = data[0][1]
+    email_message = email.message_from_string(raw_email)
+    result = get_first_text_block(email_message)
+    return result
+
+
+def get_first_text_block(email_message_instance):
+    """fetches first text block from the mail"""
+    maintype = email_message_instance.get_content_maintype()
+    if maintype == 'multipart':
+        for part in email_message_instance.get_payload():
+            if part.get_content_maintype() == 'text':
+                return part.get_payload()
+    elif maintype == 'text':
+        return email_message_instance.get_payload()
+
+
+def random_gen(id=None, 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 id:
+        return ''.join([id, '-', randomstr])
+    return randomstr
+
+
+def cleanup_resources(api_client, resources):
+    """Delete resources"""
+    for obj in resources:
+        obj.delete(api_client)
+
+
+def is_server_ssh_ready(ipaddress, port, username, password, retries=20, retryinterv=30, timeout=10.0, keyPairFileLocation=None):
+    '''
+    @Name: is_server_ssh_ready
+    @Input: timeout: tcp connection timeout flag,
+            others information need to be added
+    @Output:object for SshClient
+    Name of the function is little misnomer and is not
+              verifying anything as such mentioned
+    '''
+
+    try:
+        ssh = SshClient(
+            host=ipaddress,
+            port=port,
+            user=username,
+            passwd=password,
+            keyPairFiles=keyPairFileLocation,
+            retries=retries,
+            delay=retryinterv,
+            timeout=timeout)
+    except Exception, e:
+        raise Exception("SSH connection has Failed. Waited %ss. Error is %s" % (retries * retryinterv, str(e)))
+    else:
+        return ssh
+
+
+def format_volume_to_ext3(ssh_client, device="/dev/sda"):
+    """Format attached storage to ext3 fs"""
+    cmds = [
+        "echo -e 'n\np\n1\n\n\nw' | fdisk %s" % device,
+        "mkfs.ext3 %s1" % device,
+    ]
+    for c in cmds:
+        ssh_client.execute(c)
+
+
+def fetch_api_client(config_file='datacenterCfg'):
+    """Fetch the Cloudstack API Client"""
+    config = marvin.configGenerator.get_setup_config(config_file)
+    mgt = config.mgtSvr[0]
+    testClientLogger = logging.getLogger("testClient")
+    asyncTimeout = 3600
+    return cloudstackAPIClient.CloudStackAPIClient(
+        marvin.cloudstackConnection.cloudConnection(
+            mgt,
+            asyncTimeout,
+            testClientLogger
+        )
+    )
+
+def get_host_credentials(config, hostip):
+    """Get login information for a host `hostip` (ipv4) from marvin's `config`
+
+    @return the tuple username, password for the host else raise keyerror"""
+    for zone in config.zones:
+        for pod in zone.pods:
+            for cluster in pod.clusters:
+                for host in cluster.hosts:
+                    if str(host.url).startswith('http'):
+                        hostname = urlparse.urlsplit(str(host.url)).netloc
+                    else:
+                        hostname = str(host.url)
+                    try:
+                        if socket.getfqdn(hostip) == socket.getfqdn(hostname):
+                            return host.username, host.password
+                    except socket.error, e:
+                        raise Exception("Unresolvable host %s error is %s" % (hostip, e))
+    raise KeyError("Please provide the marvin configuration file with credentials to your hosts")
+
+
+def get_process_status(hostip, port, username, password, linklocalip, process, hypervisor=None):
+    """Double hop and returns a process status"""
+
+    #SSH to the machine
+    ssh = SshClient(hostip, port, username, password)
+    if (str(hypervisor).lower() == 'vmware'
+		or str(hypervisor).lower() == 'hyperv'):
+        ssh_command = "ssh -i /var/cloudstack/management/.ssh/id_rsa -ostricthostkeychecking=no "
+    else:
+        ssh_command = "ssh -i ~/.ssh/id_rsa.cloud -ostricthostkeychecking=no "
+
+    ssh_command = ssh_command +\
+                  "-oUserKnownHostsFile=/dev/null -p 3922 %s %s" % (
+                      linklocalip,
+                      process)
+
+    # Double hop into router
+    timeout = 5
+    # Ensure the SSH login is successful
+    while True:
+        res = ssh.execute(ssh_command)
+
+        if res[0] != "Host key verification failed.":
+            break
+        elif timeout == 0:
+            break
+
+        time.sleep(5)
+        timeout = timeout - 1
+    return res
+
+
+def isAlmostEqual(first_digit, second_digit, range=0):
+    digits_equal_within_range = False
+
+    try:
+        if ((first_digit - range) < second_digit < (first_digit + range)):
+            digits_equal_within_range = True
+    except Exception as e:
+        raise e
+    return digits_equal_within_range
+
+
+def xsplit(txt, seps):
+    """
+    Split a string in `txt` by list of delimiters in `seps`
+    @param txt: string to split
+    @param seps: list of separators
+    @return: list of split units
+    """
+    default_sep = seps[0]
+    for sep in seps[1:]: # we skip seps[0] because that's the default separator
+        txt = txt.replace(sep, default_sep)
+    return [i.strip() for i in txt.split(default_sep)]
+
+def get_hypervisor_type(apiclient):
+
+    """Return the hypervisor type of the hosts in setup"""
+
+    cmd = listHosts.listHostsCmd()
+    cmd.type = 'Routing'
+    cmd.listall = True
+    hosts = apiclient.listHosts(cmd)
+    hosts_list_validation_result = validateList(hosts)
+    assert hosts_list_validation_result[0] == PASS, "host list validation failed"
+    return hosts_list_validation_result[1].hypervisor
+
+def is_snapshot_on_nfs(apiclient, dbconn, config, zoneid, snapshotid):
+    """
+    Checks whether a snapshot with id (not UUID) `snapshotid` is present on the nfs storage
+
+    @param apiclient: api client connection
+    @param @dbconn:  connection to the cloudstack db
+    @param config: marvin configuration file
+    @param zoneid: uuid of the zone on which the secondary nfs storage pool is mounted
+    @param snapshotid: uuid of the snapshot
+    @return: True if snapshot is found, False otherwise
+    """
+    # snapshot extension to be appended to the snapshot path obtained from db
+    snapshot_extensions = {"vmware": ".ovf",
+                            "kvm": "",
+                            "xenserver": ".vhd",
+                            "simulator":""}
+
+    qresultset = dbconn.execute(
+                        "select id from snapshots where uuid = '%s';" \
+                        % str(snapshotid)
+                        )
+    if len(qresultset) == 0:
+        raise Exception(
+            "No snapshot found in cloudstack with id %s" % snapshotid)
+
+
+    snapshotid = qresultset[0][0]
+    qresultset = dbconn.execute(
+        "select install_path,store_id from snapshot_store_ref where snapshot_id='%s' and store_role='Image';" % snapshotid
+    )
+
+    assert isinstance(qresultset, list), "Invalid db query response for snapshot %s" % snapshotid
+
+    if len(qresultset) == 0:
+        #Snapshot does not exist
+        return False
+
+    from base import ImageStore
+    #pass store_id to get the exact storage pool where snapshot is stored
+    secondaryStores = ImageStore.list(apiclient, zoneid=zoneid, id=int(qresultset[0][1]))
+
+    assert isinstance(secondaryStores, list), "Not a valid response for listImageStores"
+    assert len(secondaryStores) != 0, "No image stores found in zone %s" % zoneid
+
+    secondaryStore = secondaryStores[0]
+
+    if str(secondaryStore.providername).lower() != "nfs":
+        raise Exception(
+            "is_snapshot_on_nfs works only against nfs secondary storage. found %s" % str(secondaryStore.providername))
+
+    hypervisor = get_hypervisor_type(apiclient)
+    # append snapshot extension based on hypervisor, to the snapshot path
+    snapshotPath = str(qresultset[0][0]) + snapshot_extensions[str(hypervisor).lower()]
+
+    nfsurl = secondaryStore.url
+    from urllib2 import urlparse
+    parse_url = urlparse.urlsplit(nfsurl, scheme='nfs')
+    host, path = str(parse_url.netloc), str(parse_url.path)
+
+    if not config.mgtSvr:
+        raise Exception("Your marvin configuration does not contain mgmt server credentials")
+    mgtSvr, user, passwd = config.mgtSvr[0].mgtSvrIp, config.mgtSvr[0].user, config.mgtSvr[0].passwd
+
+    try:
+        ssh_client = SshClient(
+            mgtSvr,
+            22,
+            user,
+            passwd
+        )
+
+        pathSeparator = "" #used to form host:dir format
+        if not host.endswith(':'):
+            pathSeparator= ":"
+
+        cmds = [
+
+            "mkdir -p %s /mnt/tmp",
+            "mount -t %s %s%s%s /mnt/tmp" % (
+                'nfs',
+                host,
+                pathSeparator,
+                path,
+            ),
+            "test -f %s && echo 'snapshot exists'" % (
+                os.path.join("/mnt/tmp", snapshotPath)
+            ),
+        ]
+
+        for c in cmds:
+            result = ssh_client.execute(c)
+
+        # Unmount the Sec Storage
+        cmds = [
+                "cd",
+                "umount /mnt/tmp",
+            ]
+        for c in cmds:
+            ssh_client.execute(c)
+    except Exception as e:
+        raise Exception("SSH failed for management server: %s - %s" %
+                      (config.mgtSvr[0].mgtSvrIp, e))
+    return 'snapshot exists' in result
+
+def validateList(inp):
+    """
+    @name: validateList
+    @Description: 1. A utility function to validate
+                 whether the input passed is a list
+              2. The list is empty or not
+              3. If it is list and not empty, return PASS and first element
+              4. If not reason for FAIL
+        @Input: Input to be validated
+        @output: List, containing [ Result,FirstElement,Reason ]
+                 Ist Argument('Result') : FAIL : If it is not a list
+                                          If it is list but empty
+                                         PASS : If it is list and not empty
+                 IInd Argument('FirstElement'): If it is list and not empty,
+                                           then first element
+                                            in it, default to None
+                 IIIrd Argument( 'Reason' ):  Reason for failure ( FAIL ),
+                                              default to None.
+                                              INVALID_INPUT
+                                              EMPTY_LIST
+    """
+    ret = [FAIL, None, None]
+    if inp is None:
+        ret[2] = INVALID_INPUT
+        return ret
+    if not isinstance(inp, list):
+        ret[2] = INVALID_INPUT
+        return ret
+    if len(inp) == 0:
+        ret[2] = EMPTY_LIST
+        return ret
+    return [PASS, inp[0], None]
+
+def verifyElementInList(inp, toverify, responsevar=None,  pos=0):
+    '''
+    @name: verifyElementInList
+    @Description:
+    1. A utility function to validate
+    whether the input passed is a list.
+    The list is empty or not.
+    If it is list and not empty, verify
+    whether a given element is there in that list or not
+    at a given pos
+    @Input:
+             I   : Input to be verified whether its a list or not
+             II  : Element to verify whether it exists in the list 
+             III : variable name in response object to verify 
+                   default to None, if None, we will verify for the complete 
+                   first element EX: state of response object object
+             IV  : Position in the list at which the input element to verify
+                   default to 0
+    @output: List, containing [ Result,Reason ]
+             Ist Argument('Result') : FAIL : If it is not a list
+                                      If it is list but empty
+                                      PASS : If it is list and not empty
+                                              and matching element was found
+             IIrd Argument( 'Reason' ): Reason for failure ( FAIL ),
+                                        default to None.
+                                        INVALID_INPUT
+                                        EMPTY_LIST
+                                        MATCH_NOT_FOUND
+    '''
+    if toverify is None or toverify == '' \
+       or pos is None or pos < -1 or pos == '':
+        return [FAIL, INVALID_INPUT]
+    out = validateList(inp)
+    if out[0] == FAIL:
+        return [FAIL, out[2]]
+    if len(inp) > pos:
+        if responsevar is None:
+                if inp[pos] == toverify:
+                    return [PASS, None]
+        else:
+                if responsevar in inp[pos].__dict__ and getattr(inp[pos], responsevar) == toverify:
+                    return [PASS, None]
+                else:
+                    return [FAIL, MATCH_NOT_FOUND]
+    else:
+        return [FAIL, MATCH_NOT_FOUND]
+
+def checkVolumeSize(ssh_handle=None,
+                    volume_name="/dev/sda",
+                    cmd_inp="/sbin/fdisk -l | grep Disk",
+                    size_to_verify=0):
+    '''
+    @Name : getDiskUsage
+    @Desc : provides facility to verify the volume size against the size to verify
+    @Input: 1. ssh_handle : machine against which to execute the disk size cmd
+            2. volume_name : The name of the volume against which to verify the size
+            3. cmd_inp : Input command used to veify the size
+            4. size_to_verify: size against which to compare.
+    @Output: Returns FAILED in case of an issue, else SUCCESS
+    '''
+    try:
+        if ssh_handle is None or cmd_inp is None or volume_name is None:
+            return INVALID_INPUT
+
+        cmd = cmd_inp
+        '''
+        Retrieve the cmd output
+        '''
+        if system().lower() != "windows":
+            fdisk_output = ssh_handle.runCommand(cmd_inp)
+            if fdisk_output["status"] != SUCCESS:
+                return FAILED
+            for line in fdisk_output["stdout"]:
+                if volume_name in line:
+                    parts = line.strip().split()
+                    if str(parts[-2]) == str(size_to_verify):
+                        return [SUCCESS,str(parts[-2])]
+            return [FAILED,"Volume Not Found"]
+    except Exception, e:
+        print "\n Exception Occurred under getDiskUsage: " \
+              "%s" %GetDetailExceptionInfo(e)
+        return [FAILED,GetDetailExceptionInfo(e)]
+
+        
+def verifyRouterState(apiclient, routerid, allowedstates):
+    """List the router and verify that its state is in allowed states
+    @output: List, containing [Result, Reason]
+             Ist Argument ('Result'): FAIL: If router state is not
+                                                in allowed states
+                                          PASS: If router state is in
+                                                allowed states"""
+
+    try:
+        cmd = listRouters.listRoutersCmd()
+        cmd.id = routerid
+        cmd.listall = True
+        routers = apiclient.listRouters(cmd)
+    except Exception as e:
+        return [FAIL, e]
+    listvalidationresult = validateList(routers)
+    if listvalidationresult[0] == FAIL:
+        return [FAIL, listvalidationresult[2]]
+    if routers[0].redundantstate not in allowedstates:
+        return [FAIL, "Redundant state of the router should be in %s but is %s" %
+            (allowedstates, routers[0].redundantstate)]
+    return [PASS, None]
+        

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/vm.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/vm.py b/tools/marvin/marvin/lib/cloudstack/vm.py
new file mode 100644
index 0000000..3b81778
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/vm.py
@@ -0,0 +1,706 @@
+# 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 time
+import base64
+
+from marvin.codes import (FAIL, PASS, RUNNING, STOPPED,
+                          STARTING, DESTROYED, EXPUNGING,
+                          STOPPING)
+from marvin.cloudstackException import CloudstackAPIException
+from marvin.lib.cloudstack.utils import validateList, is_server_ssh_ready, random_gen
+from marvin.lib.cloudstack.zone import Zone
+from marvin.lib.cloudstack.network import SecurityGroup,PublicIPAddress,NATRule,FireWallRule,EgressFireWallRule
+from marvin.cloudstackAPI import deployVirtualMachine,startVirtualMachine,stopVirtualMachine,rebootVirtualMachine,recoverVirtualMachine,\
+    restoreVirtualMachine,resetSSHKeyForVirtualMachine,updateVirtualMachine,destroyVirtualMachine,expungeVirtualMachine,migrateVirtualMachine,\
+    attachVolume,detachVolume,addNicToVirtualMachine,removeNicFromVirtualMachine,updateDefaultNicForVirtualMachine,attachIso,detachIso,scaleVirtualMachine,\
+    changeServiceForVirtualMachine,listVirtualMachines,resetPasswordForVirtualMachine,assignVirtualMachine,updateVMAffinityGroup,createVMSnapshot,deleteVMSnapshot,\
+    listVMSnapshot,createInstanceGroup,deleteInstanceGroup,listInstanceGroups,updateInstanceGroup,revertToVMSnapshot
+
+
+class VirtualMachine:
+    """Manage virtual machine lifecycle"""
+
+    '''Class level variables'''
+    # Variables denoting VM state - start
+    STOPPED = STOPPED
+    RUNNING = RUNNING
+    DESTROYED = DESTROYED
+    EXPUNGING = EXPUNGING
+    STOPPING = STOPPING
+    STARTING = STARTING
+    # Varibles denoting VM state - end
+
+    def __init__(self, items, services):
+        self.__dict__.update(items)
+        if "username" in services:
+            self.username = services["username"]
+        else:
+            self.username = 'root'
+        if "password" in services:
+            self.password = services["password"]
+        else:
+            self.password = 'password'
+        if "ssh_port" in services:
+            self.ssh_port = services["ssh_port"]
+        else:
+            self.ssh_port = 22
+        self.ssh_client = None
+        # extract out the ipaddress
+        self.ipaddress = self.nic[0].ipaddress
+
+    @classmethod
+    def ssh_access_group(cls, apiclient, cmd):
+        """
+        Programs the security group with SSH
+         access before deploying virtualmachine
+        @return:
+        """
+        zone_list = Zone.list(
+            apiclient,
+            id=cmd.zoneid if cmd.zoneid else None,
+            domainid=cmd.domainid if cmd.domainid else None
+        )
+        zone = zone_list[0]
+        # check if security groups settings is enabled for the zone
+        if zone.securitygroupsenabled:
+            list_security_groups = SecurityGroup.list(
+                apiclient,
+                account=cmd.account,
+                domainid=cmd.domainid,
+                listall=True,
+                securitygroupname="basic_sec_grp"
+            )
+
+            if not isinstance(list_security_groups, list):
+                basic_mode_security_group = SecurityGroup.create(
+                    apiclient,
+                    {"name": "basic_sec_grp"},
+                    cmd.account,
+                    cmd.domainid,
+                )
+                sec_grp_services = {
+                    "protocol": "TCP",
+                    "startport": 22,
+                    "endport": 22,
+                    "cidrlist": "0.0.0.0/0"
+                }
+                # Authorize security group for above ingress rule
+                basic_mode_security_group.authorize(apiclient,
+                                                    sec_grp_services,
+                                                    account=cmd.account,
+                                                    domainid=cmd.domainid)
+            else:
+                basic_mode_security_group = list_security_groups[0]
+
+            if isinstance(cmd.securitygroupids, list):
+                cmd.securitygroupids.append(basic_mode_security_group.id)
+            else:
+                cmd.securitygroupids = [basic_mode_security_group.id]
+
+    @classmethod
+    def access_ssh_over_nat(
+            cls, apiclient, services, virtual_machine, allow_egress=False,
+            networkid=None):
+        """
+        Program NAT and PF rules to open up ssh access to deployed guest
+        @return:
+        """
+        public_ip = PublicIPAddress.create(
+            apiclient=apiclient,
+            accountid=virtual_machine.account,
+            zoneid=virtual_machine.zoneid,
+            domainid=virtual_machine.domainid,
+            services=services,
+            networkid=networkid
+        )
+        FireWallRule.create(
+            apiclient=apiclient,
+            ipaddressid=public_ip.ipaddress.id,
+            protocol='TCP',
+            cidrlist=['0.0.0.0/0'],
+            startport=22,
+            endport=22
+        )
+        nat_rule = NATRule.create(
+            apiclient=apiclient,
+            virtual_machine=virtual_machine,
+            services=services,
+            ipaddressid=public_ip.ipaddress.id
+        )
+        if allow_egress:
+            try:
+                EgressFireWallRule.create(
+                    apiclient=apiclient,
+                    networkid=virtual_machine.nic[0].networkid,
+                    protocol='All',
+                    cidrlist='0.0.0.0/0'
+                )
+            except CloudstackAPIException, e:
+                # This could fail because we've already set up the same rule
+                if not "There is already a firewall rule specified".lower() in e.errorMsg.lower():
+                    raise
+        virtual_machine.ssh_ip = nat_rule.ipaddress
+        virtual_machine.public_ip = nat_rule.ipaddress
+
+    @classmethod
+    def create(cls, apiclient, services, templateid=None, accountid=None,
+               domainid=None, zoneid=None, networkids=None,
+               serviceofferingid=None, securitygroupids=None,
+               projectid=None, startvm=None, diskofferingid=None,
+               affinitygroupnames=None, affinitygroupids=None, group=None,
+               hostid=None, keypair=None, ipaddress=None, mode='default',
+               method='GET', hypervisor=None, customcpunumber=None,
+               customcpuspeed=None, custommemory=None, rootdisksize=None):
+        """Create the instance"""
+
+        cmd = deployVirtualMachine.deployVirtualMachineCmd()
+
+        if serviceofferingid:
+            cmd.serviceofferingid = serviceofferingid
+        elif "serviceoffering" in services:
+            cmd.serviceofferingid = services["serviceoffering"]
+
+        if zoneid:
+            cmd.zoneid = zoneid
+        elif "zoneid" in services:
+            cmd.zoneid = services["zoneid"]
+
+        if hypervisor:
+            cmd.hypervisor = hypervisor
+
+        if "displayname" in services:
+            cmd.displayname = services["displayname"]
+
+        if "name" in services:
+            cmd.name = services["name"]
+
+        if accountid:
+            cmd.account = accountid
+        elif "account" in services:
+            cmd.account = services["account"]
+
+        if domainid:
+            cmd.domainid = domainid
+        elif "domainid" in services:
+            cmd.domainid = services["domainid"]
+
+        if networkids:
+            cmd.networkids = networkids
+            allow_egress = False
+        elif "networkids" in services:
+            cmd.networkids = services["networkids"]
+            allow_egress = False
+        else:
+            # When no networkids are passed, network
+            # is created using the "defaultOfferingWithSourceNAT"
+            # which has an egress policy of DENY. But guests in tests
+            # need access to test network connectivity
+            allow_egress = True
+
+        if templateid:
+            cmd.templateid = templateid
+        elif "template" in services:
+            cmd.templateid = services["template"]
+
+        if diskofferingid:
+            cmd.diskofferingid = diskofferingid
+        elif "diskoffering" in services:
+            cmd.diskofferingid = services["diskoffering"]
+
+        if keypair:
+            cmd.keypair = keypair
+        elif "keypair" in services:
+            cmd.keypair = services["keypair"]
+
+        if ipaddress:
+            cmd.ipaddress = ipaddress
+        elif ipaddress in services:
+            cmd.ipaddress = services["ipaddress"]
+
+        if securitygroupids:
+            cmd.securitygroupids = [str(sg_id) for sg_id in securitygroupids]
+
+        if "affinitygroupnames" in services:
+            cmd.affinitygroupnames = services["affinitygroupnames"]
+        elif affinitygroupnames:
+            cmd.affinitygroupnames = affinitygroupnames
+
+        if affinitygroupids:
+            cmd.affinitygroupids = affinitygroupids
+
+        if projectid:
+            cmd.projectid = projectid
+
+        if startvm is not None:
+            cmd.startvm = startvm
+
+        if hostid:
+            cmd.hostid = hostid
+
+        if "userdata" in services:
+            cmd.userdata = base64.urlsafe_b64encode(services["userdata"])
+
+        cmd.details = [{}]
+
+        if customcpunumber:
+            cmd.details[0]["cpuNumber"] = customcpunumber
+
+        if customcpuspeed:
+            cmd.details[0]["cpuSpeed"] = customcpuspeed
+
+        if custommemory:
+            cmd.details[0]["memory"] = custommemory
+
+        if rootdisksize >= 0:
+            cmd.details[0]["rootdisksize"] = rootdisksize
+
+        if group:
+            cmd.group = group
+
+        # program default access to ssh
+        if mode.lower() == 'basic':
+            cls.ssh_access_group(apiclient, cmd)
+
+        virtual_machine = apiclient.deployVirtualMachine(cmd, method=method)
+
+        virtual_machine.ssh_ip = virtual_machine.nic[0].ipaddress
+        if startvm is False:
+            virtual_machine.public_ip = virtual_machine.nic[0].ipaddress
+            return VirtualMachine(virtual_machine.__dict__, services)
+
+        # program ssh access over NAT via PF
+        if mode.lower() == 'advanced':
+            cls.access_ssh_over_nat(
+                apiclient,
+                services,
+                virtual_machine,
+                allow_egress=allow_egress,
+                networkid=cmd.networkids[0] if cmd.networkids else None)
+        elif mode.lower() == 'basic':
+            if virtual_machine.publicip is not None:
+                # EIP/ELB (netscaler) enabled zone
+                vm_ssh_ip = virtual_machine.publicip
+            else:
+                # regular basic zone with security group
+                vm_ssh_ip = virtual_machine.nic[0].ipaddress
+            virtual_machine.ssh_ip = vm_ssh_ip
+            virtual_machine.public_ip = vm_ssh_ip
+
+        return VirtualMachine(virtual_machine.__dict__, services)
+
+    def start(self, apiclient):
+        """Start the instance"""
+        cmd = startVirtualMachine.startVirtualMachineCmd()
+        cmd.id = self.id
+        apiclient.startVirtualMachine(cmd)
+        response = self.getState(apiclient, VirtualMachine.RUNNING)
+        if response[0] == FAIL:
+            raise Exception(response[1])
+        return
+
+    def stop(self, apiclient, forced=None):
+        """Stop the instance"""
+        cmd = stopVirtualMachine.stopVirtualMachineCmd()
+        cmd.id = self.id
+        if forced:
+            cmd.forced = forced
+        apiclient.stopVirtualMachine(cmd)
+        response = self.getState(apiclient, VirtualMachine.STOPPED)
+        if response[0] == FAIL:
+            raise Exception(response[1])
+        return
+
+    def reboot(self, apiclient):
+        """Reboot the instance"""
+        cmd = rebootVirtualMachine.rebootVirtualMachineCmd()
+        cmd.id = self.id
+        apiclient.rebootVirtualMachine(cmd)
+
+    def recover(self, apiclient):
+        """Recover the instance"""
+        cmd = recoverVirtualMachine.recoverVirtualMachineCmd()
+        cmd.id = self.id
+        apiclient.recoverVirtualMachine(cmd)
+
+    def restore(self, apiclient, templateid=None):
+        """Restore the instance"""
+        cmd = restoreVirtualMachine.restoreVirtualMachineCmd()
+        cmd.virtualmachineid = self.id
+        if templateid:
+            cmd.templateid = templateid
+        return apiclient.restoreVirtualMachine(cmd)
+
+    def get_ssh_client(
+            self, ipaddress=None, reconnect=False, port=None,
+            keyPairFileLocation=None):
+        """Get SSH object of VM"""
+
+        # If NAT Rules are not created while VM deployment in Advanced mode
+        # then, IP address must be passed
+        if ipaddress is not None:
+            self.ssh_ip = ipaddress
+        if port:
+            self.ssh_port = port
+
+        if keyPairFileLocation is not None:
+            self.password = None
+
+        if reconnect:
+            self.ssh_client = is_server_ssh_ready(
+                self.ssh_ip,
+                self.ssh_port,
+                self.username,
+                self.password,
+                keyPairFileLocation=keyPairFileLocation
+            )
+        self.ssh_client = self.ssh_client or is_server_ssh_ready(
+            self.ssh_ip,
+            self.ssh_port,
+            self.username,
+            self.password,
+            keyPairFileLocation=keyPairFileLocation
+        )
+        return self.ssh_client
+
+    def getState(self, apiclient, state, timeout=600):
+        """List VM and check if its state is as expected
+        @returnValue - List[Result, Reason]
+                       1) Result - FAIL if there is any exception
+                       in the operation or VM state does not change
+                       to expected state in given time else PASS
+                       2) Reason - Reason for failure"""
+
+        returnValue = [FAIL, "VM state not trasited to %s,\
+                        operation timed out" % state]
+
+        while timeout > 0:
+            try:
+                projectid = None
+                if hasattr(self, "projectid"):
+                    projectid = self.projectid
+                vms = VirtualMachine.list(apiclient, projectid=projectid,
+				          id=self.id, listAll=True)
+                validationresult = validateList(vms)
+                if validationresult[0] == FAIL:
+                    raise Exception("VM list validation failed: %s" % validationresult[2])
+                elif str(vms[0].state).lower().decode("string_escape") == str(state).lower():
+                    returnValue = [PASS, None]
+                    break
+            except Exception as e:
+                returnValue = [FAIL, e]
+                break
+            time.sleep(60)
+            timeout -= 60
+        return returnValue
+
+    def resetSshKey(self, apiclient, **kwargs):
+        """Resets SSH key"""
+
+        cmd = resetSSHKeyForVirtualMachine.resetSSHKeyForVirtualMachineCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return(apiclient.resetSSHKeyForVirtualMachine(cmd))
+
+    def update(self, apiclient, **kwargs):
+        """Updates the VM data"""
+
+        cmd = updateVirtualMachine.updateVirtualMachineCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return(apiclient.updateVirtualMachine(cmd))
+
+    def delete(self, apiclient, expunge=True, **kwargs):
+        """Destroy an Instance"""
+        cmd = destroyVirtualMachine.destroyVirtualMachineCmd()
+        cmd.id = self.id
+        cmd.expunge = expunge
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        apiclient.destroyVirtualMachine(cmd)
+
+    def expunge(self, apiclient):
+        """Expunge an Instance"""
+        cmd = expungeVirtualMachine.expungeVirtualMachineCmd()
+        cmd.id = self.id
+        apiclient.expungeVirtualMachine(cmd)
+
+    def migrate(self, apiclient, hostid=None):
+        """migrate an Instance"""
+        cmd = migrateVirtualMachine.migrateVirtualMachineCmd()
+        cmd.virtualmachineid = self.id
+        if hostid:
+            cmd.hostid = hostid
+        apiclient.migrateVirtualMachine(cmd)
+
+    def attach_volume(self, apiclient, volume):
+        """Attach volume to instance"""
+        cmd = attachVolume.attachVolumeCmd()
+        cmd.id = volume.id
+        cmd.virtualmachineid = self.id
+        return apiclient.attachVolume(cmd)
+
+    def detach_volume(self, apiclient, volume):
+        """Detach volume to instance"""
+        cmd = detachVolume.detachVolumeCmd()
+        cmd.id = volume.id
+        return apiclient.detachVolume(cmd)
+
+    def add_nic(self, apiclient, networkId, ipaddress=None):
+        """Add a NIC to a VM"""
+        cmd = addNicToVirtualMachine.addNicToVirtualMachineCmd()
+        cmd.virtualmachineid = self.id
+        cmd.networkid = networkId
+
+        if ipaddress:
+            cmd.ipaddress = ipaddress
+
+        return apiclient.addNicToVirtualMachine(cmd)
+
+    def remove_nic(self, apiclient, nicId):
+        """Remove a NIC to a VM"""
+        cmd = removeNicFromVirtualMachine.removeNicFromVirtualMachineCmd()
+        cmd.nicid = nicId
+        cmd.virtualmachineid = self.id
+        return apiclient.removeNicFromVirtualMachine(cmd)
+
+    def update_default_nic(self, apiclient, nicId):
+        """Set a NIC to be the default network adapter for a VM"""
+        cmd = updateDefaultNicForVirtualMachine.\
+            updateDefaultNicForVirtualMachineCmd()
+        cmd.nicid = nicId
+        cmd.virtualmachineid = self.id
+        return apiclient.updateDefaultNicForVirtualMachine(cmd)
+
+    def attach_iso(self, apiclient, iso):
+        """Attach ISO to instance"""
+        cmd = attachIso.attachIsoCmd()
+        cmd.id = iso.id
+        cmd.virtualmachineid = self.id
+        return apiclient.attachIso(cmd)
+
+    def detach_iso(self, apiclient):
+        """Detach ISO to instance"""
+        cmd = detachIso.detachIsoCmd()
+        cmd.virtualmachineid = self.id
+        return apiclient.detachIso(cmd)
+
+    def scale_virtualmachine(self, apiclient, serviceOfferingId):
+        """ Scale up of service offering for the Instance"""
+        cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
+        cmd.id = self.id
+        cmd.serviceofferingid = serviceOfferingId
+        return apiclient.scaleVirtualMachine(cmd)
+
+    def change_service_offering(self, apiclient, serviceOfferingId):
+        """Change service offering of the instance"""
+        cmd = changeServiceForVirtualMachine.\
+            changeServiceForVirtualMachineCmd()
+        cmd.id = self.id
+        cmd.serviceofferingid = serviceOfferingId
+        return apiclient.changeServiceForVirtualMachine(cmd)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """List all VMs matching criteria"""
+
+        cmd = listVirtualMachines.listVirtualMachinesCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listVirtualMachines(cmd))
+
+    def resetPassword(self, apiclient):
+        """Resets VM password if VM created using password enabled template"""
+
+        cmd = resetPasswordForVirtualMachine.\
+            resetPasswordForVirtualMachineCmd()
+        cmd.id = self.id
+        try:
+            response = apiclient.resetPasswordForVirtualMachine(cmd)
+        except Exception as e:
+            raise Exception("Reset Password failed! - %s" % e)
+        if response is not None:
+            return response.password
+
+    def assign_virtual_machine(self, apiclient, account, domainid):
+        """Move a user VM to another user under same domain."""
+
+        cmd = assignVirtualMachine.assignVirtualMachineCmd()
+        cmd.virtualmachineid = self.id
+        cmd.account = account
+        cmd.domainid = domainid
+        try:
+            response = apiclient.assignVirtualMachine(cmd)
+            return response
+        except Exception as e:
+            raise Exception("assignVirtualMachine failed - %s" % e)
+
+    def update_affinity_group(self, apiclient, affinitygroupids=None,
+                              affinitygroupnames=None):
+        """Update affinity group of a VM"""
+        cmd = updateVMAffinityGroup.updateVMAffinityGroupCmd()
+        cmd.id = self.id
+
+        if affinitygroupids:
+            cmd.affinitygroupids = affinitygroupids
+
+        if affinitygroupnames:
+            cmd.affinitygroupnames = affinitygroupnames
+
+        return apiclient.updateVMAffinityGroup(cmd)
+
+    def scale(self, apiclient, serviceOfferingId,
+            customcpunumber=None, customcpuspeed=None, custommemory=None):
+        """Change service offering of the instance"""
+        cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
+        cmd.id = self.id
+        cmd.serviceofferingid = serviceOfferingId
+        cmd.details = [{"cpuNumber": "", "cpuSpeed": "", "memory": ""}]
+        if customcpunumber:
+            cmd.details[0]["cpuNumber"] = customcpunumber
+        if customcpuspeed:
+            cmd.details[0]["cpuSpeed"] = customcpuspeed
+        if custommemory:
+            cmd.details[0]["memory"] = custommemory
+        return apiclient.scaleVirtualMachine(cmd)
+
+
+class VmSnapshot:
+    """Manage VM Snapshot life cycle"""
+    def __init__(self, items):
+        self.__dict__.update(items)
+    @classmethod
+    def create(cls, apiclient, vmid, snapshotmemory="false",
+               name=None, description=None):
+        cmd = createVMSnapshot.createVMSnapshotCmd()
+        cmd.virtualmachineid = vmid
+
+        if snapshotmemory:
+            cmd.snapshotmemory = snapshotmemory
+        if name:
+            cmd.name = name
+        if description:
+            cmd.description = description
+        return VmSnapshot(apiclient.createVMSnapshot(cmd).__dict__)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        cmd = listVMSnapshot.listVMSnapshotCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listVMSnapshot(cmd))
+
+    @classmethod
+    def revertToSnapshot(cls, apiclient, vmsnapshotid):
+        cmd = revertToVMSnapshot.revertToVMSnapshotCmd()
+        cmd.vmsnapshotid = vmsnapshotid
+        return apiclient.revertToVMSnapshot(cmd)
+
+    @classmethod
+    def deleteVMSnapshot(cls, apiclient, vmsnapshotid):
+        cmd = deleteVMSnapshot.deleteVMSnapshotCmd()
+        cmd.vmsnapshotid = vmsnapshotid
+        return apiclient.deleteVMSnapshot(cmd)
+
+
+class InstanceGroup:
+    """Manage VM instance groups"""
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, name=None, account=None, domainid=None,
+               projectid=None, networkid=None, rand_name=True):
+        """Creates instance groups"""
+
+        cmd = createInstanceGroup.createInstanceGroupCmd()
+        cmd.name = "-".join([name, random_gen()]) if rand_name else name
+        if account is not None:
+            cmd.account = account
+        if domainid is not None:
+            cmd.domainid = domainid
+        if projectid is not None:
+            cmd.projectid = projectid
+        if networkid is not None:
+            cmd.networkid = networkid
+        return InstanceGroup(apiclient.createInstanceGroup(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete instance group"""
+        cmd = deleteInstanceGroup.deleteInstanceGroupCmd()
+        cmd.id = self.id
+        apiclient.deleteInstanceGroup(cmd)
+
+    def update(self, apiclient, **kwargs):
+        """Updates the instance groups"""
+        cmd = updateInstanceGroup.updateInstanceGroupCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return (apiclient.updateInstanceGroup(cmd))
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """List all instance groups"""
+        cmd = listInstanceGroups.listInstanceGroupsCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return (apiclient.listInstanceGroups(cmd))
+
+    def startInstances(self, apiclient):
+        """Starts all instances in a VM tier"""
+
+        cmd = startVirtualMachine.startVirtualMachineCmd()
+        cmd.group = self.id
+        return apiclient.startVirtualMachine(cmd)
+
+    def stopInstances(self, apiclient):
+        """Stops all instances in a VM tier"""
+
+        cmd = stopVirtualMachine.stopVirtualMachineCmd()
+        cmd.group = self.id
+        return apiclient.stopVirtualMachine(cmd)
+
+    def rebootInstances(self, apiclient):
+        """Reboot all instances in a VM tier"""
+
+        cmd = rebootVirtualMachine.rebootVirtualMachineCmd()
+        cmd.group = self.id
+        return apiclient.rebootVirtualMachine(cmd)
+
+    def deleteInstances(self, apiclient):
+        """Stops all instances in a VM tier"""
+
+        cmd = destroyVirtualMachine.destroyVirtualMachineCmd()
+        cmd.group = self.id
+        return apiclient.destroyVirtualMachine(cmd)
+
+    def changeServiceOffering(self, apiclient, serviceOfferingId):
+        """Change service offering of the vm tier"""
+
+        cmd = changeServiceForVirtualMachine.\
+            changeServiceForVirtualMachineCmd()
+        cmd.group = self.id
+        cmd.serviceofferingid = serviceOfferingId
+        return apiclient.changeServiceForVirtualMachine(cmd)
+
+    def recoverInstances(self, apiclient):
+        """Recover the instances from vm tier"""
+        cmd = recoverVirtualMachine.recoverVirtualMachineCmd()
+        cmd.group = self.id
+        apiclient.recoverVirtualMachine(cmd)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/volume.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/volume.py b/tools/marvin/marvin/lib/cloudstack/volume.py
new file mode 100644
index 0000000..be73c7a
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/volume.py
@@ -0,0 +1,198 @@
+# 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 time
+
+from marvin.cloudstackAPI import createVolume,deleteVolume,listVolumes,resizeVolume,uploadVolume,extractVolume,migrateVolume
+from marvin.lib.cloudstack.utils import random_gen
+
+
+class Volume:
+    """Manage Volume Life cycle
+    """
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, services, zoneid=None, account=None,
+               domainid=None, diskofferingid=None, projectid=None):
+        """Create Volume"""
+        cmd = createVolume.createVolumeCmd()
+        cmd.name = services["diskname"]
+
+        if diskofferingid:
+            cmd.diskofferingid = diskofferingid
+        elif "diskofferingid" in services:
+            cmd.diskofferingid = services["diskofferingid"]
+
+        if zoneid:
+            cmd.zoneid = zoneid
+        elif "zoneid" in services:
+            cmd.zoneid = services["zoneid"]
+
+        if account:
+            cmd.account = account
+        elif "account" in services:
+            cmd.account = services["account"]
+
+        if domainid:
+            cmd.domainid = domainid
+        elif "domainid" in services:
+            cmd.domainid = services["domainid"]
+
+        if projectid:
+            cmd.projectid = projectid
+        return Volume(apiclient.createVolume(cmd).__dict__)
+
+    @classmethod
+    def create_custom_disk(cls, apiclient, services, account=None,
+                           domainid=None, diskofferingid=None):
+        """Create Volume from Custom disk offering"""
+        cmd = createVolume.createVolumeCmd()
+        cmd.name = services["diskname"]
+
+        if diskofferingid:
+            cmd.diskofferingid = diskofferingid
+        elif "customdiskofferingid" in services:
+            cmd.diskofferingid = services["customdiskofferingid"]
+
+        cmd.size = services["customdisksize"]
+        cmd.zoneid = services["zoneid"]
+
+        if account:
+            cmd.account = account
+        else:
+            cmd.account = services["account"]
+
+        if domainid:
+            cmd.domainid = domainid
+        else:
+            cmd.domainid = services["domainid"]
+
+        return Volume(apiclient.createVolume(cmd).__dict__)
+
+    @classmethod
+    def create_from_snapshot(cls, apiclient, snapshot_id, services,
+                             account=None, domainid=None):
+        """Create Volume from snapshot"""
+        cmd = createVolume.createVolumeCmd()
+        cmd.name = "-".join([services["diskname"], random_gen()])
+        cmd.snapshotid = snapshot_id
+        cmd.zoneid = services["zoneid"]
+        cmd.size = services["size"]
+        if account:
+            cmd.account = account
+        else:
+            cmd.account = services["account"]
+        if domainid:
+            cmd.domainid = domainid
+        else:
+            cmd.domainid = services["domainid"]
+        return Volume(apiclient.createVolume(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete Volume"""
+        cmd = deleteVolume.deleteVolumeCmd()
+        cmd.id = self.id
+        apiclient.deleteVolume(cmd)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """List all volumes matching criteria"""
+
+        cmd = listVolumes.listVolumesCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listVolumes(cmd))
+
+    def resize(self, apiclient, **kwargs):
+        """Resize a volume"""
+        cmd = resizeVolume.resizeVolumeCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return(apiclient.resizeVolume(cmd))
+
+    @classmethod
+    def upload(cls, apiclient, services, zoneid=None,
+               account=None, domainid=None, url=None):
+        """Uploads the volume to specified account"""
+
+        cmd = uploadVolume.uploadVolumeCmd()
+        if zoneid:
+            cmd.zoneid = zoneid
+        if account:
+            cmd.account = account
+        if domainid:
+            cmd.domainid = domainid
+        cmd.format = services["format"]
+        cmd.name = services["diskname"]
+        if url:
+            cmd.url = url
+        else:
+            cmd.url = services["url"]
+        return Volume(apiclient.uploadVolume(cmd).__dict__)
+
+    def wait_for_upload(self, apiclient, timeout=10, interval=60):
+        """Wait for upload"""
+        # Sleep to ensure template is in proper state before download
+        time.sleep(interval)
+
+        while True:
+            volume_response = Volume.list(
+                apiclient,
+                id=self.id,
+                zoneid=self.zoneid,
+            )
+            if isinstance(volume_response, list):
+
+                volume = volume_response[0]
+                # If volume is ready,
+                # volume.state = Allocated
+                if volume.state == 'Uploaded':
+                    break
+
+                elif 'Uploading' in volume.state:
+                    time.sleep(interval)
+
+                elif 'Installing' not in volume.state:
+                    raise Exception(
+                        "Error in uploading volume: status - %s" %
+                        volume.state)
+            elif timeout == 0:
+                break
+
+            else:
+                time.sleep(interval)
+                timeout = timeout - 1
+        return
+
+    @classmethod
+    def extract(cls, apiclient, volume_id, zoneid, mode):
+        """Extracts the volume"""
+
+        cmd = extractVolume.extractVolumeCmd()
+        cmd.id = volume_id
+        cmd.zoneid = zoneid
+        cmd.mode = mode
+        return Volume(apiclient.extractVolume(cmd).__dict__)
+
+    @classmethod
+    def migrate(cls, apiclient, **kwargs):
+        """Migrate a volume"""
+        cmd = migrateVolume.migrateVolumeCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return(apiclient.migrateVolume(cmd))

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d002c52a/tools/marvin/marvin/lib/cloudstack/zone.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/cloudstack/zone.py b/tools/marvin/marvin/lib/cloudstack/zone.py
new file mode 100644
index 0000000..614fa50
--- /dev/null
+++ b/tools/marvin/marvin/lib/cloudstack/zone.py
@@ -0,0 +1,67 @@
+# 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.
+from marvin.cloudstackAPI import createZone,deleteZone,updateZone,listZones
+class Zone:
+    """Manage Zone"""
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, services, domainid=None):
+        """Create zone"""
+        cmd = createZone.createZoneCmd()
+        cmd.dns1 = services["dns1"]
+        cmd.internaldns1 = services["internaldns1"]
+        cmd.name = services["name"]
+        cmd.networktype = services["networktype"]
+
+        if "dns2" in services:
+            cmd.dns2 = services["dns2"]
+        if "internaldns2" in services:
+            cmd.internaldns2 = services["internaldns2"]
+        if domainid:
+            cmd.domainid = domainid
+        if "securitygroupenabled" in services:
+            cmd.securitygroupenabled = services["securitygroupenabled"]
+
+        return Zone(apiclient.createZone(cmd).__dict__)
+
+    def delete(self, apiclient):
+        """Delete Zone"""
+
+        cmd = deleteZone.deleteZoneCmd()
+        cmd.id = self.id
+        apiclient.deleteZone(cmd)
+
+    def update(self, apiclient, **kwargs):
+        """Update the zone"""
+
+        cmd = updateZone.updateZoneCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        return apiclient.updateZone(cmd)
+
+    @classmethod
+    def list(cls, apiclient, **kwargs):
+        """List all Zones matching criteria"""
+
+        cmd = listZones.listZonesCmd()
+        [setattr(cmd, k, v) for k, v in kwargs.items()]
+        if 'account' in kwargs.keys() and 'domainid' in kwargs.keys():
+            cmd.listall = True
+        return(apiclient.listZones(cmd))