Merge remote-tracking branch 'origin/4.15'

This commit is contained in:
Rohit Yadav 2021-04-06 15:09:48 +05:30
commit 49acd1dec7
10 changed files with 327 additions and 38 deletions

View File

@ -19,6 +19,8 @@
package org.apache.cloudstack.storage.command;
import java.util.Map;
import com.cloud.agent.api.to.DiskTO;
public class DettachCommand extends StorageSubSystemCommand {
@ -28,6 +30,7 @@ public class DettachCommand extends StorageSubSystemCommand {
private String _iScsiName;
private String _storageHost;
private int _storagePort;
private Map<String, String> params;
public DettachCommand(final DiskTO disk, final String vmName) {
super();
@ -35,6 +38,13 @@ public class DettachCommand extends StorageSubSystemCommand {
this.vmName = vmName;
}
public DettachCommand(final DiskTO disk, final String vmName, Map<String, String> params) {
super();
this.disk = disk;
this.vmName = vmName;
this.params = params;
}
@Override
public boolean executeInSequence() {
return false;
@ -88,6 +98,14 @@ public class DettachCommand extends StorageSubSystemCommand {
return _storagePort;
}
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
@Override
public void setExecuteInSequence(final boolean inSeq) {

View File

@ -2426,7 +2426,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
DiskDef.DiskBus busT = getDiskModelFromVMDetail(vmTO);
if (busT == null) {
busT = getGuestDiskModel(vmTO.getPlatformEmulator());
busT = getGuestDiskModel(vmTO.getPlatformEmulator(), isUefiEnabled);
}
// If we're using virtio scsi, then we need to add a virtual scsi controller
@ -2521,7 +2521,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
});
if (MapUtils.isNotEmpty(details) && details.containsKey(GuestDef.BootType.UEFI.toString())) {
boolean isUefiEnabled = MapUtils.isNotEmpty(details) && details.containsKey(GuestDef.BootType.UEFI.toString());
if (isUefiEnabled) {
isSecureBoot = isSecureMode(details.get(GuestDef.BootType.UEFI.toString()));
}
if (vmSpec.getOs().toLowerCase().contains("window")) {
@ -2589,7 +2590,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
// if params contains a rootDiskController key, use its value (this is what other HVs are doing)
DiskDef.DiskBus diskBusType = getDiskModelFromVMDetail(vmSpec);
if (diskBusType == null) {
diskBusType = getGuestDiskModel(vmSpec.getPlatformEmulator());
diskBusType = getGuestDiskModel(vmSpec.getPlatformEmulator(), isUefiEnabled);
}
DiskDef.DiskBus diskBusTypeData = getDataDiskModelFromVMDetail(vmSpec);
@ -2600,16 +2601,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final DiskDef disk = new DiskDef();
int devId = volume.getDiskSeq().intValue();
if (volume.getType() == Volume.Type.ISO) {
if (volPath == null) {
if (isSecureBoot) {
disk.defISODisk(null, devId,isSecureBoot,isWindowsTemplate);
} else {
/* Add iso as placeholder */
disk.defISODisk(null, devId);
}
} else {
disk.defISODisk(volPath, devId);
}
disk.defISODisk(volPath, devId, isUefiEnabled);
if (_guestCpuArch != null && _guestCpuArch.equals("aarch64")) {
disk.setBusType(DiskDef.DiskBus.SCSI);
}
@ -2636,14 +2630,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
disk.defNetworkBasedDisk(glusterVolume + path.replace(mountpoint, ""), pool.getSourceHost(), pool.getSourcePort(), null,
null, devId, diskBusType, DiskProtocol.GLUSTER, DiskDef.DiskFmtType.QCOW2);
} else if (pool.getType() == StoragePoolType.CLVM || physicalDisk.getFormat() == PhysicalDiskFormat.RAW) {
if (volume.getType() == Volume.Type.DATADISK) {
if (volume.getType() == Volume.Type.DATADISK && !(isWindowsTemplate && isUefiEnabled)) {
disk.defBlockBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData);
}
else {
disk.defBlockBasedDisk(physicalDisk.getPath(), devId, diskBusType);
}
} else {
if (volume.getType() == Volume.Type.DATADISK) {
if (volume.getType() == Volume.Type.DATADISK && !(isWindowsTemplate && isUefiEnabled)) {
disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData, DiskDef.DiskFmtType.QCOW2);
} else {
if (isSecureBoot) {
@ -3429,7 +3423,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
boolean isGuestPVEnabled(final String guestOSName) {
DiskDef.DiskBus db = getGuestDiskModel(guestOSName);
DiskDef.DiskBus db = getGuestDiskModel(guestOSName, false);
return db != DiskDef.DiskBus.IDE;
}
@ -3483,7 +3477,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return null;
}
private DiskDef.DiskBus getGuestDiskModel(final String platformEmulator) {
private DiskDef.DiskBus getGuestDiskModel(final String platformEmulator, boolean isUefiEnabled) {
if (_guestCpuArch != null && _guestCpuArch.equals("aarch64")) {
return DiskDef.DiskBus.SCSI;
}
@ -3492,6 +3486,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return DiskDef.DiskBus.IDE;
} else if (platformEmulator.startsWith("Other PV Virtio-SCSI")) {
return DiskDef.DiskBus.SCSI;
} else if (isUefiEnabled && platformEmulator.startsWith("Windows")) {
return DiskDef.DiskBus.SATA;
} else if (platformEmulator.contains("Ubuntu") ||
platformEmulator.startsWith("Fedora") ||
platformEmulator.startsWith("CentOS") ||

View File

@ -716,16 +716,14 @@ public class LibvirtVMDef {
} else if (bus == DiskBus.VIRTIO) {
return "vd" + getDevLabelSuffix(devId);
} else if (bus == DiskBus.SATA){
if (!forIso) {
return "sda";
}
return "sd" + getDevLabelSuffix(devId);
}
if (forIso) {
devId --;
} else if(devId >= 2) {
devId += 2;
}
return (DiskBus.SATA == bus) ? "sdb" : "hd" + getDevLabelSuffix(devId);
return "hd" + getDevLabelSuffix(devId);
}
@ -784,6 +782,16 @@ public class LibvirtVMDef {
_bus = DiskBus.IDE;
}
public void defISODisk(String volPath, boolean isUefiEnabled) {
_diskType = DiskType.FILE;
_deviceType = DeviceType.CDROM;
_sourcePath = volPath;
_bus = isUefiEnabled ? DiskBus.SATA : DiskBus.IDE;
_diskLabel = getDevLabel(3, _bus, true);
_diskFmtType = DiskFmtType.RAW;
_diskCacheMode = DiskCacheMode.NONE;
}
public void defISODisk(String volPath, Integer devId) {
if (devId == null) {
defISODisk(volPath);
@ -798,20 +806,15 @@ public class LibvirtVMDef {
}
}
public void defISODisk(String volPath, Integer devId,boolean isSecure, boolean isWindowOs) {
public void defISODisk(String volPath, Integer devId,boolean isSecure) {
if (!isSecure) {
defISODisk(volPath, devId);
} else {
_diskType = DiskType.FILE;
_deviceType = DeviceType.CDROM;
_sourcePath = volPath;
if (isWindowOs) {
_diskLabel = getDevLabel(devId, DiskBus.SATA, true);
_bus = DiskBus.SATA;
} else {
_diskLabel = getDevLabel(devId, DiskBus.SCSI, true);
_bus = DiskBus.SCSI;
}
_diskLabel = getDevLabel(devId, DiskBus.SATA, true);
_bus = DiskBus.SATA;
_diskFmtType = DiskFmtType.RAW;
_diskCacheMode = DiskCacheMode.NONE;

View File

@ -67,6 +67,7 @@ import org.apache.cloudstack.utils.qemu.QemuImg;
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
import org.apache.cloudstack.utils.qemu.QemuImgException;
import org.apache.cloudstack.utils.qemu.QemuImgFile;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
@ -1081,9 +1082,10 @@ public class KVMStorageProcessor implements StorageProcessor {
}
}
protected synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach) throws LibvirtException, URISyntaxException,
protected synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, Map<String, String> params) throws LibvirtException, URISyntaxException,
InternalErrorException {
String isoXml = null;
boolean isUefiEnabled = MapUtils.isNotEmpty(params) && params.containsKey("UEFI");
if (isoPath != null && isAttach) {
final int index = isoPath.lastIndexOf("/");
final String path = isoPath.substring(0, index);
@ -1093,11 +1095,11 @@ public class KVMStorageProcessor implements StorageProcessor {
isoPath = isoVol.getPath();
final DiskDef iso = new DiskDef();
iso.defISODisk(isoPath);
iso.defISODisk(isoPath, isUefiEnabled);
isoXml = iso.toString();
} else {
final DiskDef iso = new DiskDef();
iso.defISODisk(null);
iso.defISODisk(null, isUefiEnabled);
isoXml = iso.toString();
}
@ -1123,7 +1125,7 @@ public class KVMStorageProcessor implements StorageProcessor {
try {
String dataStoreUrl = getDataStoreUrlFromStore(store);
final Connect conn = LibvirtConnection.getConnectionByVmName(cmd.getVmName());
attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl + File.separator + isoTO.getPath(), true);
attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl + File.separator + isoTO.getPath(), true, cmd.getControllerInfo());
} catch (final LibvirtException e) {
return new Answer(cmd, false, e.toString());
} catch (final URISyntaxException e) {
@ -1146,7 +1148,7 @@ public class KVMStorageProcessor implements StorageProcessor {
try {
String dataStoreUrl = getDataStoreUrlFromStore(store);
final Connect conn = LibvirtConnection.getConnectionByVmName(cmd.getVmName());
attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl + File.separator + isoTO.getPath(), false);
attachOrDetachISO(conn, cmd.getVmName(), dataStoreUrl + File.separator + isoTO.getPath(), false, cmd.getParams());
} catch (final LibvirtException e) {
return new Answer(cmd, false, e.toString());
} catch (final URISyntaxException e) {

View File

@ -112,7 +112,7 @@
<cs.selenium.server.version>1.0-20081010.060147</cs.selenium.server.version>
<cs.selenium-java-client-driver.version>1.0.1</cs.selenium-java-client-driver.version>
<cs.testng.version>7.1.0</cs.testng.version>
<cs.wiremock.version>2.11.0</cs.wiremock.version>
<cs.wiremock.version>2.27.2</cs.wiremock.version>
<cs.xercesImpl.version>2.12.0</cs.xercesImpl.version>
<!-- Dependencies versions -->

View File

@ -112,6 +112,7 @@ import com.cloud.agent.api.to.DataTO;
import com.cloud.agent.api.to.DatadiskTO;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.api.ApiDBUtils;
import com.cloud.api.ApiResponseHelper;
import com.cloud.api.query.dao.UserVmJoinDao;
@ -136,6 +137,8 @@ import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.HypervisorGuru;
import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.projects.Project;
import com.cloud.projects.ProjectManager;
@ -199,6 +202,7 @@ import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.VirtualMachineProfileImpl;
import com.cloud.vm.VmDetailConstants;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
@ -283,6 +287,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
MessageBus _messageBus;
@Inject
private VMTemplateDetailsDao _tmpltDetailsDao;
@Inject
private HypervisorGuruManager _hvGuruMgr;
private boolean _disableExtraction = false;
private List<TemplateAdapter> _adapters;
@ -1289,11 +1295,16 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
DataTO isoTO = tmplt.getTO();
DiskTO disk = new DiskTO(isoTO, null, null, Volume.Type.ISO);
HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
VirtualMachineTO vmTO = hvGuru.implement(profile);
Command cmd = null;
if (attach) {
cmd = new AttachCommand(disk, vmName);
cmd = new AttachCommand(disk, vmName, vmTO.getDetails());
} else {
cmd = new DettachCommand(disk, vmName);
cmd = new DettachCommand(disk, vmName, vmTO.getDetails());
}
Answer a = _agentMgr.easySend(vm.getHostId(), cmd);
return (a != null && a.getResult());

View File

@ -30,6 +30,7 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.storage.Storage;
import com.cloud.storage.TemplateProfile;
import com.cloud.projects.ProjectManager;
@ -180,6 +181,8 @@ public class TemplateManagerImplTest {
@Inject
private VMTemplateDao _tmpltDao;
@Inject
HypervisorGuruManager _hvGuruMgr;
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
AtomicInteger ai = new AtomicInteger(0);
@ -697,6 +700,11 @@ public class TemplateManagerImplTest {
return Mockito.mock(VMTemplateDetailsDao.class);
}
@Bean
public HypervisorGuruManager hypervisorGuruManager() {
return Mockito.mock(HypervisorGuruManager.class);
}
public static class Library implements TypeFilter {
@Override
public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {

View File

@ -0,0 +1,235 @@
# 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.
""" Deploy VM from ISO with UEFI
"""
# Import Local Modules
from nose.plugins.attrib import attr
from marvin.cloudstackTestCase import cloudstackTestCase, unittest
from marvin.lib.utils import cleanup_resources
from marvin.lib.base import (Account,
VirtualMachine,
ServiceOffering,
Iso,
DiskOffering,
Host,
)
from marvin.lib.common import (get_zone,
get_domain,
list_hosts,
)
from marvin.codes import PASS
from marvin.sshClient import SshClient
import xml.etree.ElementTree as ET
from lxml import etree
class TestDeployVMFromISOWithUefi(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.testClient = super(TestDeployVMFromISOWithUefi, cls).getClsTestClient()
cls.apiclient = cls.testClient.getApiClient()
cls.hypervisor = cls.testClient.getHypervisorInfo()
if cls.hypervisor != 'kvm':
raise unittest.SkipTest("Those tests can be run only for KVM hypervisor")
cls.testdata = cls.testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
hosts = list_hosts(cls.apiclient, zoneid = cls.zone.id, type="Routing")
if not cls.isUefiEnabledOnAtLeastOnHost(hosts):
raise unittest.SkipTest("At least one host should support UEFI")
cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__
# Create service, disk offerings etc
cls.service_offering = ServiceOffering.create(
cls.apiclient,
cls.testdata["service_offering"]
)
cls.disk_offering = DiskOffering.create(
cls.apiclient,
cls.testdata["disk_offering"]
)
cls.testdata["virtual_machine"]["zoneid"] = cls.zone.id
cls.testdata["iso1"]["zoneid"] = cls.zone.id
cls.testdata["iso3"]["zoneid"] = cls.zone.id
cls.account = Account.create(
cls.apiclient,
cls.testdata["account"],
domainid=cls.domain.id
)
cls._cleanup = [cls.account, cls.service_offering, cls.disk_offering]
cls.centos_iso = Iso.create(
cls.apiclient,
cls.testdata["iso1"],
account=cls.account.name,
domainid=cls.account.domainid,
zoneid=cls.zone.id
)
try:
# Download the ISO
cls.centos_iso.download(cls.apiclient)
except Exception as e:
raise Exception("Exception while downloading ISO %s: %s"
% (cls.centos_iso.id, e))
cls.windows_iso = Iso.create(
cls.apiclient,
cls.testdata["iso3"],
account=cls.account.name,
domainid=cls.account.domainid,
zoneid=cls.zone.id
)
try:
# Download the ISO
cls.windows_iso.download(cls.apiclient)
except Exception as e:
raise Exception("Exception while downloading ISO %s: %s"
% (cls.windows_iso.id, e))
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.hypervisor = self.testClient.getHypervisorInfo()
if self.hypervisor != 'kvm':
raise self.skipTest("Skipping test case for non-kvm hypervisor")
return
@attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true")
def test_01_deploy_vm_from_iso_uefi_secure(self):
"""Test Deploy CentOS Virtual Machine from ISO with UEFI Secure
"""
self.deployVmAndTestUefi(self.centos_iso, 'Secure', 'yes')
return
@attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true")
def test_02_deploy_vm_from_iso_uefi_legacy(self):
"""Test Deploy CentOS Virtual Machine from ISO with UEFI Legacy mode
"""
self.deployVmAndTestUefi(self.centos_iso, 'Legacy', 'no')
return
@attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true")
def test_03_deploy_windows_vm_from_iso_uefi_legacy(self):
"""Test Deploy Windows Virtual Machine from ISO with UEFI Legacy mode
"""
self.deployVmAndTestUefi(self.windows_iso, 'Legacy', 'no')
return
@attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true")
def test_04_deploy_windows_vm_from_iso_uefi_secure(self):
"""Test Deploy Windows Virtual Machine from ISO with UEFI Secure mode
"""
self.deployVmAndTestUefi(self.windows_iso, 'Secure', 'yes')
return
def getVirshXML(self, host, instancename):
self.assertIsNotNone(host, "Host should not be None")
self.assertIsNotNone(instancename, "Instance name should not be None")
sshc = SshClient(
host=host.ipaddress,
port=22,
user=self.hostConfig['username'],
passwd=self.hostConfig['password'])
virsh_cmd = 'virsh dumpxml %s' % instancename
xml_res = sshc.execute(virsh_cmd)
xml_as_str = ''.join(xml_res)
parser = etree.XMLParser(remove_blank_text=True)
return ET.fromstring(xml_as_str, parser=parser)
def checkBootTypeAndMode(self, root, bootmodesecure, isWindowsIso):
machine = root.find(".os/type").get("machine")
self.assertEqual(("q35" in machine), True, "The virtual machine is not with UEFI boot type")
bootmode = root.find(".os/loader").get("secure")
self.assertEqual((bootmode == bootmodesecure), True, "The VM is not in the right boot mode")
if isWindowsIso:
disks = root.findall("devices/disk")
for disk in disks:
bus = disk.find("target").get("bus")
self.debug("bus is %s" % bus)
self.assertEqual(bus == 'sata', True, "All disks of the VM should be with bus SATA")
def deployVmAndTestUefi(self, iso, bootmode, bootmodesecure):
self.virtual_machine = VirtualMachine.create(
self.apiclient,
self.testdata["virtual_machine"],
accountid=self.account.name,
domainid=self.account.domainid,
templateid=iso.id,
serviceofferingid=self.service_offering.id,
diskofferingid=self.disk_offering.id,
hypervisor=self.hypervisor,
boottype='UEFI',
bootmode=bootmode,
zoneid=self.zone.id
)
response = self.virtual_machine.getState(
self.apiclient,
VirtualMachine.RUNNING)
self.assertEqual(response[0], PASS, response[1])
hosts = Host.list(self.apiclient, id=self.virtual_machine.hostid)
if len(hosts) != 1:
assert False, "Could not find host with id " + self.virtual_machine.hostid
host = hosts[0]
isWindowsIso = False
if "Windows" in iso.ostypename:
isWindowsIso = True
instancename = self.virtual_machine.instancename
virshxml = self.getVirshXML(host, instancename)
self.checkBootTypeAndMode(virshxml, bootmodesecure, isWindowsIso)
@classmethod
def isUefiEnabledOnAtLeastOnHost(cls, hosts):
for h in hosts:
if h.ueficapability:
return True
return False

View File

@ -940,6 +940,16 @@ test_data = {
"ostype": "CentOS 5.6 (64-bit)",
"mode": 'HTTP_DOWNLOAD',
},
"iso3": {
"displaytext": "Test ISO 3",
"name": "ISO 3",
"url": "http://people.apache.org/~tsp/dummy.iso",
"isextractable": True,
"isfeatured": True,
"ispublic": True,
"ostype": "Windows Server 2012 (64-bit)",
"mode": 'HTTP_DOWNLOAD',
},
"isfeatured": True,
"ispublic": True,
"isextractable": True,

View File

@ -522,7 +522,7 @@ class VirtualMachine:
method='GET', hypervisor=None, customcpunumber=None,
customcpuspeed=None, custommemory=None, rootdisksize=None,
rootdiskcontroller=None, vpcid=None, macaddress=None, datadisktemplate_diskoffering_list={},
properties=None, nicnetworklist=None):
properties=None, nicnetworklist=None, bootmode=None, boottype=None):
"""Create the instance"""
cmd = deployVirtualMachine.deployVirtualMachineCmd()
@ -657,6 +657,12 @@ class VirtualMachine:
if nicnetworklist:
cmd.nicnetworklist = nicnetworklist
if bootmode:
cmd.bootmode = bootmode
if boottype:
cmd.boottype = boottype
virtual_machine = apiclient.deployVirtualMachine(cmd, method=method)
if 'password' in virtual_machine.__dict__.keys():