You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2014/07/28 23:13:54 UTC
[26/50] [abbrv] CLOUDSTACK-1466: Automation - Priamary Storage Limits
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/12b6cf1b/test/integration/component/test_ps_resize_volume.py
----------------------------------------------------------------------
diff --git a/test/integration/component/test_ps_resize_volume.py b/test/integration/component/test_ps_resize_volume.py
new file mode 100644
index 0000000..737f910
--- /dev/null
+++ b/test/integration/component/test_ps_resize_volume.py
@@ -0,0 +1,339 @@
+# 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.
+
+""" P1 tests for testing resize volume functionality with primary storage limit constraints on
+ account/domain
+
+ Test Plan: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Limit+Resources+to+domain+or+accounts
+
+ Issue Link: https://issues.apache.org/jira/browse/CLOUDSTACK-1466
+
+ Feature Specifications: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Limit+Resources+to+domains+and+accounts
+"""
+# Import Local Modules
+from nose.plugins.attrib import attr
+from marvin.cloudstackTestCase import cloudstackTestCase, unittest
+from marvin.lib.base import (Account,
+ ServiceOffering,
+ VirtualMachine,
+ Resources,
+ Domain,
+ DiskOffering,
+ Volume)
+from marvin.lib.common import (get_domain,
+ get_zone,
+ get_template,
+ matchResourceCount,
+ isDomainResourceCountEqualToExpectedCount)
+from marvin.lib.utils import (cleanup_resources,
+ get_hypervisor_type)
+from marvin.codes import (PASS,
+ FAIL,
+ FAILED,
+ RESOURCE_PRIMARY_STORAGE,
+ RESOURCE_SECONDARY_STORAGE,
+ XEN_SERVER)
+
+class TestResizeVolume(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cloudstackTestClient = super(TestResizeVolume,
+ cls).getClsTestClient()
+ cls.api_client = cloudstackTestClient.getApiClient()
+ # Fill services from the external config file
+ cls.services = cloudstackTestClient.getParsedTestDataConfig()
+ # Get Zone, Domain and templates
+ cls.domain = get_domain(cls.api_client)
+ cls.zone = get_zone(cls.api_client, cloudstackTestClient.getZoneForTests())
+ cls.services["mode"] = cls.zone.networktype
+ cls.resourcetypemapping = {RESOURCE_PRIMARY_STORAGE: 10,
+ RESOURCE_SECONDARY_STORAGE: 11}
+
+ cls.template = get_template(
+ cls.api_client,
+ cls.zone.id,
+ cls.services["ostype"]
+ )
+
+ cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+ cls.services["virtual_machine"]["template"] = cls.template.id
+ cls.services["volume"]["zoneid"] = cls.zone.id
+
+ cls._cleanup = []
+ try:
+ cls.hypervisor = str(get_hypervisor_type(cls.api_client)).lower()
+
+ # Creating service offering with normal config
+ cls.service_offering = ServiceOffering.create(cls.api_client,
+ cls.services["service_offering"])
+ cls._cleanup.append(cls.service_offering)
+
+ cls.services["disk_offering"]["disksize"] = 5
+ cls.disk_offering_5_GB = DiskOffering.create(
+ cls.api_client,
+ cls.services["disk_offering"]
+ )
+ cls._cleanup.append(cls.disk_offering_5_GB)
+
+ cls.services["disk_offering"]["disksize"] = 20
+ cls.disk_offering_20_GB = DiskOffering.create(
+ cls.api_client,
+ cls.services["disk_offering"]
+ )
+ cls._cleanup.append(cls.disk_offering_20_GB)
+ except Exception as e:
+ cls.tearDownClass()
+ raise unittest.SkipTest("Failure while creating disk offering: %s" % e)
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created instance, volumes and snapshots
+ cleanup_resources(self.apiclient, self.cleanup)
+ pass
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def updateResourceLimits(self, accountLimit=None, domainLimit=None):
+ """Update primary storage limits of the parent domain and its
+ child domains"""
+
+ try:
+ if domainLimit:
+ #Update resource limit for domain
+ Resources.updateLimit(self.apiclient, resourcetype=10,
+ max=domainLimit,
+ domainid=self.parent_domain.id)
+ if accountLimit:
+ #Update resource limit for domain
+ Resources.updateLimit(self.apiclient, resourcetype=10,
+ max=accountLimit, account=self.parentd_admin.name,
+ domainid=self.parent_domain.id)
+ except Exception as e:
+ return [FAIL, e]
+ return [PASS, None]
+
+ def setupAccounts(self):
+ try:
+ self.parent_domain = Domain.create(self.apiclient,
+ services=self.services["domain"],
+ parentdomainid=self.domain.id)
+ self.parentd_admin = Account.create(self.apiclient, self.services["account"],
+ admin=True, domainid=self.parent_domain.id)
+
+ # Cleanup the resources created at end of test
+ self.cleanup.append(self.parentd_admin)
+ self.cleanup.append(self.parent_domain)
+ except Exception as e:
+ return [FAIL, e]
+ return [PASS, None]
+
+ @attr(tags=["advanced", "selfservice"])
+ def test_01_increase_volume_size_within_account_limit(self):
+ """Test increasing volume size within the account limit and verify primary storage usage
+
+ # Validate the following
+ # 1. Create a domain and its admin account
+ # 2. Set account primary storage limit well beyond (20 GB volume + template size of VM)
+ # 3. Deploy a VM without any disk offering (only root disk)
+ # 4. Create a volume of 5 GB in the account and attach it to the VM
+ # 5. Increase (resize) the volume to 20 GB
+ # 6. Resize opearation should be successful and primary storage counnt for
+ # account should be updated successfully"""
+
+ # Setting up account and domain hierarchy
+ result = self.setupAccounts()
+ self.assertEqual(result[0], PASS, result[1])
+
+ apiclient = self.testClient.getUserApiClient(
+ UserName=self.parentd_admin.name,
+ DomainName=self.parentd_admin.domain)
+ self.assertNotEqual(apiclient, FAILED, "Failed to get api client\
+ of account: %s" % self.parentd_admin.name)
+
+ templateSize = (self.template.size / (1024**3))
+ accountLimit = (templateSize + self.disk_offering_20_GB.disksize)
+ response = self.updateResourceLimits(accountLimit=accountLimit)
+ self.assertEqual(response[0], PASS, response[1])
+ try:
+ virtualMachine = VirtualMachine.create(
+ apiclient, self.services["virtual_machine"],
+ accountid=self.parentd_admin.name, domainid=self.parent_domain.id,
+ serviceofferingid=self.service_offering.id
+ )
+
+ volume = Volume.create(
+ apiclient,self.services["volume"],zoneid=self.zone.id,
+ account=self.parentd_admin.name,domainid=self.parent_domain.id,
+ diskofferingid=self.disk_offering_5_GB.id)
+
+ virtualMachine.attach_volume(apiclient, volume=volume)
+
+ expectedCount = (templateSize + self.disk_offering_5_GB.disksize)
+ response = matchResourceCount(
+ self.apiclient, expectedCount,
+ RESOURCE_PRIMARY_STORAGE,
+ accountid=self.parentd_admin.id)
+ if response[0] == FAIL:
+ raise Exception(response[1])
+
+ if self.hypervisor == str(XEN_SERVER).lower():
+ virtualMachine.stop(self.apiclient)
+ volume.resize(apiclient, diskofferingid=self.disk_offering_20_GB.id)
+
+ expectedCount = (templateSize + self.disk_offering_20_GB.disksize)
+ response = matchResourceCount(
+ self.apiclient, expectedCount,
+ RESOURCE_PRIMARY_STORAGE,
+ accountid=self.parentd_admin.id)
+ if response[0] == FAIL:
+ raise Exception(response[1])
+ except Exception as e:
+ self.fail("Failed with exception: %s" % e)
+ return
+
+ @attr(tags=["advanced", "selfservice"])
+ def test_02_increase_volume_size_above_account_limit(self):
+ """Test increasing volume size above the account limit
+
+ # Validate the following
+ # 1. Create a domain and its admin account
+ # 2. Set account primary storage limit more than (5 GB volume + template size of VM)
+ # and less than (20 GB volume+ template size of VM)
+ # 3. Deploy a VM without any disk offering (only root disk)
+ # 4. Create a volume of 5 GB in the account and attach it to the VM
+ # 5. Try to (resize) the volume to 20 GB
+ # 6. Resize opearation should fail"""
+
+ # Setting up account and domain hierarchy
+ result = self.setupAccounts()
+ self.assertEqual(result[0], PASS, result[1])
+
+ templateSize = (self.template.size / (1024**3))
+ accountLimit = ((templateSize + self.disk_offering_20_GB.disksize) - 1)
+ response = self.updateResourceLimits(accountLimit=accountLimit)
+ self.assertEqual(response[0], PASS, response[1])
+
+ apiclient = self.testClient.getUserApiClient(
+ UserName=self.parentd_admin.name,
+ DomainName=self.parentd_admin.domain)
+ self.assertNotEqual(apiclient, FAILED, "Failed to get api client\
+ of account: %s" % self.parentd_admin.name)
+
+ try:
+ virtualMachine = VirtualMachine.create(
+ apiclient, self.services["virtual_machine"],
+ accountid=self.parentd_admin.name, domainid=self.parent_domain.id,
+ serviceofferingid=self.service_offering.id
+ )
+
+ volume = Volume.create(
+ apiclient,self.services["volume"],zoneid=self.zone.id,
+ account=self.parentd_admin.name,domainid=self.parent_domain.id,
+ diskofferingid=self.disk_offering_5_GB.id)
+
+ virtualMachine.attach_volume(apiclient, volume=volume)
+
+ expectedCount = (templateSize + self.disk_offering_5_GB.disksize)
+ response = matchResourceCount(
+ self.apiclient, expectedCount,
+ RESOURCE_PRIMARY_STORAGE,
+ accountid=self.parentd_admin.id)
+ if response[0] == FAIL:
+ raise Exception(response[1])
+ except Exception as e:
+ self.fail("Failed with exception: %s" % e)
+
+ if self.hypervisor == str(XEN_SERVER).lower():
+ virtualMachine.stop(self.apiclient)
+ with self.assertRaises(Exception):
+ volume.resize(apiclient, diskofferingid=self.disk_offering_20_GB.id)
+ return
+
+ @attr(tags=["advanced", "selfservice"])
+ def test_03_increase_volume_size_above_domain_limit(self):
+ """Test increasing volume size above the domain limit
+
+ # Validate the following
+ # 1. Create a domain and its admin account
+ # 2. Set domain primary storage limit more than (5 GB volume + template size of VM)
+ # and less than (20 GB volume+ template size of VM)
+ # 3. Deploy a VM without any disk offering (only root disk)
+ # 4. Create a volume of 5 GB in the account and attach it to the VM
+ # 5. Try to (resize) the volume to 20 GB
+ # 6. Resize opearation should fail"""
+
+ # Setting up account and domain hierarchy
+ result = self.setupAccounts()
+ self.assertEqual(result[0], PASS, result[1])
+
+ templateSize = (self.template.size / (1024**3))
+ domainLimit = ((templateSize + self.disk_offering_20_GB.disksize) - 1)
+ response = self.updateResourceLimits(domainLimit=domainLimit)
+ self.assertEqual(response[0], PASS, response[1])
+
+ apiclient = self.testClient.getUserApiClient(
+ UserName=self.parentd_admin.name,
+ DomainName=self.parentd_admin.domain)
+ self.assertNotEqual(apiclient, FAILED, "Failed to get api client\
+ of account: %s" % self.parentd_admin.name)
+
+ try:
+ virtualMachine = VirtualMachine.create(
+ apiclient, self.services["virtual_machine"],
+ accountid=self.parentd_admin.name, domainid=self.parent_domain.id,
+ serviceofferingid=self.service_offering.id
+ )
+
+ volume = Volume.create(
+ apiclient,self.services["volume"],zoneid=self.zone.id,
+ account=self.parentd_admin.name,domainid=self.parent_domain.id,
+ diskofferingid=self.disk_offering_5_GB.id)
+
+ virtualMachine.attach_volume(apiclient, volume=volume)
+
+ expectedCount = (templateSize + self.disk_offering_5_GB.disksize)
+ result = isDomainResourceCountEqualToExpectedCount(
+ self.apiclient, self.parent_domain.id,
+ expectedCount, RESOURCE_PRIMARY_STORAGE)
+ self.assertFalse(result[0], result[1])
+ self.assertTrue(result[2], "Resource count does not match")
+ except Exception as e:
+ self.fail("Failed with exception: %s" % e)
+
+ if self.hypervisor == str(XEN_SERVER).lower():
+ virtualMachine.stop(self.apiclient)
+ with self.assertRaises(Exception):
+ volume.resize(apiclient, diskofferingid=self.disk_offering_20_GB.id)
+ return
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/12b6cf1b/tools/marvin/marvin/codes.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/codes.py b/tools/marvin/marvin/codes.py
index c72a6bd..e6e5602 100644
--- a/tools/marvin/marvin/codes.py
+++ b/tools/marvin/marvin/codes.py
@@ -30,12 +30,28 @@
@DateAdded: 20th October 2013
"""
+'''
+VM STATES - START
+'''
RUNNING = "Running"
STOPPED = "Stopped"
STOPPING = "Stopping"
STARTING = "Starting"
DESTROYED = "Destroyed"
EXPUNGING = "Expunging"
+'''
+VM STATES - END
+'''
+
+'''
+Snapshot States - START
+'''
+BACKED_UP = "backedup"
+BACKING_UP = "backingup"
+'''
+Snapshot States - END
+'''
+
RECURRING = "RECURRING"
ENABLED = "Enabled"
NETWORK_OFFERING = "network_offering"
@@ -82,3 +98,11 @@ USER = 0
XEN_SERVER = "XenServer"
ADMIN_ACCOUNT = 'ADMIN_ACCOUNT'
USER_ACCOUNT = 'USER_ACCOUNT'
+RESOURCE_CPU = 8
+RESOURCE_MEMORY = 9
+RESOURCE_PRIMARY_STORAGE = 10
+RESOURCE_SECONDARY_STORAGE = 11
+KVM = "kvm"
+VMWARE = "vmware"
+ROOT_DOMAIN_ADMIN="root domain admin"
+CHILD_DOMAIN_ADMIN="child domain admin"
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/12b6cf1b/tools/marvin/marvin/lib/base.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 9b011f2..d45b7cc 100644
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -20,11 +20,10 @@
"""
import marvin
-from utils import is_server_ssh_ready, random_gen
from marvin.cloudstackAPI import *
from marvin.codes import (FAILED, FAIL, PASS, RUNNING, STOPPED,
STARTING, DESTROYED, EXPUNGING,
- STOPPING)
+ STOPPING, BACKED_UP, BACKING_UP)
from marvin.cloudstackException import GetDetailExceptionInfo
from marvin.lib.utils import validateList, is_server_ssh_ready, random_gen
# Import System modules
@@ -957,6 +956,12 @@ 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)
@@ -990,6 +995,33 @@ class Snapshot:
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 Template:
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/12b6cf1b/tools/marvin/marvin/lib/common.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/common.py b/tools/marvin/marvin/lib/common.py
index 7753385..42ffc51 100644
--- a/tools/marvin/marvin/lib/common.py
+++ b/tools/marvin/marvin/lib/common.py
@@ -60,12 +60,29 @@ from marvin.cloudstackAPI import (listConfigurations,
from marvin.sshClient import SshClient
-from marvin.codes import (PASS, ISOLATED_NETWORK, VPC_NETWORK,
- BASIC_ZONE, FAIL, NAT_RULE, STATIC_NAT_RULE, FAILED)
+from marvin.codes import (PASS, FAILED, ISOLATED_NETWORK, VPC_NETWORK,
+ BASIC_ZONE, FAIL, NAT_RULE, STATIC_NAT_RULE,
+ RESOURCE_PRIMARY_STORAGE, RESOURCE_SECONDARY_STORAGE,
+ RESOURCE_CPU, RESOURCE_MEMORY)
+from marvin.lib.utils import (validateList, xsplit, get_process_status)
+from marvin.lib.base import (PhysicalNetwork,
+ PublicIPAddress,
+ NetworkOffering,
+ NATRule,
+ StaticNATRule,
+ Volume,
+ Account,
+ Project,
+ Snapshot,
+ NetScaler,
+ VirtualMachine,
+ FireWallRule,
+ Template,
+ Network,
+ Host,
+ Resources,
+ Configurations)
import random
-from marvin.lib.utils import *
-from marvin.lib.base import *
-from marvin.codes import PASS
# Import System modules
@@ -1222,3 +1239,111 @@ def getPortableIpRangeServices(config):
services = FAILED
return services
+
+
+def uploadVolume(apiclient, zoneid, account, services):
+ try:
+ # Upload the volume
+ volume = Volume.upload(apiclient, services["volume"],
+ zoneid=zoneid, account=account.name,
+ domainid=account.domainid, url=services["url"])
+
+ volume.wait_for_upload(apiclient)
+
+ # Check List Volume response for newly created volume
+ volumes = Volume.list(apiclient, id=volume.id,
+ zoneid=zoneid, listall=True)
+ validationresult = validateList(volumes)
+ assert validationresult[0] == PASS,\
+ "volumes list validation failed: %s" % validationresult[2]
+ assert str(volumes[0].state).lower() == "uploaded",\
+ "Volume state should be 'uploaded' but it is %s" % volumes[0].state
+ except Exception as e:
+ return [FAIL, e]
+ return [PASS, volume]
+
+def matchResourceCount(apiclient, expectedCount, resourceType,
+ accountid=None, projectid=None):
+ """Match the resource count of account/project with the expected
+ resource count"""
+ try:
+ resourceholderlist = None
+ if accountid:
+ resourceholderlist = Account.list(apiclient, id=accountid)
+ elif projectid:
+ resourceholderlist = Project.list(apiclient, id=projectid, listall=True)
+ validationresult = validateList(resourceholderlist)
+ assert validationresult[0] == PASS,\
+ "accounts list validation failed"
+ if resourceType == RESOURCE_PRIMARY_STORAGE:
+ resourceCount = resourceholderlist[0].primarystoragetotal
+ elif resourceType == RESOURCE_SECONDARY_STORAGE:
+ resourceCount = resourceholderlist[0].secondarystoragetotal
+ elif resourceType == RESOURCE_CPU:
+ resourceCount = resourceholderlist[0].cputotal
+ elif resourceType == RESOURCE_MEMORY:
+ resourceCount = resourceholderlist[0].memorytotal
+ assert str(resourceCount) == str(expectedCount),\
+ "Resource count %s should match with the expected resource count %s" %\
+ (resourceCount, expectedCount)
+ except Exception as e:
+ return [FAIL, e]
+ return [PASS, None]
+
+def createSnapshotFromVirtualMachineVolume(apiclient, account, vmid):
+ """Create snapshot from volume"""
+
+ try:
+ volumes = Volume.list(apiclient, account=account.name,
+ domainid=account.domainid, virtualmachineid=vmid)
+ validationresult = validateList(volumes)
+ assert validateList(volumes)[0] == PASS,\
+ "List volumes should return a valid response"
+ snapshot = Snapshot.create(apiclient, volume_id=volumes[0].id,
+ account=account.name, domainid=account.domainid)
+ snapshots = Snapshot.list(apiclient, id=snapshot.id,
+ listall=True)
+ validationresult = validateList(snapshots)
+ assert validationresult[0] == PASS,\
+ "List snapshot should return a valid list"
+ except Exception as e:
+ return[FAIL, e]
+ return [PASS, snapshot]
+
+def isVmExpunged(apiclient, vmid, projectid=None, timeout=600):
+ """Verify if VM is expunged or not"""
+ vmExpunged= False
+ while timeout>=0:
+ try:
+ vms = VirtualMachine.list(apiclient, id=vmid, projectid=projectid)
+ if vms is None:
+ vmExpunged = True
+ break
+ timeout -= 60
+ time.sleep(60)
+ except Exception:
+ vmExpunged = True
+ break
+ #end while
+ return vmExpunged
+
+def isDomainResourceCountEqualToExpectedCount(apiclient, domainid, expectedcount,
+ resourcetype):
+ """Get the resource count of specific domain and match
+ it with the expected count
+ Return list [isExceptionOccured, reasonForException, isResourceCountEqual]"""
+ isResourceCountEqual = False
+ isExceptionOccured = False
+ reasonForException = None
+ try:
+ response = Resources.updateCount(apiclient, domainid=domainid,
+ resourcetype=resourcetype)
+ except Exception as e:
+ reasonForException = "Failed while updating resource count: %s" % e
+ isExceptionOccured = True
+ return [isExceptionOccured, reasonForException, isResourceCountEqual]
+
+ resourcecount = (response[0].resourcecount / (1024**3))
+ if resourcecount == expectedcount:
+ isResourceCountEqual = True
+ return [isExceptionOccured, reasonForException, isResourceCountEqual]