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 b9a809f99567..b2afa1d6f865 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -590,6 +590,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 f2132cfb5b40..914d54dec8b0 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -34,7 +34,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; @@ -304,6 +303,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; @@ -2614,6 +2614,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 790a5709b0f0..581a6da6a1e8 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; @@ -605,6 +601,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; @@ -614,6 +611,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; @@ -663,9 +661,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; @@ -4459,7 +4457,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) { @@ -4468,19 +4473,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)) { diff --git a/test/integration/component/test_hypervisor_capabilities.py b/test/integration/component/test_hypervisor_capabilities.py new file mode 100644 index 000000000000..ba02ef7f6cec --- /dev/null +++ b/test/integration/component/test_hypervisor_capabilities.py @@ -0,0 +1,289 @@ +# 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 + + 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): + super(TestHypervisorCapabilities, cls).tearDownClass() + + 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) + + super(TestHypervisorCapabilities, self).tearDown() + 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") + 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_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( + 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.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_02_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 + """ + 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") + 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 + ) + self.cleanup.append(vm) + + try: + fail_snapshot = VmSnapshot.create( + self.userapiclient, + vmid=vm.id, + snapshotmemory="false", + name="Test Snapshot", + description="Test Snapshot Desc" + ) + 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) + + 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" + ) + self.cleanup.append(vm_snapshot) + + 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) + 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 """