diff --git a/api/src/com/cloud/vm/VmDetailConstants.java b/api/src/com/cloud/vm/VmDetailConstants.java index d34afc13a16..c3c6db7a296 100644 --- a/api/src/com/cloud/vm/VmDetailConstants.java +++ b/api/src/com/cloud/vm/VmDetailConstants.java @@ -23,4 +23,5 @@ public interface VmDetailConstants { public static final String NESTED_VIRTUALIZATION_FLAG = "nestedVirtualizationFlag"; public static final String HYPERVISOR_TOOLS_VERSION = "hypervisortoolsversion"; public static final String DATA_DISK_CONTROLLER = "dataDiskController"; + public static final String SVGA_VRAM_SIZE = "svga.vramSize"; } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 238ba3ed864..9b7885a74e1 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1946,6 +1946,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa vmConfigSpec.getExtraConfig().addAll( Arrays.asList(configureVnc(extraOptions.toArray(new OptionValue[0]), hyperHost, vmInternalCSName, vmSpec.getVncPassword(), keyboardLayout))); + // config video card + configureVideoCard(vmMo, vmSpec, vmConfigSpec); + // // Configure VM // @@ -1964,8 +1967,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa postDiskConfigBeforeStart(vmMo, vmSpec, sortedDisks, ideControllerKey, scsiControllerKey, iqnToPath, hyperHost, context); - postVideoCardMemoryConfigBeforeStart(vmMo, vmSpec); - // // Power-on VM // @@ -2015,26 +2016,24 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } /** - * Sets video card memory to the one provided in detail svga.vramSize (if provided). + * Sets video card memory to the one provided in detail svga.vramSize (if provided) on {@code vmConfigSpec}. * 64MB was always set before. * Size must be in KB. * @param vmMo virtual machine mo * @param vmSpec virtual machine specs + * @param vmConfigSpec virtual machine config spec + * @throws Exception exception */ - protected void postVideoCardMemoryConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec) { - String paramVRamSize = "svga.vramSize"; - if (vmSpec.getDetails().containsKey(paramVRamSize)){ - String value = vmSpec.getDetails().get(paramVRamSize); + protected void configureVideoCard(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, VirtualMachineConfigSpec vmConfigSpec) throws Exception { + if (vmSpec.getDetails().containsKey(VmDetailConstants.SVGA_VRAM_SIZE)){ + String value = vmSpec.getDetails().get(VmDetailConstants.SVGA_VRAM_SIZE); try { long svgaVmramSize = Long.parseLong(value); - setNewVRamSizeVmVideoCard(vmMo, svgaVmramSize); + setNewVRamSizeVmVideoCard(vmMo, svgaVmramSize, vmConfigSpec); } catch (NumberFormatException e){ s_logger.error("Unexpected value, cannot parse " + value + " to long due to: " + e.getMessage()); } - catch (Exception e){ - s_logger.error("Error while reconfiguring vm due to: " + e.getMessage()); - } } } @@ -2042,39 +2041,38 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa * Search for vm video card iterating through vm device list * @param vmMo virtual machine mo * @param svgaVmramSize new svga vram size (in KB) + * @param vmConfigSpec virtual machine config spec */ - private void setNewVRamSizeVmVideoCard(VirtualMachineMO vmMo, long svgaVmramSize) throws Exception { + protected void setNewVRamSizeVmVideoCard(VirtualMachineMO vmMo, long svgaVmramSize, VirtualMachineConfigSpec vmConfigSpec) throws Exception { for (VirtualDevice device : vmMo.getAllDeviceList()){ if (device instanceof VirtualMachineVideoCard){ VirtualMachineVideoCard videoCard = (VirtualMachineVideoCard) device; - modifyVmVideoCardVRamSize(videoCard, vmMo, svgaVmramSize); + modifyVmVideoCardVRamSize(videoCard, vmMo, svgaVmramSize, vmConfigSpec); } } } /** - * Modifies vm vram size if it was set to a different size to the one provided in svga.vramSize (user_vm_details or template_vm_details) + * Modifies vm vram size if it was set to a different size to the one provided in svga.vramSize (user_vm_details or template_vm_details) on {@code vmConfigSpec} * @param videoCard vm's video card device * @param vmMo virtual machine mo * @param svgaVmramSize new svga vram size (in KB) + * @param vmConfigSpec virtual machine config spec */ - private void modifyVmVideoCardVRamSize(VirtualMachineVideoCard videoCard, VirtualMachineMO vmMo, long svgaVmramSize) throws Exception { + protected void modifyVmVideoCardVRamSize(VirtualMachineVideoCard videoCard, VirtualMachineMO vmMo, long svgaVmramSize, VirtualMachineConfigSpec vmConfigSpec) { if (videoCard.getVideoRamSizeInKB().longValue() != svgaVmramSize){ s_logger.info("Video card memory was set " + videoCard.getVideoRamSizeInKB().longValue() + "kb instead of " + svgaVmramSize + "kb"); - VirtualMachineConfigSpec newSizeSpecs = configSpecVideoCardNewVRamSize(videoCard, svgaVmramSize); - boolean res = vmMo.configureVm(newSizeSpecs); - if (res) { - s_logger.info("Video card memory successfully updated to " + svgaVmramSize + "kb"); - } + configureSpecVideoCardNewVRamSize(videoCard, svgaVmramSize, vmConfigSpec); } } /** - * Returns a VirtualMachineConfigSpec to edit its svga vram size + * Add edit spec on {@code vmConfigSpec} to modify svga vram size * @param videoCard video card device to edit providing the svga vram size * @param svgaVmramSize new svga vram size (in KB) + * @param vmConfigSpec virtual machine spec */ - private VirtualMachineConfigSpec configSpecVideoCardNewVRamSize(VirtualMachineVideoCard videoCard, long svgaVmramSize){ + protected void configureSpecVideoCardNewVRamSize(VirtualMachineVideoCard videoCard, long svgaVmramSize, VirtualMachineConfigSpec vmConfigSpec){ videoCard.setVideoRamSizeInKB(svgaVmramSize); videoCard.setUseAutoDetect(false); @@ -2082,9 +2080,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa arrayVideoCardConfigSpecs.setDevice(videoCard); arrayVideoCardConfigSpecs.setOperation(VirtualDeviceConfigSpecOperation.EDIT); - VirtualMachineConfigSpec changeVideoCardSpecs = new VirtualMachineConfigSpec(); - changeVideoCardSpecs.getDeviceChange().add(arrayVideoCardConfigSpecs); - return changeVideoCardSpecs; + vmConfigSpec.getDeviceChange().add(arrayVideoCardConfigSpecs); } private void tearDownVm(VirtualMachineMO vmMo) throws Exception{ diff --git a/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java b/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java index 22388a8a69c..33e7cd2fc70 100644 --- a/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java +++ b/plugins/hypervisors/vmware/test/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java @@ -19,13 +19,14 @@ package com.cloud.hypervisor.vmware.resource; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.mock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.never; +import static org.mockito.Matchers.eq; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -34,8 +35,10 @@ import org.apache.cloudstack.storage.command.CopyCommand; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.powermock.api.mockito.PowerMockito; @@ -43,6 +46,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.vmware.vim25.VirtualDevice; +import com.vmware.vim25.VirtualDeviceConfigSpec; import com.vmware.vim25.VirtualMachineConfigSpec; import com.vmware.vim25.VirtualMachineVideoCard; import com.cloud.agent.api.Command; @@ -99,6 +103,10 @@ public class VmwareResourceTest { @Mock VirtualMachineTO vmSpec3dgpu; @Mock + VirtualMachineVideoCard videoCard; + @Mock + VirtualDevice virtualDevice; + @Mock DataTO srcDataTO; @Mock NfsTO srcDataNfsTO; @@ -107,9 +115,11 @@ public class VmwareResourceTest { private static final Integer NFS_VERSION = Integer.valueOf(3); private static final Integer NFS_VERSION_NOT_PRESENT = null; + private static final long VRAM_MEMORY_SIZE = 131072l; + private static final long VIDEO_CARD_MEMORY_SIZE = 65536l; @Before - public void setup() { + public void setup() throws Exception { MockitoAnnotations.initMocks(this); storageCmd = PowerMockito.mock(CopyCommand.class); doReturn(context).when(_resource).getServiceContext(null); @@ -117,6 +127,7 @@ public class VmwareResourceTest { when(storageCmd.getSrcTO()).thenReturn(srcDataTO); when(srcDataTO.getDataStore()).thenReturn(srcDataNfsTO); when(srcDataNfsTO.getNfsVersion()).thenReturn(NFS_VERSION); + when(videoCard.getVideoRamSizeInKB()).thenReturn(VIDEO_CARD_MEMORY_SIZE); } //Test successful scaling up the vm @@ -138,19 +149,56 @@ public class VmwareResourceTest { } @Test - public void testStartVm3dgpuEnabled() throws Exception{ + public void testConfigureVideoCardSvgaVramProvided() throws Exception { Map specDetails = new HashMap(); - specDetails.put("svga.vramSize", "131072"); + specDetails.put("svga.vramSize", String.valueOf(VRAM_MEMORY_SIZE)); when(vmSpec3dgpu.getDetails()).thenReturn(specDetails); - VirtualMachineVideoCard videoCard = mock(VirtualMachineVideoCard.class); - when(videoCard.getVideoRamSizeInKB()).thenReturn(65536l); - when(vmMo3dgpu.getAllDeviceList()).thenReturn(Arrays.asList((VirtualDevice) videoCard)); + _resource.configureVideoCard(vmMo3dgpu, vmSpec3dgpu, vmConfigSpec); + verify(_resource).setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec); + } - when(vmMo3dgpu.configureVm(any(VirtualMachineConfigSpec.class))).thenReturn(true); + @Test + public void testConfigureVideoCardNotSvgaVramProvided() throws Exception { + _resource.configureVideoCard(vmMo3dgpu, vmSpec3dgpu, vmConfigSpec); + verify(_resource, never()).setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec); + } - _resource.postVideoCardMemoryConfigBeforeStart(vmMo3dgpu, vmSpec3dgpu); - verify(vmMo3dgpu).configureVm(any(VirtualMachineConfigSpec.class)); + @Test + public void testModifyVmVideoCardVRamSizeDifferentVramSizes() { + _resource.modifyVmVideoCardVRamSize(videoCard, vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec); + verify(_resource).configureSpecVideoCardNewVRamSize(videoCard, VRAM_MEMORY_SIZE, vmConfigSpec); + } + + @Test + public void testModifyVmVideoCardVRamSizeEqualSizes() { + _resource.modifyVmVideoCardVRamSize(videoCard, vmMo3dgpu, VIDEO_CARD_MEMORY_SIZE, vmConfigSpec); + verify(_resource, never()).configureSpecVideoCardNewVRamSize(videoCard, VIDEO_CARD_MEMORY_SIZE, vmConfigSpec); + } + + @Test + public void testSetNewVRamSizeVmVideoCardPresent() throws Exception { + when(vmMo3dgpu.getAllDeviceList()).thenReturn(Arrays.asList(videoCard, virtualDevice)); + _resource.setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec); + verify(_resource).modifyVmVideoCardVRamSize(videoCard, vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec); + } + + @Test + public void testSetNewVRamSizeVmVideoCardNotPresent() throws Exception { + when(vmMo3dgpu.getAllDeviceList()).thenReturn(Arrays.asList(virtualDevice)); + _resource.setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec); + verify(_resource, never()).modifyVmVideoCardVRamSize(any(VirtualMachineVideoCard.class), eq(vmMo3dgpu), eq(VRAM_MEMORY_SIZE), eq(vmConfigSpec)); + } + + @Test + public void testConfigureSpecVideoCardNewVRamSize() { + when(vmConfigSpec.getDeviceChange()).thenReturn(new ArrayList()); + _resource.configureSpecVideoCardNewVRamSize(videoCard, VRAM_MEMORY_SIZE, vmConfigSpec); + + InOrder inOrder = Mockito.inOrder(videoCard, vmConfigSpec); + inOrder.verify(videoCard).setVideoRamSizeInKB(VRAM_MEMORY_SIZE); + inOrder.verify(videoCard).setUseAutoDetect(false); + inOrder.verify(vmConfigSpec).getDeviceChange(); } // --------------------------------------------------------------------------------------------------- diff --git a/test/integration/smoke/test_deploy_vgpu_enabled_vm.py b/test/integration/smoke/test_deploy_vgpu_enabled_vm.py index c9eb7672e2d..d49ab08d484 100644 --- a/test/integration/smoke/test_deploy_vgpu_enabled_vm.py +++ b/test/integration/smoke/test_deploy_vgpu_enabled_vm.py @@ -24,21 +24,20 @@ from marvin.cloudstackTestCase import cloudstackTestCase # base - contains all resources as entities and defines create, delete, # list operations on them -from marvin.lib.base import Account, VirtualMachine, ServiceOffering +from marvin.lib.base import Account, VirtualMachine, ServiceOffering, NetworkOffering, Network, Template # utils - utility classes for common cleanup, external library wrappers etc -from marvin.lib.utils import cleanup_resources +from marvin.lib.utils import cleanup_resources, get_hypervisor_type, validateList # common - commonly used methods for all tests are listed here from marvin.lib.common import get_zone, get_domain, get_template, list_hosts from marvin.sshClient import SshClient -from marvin.codes import FAILED +from marvin.codes import FAILED, PASS from nose.plugins.attrib import attr - class TestDeployvGPUenabledVM(cloudstackTestCase): """ @@ -100,48 +99,76 @@ class TestDeployvGPUenabledVM(cloudstackTestCase): def setUp(self): self.testdata = self.testClient.getParsedTestDataConfig()["vgpu"] self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() if self.noSuitableHost or self.unsupportedHypervisor: - self.skipTest("Skipping test because suitable hypervisor/host not\ - present") + self.hypervisor = get_hypervisor_type(self.apiclient) + if self.hypervisor.lower() not in ["vmware"]: + self.skipTest("Skipping test because suitable hypervisor/host not\ + present") + self.testdata = self.testClient.getParsedTestDataConfig() # Get Zone, Domain and Default Built-in template self.domain = get_domain(self.apiclient) - self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests()) - self.testdata["mode"] = self.zone.networktype - # Before running this test, register a windows template with ostype as - # 'Windows 7 (32-bit)' + self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests()) + + # Before running this test for Xen Server, register a windows template with ostype as + # 'Windows 7 (32-bit)' self.template = get_template( - self.apiclient, - self.zone.id, - self.testdata["ostype"]) + self.apiclient, + self.zone.id, + self.testdata["ostype"]) - if self.template == FAILED: - assert False, "get_template() failed to return template with description %s" % self.testdata[ - "ostype"] - # create a user account + # create a user account self.account = Account.create( - self.apiclient, - self.testdata["account"], - domainid=self.domain.id + self.apiclient, + self.testdata["account"], + domainid=self.domain.id ) + self.cleanup = [] - self.testdata["small"]["zoneid"] = self.zone.id - self.testdata["small"]["template"] = self.template.id + if self.hypervisor.lower() in ["xenserver"]: + self.testdata["mode"] = self.zone.networktype - self.testdata["service_offerings"]["vgpu260qwin"]["serviceofferingdetails"] = [ - { - 'pciDevice': 'Group of NVIDIA Corporation GK107GL [GRID K1] GPUs'}, { - 'vgpuType': 'GRID K120Q'}] - # create a service offering - self.service_offering = ServiceOffering.create( - self.apiclient, - self.testdata["service_offerings"]["vgpu260qwin"], - ) - # build cleanup list - self.cleanup = [ - self.service_offering, - self.account - ] + if self.template == FAILED: + assert False, "get_template() failed to return template with description %s" % self.testdata[ + "ostype"] + + self.testdata["small"]["zoneid"] = self.zone.id + self.testdata["small"]["template"] = self.template.id + + self.testdata["service_offerings"]["vgpu260qwin"]["serviceofferingdetails"] = [ + { + 'pciDevice': 'Group of NVIDIA Corporation GK107GL [GRID K1] GPUs'}, { + 'vgpuType': 'GRID K120Q'}] + # create a service offering + self.service_offering = ServiceOffering.create( + self.apiclient, + self.testdata["service_offerings"]["vgpu260qwin"], + ) + # build cleanup list + self.cleanup = [ + self.service_offering, + self.account + ] + elif self.hypervisor.lower() in ["vmware"]: + self.testdata["isolated_network"]["zoneid"] = self.zone.id + self.userapiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + self.service_offering = ServiceOffering.create( + self.apiclient, + self.testdata["service_offering"]) + self.cleanup.append(self.service_offering) + + # Create Shared Network Offering + self.isolated_network_offering = NetworkOffering.create( + self.apiclient, + self.testdata["isolated_network_offering"]) + self.cleanup.append(self.isolated_network_offering) + # Enable Isolated Network offering + self.isolated_network_offering.update(self.apiclient, state='Enabled') + @attr(tags=['advanced', 'basic', 'vgpu'], required_hardware="true") def test_deploy_vgpu_enabled_vm(self): @@ -152,6 +179,10 @@ class TestDeployvGPUenabledVM(cloudstackTestCase): # 2. Virtual Machine is vGPU enabled (via SSH) # 3. listVirtualMachines returns accurate information """ + + if self.hypervisor.lower() not in ["xenserver"]: + self.skipTest("This test case is written specifically\ + for XenServer hypervisor") self.virtual_machine = VirtualMachine.create( self.apiclient, self.testdata["small"], @@ -228,3 +259,80 @@ class TestDeployvGPUenabledVM(cloudstackTestCase): cleanup_resources(self.apiclient, self.cleanup) except Exception as e: self.debug("Warning! Exception in tearDown: %s" % e) + + @attr(tags=["advanced"]) + def test_3d_gpu_support(self): + """ + + # 1. Register a template for VMware with nicAdapter vmxnet3 and 3D GPU details + # 2. Deploy a VM using this template + # 3. Create an isolated network + # 4. Add network to VM + # 5. Verify vm details for 3D GPU details + """ + + if self.hypervisor.lower() not in ["vmware"]: + self.skipTest("This test case is written specifically\ + for Vmware hypervisor") + + # Register a private template in the account with nic adapter vmxnet3 + # Also add required 3D GPU details for enabling it + template = Template.register( + self.userapiclient, + self.testdata["configurableData"]["vmxnet3template"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + details=[{"mks.enable3d" : "true", "mks.use3dRenderer" : "automatic", + "svga.autodetect" : "false", "svga.vramSize" : "131072"}] + ) + self.cleanup.append(template) + template.download(self.apiclient) + + templates = Template.list( + self.userapiclient, + listall=True, + id=template.id, + templatefilter="self" + ) + + self.assertEqual( + validateList(templates)[0], + PASS, + "Templates list validation failed" + ) + + self.testdata["virtual_machine"]["zoneid"] = self.zone.id + self.testdata["virtual_machine"]["template"] = template.id + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.testdata["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + templateid=template.id, + serviceofferingid=self.service_offering.id) + + isolated_network = Network.create( + self.apiclient, + self.testdata["isolated_network"], + self.account.name, + self.account.domainid, + networkofferingid=self.isolated_network_offering.id) + + virtual_machine.add_nic(self.apiclient, isolated_network.id) + + qresultset = self.dbclient.execute("select id from vm_instance where uuid = '%s';" % virtual_machine.id) + vm_id = qresultset[0] + qresultset = self.dbclient.execute("select name, value from user_vm_details where vm_id = '%d';" % vm_id) + detailKeys = [x[0] for x in qresultset] + + self.assertTrue('mks.enable3d' in detailKeys and 'mks.use3dRenderer' in detailKeys and 'svga.autodetect' in detailKeys and 'svga.vramSize' in detailKeys, "VM details do not contain 3D GPU details") + + self.assertEquals('true', qresultset[detailKeys.index('mks.enable3d')][1], "Expected detail 'mks.enable3d'='true'") + + self.assertEquals('automatic', qresultset[detailKeys.index('mks.use3dRenderer')][1], "Expected detail 'mks.use3dRenderer'='automatic'") + + self.assertEquals('false', qresultset[detailKeys.index('svga.autodetect')][1], "Expected detail 'svga.autodetect'='false'") + + self.assertEquals('131072', qresultset[detailKeys.index('svga.vramSize')][1], "Expected detail 'svga.vramSize'='131072'")