Skip to content

Commit bf20940

Browse files
GabrielBrascherrafaelweingartner
authored andcommitted
Allow KVM VM live migration with ROOT volume on file storage type (#2997)
* Allow KVM VM live migration with ROOT volume on file * Allow KVM VM live migration with ROOT volume on file - Add JUnit tests * Address reviewers and change some variable names to ease future implementation (developers can easily guess the name and use autocomplete)
1 parent ecd2b95 commit bf20940

File tree

17 files changed

+899
-104
lines changed

17 files changed

+899
-104
lines changed

api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class VirtualMachineTO {
2828
private long id;
2929
private String name;
3030
private BootloaderType bootloader;
31+
private VirtualMachine.State state;
3132
Type type;
3233
int cpus;
3334

@@ -147,6 +148,14 @@ public void setBootloader(BootloaderType bootloader) {
147148
this.bootloader = bootloader;
148149
}
149150

151+
public VirtualMachine.State getState() {
152+
return state;
153+
}
154+
155+
public void setState(VirtualMachine.State state) {
156+
this.state = state;
157+
}
158+
150159
public int getCpus() {
151160
return cpus;
152161
}

api/src/main/java/com/cloud/vm/DiskProfile.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public DiskProfile(Volume vol, DiskOffering offering, HypervisorType hyperType)
7272
offering.isCustomized(),
7373
null);
7474
this.hyperType = hyperType;
75+
this.provisioningType = offering.getProvisioningType();
7576
}
7677

