# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. from marvin.codes import FAILED from nose.plugins.attrib import attr from marvin.cloudstackTestCase import cloudstackTestCase from marvin.lib.utils import (cleanup_resources, is_snapshot_on_nfs, validateList) from marvin.lib.base import (VirtualMachine, Account, Template, ServiceOffering, Snapshot, StoragePool, Volume, DiskOffering) from marvin.lib.common import (get_domain, get_test_template, get_zone, get_pod, list_volumes, list_snapshots, list_storage_pools, list_clusters) from marvin.lib.decoratorGenerators import skipTestIf from marvin.codes import PASS class TestSnapshotRootDisk(cloudstackTestCase): @classmethod def setUpClass(cls): testClient = super(TestSnapshotRootDisk, cls).getClsTestClient() cls.apiclient = testClient.getApiClient() cls.services = testClient.getParsedTestDataConfig() # Get Zone, Domain and templates cls.domain = get_domain(cls.apiclient) cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) cls.pod = get_pod(cls.apiclient, cls.zone.id) cls.services['mode'] = cls.zone.networktype cls.hypervisorNotSupported = False cls.hypervisor = cls.testClient.getHypervisorInfo() if cls.hypervisor.lower() in ['hyperv', 'lxc'] or 'kvm-centos6' in cls.testClient.getZoneForTests(): cls.hypervisorNotSupported = True cls._cleanup = [] if not cls.hypervisorNotSupported: cls.template = get_test_template(cls.apiclient, cls.zone.id, cls.hypervisor) if cls.template == FAILED: assert False, "get_test_template() failed to return template" cls.services["domainid"] = cls.domain.id cls.services["small"]["zoneid"] = cls.zone.id cls.services["templates"]["ostypeid"] = cls.template.ostypeid cls.services["zoneid"] = cls.zone.id # Create VMs, NAT Rules etc cls.account = Account.create( cls.apiclient, cls.services["account"], domainid=cls.domain.id ) cls.service_offering = ServiceOffering.create( cls.apiclient, cls.services["service_offerings"]["tiny"] ) cls.disk_offering = DiskOffering.create( cls.apiclient, cls.services["disk_offering"] ) cls.virtual_machine = cls.virtual_machine_with_disk = \ VirtualMachine.create( cls.apiclient, cls.services["small"], templateid=cls.template.id, accountid=cls.account.name, domainid=cls.account.domainid, zoneid=cls.zone.id, serviceofferingid=cls.service_offering.id, mode=cls.services["mode"] ) cls._cleanup.append(cls.virtual_machine_with_disk) cls._cleanup.append(cls.service_offering) cls._cleanup.append(cls.account) cls._cleanup.append(cls.disk_offering) return @classmethod def tearDownClass(cls): try: # Cleanup resources used cleanup_resources(cls.apiclient, 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) except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return @skipTestIf("hypervisorNotSupported") @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") def test_01_snapshot_root_disk(self): """Test Snapshot Root Disk """ # Validate the following # 1. listSnapshots should list the snapshot that was created. # 2. verify that secondary storage NFS share contains # the reqd volume under # /secondary/snapshots//$account_id/$volumeid/$snapshot_uuid # 3. verify backup_snap_id was non null in the `snapshots` table # 4. Verify that zoneid is returned in listSnapshots API response volumes = list_volumes( self.apiclient, virtualmachineid=self.virtual_machine_with_disk.id, type='ROOT', listall=True ) snapshot = Snapshot.create( self.apiclient, volumes[0].id, account=self.account.name, domainid=self.account.domainid ) self.cleanup.append(snapshot) self.debug("Snapshot created: ID - %s" % snapshot.id) snapshots = list_snapshots( self.apiclient, id=snapshot.id ) self.assertEqual( isinstance(snapshots, list), True, "Check list response returns a valid list" ) self.assertNotEqual( snapshots, None, "Check if result exists in list item call" ) self.assertEqual( snapshots[0].id, snapshot.id, "Check resource id in list resources call" ) self.assertIsNotNone(snapshots[0].zoneid, "Zone id is not none in listSnapshots") self.assertEqual( snapshots[0].zoneid, self.zone.id, "Check zone id in the list snapshots" ) self.debug( "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" % str(snapshot.id) ) qresultset = self.dbclient.execute( "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" % str(snapshot.id) ) self.assertNotEqual( len(qresultset), 0, "Check DB Query result set" ) qresult = qresultset[0] snapshot_uuid = qresult[0] # backup_snap_id = snapshot UUID self.assertNotEqual( str(snapshot_uuid), 'NULL', "Check if backup_snap_id is not null" ) self.assertTrue(is_snapshot_on_nfs( self.apiclient, self.dbclient, self.config, self.zone.id, snapshot.id)) return @skipTestIf("hypervisorNotSupported") @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") def test_02_list_snapshots_with_removed_data_store(self): """Test listing volume snapshots with removed data stores """ # 1 - Create new volume -> V # 2 - Create new Primary Storage -> PS # 3 - Attach and detach volume V from vm # 4 - Migrate volume V to PS # 5 - Take volume V snapshot -> S # 6 - List snapshot and verify it gets properly listed although Primary Storage was removed # Create new volume vol = Volume.create( self.apiclient, self.services["volume"], diskofferingid=self.disk_offering.id, zoneid=self.zone.id, account=self.account.name, domainid=self.account.domainid, ) self.cleanup.append(vol) self.assertIsNotNone(vol, "Failed to create volume") vol_res = Volume.list( self.apiclient, id=vol.id ) self.assertEqual( validateList(vol_res)[0], PASS, "Invalid response returned for list volumes") vol_uuid = vol_res[0].id clusters = list_clusters( self.apiclient, zoneid=self.zone.id ) assert isinstance(clusters,list) and len(clusters)>0 # Attach created volume to vm, then detach it to be able to migrate it self.virtual_machine_with_disk.stop(self.apiclient) self.virtual_machine_with_disk.attach_volume( self.apiclient, vol ) # Create new Primary Storage storage = StoragePool.create(self.apiclient, self.services["nfs2"], clusterid=clusters[0].id, zoneid=self.zone.id, podid=self.pod.id ) self.cleanup.append(storage) self.assertEqual( storage.state, 'Up', "Check primary storage state" ) self.assertEqual( storage.type, 'NetworkFilesystem', "Check storage pool type" ) storage_pools_response = list_storage_pools(self.apiclient, id=storage.id) self.assertEqual( isinstance(storage_pools_response, list), True, "Check list response returns a valid list" ) self.assertNotEqual( len(storage_pools_response), 0, "Check list Hosts response" ) storage_response = storage_pools_response[0] self.assertEqual( storage_response.id, storage.id, "Check storage pool ID" ) self.assertEqual( storage.type, storage_response.type, "Check storage pool type " ) self.virtual_machine_with_disk.detach_volume( self.apiclient, vol ) # Migrate volume to new Primary Storage Volume.migrate(self.apiclient, storageid=storage.id, volumeid=vol.id ) volume_response = list_volumes( self.apiclient, id=vol.id, ) self.assertNotEqual( len(volume_response), 0, "Check list Volumes response" ) volume_migrated = volume_response[0] self.assertEqual( volume_migrated.storageid, storage.id, "Check volume storage id" ) # Take snapshot of new volume snapshot = Snapshot.create( self.apiclient, volume_migrated.id, account=self.account.name, domainid=self.account.domainid ) self.debug("Snapshot created: ID - %s" % snapshot.id) # Delete volume, VM and created Primary Storage cleanup_resources(self.apiclient, self.cleanup) # List snapshot and verify it gets properly listed although Primary Storage was removed snapshot_response = Snapshot.list( self.apiclient, id=snapshot.id ) self.assertNotEqual( len(snapshot_response), 0, "Check list Snapshot response" ) self.assertEqual( snapshot_response[0].id, snapshot.id, "Check snapshot id" ) # Delete snapshot and verify it gets properly deleted (should not be listed) self.cleanup = [snapshot] cleanup_resources(self.apiclient, self.cleanup) self.cleanup = [] snapshot_response_2 = Snapshot.list( self.apiclient, id=snapshot.id ) self.assertEqual( snapshot_response_2, None, "Check list Snapshot response" ) return