mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
* Remove constraint for NFS storage * Add new property on agent.properties * Add free disk space on the host prior template download * Add unit tests for the free space check * Fix free space check - retrieve avaiable size in bytes * Update default location for direct download * Improve the method to retrieve hosts to retry on depending on the destination pool type and scope * Verify location for temporary download exists before checking free space * In progress - refactor and extension * Refactor and fix * Last fixes and marvin tests * Remove unused test file * Improve logging * Change default path for direct download * Fix upload certificate * Fix ISO failure after retry * Fix metalink filename mismatch error * Fix iso direct download * Fix for direct download ISOs on local storage and shared mount point * Last fix iso * Fix VM migration with ISO * Refactor volume migration to remove secondary storage intermediate * Fix simulator issue
364 lines
16 KiB
Python
364 lines
16 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.
|
|
""" Test for Direct Downloads of Templates and ISOs
|
|
"""
|
|
# Import Local Modules
|
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
|
from marvin.lib.utils import (cleanup_resources)
|
|
from marvin.lib.base import (ServiceOffering,
|
|
NetworkOffering,
|
|
Network,
|
|
Template,
|
|
VirtualMachine,
|
|
StoragePool)
|
|
from marvin.lib.common import (get_pod,
|
|
get_zone)
|
|
from nose.plugins.attrib import attr
|
|
from marvin.cloudstackAPI import (uploadTemplateDirectDownloadCertificate, revokeTemplateDirectDownloadCertificate)
|
|
from marvin.lib.decoratorGenerators import skipTestIf
|
|
import uuid
|
|
|
|
|
|
class TestUploadDirectDownloadCertificates(cloudstackTestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.testClient = super(TestUploadDirectDownloadCertificates, cls).getClsTestClient()
|
|
cls.apiclient = cls.testClient.getApiClient()
|
|
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
|
cls.dbclient = cls.testClient.getDbConnection()
|
|
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
|
cls.pod = get_pod(cls.apiclient, cls.zone.id)
|
|
cls.services = cls.testClient.getParsedTestDataConfig()
|
|
|
|
cls._cleanup = []
|
|
cls.hypervisorNotSupported = False
|
|
if cls.hypervisor.lower() not in ['kvm', 'lxc']:
|
|
cls.hypervisorNotSupported = True
|
|
|
|
if not cls.hypervisorNotSupported:
|
|
cls.certificates = {
|
|
"expired": "MIIDSTCCAjECFDi8s70TWFhwVN9cj67RJoAF99c8MA0GCSqGSIb3DQEBCwUAMGExCzAJBgNVBAYTAkNTMQswCQYDVQQIDAJDUzELMAkGA1UEBwwCQ1MxCzAJBgNVBAoMAkNTMQswCQYDVQQLDAJDUzELMAkGA1UEAwwCQ1MxETAPBgkqhkiG9w0BCQEWAkNTMB4XDTE5MDQyNDE1NTQxM1oXDTE5MDQyMjE1NTQxM1owYTELMAkGA1UEBhMCQ1MxCzAJBgNVBAgMAkNTMQswCQYDVQQHDAJDUzELMAkGA1UECgwCQ1MxCzAJBgNVBAsMAkNTMQswCQYDVQQDDAJDUzERMA8GCSqGSIb3DQEJARYCQ1MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrLS0XDBgqOvtoaI0TIxLropp1qdu8n2IQ1MPwo9NzCXvygjocLA6l/NkDSK/2zbS3RX9mSRFEHdPLCy5R3lMvcuWjnMbfUhsdo8aJajuDAS+wVt3ZJTtCL42hHwXhT+rnc/Go3lbq/4jz2W9hHXdM5V7h7w6M30IrL26biSJp01FcdEXglz9O+TwRr5akF1trhIbfhP8Nx/P9q62tyRVeiecXO+yskEqtrdftmVOzyYsv66aV3+a407zZusnX2oPP2r+5XILNp7XZCuJKAJpJkToQMDaJ5S+3A8SAvN3CjqjhYkF0sK9SIKC3+8wTWnyqE5Um/3r+mbdfAVxDCxqZAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAG/R9sJ2pFbu35MliIJIhWkwP7FeP/7gYCNvOXFt6vVGXmcOwuw9WGBxsmsGESQRB4+NnJFjyGQ1Ck+ps5XRRMizyvq6bCQxVuC5M+vYS4J0q8YoL0RJ20pN9iwTsosZjSEKmfUlVgsufqCG2nyusV71LSaQU6f/bylJcJkKwGUhThExh+PVLZ66H5cF4/SzuK6WzWnj5p6+YX8TP+qPUkXN1mapgVKfVMo6mqLsH+eLKH+zqdy5ZZ5znNSbJFgHufYbEFlutTaxHEvKNMEgMCFkFGiyPwRuD6oaPnZFquJLh/mBZOLogpxVD5v20AcUTANtbXSlPaqOnEQFcbiVCb8=",
|
|
"invalid": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
"valid": "MIIDSzCCAjMCFDa0LoW+1O8/cEwCI0nIqfl8c1TLMA0GCSqGSIb3DQEBCwUAMGExCzAJBgNVBAYTAkNTMQswCQYDVQQIDAJDUzELMAkGA1UEBwwCQ1MxCzAJBgNVBAoMAkNTMQswCQYDVQQLDAJDUzELMAkGA1UEAwwCQ1MxETAPBgkqhkiG9w0BCQEWAkNTMCAXDTE5MDQyNDE1NTIzNVoYDzIwOTgwOTE1MTU1MjM1WjBhMQswCQYDVQQGEwJDUzELMAkGA1UECAwCQ1MxCzAJBgNVBAcMAkNTMQswCQYDVQQKDAJDUzELMAkGA1UECwwCQ1MxCzAJBgNVBAMMAkNTMREwDwYJKoZIhvcNAQkBFgJDUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKstLRcMGCo6+2hojRMjEuuimnWp27yfYhDUw/Cj03MJe/KCOhwsDqX82QNIr/bNtLdFf2ZJEUQd08sLLlHeUy9y5aOcxt9SGx2jxolqO4MBL7BW3dklO0IvjaEfBeFP6udz8ajeVur/iPPZb2Edd0zlXuHvDozfQisvbpuJImnTUVx0ReCXP075PBGvlqQXW2uEht+E/w3H8/2rra3JFV6J5xc77KyQSq2t1+2ZU7PJiy/rppXf5rjTvNm6ydfag8/av7lcgs2ntdkK4koAmkmROhAwNonlL7cDxIC83cKOqOFiQXSwr1IgoLf7zBNafKoTlSb/ev6Zt18BXEMLGpkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVS5uWZRz2m3yx7EUQm47RTMW5WMXU4pI8D+N5WZ9xubYOqtU3r2OAYpfL/QO8iT7jcqNYGoDqe8ZjEaNvfxiTG8cOI6TSXhKBG6hjSaSFQSHOZ5mfstM36y/3ENFh6JCJ2ao1rgWSbfDRyAaHuvt6aCkaV6zRq2OMEgoJqZSgwxLQO230xa2hYgKXOePMVZyHFA2oKJtSOc3jCke9Y8zDUwm0McGdMRBD8tVB0rcaOqQ0PlDLjB9sQuhhLu8vjdgbznmPbUmMG7JN0yhT1eJbIX5ImXyh0DoTwiaGcYwW6SqYodjXACsC37xaQXAPYBiaAs4iI80TJSx1DVFO1LV0g=="
|
|
}
|
|
|
|
return
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
try:
|
|
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:
|
|
cleanup_resources(self.apiclient, self.cleanup)
|
|
except Exception as e:
|
|
raise Exception("Warning: Exception during cleanup : %s" % e)
|
|
return
|
|
|
|
@skipTestIf("hypervisorNotSupported")
|
|
@attr(tags=["advanced", "basic", "eip", "advancedns", "sg"], required_hardware="false")
|
|
def test_01_sanity_check_on_certificates(self):
|
|
"""Test Verify certificates before uploading to KVM hosts
|
|
"""
|
|
|
|
# Validate the following
|
|
# 1. Invalid certificates cannot be uploaded to hosts for direct downloads
|
|
# 2. Expired certificates cannot be uploaded to hosts for direct downloads
|
|
|
|
cmd = uploadTemplateDirectDownloadCertificate.uploadTemplateDirectDownloadCertificateCmd()
|
|
cmd.hypervisor = self.hypervisor
|
|
cmd.name = "marvin-test-verify-certs" + str(uuid.uuid1())
|
|
cmd.certificate = self.certificates["invalid"]
|
|
cmd.zoneid = self.zone.id
|
|
|
|
invalid_cert_uploadFails = False
|
|
expired_cert_upload_fails = False
|
|
try:
|
|
self.apiclient.uploadTemplateDirectDownloadCertificate(cmd)
|
|
self.fail("Invalid certificate must not be uploaded")
|
|
except Exception as e:
|
|
invalid_cert_uploadFails = True
|
|
|
|
cmd.certificate = self.certificates["expired"]
|
|
try:
|
|
self.apiclient.uploadTemplateDirectDownloadCertificate(cmd)
|
|
self.fail("Expired certificate must not be uploaded")
|
|
except Exception as e:
|
|
expired_cert_upload_fails = True
|
|
|
|
self.assertTrue(invalid_cert_uploadFails and expired_cert_upload_fails,
|
|
"Invalid or expired certificates must not be uploaded")
|
|
return
|
|
|
|
@skipTestIf("hypervisorNotSupported")
|
|
@attr(tags=["advanced", "basic", "eip", "advancedns", "sg"], required_hardware="false")
|
|
def test_02_upload_direct_download_certificates(self):
|
|
"""Test Upload certificates to KVM hosts for direct download
|
|
"""
|
|
|
|
# Validate the following
|
|
# 1. Valid certificates are uploaded to hosts
|
|
# 2. Revoke uploaded certificate from host
|
|
|
|
cmd = uploadTemplateDirectDownloadCertificate.uploadTemplateDirectDownloadCertificateCmd()
|
|
cmd.hypervisor = self.hypervisor
|
|
cmd.name = "marvin-test-verify-certs" + str(uuid.uuid1())
|
|
cmd.certificate = self.certificates["valid"]
|
|
cmd.zoneid = self.zone.id
|
|
|
|
try:
|
|
self.apiclient.uploadTemplateDirectDownloadCertificate(cmd)
|
|
except Exception as e:
|
|
self.fail("Valid certificate must be uploaded")
|
|
|
|
revokecmd = revokeTemplateDirectDownloadCertificate.revokeTemplateDirectDownloadCertificateCmd()
|
|
revokecmd.hypervisor = self.hypervisor
|
|
revokecmd.name = cmd.name
|
|
revokecmd.zoneid = self.zone.id
|
|
|
|
try:
|
|
self.apiclient.revokeTemplateDirectDownloadCertificate(revokecmd)
|
|
except Exception as e:
|
|
self.fail("Uploaded certificates should be revoked when needed")
|
|
|
|
return
|
|
|
|
|
|
class TestDirectDownloadTemplates(cloudstackTestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.testClient = super(TestDirectDownloadTemplates, cls).getClsTestClient()
|
|
cls.apiclient = cls.testClient.getApiClient()
|
|
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
|
cls.dbclient = cls.testClient.getDbConnection()
|
|
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
|
cls.pod = get_pod(cls.apiclient, cls.zone.id)
|
|
cls.services = cls.testClient.getParsedTestDataConfig()
|
|
|
|
cls._cleanup = []
|
|
cls.hypervisorSupported = False
|
|
cls.nfsStorageFound = False
|
|
cls.localStorageFound = False
|
|
cls.sharedMountPointFound = False
|
|
|
|
if cls.hypervisor.lower() in ['kvm', 'lxc']:
|
|
cls.hypervisorSupported = True
|
|
|
|
if cls.hypervisorSupported:
|
|
cls.services["test_templates"]["kvm"]["directdownload"] = "true"
|
|
cls.template = Template.register(cls.apiclient, cls.services["test_templates"]["kvm"],
|
|
zoneid=cls.zone.id, hypervisor=cls.hypervisor)
|
|
cls._cleanup.append(cls.template)
|
|
|
|
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
|
|
cls.services["virtual_machine"]["template"] = cls.template.id
|
|
cls.services["virtual_machine"]["hypervisor"] = cls.hypervisor
|
|
cls.service_offering = ServiceOffering.create(
|
|
cls.apiclient,
|
|
cls.services["service_offerings"]["tiny"]
|
|
)
|
|
cls._cleanup.append(cls.service_offering)
|
|
cls.network_offering = NetworkOffering.create(
|
|
cls.apiclient,
|
|
cls.services["l2-network_offering"],
|
|
)
|
|
cls.network_offering.update(cls.apiclient, state='Enabled')
|
|
cls.services["network"]["networkoffering"] = cls.network_offering.id
|
|
cls.l2_network = Network.create(
|
|
cls.apiclient,
|
|
cls.services["l2-network"],
|
|
zoneid=cls.zone.id,
|
|
networkofferingid=cls.network_offering.id
|
|
)
|
|
cls._cleanup.append(cls.l2_network)
|
|
cls._cleanup.append(cls.network_offering)
|
|
|
|
storage_pools = StoragePool.list(
|
|
cls.apiclient,
|
|
zoneid=cls.zone.id
|
|
)
|
|
for pool in storage_pools:
|
|
if not cls.nfsStorageFound and pool.type == "NetworkFilesystem":
|
|
cls.nfsStorageFound = True
|
|
cls.nfsPoolId = pool.id
|
|
elif not cls.localStorageFound and pool.type == "Filesystem":
|
|
cls.localStorageFound = True
|
|
cls.localPoolId = pool.id
|
|
elif not cls.sharedMountPointFound and pool.type == "SharedMountPoint":
|
|
cls.sharedMountPointFound = True
|
|
cls.sharedPoolId = pool.id
|
|
|
|
cls.nfsKvmNotAvailable = not cls.hypervisorSupported or not cls.nfsStorageFound
|
|
cls.localStorageKvmNotAvailable = not cls.hypervisorSupported or not cls.localStorageFound
|
|
cls.sharedMountPointKvmNotAvailable = not cls.hypervisorSupported or not cls.sharedMountPointFound
|
|
return
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
try:
|
|
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:
|
|
cleanup_resources(self.apiclient, self.cleanup)
|
|
except Exception as e:
|
|
raise Exception("Warning: Exception during cleanup : %s" % e)
|
|
return
|
|
|
|
def getCurrentStoragePoolTags(self, poolId):
|
|
local_pool = StoragePool.list(
|
|
self.apiclient,
|
|
id=poolId
|
|
)
|
|
return local_pool[0].tags
|
|
|
|
def updateStoragePoolTags(self, poolId, tags):
|
|
StoragePool.update(
|
|
self.apiclient,
|
|
id=poolId,
|
|
tags=tags
|
|
)
|
|
|
|
def createServiceOffering(self, name, type, tags):
|
|
services = {
|
|
"cpunumber": 1,
|
|
"cpuspeed": 512,
|
|
"memory": 256,
|
|
"displaytext": name,
|
|
"name": name,
|
|
"storagetype": type
|
|
}
|
|
return ServiceOffering.create(
|
|
self.apiclient,
|
|
services,
|
|
tags=tags
|
|
)
|
|
|
|
|
|
@skipTestIf("nfsKvmNotAvailable")
|
|
@attr(tags=["advanced", "basic", "eip", "advancedns", "sg"], required_hardware="false")
|
|
def test_01_deploy_vm_from_direct_download_template_nfs_storage(self):
|
|
"""Test Deploy VM from direct download template on NFS storage
|
|
"""
|
|
|
|
# Create service offering for local storage using storage tags
|
|
tags = self.getCurrentStoragePoolTags(self.nfsPoolId)
|
|
test_tag = "marvin_test_nfs_storage_direct_download"
|
|
self.updateStoragePoolTags(self.nfsPoolId, test_tag)
|
|
nfs_storage_offering = self.createServiceOffering("TestNFSStorageDirectDownload", "shared", test_tag)
|
|
|
|
vm = VirtualMachine.create(
|
|
self.apiclient,
|
|
self.services["virtual_machine"],
|
|
serviceofferingid=nfs_storage_offering.id,
|
|
networkids=self.l2_network.id
|
|
)
|
|
self.assertEqual(
|
|
vm.state,
|
|
"Running",
|
|
"Check VM deployed from direct download template is running on NFS storage"
|
|
)
|
|
|
|
# Revert storage tags for the storage pool used in this test
|
|
self.updateStoragePoolTags(self.nfsPoolId, tags)
|
|
self.cleanup.append(vm)
|
|
self.cleanup.append(nfs_storage_offering)
|
|
return
|
|
|
|
@skipTestIf("localStorageKvmNotAvailable")
|
|
@attr(tags=["advanced", "basic", "eip", "advancedns", "sg"], required_hardware="false")
|
|
def test_02_deploy_vm_from_direct_download_template_local_storage(self):
|
|
"""Test Deploy VM from direct download template on local storage
|
|
"""
|
|
|
|
# Create service offering for local storage using storage tags
|
|
tags = self.getCurrentStoragePoolTags(self.localPoolId)
|
|
test_tag = "marvin_test_local_storage_direct_download"
|
|
self.updateStoragePoolTags(self.localPoolId, test_tag)
|
|
local_storage_offering = self.createServiceOffering("TestLocalStorageDirectDownload", "local", test_tag)
|
|
|
|
# Deploy VM
|
|
vm = VirtualMachine.create(
|
|
self.apiclient,
|
|
self.services["virtual_machine"],
|
|
serviceofferingid=local_storage_offering.id,
|
|
networkids=self.l2_network.id,
|
|
)
|
|
self.assertEqual(
|
|
vm.state,
|
|
"Running",
|
|
"Check VM deployed from direct download template is running on local storage"
|
|
)
|
|
|
|
# Revert storage tags for the storage pool used in this test
|
|
self.updateStoragePoolTags(self.localPoolId, tags)
|
|
self.cleanup.append(vm)
|
|
self.cleanup.append(local_storage_offering)
|
|
return
|
|
|
|
@skipTestIf("sharedMountPointKvmNotAvailable")
|
|
@attr(tags=["advanced", "basic", "eip", "advancedns", "sg"], required_hardware="false")
|
|
def test_03_deploy_vm_from_direct_download_template_shared_mount_point_storage(self):
|
|
"""Test Deploy VM from direct download template on shared mount point
|
|
"""
|
|
|
|
# Create service offering for local storage using storage tags
|
|
tags = self.getCurrentStoragePoolTags(self.sharedPoolId)
|
|
test_tag = "marvin_test_shared_mount_point_storage_direct_download"
|
|
self.updateStoragePoolTags(self.sharedPoolId, test_tag)
|
|
shared_offering = self.createServiceOffering("TestSharedMountPointStorageDirectDownload", "shared", test_tag)
|
|
|
|
# Deploy VM
|
|
vm = VirtualMachine.create(
|
|
self.apiclient,
|
|
self.services["virtual_machine"],
|
|
serviceofferingid=shared_offering.id,
|
|
networkids=self.l2_network.id,
|
|
)
|
|
self.assertEqual(
|
|
vm.state,
|
|
"Running",
|
|
"Check VM deployed from direct download template is running on shared mount point"
|
|
)
|
|
|
|
# Revert storage tags for the storage pool used in this test
|
|
self.updateStoragePoolTags(self.sharedPoolId, tags)
|
|
self.cleanup.append(vm)
|
|
self.cleanup.append(shared_offering)
|
|
return
|