Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 910A911D03 for ; Mon, 28 Jul 2014 21:13:40 +0000 (UTC) Received: (qmail 80789 invoked by uid 500); 28 Jul 2014 21:13:30 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 80716 invoked by uid 500); 28 Jul 2014 21:13:30 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 79811 invoked by uid 99); 28 Jul 2014 21:13:29 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 28 Jul 2014 21:13:29 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id CCB1F9B8A50; Mon, 28 Jul 2014 21:13:29 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: dahn@apache.org To: commits@cloudstack.apache.org Date: Mon, 28 Jul 2014 21:13:54 -0000 Message-Id: In-Reply-To: <294c500168744efe89e6fc45a6409bfa@git.apache.org> References: <294c500168744efe89e6fc45a6409bfa@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [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]