From adc3208dbaa2295f7921b526e57f83c1e0bd8b83 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 20 Sep 2021 13:56:54 +0530 Subject: [PATCH 1/6] api,server: add params for updatehypervisorcapabilities API Allows updating following capabilities for a hypervisor, version: - Max DATA volumes limit - Storage motion supported - Max hosts per cluster - VM snapshot enabled Signed-off-by: Abhishek Kumar --- .../hypervisor/HypervisorCapabilities.java | 2 + .../com/cloud/server/ManagementService.java | 3 +- .../apache/cloudstack/api/ApiConstants.java | 1 + .../UpdateHypervisorCapabilitiesCmd.java | 30 +++++++++++++- .../HypervisorCapabilitiesResponse.java | 15 ++++++- .../hypervisor/HypervisorCapabilitiesVO.java | 3 +- .../dao/HypervisorCapabilitiesDaoImpl.java | 2 +- .../java/com/cloud/api/ApiResponseHelper.java | 3 +- .../cloud/server/ManagementServerImpl.java | 41 +++++++++++++++---- 9 files changed, 84 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorCapabilities.java b/api/src/main/java/com/cloud/hypervisor/HypervisorCapabilities.java index 368ac8423eb3..03521888f9d5 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorCapabilities.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorCapabilities.java @@ -52,4 +52,6 @@ public interface HypervisorCapabilities extends Identity, InternalIdentity { boolean isStorageMotionSupported(); + Boolean isVmSnapshotEnabled(); + } diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java index 56f36a8d90ca..868e606a333c 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -22,6 +22,7 @@ import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd; +import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd; @@ -407,7 +408,7 @@ public interface ManagementService { Pair, Integer> listHypervisorCapabilities(Long id, HypervisorType hypervisorType, String keyword, Long startIndex, Long pageSizeVal); - HypervisorCapabilities updateHypervisorCapabilities(Long id, Long maxGuestsLimit, Boolean securityGroupEnabled); + HypervisorCapabilities updateHypervisorCapabilities(UpdateHypervisorCapabilitiesCmd cmd); /** * list all the top consumed resources across different capacity types diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index fd0dc7800d53..5c920593a859 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -585,6 +585,7 @@ public class ApiConstants { public static final String MIGRATE_ALLOWED = "migrateallowed"; public static final String MIGRATE_TO = "migrateto"; public static final String GUID = "guid"; + public static final String VM_SNAPSHOT_ENABELD = "vmsnapshotenabled"; public static final String VSWITCH_TYPE_GUEST_TRAFFIC = "guestvswitchtype"; public static final String VSWITCH_TYPE_PUBLIC_TRAFFIC = "publicvswitchtype"; public static final String VSWITCH_NAME_GUEST_TRAFFIC = "guestvswitchname"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java index 6550c89240ba..8eb844614f7e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java @@ -52,6 +52,18 @@ public class UpdateHypervisorCapabilitiesCmd extends BaseCmd { @Parameter(name = ApiConstants.MAX_GUESTS_LIMIT, type = CommandType.LONG, description = "the max number of Guest VMs per host for this hypervisor.") private Long maxGuestsLimit; + @Parameter(name = ApiConstants.MAX_DATA_VOLUMES_LIMIT, type = CommandType.INTEGER, description = "the maximum number of Data Volumes that can be attached to a VM for this hypervisor.", since = "4.16.0") + private Integer maxDataVolumesLimit; + + @Parameter(name = ApiConstants.STORAGE_MOTION_ENABLED, type = CommandType.BOOLEAN, description = "set true to enable storage motion support for this hypervisor", since = "4.16.0") + private Boolean storageMotionSupported; + + @Parameter(name = ApiConstants.MAX_HOSTS_PER_CLUSTER, type = CommandType.INTEGER, description = "the maximum number of the hypervisor hosts per cluster ", since = "4.16.0") + private Integer maxHostsPerClusterLimit; + + @Parameter(name = ApiConstants.VM_SNAPSHOT_ENABELD, type = CommandType.BOOLEAN, description = "set true to enable VM snapshots for this hypervisor", since = "4.16.0") + private Boolean vmSnapshotEnabled; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -68,6 +80,22 @@ public Long getMaxGuestsLimit() { return maxGuestsLimit; } + public Integer getMaxDataVolumesLimit() { + return maxDataVolumesLimit; + } + + public Boolean getStorageMotionSupported() { + return storageMotionSupported; + } + + public Integer getMaxHostsPerClusterLimit() { + return maxHostsPerClusterLimit; + } + + public Boolean getVmSnapshotEnabled() { + return vmSnapshotEnabled; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -84,7 +112,7 @@ public long getEntityOwnerId() { @Override public void execute() { - HypervisorCapabilities result = _mgr.updateHypervisorCapabilities(getId(), getMaxGuestsLimit(), getSecurityGroupEnabled()); + HypervisorCapabilities result = _mgr.updateHypervisorCapabilities(this); if (result != null) { HypervisorCapabilitiesResponse response = _responseGenerator.createHypervisorCapabilitiesResponse(result); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java index d6f7c2e2e446..0a7c03de86a3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.api.response; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; @@ -25,6 +23,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorCapabilities; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; @EntityReference(value = HypervisorCapabilities.class) public class HypervisorCapabilitiesResponse extends BaseResponse { @@ -60,6 +59,10 @@ public class HypervisorCapabilitiesResponse extends BaseResponse { @Param(description = "true if storage motion is supported") private boolean isStorageMotionSupported; + @SerializedName(ApiConstants.VM_SNAPSHOT_ENABELD) + @Param(description = "true if VM snapshots are enabled for this hypervisor") + private boolean isVmSnapshotEnabled; + public String getId() { return id; } @@ -123,4 +126,12 @@ public Integer getMaxHostsPerCluster() { public void setMaxHostsPerCluster(Integer maxHostsPerCluster) { this.maxHostsPerCluster = maxHostsPerCluster; } + + public boolean isVmSnapshotEnabled() { + return isVmSnapshotEnabled; + } + + public void setVmSnapshotEnabled(boolean vmSnapshotEnabled) { + isVmSnapshotEnabled = vmSnapshotEnabled; + } } diff --git a/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java b/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java index 2deeb2e2e348..5ab684c1100b 100644 --- a/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java +++ b/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java @@ -189,7 +189,8 @@ public void setMaxHostsPerCluster(Integer maxHostsPerCluster) { this.maxHostsPerCluster = maxHostsPerCluster; } - public Boolean getVmSnapshotEnabled() { + @Override + public Boolean isVmSnapshotEnabled() { return vmSnapshotEnabled; } diff --git a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java index 35f765b554ac..9a1f29014f97 100644 --- a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java @@ -105,7 +105,7 @@ public Integer getMaxHostsPerCluster(HypervisorType hypervisorType, String hyper @Override public Boolean isVmSnapshotEnabled(HypervisorType hypervisorType, String hypervisorVersion) { HypervisorCapabilitiesVO result = getCapabilities(hypervisorType, hypervisorVersion); - return result.getVmSnapshotEnabled(); + return result.isVmSnapshotEnabled(); } @Override diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index af4402a6e45b..be8a264809dd 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -33,7 +33,6 @@ import javax.inject.Inject; -import com.cloud.server.ResourceIcon; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -303,6 +302,7 @@ import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; import com.cloud.resource.RollingMaintenanceManager; +import com.cloud.server.ResourceIcon; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.service.ServiceOfferingVO; @@ -2553,6 +2553,7 @@ public HypervisorCapabilitiesResponse createHypervisorCapabilitiesResponse(Hyper hpvCapabilitiesResponse.setMaxDataVolumesLimit(hpvCapabilities.getMaxDataVolumesLimit()); hpvCapabilitiesResponse.setMaxHostsPerCluster(hpvCapabilities.getMaxHostsPerCluster()); hpvCapabilitiesResponse.setIsStorageMotionSupported(hpvCapabilities.isStorageMotionSupported()); + hpvCapabilitiesResponse.setVmSnapshotEnabled(hpvCapabilities.isVmSnapshotEnabled()); return hpvCapabilitiesResponse; } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 49f45d9e3ff5..1c8bdcce1670 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -35,8 +35,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; - - import java.util.stream.Collectors; import javax.crypto.Mac; @@ -44,8 +42,6 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.dc.DomainVlanMapVO; -import com.cloud.dc.dao.DomainVlanMapDao; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; @@ -602,6 +598,7 @@ import com.cloud.dc.AccountVlanMapVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DomainVlanMapVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; @@ -611,6 +608,7 @@ import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DomainVlanMapDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; @@ -660,9 +658,9 @@ import com.cloud.network.dao.LoadBalancerVO; import com.cloud.network.dao.NetworkAccountDao; import com.cloud.network.dao.NetworkAccountVO; +import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDomainDao; import com.cloud.network.dao.NetworkDomainVO; -import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.vpc.dao.VpcDao; import com.cloud.org.Cluster; @@ -4443,7 +4441,14 @@ public Pair, Integer> listHypervisorCapab } @Override - public HypervisorCapabilities updateHypervisorCapabilities(final Long id, final Long maxGuestsLimit, final Boolean securityGroupEnabled) { + public HypervisorCapabilities updateHypervisorCapabilities(UpdateHypervisorCapabilitiesCmd cmd) { + final Long id = cmd.getId(); + final Boolean securityGroupEnabled = cmd.getSecurityGroupEnabled(); + final Long maxGuestsLimit = cmd.getMaxGuestsLimit(); + final Integer maxDataVolumesLimit = cmd.getMaxDataVolumesLimit(); + final Boolean storageMotionSupported = cmd.getStorageMotionSupported(); + final Integer maxHostsPerClusterLimit = cmd.getMaxHostsPerClusterLimit(); + final Boolean vmSnapshotEnabled = cmd.getVmSnapshotEnabled(); HypervisorCapabilitiesVO hpvCapabilities = _hypervisorCapabilitiesDao.findById(id, true); if (hpvCapabilities == null) { @@ -4452,19 +4457,37 @@ public HypervisorCapabilities updateHypervisorCapabilities(final Long id, final throw ex; } - final boolean updateNeeded = maxGuestsLimit != null || securityGroupEnabled != null; + final boolean updateNeeded = securityGroupEnabled != null || maxGuestsLimit != null || + maxDataVolumesLimit != null || storageMotionSupported != null || maxHostsPerClusterLimit != null || + vmSnapshotEnabled != null; if (!updateNeeded) { return hpvCapabilities; } hpvCapabilities = _hypervisorCapabilitiesDao.createForUpdate(id); + if (securityGroupEnabled != null) { + hpvCapabilities.setSecurityGroupEnabled(securityGroupEnabled); + } + if (maxGuestsLimit != null) { hpvCapabilities.setMaxGuestsLimit(maxGuestsLimit); } - if (securityGroupEnabled != null) { - hpvCapabilities.setSecurityGroupEnabled(securityGroupEnabled); + if (maxDataVolumesLimit != null) { + hpvCapabilities.setMaxDataVolumesLimit(maxDataVolumesLimit); + } + + if (storageMotionSupported != null) { + hpvCapabilities.setStorageMotionSupported(storageMotionSupported); + } + + if (maxHostsPerClusterLimit != null) { + hpvCapabilities.setMaxHostsPerCluster(maxHostsPerClusterLimit); + } + + if (vmSnapshotEnabled != null) { + hpvCapabilities.setVmSnapshotEnabled(vmSnapshotEnabled); } if (_hypervisorCapabilitiesDao.update(id, hpvCapabilities)) { From 9af03f5649e3f95189c6311e44a482aa96325d8b Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 28 Jan 2022 17:56:30 +0530 Subject: [PATCH 2/6] added test Signed-off-by: Abhishek Kumar --- .../smoke/test_hypervisor_capabilities.py | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 test/integration/smoke/test_hypervisor_capabilities.py diff --git a/test/integration/smoke/test_hypervisor_capabilities.py b/test/integration/smoke/test_hypervisor_capabilities.py new file mode 100644 index 000000000000..58cdac912f08 --- /dev/null +++ b/test/integration/smoke/test_hypervisor_capabilities.py @@ -0,0 +1,291 @@ +# 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 cases for Testing Hypervisor Capabilities +""" +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.utils import (cleanup_resources, + validateList) +from marvin.lib.base import (Account, + ServiceOffering, + DiskOffering, + VirtualMachine, + Volume, + Host, + VmSnapshot) +from marvin.lib.common import (get_domain, + get_zone, + get_template, + list_virtual_machines, + list_ssvms, + list_routers) +from marvin.lib.decoratorGenerators import skipTestIf + + +from marvin.cloudstackAPI import (updateHypervisorCapabilities, + listHypervisorCapabilities) + +from marvin.codes import PASS + + +class TestHypervisorCapabilities(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestHypervisorCapabilities, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.domain = get_domain(cls.apiclient) + + cls.hypervisor = cls.testClient.getHypervisorInfo() + # Get Zone, Domain and templates + + cls.notSupported = True + cls._cleanup = [] + cls.hosts = Host.list(cls.apiclient, zoneid=cls.zone.id, type='Routing') + if isinstance(cls.hosts, list) and len(cls.hosts) > 0: + cls.host = cls.hosts[0] + cls.notSupported = False + + cls.hypervisorversion = "default" + if hasattr(cls.host, 'hypervisorversion'): + cls.hypervisorversion = cls.host.hypervisorversion + + if cls.notSupported == False: + cls.notSupported = True + cmdList = listHypervisorCapabilities.listHypervisorCapabilitiesCmd() + cmdList.hypervisor = cls.hypervisor + capabilities = cls.apiclient.listHypervisorCapabilities(cmdList) + for capability in capabilities: + if capability.hypervisorversion == cls.hypervisorversion: + cls.hostCapability = capability + cls.notSupported = False + break + + if cls.notSupported == True: + cls.hypervisorversion = "default" + for capability in capabilities: + if capability.hypervisorversion == cls.hypervisorversion: + cls.hostCapability = capability + cls.notSupported = False + break + + print("Test-- %s, %s, %s" % (cls.notSupported, cls.hostCapability.id, cls.hostCapability.maxdatavolumeslimit)) + if cls.notSupported == False: + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.services["ostype"]) + + # Create an account + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + + # Create user api client of the account + cls.userapiclient = testClient.getUserApiClient( + UserName=cls.account.name, + DomainName=cls.account.domain + ) + # Create Service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offering"], + hosttags="host1" + ) + cls._cleanup.append(cls.service_offering) + + cls.disk_offering = DiskOffering.create( + cls.apiclient, + cls.services["disk_offering"] + ) + cls._cleanup.append(cls.disk_offering) + + return + + @classmethod + def tearDownClass(cls): + try: + cleanup_resources(cls.apiclient, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def setUp(self): + self.cleanup = [] + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + + def tearDown(self): + try: + if self.notSupported == False: + Host.update(self.apiclient, id=self.host.id, hosttags="") + self.updateHostHypervisorCapability(self.hostCapability.id, + self.hostCapability.maxdatavolumeslimit, + self.hostCapability.vmsnapshotenabled) + + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @skipTestIf("notSupported") + @attr(tags=["advanced", "basic"], required_hardware="false") + def test_01_check_hypervisor_max_data_volume_limit(self): + """ Test hypervisor maxdatavolumeslimit effect + + # 1. Set maxdatavolumeslimit to 1 for hypervisor + # 2. List capabilities and verify value + # 3. Deploy a VM and attach a volume to it + # 4. Try attach another volume, it should fail + # 5. Set maxdatavolumeslimit to 32 for hypervisor + # 6. Try attach second volume, it should succeed + """ + self.updateHostHypervisorCapability(self.hostCapability.id, 1) + capabilities = self.listHostHypervisorCapabilities(self.hostCapability.id) + self.assertTrue(isinstance(capabilities, list), "listHypervisorCapabilities response not a valid list") + self.assertEqual(len(capabilities), 1, "listHypervisorCapabilities response not valid") + self.assertEqual(capabilities[0].maxdatavolumeslimit, + 1, + "listHypervisorCapabilities response maxdatavolumeslimit value not 1") + + Host.update(self.apiclient, id=self.host.id, hosttags="host1") + vm = VirtualMachine.create( + self.userapiclient, + self.services["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + zoneid=self.zone.id + ) + volume_created1 = Volume.create( + self.userapiclient, + self.services["volume"], + zoneid=self.zone.id, + diskofferingid=self.disk_offering.id + ) + volume_created2 = Volume.create( + self.userapiclient, + self.services["volume"], + zoneid=self.zone.id, + diskofferingid=self.disk_offering.id + ) + self.cleanup.append(volume_created1) + self.cleanup.append(volume_created2) + self.cleanup.append(vm) + + vm.attach_volume( + self.userapiclient, + volume_created1 + ) + + try: + vm.attach_volume( + self.userapiclient, + volume_created2 + ) + vm.detach_volume(self.userapiclient, volume_created1) + vm.detach_volume(self.userapiclient, volume_created2) + self.fail("Successful to attach 2 DATA disks when max DATA disk limit was set to 1") + except Exception as e: + self.debug("Failed to attach 2nd DATA disk when max DATA disk limit was set to 1: %s" % e) + + self.updateHostHypervisorCapability(self.hostCapability.id, 32) + + vm.attach_volume( + self.userapiclient, + volume_created2 + ) + vm.detach_volume(self.userapiclient, volume_created1) + vm.detach_volume(self.userapiclient, volume_created2) + + @skipTestIf("notSupported") + @attr(tags=["advanced", "basic"], required_hardware="false") + def test_01_check_hypervisor_vm_snapshot(self): + """ Test hypervisor vmsnapshotenabled effect + + # 1. Set vmsnapshotenabled to false for hypervisor + # 2. List capabilities and verify value + # 3. Deploy a VM + # 4. Try VM snapshot, it should fail + # 5. Set vmsnapshotenabled to true for hypervisor + # 6. Try VM snapshot again, it should succeed + """ + self.updateHostHypervisorCapability(self.hostCapability.id, None, False) + capabilities = self.listHostHypervisorCapabilities(self.hostCapability.id) + self.assertTrue(isinstance(capabilities, list), "listHypervisorCapabilities response not a valid list") + self.assertEqual(len(capabilities), 1, "listHypervisorCapabilities response not valid") + self.assertEqual(capabilities[0].vmsnapshotenabled, + False, + "listHypervisorCapabilities response vmsnapshotenabled value not False") + + Host.update(self.apiclient, id=self.host.id, hosttags="host1") + vm = VirtualMachine.create( + self.userapiclient, + self.services["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + zoneid=self.zone.id + ) + + try: + fail_snapshot = VmSnapshot.create( + self.userapiclient, + vmid=vm.id, + snapshotmemory="false", + name="Test Snapshot", + description="Test Snapshot Desc" + ) + if vm_snapshot: + VmSnapshot.deleteVMSnapshot(self.userapiclient, fail_snapshot.id) + self.fail("Successful to take VM snapshot even when vmsnapshotenabled was set to False") + except Exception as e: + self.debug("Failed to take VM snapshot even vmsnapshotenabled was set to False: %s" % e) + + self.updateHostHypervisorCapability(self.hostCapability.id, None, True) + + vm_snapshot = VmSnapshot.create( + self.userapiclient, + vmid=vm.id, + snapshotmemory="false", + name="Test Snapshot", + description="Test Snapshot Desc" + ) + if vm_snapshot: + VmSnapshot.deleteVMSnapshot(self.userapiclient, vm_snapshot.id) + + def updateHostHypervisorCapability(self, id, maxDataVolumes, vmSnapshotEnabled=None): + cmd = updateHypervisorCapabilities.updateHypervisorCapabilitiesCmd() + cmd.id = id + if maxDataVolumes != None: + cmd.maxdatavolumeslimit = maxDataVolumes + if vmSnapshotEnabled != None: + cmd.vmsnapshotenabled = vmSnapshotEnabled + self.apiclient.updateHypervisorCapabilities(cmd) + + def listHostHypervisorCapabilities(self, id, hypervisor=None): + cmd = listHypervisorCapabilities.listHypervisorCapabilitiesCmd() + cmd.id = id + return self.apiclient.listHypervisorCapabilities(cmd) + From 5ccde24ced500f39e00eab209603dce051bd82ba Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 28 Jan 2022 23:14:31 +0530 Subject: [PATCH 3/6] Update test/integration/smoke/test_hypervisor_capabilities.py Co-authored-by: dahn --- test/integration/smoke/test_hypervisor_capabilities.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/integration/smoke/test_hypervisor_capabilities.py b/test/integration/smoke/test_hypervisor_capabilities.py index 58cdac912f08..8d3a2abf212b 100644 --- a/test/integration/smoke/test_hypervisor_capabilities.py +++ b/test/integration/smoke/test_hypervisor_capabilities.py @@ -123,10 +123,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - try: - cleanup_resources(cls.apiclient, cls._cleanup) - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) + super(TestHypervisorCapabilities, cls).tearDownClass() def setUp(self): self.cleanup = [] From 04a1eb31eb44e76fce63c5ea6b0e52962956342c Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 28 Jan 2022 23:14:38 +0530 Subject: [PATCH 4/6] Update test/integration/smoke/test_hypervisor_capabilities.py Co-authored-by: dahn --- test/integration/smoke/test_hypervisor_capabilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/smoke/test_hypervisor_capabilities.py b/test/integration/smoke/test_hypervisor_capabilities.py index 8d3a2abf212b..7d08f6db4fcc 100644 --- a/test/integration/smoke/test_hypervisor_capabilities.py +++ b/test/integration/smoke/test_hypervisor_capabilities.py @@ -139,7 +139,7 @@ def tearDown(self): self.hostCapability.maxdatavolumeslimit, self.hostCapability.vmsnapshotenabled) - cleanup_resources(self.apiclient, self.cleanup) + super(TestHypervisorCapabilities, self).tearDown() except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return From 75142e287d2293a78460343524d44b20d1b756b5 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Sat, 29 Jan 2022 00:24:54 +0530 Subject: [PATCH 5/6] changes Signed-off-by: Abhishek Kumar --- .../smoke/test_hypervisor_capabilities.py | 28 +++++++++---------- tools/marvin/marvin/lib/base.py | 5 ++++ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/test/integration/smoke/test_hypervisor_capabilities.py b/test/integration/smoke/test_hypervisor_capabilities.py index 7d08f6db4fcc..d2d79908de26 100644 --- a/test/integration/smoke/test_hypervisor_capabilities.py +++ b/test/integration/smoke/test_hypervisor_capabilities.py @@ -85,7 +85,6 @@ def setUpClass(cls): cls.notSupported = False break - print("Test-- %s, %s, %s" % (cls.notSupported, cls.hostCapability.id, cls.hostCapability.maxdatavolumeslimit)) if cls.notSupported == False: cls.template = get_template( cls.apiclient, @@ -165,29 +164,29 @@ def test_01_check_hypervisor_max_data_volume_limit(self): "listHypervisorCapabilities response maxdatavolumeslimit value not 1") Host.update(self.apiclient, id=self.host.id, hosttags="host1") - vm = VirtualMachine.create( - self.userapiclient, - self.services["small"], - templateid=self.template.id, - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - zoneid=self.zone.id - ) volume_created1 = Volume.create( self.userapiclient, self.services["volume"], zoneid=self.zone.id, diskofferingid=self.disk_offering.id ) + self.cleanup.append(volume_created1) volume_created2 = Volume.create( self.userapiclient, self.services["volume"], zoneid=self.zone.id, diskofferingid=self.disk_offering.id ) - self.cleanup.append(volume_created1) self.cleanup.append(volume_created2) + vm = VirtualMachine.create( + self.userapiclient, + self.services["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + zoneid=self.zone.id + ) self.cleanup.append(vm) vm.attach_volume( @@ -245,6 +244,7 @@ def test_01_check_hypervisor_vm_snapshot(self): serviceofferingid=self.service_offering.id, zoneid=self.zone.id ) + self.cleanup.append(vm) try: fail_snapshot = VmSnapshot.create( @@ -254,8 +254,7 @@ def test_01_check_hypervisor_vm_snapshot(self): name="Test Snapshot", description="Test Snapshot Desc" ) - if vm_snapshot: - VmSnapshot.deleteVMSnapshot(self.userapiclient, fail_snapshot.id) + self.cleanup.append(fail_snapshot) self.fail("Successful to take VM snapshot even when vmsnapshotenabled was set to False") except Exception as e: self.debug("Failed to take VM snapshot even vmsnapshotenabled was set to False: %s" % e) @@ -269,8 +268,7 @@ def test_01_check_hypervisor_vm_snapshot(self): name="Test Snapshot", description="Test Snapshot Desc" ) - if vm_snapshot: - VmSnapshot.deleteVMSnapshot(self.userapiclient, vm_snapshot.id) + self.cleanup.append(vm_snapshot) def updateHostHypervisorCapability(self, id, maxDataVolumes, vmSnapshotEnabled=None): cmd = updateHypervisorCapabilities.updateHypervisorCapabilitiesCmd() diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 910e8bd7c047..9096a3b9d6b7 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -5097,6 +5097,11 @@ def deleteVMSnapshot(cls, apiclient, vmsnapshotid): cmd.vmsnapshotid = vmsnapshotid return apiclient.deleteVMSnapshot(cmd) + def delete(self, apiclient): + cmd = deleteVMSnapshot.deleteVMSnapshotCmd() + cmd.vmsnapshotid = self.id + return apiclient.deleteVMSnapshot(cmd) + class Region: """ Regions related Api """ From 9c3770f84ec0b2b42f172dbcfb6bedd1b2dfd7c9 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 2 Feb 2022 12:06:26 +0530 Subject: [PATCH 6/6] changes Signed-off-by: Abhishek Kumar --- .../{smoke => component}/test_hypervisor_capabilities.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename test/integration/{smoke => component}/test_hypervisor_capabilities.py (97%) diff --git a/test/integration/smoke/test_hypervisor_capabilities.py b/test/integration/component/test_hypervisor_capabilities.py similarity index 97% rename from test/integration/smoke/test_hypervisor_capabilities.py rename to test/integration/component/test_hypervisor_capabilities.py index d2d79908de26..ba02ef7f6cec 100644 --- a/test/integration/smoke/test_hypervisor_capabilities.py +++ b/test/integration/component/test_hypervisor_capabilities.py @@ -211,12 +211,13 @@ def test_01_check_hypervisor_max_data_volume_limit(self): self.userapiclient, volume_created2 ) + vm.stop(self.userapiclient, forced=True) vm.detach_volume(self.userapiclient, volume_created1) vm.detach_volume(self.userapiclient, volume_created2) @skipTestIf("notSupported") @attr(tags=["advanced", "basic"], required_hardware="false") - def test_01_check_hypervisor_vm_snapshot(self): + def test_02_check_hypervisor_vm_snapshot(self): """ Test hypervisor vmsnapshotenabled effect # 1. Set vmsnapshotenabled to false for hypervisor @@ -226,6 +227,8 @@ def test_01_check_hypervisor_vm_snapshot(self): # 5. Set vmsnapshotenabled to true for hypervisor # 6. Try VM snapshot again, it should succeed """ + if self.hypervisor == "KVM": + self.skipTest("Skipping test: Reason - VM Snapshot of running VM is not supported for KVM") self.updateHostHypervisorCapability(self.hostCapability.id, None, False) capabilities = self.listHostHypervisorCapabilities(self.hostCapability.id) self.assertTrue(isinstance(capabilities, list), "listHypervisorCapabilities response not a valid list")