cloudstack/test/integration/smoke/test_primary_storage.py
Rafael Weingärtner f2efbcecec
[CLOUDSTACK-10240] ACS cannot migrate a local volume to shared storage (#2425)
* [CLOUDSTACK-10240] ACS cannot migrate a volume from local to shared storage.

CloudStack is logically restricting the migration of local storages to shared storage and vice versa. This restriction is a logical one and can be removed for XenServer deployments. Therefore, we will enable migration of volumes between local-shared storages in XenServers independently of their service offering. This will work as an override mechanism to the disk offering used by volumes. If administrators want to migrate local volumes to a shared storage, they should be able to do so (the hypervisor already allows that). The same the other way around.

* Cleanups implemented while working on [CLOUDSTACK-10240]

* Fix test case test_03_migrate_options_storage_tags

The changes applied were:
- When loading hypervisors capabilities we must use "default" instead of nulls
- "Enable" storage migration for simulator hypervisor
- Remove restriction on "ClusterScopeStoragePoolAllocator" to find shared pools
2018-03-07 18:23:15 -03:00

693 lines
27 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.
""" BVT tests for Primary Storage
"""
#Import Local Modules
import marvin
from marvin.cloudstackTestCase import *
from marvin.cloudstackAPI import *
from marvin.lib.utils import *
from marvin.lib.base import *
from marvin.lib.common import *
from nose.plugins.attrib import attr
import logging
from marvin.lib.decoratorGenerators import skipTestIf
#Import System modules
import time
_multiprocess_shared_ = True
class TestPrimaryStorageServices(cloudstackTestCase):
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.services = self.testClient.getParsedTestDataConfig()
self.cleanup = []
# Get Zone and pod
self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests())
self.pod = get_pod(self.apiclient, self.zone.id)
self.hypervisor = self.testClient.getHypervisorInfo()
self.domain = get_domain(self.apiclient)
self.template = get_template(self.apiclient, self.zone.id, self.services["ostype"])
return
def tearDown(self):
try:
#Clean up, terminate the created templates
cleanup_resources(self.apiclient, self.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
@attr(tags = ["advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_01_primary_storage_nfs(self):
"""Test primary storage pools - XEN, KVM, VMWare. Not Supported for hyperv
"""
if self.hypervisor.lower() in ["hyperv"]:
raise self.skipTest("NFS primary storage not supported for Hyper-V")
# Validate the following:
# 1. List Clusters
# 2. verify that the cluster is in 'Enabled' allocation state
# 3. verify that the host is added successfully and
# in Up state with listHosts api response
#Create NFS storage pools with on XEN/KVM/VMWare clusters
clusters = list_clusters(
self.apiclient,
zoneid=self.zone.id
)
assert isinstance(clusters,list) and len(clusters)>0
for cluster in clusters:
#Host should be present before adding primary storage
list_hosts_response = list_hosts(
self.apiclient,
clusterid=cluster.id
)
self.assertEqual(
isinstance(list_hosts_response, list),
True,
"Check list response returns a valid list"
)
self.assertNotEqual(
len(list_hosts_response),
0,
"Check list Hosts in the cluster: " + cluster.name
)
storage = StoragePool.create(self.apiclient,
self.services["nfs"],
clusterid=cluster.id,
zoneid=self.zone.id,
podid=self.pod.id
)
self.cleanup.append(storage)
self.debug("Created storage pool in cluster: %s" % cluster.id)
self.assertEqual(
storage.state,
'Up',
"Check primary storage state "
)
self.assertEqual(
storage.type,
'NetworkFilesystem',
"Check storage pool type "
)
#Verify List Storage pool Response has newly added storage pool
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 "
)
# Call cleanup for reusing primary storage
cleanup_resources(self.apiclient, self.cleanup)
self.cleanup = []
return
@attr(tags = ["advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="true")
def test_01_primary_storage_iscsi(self):
"""Test primary storage pools - XEN. Not Supported for kvm,hyperv,vmware
"""
if self.hypervisor.lower() in ["kvm","hyperv", "vmware", "lxc"]:
raise self.skipTest("iscsi primary storage not supported on kvm, VMWare, Hyper-V, or LXC")
if not self.services["configurableData"]["iscsi"]["url"]:
raise self.skipTest("iscsi test storage url not setup, skipping")
# Validate the following:
# 1. List Clusters
# 2. verify that the cluster is in 'Enabled' allocation state
# 3. verify that the host is added successfully and
# in Up state with listHosts api response
# Create iSCSI storage pools with on XEN/KVM clusters
clusters = list_clusters(
self.apiclient,
zoneid=self.zone.id
)
assert isinstance(clusters,list) and len(clusters)>0
for cluster in clusters:
#Host should be present before adding primary storage
list_hosts_response = list_hosts(
self.apiclient,
clusterid=cluster.id
)
self.assertEqual(
isinstance(list_hosts_response, list),
True,
"Check list response returns a valid list"
)
self.assertNotEqual(
len(list_hosts_response),
0,
"Check list Hosts in the cluster: " + cluster.name
)
storage = StoragePool.create(self.apiclient,
self.services["configurableData"]["iscsi"],
clusterid=cluster.id,
zoneid=self.zone.id,
podid=self.pod.id
)
self.cleanup.append(storage)
self.debug("Created storage pool in cluster: %s" % cluster.id)
self.assertEqual(
storage.state,
'Up',
"Check primary storage state "
)
self.assertEqual(
storage.type,
'IscsiLUN',
"Check storage pool type "
)
#Verify List Storage pool Response has newly added storage pool
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 "
)
# Call cleanup for reusing primary storage
cleanup_resources(self.apiclient, self.cleanup)
self.cleanup = []
return
@attr(tags = ["advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_01_add_primary_storage_disabled_host(self):
"""Test add primary storage pool with disabled host
"""
#Disable a host
clusters = list_clusters(
self.apiclient,
zoneid=self.zone.id
)
assert isinstance(clusters,list) and len(clusters)>0
for cluster in clusters:
list_hosts_response = list_hosts(
self.apiclient,
clusterid=cluster.id,
type="Routing"
)
assert isinstance(list_hosts_response,list)
if len(list_hosts_response) < 2:
continue
selected_cluster = cluster
selected_host = list_hosts_response[0]
Host.update(self.apiclient, id=selected_host.id, allocationstate="Disable")
#create a pool
storage_pool_2 = StoragePool.create(
self.apiclient,
self.services["nfs2"],
clusterid=selected_cluster.id,
zoneid=self.zone.id,
podid=self.pod.id
)
#self.cleanup.append(storage_pool_2)
#Enable host and disable others
Host.update(self.apiclient, id=selected_host.id, allocationstate="Enable")
for host in list_hosts_response :
if(host.id == selected_host.id) :
continue
Host.update(self.apiclient, id=host.id, allocationstate="Disable")
#put other pools in maintenance
storage_pool_list = StoragePool.list(self.apiclient, zoneid = self.zone.id)
for pool in storage_pool_list :
if(pool.id == storage_pool_2.id) :
continue
StoragePool.update(self.apiclient,id=pool.id, enabled=False)
#deployvm
try:
# Create Account
account = Account.create(
self.apiclient,
self.services["account"],
domainid=self.domain.id
)
service_offering = ServiceOffering.create(
self.apiclient,
self.services["service_offerings"]["tiny"]
)
self.cleanup.append(service_offering)
self.virtual_machine = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
accountid=account.name,
zoneid=self.zone.id,
domainid=self.domain.id,
templateid=self.template.id,
serviceofferingid=service_offering.id
)
self.cleanup.append(self.virtual_machine)
self.cleanup.append(account)
finally:
#cancel maintenance
for pool in storage_pool_list :
if(pool.id == storage_pool_2.id) :
continue
StoragePool.update(self.apiclient,id=pool.id, enabled=True)
#Enable all hosts
for host in list_hosts_response :
if(host.id == selected_host.id) :
continue
Host.update(self.apiclient, id=host.id, allocationstate="Enable")
cleanup_resources(self.apiclient, self.cleanup)
self.cleanup = []
StoragePool.enableMaintenance(self.apiclient,storage_pool_2.id)
time.sleep(30);
cmd = deleteStoragePool.deleteStoragePoolCmd()
cmd.id = storage_pool_2.id
cmd.forced = True
self.apiclient.deleteStoragePool(cmd)
return
class StorageTagsServices:
"""Test Storage Tags Data Class.
"""
def __init__(self):
self.storage_tags = {
"a" : "NFS-A",
"b" : "NFS-B"
}
class TestStorageTags(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.logger = logging.getLogger('TestStorageTags')
cls.stream_handler = logging.StreamHandler()
cls.logger.setLevel(logging.DEBUG)
cls.logger.addHandler(cls.stream_handler)
test_case = super(TestStorageTags, cls)
testClient = test_case.getClsTestClient()
cls.config = test_case.getClsConfig()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.pod = get_pod(cls.apiclient, cls.zone.id)
cls.hypervisor = testClient.getHypervisorInfo()
cls.domain = get_domain(cls.apiclient)
cls.template = get_template(cls.apiclient, cls.zone.id, cls.services["ostype"])
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
cls.services["virtual_machine"]["template"] = cls.template.id
cls.services["storage_tags"] = StorageTagsServices().storage_tags
cls.hypervisorNotSupported = False
if cls.hypervisor.lower() in ["hyperv"]:
cls.hypervisorNotSupported = True
cls._cleanup = []
if not cls.hypervisorNotSupported:
cls.clusters = list_clusters(
cls.apiclient,
zoneid=cls.zone.id
)
assert isinstance(cls.clusters, list) and len(cls.clusters) > 0
# Create PS with Storage Tag
cls.storage_pool_1 = StoragePool.create(cls.apiclient,
cls.services["nfs"],
clusterid=cls.clusters[0].id,
zoneid=cls.zone.id,
podid=cls.pod.id,
tags=cls.services["storage_tags"]["a"]
)
#PS not appended to _cleanup, it is removed on tearDownClass before cleaning up resources
assert cls.storage_pool_1.state == 'Up'
storage_pools_response = list_storage_pools(cls.apiclient,
id=cls.storage_pool_1.id)
assert isinstance(storage_pools_response, list) and len(storage_pools_response) > 0
storage_response = storage_pools_response[0]
assert storage_response.id == cls.storage_pool_1.id and storage_response.type == cls.storage_pool_1.type
# Create Service Offerings with different Storage Tags
cls.service_offering_1 = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["tiny"],
tags=cls.services["storage_tags"]["a"]
)
cls._cleanup.append(cls.service_offering_1)
cls.service_offering_2 = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["tiny"],
tags=cls.services["storage_tags"]["b"]
)
cls._cleanup.append(cls.service_offering_2)
# Create Disk Offerings with different Storage Tags
cls.disk_offering_1 = DiskOffering.create(
cls.apiclient,
cls.services["disk_offering"],
tags=cls.services["storage_tags"]["a"]
)
cls._cleanup.append(cls.disk_offering_1)
cls.disk_offering_2 = DiskOffering.create(
cls.apiclient,
cls.services["disk_offering"],
tags=cls.services["storage_tags"]["b"]
)
cls._cleanup.append(cls.disk_offering_2)
# Create Account
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
# Create VM-1 with using Service Offering 1
cls.virtual_machine_1 = VirtualMachine.create(
cls.apiclient,
cls.services["virtual_machine"],
accountid=cls.account.name,
domainid=cls.account.domainid,
templateid=cls.template.id,
serviceofferingid=cls.service_offering_1.id,
hypervisor=cls.hypervisor,
mode=cls.zone.networktype
)
# VM-1 not appended to _cleanup, it is expunged on tearDownClass before cleaning up resources
return
@classmethod
def tearDownClass(cls):
try:
# First expunge vm, so PS can be cleaned up
cls.virtual_machine_1.delete(cls.apiclient)
time.sleep(60)
# Force delete primary storage
cmd = enableStorageMaintenance.enableStorageMaintenanceCmd()
cmd.id = cls.storage_pool_1.id
cls.apiclient.enableStorageMaintenance(cmd)
time.sleep(45)
cmd = deleteStoragePool.deleteStoragePoolCmd()
cmd.id = cls.storage_pool_1.id
cmd.forced = True
cls.apiclient.deleteStoragePool(cmd)
time.sleep(30)
cleanup_resources(cls.apiclient, cls._cleanup)
except Exception as e:
raise Exception("Cleanup failed with %s" % e)
def setUp(self):
self.dbclient = self.testClient.getDbConnection()
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)
@attr(tags=["advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
@skipTestIf("hypervisorNotSupported")
def test_01_deploy_vms_storage_tags(self):
"""Test Deploy VMS using different Service Offerings with Storage Tags
"""
# Save cleanup size before trying to deploy VM-2
cleanup_size = len(self.cleanup)
# Try deploying VM-2 using CO-2 -> Should fail to find storage and fail deployment
try:
self.virtual_machine_2 = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
accountid=self.account.name,
domainid=self.account.domainid,
templateid=self.template.id,
serviceofferingid=self.service_offering_2.id,
hypervisor=self.hypervisor
)
self.cleanup.append(self.virtual_machine_2)
except Exception as e:
self.debug("Expected exception %s: " % e)
self.debug("Asssert that vm2 was not deployed, so it couldn't be appended to cleanup")
self.assertEquals(cleanup_size, len(self.cleanup))
# Create V-1 using DO-1
self.volume_1 = Volume.create(
self.apiclient,
self.services,
zoneid=self.zone.id,
account=self.account.name,
domainid=self.account.domainid,
diskofferingid=self.disk_offering_1.id
)
self.cleanup.append(self.volume_1)
# Create V-2 using DO-2
self.volume_2 = Volume.create(
self.apiclient,
self.services,
zoneid=self.zone.id,
account=self.account.name,
domainid=self.account.domainid,
diskofferingid=self.disk_offering_2.id
)
self.cleanup.append(self.volume_2)
# Try attaching V-2 to VM-1 -> Should fail finding storage and fail attachment
try:
self.virtual_machine_1.attach_volume(
self.apiclient,
self.volume_2
)
except Exception as e:
self.debug("Expected exception %s: " % e)
vm_1_volumes = Volume.list(
self.apiclient,
virtualmachineid=self.virtual_machine_1.id,
type='DATADISK',
listall=True
)
self.debug("VM-1 Volumes: %s" % vm_1_volumes)
self.assertEquals(None, vm_1_volumes, "Check that volume V-2 has not been attached to VM-1")
# Attach V_1 to VM_1
self.virtual_machine_1.attach_volume(self.apiclient,self.volume_1)
vm_1_volumes = Volume.list(
self.apiclient,
virtualmachineid=self.virtual_machine_1.id,
type='DATADISK',
listall=True
)
self.debug("VM-1 Volumes: %s" % vm_1_volumes)
self.assertEquals(vm_1_volumes[0].id, self.volume_1.id, "Check that volume V-1 has been attached to VM-1")
self.virtual_machine_1.detach_volume(self.apiclient, self.volume_1)
return
def check_storage_pool_tag(self, poolid, tag):
cmd = listStorageTags.listStorageTagsCmd()
storage_tags_response = self.apiclient.listStorageTags(cmd)
pool_tags = filter(lambda x: x.poolid == poolid, storage_tags_response)
self.assertEquals(1, len(pool_tags), "Check storage tags size")
self.assertEquals(tag, pool_tags[0].name, "Check storage tag on storage pool")
@attr(tags=["advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
@skipTestIf("hypervisorNotSupported")
def test_02_edit_primary_storage_tags(self):
""" Test Edit Storage Tags
"""
qresultset = self.dbclient.execute(
"select id from storage_pool where uuid = '%s';"
% str(self.storage_pool_1.id)
)
self.assertEquals(1, len(qresultset), "Check DB Query result set")
qresult = qresultset[0]
storage_pool_db_id = qresult[0]
self.check_storage_pool_tag(storage_pool_db_id, self.services["storage_tags"]["a"])
# Update Storage Tag
StoragePool.update(
self.apiclient,
id=self.storage_pool_1.id,
tags=self.services["storage_tags"]["b"]
)
self.check_storage_pool_tag(storage_pool_db_id, self.services["storage_tags"]["b"])
# Revert Storage Tag
StoragePool.update(
self.apiclient,
id=self.storage_pool_1.id,
tags=self.services["storage_tags"]["a"]
)
self.check_storage_pool_tag(storage_pool_db_id, self.services["storage_tags"]["a"])
return
@attr(tags=["advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
@skipTestIf("hypervisorNotSupported")
def test_03_migration_options_storage_tags(self):
""" Test Volume migration options for Storage Pools with different Storage Tags
"""
# Create PS-2 using Storage Tag
storage_pool_2 = StoragePool.create(self.apiclient,
self.services["nfs2"],
clusterid=self.clusters[0].id,
zoneid=self.zone.id,
podid=self.pod.id,
tags=self.services["storage_tags"]["a"]
)
self.cleanup.append(storage_pool_2)
assert storage_pool_2.state == 'Up'
storage_pools_response = list_storage_pools(self.apiclient,
id=storage_pool_2.id)
assert isinstance(storage_pools_response, list) and len(storage_pools_response) > 0
storage_response = storage_pools_response[0]
assert storage_response.id == storage_pool_2.id and storage_response.type == storage_pool_2.type
vm_1_volumes = Volume.list(
self.apiclient,
virtualmachineid=self.virtual_machine_1.id,
type='ROOT',
listall=True
)
vol = vm_1_volumes[0]
if self.hypervisor.lower() not in ["vmware", "xenserver"]:
self.virtual_machine_1.stop(self.apiclient)
volumePool = StoragePool.list(
self.apiclient,
id=vol.storageid
)
self.debug("Volume %s is on storage: %s" % (vol.id, volumePool))
allStoragePools = StoragePool.list(
self.apiclient
)
self.debug("All storage pools in the system: %s" % (allStoragePools))
# Check migration options for volume
pools_response = StoragePool.listForMigration(
self.apiclient,
id=vol.id
)
pools_suitable = filter(lambda p : p.suitableformigration, pools_response)
self.debug("Suitable storage pools found: %s" % len(pools_suitable))
self.assertEquals(1, len(pools_suitable), "Check that there is only one item on the list")
self.assertEquals(pools_suitable[0].id, storage_pool_2.id, "Check that PS-2 is the migration option for volume")
# Update PS-2 Storage Tags
StoragePool.update(
self.apiclient,
id=storage_pool_2.id,
tags=self.services["storage_tags"]["b"]
)
# Check migration options for volume after updating PS-2 Storage Tags
pools_response = StoragePool.listForMigration(
self.apiclient,
id=vol.id
)
pools_suitable = filter(lambda p : p.suitableformigration, pools_response)
self.debug("Suitable storage pools found: %s" % len(pools_suitable))
self.assertEquals(0, len(pools_suitable), "Check that there is no migration option for volume")
return