cloudstack/test/integration/component/test_volume_destroy_recover.py
Wei Zhou fd5bea838b
New feature: Add support to destroy/recover volumes (#3688)
* server: fix resource count of primary storage if some volumes are Expunged but not removed

Steps to reproduce the issue
(1) create a vm and stop it. check resource count of primary storage
(2) download volume. resource count of primary storage is not changed.
(3) expunge the vm, the volume will be Expunged state as there is a volume snapshot on secondary storage. The resource count of primary storage decreased.
(4) update resource count of the account (or domain), the resource count of primary storage is reset to the value in step (2).

* New feature: Add support to destroy/recover volumes

* Add integration test for volume destroy/recover

* marvin: check resource count of more types

* messages translate to JP

* Update messages for CN

* translate message for NL

* fix two issues per Daan's comments

Co-authored-by: Andrija Panic <45762285+andrijapanicsb@users.noreply.github.com>
2020-02-07 11:25:10 +01:00

508 lines
21 KiB
Python

# 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 Volume improvement (Destroy/Recover) in cloudstack 4.14.0.0
"""
# Import Local Modules
from nose.plugins.attrib import attr
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.cloudstackAPI import (deleteVolume, extractVolume, recoverVolume)
from marvin.lib.utils import (validateList,
cleanup_resources)
from marvin.lib.base import (Resources,
Volume,
Account,
Domain,
Network,
NetworkOffering,
VirtualMachine,
ServiceOffering,
DiskOffering,
Zone)
from marvin.lib.common import (get_domain,
get_zone,
get_template,
matchResourceCount,
isAccountResourceCountEqualToExpectedCount)
from marvin.codes import (PASS, FAILED, RESOURCE_PRIMARY_STORAGE, RESOURCE_VOLUME)
import logging
import random
import time
class TestVolumeDestroyRecover(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.testClient = super(
TestVolumeDestroyRecover,
cls).getClsTestClient()
cls.apiclient = cls.testClient.getApiClient()
cls.services = cls.testClient.getParsedTestDataConfig()
zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
cls.zone = Zone(zone.__dict__)
cls._cleanup = []
cls.logger = logging.getLogger("TestVolumeDestroyRecover")
cls.stream_handler = logging.StreamHandler()
cls.logger.setLevel(logging.DEBUG)
cls.logger.addHandler(cls.stream_handler)
# Get Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.template = get_template(cls.apiclient, cls.zone.id, hypervisor="KVM")
if cls.template == FAILED:
sys.exit(1)
cls.templatesize = (cls.template.size / (1024 ** 3))
cls.services['mode'] = cls.zone.networktype
# Create Account
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
admin=True,
domainid=cls.domain.id
)
accounts = Account.list(cls.apiclient, id=cls.account.id)
cls.expectedCount = int(accounts[0].primarystoragetotal)
cls.volumeTotal = int(accounts[0].volumetotal)
if cls.zone.securitygroupsenabled:
cls.services["shared_network_offering"]["specifyVlan"] = 'True'
cls.services["shared_network_offering"]["specifyIpRanges"] = 'True'
cls.network_offering = NetworkOffering.create(
cls.apiclient,
cls.services["shared_network_offering"]
)
cls.network_offering.update(cls.apiclient, state='Enabled')
cls.account_network = Network.create(
cls.apiclient,
cls.services["network2"],
networkofferingid=cls.network_offering.id,
zoneid=cls.zone.id,
accountid=cls.account.name,
domainid=cls.account.domainid
)
else:
cls.network_offering = NetworkOffering.create(
cls.apiclient,
cls.services["isolated_network_offering"],
)
# Enable Network offering
cls.network_offering.update(cls.apiclient, state='Enabled')
# Create account network
cls.services["network"]["zoneid"] = cls.zone.id
cls.services["network"]["networkoffering"] = cls.network_offering.id
cls.account_network = Network.create(
cls.apiclient,
cls.services["network"],
cls.account.name,
cls.account.domainid
)
# Create small service offering
cls.service_offering = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["small"]
)
# Create disk offering
cls.disk_offering = DiskOffering.create(
cls.apiclient,
cls.services["disk_offering"],
)
cls._cleanup.append(cls.disk_offering)
cls._cleanup.append(cls.service_offering)
cls._cleanup.append(cls.account);
cls._cleanup.append(cls.network_offering)
@classmethod
def tearDownClass(self):
try:
cleanup_resources(self.apiclient, self._cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.cleanup = []
return
def tearDown(self):
try:
cleanup_resources(self.apiclient, self.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
def verify_resource_count_primary_storage(self, expectedCount, volumeTotal):
response = matchResourceCount(
self.apiclient, expectedCount,
RESOURCE_PRIMARY_STORAGE,
accountid=self.account.id)
self.assertEqual(response[0], PASS, response[1])
result = isAccountResourceCountEqualToExpectedCount(
self.apiclient, self.account.domainid, self.account.name,
expectedCount, RESOURCE_PRIMARY_STORAGE)
self.assertFalse(result[0], result[1])
self.assertTrue(result[2], "Resource count of primary storage does not match")
response = matchResourceCount(
self.apiclient, volumeTotal,
RESOURCE_VOLUME,
accountid=self.account.id)
self.assertEqual(response[0], PASS, response[1])
result = isAccountResourceCountEqualToExpectedCount(
self.apiclient, self.account.domainid, self.account.name,
volumeTotal, RESOURCE_VOLUME)
self.assertFalse(result[0], result[1])
self.assertTrue(result[2], "Resource count of volume does not match")
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_01_create_vm_with_data_disk(self):
"""Create VM with DATA disk, then destroy it (expunge=False) and expunge it
Steps:
# 1. create vm with root disk and data disk
# 2. destroy vm, resource count of primary storage is not changed
# 3. expunge vm, resource count of primary storage decreased with size of root disk.
# 4. delete volume (data disk), resource count of primary storage decreased with size of data disk
"""
try:
virtual_machine_1 = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.service_offering.id,
diskofferingid=self.disk_offering.id,
templateid=self.template.id,
zoneid=self.zone.id
)
except Exception as e:
self.fail("Exception while deploying virtual machine: %s" % e)
self.expectedCount = self.expectedCount + self.templatesize + self.disk_offering.disksize
self.volumeTotal = self.volumeTotal + 2
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
root_volumes_list = Volume.list(
self.apiclient,
virtualmachineid=virtual_machine_1.id,
type='ROOT',
listall=True
)
status = validateList(root_volumes_list)
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
root_volume_id = root_volumes_list[0].id
data_volumes_list = Volume.list(
self.apiclient,
virtualmachineid=virtual_machine_1.id,
type='DATADISK',
listall=True
)
status = validateList(data_volumes_list)
self.assertEqual(status[0], PASS, "DATADISK Volume List Validation Failed")
data_volume_id = data_volumes_list[0].id
# destroy vm
virtual_machine_1.delete(self.apiclient, expunge=False)
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
# expunge vm
virtual_machine_1.expunge(self.apiclient)
self.expectedCount = self.expectedCount - self.templatesize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
# delete datadisk
cmd = deleteVolume.deleteVolumeCmd()
cmd.id = data_volume_id
self.apiclient.deleteVolume(cmd)
self.expectedCount = self.expectedCount - self.disk_offering.disksize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_02_destroy_allocated_volume(self):
"""Create volume, destroy it when expunge=false and expunge=true
Steps:
# 1. create volume, resource count increases.
# 2. destroy volume (expunge = false), Exception happened. resource count no changes
# 3. destroy volume (expunge = True), resource count of primary storage decreased with size of volume.
"""
# Create volume
volume = Volume.create(
self.apiclient, self.services["volume"],
zoneid=self.zone.id, account=self.account.name,
domainid=self.account.domainid, diskofferingid=self.disk_offering.id
)
self.expectedCount = self.expectedCount + self.disk_offering.disksize
self.volumeTotal = self.volumeTotal + 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Destroy volume (expunge=False)
with self.assertRaises(Exception):
volume.destroy(self.apiclient)
# Destroy volume (expunge=True)
volume.destroy(self.apiclient, expunge=True)
self.expectedCount = self.expectedCount - self.disk_offering.disksize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_03_destroy_detached_volume(self):
"""Create volume, attach/detach it, then destroy it when expunge=false and expunge=true
Steps:
# 1. create vm without data disk, resource count increases.
# 2. create volume, resource count increases.
# 3. attach volume to a vm. resource count no changes.
# 4. detach volume from a vm. resource count no changes.
# 5. destroy volume (expunge = false), volume is Destroy. resource count decreased with size of volume.
# 6. destroy volume (expunge = true), volume is not found. resource count no changes.
# 7. destroy vm (expunge=True). resource count decreased with size of root disk
"""
# Create vm
try:
virtual_machine_2 = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.service_offering.id,
templateid=self.template.id,
zoneid=self.zone.id
)
except Exception as e:
self.fail("Exception while deploying virtual machine: %s" % e)
self.expectedCount = self.expectedCount + self.templatesize
self.volumeTotal = self.volumeTotal + 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Create volume
volume = Volume.create(
self.apiclient, self.services["volume"],
zoneid=self.zone.id, account=self.account.name,
domainid=self.account.domainid, diskofferingid=self.disk_offering.id
)
self.expectedCount = self.expectedCount + self.disk_offering.disksize
self.volumeTotal = self.volumeTotal + 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Attach volume to vm
virtual_machine_2.attach_volume(self.apiclient, volume)
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Detach volume from vm
virtual_machine_2.detach_volume(self.apiclient, volume)
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Destroy volume (expunge=False)
volume.destroy(self.apiclient)
self.expectedCount = self.expectedCount - self.disk_offering.disksize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Destroy volume (expunge=True)
volume.destroy(self.apiclient, expunge=True)
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Destroy VM (expunge=True)
virtual_machine_2.delete(self.apiclient, expunge=True)
self.expectedCount = self.expectedCount - self.templatesize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_04_recover_root_volume_after_restorevm(self):
"""Restore VM, recover/delete old root disk
Steps:
# 1. create vm without data disk, resource count increases.
# 2. restore vm. resource count no changes.
# 3. check old root disk , should be Destroy state
# 4. recover old root disk. resource count increases.
# 5. delete old root disk . resource count decreases.
# 6. destroy vm (expunge=True). resource count decreased with size of root disk
"""
# Create vm
try:
virtual_machine_3 = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.service_offering.id,
templateid=self.template.id,
zoneid=self.zone.id
)
except Exception as e:
self.fail("Exception while deploying virtual machine: %s" % e)
self.expectedCount = self.expectedCount + self.templatesize
self.volumeTotal = self.volumeTotal + 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Get id of root disk
root_volumes_list = Volume.list(
self.apiclient,
virtualmachineid=virtual_machine_3.id,
type='ROOT',
listall=True
)
status = validateList(root_volumes_list)
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
root_volume_id = root_volumes_list[0].id
# restore vm
virtual_machine_3.restore(self.apiclient)
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# check old root disk state
root_volumes_list = Volume.list(
self.apiclient,
id=root_volume_id,
listall=True
)
status = validateList(root_volumes_list)
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
root_volume = root_volumes_list[0]
self.assertEqual(root_volume['state'], 'Destroy', "ROOT volume should be Destroy after restorevm")
# recover old root disk
cmd = recoverVolume.recoverVolumeCmd()
cmd.id = root_volume.id
self.apiclient.recoverVolume(cmd)
self.expectedCount = self.expectedCount + self.templatesize
self.volumeTotal = self.volumeTotal + 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# delete old root disk
cmd = deleteVolume.deleteVolumeCmd()
cmd.id = root_volume.id
self.apiclient.deleteVolume(cmd)
self.expectedCount = self.expectedCount - self.templatesize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
# Destroy VM (expunge=True)
virtual_machine_3.delete(self.apiclient, expunge=True)
self.expectedCount = self.expectedCount - self.templatesize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_05_extract_root_volume_and_destroy_vm(self):
"""Create VM, extract root volume, then destroy vm and volume
Steps:
# 1. create vm without data disk, resource count increases.
# 2. stop vm
# 3. extract root volume
# 4. expunge vm, root volume in Expunged state. resource count decreased with size of root disk.
# 5. destroy volume (expunge = false), Exception happened. resource count no changes
# 6. destroy volume (expunge = true). volume is not found. resource count no changes.
"""
# Create vm
try:
virtual_machine_4 = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.service_offering.id,
templateid=self.template.id,
zoneid=self.zone.id
)
except Exception as e:
self.fail("Exception while deploying virtual machine: %s" % e)
self.expectedCount = self.expectedCount + self.templatesize
self.volumeTotal = self.volumeTotal + 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# Get id of root disk
root_volumes_list = Volume.list(
self.apiclient,
virtualmachineid=virtual_machine_4.id,
type='ROOT',
listall=True
)
status = validateList(root_volumes_list)
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
root_volume_id = root_volumes_list[0].id
# Stop vm
virtual_machine_4.stop(self.apiclient)
# extract root volume
cmd = extractVolume.extractVolumeCmd()
cmd.id = root_volume_id
cmd.mode = "HTTP_DOWNLOAD"
cmd.zoneid = self.zone.id
self.apiclient.extractVolume(cmd)
# Destroy VM (expunge=True)
virtual_machine_4.delete(self.apiclient, expunge=True)
self.expectedCount = self.expectedCount - self.templatesize
self.volumeTotal = self.volumeTotal - 1
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
# check root disk state
root_volumes_list = Volume.list(
self.apiclient,
id=root_volume_id,
listall=True
)
status = validateList(root_volumes_list)
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
root_volume = root_volumes_list[0]
self.assertEqual(root_volume['state'], 'Expunged', "ROOT volume should be Destroy after restorevm")
# delete root disk
cmd = deleteVolume.deleteVolumeCmd()
cmd.id = root_volume.id
self.apiclient.deleteVolume(cmd)
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_06_delete_network(self):
"""Delete account network, resource count should not be changed
Steps:
# 1. Delete account network
# 2. resource count should not be changed
"""
self.account_network.delete(self.apiclient)
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)