Skip to content

Commit 1f41a16

Browse files
committed
CLVM: Prepare migration on source host
1 parent e6dccd4 commit 1f41a16

File tree

3 files changed

+125
-36
lines changed

3 files changed

+125
-36
lines changed

core/src/main/java/com/cloud/agent/api/PrepareForMigrationCommand.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,27 @@ public class PrepareForMigrationCommand extends Command {
2525
private VirtualMachineTO vm;
2626
private boolean rollback;
2727

28-
protected PrepareForMigrationCommand() {
29-
}
28+
/**
29+
* Indicates if this command is for the source host (true) or destination host (false, default).
30+
*/
31+
private boolean isSource = false; // default: destination
3032

33+
/**
34+
* Legacy constructor that defaults to destination host.
35+
* @param vm VirtualMachineTO
36+
*/
3137
public PrepareForMigrationCommand(VirtualMachineTO vm) {
38+
this(vm, false);
39+
}
40+
41+
/**
42+
* Create a PrepareForMigrationCommand for a VM, specifying if this is for the source host.
43+
* @param vm VirtualMachineTO
44+
* @param isSource true if source host, false if destination host
45+
*/
46+
public PrepareForMigrationCommand(VirtualMachineTO vm, boolean isSource) {
3247
this.vm = vm;
48+
this.isSource = isSource;
3349
}
3450

3551
public VirtualMachineTO getVirtualMachine() {
@@ -44,6 +60,20 @@ public boolean isRollback() {
4460
return rollback;
4561
}
4662

63+
/**
64+
* Returns true if this command is for the source host, false if destination.
65+
*/
66+
public boolean isSource() {
67+
return isSource;
68+
}
69+
70+
/**
71+
* Set whether this command is for the source host.
72+
*/
73+
public void setSource(boolean isSource) {
74+
this.isSource = isSource;
75+
}
76+
4777
@Override
4878
public boolean executeInSequence() {
4979
return true;

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3104,7 +3104,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy
31043104
updateOverCommitRatioForVmProfile(profile, dest.getHost().getClusterId());
31053105

31063106
final VirtualMachineTO to = toVmTO(profile);
3107-
final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to);
3107+
final PrepareForMigrationCommand pfmcDest = new PrepareForMigrationCommand(to, false);
31083108
setVmNetworkDetails(vm, to);
31093109

31103110
ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
@@ -3113,19 +3113,42 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy
31133113
work.setResourceId(dstHostId);
31143114
work = _workDao.persist(work);
31153115

3116-
Answer pfma = null;
3116+
// For KVM, we need to prepare both source and destination before migration because of the way KVM handles storage during migration.
3117+
// For other hypervisors, we only need to prepare the destination before migration.
3118+
if (HypervisorType.KVM.equals(vm.getHypervisorType())) {
3119+
final PrepareForMigrationCommand pfmcSource = new PrepareForMigrationCommand(to, true);
3120+
Answer pfmaSource = null;
3121+
try {
3122+
pfmaSource = _agentMgr.send(srcHostId, pfmcSource);
3123+
if (pfmaSource == null || !pfmaSource.getResult()) {
3124+
final String details = pfmaSource != null ? pfmaSource.getDetails() : "null answer returned";
3125+
pfmaSource = null;
3126+
throw new AgentUnavailableException(String.format("Unable to prepare source for migration due to [%s].", details), srcHostId);
3127+
}
3128+
} catch (final OperationTimedoutException e1) {
3129+
throw new AgentUnavailableException("Operation timed out", srcHostId);
3130+
} finally {
3131+
if (pfmaSource == null) {
3132+
_networkMgr.rollbackNicForMigration(vmSrc, profile);
3133+
volumeMgr.release(vm.getId(), dstHostId);
3134+
work.setStep(Step.Done);
3135+
_workDao.update(work.getId(), work);
3136+
}
3137+
}
3138+
}
3139+
3140+
Answer pfmaDest = null;
31173141
try {
3118-
pfma = _agentMgr.send(dstHostId, pfmc);
3119-
if (pfma == null || !pfma.getResult()) {
3120-
final String details = pfma != null ? pfma.getDetails() : "null answer returned";
3121-
final String msg = "Unable to prepare for migration due to " + details;
3122-
pfma = null;
3123-
throw new AgentUnavailableException(msg, dstHostId);
3142+
pfmaDest = _agentMgr.send(dstHostId, pfmcDest);
3143+
if (pfmaDest == null || !pfmaDest.getResult()) {
3144+
final String details = pfmaDest != null ? pfmaDest.getDetails() : "null answer returned";
3145+
pfmaDest = null;
3146+
throw new AgentUnavailableException("Unable to prepare destination for migration due to " + details, dstHostId);
31243147
}
31253148
} catch (final OperationTimedoutException e1) {
31263149
throw new AgentUnavailableException("Operation timed out", dstHostId);
31273150
} finally {
3128-
if (pfma == null) {
3151+
if (pfmaDest == null) {
31293152
_networkMgr.rollbackNicForMigration(vmSrc, profile);
31303153
volumeMgr.release(vm.getId(), dstHostId);
31313154
work.setStep(Step.Done);
@@ -3167,7 +3190,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy
31673190
boolean migrated = false;
31683191
Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
31693192
try {
3170-
final MigrateCommand mc = buildMigrateCommand(vm, to, dest, pfma, dpdkInterfaceMapping);
3193+
final MigrateCommand mc = buildMigrateCommand(vm, to, dest, pfmaDest, dpdkInterfaceMapping);
31713194

31723195
try {
31733196
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
@@ -4897,26 +4920,48 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI
48974920
volumeMgr.prepareForMigration(profile, dest);
48984921

48994922
final VirtualMachineTO to = toVmTO(profile);
4900-
final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to);
4923+
final PrepareForMigrationCommand pfmcDest = new PrepareForMigrationCommand(to, false);
49014924

49024925
ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
49034926
work.setStep(Step.Prepare);
49044927
work.setResourceType(ItWorkVO.ResourceType.Host);
49054928
work.setResourceId(dstHostId);
49064929
work = _workDao.persist(work);
49074930

4908-
Answer pfma = null;
4931+
// For KVM, we need to prepare both source and destination before migration because of the way KVM handles storage during migration.
4932+
// For other hypervisors, we only need to prepare the destination before migration.
4933+
if (HypervisorType.KVM.equals(vm.getHypervisorType())) {
4934+
final PrepareForMigrationCommand pfmcSource = new PrepareForMigrationCommand(to, true);
4935+
Answer pfmaSource = null;
4936+
try {
4937+
pfmaSource = _agentMgr.send(srcHostId, pfmcSource);
4938+
if (pfmaSource == null || !pfmaSource.getResult()) {
4939+
final String details = pfmaSource != null ? pfmaSource.getDetails() : "null answer returned";
4940+
pfmaSource = null;
4941+
throw new AgentUnavailableException(String.format("Unable to prepare source for migration due to [%s].", details), srcHostId);
4942+
}
4943+
} catch (final OperationTimedoutException e1) {
4944+
throw new AgentUnavailableException("Operation timed out", srcHostId);
4945+
} finally {
4946+
if (pfmaSource == null) {
4947+
work.setStep(Step.Done);
4948+
_workDao.update(work.getId(), work);
4949+
}
4950+
}
4951+
}
4952+
4953+
Answer pfmaDest = null;
49094954
try {
4910-
pfma = _agentMgr.send(dstHostId, pfmc);
4911-
if (pfma == null || !pfma.getResult()) {
4912-
final String details = pfma != null ? pfma.getDetails() : "null answer returned";
4913-
pfma = null;
4955+
pfmaDest = _agentMgr.send(dstHostId, pfmcDest);
4956+
if (pfmaDest == null || !pfmaDest.getResult()) {
4957+
final String details = pfmaDest != null ? pfmaDest.getDetails() : "null answer returned";
4958+
pfmaDest = null;
49144959
throw new AgentUnavailableException(String.format("Unable to prepare for migration to destination host [%s] due to [%s].", dest.getHost(), details), dstHostId);
49154960
}
49164961
} catch (final OperationTimedoutException e1) {
49174962
throw new AgentUnavailableException("Operation timed out", dstHostId);
49184963
} finally {
4919-
if (pfma == null) {
4964+
if (pfmaDest == null) {
49204965
work.setStep(Step.Done);
49214966
_workDao.update(work.getId(), work);
49224967
}
@@ -4937,7 +4982,7 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI
49374982

49384983
boolean migrated = false;
49394984
try {
4940-
final MigrateCommand mc = buildMigrateCommand(vm, to, dest, pfma, null);
4985+
final MigrateCommand mc = buildMigrateCommand(vm, to, dest, pfmaDest, null);
49414986

49424987
try {
49434988
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,13 @@ public Answer execute(final PrepareForMigrationCommand command, final LibvirtCom
6060
return handleRollback(command, libvirtComputingResource);
6161
}
6262

63+
if (command.isSource()) {
64+
logger.debug("Preparing source host for migration of VM {}", vm.getName());
65+
return handleMigrationHostPreparation(command, libvirtComputingResource, null, vm);
66+
}
67+
6368
if (logger.isDebugEnabled()) {
64-
logger.debug("Preparing host for migrating " + vm);
69+
logger.debug("Preparing destination host for migrating " + vm);
6570
}
6671

6772
final NicTO[] nics = vm.getNics();
@@ -123,21 +128,7 @@ public Answer execute(final PrepareForMigrationCommand command, final LibvirtCom
123128
return new PrepareForMigrationAnswer(command, "failed to connect physical disks to host");
124129
}
125130

126-
// Activate CLVM volumes in shared mode on destination host for live migration
127-
try {
128-
List<LibvirtVMDef.DiskDef> disks = libvirtComputingResource.getDisks(conn, vm.getName());
129-
LibvirtComputingResource.modifyClvmVolumesStateForMigration(
130-
disks,
131-
libvirtComputingResource,
132-
vm,
133-
LibvirtComputingResource.ClvmVolumeState.SHARED
134-
);
135-
} catch (Exception e) {
136-
logger.warn("Failed to activate CLVM volumes in shared mode on destination for VM {}: {}",
137-
vm.getName(), e.getMessage(), e);
138-
}
139-
140-
logger.info("Successfully prepared destination host for migration of VM {}", vm.getName());
131+
handleMigrationHostPreparation(command, libvirtComputingResource, conn, vm);
141132

142133
return createPrepareForMigrationAnswer(command, dpdkInterfaceMapping, libvirtComputingResource, vm);
143134
} catch (final LibvirtException | CloudRuntimeException | InternalErrorException | URISyntaxException e) {
@@ -180,4 +171,27 @@ private Answer handleRollback(PrepareForMigrationCommand command, LibvirtComputi
180171

181172
return new PrepareForMigrationAnswer(command);
182173
}
174+
175+
private Answer handleMigrationHostPreparation(PrepareForMigrationCommand command, LibvirtComputingResource libvirtComputingResource,
176+
Connect conn, VirtualMachineTO vm) {
177+
try {
178+
if (conn == null) {
179+
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
180+
conn = libvirtUtilitiesHelper.getConnectionByVmName(vm.getName());
181+
}
182+
List<LibvirtVMDef.DiskDef> disks = libvirtComputingResource.getDisks(conn, vm.getName());
183+
LibvirtComputingResource.modifyClvmVolumesStateForMigration(
184+
disks,
185+
libvirtComputingResource,
186+
vm,
187+
LibvirtComputingResource.ClvmVolumeState.SHARED
188+
);
189+
} catch (Exception e) {
190+
logger.warn("Failed to activate CLVM volumes in shared mode on destination for VM {}: {}",
191+
vm.getName(), e.getMessage(), e);
192+
}
193+
194+
logger.info("Successfully prepared host for migration of VM {}", vm.getName());
195+
return new PrepareForMigrationAnswer(command);
196+
}
183197
}

0 commit comments

Comments
 (0)