| # 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 |