# 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. """ BVT tests for volume snapshot copy functionality """ # Import Local Modules from marvin.cloudstackTestCase import cloudstackTestCase from marvin.cloudstackAPI import (createSnapshot, deleteSnapshot, copySnapshot, createVolume, createTemplate, listOsTypes) from marvin.lib.utils import (cleanup_resources, random_gen) from marvin.lib.base import (Account, Zone, ServiceOffering, DiskOffering, VirtualMachine, Volume, Snapshot, Template) from marvin.lib.common import (get_domain, get_zone, get_template) from marvin.lib.decoratorGenerators import skipTestIf from marvin.codes import FAILED, PASS from nose.plugins.attrib import attr import logging # Import System modules import math _multiprocess_shared_ = True class TestSnapshotCopy(cloudstackTestCase): @classmethod def setUpClass(cls): testClient = super(TestSnapshotCopy, 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.services['mode'] = cls.zone.networktype cls._cleanup = [] cls.logger = logging.getLogger('TestSnapshotCopy') cls.testsNotSupported = False cls.zones = Zone.list(cls.apiclient) enabled_core_zones = [] if not isinstance(cls.zones, list): cls.testsNotSupported = True elif len(cls.zones) < 2: cls.testsNotSupported = True else: for z in cls.zones: if z.type == 'Core' and z.allocationstate == 'Enabled': enabled_core_zones.append(z) if len(enabled_core_zones) < 2: cls.testsNotSupported = True if cls.testsNotSupported == True: self.logger.info("Unsupported") return cls.additional_zone = None for z in enabled_core_zones: if z.id != cls.zone.id: cls.additional_zone = z template = get_template( cls.apiclient, cls.zone.id, cls.services["ostype"]) if template == FAILED: assert False, "get_template() failed to return template with description %s" % cls.services["ostype"] # Set Zones and disk offerings cls.services["small"]["zoneid"] = cls.zone.id cls.services["small"]["template"] = template.id cls.services["iso"]["zoneid"] = cls.zone.id cls.account = Account.create( cls.apiclient, cls.services["account"], domainid=cls.domain.id) cls._cleanup.append(cls.account) compute_offering_service = cls.services["service_offerings"]["tiny"].copy() cls.service_offering = ServiceOffering.create( cls.apiclient, compute_offering_service) cls._cleanup.append(cls.service_offering) cls.services["virtual_machine"]["zoneid"] = cls.zone.id cls.services["virtual_machine"]["template"] = template.id cls.virtual_machine = VirtualMachine.create( cls.apiclient, cls.services["virtual_machine"], accountid=cls.account.name, domainid=cls.account.domainid, serviceofferingid=cls.service_offering.id, mode=cls.services["mode"] ) cls._cleanup.append(cls.virtual_machine) cls.volume = Volume.list( cls.apiclient, virtualmachineid=cls.virtual_machine.id, type='ROOT', listall=True )[0] @classmethod def tearDownClass(cls): super(TestSnapshotCopy, cls).tearDownClass() def setUp(self): self.apiclient = self.testClient.getApiClient() self.userapiclient = self.testClient.getUserApiClient( UserName=self.account.name, DomainName=self.account.domain ) self.dbclient = self.testClient.getDbConnection() self.snapshot_id = None self.cleanup = [] def tearDown(self): super(TestSnapshotCopy, self).tearDown() def create_snapshot(self, apiclient, zoneids): cmd = createSnapshot.createSnapshotCmd() cmd.volumeid = self.volume.id cmd.account = self.account.name cmd.domainid = self.account.domainid if zoneids: cmd.zoneids = zoneids snapshot = Snapshot(apiclient.createSnapshot(cmd).__dict__) self.cleanup.append(snapshot) return snapshot def delete_snapshot(self, apiclient, snapshot_id, zone_id=None): cmd = deleteSnapshot.deleteSnapshotCmd() cmd.id = snapshot_id if zone_id: cmd.zoneid = zone_id apiclient.deleteSnapshot(cmd) def copy_snapshot(self, apiclient, snapshot_id, zone_ids, source_zone_id=None): cmd = copySnapshot.copySnapshotCmd() cmd.id = snapshot_id cmd.destzoneids = zone_ids if source_zone_id: cmd.sourcezoneid = source_zone_id return apiclient.copySnapshot(cmd) def create_snapshot_volume(self, apiclient, snapshot_id, zone_id=None, disk_offering_id=None): cmd = createVolume.createVolumeCmd() cmd.name = "-".join(["VolumeFromSnap", random_gen()]) cmd.snapshotid = snapshot_id if zone_id: cmd.zoneid = zone_id if disk_offering_id: cmd.diskofferingid = disk_offering_id volume_from_snapshot = Volume(apiclient.createVolume(cmd).__dict__) self.cleanup.append(volume_from_snapshot) return volume_from_snapshot def create_snapshot_template(self, apiclient, services, snapshot_id, zone_id): cmd = createTemplate.createTemplateCmd() cmd.displaytext = "TemplateFromSnap" name = "-".join([cmd.displaytext, random_gen()]) cmd.name = name if "ostypeid" in services: cmd.ostypeid = services["ostypeid"] elif "ostype" in services: # Find OSTypeId from Os type sub_cmd = listOsTypes.listOsTypesCmd() sub_cmd.description = services["ostype"] ostypes = apiclient.listOsTypes(sub_cmd) if not isinstance(ostypes, list): self.fail("Unable to find Ostype id with desc: %s" % services["ostype"]) cmd.ostypeid = ostypes[0].id else: self.fail("Unable to find Ostype is required for creating template") cmd.isfeatured = True cmd.ispublic = True cmd.isextractable = False cmd.snapshotid = snapshot_id cmd.zoneid = zone_id apiclient.createTemplate(cmd) templates = Template.list(apiclient, name=name, templatefilter="self") if not isinstance(templates, list) and len(templates) < 0: self.fail("Unable to find created template with name %s" % name) template = Template(templates[0].__dict__) self.cleanup.append(template) return template def verify_snapshot_copies(self, snapshot_id, zone_ids): snapshot_entries = Snapshot.list(self.userapiclient, id=snapshot_id, showunique=False, locationtype="Secondary") if not isinstance(snapshot_entries, list): self.fail("Unable to list snapshot for multiple zones") elif len(snapshot_entries) != len(zone_ids): self.fail("Undesired list snapshot size for multiple zones") for zone_id in zone_ids: zone_found = False for entry in snapshot_entries: if entry.zoneid == zone_id: zone_found = True break if zone_found == False: self.fail("Unable to find snapshot entry for the zone ID: %s" % zone_id) @skipTestIf("testsNotSupported") @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") def test_01_take_snapshot_multi_zone(self): """Test to take volume snapshot in multiple zones """ # Validate the following: # 1. Take snapshot in multiple zone # 2. Verify snapshot = self.create_snapshot(self.userapiclient, [str(self.additional_zone.id)]) self.snapshot_id = snapshot.id self.verify_snapshot_copies(self.snapshot_id, [self.zone.id, self.additional_zone.id]) return @skipTestIf("testsNotSupported") @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") def test_02_copy_snapshot_multi_zone(self): """Test to take volume snapshot in a zone and then copy """ # Validate the following: # 1. Take snapshot in the native zone # 2. Copy snapshot in the additional zone # 3. Verify snapshot = self.create_snapshot(self.userapiclient, None) self.snapshot_id = snapshot.id self.copy_snapshot(self.userapiclient, self.snapshot_id, [str(self.additional_zone.id)], self.zone.id) self.verify_snapshot_copies(self.snapshot_id, [self.zone.id, self.additional_zone.id]) return @skipTestIf("testsNotSupported") @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") def test_03_take_snapshot_multi_zone_delete_single_zone(self): """Test to take volume snapshot in multiple zones and delete from one zone """ # Validate the following: # 1. Take snapshot in multiple zone # 2. Verify # 3. Delete from one zone # 4. Verify snapshot = self.create_snapshot(self.userapiclient, [str(self.additional_zone.id)]) self.snapshot_id = snapshot.id self.verify_snapshot_copies(self.snapshot_id, [self.zone.id, self.additional_zone.id]) self.delete_snapshot(self.userapiclient, self.snapshot_id, self.zone.id) self.verify_snapshot_copies(self.snapshot_id, [self.additional_zone.id]) return @skipTestIf("testsNotSupported") @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") def test_04_copy_snapshot_multi_zone_delete_all(self): """Test to take volume snapshot in a zone, copy in another zone and delete for all """ # Validate the following: # 1. Take snapshot in the native zone # 2. Copy snapshot in the additional zone # 3. Verify # 4. Delete for all zones # 5. Verify snapshot = self.create_snapshot(self.userapiclient, None) self.snapshot_id = snapshot.id self.copy_snapshot(self.userapiclient, self.snapshot_id, [str(self.additional_zone.id)], self.zone.id) self.verify_snapshot_copies(self.snapshot_id, [self.zone.id, self.additional_zone.id]) self.delete_snapshot(self.userapiclient, self.snapshot_id) snapshot_entries = Snapshot.list(self.userapiclient, id=snapshot.id) if snapshot_entries and isinstance(snapshot_entries, list) and len(snapshot_entries) > 0: self.fail("Snapshot delete for all zones failed") self.cleanup.remove(snapshot) return @skipTestIf("testsNotSupported") @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") def test_05_take_snapshot_multi_zone_create_volume_additional_zone(self): """Test to take volume snapshot in multiple zones and create a volume in one of the additional zones """ # Validate the following: # 1. Take snapshot in multiple zone # 2. Verify # 3. Create volume in the additional zone # 4. Verify volume zone snapshot = self.create_snapshot(self.userapiclient, [str(self.additional_zone.id)]) self.snapshot_id = snapshot.id self.verify_snapshot_copies(self.snapshot_id, [self.zone.id, self.additional_zone.id]) disk_offering_id = None if snapshot.volumetype == 'ROOT': service = self.services["disk_offering"] service["disksize"] = math.ceil(snapshot.virtualsize/(1024*1024*1024)) self.disk_offering = DiskOffering.create( self.apiclient, service ) self.cleanup.append(self.disk_offering) disk_offering_id = self.disk_offering.id self.volume = self.create_snapshot_volume(self.userapiclient, self.snapshot_id, self.additional_zone.id, disk_offering_id) if self.additional_zone.id != self.volume.zoneid: self.fail("Volume from snapshot not created in the additional zone") return @skipTestIf("testsNotSupported") @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") def test_06_take_snapshot_multi_zone_create_template_additional_zone(self): """Test to take volume snapshot in multiple zones and create a volume in one of the additional zones """ # Validate the following: # 1. Take snapshot in multiple zone # 2. Verify # 3. Create template in the additional zone # 4. Verify template zone snapshot = self.create_snapshot(self.userapiclient, [str(self.additional_zone.id)]) self.snapshot_id = snapshot.id self.verify_snapshot_copies(self.snapshot_id, [self.zone.id, self.additional_zone.id]) self.template = self.create_snapshot_template(self.userapiclient, self.services, self.snapshot_id, self.additional_zone.id) if self.additional_zone.id != self.template.zoneid: self.fail("Template from snapshot not created in the additional zone") return