mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
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.dsilva@shapeblue.com>
This commit is contained in:
parent
8c1d749360
commit
a73712ec4e
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
/**
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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))) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -88,6 +88,11 @@ public class BaremetalUserdataElement extends AdapterBase implements NetworkElem
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveHypervisorHostname(NicProfile profile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Service, Map<Capability, String>> getCapabilities() {
|
||||
return capabilities;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +55,7 @@ import com.cloud.vm.ReservationContext;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import com.cloud.vm.dao.DomainRouterDao;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
@ -147,7 +148,7 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem
|
||||
}
|
||||
|
||||
private VmDataCommand generateVmDataCommand(String vmPrivateIpAddress, String userData, String serviceOffering, String zoneName, String guestIpAddress,
|
||||
String vmName, String vmInstanceName, long vmId, String vmUuid, String publicKey) {
|
||||
String vmName, String vmInstanceName, long vmId, String vmUuid, String publicKey, String hostname) {
|
||||
VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress, vmName, _networkMgr.getExecuteInSeqNtwkElmtCmd());
|
||||
// if you add new metadata files, also edit systemvm/patches/debian/config/var/www/html/latest/.htaccess
|
||||
cmd.addVmData("userdata", "user-data", userData);
|
||||
@ -163,7 +164,7 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem
|
||||
setVmInstanceId(vmUuid, cmd);
|
||||
}
|
||||
cmd.addVmData("metadata", "public-keys", publicKey);
|
||||
|
||||
cmd.addVmData("metadata", "hypervisor-host-name", hostname);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@ -217,11 +218,11 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem
|
||||
}
|
||||
String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(uservm.getServiceOfferingId()).getDisplayText();
|
||||
String zoneName = _dcDao.findById(network.getDataCenterId()).getName();
|
||||
|
||||
String destHostname = VirtualMachineManager.getHypervisorHostname(dest.getHost().getName());
|
||||
cmds.addCommand(
|
||||
"vmdata",
|
||||
generateVmDataCommand(nic.getIPv4Address(), userData, serviceOffering, zoneName, nic.getIPv4Address(), uservm.getHostName(), uservm.getInstanceName(),
|
||||
uservm.getId(), uservm.getUuid(), sshPublicKey));
|
||||
uservm.getId(), uservm.getUuid(), sshPublicKey, destHostname));
|
||||
try {
|
||||
_agentManager.send(dest.getHost().getId(), cmds);
|
||||
} catch (OperationTimedoutException e) {
|
||||
@ -251,6 +252,11 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveHypervisorHostname(NicProfile profile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException {
|
||||
// TODO Auto-generated method stub
|
||||
@ -261,5 +267,4 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem
|
||||
public boolean verifyServicesCombination(Set<Service> services) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||
import org.apache.cloudstack.storage.configdrive.ConfigDrive;
|
||||
import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
@ -39,13 +40,16 @@ import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
|
||||
import com.cloud.agent.api.to.DiskTO;
|
||||
import com.cloud.configuration.ConfigurationManager;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.HostPodDao;
|
||||
import com.cloud.deploy.DeployDestination;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.exception.UnsupportedServiceException;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.HypervisorGuruManager;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.Network.Capability;
|
||||
import com.cloud.network.Network.Provider;
|
||||
@ -66,6 +70,7 @@ import com.cloud.storage.dao.GuestOSDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import com.cloud.utils.crypt.DBEncryptionUtil;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.StateListener;
|
||||
import com.cloud.utils.fsm.StateMachine2;
|
||||
@ -110,11 +115,23 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
@Inject
|
||||
HostDao _hostDao;
|
||||
@Inject
|
||||
HostPodDao _podDao;
|
||||
@Inject
|
||||
ClusterDao _clusterDao;
|
||||
@Inject
|
||||
AgentManager agentManager;
|
||||
@Inject
|
||||
DataStoreManager _dataStoreMgr;
|
||||
@Inject
|
||||
EndPointSelector _ep;
|
||||
@Inject
|
||||
EntityManager _entityMgr;
|
||||
@Inject
|
||||
ServiceOfferingDao _offeringDao;
|
||||
@Inject
|
||||
ImageStoreDao imgstore;
|
||||
@Inject
|
||||
private HypervisorGuruManager _hvGuruMgr;
|
||||
|
||||
private final static Integer CONFIGDRIVEDISKSEQ = 4;
|
||||
|
||||
@ -204,8 +221,8 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context)
|
||||
throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
|
||||
return (canHandle(network.getTrafficType())
|
||||
&& configureConfigDriveData(profile, nic))
|
||||
&& createConfigDriveIso(profile, dest);
|
||||
&& configureConfigDriveData(profile, nic, dest))
|
||||
&& createConfigDriveIso(profile, dest, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -244,6 +261,19 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
return canHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveHypervisorHostname(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException {
|
||||
if (vm.getVirtualMachine().getType() == VirtualMachine.Type.User) {
|
||||
try {
|
||||
recreateConfigDriveIso(nic, network, vm, dest);
|
||||
} catch (ResourceUnavailableException e) {
|
||||
LOG.error("Failed to add config disk drive due to: ", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveUserData(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException {
|
||||
// saveUserData is called by updateVirtualMachine API which requires VM to be shutdown
|
||||
@ -325,6 +355,32 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) {
|
||||
}
|
||||
|
||||
private void recreateConfigDriveIso(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException {
|
||||
if (nic.isDefaultNic() && _networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive)) {
|
||||
DiskTO diskToUse = null;
|
||||
for (DiskTO disk : vm.getDisks()) {
|
||||
if (disk.getType() == Volume.Type.ISO && disk.getPath() != null && disk.getPath().contains("configdrive")) {
|
||||
diskToUse = disk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final UserVmVO userVm = _userVmDao.findById(vm.getId());
|
||||
|
||||
if (userVm != null) {
|
||||
final boolean isWindows = isWindows(userVm.getGuestOSId());
|
||||
List<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());
|
||||
}
|
||||
|
||||
@ -31,7 +31,10 @@ import org.cloud.network.router.deployment.RouterDeploymentDefinitionBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.vm.VirtualMachineProfileImpl;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.dao.NicDao;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd;
|
||||
@ -117,7 +120,7 @@ import com.cloud.vm.dao.UserVmDao;
|
||||
|
||||
public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider,
|
||||
StaticNatServiceProvider, FirewallServiceProvider, LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer,
|
||||
NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServiceProvider {
|
||||
NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServiceProvider{
|
||||
private static final Logger s_logger = Logger.getLogger(VirtualRouterElement.class);
|
||||
public static final AutoScaleCounterType AutoScaleCounterCpu = new AutoScaleCounterType("cpu");
|
||||
public static final AutoScaleCounterType AutoScaleCounterMemory = new AutoScaleCounterType("memory");
|
||||
@ -163,6 +166,10 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
||||
DataCenterDao _dcDao;
|
||||
@Inject
|
||||
NetworkModel _networkModel;
|
||||
@Inject
|
||||
NicDao _nicDao;
|
||||
@Inject
|
||||
VMTemplateDao _templateDao;
|
||||
|
||||
@Inject
|
||||
NetworkTopologyContext networkTopologyContext;
|
||||
@ -765,6 +772,33 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveHypervisorHostname(NicProfile nicProfile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException {
|
||||
if (_networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.VirtualRouter) && vm.getVirtualMachine().getType() == VirtualMachine.Type.User) {
|
||||
VirtualMachine uvm = vm.getVirtualMachine();
|
||||
UserVmVO destVm = _userVmDao.findById(uvm.getId());
|
||||
VirtualMachineProfile profile = null;
|
||||
|
||||
if (destVm != null) {
|
||||
destVm.setHostId(dest.getHost().getId());
|
||||
_userVmDao.update(uvm.getId(), destVm);
|
||||
profile = new VirtualMachineProfileImpl(destVm);
|
||||
profile.setDisks(vm.getDisks());
|
||||
profile.setNics(vm.getNics());
|
||||
profile.setVmData(vm.getVmData());
|
||||
} else {
|
||||
profile = vm;
|
||||
}
|
||||
|
||||
updateUserVmData(nicProfile, network, profile);
|
||||
if (destVm != null) {
|
||||
destVm.setHostId(uvm.getHostId());
|
||||
_userVmDao.update(uvm.getId(), destVm);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveUserData(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException {
|
||||
if (!canHandle(network, null)) {
|
||||
@ -1066,6 +1100,8 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
||||
}
|
||||
|
||||
final VirtualMachineProfile uservm = vm;
|
||||
List<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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -940,6 +940,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHypervisorHostname(VirtualMachineProfile vm, DeployDestination dest, boolean migrationSuccessful) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareNicForMigration(VirtualMachineProfile vm, DeployDestination dest) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
337
test/integration/component/test_vr_metadata.py
Normal file
337
test/integration/component/test_vr_metadata.py
Normal file
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user