This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/master by this push:
     new a73712e  server: Enable sending hypervior host name via metadata - VR 
and Config Drive (#3976)
a73712e is described below

commit a73712ec4ea0deae58d0b43edcbe9ca5e8f33fd4
Author: Pearl Dsilva <pearl1...@gmail.com>
AuthorDate: Wed Jul 1 08:44:11 2020 +0530

    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 <pearl.dsi...@shapeblue.com>
---
 .../main/java/com/cloud/network/NetworkModel.java  |   3 +-
 .../network/element/UserDataServiceProvider.java   |  11 +
 .../java/com/cloud/vm/VirtualMachineProfile.java   |   4 +
 .../com/cloud/agent/api/routing/VmDataCommand.java |   1 -
 .../java/com/cloud/vm/VirtualMachineManager.java   |  14 +
 .../service/NetworkOrchestrationService.java       |  10 +
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |  22 +-
 .../engine/orchestration/NetworkOrchestrator.java  |  23 +-
 .../main/java/com/cloud/vm/dao/UserVmDaoImpl.java  |   6 +-
 .../networkservice/BaremetalUserdataElement.java   |   5 +
 .../wrapper/LibvirtMigrateCommandWrapper.java      | 132 ++++++--
 .../kvm/resource/LibvirtComputingResourceTest.java |   3 +-
 .../java/com/cloud/network/NetworkModelImpl.java   |   4 +-
 .../network/element/CloudZonesNetworkElement.java  |  15 +-
 .../network/element/ConfigDriveNetworkElement.java |  99 +++++-
 .../network/element/VirtualRouterElement.java      |  52 +++-
 .../cloud/network/router/CommandSetupHelper.java   |  29 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |   4 +-
 .../network/topology/BasicNetworkTopology.java     |   1 -
 .../com/cloud/network/MockNetworkModelImpl.java    |   2 +-
 .../element/ConfigDriveNetworkElementTest.java     |  15 +-
 .../java/com/cloud/vpc/MockNetworkManagerImpl.java |   5 +
 .../java/com/cloud/vpc/MockNetworkModelImpl.java   |   2 +-
 test/integration/component/test_configdrive.py     | 288 +++++++++++++++++-
 test/integration/component/test_vr_metadata.py     | 337 +++++++++++++++++++++
 25 files changed, 1005 insertions(+), 82 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java 
b/api/src/main/java/com/cloud/network/NetworkModel.java
index 002271a..e933a1c 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<String[]> 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 bf6d7e8..13f72a7 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 e6c196b..c17a716 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<String[]> vmData);
 
