Skip to content

Commit f185688

Browse files
committed
Fix KVM incremental snapshot migration between secondary storages
1 parent 6dc259c commit f185688

File tree

12 files changed

+535
-32
lines changed

12 files changed

+535
-32
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
20+
package com.cloud.agent.api;
21+
22+
import com.cloud.utils.Pair;
23+
24+
import java.util.List;
25+
26+
public class MigrateBetweenSecondaryStoragesCommandAnswer extends Answer {
27+
28+
List<Pair<Long, String>> migratedResourcesIdAndCheckpointPath;
29+
30+
public MigrateBetweenSecondaryStoragesCommandAnswer() {
31+
}
32+
33+
public MigrateBetweenSecondaryStoragesCommandAnswer(MigrateSnapshotsBetweenSecondaryStoragesCommand cmd, boolean success, String result, List<Pair<Long, String>> migratedResourcesIdAndCheckpointPath) {
34+
super(cmd, success, result);
35+
this.migratedResourcesIdAndCheckpointPath = migratedResourcesIdAndCheckpointPath;
36+
}
37+
38+
public List<Pair<Long, String>> getMigratedResources() {
39+
return migratedResourcesIdAndCheckpointPath;
40+
}
41+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
//
18+
19+
package com.cloud.agent.api;
20+
21+
import com.cloud.agent.api.to.DataStoreTO;
22+
import com.cloud.agent.api.to.DataTO;
23+
24+
import java.util.List;
25+
import java.util.Set;
26+
27+
public class MigrateSnapshotsBetweenSecondaryStoragesCommand extends Command {
28+
29+
DataStoreTO srcDataStore;
30+
DataStoreTO destDataStore;
31+
List<DataTO> snapshotChain;
32+
Set<Long> snapshotsIdToMigrate;
33+
34+
public MigrateSnapshotsBetweenSecondaryStoragesCommand() {
35+
}
36+
37+
public MigrateSnapshotsBetweenSecondaryStoragesCommand(List<DataTO> snapshotChain, DataStoreTO srcDataStore, DataStoreTO destDataStore, Set<Long> snapshotsIdToMigrate) {
38+
this.srcDataStore = srcDataStore;
39+
this.destDataStore = destDataStore;
40+
this.snapshotChain = snapshotChain;
41+
this.snapshotsIdToMigrate = snapshotsIdToMigrate;
42+
}
43+
44+
@Override
45+
public boolean executeInSequence() {
46+
return false;
47+
}
48+
49+
public List<DataTO> getSnapshotChain() {
50+
return snapshotChain;
51+
}
52+
53+
public Set<Long> getSnapshotsIdToMigrate() {
54+
return snapshotsIdToMigrate;
55+
}
56+
57+
public DataStoreTO getSrcDataStore() {
58+
return srcDataStore;
59+
}
60+
61+
public DataStoreTO getDestDataStore() {
62+
return destDataStore;
63+
}
64+
}

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public interface SnapshotInfo extends DataObject, Snapshot {
3030

3131
SnapshotInfo getParent();
3232

33+
List<SnapshotInfo> getParents();
34+
3335
String getPath();
3436

3537
DataStore getImageStore();
@@ -40,6 +42,8 @@ public interface SnapshotInfo extends DataObject, Snapshot {
4042

4143
List<SnapshotInfo> getChildren();
4244

45+
List<SnapshotInfo> getChildAndGrandchildren();
46+
4347
VolumeInfo getBaseVolume();
4448

4549
void addPayload(Object data);

engine/components-api/src/main/java/com/cloud/storage/StorageManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ public interface StorageManager extends StorageService {
228228
ConfigKey.Scope.Global,
229229
null);
230230

231+
ConfigKey<Integer> AgentMaxDataMigrationWaitTime = new ConfigKey<>("Advanced", Integer.class, "agent.max.data.migration.wait.time", "3600",
232+
"The maximum time (in seconds) that the secondary storage data migration command sent to the KVM Agent will be executed before a timeout occurs.", true, ConfigKey.Scope.Cluster);
233+
231234
/**
232235
* should we execute in sequence not involving any storages?
233236
* @return true if commands should execute in sequence

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
import java.util.Collections;
2323
import java.util.Comparator;
2424
import java.util.HashMap;
25+
import java.util.HashSet;
2526
import java.util.LinkedHashMap;
2627
import java.util.LinkedList;
2728
import java.util.List;
2829
import java.util.Map;
30+
import java.util.Set;
31+
import java.util.stream.Collectors;
2932

3033
import javax.inject.Inject;
3134

@@ -92,7 +95,7 @@ public class DataMigrationUtility {
9295
* "Ready" "Allocated", "Destroying", "Destroyed", "Failed". If this is the case, and if the migration policy is complete,
9396
* the migration is terminated.
9497
*/
95-
public boolean filesReadyToMigrate(Long srcDataStoreId, List<TemplateDataStoreVO> templates, List<SnapshotDataStoreVO> snapshots, List<VolumeDataStoreVO> volumes) {
98+
public boolean filesReadyToMigrate(List<TemplateDataStoreVO> templates, List<SnapshotDataStoreVO> snapshots, List<VolumeDataStoreVO> volumes) {
9699
State[] validStates = {State.Ready, State.Allocated, State.Destroying, State.Destroyed, State.Failed};
97100
boolean isReady = true;
98101
for (TemplateDataStoreVO template : templates) {
@@ -114,7 +117,8 @@ private boolean filesReadyToMigrate(Long srcDataStoreId) {
114117
List<TemplateDataStoreVO> templates = templateDataStoreDao.listByStoreId(srcDataStoreId);
115118
List<SnapshotDataStoreVO> snapshots = snapshotDataStoreDao.listByStoreId(srcDataStoreId, DataStoreRole.Image);
116119
List<VolumeDataStoreVO> volumes = volumeDataStoreDao.listByStoreId(srcDataStoreId);
117-
return filesReadyToMigrate(srcDataStoreId, templates, snapshots, volumes);
120+
121+
return filesReadyToMigrate(templates, snapshots, volumes);
118122
}
119123

120124
protected void checkIfCompleteMigrationPossible(ImageStoreService.MigrationPolicy policy, Long srcDataStoreId) {
@@ -163,29 +167,40 @@ public int compare(Map.Entry<Long, Pair<Long, Long>> e1, Map.Entry<Long, Pair<Lo
163167
}
164168

165169
protected List<DataObject> getSortedValidSourcesList(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains,
166-
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates, List<TemplateDataStoreVO> templates, List<SnapshotDataStoreVO> snapshots) {
170+
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates, List<TemplateDataStoreVO> templates, List<SnapshotDataStoreVO> snapshots, Set<Long> snapshotIdsToMigrate) {
167171
List<DataObject> files = new ArrayList<>();
168172

169173
files.addAll(getAllReadyTemplates(srcDataStore, childTemplates, templates));
170-
files.addAll(getAllReadySnapshotsAndChains(srcDataStore, snapshotChains, snapshots));
174+
files.addAll(getAllReadySnapshotsAndChains(srcDataStore, snapshotChains, snapshots, snapshotIdsToMigrate));
171175

172176
files = sortFilesOnSize(files, snapshotChains);
173177

174178
return files;
175179
}
176180

177181
protected List<DataObject> getSortedValidSourcesList(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains,
178-
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates) {
182+
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates, Set<Long> snapshotIdsToMigrate) {
179183
List<DataObject> files = new ArrayList<>();
180184
files.addAll(getAllReadyTemplates(srcDataStore, childTemplates));
181-
files.addAll(getAllReadySnapshotsAndChains(srcDataStore, snapshotChains));
185+
files.addAll(getAllReadySnapshotsAndChains(srcDataStore, snapshotChains, snapshotIdsToMigrate));
182186
files.addAll(getAllReadyVolumes(srcDataStore));
183187

184188
files = sortFilesOnSize(files, snapshotChains);
185189

186190
return files;
187191
}
188192

193+
private List<SnapshotInfo> createKvmIncrementalSnapshotChain(SnapshotDataStoreVO snapshot) {
194+
List<SnapshotInfo> chain = new LinkedList<>();
195+
SnapshotInfo snapshotInfo = snapshotFactory.getSnapshot(snapshot.getSnapshotId(), snapshot.getDataStoreId(), snapshot.getRole());
196+
197+
chain.addAll(snapshotInfo.getParents());
198+
chain.add(snapshotInfo);
199+
chain.addAll(snapshotInfo.getChildAndGrandchildren());
200+
201+
return chain;
202+
}
203+
189204
protected List<DataObject> sortFilesOnSize(List<DataObject> files, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains) {
190205
Collections.sort(files, new Comparator<DataObject>() {
191206
@Override
@@ -261,16 +276,24 @@ protected boolean shouldMigrateTemplate(TemplateDataStoreVO template, VMTemplate
261276
* for each parent snapshot and the cumulative size of the chain - this is done to ensure that all the snapshots in a chain
262277
* are migrated to the same datastore
263278
*/
264-
protected List<DataObject> getAllReadySnapshotsAndChains(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains, List<SnapshotDataStoreVO> snapshots) {
279+
protected List<DataObject> getAllReadySnapshotsAndChains(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains, List<SnapshotDataStoreVO> snapshots, Set<Long> snapshotIdsToMigrate) {
265280
List<SnapshotInfo> files = new LinkedList<>();
281+
Set<Long> snapshotIdsAlreadyInChain = new HashSet<>();
266282
for (SnapshotDataStoreVO snapshot : snapshots) {
267283
SnapshotVO snapshotVO = snapshotDao.findById(snapshot.getSnapshotId());
268284
if (snapshot.getState() == ObjectInDataStoreStateMachine.State.Ready &&
269-
snapshotVO != null && snapshotVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator
270-
&& snapshot.getParentSnapshotId() == 0 ) {
271-
SnapshotInfo snap = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), snapshot.getDataStoreId(), snapshot.getRole());
272-
if (snap != null) {
273-
files.add(snap);
285+
snapshotVO != null && snapshotVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator) {
286+
if (snapshotVO.getHypervisorType() == Hypervisor.HypervisorType.KVM && snapshot.getKvmCheckpointPath() != null && !snapshotIdsAlreadyInChain.contains(snapshotVO.getId())) {
287+
List<SnapshotInfo> kvmIncrementalSnapshotChain = createKvmIncrementalSnapshotChain(snapshot);
288+
SnapshotInfo parent = kvmIncrementalSnapshotChain.get(0);
289+
snapshotIdsAlreadyInChain.addAll(kvmIncrementalSnapshotChain.stream().map(DataObject::getId).collect(Collectors.toSet()));
290+
snapshotChains.put(parent, new Pair<>(kvmIncrementalSnapshotChain, getTotalChainSize(kvmIncrementalSnapshotChain.stream().filter(snap -> snapshotIdsToMigrate.contains(snap.getId())).collect(Collectors.toList()))));
291+
files.add(parent);
292+
} else if (snapshot.getParentSnapshotId() == 0) {
293+
SnapshotInfo snap = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), snapshot.getDataStoreId(), snapshot.getRole());
294+
if (snap != null) {
295+
files.add(snap);
296+
}
274297
}
275298
}
276299
}
@@ -285,15 +308,16 @@ protected List<DataObject> getAllReadySnapshotsAndChains(DataStore srcDataStore,
285308
chain.addAll(children);
286309
}
287310
}
288-
snapshotChains.put(parent, new Pair<List<SnapshotInfo>, Long>(chain, getTotalChainSize(chain)));
311+
snapshotChains.putIfAbsent(parent, new Pair<>(chain, getTotalChainSize(chain)));
289312
}
290313

291314
return (List<DataObject>) (List<?>) files;
292315
}
293316

294-
protected List<DataObject> getAllReadySnapshotsAndChains(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains) {
317+
protected List<DataObject> getAllReadySnapshotsAndChains(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains, Set<Long> snapshotIdsToMigrate) {
295318
List<SnapshotDataStoreVO> snapshots = snapshotDataStoreDao.listByStoreId(srcDataStore.getId(), DataStoreRole.Image);
296-
return getAllReadySnapshotsAndChains(srcDataStore, snapshotChains, snapshots);
319+
snapshotIdsToMigrate.addAll(snapshots.stream().map(SnapshotDataStoreVO::getSnapshotId).collect(Collectors.toSet()));
320+
return getAllReadySnapshotsAndChains(srcDataStore, snapshotChains, snapshots, snapshotIdsToMigrate);
297321
}
298322

299323
protected Long getTotalChainSize(List<? extends DataObject> chain) {

0 commit comments

Comments
 (0)