# 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. """ Test cases for validating global limit for concurrent snapshots """ from nose.plugins.attrib import attr from marvin.cloudstackTestCase import cloudstackTestCase from marvin.lib.utils import (cleanup_resources, validateList) from marvin.lib.base import (Account, ServiceOffering, VirtualMachine, Snapshot, Volume, Configurations ) from marvin.lib.common import (get_domain, get_zone, get_template ) from marvin.codes import PASS, BACKED_UP from threading import Thread class TestConcurrentSnapshotLimit(cloudstackTestCase): @classmethod def setUpClass(cls): testClient = super(TestConcurrentSnapshotLimit, cls).getClsTestClient() cls.apiclient = testClient.getApiClient() cls.testdata = testClient.getParsedTestDataConfig() cls.hypervisor = cls.testClient.getHypervisorInfo() # Get Zone, Domain and templates cls.domain = get_domain(cls.apiclient) cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) cls.template = get_template( cls.apiclient, cls.zone.id, cls.testdata["ostype"]) cls._cleanup = [] cls.supportedHypervisor = True if cls.hypervisor.lower() in [ "hyperv", "lxc"]: cls.supportedHypervisor = False return # Create Service offering cls.service_offering = ServiceOffering.create( cls.apiclient, cls.testdata["service_offering"], ) cls._cleanup.append(cls.service_offering) return @classmethod def tearDownClass(cls): try: cleanup_resources(cls.apiclient, cls._cleanup) except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() self.cleanup = [] self.exceptionOccurred = False if not self.supportedHypervisor: self.skipTest("Snapshot not supported on %s" % self.hypervisor) def createSnapshot(self, volumeid): try: Snapshot.create( self.apiclient, volumeid ) except Exception as e: self.debug("Exception occurred: %s" % e) self.exceptionOccurred = True def tearDown(self): try: cleanup_resources(self.apiclient, self.cleanup) except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return @attr(tags=["advanced", "basic"], required_hardware="true") def test_01_concurrent_snapshot_global_limit(self): """ Test if global value concurrent.snapshots.threshold.perhost value respected This is positive test cases and tests if we are able to create as many snapshots mentioned in global value # 1. Create an account and a VM in it # 2. Read the global value for concurrent.snapshots.threshold.perhost # 3. If the value is Null, create at least 10 concurrent snapshots and verify they are created successfully # 4. Else, create as many snapshots specified in the global value, and verify they are created successfully """ # Create an account account = Account.create( self.apiclient, self.testdata["account"], domainid=self.domain.id ) self.cleanup.append(account) # Create user api client of the account userapiclient = self.testClient.getUserApiClient( UserName=account.name, DomainName=account.domain ) # Create VM virtual_machine = VirtualMachine.create( userapiclient, self.testdata["small"], templateid=self.template.id, accountid=account.name, domainid=account.domainid, serviceofferingid=self.service_offering.id, zoneid=self.zone.id ) # Create 10 concurrent snapshots by default # We can have any value, so keeping it 10 as it # seems good enough to test concurrentSnapshots = 10 # Step 1 # Get ROOT Volume Id volumes = Volume.list( self.apiclient, virtualmachineid=virtual_machine.id, type='ROOT', listall=True ) self.assertEqual(validateList(volumes)[0], PASS, "Volumes list validation failed") root_volume = volumes[0] config = Configurations.list( self.apiclient, name="concurrent.snapshots.threshold.perhost" ) if config[0].value: self.assertEqual( isinstance( config, list), True, "concurrent.snapshots.threshold.perhost should be present\ in global config") concurrentSnapshots = int(config[0].value) self.debug("concurrent Snapshots: %s" % concurrentSnapshots) threads = [] for i in range(0, (concurrentSnapshots)): thread = Thread( target=Snapshot.create, args=( self.apiclient, root_volume.id )) threads.append(thread) thread.start() for thread in threads: thread.join() snapshots = Snapshot.list(self.apiclient, volumeid=root_volume.id, listall=True) self.assertEqual(validateList(snapshots)[0], PASS, "Snapshots list validation failed") self.assertEqual( len(snapshots), concurrentSnapshots, "There should be exactly %s snapshots present" % concurrentSnapshots) for snapshot in snapshots: self.assertEqual(str(snapshot.state).lower(), BACKED_UP, "Snapshot state should be backedUp but it is\ %s" % snapshot.state) return @attr(tags=["advanced", "basic"], required_hardware="true") def test_02_concurrent_snapshot_global_limit(self): """ Test if global value concurrent.snapshots.threshold.perhost value is respected This is negative test cases and tests no more concurrent snapshots as specified in global value are created # 1. Read the global value for concurrent.snapshots.threshold.perhost # 2. If the value is Null, skip the test case # 3. Create an account and a VM in it # 4. Create more concurrent snapshots than specified in global allowed limit # 5. Verify that exception is raised while creating snapshots """ config = Configurations.list( self.apiclient, name="concurrent.snapshots.threshold.perhost" ) if config[0].value: self.assertEqual( isinstance( config, list), True, "concurrent.snapshots.threshold.perhost should be present\ in global config") concurrentSnapshots = int(config[0].value) else: self.skipTest("Skipping tests as the config value \ concurrent.snapshots.threshold.perhost is Null") # Create an account account = Account.create( self.apiclient, self.testdata["account"], domainid=self.domain.id ) self.cleanup.append(account) # Create user api client of the account userapiclient = self.testClient.getUserApiClient( UserName=account.name, DomainName=account.domain ) # Create VM virtual_machine = VirtualMachine.create( userapiclient, self.testdata["small"], templateid=self.template.id, accountid=account.name, domainid=account.domainid, serviceofferingid=self.service_offering.id, zoneid=self.zone.id ) # Step 1 # Get ROOT Volume Id volumes = Volume.list( self.apiclient, virtualmachineid=virtual_machine.id, type='ROOT', listall=True ) self.assertEqual(validateList(volumes)[0], PASS, "Volumes list validation failed") root_volume = volumes[0] threads = [] for i in range(0, (concurrentSnapshots + 1)): thread = Thread( target=self.createSnapshot, args=( self.apiclient, root_volume.id )) threads.append(thread) thread.start() for thread in threads: thread.join() self.assertTrue(self.exceptionOccurred, "Concurrent snapshots\ more than concurrent.snapshots.threshold.perhost\ value successfully created") return @attr(tags=["advanced", "basic"], required_hardware="false") def test_03_concurrent_snapshot_global_value_assignment(self): """ Test verifies that exception is raised if string value is assigned to concurrent.snapshots.threshold.perhost parameter. """ with self.assertRaises(Exception): Configurations.update( self.apiclient, "concurrent.snapshots.threshold.perhost", "String" ) return