+    void setDisks(List<DiskTO> disks);
+
+    void setNics(List<NicProfile> 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 c74c7cf..dfcdcfd 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 ea2fa76..9165631 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<Boolean> ResoureCountRunningVMsonly = new 
ConfigKey<Boolean>("Advanced", Boolean.class, 
"resource.count.running.vms.only", "false",
             "Count the resources of only running VMs in resource limitation.", 
true);
 
+    ConfigKey<Boolean> AllowExposeHypervisorHostnameAccountLevel = new 
ConfigKey<Boolean>("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<Boolean> AllowExposeHypervisorHostname = new 
ConfigKey<Boolean>("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 6826602..78d4271 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<NetworkGuru> 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 e74e6ff..0ae249c 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<String[]> vmData = null;
 
         boolean migrated = false;
         try {
@@ -2916,7 +2917,6 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
 
             Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
 
-            List<String[]> vmData = null;
             if (defaultNic != null) {
                 UserVmVO userVm = _userVmDao.findById(vm.getId());
                 Map<String, String> 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<StoragePoolAllocator> 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 078cba2..e16bde6 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<NicVO> 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 6ed31a1..25479d6 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<UserVmVO, Long> implements Use
 
     @Override
     public void loadDetails(UserVmVO vm) {
-        Map<String, String> details = 
_detailsDao.listDetailsKeyPairs(vm.getId());
-        vm.setDetails(details);
+        if (vm != null ) {
+            Map<String, String> 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 b00d490..d2c9731 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
@@ -89,6 +89,11 @@ public class BaremetalUserdataElement extends AdapterBase 
implements NetworkElem
     }
 
     @Override
+    public boolean saveHypervisorHostname(NicProfile profile, Network network, 
VirtualMachineProfile vm, DeployDestination dest) throws 
ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
     public Map<Service, Map<Capability, String>> 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 f0eb287..0524be7 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<MigrateCo
             conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
             ifaces = libvirtComputingResource.getInterfaces(conn, vmName);
             disks = libvirtComputingResource.getDisks(conn, vmName);
+
+            VirtualMachineTO to = command.getVirtualMachine();
+
             dm = conn.domainLookupByName(vmName);
             /*
                 We replace the private IP address with the address of the 
destination host.
@@ -141,6 +146,12 @@ public final class LibvirtMigrateCommandWrapper extends 
CommandWrapper<MigrateCo
             xmlDesc = dm.getXMLDesc(xmlFlag);
             xmlDesc = replaceIpForVNCInDescFile(xmlDesc, target);
 
+            String oldIsoVolumePath = getOldVolumePath(disks, vmName);
+            String newIsoVolumePath = 
getNewVolumePathIfDatastoreHasChanged(libvirtComputingResource, conn, to);
+            if (newIsoVolumePath != null && 
!newIsoVolumePath.equals(oldIsoVolumePath)) {
+                s_logger.debug("Editing mount path");
+                xmlDesc = replaceDiskSourceFile(xmlDesc, newIsoVolumePath, 
vmName);
+            }
             // delete the metadata of vm snapshots before migration
             vmsnapshots = libvirtComputingResource.cleanVMSnapshotMetadata(dm);
 
@@ -226,30 +237,16 @@ public final class LibvirtMigrateCommandWrapper extends 
CommandWrapper<MigrateCo
             if (result.startsWith("unable to connect to server") && 
result.endsWith("refused")) {
                 result = String.format("Migration was refused connection to 
destination: %s. Please check libvirt configuration compatibility and firewall 
rules on the source and destination hosts.", destinationUri);
             }
-        } catch (final InterruptedException e) {
-            s_logger.debug("Interrupted while migrating domain: " + 
e.getMessage());
-            result = e.getMessage();
-        } catch (final ExecutionException e) {
-            s_logger.debug("Failed to execute while migrating domain: " + 
e.getMessage());
-            result = e.getMessage();
-        } catch (final TimeoutException e) {
-            s_logger.debug("Timed out while migrating domain: " + 
e.getMessage());
-            result = e.getMessage();
-        } catch (final IOException e) {
-            s_logger.debug("IOException: " + e.getMessage());
-            result = e.getMessage();
-        } catch (final ParserConfigurationException e) {
-            s_logger.debug("ParserConfigurationException: " + e.getMessage());
-            result = e.getMessage();
-        } catch (final SAXException e) {
-            s_logger.debug("SAXException: " + e.getMessage());
-            result = e.getMessage();
-        } catch (final TransformerConfigurationException e) {
-            s_logger.debug("TransformerConfigurationException: " + 
e.getMessage());
-            result = e.getMessage();
-        } catch (final TransformerException e) {
-            s_logger.debug("TransformerException: " + e.getMessage());
-            result = e.getMessage();
+        } catch (final InterruptedException
+            | ExecutionException
+            | TimeoutException
+            | IOException
+            | ParserConfigurationException
+            | SAXException
+            | TransformerException
+            | URISyntaxException e) {
+            s_logger.debug(String.format("%s : %s", 
e.getClass().getSimpleName(), e.getMessage()));
+            result = "Exception during migrate: " + e.getMessage();
         } finally {
             try {
                 if (dm != null && result != null) {
@@ -507,6 +504,33 @@ public final class LibvirtMigrateCommandWrapper extends 
CommandWrapper<MigrateCo
         return getXml(doc);
     }
 
+    private  String getOldVolumePath(List<DiskDef> 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<String> paths, String sourceText) 
{
         if (paths != null && !StringUtils.isBlank(sourceText)) {
             for (String path : paths) {
@@ -572,4 +596,62 @@ public final class LibvirtMigrateCommandWrapper extends 
CommandWrapper<MigrateCo
 
         return byteArrayOutputStream.toString();
     }
+
+    private String replaceDiskSourceFile(String xmlDesc, String isoPath, 
String vmName) throws IOException, SAXException, ParserConfigurationException, 
TransformerException {
+        InputStream in = IOUtils.toInputStream(xmlDesc);
+
+        DocumentBuilderFactory docFactory = 
DocumentBuilderFactory.newInstance();
+        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+        Document doc = docBuilder.parse(in);
+
+        // Get the root element
+        Node domainNode = doc.getFirstChild();
+
+        NodeList domainChildNodes = domainNode.getChildNodes();
+
+        for (int i = 0; i < domainChildNodes.getLength(); i++) {
+            Node domainChildNode = domainChildNodes.item(i);
+
+            if ("devices".equals(domainChildNode.getNodeName())) {
+                NodeList devicesChildNodes = domainChildNode.getChildNodes();
+                if (findDiskNode(doc, devicesChildNodes, vmName, isoPath)) {
+                    break;
+                }
+            }
+        }
+        return getXml(doc);
+    }
+
+    private boolean findDiskNode(Document doc, NodeList devicesChildNodes, 
String vmName, String isoPath) {
+        for (int x = 0; x < devicesChildNodes.getLength(); x++) {
+            Node deviceChildNode = devicesChildNodes.item(x);
+            if ("disk".equals(deviceChildNode.getNodeName())) {
+                Node diskNode = deviceChildNode;
+                if (findSourceNode(doc, diskNode, vmName, isoPath)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean findSourceNode(Document doc, Node diskNode, String vmName, 
String isoPath) {
+        NodeList diskChildNodes = diskNode.getChildNodes();
+        for (int z = 0; z < diskChildNodes.getLength(); z++) {
+            Node diskChildNode = diskChildNodes.item(z);
+            if ("source".equals(diskChildNode.getNodeName())) {
+                Node sourceNode = diskChildNode;
+                NamedNodeMap sourceNodeAttributes = sourceNode.getAttributes();
+                Node sourceNodeAttribute = 
sourceNodeAttributes.getNamedItem("file");
+                if ( sourceNodeAttribute.getNodeValue().contains(vmName)) {
+                    diskNode.removeChild(diskChildNode);
+                    Element newChildSourceNode = doc.createElement("source");
+                    newChildSourceNode.setAttribute("file", isoPath);
+                    diskNode.appendChild(newChildSourceNode);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 }
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index 1bf27d0..aa5f6ea 100644
--- 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -1289,10 +1289,11 @@ public class LibvirtComputingResourceTest {
         final boolean isWindows = false;
         final VirtualMachineTO vmTO = Mockito.mock(VirtualMachineTO.class);
         final boolean executeInSequence = false;
-
+        DiskTO[] diskTOS = new DiskTO[]{};
         final MigrateCommand command = new MigrateCommand(vmName, destIp, 
isWindows, vmTO, executeInSequence );
 
         
when(libvirtComputingResource.getLibvirtUtilitiesHelper()).thenReturn(libvirtUtilitiesHelper);
+        when(vmTO.getDisks()).thenReturn(diskTOS);
         try {
             
when(libvirtUtilitiesHelper.getConnectionByVmName(vmName)).thenReturn(conn);
             when(libvirtUtilitiesHelper.retrieveQemuConnection("qemu+tcp://" + 
command.getDestinationIp() + "/system")).thenReturn(dconn);
diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java 
b/server/src/main/java/com/cloud/network/NetworkModelImpl.java
index 031b843..aeba07c 100644
--- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java
@@ -2389,7 +2389,7 @@ public class NetworkModelImpl extends ManagerBase 
implements NetworkModel, Confi
     @Override
     public List<String[]> 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 cb3fac8..3ed5fcc 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) {
@@ -252,6 +253,11 @@ public class CloudZonesNetworkElement extends AdapterBase 
implements NetworkElem
     }
 
     @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
         return false;
@@ -261,5 +267,4 @@ public class CloudZonesNetworkElement extends AdapterBase 
implements NetworkElem
     public boolean verifyServicesCombination(Set<Service> 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 5331222..4daeda6 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
@@ -245,6 +262,19 @@ public class ConfigDriveNetworkElement extends AdapterBase 
implements NetworkEle
     }
 
     @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
         // Upper layers should save userdata in db, we do not need to 
update/create config drive iso at this point
@@ -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<String[]> 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<DataStore> 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<String[]> 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 0b664a9..bbc6aa7 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;
@@ -766,6 +773,33 @@ NetworkMigrationResponder, AggregatedCommandExecutor, 
RedundantResource, DnsServ
     }
 
     @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)) {
             return false;
@@ -1066,6 +1100,8 @@ NetworkMigrationResponder, AggregatedCommandExecutor, 
RedundantResource, DnsServ
             }
 
             final VirtualMachineProfile uservm = vm;
+            List<java.lang.String[]> vmData = uservm.getVmData();
+            uservm.setVmData(vmData);
 
             final List<DomainRouterVO> 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 63e9d80..00da0d3 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<? extends VpnUser> 
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 4e62044..4ef4d1e 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<String[]> 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 a7fbe31..20b4535 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<RuleApplier>(userdataToRouterRules));
     }
 
diff --git a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java 
b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java
index a35cec5..6fd57fc 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<String[]> 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<String[]> 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 d4f96c9..5d206f4 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<VirtualMachineProfile.Param, Object> 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 b96b58e..48ff307 100644
--- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java
+++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java
@@ -941,6 +941,11 @@ public class MockNetworkManagerImpl extends ManagerBase 
implements NetworkOrches
     }
 
     @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 56670d2..a731f7c 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<String[]> 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<String[]> 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 c8ed337..38a9a7d 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 0000000..c566510
--- /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
+

Reply via email to