CLOUDSTACK-8324: Added migration changes for config drive

This commit is contained in:
Jayapal 2015-06-09 16:13:21 +05:30
parent 469d9ebed4
commit deb694c201
8 changed files with 337 additions and 12 deletions

View File

@ -0,0 +1,58 @@
//
// 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.
//
package com.cloud.agent.api;
import java.util.List;
public class AttachOrDettachConfigDriveCommand extends Command {
String vmName;
List<String[]> vmData;
String configDriveLabel;
boolean isAttach = false;
public AttachOrDettachConfigDriveCommand(String vmName, List<String[]> vmData, String label, boolean attach) {
this.vmName = vmName;
this.vmData = vmData;
this.configDriveLabel = label;
this.isAttach = attach;
}
@Override
public boolean executeInSequence() {
return false;
}
public String getVmName() {
return vmName;
}
public List<String[]> getVmData() {
return vmData;
}
public boolean isAttach() {
return isAttach;
}
public String getConfigDriveLabel() {
return configDriveLabel;
}
}

View File

@ -38,6 +38,7 @@ import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.agent.api.AttachOrDettachConfigDriveCommand;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@ -273,6 +274,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
protected VGPUTypesDao _vgpuTypesDao;
@Inject
protected EntityManager _entityMgr;
@Inject
protected GuestOSCategoryDao _guestOSCategoryDao;
@Inject
protected GuestOSDao _guestOSDao = null;
@Inject
protected UserVmDetailsDao _vmDetailsDao;
@Inject
ServiceOfferingDao _serviceOfferingDao = null;
@Inject
ConfigDepot _configDepot;
@ -315,6 +324,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
protected VmWorkJobDao _workJobDao;
@Inject
protected AsyncJobManager _jobMgr;
@Inject
protected UserVmService _userVmSrv;
VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this);
@ -352,6 +363,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
Integer.class, "vm.job.report.interval", "60",
"Interval to send application level pings to make sure the connection is still working", false);
static final ConfigKey<String> VmConfigDriveLabel = new ConfigKey<String>("Hidden", String.class, "vm.configdrive.label", "config",
"The default lable name for the config drive", false);
ScheduledExecutorService _executor = null;
protected long _nodeId;
@ -1916,6 +1930,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()), null, null);
_networkMgr.prepareNicForMigration(profile, dest);
volumeMgr.prepareForMigration(profile, dest);
profile.setConfigDriveLabel( _userVmSrv.VmConfigDriveLabel.value());
final VirtualMachineTO to = toVmTO(profile);
final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to);
@ -2215,6 +2230,48 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
boolean migrated = false;
try {
// config drive: Detach the config drive at source host
// After migration successful attach the config drive in destination host
// On migration failure VM will be stopped, So configIso will be deleted
Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
List<String[]> vmData = null;
if (defaultNic != null) {
UserVmVO userVm = _userVmDao.findById(vm.getId());
Map<String, String> details = _vmDetailsDao.listDetailsKeyPairs(vm.getId());
vm.setDetails(details);
Network network = _networkModel.getNetwork(defaultNic.getNetworkId());
if (_networkModel.isSharedNetworkWithoutServices(network.getId())) {
final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText();
final String zoneName = _dcDao.findById(vm.getDataCenterId()).getName();
boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
vmData = _networkModel.generateVmData(userVm.getUserData(), serviceOffering, zoneName, vm.getInstanceName(), vm.getId(),
(String) profile.getParameter(VirtualMachineProfile.Param.VmSshPubKey), (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
String vmName = vm.getInstanceName();
String configDriveIsoRootFolder = "/tmp";
String isoFile = configDriveIsoRootFolder + "/" + vmName + "/configDrive/" + vmName + ".iso";
profile.setVmData(vmData);
profile.setConfigDriveLabel(VmConfigDriveLabel.value());
profile.setConfigDriveIsoRootFolder(configDriveIsoRootFolder);
profile.setConfigDriveIsoFile(isoFile);
// At source host detach the config drive iso.
AttachOrDettachConfigDriveCommand dettachCommand = new AttachOrDettachConfigDriveCommand(vm.getInstanceName(), vmData, VmConfigDriveLabel.value(), false);
try {
_agentMgr.send(srcHost.getId(), dettachCommand);
s_logger.debug("Deleted config drive ISO for vm " + vm.getInstanceName() + " In host " + srcHost);
} catch (OperationTimedoutException e) {
s_logger.debug("TIme out occured while exeuting command AttachOrDettachConfigDrive " + e.getMessage());
}
}
}
// Migrate the vm and its volume.
volumeMgr.migrateVolumes(vm, to, srcHost, destHost, volumeToPoolMap);
@ -3614,7 +3671,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {ClusterDeltaSyncInterval, StartRetry, VmDestroyForcestop, VmOpCancelInterval, VmOpCleanupInterval, VmOpCleanupWait,
VmOpLockStateRetry,
VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval};
VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval, VmConfigDriveLabel};
}
public List<StoragePoolAllocator> getStoragePoolAllocators() {

View File

@ -258,7 +258,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
protected String _configDriveIsopath = "/opt/xensource/packages/configdrive_iso/";
protected String _configDriveSRName = "ConfigDriveISOs";
protected String _attachIsoDeviceNum = "3";
public String _attachIsoDeviceNum = "3";
protected XenServerUtilitiesHelper xenServerUtilitiesHelper = new XenServerUtilitiesHelper();
@ -3908,17 +3908,29 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return new ExecutionResult(true, null);
}
public void prepareISO(final Connection conn, final String vmName) throws XmlRpcException, XenAPIException {
public void prepareISO(final Connection conn, final String vmName, List<String[]> vmDataList, String configDriveLabel) throws XmlRpcException, XenAPIException {
final Set<VM> vms = VM.getByNameLabel(conn, vmName);
if (vms == null || vms.size() != 1) {
throw new CloudRuntimeException("There are " + (vms == null ? "0" : vms.size()) + " VMs named " + vmName);
}
final VM vm = vms.iterator().next();
if (vmDataList != null) {
// create SR
SR sr = createLocalIsoSR(conn, _configDriveSRName+getHost().getIp());
// 1. create vm data files
createVmdataFiles(vmName, vmDataList, configDriveLabel);
// 2. copy config drive iso to host
copyConfigDriveIsoToHost(conn, sr, vmName);
}
final Set<VBD> vbds = vm.getVBDs(conn);
for (final VBD vbd : vbds) {
final VBD.Record vbdr = vbd.getRecord(conn);
if (vbdr.type == Types.VbdType.CD && vbdr.empty == false) {
if (vbdr.type == Types.VbdType.CD && vbdr.empty == false && vbdr.userdevice.equals(_attachIsoDeviceNum)) {
final VDI vdi = vbdr.VDI;
final SR sr = vdi.getSR(conn);
final Set<PBD> pbds = sr.getPBDs(conn);
@ -5288,4 +5300,86 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
}
protected SR getSRByNameLabel(Connection conn, String name) throws BadServerResponse, XenAPIException, XmlRpcException {
Set<SR> srs = SR.getByNameLabel(conn, name);
SR ressr = null;
for (SR sr : srs) {
Set<PBD> pbds;
pbds = sr.getPBDs(conn);
for (PBD pbd : pbds) {
PBD.Record pbdr = pbd.getRecord(conn);
if (pbdr.host != null) {
ressr = sr;
break;
}
}
}
return ressr;
}
public boolean AttachConfigDriveToMigratedVm(Connection conn, String vmName, String ipAddr) {
// attach the config drive in destination host
try {
s_logger.debug("Attaching config drive iso device for the VM "+ vmName + " In host "+ ipAddr);
Set<VM> vms = VM.getByNameLabel(conn, vmName);
SR sr = getSRByNameLabel(conn, _configDriveSRName + ipAddr);
//Here you will find only two vdis with the <vmname>.iso.
//one is from source host and second from dest host
Set<VDI> vdis = VDI.getByNameLabel(conn, vmName + ".iso");
if (vdis.isEmpty()) {
s_logger.debug("Could not find config drive ISO: " + vmName);
return false;
}
VDI configdriveVdi = null;
for (VDI vdi : vdis) {
SR vdiSr = vdi.getSR(conn);
if (vdiSr.getUuid(conn).equals(sr.getUuid(conn))) {
//get this vdi to attach to vbd
configdriveVdi = vdi;
s_logger.debug("VDI for the config drive ISO " + vdi);
} else {
// delete the vdi in source host so that the <vmname>.iso file is get removed
s_logger.debug("Removing the source host VDI for the config drive ISO " + vdi);
vdi.destroy(conn);
}
}
if (configdriveVdi == null) {
s_logger.debug("Config drive ISO VDI is not found ");
return false;
}
for (VM vm : vms) {
//create vbd
VBD.Record cfgDriveVbdr = new VBD.Record();
cfgDriveVbdr.VM = vm;
cfgDriveVbdr.empty = true;
cfgDriveVbdr.bootable = false;
cfgDriveVbdr.userdevice = "autodetect";
cfgDriveVbdr.mode = Types.VbdMode.RO;
cfgDriveVbdr.type = Types.VbdType.CD;
VBD cfgDriveVBD = VBD.create(conn, cfgDriveVbdr);
s_logger.debug("Inserting vbd " + configdriveVdi);
cfgDriveVBD.insert(conn, configdriveVdi);
break;
}
return true;
}catch (Exception ex) {
s_logger.debug("Failed to attach config drive ISO to the VM "+ vmName + " In host " + ipAddr );
return false;
}
}
}

View File

@ -67,7 +67,7 @@ public final class XenServer610MigrateWithStorageCommandWrapper extends CommandW
final XsHost xsHost = xenServer610Resource.getHost();
final String uuid = xsHost.getUuid();
try {
xenServer610Resource.prepareISO(connection, vmName);
xenServer610Resource.prepareISO(connection, vmName, null, null);
// Get the list of networks and recreate VLAN, if required.
for (final NicTO nicTo : vmSpec.getNics()) {

View File

@ -0,0 +1,95 @@
//
// 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.
//
package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase;
import java.util.List;
import java.util.Set;
import com.cloud.agent.api.AttachOrDettachConfigDriveCommand;
import com.cloud.resource.ResourceWrapper;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.VBD;
import com.xensource.xenapi.VDI;
import com.xensource.xenapi.VM;
import com.xensource.xenapi.Types;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
import com.cloud.resource.CommandWrapper;
import org.apache.xmlrpc.XmlRpcException;
@ResourceWrapper(handles = AttachOrDettachConfigDriveCommand.class)
public final class CitrixAttachOrDettachConfigDriveCommandWrapper extends CommandWrapper<AttachOrDettachConfigDriveCommand, Answer, CitrixResourceBase> {
private static final Logger s_logger = Logger.getLogger(CitrixAttachOrDettachConfigDriveCommandWrapper.class);
@Override
public Answer execute(final AttachOrDettachConfigDriveCommand command, final CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
String vmName = command.getVmName();
List<String[]> vmData = command.getVmData();
String label = command.getConfigDriveLabel();
Boolean isAttach = command.isAttach();
try {
Set<VM> vms = VM.getByNameLabel(conn, vmName);
for (VM vm : vms) {
if (isAttach) {
if (!citrixResourceBase.createAndAttachConfigDriveIsoForVM(conn, vm, vmData, label)) {
s_logger.debug("Failed to attach config drive iso to VM " + vmName);
}
} else {
// delete the config drive iso attached to VM
Set<VDI> vdis = VDI.getByNameLabel(conn, vmName+".iso");
if (vdis != null && !vdis.isEmpty()) {
s_logger.debug("Deleting config drive for the VM " + vmName);
VDI vdi = vdis.iterator().next();
// Find the VM's CD-ROM VBD
Set<VBD> vbds = vdi.getVBDs(conn);
for (VBD vbd : vbds) {
VBD.Record vbdRec = vbd.getRecord(conn);
if (vbdRec.type.equals(Types.VbdType.CD) && !vbdRec.empty && !vbdRec.userdevice.equals(citrixResourceBase._attachIsoDeviceNum)) {
if (vbdRec.currentlyAttached) {
vbd.eject(conn);
}
vbd.destroy(conn);
}
}
vdi.destroy(conn);
}
s_logger.debug("Successfully dettached config drive iso from the VM " + vmName);
}
}
}catch (Types.XenAPIException ex) {
s_logger.debug("Failed to attach config drive iso to VM " + vmName + " " + ex.getMessage() );
}catch (XmlRpcException ex) {
s_logger.debug("Failed to attach config drive iso to VM " + vmName + " "+ex.getMessage());
}
return new Answer(command, true, "success");
}
}

View File

@ -44,24 +44,23 @@ public final class CitrixMigrateCommandWrapper extends CommandWrapper<MigrateCom
public Answer execute(final MigrateCommand command, final CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
final String vmName = command.getVmName();
final String dstHostIpAddr = command.getDestinationIp();
try {
final Set<VM> vms = VM.getByNameLabel(conn, vmName);
final String ipaddr = command.getDestinationIp();
final Set<Host> hosts = Host.getAll(conn);
Host dsthost = null;
if(hosts != null) {
for (final Host host : hosts) {
if (host.getAddress(conn).equals(ipaddr)) {
if (host.getAddress(conn).equals(dstHostIpAddr)) {
dsthost = host;
break;
}
}
}
if (dsthost == null) {
final String msg = "Migration failed due to unable to find host " + ipaddr + " in XenServer pool " + citrixResourceBase.getHost().getPool();
final String msg = "Migration failed due to unable to find host " + dstHostIpAddr + " in XenServer pool " + citrixResourceBase.getHost().getPool();
s_logger.warn(msg);
return new MigrateAnswer(command, false, msg, null);
}
@ -71,12 +70,25 @@ public final class CitrixMigrateCommandWrapper extends CommandWrapper<MigrateCom
final VBD.Record vbdRec = vbd.getRecord(conn);
if (vbdRec.type.equals(Types.VbdType.CD) && !vbdRec.empty) {
vbd.eject(conn);
break;
// for config drive vbd destroy the vbd.
if (!vbdRec.userdevice.equals(citrixResourceBase._attachIsoDeviceNum)) {
if (vbdRec.currentlyAttached) {
vbd.destroy(conn);
}
}
continue;
}
}
citrixResourceBase.migrateVM(conn, dsthost, vm, vmName);
vm.setAffinity(conn, dsthost);
}
// The iso can be attached to vm only once the vm is (present in the host) migrated.
// Attach the config drive iso device to VM
if (!citrixResourceBase.AttachConfigDriveToMigratedVm(conn, vmName, dstHostIpAddr)) {
s_logger.debug("Config drive ISO attach failed after migration for vm "+vmName);
}
return new MigrateAnswer(command, true, "migration succeeded", null);
} catch (final Exception e) {
s_logger.warn(e.getMessage(), e);

View File

@ -31,6 +31,8 @@ import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.xensource.xenapi.Connection;
import java.util.List;
@ResourceWrapper(handles = PrepareForMigrationCommand.class)
public final class CitrixPrepareForMigrationCommandWrapper extends CommandWrapper<PrepareForMigrationCommand, Answer, CitrixResourceBase> {
@ -41,13 +43,20 @@ public final class CitrixPrepareForMigrationCommandWrapper extends CommandWrappe
final Connection conn = citrixResourceBase.getConnection();
final VirtualMachineTO vm = command.getVirtualMachine();
List<String[]> vmDataList = vm.getVmData();
String configDriveLabel = vm.getConfigDriveLabel();
if (configDriveLabel == null) {
configDriveLabel = "config";
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Preparing host for migrating " + vm);
}
final NicTO[] nics = vm.getNics();
try {
citrixResourceBase.prepareISO(conn, vm.getName());
citrixResourceBase.prepareISO(conn, vm.getName(), vmDataList, configDriveLabel);
for (final NicTO nic : nics) {
citrixResourceBase.getNetwork(conn, nic);

View File

@ -170,7 +170,7 @@ public class XenServer610WrapperTest {
verify(xenServer610Resource, times(1)).getConnection();
try {
verify(xenServer610Resource, times(1)).prepareISO(conn, vmName);
verify(xenServer610Resource, times(1)).prepareISO(conn, vmName, null, null);
verify(xenServer610Resource, times(1)).getNetwork(conn, nicTO1);
verify(xenServer610Resource, times(1)).getNetwork(conn, nicTO2);
verify(xenServer610Resource, times(1)).getNetwork(conn, nicTO3);