diff --git a/test/integration/component/test_ps_domain_limits.py b/test/integration/component/test_ps_domain_limits.py new file mode 100644 index 00000000000..afb09557041 --- /dev/null +++ b/test/integration/component/test_ps_domain_limits.py @@ -0,0 +1,690 @@ +# 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 primary storage domain limits + + 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, + Volume, + DiskOffering, + Snapshot) +from marvin.lib.common import (get_domain, + get_zone, + get_template, + createSnapshotFromVirtualMachineVolume, + isVmExpunged, + isDomainResourceCountEqualToExpectedCount) +from marvin.lib.utils import (cleanup_resources) +from marvin.codes import (PASS, + FAIL, + FAILED, + RESOURCE_PRIMARY_STORAGE) + +class TestMultipleChildDomain(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cloudstackTestClient = super(TestMultipleChildDomain, + 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.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.service_offering = ServiceOffering.create(cls.api_client, + cls.services["service_offering"]) + cls._cleanup.append(cls.service_offering) + except Exception as e: + cls.tearDownClass() + raise unittest.SkipTest("Exception in setUpClass: %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 = [] + self.services["disk_offering"]["disksize"] = 5 + try: + self.disk_offering = DiskOffering.create( + self.apiclient, + self.services["disk_offering"] + ) + self.assertNotEqual(self.disk_offering, None, \ + "Disk offering is None") + self.cleanup.append(self.disk_offering) + except Exception as e: + self.tearDown() + self.skipTest("Failure while creating disk offering: %s" % e) + 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 updateDomainResourceLimits(self, parentdomainlimit, subdomainlimit): + """Update primary storage limits of the parent domain and its + child domains""" + + try: + #Update resource limit for domain + Resources.updateLimit(self.apiclient, resourcetype=10, + max=parentdomainlimit, + domainid=self.parent_domain.id) + + # Update Resource limit for sub-domains + Resources.updateLimit(self.apiclient, resourcetype=10, + max=subdomainlimit, + domainid=self.cadmin_1.domainid) + + Resources.updateLimit(self.apiclient, resourcetype=10, + max=subdomainlimit, + domainid=self.cadmin_2.domainid) + 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) + + # Create sub-domains and their admin accounts + self.cdomain_1 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) + self.cdomain_2 = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.parent_domain.id) + + self.cadmin_1 = Account.create(self.apiclient, self.services["account"], + admin=True, domainid=self.cdomain_1.id) + + self.cadmin_2 = Account.create(self.apiclient, self.services["account"], + admin=True, domainid=self.cdomain_2.id) + + # Cleanup the resources created at end of test + self.cleanup.append(self.cadmin_1) + self.cleanup.append(self.cadmin_2) + self.cleanup.append(self.cdomain_1) + self.cleanup.append(self.cdomain_2) + self.cleanup.append(self.parentd_admin) + self.cleanup.append(self.parent_domain) + + users = { + self.cdomain_1: self.cadmin_1, + self.cdomain_2: self.cadmin_2 + } + except Exception as e: + return [FAIL, e, None] + return [PASS, None, users] + + @attr(tags=["advanced","selfservice"]) + def test_01_multiple_domains_primary_storage_limits(self): + """Test primary storage limit of domain and its sub-domains + + # Steps + 1. Create a parent domain and two sub-domains in it (also admin accounts + of each domain) + 2. Update primary storage limits of the parent domain and child domains + 3. Deploy VM in child domain 1 so that total primary storage + is less than the limit of child domain + 4. Repeat step 3 for child domain 2 + 5. Try to deploy VM in parent domain now so that the total primary storage in + parent domain (including that in sub-domains is more than the primary + storage limit of the parent domain + 6. Delete the admin account of child domain 1 and check resource count + of the parent domain + 7. Delete VM deployed in account 2 and check primary storage count + of parent domain + + # Validations: + 1. Step 3 and 4 should succeed + 2. Step 5 should fail as the resource limit exceeds in parent domain + 3. After step 6, resource count in parent domain should decrease by equivalent + quantity + 4. After step 7, resource count in parent domain should be 0""" + + # Setting up account and domain hierarchy + result = self.setupAccounts() + self.assertEqual(result[0], PASS,\ + "Failure while setting up accounts and domains: %s" % result[1]) + + templatesize = (self.template.size / (1024**3)) + disksize = 10 + subdomainlimit = (templatesize + disksize) + + result = self.updateDomainResourceLimits(((subdomainlimit*3)- 1), subdomainlimit) + self.assertEqual(result[0], PASS,\ + "Failure while updating resource limits: %s" % result[1]) + + try: + self.services["disk_offering"]["disksize"] = disksize + disk_offering_custom = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + self.cleanup.append(disk_offering_custom) + except Exception as e: + self.fail("Failed to create disk offering") + + # Get API clients of parent and child domain admin accounts + api_client_admin = self.testClient.getUserApiClient( + UserName=self.parentd_admin.name, + DomainName=self.parentd_admin.domain) + self.assertNotEqual(api_client_admin, FAILED,\ + "Failed to create api client for account: %s" % self.parentd_admin.name) + + api_client_cadmin_1 = self.testClient.getUserApiClient( + UserName=self.cadmin_1.name, + DomainName=self.cadmin_1.domain) + self.assertNotEqual(api_client_cadmin_1, FAILED,\ + "Failed to create api client for account: %s" % self.cadmin_1.name) + + api_client_cadmin_2 = self.testClient.getUserApiClient( + UserName=self.cadmin_2.name, + DomainName=self.cadmin_2.domain) + self.assertNotEqual(api_client_cadmin_2, FAILED,\ + "Failed to create api client for account: %s" % self.cadmin_2.name) + + VirtualMachine.create( + api_client_cadmin_1, self.services["virtual_machine"], + accountid=self.cadmin_1.name, domainid=self.cadmin_1.domainid, + diskofferingid=disk_offering_custom.id, serviceofferingid=self.service_offering.id + ) + + self.initialResourceCount = (templatesize + disksize) + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.parent_domain.id, + self.initialResourceCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + # Create VM in second child domain + vm_2 = VirtualMachine.create( + api_client_cadmin_2, self.services["virtual_machine"], + accountid=self.cadmin_2.name, domainid=self.cadmin_2.domainid, + diskofferingid=disk_offering_custom.id, serviceofferingid=self.service_offering.id + ) + + # Now the VMs in two child domains have exhausted the primary storage limit + # of parent domain, hence VM creation in parent domain with custom disk offering + # should fail + with self.assertRaises(Exception): + VirtualMachine.create( + api_client_admin, self.services["virtual_machine"], + accountid=self.parentd_admin.name, domainid=self.parentd_admin.domainid, + diskofferingid=disk_offering_custom.id, serviceofferingid=self.service_offering.id + ) + + # Deleting user account + self.cadmin_1.delete(self.apiclient) + self.cleanup.remove(self.cadmin_1) + + expectedCount = self.initialResourceCount + 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") + + try: + vm_2.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + self.assertTrue(isVmExpunged(self.apiclient, vm_2.id), "VM not expunged \ + in allotted time") + + expectedCount = 0 + 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") + return + + @attr(tags=["advanced", "selfservice"]) + def test_02_multiple_domains_primary_storage_limits(self): + """Test primary storage counts in multiple child domains + # Steps + 1. Create a parent domain and two sub-domains in it (also admin accounts + of each domain) + Repeat following steps for both the child domains + 2. Deploy VM in child domain + 3. Check if the resource count for domain is updated correctly + 4. Create a volume and attach it to the VM + 5. Check if the primary storage resource count is updated correctly + + """ + + # Setting up account and domain hierarchy + result = self.setupAccounts() + self.assertEqual(result[0], PASS,\ + "Failure while setting up accounts and domains: %s" % result[1]) + users = result[2] + + templatesize = (self.template.size / (1024**3)) + + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + apiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + self.assertNotEqual(apiclient, FAILED,\ + "Failed to create api client for account: %s" % self.account.name) + try: + vm = VirtualMachine.create( + apiclient, self.services["virtual_machine"], + accountid=self.account.name, domainid=self.account.domainid, + diskofferingid=self.disk_offering.id, serviceofferingid=self.service_offering.id + ) + + expectedCount = templatesize + self.disk_offering.disksize + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + # Creating service offering with 10 GB volume + self.services["disk_offering"]["disksize"] = 10 + disk_offering_10_GB = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + + self.cleanup.append(disk_offering_10_GB) + + volume = Volume.create( + apiclient, self.services["volume"], + zoneid=self.zone.id, account=self.account.name, + domainid=self.account.domainid, diskofferingid=disk_offering_10_GB.id + ) + + volumeSize = (volume.size / (1024**3)) + expectedCount += volumeSize + + vm.attach_volume(apiclient, volume=volume) + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + expectedCount -= volumeSize + vm.detach_volume(apiclient, volume=volume) + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.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("Failure: %s" % e) + return + + @attr(tags=["advanced", "selfservice"]) + def test_03_multiple_domains_multiple_volumes(self): + """Test primary storage counts in multiple child domains + # Steps + 1. Create a parent domain and two sub-domains in it (also admin accounts + of each domain) + Repeat following steps for both the child domains + 2. Deploy VM in child domain + 3. Check if the resource count for domain is updated correctly + 4. Create multiple volumes and attach it to the VM + 5. Check if the primary storage resource count is updated correctly + 6. Delete one of the volumes and check if the primary storage resource count + reduced by equivalent number + 7. Detach other volume and check primary storage resource count remains the same + + """ + # Setting up account and domain hierarchy + result = self.setupAccounts() + if result[0] == FAIL: + self.fail("Failure while setting up accounts and domains: %s" % result[1]) + else: + users = result[2] + + templatesize = (self.template.size / (1024**3)) + + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + apiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + self.assertNotEqual(apiclient, FAILED,\ + "Failed to create api client for account: %s" % self.account.name) + + try: + vm = VirtualMachine.create( + apiclient, self.services["virtual_machine"], + accountid=self.account.name, domainid=self.account.domainid, + diskofferingid=self.disk_offering.id, serviceofferingid=self.service_offering.id + ) + + expectedCount = templatesize + self.disk_offering.disksize + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + volume1size = self.services["disk_offering"]["disksize"] = 15 + disk_offering_15_GB = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + + self.cleanup.append(disk_offering_15_GB) + + volume2size = self.services["disk_offering"]["disksize"] = 20 + disk_offering_20_GB = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + + self.cleanup.append(disk_offering_20_GB) + + volume_1 = Volume.create( + apiclient, self.services["volume"], + zoneid=self.zone.id, account=self.account.name, + domainid=self.account.domainid, diskofferingid=disk_offering_15_GB.id + ) + + volume_2 = Volume.create( + apiclient, self.services["volume"], + zoneid=self.zone.id, account=self.account.name, + domainid=self.account.domainid, diskofferingid=disk_offering_20_GB.id + ) + + vm.attach_volume(apiclient, volume=volume_1) + vm.attach_volume(apiclient, volume=volume_2) + + expectedCount += volume1size + volume2size + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + vm.detach_volume(apiclient, volume=volume_1) + volume_1.delete(apiclient) + + expectedCount -= volume1size + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + expectedCount -= volume2size + vm.detach_volume(apiclient, volume=volume_2) + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.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("Failure: %s" % e) + return + + @attr(tags=["advanced", "selfservice"]) + def test_04_create_template_snapshot(self): + """Test create snapshot and templates from volume + + # Validate the following + 1. Create parent domain with two child sub-domains (and their admin accounts) + Follow these steps for both the domains + # 1. Create template from snapshot and verify secondary storage resource count + # 2. Create Volume from Snapshot and verify primary storage resource count + # 3. Attach volume to instance which was created from snapshot and + # verify primary storage resource count + # 4. Detach volume from instance which was created from snapshot and + # verify the primary storage resource count + # 5. Delete volume which was created from snapshot and verify primary storage + resource count""" + + result = self.setupAccounts() + if result[0] == FAIL: + self.fail("Failure while setting up accounts and domains: %s" % result[1]) + users = result[2] + + for domain, admin in users.items(): + self.account = admin + self.domain = domain + + try: + apiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + self.assertNotEqual(apiclient, FAILED,\ + "Failed to create api client for account: %s" % self.account.name) + + vm = VirtualMachine.create( + apiclient, self.services["virtual_machine"], + accountid=self.account.name, domainid=self.account.domainid, + diskofferingid=self.disk_offering.id, serviceofferingid=self.service_offering.id + ) + + templatesize = (self.template.size / (1024**3)) + + initialResourceCount = expectedCount = templatesize + self.disk_offering.disksize + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + initialResourceCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + vm.stop(self.apiclient) + + response = createSnapshotFromVirtualMachineVolume(apiclient, self.account, vm.id) + self.assertEqual(response[0], PASS, response[1]) + snapshot = response[1] + + response = snapshot.validateState(apiclient, Snapshot.BACKED_UP) + self.assertEqual(response[0], PASS, response[1]) + + self.services["volume"]["size"] = self.services["disk_offering"]["disksize"] + volume = Volume.create_from_snapshot(apiclient, + snapshot_id=snapshot.id, + services=self.services["volume"], + account=self.account.name, + domainid=self.account.domainid) + volumeSize = (volume.size / (1024**3)) + vm.attach_volume(apiclient, volume) + expectedCount = initialResourceCount + (volumeSize) + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + expectedCount -= volumeSize + vm.detach_volume(apiclient, volume) + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.domain.id, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + volume.delete(apiclient) + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.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) + return + + @attr(tags=["advanced", "selfservice"]) + def test_05_assign_virtual_machine_different_domain(self): + """Test assign virtual machine to account belonging to different domain + + # Steps + 1. Create a parent domain and two sub-domains in it (also admin accounts + of each domain) + 2. Deploy VM in child domain 1 + 3. Check if the resource count for domain 1 is updated correctly + 4. Assign this virtual machine to account 2 in domain 2 + 5. Verify that primaru storage resource count of domain 1 is now 0 and + primary storage resource count of domain 2 is increased by equivalent number + """ + + # Setting up account and domain hierarchy + result = self.setupAccounts() + self.assertEqual(result[0], PASS, result[1]) + + apiclient = self.testClient.getUserApiClient( + UserName=self.cadmin_1.name, + DomainName=self.cadmin_1.domain) + self.assertNotEqual(apiclient, FAILED,\ + "Failed to create api client for account: %s" % self.cadmin_1.name) + + try: + vm_1 = VirtualMachine.create( + apiclient, self.services["virtual_machine"], + accountid=self.cadmin_1.name, domainid=self.cadmin_1.domainid, + diskofferingid=self.disk_offering.id, serviceofferingid=self.service_offering.id + ) + + templatesize = (self.template.size / (1024**3)) + + expectedCount = templatesize + self.disk_offering.disksize + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.cadmin_1.domainid, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + vm_1.stop(apiclient) + vm_1.assign_virtual_machine(self.apiclient, account=self.cadmin_2.name, + domainid=self.cadmin_2.domainid) + + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.cadmin_2.domainid, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + expectedCount = 0 + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.cadmin_1.domainid, + 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) + return + + @attr(tags=["advanced", "selfservice"]) + def test_06_destroy_recover_vm(self): + """Test primary storage counts while destroying and recovering VM + # Steps + 1. Create a parent domain and two sub-domains in it (also admin accounts + of each domain) + Repeat following steps for both the child domains + 2. Deploy VM in child domain + 3. Check if the resource count for domain is updated correctly + 4. Destroy the VM + 5. Verify that the primary storage resource count remains the same + 6. Recover the VM + 7. Verify that the primary storage resource count remains the same + """ + + # Setting up account and domain hierarchy + result = self.setupAccounts() + self.assertEqual(result[0], PASS, result[1]) + users = result[2] + + for domain, admin in users.items(): + self.account = admin + self.domain = domain + try: + vm_1 = VirtualMachine.create( + self.apiclient, self.services["virtual_machine"], + accountid=self.account.name, domainid=self.account.domainid, + diskofferingid=self.disk_offering.id, serviceofferingid=self.service_offering.id + ) + + templatesize = (self.template.size / (1024**3)) + + expectedCount = templatesize + self.disk_offering.disksize + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.account.domainid, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + vm_1.delete(self.apiclient) + + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.account.domainid, + expectedCount, RESOURCE_PRIMARY_STORAGE) + self.assertFalse(result[0], result[1]) + self.assertTrue(result[2], "Resource count does not match") + + vm_1.recover(self.apiclient) + + result = isDomainResourceCountEqualToExpectedCount( + self.apiclient, self.account.domainid, + 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) + return diff --git a/test/integration/component/test_ps_limits.py b/test/integration/component/test_ps_limits.py new file mode 100644 index 00000000000..1993e934b3b --- /dev/null +++ b/test/integration/component/test_ps_limits.py @@ -0,0 +1,588 @@ +# 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 primary storage limits + + 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, + Domain, + Volume, + DiskOffering) +from marvin.lib.common import (get_domain, + get_zone, + get_template, + matchResourceCount, + createSnapshotFromVirtualMachineVolume, + isVmExpunged) +from marvin.lib.utils import (cleanup_resources, + validateList) +from marvin.codes import (PASS, + FAIL, + RESOURCE_PRIMARY_STORAGE, + CHILD_DOMAIN_ADMIN, + ROOT_DOMAIN_ADMIN) +from ddt import ddt, data + +@ddt +class TestVolumeLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cloudstackTestClient = super(TestVolumeLimits, + 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.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.service_offering = ServiceOffering.create(cls.api_client, cls.services["service_offering"]) + cls._cleanup.append(cls.service_offering) + except Exception as e: + cls.tearDownClass() + raise unittest.SkipTest("Exception in setUpClass: %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 = [] + try: + self.services["disk_offering"]["disksize"] = 2 + self.disk_offering = DiskOffering.create(self.apiclient, self.services["disk_offering"]) + self.assertNotEqual(self.disk_offering, None,\ + "Disk offering is None") + self.cleanup.append(self.disk_offering) + except Exception as e: + self.tearDown() + self.skipTest("Failure in setup: %s" % e) + 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 setupAccount(self, accountType): + """Setup the account required for the test""" + + try: + if accountType == CHILD_DOMAIN_ADMIN: + self.domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + + self.account = Account.create(self.apiclient, self.services["account"], + domainid=self.domain.id, admin=True) + self.cleanup.append(self.account) + if accountType == CHILD_DOMAIN_ADMIN: + self.cleanup.append(self.domain) + + self.virtualMachine = VirtualMachine.create(self.api_client, self.services["virtual_machine"], + accountid=self.account.name, domainid=self.account.domainid, + diskofferingid=self.disk_offering.id, + serviceofferingid=self.service_offering.id) + + accounts = Account.list(self.apiclient, id=self.account.id) + + self.assertEqual(validateList(accounts)[0], PASS, + "accounts list validation failed") + + self.initialResourceCount = int(accounts[0].primarystoragetotal) + except Exception as e: + return [FAIL, e] + return [PASS, None] + + @data(ROOT_DOMAIN_ADMIN, CHILD_DOMAIN_ADMIN) + @attr(tags=["advanced", "selfservice"]) + def test_stop_start_vm(self, value): + """Test Deploy VM with 5 GB volume & verify the usage + + # Validate the following + # 1. Create a VM with custom disk offering and check the primary storage count + # 2. Stop VM and verify the resource count remains same + # 3. Start VM and verify resource count remains same""" + + response = self.setupAccount(value) + self.assertEqual(response[0], PASS, response[1]) + + expectedCount = self.initialResourceCount + # Stopping instance + try: + self.virtualMachine.stop(self.apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + # Starting instance + try: + self.virtualMachine.start(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @unittest.skip("skip") + @data(ROOT_DOMAIN_ADMIN, CHILD_DOMAIN_ADMIN) + @attr(tags=["advanced", "selfservice"]) + def test_destroy_recover_vm(self, value): + """Test delete and recover instance + + # Validate the following + # 1. Create a VM with custom disk offering and check the primary storage count + # 2. Destroy VM and verify the resource count remains same + # 3. Recover VM and verify resource count remains same""" + + response = self.setupAccount(value) + self.assertEqual(response[0], PASS, response[1]) + + expectedCount = self.initialResourceCount + # Stopping instance + try: + self.virtualMachine.delete(self.apiclient) + except Exception as e: + self.fail("Failed to destroy instance: %s" % e) + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + # Recovering instance + try: + self.virtualMachine.recover(self.apiclient) + except Exception as e: + self.fail("Failed to start instance: %s" % e) + + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @data(ROOT_DOMAIN_ADMIN, CHILD_DOMAIN_ADMIN) + @attr(tags=["advanced", "selfservice"]) + def test_attach_detach_volume(self, value): + """Stop attach and detach volume from VM + + # Validate the following + # 1. Create a VM with custom disk offering and check the primary storage count + # of account + # 2. Create custom volume in account + # 3. Verify that primary storage count increases by same amount + # 4. Attach volume to VM and verify resource count remains the same + # 5. Detach volume and verify resource count remains the same""" + + response = self.setupAccount(value) + self.assertEqual(response[0], PASS, response[1]) + + apiclient = self.apiclient + if value == CHILD_DOMAIN_ADMIN: + apiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + self.assertNotEqual(apiclient, FAIL, "Failure while getting\ + api client of account: %s" % self.account.name) + + try: + self.services["disk_offering"]["disksize"] = 4 + expectedCount = self.initialResourceCount + int(self.services["disk_offering"]["disksize"]) + disk_offering = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + + self.cleanup.append(disk_offering) + + volume = Volume.create( + apiclient,self.services["volume"],zoneid=self.zone.id, + account=self.account.name,domainid=self.account.domainid, + diskofferingid=disk_offering.id) + except Exception as e: + self.fail("Failure: %s" % e) + + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + self.virtualMachine.attach_volume(apiclient, volume=volume) + except Exception as e: + self.fail("Failed while attaching volume to VM: %s" % e) + + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + self.virtualMachine.detach_volume(apiclient, volume=volume) + except Exception as e: + self.fail("Failure while detaching volume: %s" % e) + + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @data(ROOT_DOMAIN_ADMIN, CHILD_DOMAIN_ADMIN) + @attr(tags=["advanced", "selfservice"]) + def test_create_multiple_volumes(self, value): + """Test create multiple volumes + + # Validate the following + # 1. Create a VM with custom disk offering and check the primary storage count + # of account + # 2. Create multiple volumes in account + # 3. Verify that primary storage count increases by same amount + # 4. Attach volumes to VM and verify resource count remains the same + # 5. Detach and delete both volumes one by one and verify resource count decreases + # proportionately""" + + # Creating service offering with 10 GB volume + + response = self.setupAccount(value) + self.assertEqual(response[0], PASS, response[1]) + + apiclient = self.apiclient + if value == CHILD_DOMAIN_ADMIN: + apiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + self.assertNotEqual(apiclient, FAIL, "Failure while getting\ + api client of account %s" % self.account.name) + + try: + self.services["disk_offering"]["disksize"] = 5 + disk_offering_5_GB = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + self.cleanup.append(disk_offering_5_GB) + + self.services["disk_offering"]["disksize"] = 10 + disk_offering_10_GB = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + + self.cleanup.append(disk_offering_10_GB) + + volume_1 = Volume.create( + apiclient,self.services["volume"],zoneid=self.zone.id, + account=self.account.name,domainid=self.account.domainid, + diskofferingid=disk_offering_5_GB.id) + + volume_2 = Volume.create( + apiclient,self.services["volume"],zoneid=self.zone.id, + account=self.account.name,domainid=self.account.domainid, + diskofferingid=disk_offering_10_GB.id) + + self.debug("Attaching volume %s to vm %s" % (volume_1.name, self.virtualMachine.name)) + self.virtualMachine.attach_volume(apiclient, volume=volume_1) + + self.debug("Attaching volume %s to vm %s" % (volume_2.name, self.virtualMachine.name)) + self.virtualMachine.attach_volume(apiclient, volume=volume_2) + except Exception as e: + self.fail("Failure: %s" % e) + + expectedCount = self.initialResourceCount + 15 # (5 + 10) + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + # Detaching and deleting volume 1 + self.virtualMachine.detach_volume(apiclient, volume=volume_1) + volume_1.delete(apiclient) + except Exception as e: + self.fail("Failure while volume operation: %s" % e) + + expectedCount -= 5 #After deleting first volume + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + # Detaching and deleting volume 2 + self.virtualMachine.detach_volume(apiclient, volume=volume_2) + volume_2.delete(apiclient) + except Exception as e: + self.fail("Failure while volume operation: %s" % e) + + expectedCount -= 10 + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @data(ROOT_DOMAIN_ADMIN, CHILD_DOMAIN_ADMIN) + @attr(tags=["advanced", "selfservice"]) + def test_deploy_multiple_vm(self, value): + """Test Deploy multiple VMs with & verify the usage + # Validate the following + # 1. Deploy multiple VMs with this service offering + # 2. Update Resource count for the root admin Primary Storage usage + # 3. Primary Storage usage should list properly + # 4. Destroy one VM among multiple VM's and verify that primary storage count + # decreases by equivalent amount + """ + + response = self.setupAccount(value) + self.assertEqual(response[0], PASS, response[1]) + + self.virtualMachine_2 = VirtualMachine.create(self.api_client, self.services["virtual_machine"], + accountid=self.account.name, domainid=self.account.domainid, + diskofferingid=self.disk_offering.id, + serviceofferingid=self.service_offering.id) + + expectedCount = (self.initialResourceCount * 2) #Total 2 vms + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + self.virtualMachine_3 = VirtualMachine.create(self.api_client, self.services["virtual_machine"], + accountid=self.account.name, domainid=self.account.domainid, + diskofferingid=self.disk_offering.id, + serviceofferingid=self.service_offering.id) + + expectedCount = (self.initialResourceCount * 3) #Total 3 vms + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + self.debug("Destroying instance: %s" % self.virtualMachine_2.name) + try: + self.virtualMachine_2.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete instance: %s" % e) + + self.assertTrue(isVmExpunged(self.apiclient, self.virtualMachine_2.id), "VM not expunged \ + in allotted time") + + expectedCount = (self.initialResourceCount * 2) #Total 2 vms + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @data(ROOT_DOMAIN_ADMIN, CHILD_DOMAIN_ADMIN) + @attr(tags=["advanced","selfservice"]) + def test_assign_vm_different_account(self, value): + """Test assign Vm to different account + # Validate the following + # 1. Deploy VM in account and check the primary storage resource count + # 2. Assign VM to another account + # 3. Resource count for first account should now equal to 0 + # 4. Resource count for the account to which VM is assigned should + # increase to that of initial resource count of first account + """ + + response = self.setupAccount(value) + self.assertEqual(response[0], PASS, response[1]) + + try: + account_2 = Account.create(self.apiclient, self.services["account"], + domainid=self.domain.id, admin=True) + self.cleanup.insert(0, account_2) + except Exception as e: + self.fail("Failed to create account: %s" % e) + + expectedCount = self.initialResourceCount + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + self.virtualMachine.stop(self.apiclient) + self.virtualMachine.assign_virtual_machine(self.apiclient, + account_2.name ,account_2.domainid) + except Exception as e: + self.fail("Failed to assign virtual machine to account %s: %s" % + (account_2.name,e)) + + # Checking resource count for account 2 + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=account_2.id) + self.assertEqual(response[0], PASS, response[1]) + + expectedCount = 0 + # Checking resource count for original account + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @data(ROOT_DOMAIN_ADMIN, CHILD_DOMAIN_ADMIN) + @attr(tags=["advanced", "selfservice"]) + def test_create_template_snapshot(self, value): + """Test create snapshot and templates from volume + + # Validate the following + # 1. Deploy VM with custoom disk offering and check the + # primary storage resource count + # 2. Stop the VM and create Snapshot from VM's volume + # 3. Create volume againt from this snapshto and attach to VM + # 4. Verify that primary storage count increases by the volume size + # 5. Detach and delete volume, verify primary storage count decreaes by volume size""" + + response = self.setupAccount(value) + self.debug(response[0]) + self.debug(response[1]) + self.assertEqual(response[0], PASS, response[1]) + + apiclient = self.apiclient + if value == CHILD_DOMAIN_ADMIN: + apiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + self.assertNotEqual(apiclient, FAIL, "Failure while getting api\ + client of account: %s" % self.account.name) + + try: + self.virtualMachine.stop(apiclient) + except Exception as e: + self.fail("Failed to stop instance: %s" % e) + expectedCount = self.initialResourceCount + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + self.debug("Creating snapshot from ROOT volume: %s" % self.virtualMachine.name) + snapshot = None + response = createSnapshotFromVirtualMachineVolume(apiclient, self.account, self.virtualMachine.id) + self.assertEqual(response[0], PASS, response[1]) + snapshot = response[1] + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + self.services["volume"]["size"] = self.services["disk_offering"]["disksize"] + volume = Volume.create_from_snapshot(apiclient, + snapshot_id=snapshot.id, + services=self.services["volume"], + account=self.account.name, + domainid=self.account.domainid) + + self.debug("Attaching the volume to vm: %s" % self.virtualMachine.name) + self.virtualMachine.attach_volume(apiclient, volume) + except Exception as e: + self.fail("Failure in volume operation: %s" % e) + + expectedCount += int(self.services["volume"]["size"]) + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + self.virtualMachine.detach_volume(apiclient, volume) + except Exception as e: + self.fail("Failure in detach volume operation: %s" % e) + + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + self.debug("deleting the volume: %s" % volume.name) + volume.delete(apiclient) + except Exception as e: + self.fail("Failure while deleting volume: %s" % e) + + expectedCount -= int(self.services["volume"]["size"]) + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + accountid=self.account.id) + self.assertEqual(response[0], PASS, response[1]) + return diff --git a/test/integration/component/test_ps_max_limits.py b/test/integration/component/test_ps_max_limits.py new file mode 100644 index 00000000000..8d047ef992a --- /dev/null +++ b/test/integration/component/test_ps_max_limits.py @@ -0,0 +1,300 @@ +# 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. + +""" Tests for praimary storage - Maximum Limits + + 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, + Project, + Volume, + DiskOffering) +from marvin.lib.common import (get_domain, + get_zone, + get_template) +from marvin.lib.utils import (cleanup_resources, + validateList) +from marvin.codes import PASS, FAIL + +class TestMaxPrimaryStorageLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cloudstackTestClient = super(TestMaxPrimaryStorageLimits, + 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.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.service_offering = ServiceOffering.create(cls.api_client, cls.services["service_offering"]) + cls._cleanup.append(cls.service_offering) + except Exception as e: + cls.tearDownClass() + raise unittest.SkipTest("Exception in setUpClass: %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 = [] + try: + response = self.setupAccounts() + if response[0] == FAIL: + self.skipTest("Failure while setting up accounts: %s" % response[1]) + self.services["disk_offering"]["disksize"] = 2 + self.disk_offering = DiskOffering.create(self.apiclient, self.services["disk_offering"]) + self.cleanup.append(self.disk_offering) + except Exception as e: + self.tearDown() + self.skipTest("Failure in setup: %s" % e) + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setupAccounts(self): + + try: + self.child_domain = Domain.create(self.apiclient,services=self.services["domain"], + parentdomainid=self.domain.id) + + self.child_do_admin = Account.create(self.apiclient, self.services["account"], admin=True, + domainid=self.child_domain.id) + + # Create project as a domain admin + self.project = Project.create(self.apiclient, self.services["project"], + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid) + + # Cleanup created project at end of test + self.cleanup.append(self.project) + + # Cleanup accounts created + self.cleanup.append(self.child_do_admin) + self.cleanup.append(self.child_domain) + except Exception as e: + return [FAIL, e] + return [PASS, None] + + def updatePrimaryStorageLimits(self, accountLimit=None, domainLimit=None, + projectLimit=None): + + try: + # Update resource limits for account + if accountLimit: + Resources.updateLimit(self.apiclient, resourcetype=10, + max=accountLimit, account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid) + + if projectLimit: + Resources.updateLimit(self.apiclient, resourcetype=10, + max=projectLimit, projectid=self.project.id) + + if domainLimit: + Resources.updateLimit(self.apiclient, resourcetype=10, + max=domainLimit, domainid=self.child_domain.id) + except Exception as e: + return [FAIL, e] + return [PASS, None] + + @attr(tags=["advanced","selfservice"]) + def test_01_deploy_vm_domain_limit_reached(self): + """Test Try to deploy VM with admin account where account has not used + the resources but @ domain they are not available + + # Validate the following + # 1. Try to deploy VM with admin account where account has not used the + # resources but @ domain they are not available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds""" + + self.virtualMachine = VirtualMachine.create(self.api_client, self.services["virtual_machine"], + accountid=self.child_do_admin.name, domainid=self.child_do_admin.domainid, + serviceofferingid=self.service_offering.id) + + accounts = Account.list(self.apiclient, id=self.child_do_admin.id) + self.assertEqual(validateList(accounts)[0], PASS, + "accounts list validation failed") + + self.initialResourceCount = int(accounts[0].primarystoragetotal) + domainLimit = self.initialResourceCount + 3 + + self.debug("Setting up account and domain hierarchy") + response = self.updatePrimaryStorageLimits(domainLimit=domainLimit) + self.assertEqual(response[0], PASS, response[1]) + + self.services["volume"]["size"] = self.services["disk_offering"]["disksize"] = 2 + + try: + disk_offering = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + self.cleanup.append(disk_offering) + Volume.create(self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid, + diskofferingid=disk_offering.id) + except Exception as e: + self.fail("Exception occured: %s" % e) + + with self.assertRaises(Exception): + Volume.create(self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid, + diskofferingid=disk_offering.id) + return + + @attr(tags=["advanced","selfservice"]) + def test_02_deploy_vm_account_limit_reached(self): + """Test Try to deploy VM with admin account where account has used + the resources but @ domain they are available""" + + self.virtualMachine = VirtualMachine.create(self.api_client, self.services["virtual_machine"], + accountid=self.child_do_admin.name, domainid=self.child_do_admin.domainid, + diskofferingid=self.disk_offering.id, + serviceofferingid=self.service_offering.id) + + accounts = Account.list(self.apiclient, id=self.child_do_admin.id) + self.assertEqual(validateList(accounts)[0], PASS, + "accounts list validation failed") + + self.initialResourceCount = int(accounts[0].primarystoragetotal) + accountLimit = self.initialResourceCount + 3 + + self.debug("Setting up account and domain hierarchy") + response = self.updatePrimaryStorageLimits(accountLimit=accountLimit) + self.assertEqual(response[0], PASS, response[1]) + + self.services["volume"]["size"] = self.services["disk_offering"]["disksize"] = 2 + + try: + disk_offering = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + self.cleanup.append(disk_offering) + Volume.create(self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid, + diskofferingid=disk_offering.id) + except Exception as e: + self.fail("failed to create volume: %s" % e) + + with self.assertRaises(Exception): + Volume.create(self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.child_do_admin.name, + domainid=self.child_do_admin.domainid, + diskofferingid=disk_offering.id) + return + + @attr(tags=["advanced","selfservice"]) + def test_03_deploy_vm_project_limit_reached(self): + """Test TTry to deploy VM with admin account where account has not used + the resources but @ project they are not available + + # Validate the following + # 1. Try to deploy VM with admin account where account has not used the + # resources but @ project they are not available + # 2. Deploy VM should error out saying ResourceAllocationException + # with "resource limit exceeds""" + + self.virtualMachine = VirtualMachine.create(self.api_client, self.services["virtual_machine"], + projectid=self.project.id, + diskofferingid=self.disk_offering.id, + serviceofferingid=self.service_offering.id) + + try: + projects = Project.list(self.apiclient, id=self.project.id, listall=True) + except Exception as e: + self.fail("failed to get projects list: %s" % e) + + self.assertEqual(validateList(projects)[0], PASS, + "projects list validation failed") + self.initialResourceCount = int(projects[0].primarystoragetotal) + + projectLimit = self.initialResourceCount + 3 + + self.debug("Setting up account and domain hierarchy") + response = self.updatePrimaryStorageLimits(projectLimit=projectLimit) + self.assertEqual(response[0], PASS, response[1]) + + self.services["volume"]["size"] = self.services["disk_offering"]["disksize"] = 2 + + try: + disk_offering = DiskOffering.create(self.apiclient, + services=self.services["disk_offering"]) + self.cleanup.append(disk_offering) + Volume.create(self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + projectid=self.project.id, + diskofferingid=disk_offering.id) + except Exception as e: + self.fail("Exception occured: %s" % e) + + with self.assertRaises(Exception): + Volume.create(self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + projectid=self.project.id, + diskofferingid=disk_offering.id) + return diff --git a/test/integration/component/test_ps_project_limits.py b/test/integration/component/test_ps_project_limits.py new file mode 100644 index 00000000000..471b4a187ad --- /dev/null +++ b/test/integration/component/test_ps_project_limits.py @@ -0,0 +1,238 @@ +# 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 primary storage Project limits + + 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, + Host, + Domain, + Project, + Volume) +from marvin.lib.common import (get_domain, + get_zone, + get_template, + matchResourceCount, + isVmExpunged) +from marvin.lib.utils import (cleanup_resources, + validateList) +from marvin.codes import (PASS, + FAIL, + RESOURCE_PRIMARY_STORAGE) +import time + +class TestProjectsVolumeLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cloudstackTestClient = super(TestProjectsVolumeLimits, + 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.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["volume"]["zoneid"] = cls.zone.id + cls._cleanup = [] + try: + cls.service_offering = ServiceOffering.create(cls.api_client, cls.services["service_offering"]) + cls._cleanup.append(cls.service_offering) + except Exception as e: + cls.tearDownClass() + raise unittest.SkipTest("Exception in setUpClass: %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 = [] + response = self.setupProjectAccounts() + self.assertEqual(response[0], PASS, response[1]) + + try: + self.vm = VirtualMachine.create( + self.apiclient,self.services["virtual_machine"], + templateid=self.template.id,projectid=self.project.id, + serviceofferingid=self.service_offering.id) + projects = Project.list(self.apiclient,id=self.project.id, listall=True) + self.assertEqual(validateList(projects)[0], PASS,\ + "projects list validation failed") + self.initialResourceCount = projects[0].primarystoragetotal + except Exception as e: + self.tearDown() + self.skipTest("Exception occured in setup: %s" % e) + 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 setupProjectAccounts(self): + + try: + self.domain = Domain.create(self.apiclient, + services=self.services["domain"], + parentdomainid=self.domain.id) + self.admin = Account.create( + self.apiclient, self.services["account"], + admin=True, domainid=self.domain.id) + + # Create project as a domain admin + self.project = Project.create( + self.apiclient,self.services["project"], + account=self.admin.name,domainid=self.admin.domainid) + # Cleanup created project at end of test + self.cleanup.append(self.project) + self.cleanup.append(self.admin) + self.cleanup.append(self.domain) + except Exception as e: + return [FAIL, e] + return [PASS, None] + + @attr(tags=["advanced", "selfservice"]) + def test_01_VM_start_stop(self): + """Test project primary storage count with VM stop/start operation + + # Validate the following + # 1. Create VM with custom disk offering in a project and check + # initial primary storage count + # 2. Stop the VM and verify primary storage count remains the same + # 3. Start the VM and verify priamay storage count remains the same + """ + + try: + self.vm.stop(self.apiclient) + except Exception as e: + self.fail("Faield to stop VM: %s" % e) + + expectedCount = self.initialResourceCount + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + projectid=self.project.id) + self.assertEqual(response[0], PASS, response[1]) + + try: + self.vm.start(self.apiclient) + except Exception as e: + self.fail("Failed to start VM: %s" % e) + + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + projectid=self.project.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @attr(tags=["advanced", "selfservice"]) + def test_02_migrate_vm(self): + """Test migrate VM in project + + # Validate the following + # 1. Create VM with custom disk offering in a project and check + # initial primary storage count + # 2. List the hosts suitable for migrating the VM + # 3. Migrate the VM and verify that primary storage count of project remains same""" + + try: + hosts = Host.list(self.apiclient,virtualmachineid=self.vm.id, + listall=True) + self.assertEqual(validateList(hosts)[0], PASS, "hosts list validation failed") + host = hosts[0] + self.vm.migrate(self.apiclient, host.id) + except Exception as e: + self.fail("Exception occured" % e) + + expectedCount = self.initialResourceCount + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + projectid=self.project.id) + self.assertEqual(response[0], PASS, response[1]) + return + + @attr(tags=["advanced", "selfservice"]) + def test_03_delete_vm(self): + """Test delete VM belonging to project + + # Validate the following + # 1. Create VM with custom disk offering in a project and check + # initial primary storage count + # 2. Delete VM and verify that it's expunged + # 3. Verify that primary storage count of project equals 0""" + + try: + self.vm.delete(self.apiclient) + except Exception as e: + self.fail("Failed to detroy VM: %s" % e) + + self.assertTrue(isVmExpunged(self.apiclient, self.vm.id, self.project.id),\ + "VM not expunged") + + totalallottedtime = timeout = 600 + while timeout >= 0: + volumes = Volume.list(self.apiclient, projectid=self.project.id, listall=True) + if volumes is None: + break + if timeout == 0: + self.fail("Volume attached to VM not cleaned up even\ + after %s seconds" % totalallottedtime) + timeout -= 60 + time.sleep(60) + + expectedCount = 0 + response = matchResourceCount( + self.apiclient, expectedCount, + RESOURCE_PRIMARY_STORAGE, + projectid=self.project.id) + self.assertEqual(response[0], PASS, response[1]) + return 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 00000000000..737f9103319 --- /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 diff --git a/tools/marvin/marvin/codes.py b/tools/marvin/marvin/codes.py index ef49c0c3c8f..a1e708599da 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" @@ -81,3 +97,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" diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 94606df4ccd..ad2b8752ddd 100755 --- 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 @@ -946,6 +945,12 @@ class Volume: 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) @@ -979,6 +984,31 @@ 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: """Manage template life cycle""" diff --git a/tools/marvin/marvin/lib/common.py b/tools/marvin/marvin/lib/common.py index a2cb95913d5..29677d3a0ca 100644 --- a/tools/marvin/marvin/lib/common.py +++ b/tools/marvin/marvin/lib/common.py @@ -62,12 +62,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 @@ -1182,3 +1199,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]