7778
public DiskProfile(DiskProfile dp) {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
package com.cloud.agent.api;
2121

22+
import java.util.ArrayList;
2223
import java.util.HashMap;
24+
import java.util.List;
2325
import java.util.Map;
2426

2527
import com.cloud.agent.api.to.VirtualMachineTO;
@@ -33,6 +35,7 @@ public class MigrateCommand extends Command {
3335
private boolean isWindows;
3436
private VirtualMachineTO vmTO;
3537
private boolean executeInSequence = false;
38+
private List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
3639

3740
protected MigrateCommand() {
3841
}
@@ -90,6 +93,14 @@ public boolean executeInSequence() {
9093
return executeInSequence;
9194
}
9295

96+
public List<MigrateDiskInfo> getMigrateDiskInfoList() {
97+
return migrateDiskInfoList;
98+
}
99+
100+
public void setMigrateDiskInfoList(List<MigrateDiskInfo> migrateDiskInfoList) {
101+
this.migrateDiskInfoList = migrateDiskInfoList;
102+
}
103+
93104
public static class MigrateDiskInfo {
94105
public enum DiskType {
95106
FILE, BLOCK;
@@ -123,6 +134,7 @@ public String toString() {
123134
private final DriverType driverType;
124135
private final Source source;
125136
private final String sourceText;
137+
private boolean isSourceDiskOnStorageFileSystem;
126138

127139
public MigrateDiskInfo(final String serialNumber, final DiskType diskType, final DriverType driverType, final Source source, final String sourceText) {
128140
this.serialNumber = serialNumber;
@@ -151,5 +163,13 @@ public Source getSource() {
151163
public String getSourceText() {
152164
return sourceText;
153165
}
166+
167+
public boolean isSourceDiskOnStorageFileSystem() {
168+
return isSourceDiskOnStorageFileSystem;
169+
}
170+
171+
public void setSourceDiskOnStorageFileSystem(boolean isDiskOnFileSystemStorage) {
172+
this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage;
173+
}
154174
}
155175
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.cloudstack.storage.motion;
20+
21+
import java.io.File;
22+
import java.util.Map;
23+
import java.util.Set;
24+
25+
import javax.inject.Inject;
26+
27+
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
28+
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
29+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
30+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
31+
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
32+
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
33+
34+
import com.cloud.agent.api.Answer;
35+
import com.cloud.agent.api.MigrateCommand;
36+
import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
37+
import com.cloud.agent.api.storage.CreateAnswer;
38+
import com.cloud.agent.api.storage.CreateCommand;
39+
import com.cloud.agent.api.to.VirtualMachineTO;
40+
import com.cloud.host.Host;
41+
import com.cloud.hypervisor.Hypervisor.HypervisorType;
42+
import com.cloud.storage.DataStoreRole;
43+
import com.cloud.storage.DiskOfferingVO;
44+
import com.cloud.storage.Storage.StoragePoolType;
45+
import com.cloud.storage.VolumeVO;
46+
import com.cloud.utils.exception.CloudRuntimeException;
47+
import com.cloud.vm.DiskProfile;
48+
49+
/**
50+
* Extends {@link StorageSystemDataMotionStrategy}, allowing KVM hosts to migrate VMs with the ROOT volume on a non managed local storage pool.
51+
* As {@link StorageSystemDataMotionStrategy} is considering KVM, this implementation also migrates only from/to KVM hosts.
52+
*/
53+
public class KvmNonManagedStorageDataMotionStrategy extends StorageSystemDataMotionStrategy {
54+
55+
@Inject
56+
private TemplateDataFactory templateDataFactory;
57+
58+
/**
59+
* Uses the canHandle from the Super class {@link StorageSystemDataMotionStrategy}. If the storage pool is of file and the internalCanHandle from {@link StorageSystemDataMotionStrategy} CANT_HANDLE, returns the StrategyPriority.HYPERVISOR strategy priority. otherwise returns CANT_HANDLE.
60+
* Note that the super implementation (override) is called by {@link #canHandle(Map, Host, Host)} which ensures that {@link #internalCanHandle(Map)} will be executed only if the source host is KVM.
61+
*/
62+
@Override
63+
protected StrategyPriority internalCanHandle(Map<VolumeInfo, DataStore> volumeMap) {
64+
if (super.internalCanHandle(volumeMap) == StrategyPriority.CANT_HANDLE) {
65+
Set<VolumeInfo> volumeInfoSet = volumeMap.keySet();
66+
67+
for (VolumeInfo volumeInfo : volumeInfoSet) {
68+
StoragePoolVO storagePoolVO = _storagePoolDao.findById(volumeInfo.getPoolId());
69+
if (storagePoolVO.getPoolType() != StoragePoolType.Filesystem && storagePoolVO.getPoolType() != StoragePoolType.NetworkFilesystem) {
70+
return StrategyPriority.CANT_HANDLE;
71+
}
72+
}
73+
return StrategyPriority.HYPERVISOR;
74+
}
75+
return StrategyPriority.CANT_HANDLE;
76+
}
77+
78+
/**
79+
* Configures a {@link MigrateDiskInfo} object configured for migrating a File System volume and calls rootImageProvisioning.
80+
*/
81+
@Override
82+
protected MigrateCommand.MigrateDiskInfo configureMigrateDiskInfo(VolumeInfo srcVolumeInfo, String destPath) {
83+
return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(), MigrateCommand.MigrateDiskInfo.DiskType.FILE, MigrateCommand.MigrateDiskInfo.DriverType.QCOW2,
84+
MigrateCommand.MigrateDiskInfo.Source.FILE, destPath);
85+
}
86+
87+
/**
88+
* Generates the volume path by appending the Volume UUID to the Libvirt destiny images path.</br>
89+
* Example: /var/lib/libvirt/images/f3d49ecc-870c-475a-89fa-fd0124420a9b
90+
*/
91+
@Override
92+
protected String generateDestPath(VirtualMachineTO vmTO, VolumeVO srcVolume, Host destHost, StoragePoolVO destStoragePool, VolumeInfo destVolumeInfo) {
93+
DiskOfferingVO diskOffering = _diskOfferingDao.findById(srcVolume.getDiskOfferingId());
94+
DiskProfile diskProfile = new DiskProfile(destVolumeInfo, diskOffering, HypervisorType.KVM);
95+
String templateUuid = getTemplateUuid(destVolumeInfo.getTemplateId());
96+
CreateCommand rootImageProvisioningCommand = new CreateCommand(diskProfile, templateUuid, destStoragePool, true);
97+
98+
Answer rootImageProvisioningAnswer = _agentMgr.easySend(destHost.getId(), rootImageProvisioningCommand);
99+
100+
if (rootImageProvisioningAnswer == null) {
101+
throw new CloudRuntimeException(String.format("Migration with storage of vm [%s] failed while provisioning root image", vmTO.getName()));
102+
}
103+
104+
if (!rootImageProvisioningAnswer.getResult()) {
105+
throw new CloudRuntimeException(String.format("Unable to modify target volume on the host [host id:%s, name:%s]", destHost.getId(), destHost.getName()));
106+
}
107+
108+
String libvirtDestImgsPath = null;
109+
if (rootImageProvisioningAnswer instanceof CreateAnswer) {
110+
libvirtDestImgsPath = ((CreateAnswer)rootImageProvisioningAnswer).getVolume().getName();
111+
}
112+
// File.getAbsolutePath is used to keep the file separator as it should be and eliminate a verification to check if exists a file separator in the last character of libvirtDestImgsPath.
113+
return new File(libvirtDestImgsPath, destVolumeInfo.getUuid()).getAbsolutePath();
114+
}
115+
116+
/**
117+
* Returns the template UUID with the given id. If the template ID is null, it returns null.
118+
*/
119+
protected String getTemplateUuid(Long templateId) {
120+
if (templateId == null) {
121+
return null;
122+
}
123+
TemplateInfo templateImage = templateDataFactory.getTemplate(templateId, DataStoreRole.Image);
124+
return templateImage.getUuid();
125+
}
126+
127+
/**
128+
* Sets the volume path as the volume UUID.
129+
*/
130+
@Override
131+
protected void setVolumePath(VolumeVO volume) {
132+
volume.setPath(volume.getUuid());
133+
}
134+
135+
/**
136+
* Return true if the volume should be migrated. Currently only supports migrating volumes on storage pool of the type StoragePoolType.Filesystem.
137+
* This ensures that volumes on shared storage are not migrated and those on local storage pools are migrated.
138+
*/
139+
@Override
140+
protected boolean shouldMigrateVolume(StoragePoolVO sourceStoragePool, Host destHost, StoragePoolVO destStoragePool) {
141+
return sourceStoragePool.getPoolType() == StoragePoolType.Filesystem;
142+
}
143+
}

0 commit comments

Comments
 (0)