Skip to content

Commit c6fbf56

Browse files
author
Gabriel Beims Bräscher
committed
Allow KVM VM live migration with ROOT volume on file
1 parent 17c164d commit c6fbf56

File tree

14 files changed

+330
-59
lines changed

14 files changed

+330
-59
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

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

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

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: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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+
import org.apache.commons.lang.StringUtils;
34+
import org.springframework.stereotype.Component;
35+
36+
import com.cloud.agent.api.Answer;
37+
import com.cloud.agent.api.MigrateCommand;
38+
import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
39+
import com.cloud.agent.api.storage.CreateAnswer;
40+
import com.cloud.agent.api.storage.CreateCommand;
41+
import com.cloud.agent.api.to.VirtualMachineTO;
42+
import com.cloud.host.Host;
43+
import com.cloud.hypervisor.Hypervisor.HypervisorType;
44+
import com.cloud.storage.DataStoreRole;
45+
import com.cloud.storage.DiskOfferingVO;
46+
import com.cloud.storage.Storage.StoragePoolType;
47+
import com.cloud.storage.VolumeVO;
48+
import com.cloud.utils.exception.CloudRuntimeException;
49+
import com.cloud.vm.DiskProfile;
50+
51+
/**
52+
* Extends {@link StorageSystemDataMotionStrategy}, allowing KVM hosts to migrate VMs with the ROOT volume on a non managed local storage pool.
53+
* As {@link StorageSystemDataMotionStrategy} is considering KVM, this implementation also migrates only from/to KVM hosts.
54+
*/
55+
@Component
56+
public class KvmNonManagedStorageDataMotionStrategy extends StorageSystemDataMotionStrategy {
57+
58+
@Inject
59+
private TemplateDataFactory tmplFactory;
60+
61+
/**
62+
* 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 HIGHEST strategy priority. otherwise returns CANT_HANDLE.
63+
* 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.
64+
*/
65+
@Override
66+
protected StrategyPriority internalCanHandle(Map<VolumeInfo, DataStore> volumeMap) {
67+
if (super.internalCanHandle(volumeMap) == StrategyPriority.CANT_HANDLE) {
68+
Set<VolumeInfo> volumeInfoSet = volumeMap.keySet();
69+
70+
for (VolumeInfo volumeInfo : volumeInfoSet) {
71+
StoragePoolVO storagePoolVO = _storagePoolDao.findById(volumeInfo.getPoolId());
72+
if (storagePoolVO.getPoolType() != StoragePoolType.Filesystem) {
73+
return StrategyPriority.CANT_HANDLE;
74+
}
75+
}
76+
return StrategyPriority.HYPERVISOR;
77+
}
78+
return StrategyPriority.CANT_HANDLE;
79+
}
80+
81+
/**
82+
* Configures a {@link MigrateDiskInfo} object configured for migrating a File System volume and calls rootImageProvisioning.
83+
*/
84+
@Override
85+
protected MigrateCommand.MigrateDiskInfo configureMigrateDiskInfo(VolumeInfo srcVolumeInfo, String destPath) {
86+
return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(), MigrateCommand.MigrateDiskInfo.DiskType.FILE, MigrateCommand.MigrateDiskInfo.DriverType.QCOW2,
87+
MigrateCommand.MigrateDiskInfo.Source.FILE, destPath);
88+
}
89+
90+
/**
91+
* Generates the volume path by appending the Volume UUID to the Libvirt destiny images path.</br>
92+
* Example: /var/lib/libvirt/images/f3d49ecc-870c-475a-89fa-fd0124420a9b
93+
*/
94+
@Override
95+
protected String generateDestPath(VirtualMachineTO vmTO, VolumeVO srcVolume, Host destHost, StoragePoolVO destStoragePool, VolumeInfo destVolumeInfo) {
96+
DiskOfferingVO diskOffering = _diskOfferingDao.findById(srcVolume.getDiskOfferingId());
97+
DiskProfile diskProfile = new DiskProfile(destVolumeInfo, diskOffering, HypervisorType.KVM);
98+
String templateUuid = getTemplateUuid(destVolumeInfo.getTemplateId());
99+
CreateCommand rootImageProvisioningCommand = new CreateCommand(diskProfile, templateUuid, destStoragePool, true);
100+
101+
Answer rootImageProvisioningAnswer = _agentMgr.easySend(destHost.getId(), rootImageProvisioningCommand);
102+
103+
if (rootImageProvisioningAnswer == null) {
104+
throw new CloudRuntimeException(String.format("Migration with storage of vm [%s] failed while provisioning root image", vmTO.getName()));
105+
}
106+
107+
if (!rootImageProvisioningAnswer.getResult()) {
108+
throw new CloudRuntimeException(String.format("Unable to modify target volume on the host [host id:%s, name:%s]", destHost.getId(), destHost.getName()));
109+
}
110+
111+
String libvirtDestImgsPath = StringUtils.EMPTY;
112+
if (rootImageProvisioningAnswer instanceof CreateAnswer) {
113+
libvirtDestImgsPath = ((CreateAnswer)rootImageProvisioningAnswer).getVolume().getName() + File.separator;
114+
}
115+
return libvirtDestImgsPath + destVolumeInfo.getUuid();
116+
}
117+
118+
/**
119+
* Returns the template UUID of the given {@link VolumeVO}.
120+
*/
121+
protected String getTemplateUuid(Long templateId) {
122+
if (templateId == null) {
123+
return null;
124+
}
125+
TemplateInfo templateImage = tmplFactory.getTemplate(templateId, DataStoreRole.Image);
126+
return templateImage.getUuid();
127+
}
128+
129+
/**
130+
* Sets the volume path as the volume UUID.
131+
*/
132+
@Override
133+
protected void setVolumePath(VolumeVO volume) {
134+
volume.setPath(volume.getUuid());
135+
}
136+
}

0 commit comments

Comments
 (0)