From a73712ec4ea0deae58d0b43edcbe9ca5e8f33fd4 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Wed, 1 Jul 2020 08:44:11 +0530 Subject: [PATCH] server: Enable sending hypervior host name via metadata - VR and Config Drive (#3976) Enable sending hypervisor host details via metadata for VR and Config Drive providers Co-authored-by: Pearl Dsilva --- .../java/com/cloud/network/NetworkModel.java | 3 +- .../element/UserDataServiceProvider.java | 11 + .../com/cloud/vm/VirtualMachineProfile.java | 4 + .../agent/api/routing/VmDataCommand.java | 1 - .../com/cloud/vm/VirtualMachineManager.java | 14 + .../service/NetworkOrchestrationService.java | 10 + .../cloud/vm/VirtualMachineManagerImpl.java | 22 +- .../orchestration/NetworkOrchestrator.java | 23 +- .../java/com/cloud/vm/dao/UserVmDaoImpl.java | 6 +- .../BaremetalUserdataElement.java | 5 + .../wrapper/LibvirtMigrateCommandWrapper.java | 132 +++++-- .../LibvirtComputingResourceTest.java | 3 +- .../com/cloud/network/NetworkModelImpl.java | 4 +- .../element/CloudZonesNetworkElement.java | 15 +- .../element/ConfigDriveNetworkElement.java | 99 ++++- .../network/element/VirtualRouterElement.java | 52 ++- .../network/router/CommandSetupHelper.java | 29 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 4 +- .../topology/BasicNetworkTopology.java | 1 - .../cloud/network/MockNetworkModelImpl.java | 2 +- .../ConfigDriveNetworkElementTest.java | 15 +- .../com/cloud/vpc/MockNetworkManagerImpl.java | 5 + .../com/cloud/vpc/MockNetworkModelImpl.java | 2 +- .../integration/component/test_configdrive.py | 288 ++++++++++++++- .../integration/component/test_vr_metadata.py | 337 ++++++++++++++++++ 25 files changed, 1005 insertions(+), 82 deletions(-) create mode 100644 test/integration/component/test_vr_metadata.py diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index 002271ab854..e933a1cc7bd 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -66,6 +66,7 @@ public interface NetworkModel { String VM_ID_FILE = "vm-id"; String PUBLIC_KEYS_FILE = "public-keys"; String CLOUD_IDENTIFIER_FILE = "cloud-identifier"; + String HYPERVISOR_HOST_NAME_FILE = "hypervisor-host-name"; int CONFIGDATA_DIR = 0; int CONFIGDATA_FILE = 1; int CONFIGDATA_CONTENT = 2; @@ -313,7 +314,7 @@ public interface NetworkModel { boolean getNetworkEgressDefaultPolicy(Long networkId); List generateVmData(String userData, String serviceOffering, long datacenterId, - String vmName, String vmHostName, long vmId, String vmUuid, String guestIpAddress, String publicKey, String password, Boolean isWindows); + String vmName, String vmHostName, long vmId, String vmUuid, String guestIpAddress, String publicKey, String password, Boolean isWindows, String hostname); String getValidNetworkCidr(Network guestNetwork); diff --git a/api/src/main/java/com/cloud/network/element/UserDataServiceProvider.java b/api/src/main/java/com/cloud/network/element/UserDataServiceProvider.java index bf6d7e819e9..13f72a79824 100644 --- a/api/src/main/java/com/cloud/network/element/UserDataServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/UserDataServiceProvider.java @@ -34,4 +34,15 @@ public interface UserDataServiceProvider extends NetworkElement { boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException; boolean saveSSHKey(Network network, NicProfile nic, VirtualMachineProfile vm, String sshPublicKey) throws ResourceUnavailableException; + + /** + * Saves the hypervisor hostname in configdrive ISO or in the virtual router + * @param profile + * @param network + * @param vm + * @param dest + * @return + * @throws ResourceUnavailableException + */ + boolean saveHypervisorHostname(NicProfile profile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException; } diff --git a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java index e6c196b1142..c17a716666d 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java @@ -38,6 +38,10 @@ public interface VirtualMachineProfile { void setVmData(List vmData); + void setDisks(List disks); + + void setNics(List nics); + String getConfigDriveLabel(); void setConfigDriveLabel(String configDriveLabel); diff --git a/core/src/main/java/com/cloud/agent/api/routing/VmDataCommand.java b/core/src/main/java/com/cloud/agent/api/routing/VmDataCommand.java index c74c7cf7d88..dfcdcfd8792 100644 --- a/core/src/main/java/com/cloud/agent/api/routing/VmDataCommand.java +++ b/core/src/main/java/com/cloud/agent/api/routing/VmDataCommand.java @@ -72,5 +72,4 @@ public class VmDataCommand extends NetworkElementCommand { public void addVmData(String folder, String file, String contents) { vmData.add(new String[] {folder, file, contents}); } - } diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index ea2fa760c81..9165631ca81 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -21,6 +21,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.agent.api.to.NicTO; @@ -41,6 +42,7 @@ import com.cloud.offering.DiskOfferingInfo; import com.cloud.offering.ServiceOffering; import com.cloud.storage.StoragePool; import com.cloud.template.VirtualMachineTemplate; +import com.cloud.user.Account; import com.cloud.utils.component.Manager; import com.cloud.utils.fsm.NoTransitionException; @@ -61,6 +63,12 @@ public interface VirtualMachineManager extends Manager { ConfigKey ResoureCountRunningVMsonly = new ConfigKey("Advanced", Boolean.class, "resource.count.running.vms.only", "false", "Count the resources of only running VMs in resource limitation.", true); + ConfigKey AllowExposeHypervisorHostnameAccountLevel = new ConfigKey("Advanced", Boolean.class, "account.allow.expose.host.hostname", + "false", "If set to true, it allows the hypervisor host name on which the VM is spawned on to be exposed to the VM", true, ConfigKey.Scope.Account); + + ConfigKey AllowExposeHypervisorHostname = new ConfigKey("Advanced", Boolean.class, "global.allow.expose.host.hostname", + "false", "If set to true, it allows the hypervisor host name on which the VM is spawned on to be exposed to the VM", true, ConfigKey.Scope.Global); + interface Topics { String VM_POWER_STATE = "vm.powerstate"; } @@ -214,6 +222,12 @@ public interface VirtualMachineManager extends Manager { boolean getExecuteInSequence(HypervisorType hypervisorType); + static String getHypervisorHostname(String name) { + final Account caller = CallContext.current().getCallingAccount(); + String destHostname = (AllowExposeHypervisorHostname.value() && AllowExposeHypervisorHostnameAccountLevel.valueIn(caller.getId())) ? name : null; + return destHostname; + } + /** * Unmanage a VM from CloudStack: * - Remove the references of the VM and its volumes, nics, IPs from database diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index 682660252b3..78d427164bb 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -294,6 +294,16 @@ public interface NetworkOrchestrationService { void finalizeUpdateInSequence(Network network, boolean success); + /** + * Adds hypervisor hostname to a file - hypervisor-host-name if the userdata + * service provider is ConfigDrive or VirtualRouter + * @param vm holds the details of the Virtual Machine + * @param dest holds information of the destination + * @param migrationSuccessful + * @throws ResourceUnavailableException in case Datastore or agent to which a command is to be sent is unavailable + */ + void setHypervisorHostname(VirtualMachineProfile vm, DeployDestination dest, boolean migrationSuccessful) throws ResourceUnavailableException; + List getNetworkGurus(); /** diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index e74e6fffa4d..0ae249c584f 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -2432,7 +2432,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac protected void migrate(final VMInstanceVO vm, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException { s_logger.info("Migrating " + vm + " to " + dest); - + final UserVmVO userVm = _userVmDao.findById(vm.getId()); final long dstHostId = dest.getHost().getId(); final Host fromHost = _hostDao.findById(srcHostId); if (fromHost == null) { @@ -2582,7 +2582,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (final OperationTimedoutException e) { s_logger.debug("Error while checking the vm " + vm + " on host " + dstHostId, e); } - migrated = true; } finally { if (!migrated) { @@ -2597,7 +2596,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (final AgentUnavailableException ae) { s_logger.info("Looks like the destination Host is unavailable for cleanup"); } - + _networkMgr.setHypervisorHostname(profile, dest, false); try { stateTransitTo(vm, Event.OperationFailed, srcHostId); } catch (final NoTransitionException e) { @@ -2605,6 +2604,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } else { _networkMgr.commitNicForMigration(vmSrc, profile); + _networkMgr.setHypervisorHostname(profile, dest, true); } work.setStep(Step.Done); @@ -2906,6 +2906,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac vm.setLastHostId(srcHostId); vm.setPodIdToDeployIn(destHost.getPodId()); moveVmToMigratingState(vm, destHostId, work); + List vmData = null; boolean migrated = false; try { @@ -2916,7 +2917,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac Nic defaultNic = _networkModel.getDefaultNic(vm.getId()); - List vmData = null; if (defaultNic != null) { UserVmVO userVm = _userVmDao.findById(vm.getId()); Map details = userVmDetailsDao.listDetailsKeyPairs(vm.getId()); @@ -2926,9 +2926,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (_networkModel.isSharedNetworkWithoutServices(network.getId())) { final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); - vmData = _networkModel.generateVmData(userVm.getUserData(), serviceOffering, vm.getDataCenterId(), vm.getInstanceName(), vm.getHostName(), vm.getId(), - vm.getUuid(), defaultNic.getMacAddress(), userVm.getDetail("SSH.PublicKey"), (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows); + vm.getUuid(), defaultNic.getMacAddress(), userVm.getDetail("SSH.PublicKey"), (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows, VirtualMachineManager.getHypervisorHostname(destination.getHost().getName())); String vmName = vm.getInstanceName(); String configDriveIsoRootFolder = "/tmp"; String isoFile = configDriveIsoRootFolder + "/" + vmName + "/configDrive/" + vmName + ".iso"; @@ -2946,7 +2945,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("TIme out occured while exeuting command AttachOrDettachConfigDrive " + e.getMessage()); } - } } @@ -2970,7 +2968,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (final OperationTimedoutException e) { s_logger.warn("Error while checking the vm " + vm + " is on host " + destHost, e); } - migrated = true; } finally { if (!migrated) { @@ -2987,6 +2984,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (final NoTransitionException e) { s_logger.error("Error while transitioning vm from migrating to running state.", e); } + _networkMgr.setHypervisorHostname(profile, destination, false); + } else { + _networkMgr.setHypervisorHostname(profile, destination, true); } work.setStep(Step.Done); @@ -4201,12 +4201,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (final AgentUnavailableException ae) { s_logger.info("Looks like the destination Host is unavailable for cleanup"); } - + _networkMgr.setHypervisorHostname(profile, dest, false); try { stateTransitTo(vm, Event.OperationFailed, srcHostId); } catch (final NoTransitionException e) { s_logger.warn(e.getMessage()); } + } else { + _networkMgr.setHypervisorHostname(profile, dest, true); } work.setStep(Step.Done); @@ -4464,7 +4466,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return new ConfigKey[] {ClusterDeltaSyncInterval, StartRetry, VmDestroyForcestop, VmOpCancelInterval, VmOpCleanupInterval, VmOpCleanupWait, VmOpLockStateRetry, VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval, VmConfigDriveLabel, VmConfigDriveOnPrimaryPool, HaVmRestartHostUp, - ResoureCountRunningVMsonly }; + ResoureCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel }; } public List getStoragePoolAllocators() { diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 078cba26893..e16bde62b3f 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -143,6 +143,7 @@ import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.dao.RemoteAccessVpnDao; import com.cloud.network.dao.RemoteAccessVpnVO; import com.cloud.network.element.AggregatedCommandExecutor; +import com.cloud.network.element.ConfigDriveNetworkElement; import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.DnsServiceProvider; import com.cloud.network.element.IpDeployer; @@ -151,6 +152,7 @@ import com.cloud.network.element.NetworkElement; import com.cloud.network.element.RedundantResource; import com.cloud.network.element.StaticNatServiceProvider; import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.network.element.VirtualRouterElement; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.guru.NetworkGuruAdditionalFunctions; import com.cloud.network.lb.LoadBalancingRulesManager; @@ -1614,6 +1616,26 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } + @Override + public void setHypervisorHostname(VirtualMachineProfile vm, DeployDestination dest, boolean migrationSuccessful) throws ResourceUnavailableException { + final List nics = _nicDao.listByVmId(vm.getId()); + for (final NicVO nic : nics) { + final NetworkVO network = _networksDao.findById(nic.getNetworkId()); + final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); + final NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), + _networkModel.getNetworkTag(vm.getHypervisorType(), network)); + for (final NetworkElement element : networkElements) { + if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData) && element instanceof UserDataServiceProvider) { + if (element instanceof ConfigDriveNetworkElement && !migrationSuccessful || element instanceof VirtualRouterElement && migrationSuccessful) { + final UserDataServiceProvider sp = (UserDataServiceProvider) element; + if (!sp.saveHypervisorHostname(profile, network, vm, dest)) { + throw new CloudRuntimeException("Failed to Add hypervisor hostname"); + } + } + } + } + } + } @DB protected void updateNic(final NicVO nic, final long networkId, final int count) { @@ -1749,7 +1771,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra .collect(Collectors.toMap(NicExtraDhcpOptionVO::getCode, NicExtraDhcpOptionVO::getValue)); } - @Override public void prepareNicForMigration(final VirtualMachineProfile vm, final DeployDestination dest) { if(vm.getType().equals(VirtualMachine.Type.DomainRouter) && (vm.getHypervisorType().equals(HypervisorType.KVM) || vm.getHypervisorType().equals(HypervisorType.VMware))) { diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java index 6ed31a1ea56..25479d6658a 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java @@ -365,8 +365,10 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use @Override public void loadDetails(UserVmVO vm) { - Map details = _detailsDao.listDetailsKeyPairs(vm.getId()); - vm.setDetails(details); + if (vm != null ) { + Map details = _detailsDao.listDetailsKeyPairs(vm.getId()); + vm.setDetails(details); + } } @Override diff --git a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java index b00d49068c9..d2c9731ddd1 100644 --- a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java +++ b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java @@ -88,6 +88,11 @@ public class BaremetalUserdataElement extends AdapterBase implements NetworkElem return false; } + @Override + public boolean saveHypervisorHostname(NicProfile profile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { + return true; + } + @Override public Map> getCapabilities() { return capabilities; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java index f0eb287c5ea..0524be79617 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java @@ -22,6 +22,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URISyntaxException; import java.util.List; import java.util.Map; import java.util.Set; @@ -37,12 +38,12 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.DpdkTO; import org.apache.commons.collections.MapUtils; import org.apache.commons.io.IOUtils; @@ -64,6 +65,7 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.MigrateAnswer; import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo; +import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.resource.LibvirtConnection; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; @@ -115,6 +117,9 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper disks, String vmName) { + String oldIsoVolumePath = null; + for (DiskDef disk : disks) { + if (disk.getDiskPath() != null && disk.getDiskPath().contains(vmName)) { + oldIsoVolumePath = disk.getDiskPath(); + break; + } + } + return oldIsoVolumePath; + } + + private String getNewVolumePathIfDatastoreHasChanged(LibvirtComputingResource libvirtComputingResource, Connect conn, VirtualMachineTO to) throws LibvirtException, URISyntaxException { + DiskTO newDisk = null; + for (DiskTO disk : to.getDisks()) { + if (disk.getPath() != null && disk.getPath().contains("configdrive")) { + newDisk = disk; + break; + } + } + + String newIsoVolumePath = null; + if (newDisk != null) { + newIsoVolumePath = libvirtComputingResource.getVolumePath(conn, newDisk); + } + return newIsoVolumePath; + } + private String getPathFromSourceText(Set paths, String sourceText) { if (paths != null && !StringUtils.isBlank(sourceText)) { for (String path : paths) { @@ -572,4 +596,62 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper generateVmData(String userData, String serviceOffering, long datacenterId, String vmName, String vmHostName, long vmId, String vmUuid, - String guestIpAddress, String publicKey, String password, Boolean isWindows) { + String guestIpAddress, String publicKey, String password, Boolean isWindows, String hostname) { DataCenterVO dcVo = _dcDao.findById(datacenterId); final String zoneName = dcVo.getName(); @@ -2462,7 +2462,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi vmData.add(new String[]{PASSWORD_DIR, PASSWORD_FILE, password}); } - + vmData.add(new String[]{METATDATA_DIR, HYPERVISOR_HOST_NAME_FILE, hostname}); return vmData; } diff --git a/server/src/main/java/com/cloud/network/element/CloudZonesNetworkElement.java b/server/src/main/java/com/cloud/network/element/CloudZonesNetworkElement.java index cb3fac821e0..3ed5fcc7d43 100644 --- a/server/src/main/java/com/cloud/network/element/CloudZonesNetworkElement.java +++ b/server/src/main/java/com/cloud/network/element/CloudZonesNetworkElement.java @@ -55,6 +55,7 @@ import com.cloud.vm.ReservationContext; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; @@ -147,7 +148,7 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem } private VmDataCommand generateVmDataCommand(String vmPrivateIpAddress, String userData, String serviceOffering, String zoneName, String guestIpAddress, - String vmName, String vmInstanceName, long vmId, String vmUuid, String publicKey) { + String vmName, String vmInstanceName, long vmId, String vmUuid, String publicKey, String hostname) { VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress, vmName, _networkMgr.getExecuteInSeqNtwkElmtCmd()); // if you add new metadata files, also edit systemvm/patches/debian/config/var/www/html/latest/.htaccess cmd.addVmData("userdata", "user-data", userData); @@ -163,7 +164,7 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem setVmInstanceId(vmUuid, cmd); } cmd.addVmData("metadata", "public-keys", publicKey); - + cmd.addVmData("metadata", "hypervisor-host-name", hostname); return cmd; } @@ -217,11 +218,11 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem } String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(uservm.getServiceOfferingId()).getDisplayText(); String zoneName = _dcDao.findById(network.getDataCenterId()).getName(); - + String destHostname = VirtualMachineManager.getHypervisorHostname(dest.getHost().getName()); cmds.addCommand( "vmdata", generateVmDataCommand(nic.getIPv4Address(), userData, serviceOffering, zoneName, nic.getIPv4Address(), uservm.getHostName(), uservm.getInstanceName(), - uservm.getId(), uservm.getUuid(), sshPublicKey)); + uservm.getId(), uservm.getUuid(), sshPublicKey, destHostname)); try { _agentManager.send(dest.getHost().getId(), cmds); } catch (OperationTimedoutException e) { @@ -251,6 +252,11 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem return false; } + @Override + public boolean saveHypervisorHostname(NicProfile profile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { + return true; + } + @Override public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException { // TODO Auto-generated method stub @@ -261,5 +267,4 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem public boolean verifyServicesCombination(Set services) { return true; } - } diff --git a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java index 53312221c97..4daeda6a530 100644 --- a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java +++ b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.storage.configdrive.ConfigDrive; import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; @@ -39,13 +40,16 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.HandleConfigDriveIsoCommand; import com.cloud.agent.api.to.DiskTO; import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DeployDestination; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.UnsupportedServiceException; import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.HypervisorGuruManager; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; @@ -66,6 +70,7 @@ import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.crypt.DBEncryptionUtil; +import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.StateListener; import com.cloud.utils.fsm.StateMachine2; @@ -110,11 +115,23 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle @Inject HostDao _hostDao; @Inject + HostPodDao _podDao; + @Inject + ClusterDao _clusterDao; + @Inject AgentManager agentManager; @Inject DataStoreManager _dataStoreMgr; @Inject EndPointSelector _ep; + @Inject + EntityManager _entityMgr; + @Inject + ServiceOfferingDao _offeringDao; + @Inject + ImageStoreDao imgstore; + @Inject + private HypervisorGuruManager _hvGuruMgr; private final static Integer CONFIGDRIVEDISKSEQ = 4; @@ -204,8 +221,8 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { return (canHandle(network.getTrafficType()) - && configureConfigDriveData(profile, nic)) - && createConfigDriveIso(profile, dest); + && configureConfigDriveData(profile, nic, dest)) + && createConfigDriveIso(profile, dest, null); } @Override @@ -244,6 +261,19 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle return canHandle; } + @Override + public boolean saveHypervisorHostname(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { + if (vm.getVirtualMachine().getType() == VirtualMachine.Type.User) { + try { + recreateConfigDriveIso(nic, network, vm, dest); + } catch (ResourceUnavailableException e) { + LOG.error("Failed to add config disk drive due to: ", e); + return false; + } + } + return true; + } + @Override public boolean saveUserData(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException { // saveUserData is called by updateVirtualMachine API which requires VM to be shutdown @@ -325,6 +355,32 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { } + private void recreateConfigDriveIso(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { + if (nic.isDefaultNic() && _networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive)) { + DiskTO diskToUse = null; + for (DiskTO disk : vm.getDisks()) { + if (disk.getType() == Volume.Type.ISO && disk.getPath() != null && disk.getPath().contains("configdrive")) { + diskToUse = disk; + break; + } + } + final UserVmVO userVm = _userVmDao.findById(vm.getId()); + + if (userVm != null) { + final boolean isWindows = isWindows(userVm.getGuestOSId()); + List vmData = _networkModel.generateVmData(userVm.getUserData(), _serviceOfferingDao.findById(userVm.getServiceOfferingId()).getName(), userVm.getDataCenterId(), userVm.getInstanceName(), vm.getHostName(), vm.getId(), + vm.getUuid(), nic.getMacAddress(), userVm.getDetail("SSH.PublicKey"), (String) vm.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows, VirtualMachineManager.getHypervisorHostname(dest.getHost().getName())); + vm.setVmData(vmData); + vm.setConfigDriveLabel(VirtualMachineManager.VmConfigDriveLabel.value()); + createConfigDriveIso(vm, dest, diskToUse); + } + } + } + + private boolean isWindows(long guestOSId) { + return _guestOSCategoryDao.findById(_guestOSDao.findById(guestOSId).getCategoryId()).getName().equalsIgnoreCase("Windows"); + } + private DataStore findDataStore(VirtualMachineProfile profile, DeployDestination dest) { DataStore dataStore = null; if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) { @@ -422,8 +478,9 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle return agentId; } - private boolean createConfigDriveIso(VirtualMachineProfile profile, DeployDestination dest) throws ResourceUnavailableException { - final DataStore dataStore = findDataStore(profile, dest); + private boolean createConfigDriveIso(VirtualMachineProfile profile, DeployDestination dest, DiskTO disk) throws ResourceUnavailableException { + DataStore dataStore = getDatastoreForConfigDriveIso(disk, profile, dest); + final Long agentId = findAgentId(profile, dest, dataStore); if (agentId == null || dataStore == null) { throw new ResourceUnavailableException("Config drive iso creation failed, agent or datastore not available", @@ -446,6 +503,28 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle return true; } + private DataStore getDatastoreForConfigDriveIso(DiskTO disk, VirtualMachineProfile profile, DeployDestination dest) { + DataStore dataStore = null; + if (disk != null) { + String dId = disk.getData().getDataStore().getUuid(); + if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) { + dataStore = _dataStoreMgr.getDataStore(dId, DataStoreRole.Primary); + } else { + List dataStores = _dataStoreMgr.listImageStores(); + String url = disk.getData().getDataStore().getUrl(); + for(DataStore ds : dataStores) { + if (url.equals(ds.getUri()) && DataStoreRole.Image.equals(ds.getRole())) { + dataStore = ds; + break; + } + } + } + } else { + dataStore = findDataStore(profile, dest); + } + return dataStore; + } + private boolean deleteConfigDriveIso(final VirtualMachine vm) throws ResourceUnavailableException { DataStore dataStore = _dataStoreMgr.getImageStoreWithFreeCapacity(vm.getDataCenterId()); Long agentId = findAgentIdForImageStore(dataStore); @@ -502,7 +581,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle } } - private boolean configureConfigDriveData(final VirtualMachineProfile profile, final NicProfile nic) { + private boolean configureConfigDriveData(final VirtualMachineProfile profile, final NicProfile nic, final DeployDestination dest) { final UserVmVO vm = _userVmDao.findById(profile.getId()); if (vm.getType() != VirtualMachine.Type.User) { return false; @@ -512,9 +591,15 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle final String sshPublicKey = getSshKey(profile); final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); - + String hostname = _hostDao.findById(vm.getHostId()).getName(); + String destHostname = null; + if (dest.getHost() == null ) { + destHostname = VirtualMachineManager.getHypervisorHostname(hostname); + } else { + destHostname = VirtualMachineManager.getHypervisorHostname(dest.getHost().getName()); + } final List vmData = _networkModel.generateVmData(vm.getUserData(), serviceOffering, vm.getDataCenterId(), vm.getInstanceName(), vm.getHostName(), vm.getId(), - vm.getUuid(), nic.getIPv4Address(), sshPublicKey, (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows); + vm.getUuid(), nic.getIPv4Address(), sshPublicKey, (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows, destHostname); profile.setVmData(vmData); profile.setConfigDriveLabel(VirtualMachineManager.VmConfigDriveLabel.value()); } diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java index 0b664a9a167..bbc6aa77b79 100644 --- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java @@ -31,7 +31,10 @@ import org.cloud.network.router.deployment.RouterDeploymentDefinitionBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.vm.VirtualMachineProfileImpl; import com.cloud.vm.VmDetailConstants; +import com.cloud.vm.dao.NicDao; import com.google.gson.Gson; import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd; @@ -117,7 +120,7 @@ import com.cloud.vm.dao.UserVmDao; public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider, LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer, -NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServiceProvider { +NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServiceProvider{ private static final Logger s_logger = Logger.getLogger(VirtualRouterElement.class); public static final AutoScaleCounterType AutoScaleCounterCpu = new AutoScaleCounterType("cpu"); public static final AutoScaleCounterType AutoScaleCounterMemory = new AutoScaleCounterType("memory"); @@ -163,6 +166,10 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ DataCenterDao _dcDao; @Inject NetworkModel _networkModel; + @Inject + NicDao _nicDao; + @Inject + VMTemplateDao _templateDao; @Inject NetworkTopologyContext networkTopologyContext; @@ -765,6 +772,33 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ return result; } + @Override + public boolean saveHypervisorHostname(NicProfile nicProfile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { + if (_networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.VirtualRouter) && vm.getVirtualMachine().getType() == VirtualMachine.Type.User) { + VirtualMachine uvm = vm.getVirtualMachine(); + UserVmVO destVm = _userVmDao.findById(uvm.getId()); + VirtualMachineProfile profile = null; + + if (destVm != null) { + destVm.setHostId(dest.getHost().getId()); + _userVmDao.update(uvm.getId(), destVm); + profile = new VirtualMachineProfileImpl(destVm); + profile.setDisks(vm.getDisks()); + profile.setNics(vm.getNics()); + profile.setVmData(vm.getVmData()); + } else { + profile = vm; + } + + updateUserVmData(nicProfile, network, profile); + if (destVm != null) { + destVm.setHostId(uvm.getHostId()); + _userVmDao.update(uvm.getId(), destVm); + } + } + return true; + } + @Override public boolean saveUserData(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException { if (!canHandle(network, null)) { @@ -1066,6 +1100,8 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ } final VirtualMachineProfile uservm = vm; + List vmData = uservm.getVmData(); + uservm.setVmData(vmData); final List routers = getRouters(network, dest); @@ -1204,6 +1240,19 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ return true; } + private void updateUserVmData(final NicProfile nic, final Network network, final VirtualMachineProfile vm) throws ResourceUnavailableException { + if (_networkModel.areServicesSupportedByNetworkOffering(network.getNetworkOfferingId(), Service.UserData)) { + boolean result = saveUserData(network, nic, vm); + if (!result) { + s_logger.warn("Failed to update userdata for vm " + vm + " and nic " + nic); + } else { + s_logger.debug("Successfully saved user data to router"); + } + } else { + s_logger.debug("Not applying userdata for nic id=" + nic.getId() + " in vm id=" + vm.getId() + " because it is not supported in network id=" + network.getId()); + } + } + @Override public boolean prepareMigration(final NicProfile nic, final Network network, final VirtualMachineProfile vm, final DeployDestination dest, final ReservationContext context) { if (nic.getBroadcastType() != Networks.BroadcastDomainType.Pvlan) { @@ -1351,5 +1400,4 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ _routerDao.persist(router); } } - } diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index 63e9d8025f3..00da0d3a102 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -68,6 +68,8 @@ import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDao; import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.Network.Provider; @@ -177,23 +179,29 @@ public class CommandSetupHelper { private VlanDao _vlanDao; @Inject private IPAddressDao _ipAddressDao; - @Inject private RouterControlHelper _routerControlHelper; + @Inject + private HostDao _hostDao; @Autowired @Qualifier("networkHelper") protected NetworkHelper _networkHelper; public void createVmDataCommand(final VirtualRouter router, final UserVm vm, final NicVO nic, final String publicKey, final Commands cmds) { - final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); - final String zoneName = _dcDao.findById(router.getDataCenterId()).getName(); - final IPAddressVO staticNatIp = _ipAddressDao.findByVmIdAndNetworkId(nic.getNetworkId(), vm.getId()); - cmds.addCommand( - "vmdata", - generateVmDataCommand(router, nic.getIPv4Address(), vm.getUserData(), serviceOffering, zoneName, - staticNatIp == null || staticNatIp.getState() != IpAddress.State.Allocated ? null : staticNatIp.getAddress().addr(), vm.getHostName(), vm.getInstanceName(), - vm.getId(), vm.getUuid(), publicKey, nic.getNetworkId())); + if (vm != null && router != null && nic != null) { + final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); + final String zoneName = _dcDao.findById(router.getDataCenterId()).getName(); + final IPAddressVO staticNatIp = _ipAddressDao.findByVmIdAndNetworkId(nic.getNetworkId(), vm.getId()); + + Host host = _hostDao.findById(vm.getHostId()); + String destHostname = VirtualMachineManager.getHypervisorHostname(host.getName()); + cmds.addCommand( + "vmdata", + generateVmDataCommand(router, nic.getIPv4Address(), vm.getUserData(), serviceOffering, zoneName, + staticNatIp == null || staticNatIp.getState() != IpAddress.State.Allocated ? null : staticNatIp.getAddress().addr(), vm.getHostName(), vm.getInstanceName(), + vm.getId(), vm.getUuid(), publicKey, nic.getNetworkId(), destHostname)); + } } public void createApplyVpnUsersCommand(final List users, final VirtualRouter router, final Commands cmds) { @@ -1038,7 +1046,7 @@ public class CommandSetupHelper { private VmDataCommand generateVmDataCommand(final VirtualRouter router, final String vmPrivateIpAddress, final String userData, final String serviceOffering, final String zoneName, final String publicIpAddress, final String vmName, final String vmInstanceName, final long vmId, final String vmUuid, final String publicKey, - final long guestNetworkId) { + final long guestNetworkId, String hostname) { final VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress, vmName, _networkModel.getExecuteInSeqNtwkElmtCmd()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); @@ -1084,6 +1092,7 @@ public class CommandSetupHelper { } cmd.addVmData("metadata", "cloud-identifier", cloudIdentifier); + cmd.addVmData("metadata", "hypervisor-host-name", hostname); return cmd; } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 4e620443b0d..4ef4d1e42e7 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -4341,9 +4341,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (_networkModel.isSharedNetworkWithoutServices(network.getId())) { final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); - + String destHostname = VirtualMachineManager.getHypervisorHostname(dest.getHost().getName()); List vmData = _networkModel.generateVmData(vm.getUserData(), serviceOffering, vm.getDataCenterId(), vm.getInstanceName(), vm.getHostName(), vm.getId(), - vm.getUuid(), defaultNic.getIPv4Address(), vm.getDetail(VmDetailConstants.SSH_PUBLIC_KEY), (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows); + vm.getUuid(), defaultNic.getIPv4Address(), vm.getDetail(VmDetailConstants.SSH_PUBLIC_KEY), (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows, destHostname); String vmName = vm.getInstanceName(); String configDriveIsoRootFolder = "/tmp"; String isoFile = configDriveIsoRootFolder + "/" + vmName + "/configDrive/" + vmName + ".iso"; diff --git a/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java b/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java index a7fbe317828..20b45359dd5 100644 --- a/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java +++ b/server/src/main/java/org/apache/cloudstack/network/topology/BasicNetworkTopology.java @@ -349,7 +349,6 @@ public class BasicNetworkTopology implements NetworkTopology { final Long podId = null; final UserdataToRouterRules userdataToRouterRules = new UserdataToRouterRules(network, nic, profile); - return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper(userdataToRouterRules)); } diff --git a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java index a35cec59b1b..6fd57fc04d8 100644 --- a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java @@ -898,7 +898,7 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { } @Override - public List generateVmData(String userData, String serviceOffering, long datacenterId, String vmName, String vmHostName, long vmId, String vmUuid, String guestIpAddress, String publicKey, String password, Boolean isWindows) { + public List generateVmData(String userData, String serviceOffering, long datacenterId, String vmName, String vmHostName, long vmId, String vmUuid, String guestIpAddress, String publicKey, String password, Boolean isWindows, String hostname) { return null; } diff --git a/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java b/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java index d4f96c99e98..5d206f4c16d 100644 --- a/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java +++ b/server/src/test/java/com/cloud/network/element/ConfigDriveNetworkElementTest.java @@ -36,6 +36,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; @@ -83,6 +84,7 @@ import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; +import com.cloud.user.Account; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateListener; import com.cloud.utils.fsm.StateMachine2; @@ -100,6 +102,7 @@ import com.cloud.vm.dao.VMInstanceDao; import com.google.common.collect.Maps; @RunWith(PowerMockRunner.class) +@PrepareForTest(CallContext.class) public class ConfigDriveNetworkElementTest { public static final String CLOUD_ID = "xx"; @@ -146,6 +149,7 @@ public class ConfigDriveNetworkElementTest { @Mock private UserVmVO virtualMachine; @Mock private IPAddressVO publicIp; @Mock private AgentManager agentManager; + @Mock private CallContext callContextMock; @InjectMocks private final ConfigDriveNetworkElement _configDrivesNetworkElement = new ConfigDriveNetworkElement(); @InjectMocks @Spy private NetworkModelImpl _networkModel = new NetworkModelImpl(); @@ -157,7 +161,6 @@ public class ConfigDriveNetworkElementTest { _configDrivesNetworkElement._networkModel = _networkModel; when(_dataStoreMgr.getImageStoreWithFreeCapacity(DATACENTERID)).thenReturn(dataStore); - when(_ep.select(dataStore)).thenReturn(endpoint); when(_vmDao.findById(VMID)).thenReturn(virtualMachine); when(_dcDao.findById(DATACENTERID)).thenReturn(dataCenterVO); @@ -173,6 +176,7 @@ public class ConfigDriveNetworkElementTest { when(serviceOfferingVO.getDisplayText()).thenReturn(VMOFFERING); when(guestOSVO.getCategoryId()).thenReturn(0L); when(virtualMachine.getGuestOSId()).thenReturn(0L); + when(virtualMachine.getHostId()).thenReturn(0L); when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User); when(virtualMachine.getId()).thenReturn(VMID); when(virtualMachine.getServiceOfferingId()).thenReturn(SOID); @@ -254,10 +258,12 @@ public class ConfigDriveNetworkElementTest { @Test @SuppressWarnings("unchecked") - @PrepareForTest({ConfigDriveBuilder.class}) + @PrepareForTest({ConfigDriveBuilder.class, CallContext.class}) public void testAddPasswordAndUserData() throws Exception { PowerMockito.mockStatic(ConfigDriveBuilder.class); - + PowerMockito.mockStatic(CallContext.class); + PowerMockito.when(CallContext.current()).thenReturn(callContextMock); + Mockito.doReturn(Mockito.mock(Account.class)).when(callContextMock).getCallingAccount(); Method method = ReflectionUtils.getMethods(ConfigDriveBuilder.class, ReflectionUtils.withName("buildConfigDrive")).iterator().next(); PowerMockito.when(ConfigDriveBuilder.class, method).withArguments(Mockito.anyListOf(String[].class), Mockito.anyString(), Mockito.anyString()).thenReturn("content"); @@ -273,11 +279,14 @@ public class ConfigDriveNetworkElementTest { when(_userVmDetailsDao.findDetail(anyLong(), anyString())).thenReturn(userVmDetailVO); when(_ipAddressDao.findByAssociatedVmId(VMID)).thenReturn(publicIp); when(publicIp.getAddress()).thenReturn(new Ip("7.7.7.7")); + when(_hostDao.findById(virtualMachine.getHostId())).thenReturn(hostVO); + when(_hostDao.findById(virtualMachine.getHostId()).getName()).thenReturn("dest-hyp-host-name"); Map parms = Maps.newHashMap(); parms.put(VirtualMachineProfile.Param.VmPassword, PASSWORD); parms.put(VirtualMachineProfile.Param.VmSshPubKey, PUBLIC_KEY); VirtualMachineProfile profile = new VirtualMachineProfileImpl(virtualMachine, null, serviceOfferingVO, null, parms); + profile.setConfigDriveLabel("testlabel"); assertTrue(_configDrivesNetworkElement.addPasswordAndUserdata( network, nicp, profile, deployDestination, null)); diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index b96b58e5ef8..48ff307138a 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -940,6 +940,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches return; } + @Override + public void setHypervisorHostname(VirtualMachineProfile vm, DeployDestination dest, boolean migrationSuccessful) { + return; + } + @Override public void prepareNicForMigration(VirtualMachineProfile vm, DeployDestination dest) { // TODO Auto-generated method stub diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java index 56670d261cf..a731f7c7c10 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java @@ -913,7 +913,7 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { } @Override - public List generateVmData(String userData, String serviceOffering, long datacenterId, String vmName, String vmHostName, long vmId, String vmUuid, String guestIpAddress, String publicKey, String password, Boolean isWindows) { + public List generateVmData(String userData, String serviceOffering, long datacenterId, String vmName, String vmHostName, long vmId, String vmUuid, String guestIpAddress, String publicKey, String password, Boolean isWindows, String hostname) { return null; } diff --git a/test/integration/component/test_configdrive.py b/test/integration/component/test_configdrive.py index c8ed337b06e..38a9a7d2ca8 100644 --- a/test/integration/component/test_configdrive.py +++ b/test/integration/component/test_configdrive.py @@ -32,6 +32,7 @@ from marvin.cloudstackAPI import (restartVPC) from marvin.cloudstackTestCase import cloudstackTestCase from marvin.lib.base import (Account, createVlanIpRange, + Configurations, FireWallRule, Host, listVlanIpRanges, @@ -52,7 +53,8 @@ from marvin.lib.base import (Account, Hypervisor, Template) from marvin.lib.common import (get_domain, get_template, - get_zone, get_test_template) + get_zone, get_test_template, + is_config_suitable) from marvin.lib.utils import random_gen # Import System Modules from nose.plugins.attrib import attr @@ -530,6 +532,24 @@ class ConfigDriveUtils: "local-hostname.txt", "local-ipv4.txt", "public-ipv4.txt"] + # Verify hostname if the appropriate settings are true + configs = Configurations.list( + self.api_client, + name="global.allow.expose.host.hostname", + listall=True + ) + exposeHypevisorHostnameGS = configs[0].value + + configs = Configurations.list( + self.api_client, + name="account.allow.expose.host.hostname", + listall=True + ) + + exposeHypevisorHostnameAcc = configs[0].value + + if exposeHypevisorHostnameGS == 'true' and exposeHypevisorHostnameAcc == 'true': + vm_files.append("hypervisor-host-name.txt") def get_name(vm_file): return "{} metadata".format( @@ -537,9 +557,9 @@ class ConfigDriveUtils: ) metadata = {vm_file: - self._get_config_drive_data(ssh, - metadata_dir + vm_file, - get_name(vm_file)) + self._get_config_drive_data(ssh, + metadata_dir + vm_file, + get_name(vm_file)) for vm_file in vm_files} self.assertEqual( @@ -571,6 +591,18 @@ class ConfigDriveUtils: "Service offering inside metadata does not match " "with the instance offering" ) + + if exposeHypevisorHostnameGS == 'true' and exposeHypevisorHostnameAcc == 'true': + hostname = vm.hostname + + self.debug("Verifying hypervisor hostname of the VM: %s" % vm.name) + self.assertEqual( + str(metadata["hypervisor-host-name.txt"]), + hostname, + "Hostname in the metadata file does not match the host " + "on which the VM is spawned" + ) + return def _verify_openstack_metadata(self, ssh, mount_path): @@ -753,7 +785,8 @@ class ConfigDriveUtils: self.create_StaticNatRule_For_VM(vm, public_ip, network) # Verification - self.validate_StaticNat_rule_For_VM(public_ip, network, vm) + + # self.validate_StaticNat_rule_For_VM(public_ip, network, vm) if not self.vpc: fw_rule = self.create_FirewallRule(public_ip, @@ -1090,12 +1123,12 @@ class ConfigDriveUtils: self.fail("Failed to decrypt new password") except ImportError: # No pycrypto, fallback to openssl - cmd = ["echo " + password_ + + cmd = ["echo " + password_ + " | base64 -d" " | openssl rsautl -decrypt -inkey " + self.keypair.private_key_file + " 2> /dev/null" - ] + ] new_password = subprocess.check_output(cmd, shell=True) self.debug("Decrypted password %s" % new_password) @@ -1314,6 +1347,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): isstaticnat=static_nat, listall=True ) + self.debug("PUBLIC IP = " + public_ips[0]) self.assertEqual(isinstance(public_ips, list), True, "List public IP for network should return a " "valid list" @@ -1675,6 +1709,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): else: self.debug("No host available for migration. " "Test requires at-least 2 hosts") + return host # create_NetworkAclList - Creates network ACL list in the given VPC def create_NetworkAclList(self, name, description, vpc): @@ -2258,3 +2293,242 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils): keypair=self.keypair.name) self.delete(vm1, expunge=True) self.delete(shared_network.network) + + @attr(tags=["advanced", "isonw"], required_hardware="true") + def test_configdrive_isolated_network_hypervisor_hostname_exposed(self): + """Test Configdrive as provider for isolated Networks + to provide userdata and password reset functionality + """ + + # 1. Given a ConfigDrive provider and a network offering + # which has userdata provided ConfigDrive, create + # an Isolated network using that network offering. + # Verify network is successfully created and in the Allocated state + # Set the "global.allow.expose.host.hostname" and "account.allow.expose.host.hostname" flags to true + # to enable viewing hypevisor host name in the metadata file + # Deploy VM in the network created, verify metadata in the configdrive + # my mounting the configdrive ISO and verify the respective files + # + # 2. Create another Isolated network and plug NIC of the VM to this network + # make it the default NIC, verify the metadata file contents. + # + # 3. Remove the default NIC, reboot the VM and verify the metadata file contents + # + # 4. Restart network without cleanup and verify the metadata file contents + # + # 5. Restart the network with cleanup and verify the metadata file contents + # 6. Migrate the VM to another host and verify the metadata file contents + # 10. Delete all the created objects (cleanup). + + self.debug("+++ Preparation Scenario: " + "creating an Isolated networks with " + "config drive when config drive provider is " + "enabled.") + + self.given_config_drive_provider_is("Enabled") + self.given_a_network_offering_with_configdrive() + + create_network1 = self.when_I_create_a_network_with_that_offering() + self.then_the_network_is_successfully_created(create_network1) + self.then_the_network_has(create_network1, state="Allocated") + + network1 = create_network1.network + + # Update global setting for "allow.expose.host.hostname" + Configurations.update(self.api_client, + name="global.allow.expose.host.hostname", + value="true" + ) + + # Update Account level setting + Configurations.update(self.api_client, + name="account.allow.expose.host.hostname", + value="true" + ) + + # Verify that the above mentioned settings are set to true before proceeding + if not is_config_suitable( + apiclient=self.api_client, + name='global.allow.expose.host.hostname', + value='true'): + self.skipTest('global.allow.expose.host.hostname should be true. skipping') + + if not is_config_suitable( + apiclient=self.api_client, + name='account.allow.expose.host.hostname', + value='true'): + self.skipTest('Account level setting account.allow.expose.host.hostname should be true. skipping') + + self.debug("+++Deploy VM in the created Isolated network " + "with user data provider as configdrive") + + vm1 = self.when_I_deploy_a_vm(network1) + + public_ip_1 = self.when_I_create_a_static_nat_ip_to(vm1, network1) + + self.then_vr_is_as_expected(network1) + self.then_config_drive_is_as_expected( + vm1, public_ip_1, + metadata=True) + + + # ===================================================================== + # Network restart tests + # ===================================================================== + + self.debug("+++ Scenario: " + "verify config drive after restart Isolated network without" + " cleanup...") + self.when_I_restart_the_network_with(network1, cleanup=False) + self.then_config_drive_is_as_expected(vm1, public_ip_1, metadata=True) + + # ===================================================================== + self.debug("+++ Scenario: " + "verify config drive after restart Isolated network with" + " cleanup...") + self.when_I_restart_the_network_with(network1, cleanup=True) + self.then_config_drive_is_as_expected(vm1, public_ip_1, metadata=True) + + # ===================================================================== + self.debug("+++ Scenario: " + "verify vm metadata after migrate") + host = self.migrate_VM(vm1) + vm1.hostname = host.name + self.then_config_drive_is_as_expected(vm1, public_ip_1, metadata=True) + + # Reset configuration values to default values i.e., false + Configurations.update(self.api_client, + name="global.allow.expose.host.hostname", + value="false" + ) + + # Update Account level setting + Configurations.update(self.api_client, + name="account.allow.expose.host.hostname", + value="false" + ) + + self.delete(vm1, expunge=True) + self.delete(network1) + + @attr(tags=["advanced", "vpc"], required_hardware="true") + def test_configdrive_vpc_network_verify_metadata(self): + """Test Configdrive for VPC Networks + choose user data with configDrive as service provider + and test vmdata functionality using ConfigDrive + """ + + # 1. Given ConfigDrive provider is enabled in zone + # And a network offering for VPC which has + # user data provided by ConfigDrive + # And a VPC + # When I create an VPC Tier in the VPC using that network offering + # Then the network is successfully created, + # And is in the "Allocated" state. + + # 2. When I deploy a VM in the created VPC tier with user data, + # Then the network state is changed to "Implemented" + # And the VM is successfully deployed and is in the "Running" state + + # 3. And the user data in the ConfigDrive device is as expected + # 4. Verify various scenarios and check the data in configdriveIso + # 5. Delete all the created objects (cleanup). + + self.debug("+++ Preparation Scenario: " + "Create a tier with config drive " + "when config drive provider is enabled.") + + self.given_a_vpc() + self.given_config_drive_provider_is("Enabled") + self.given_a_network_offering_for_vpc_with_configdrive() + create_network1 = self.when_I_create_a_vpc_tier_with_that_offering( + gateway='10.1.1.1') + self.then_the_network_is_successfully_created(create_network1) + self.then_the_network_has(create_network1, state="Implemented") + + network1 = create_network1.network + + # Update global setting for "allow.expose.host.hostname" + Configurations.update(self.api_client, + name="global.allow.expose.host.hostname", + value="true" + ) + + # Update Account level setting + Configurations.update(self.api_client, + name="account.allow.expose.host.hostname", + value="true" + ) + + # Verify that the above mentioned settings are set to true before proceeding + if not is_config_suitable( + apiclient=self.api_client, + name='global.allow.expose.host.hostname', + value='true'): + self.skipTest('global.allow.expose.host.hostname should be true. skipping') + + if not is_config_suitable( + apiclient=self.api_client, + name='account.allow.expose.host.hostname', + value='true'): + self.skipTest('Account level setting account.allow.expose.host.hostname should be true. skipping') + + # ===================================================================== + self.debug("+++ Scenario: " + "Deploy VM in the Tier 1 with user data") + vm = self.when_I_deploy_a_vm(network1) + public_ip_1 = self.when_I_create_a_static_nat_ip_to(vm, network1) + + self.then_config_drive_is_as_expected(vm, public_ip_1, metadata=True) + + # ===================================================================== + # Network restart tests + # ===================================================================== + + self.debug("+++ Scenario: " + "verify config drive after Restart VPC with cleanup...") + self.when_I_restart_the_vpc_with(cleanup=True) + self.then_config_drive_is_as_expected(vm, public_ip_1, + metadata=True, reconnect=False) + + # ===================================================================== + self.debug("+++ Scenario: " + "verify config drive after Restart VPC without cleanup...") + self.when_I_restart_the_network_with(network1, cleanup=False) + self.then_config_drive_is_as_expected(vm, public_ip_1, + metadata=True, reconnect=False) + + # ===================================================================== + self.debug("+++ Scenario: " + "verify config drive after restart tier with cleanup...") + self.when_I_restart_the_network_with(network1, cleanup=True) + self.then_config_drive_is_as_expected(vm, public_ip_1, + metadata=True, reconnect=False) + + # ===================================================================== + self.debug("+++ Scenario: " + "validate vm metadata after reboot") + vm.reboot(self.api_client) + self.then_config_drive_is_as_expected(vm, public_ip_1, metadata=True) + + # ===================================================================== + self.debug("+++ Scenario: " + "validate updated userdata after migrate") + host = self.migrate_VM(vm) + vm.hostname = host.name + self.then_config_drive_is_as_expected(vm, public_ip_1, metadata=True) + + # Reset configuration values to default values i.e., false + Configurations.update(self.api_client, + name="global.allow.expose.host.hostname", + value="false" + ) + + # Update Account level setting + Configurations.update(self.api_client, + name="account.allow.expose.host.hostname", + value="false" + ) + + self.delete(vm, expunge=True) + self.delete(network1) diff --git a/test/integration/component/test_vr_metadata.py b/test/integration/component/test_vr_metadata.py new file mode 100644 index 00000000000..c566510379d --- /dev/null +++ b/test/integration/component/test_vr_metadata.py @@ -0,0 +1,337 @@ +# 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. + + +# this script will cover VMdeployment with Userdata tests + +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.base import * +from marvin.lib.utils import (validateList, cleanup_resources) +from marvin.lib.common import * +from nose.plugins.attrib import attr +from marvin.codes import PASS,FAIL + +_multiprocess_shared_ = True + +class Services: + def __init__(self): + self.services = { + "virtual_machine": { + "displayname": "TesVM1", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostype": 'CentOS 5.5 (64-bit)', + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 256, + }, + } + +class TestDeployVmWithMetaData(cloudstackTestCase): + @classmethod + def setUpClass(cls): + cls.testclient = super(TestDeployVmWithMetaData, cls).getClsTestClient() + cls.apiclient = cls.testclient.getApiClient() + cls._cleanup = [] + #cls.services = Services().services + cls.services = cls.testclient.getParsedTestDataConfig() + cls.zone = get_zone(cls.apiclient, cls.testclient.getZoneForTests()) + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offering"] + ) + + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.services["ostype"] + ) + + + @classmethod + def tearDownClass(cls): + try: + cls._cleanup = cls._cleanup[::-1] + cleanup_resources(cls.apiclient, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.cleanup = [] + + def tearDown(self): + try: + self.cleanup = self.cleanup[::-1] + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def migrate_VM(self, vm): + """Migrates VM to another host, if available""" + self.debug("+++ Migrating one of the VMs in the created " + "VPC Tier network to another host, if available...") + self.debug("Checking if a host is available for migration...") + hosts = Host.listForMigration(self.apiclient, virtualmachineid=vm.id) + if hosts: + self.assertEqual(isinstance(hosts, list), True, + "List hosts should return a valid list" + ) + host = hosts[0] + self.debug("Migrating VM with ID: " + "%s to Host: %s" % (vm.id, host.id)) + try: + vm.migrate(self.apiclient, hostid=host.id) + except Exception as e: + self.fail("Failed to migrate instance, %s" % e) + self.debug("Migrated VM with ID: " + "%s to Host: %s" % (vm.id, host.id)) + else: + self.debug("No host available for migration. " + "Test requires at-least 2 hosts") + return host + + def list_nics(self, vm_id): + list_vm_res = VirtualMachine.list(self.apiclient, id=vm_id) + self.assertEqual(validateList(list_vm_res)[0], PASS, "List vms returned invalid response") + nics = list_vm_res[0].nic + for nic in nics: + if nic.type == "Shared": + nic_res = NIC.list( + self.apiclient, + virtualmachineid=vm_id, + nicid=nic.id + ) + nic_ip = nic_res[0].ipaddress + self.assertIsNotNone(nic_ip, "listNics API response does not have the ip address") + else: + continue + return + + @attr(tags=["advanced"], required_hardware='True') + def test_deployVM_verify_metadata_in_VR(self): + """ + 1. Create a network (VR as a provider) + 2. Deploy a VM in the network + 3. Verify VM deployment + 4. From the VM, curl the gateway of the VR to verify the corresponding metadata - hypervisor host name + if the respective Global level and account level flags are set to true + """ + # Update global setting for "global.allow.expose.host.hostname" + Configurations.update(self.apiclient, + name="global.allow.expose.host.hostname", + value="true" + ) + + # Update Account level setting + Configurations.update(self.apiclient, + name="account.allow.expose.host.hostname", + value="true" + ) + + # Verify that the above mentioned settings are set to true before proceeding + if not is_config_suitable( + apiclient=self.apiclient, + name='global.allow.expose.host.hostname', + value='true'): + self.skipTest('global.allow.expose.host.hostname should be true. skipping') + + if not is_config_suitable( + apiclient=self.apiclient, + name='account.allow.expose.host.hostname', + value='true'): + self.skipTest('account.allow.expose.host.hostname should be true. skipping') + + self.no_isolate = NetworkOffering.create( + self.apiclient, + self.services["isolated_network_offering"] + ) + self.no_isolate.update(self.apiclient, state='Enabled') + self.isolated_network = Network.create( + self.apiclient, + self.services["network"], + networkofferingid=self.no_isolate.id, + zoneid=self.zone.id, + accountid="admin", + domainid=1 + ) + self.cleanup.append(self.isolated_network) + + self.vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid="admin", + domainid=1, + serviceofferingid=self.service_offering.id, + zoneid=self.zone.id, + networkids=[self.isolated_network.id], + ) + self.assertIsNotNone( + self.vm, + "VM creation failed in the isolated network" + ) + self.cleanup.append(self.vm) + + ip_addr = self.vm.ipaddress + self.debug("VM ip address = %s" % ip_addr) + + # Verify the retrieved ip address in listNICs API response + self.list_nics(self.vm.id) + vr_res = Router.list( + self.apiclient, + networkid=self.isolated_network.id, + listAll=True + ) + self.assertEqual(validateList(vr_res)[0], PASS, "List Routers returned invalid response") + vr_ip = vr_res[0].guestipaddress + ssh = self.vm.get_ssh_client(ipaddress=ip_addr) + cmd = "curl http://%s/latest/hypervisor-host-name" % vr_ip + res = ssh.execute(cmd) + self.debug("Verifying hypervisor hostname details in the VR") + self.assertEqual( + str(res), + self.vm.hostname, + "Failed to get the hypervisor host name from VR in isolated network" + ) + # Reset configuration values to default values i.e., false + Configurations.update(self.apiclient, + name="global.allow.expose.host.hostname", + value="false" + ) + + # Update Account level setting + Configurations.update(self.apiclient, + name="account.allow.expose.host.hostname", + value="false" + ) + return + + @attr(tags=["advanced"], required_hardware='True') + def test_deployVM_verify_metadata_in_VR_after_migration(self): + """ + 1. Create a network (VR as a provider) + 2. Deploy a VM in the network + 3. Verify VM deployment + 4. Migrate VM to another host + 4. After migration, from the VM, curl the gateway to verify the corresponding metadata - hypervisor host name + if the respective Global level and account level flags are set to true + """ + # Update global setting for "global.allow.expose.host.hostname" + Configurations.update(self.apiclient, + name="global.allow.expose.host.hostname", + value="true" + ) + + # Update Account level setting + Configurations.update(self.apiclient, + name="account.allow.expose.host.hostname", + value="true" + ) + + # Verify that the above mentioned settings are set to true before proceeding + if not is_config_suitable( + apiclient=self.apiclient, + name='global.allow.expose.host.hostname', + value='true'): + self.skipTest('global.allow.expose.host.hostname should be true. skipping') + + if not is_config_suitable( + apiclient=self.apiclient, + name='account.allow.expose.host.hostname', + value='true'): + self.skipTest('Account level account.allow.expose.host.hostname should be true. skipping') + + self.no_isolate = NetworkOffering.create( + self.apiclient, + self.services["isolated_network_offering"] + ) + self.no_isolate.update(self.apiclient, state='Enabled') + self.isolated_network = Network.create( + self.apiclient, + self.services["network"], + networkofferingid=self.no_isolate.id, + zoneid=self.zone.id, + accountid="admin", + domainid=1 + ) + self.cleanup.append(self.isolated_network) + + self.vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid="admin", + domainid=1, + serviceofferingid=self.service_offering.id, + zoneid=self.zone.id, + networkids=[self.isolated_network.id], + ) + self.assertIsNotNone( + self.vm, + "VM creation failed in the isolated network" + ) + + host = self.migrate_VM(self.vm) + + self.cleanup.append(self.vm) + + ip_addr = self.vm.ipaddress + self.debug("VM ip address = %s" % ip_addr) + + # Verify the retrieved ip address in listNICs API response + self.list_nics(self.vm.id) + vr_res = Router.list( + self.apiclient, + networkid=self.isolated_network.id, + listAll=True + ) + self.assertEqual(validateList(vr_res)[0], PASS, "List Routers returned invalid response") + vr_ip = vr_res[0].guestipaddress + ssh = self.vm.get_ssh_client(ipaddress=ip_addr) + cmd = "curl http://%s/latest/hypervisor-host-name" % vr_ip + res = ssh.execute(cmd) + self.debug("Verifying hypervisor hostname details in the VR") + self.assertEqual( + str(res), + host.name, + "Failed to get the hypervisor host name from VR in isolated network" + ) + # Reset configuration values to default values i.e., false + Configurations.update(self.apiclient, + name="global.allow.expose.host.hostname", + value="false" + ) + + # Update Account level setting + Configurations.update(self.apiclient, + name="account.allow.expose.host.hostname", + value="false" + ) + + return +