Skip to content

Commit 2774bc1

Browse files
DaanHooglandsureshanapartiPearl1594Daan HooglandPearl d'Silva
authored
use physical size instead of virtual size for migration. (#5750)
* Use Physical size to evaluate if migration is possible * Improve logging and consider files skipped as failure in complete migration * skipped can't be negative * remove useless method * group multidisk templates for secstor migration * use enum * Update engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java Co-authored-by: sureshanaparti <12028987+sureshanaparti@users.noreply.github.com> Co-authored-by: Pearl Dsilva <pearl1594@gmail.com> Co-authored-by: Daan Hoogland <dahn@onecht.net> Co-authored-by: Pearl d'Silva <pearl.dsilva@shapeblue.com>
1 parent 51f69f7 commit 2774bc1

8 files changed

Lines changed: 130 additions & 40 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public interface DataObject {
3333

3434
Long getSize();
3535

36+
long getPhysicalSize();
37+
3638
DataObjectType getType();
3739

3840
String getUuid();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,6 @@ public DataObject getData() {
3939
}
4040

4141
}
42-
AsyncCallFuture<DataObjectResult> migrateData(DataObject srcDataObject, DataStore srcDatastore, DataStore destDatastore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChain);
42+
AsyncCallFuture<DataObjectResult> migrateData(DataObject srcDataObject, DataStore srcDatastore, DataStore destDatastore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChain,
43+
Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChain);
4344
}

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

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@
3232
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
3333
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
3434
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
35+
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
3536
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
3637
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
3738
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
39+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
3840
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
3941
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
4042
import org.apache.cloudstack.storage.ImageStoreService;
@@ -59,8 +61,11 @@
5961
import com.cloud.vm.SecondaryStorageVmVO;
6062
import com.cloud.vm.VirtualMachine;
6163
import com.cloud.vm.dao.SecondaryStorageVmDao;
64+
import org.apache.log4j.Logger;
6265

6366
public class DataMigrationUtility {
67+
private static Logger LOGGER = Logger.getLogger(DataMigrationUtility.class);
68+
6469
@Inject
6570
SecondaryStorageVmDao secStorageVmDao;
6671
@Inject
@@ -87,19 +92,22 @@ public class DataMigrationUtility {
8792
* the migration is terminated.
8893
*/
8994
private boolean filesReadyToMigrate(Long srcDataStoreId) {
90-
String[] validStates = new String[]{"Ready", "Allocated", "Destroying", "Destroyed", "Failed"};
95+
State[] validStates = {State.Ready, State.Allocated, State.Destroying, State.Destroyed, State.Failed};
9196
boolean isReady = true;
9297
List<TemplateDataStoreVO> templates = templateDataStoreDao.listByStoreId(srcDataStoreId);
9398
for (TemplateDataStoreVO template : templates) {
94-
isReady &= (Arrays.asList(validStates).contains(template.getState().toString()));
99+
isReady &= (Arrays.asList(validStates).contains(template.getState()));
100+
LOGGER.trace(String.format("template state: %s", template.getState()));
95101
}
96102
List<SnapshotDataStoreVO> snapshots = snapshotDataStoreDao.listByStoreId(srcDataStoreId, DataStoreRole.Image);
97103
for (SnapshotDataStoreVO snapshot : snapshots) {
98-
isReady &= (Arrays.asList(validStates).contains(snapshot.getState().toString()));
104+
isReady &= (Arrays.asList(validStates).contains(snapshot.getState()));
105+
LOGGER.trace(String.format("snapshot state: %s", snapshot.getState()));
99106
}
100107
List<VolumeDataStoreVO> volumes = volumeDataStoreDao.listByStoreId(srcDataStoreId);
101108
for (VolumeDataStoreVO volume : volumes) {
102-
isReady &= (Arrays.asList(validStates).contains(volume.getState().toString()));
109+
isReady &= (Arrays.asList(validStates).contains(volume.getState()));
110+
LOGGER.trace(String.format("volume state: %s", volume.getState()));
103111
}
104112
return isReady;
105113
}
@@ -113,12 +121,17 @@ protected void checkIfCompleteMigrationPossible(ImageStoreService.MigrationPolic
113121
return;
114122
}
115123

116-
protected Long getFileSize(DataObject file, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChain) {
117-
Long size = file.getSize();
124+
protected Long getFileSize(DataObject file, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChain, Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChain) {
125+
Long size = file.getPhysicalSize();
118126
Pair<List<SnapshotInfo>, Long> chain = snapshotChain.get(file);
127+
Pair<List<TemplateInfo>, Long> tempChain = templateChain.get(file);
128+
119129
if (file instanceof SnapshotInfo && chain.first() != null && !chain.first().isEmpty()) {
120130
size = chain.second();
121131
}
132+
if (file instanceof TemplateInfo && tempChain.first() != null && !tempChain.first().isEmpty()) {
133+
size = tempChain.second();
134+
}
122135
return size;
123136
}
124137

@@ -144,9 +157,10 @@ public int compare(Map.Entry<Long, Pair<Long, Long>> e1, Map.Entry<Long, Pair<Lo
144157
return new ArrayList<>(temp.keySet());
145158
}
146159

147-
protected List<DataObject> getSortedValidSourcesList(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains) {
160+
protected List<DataObject> getSortedValidSourcesList(DataStore srcDataStore, Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains,
161+
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates) {
148162
List<DataObject> files = new ArrayList<>();
149-
files.addAll(getAllReadyTemplates(srcDataStore));
163+
files.addAll(getAllReadyTemplates(srcDataStore, childTemplates));
150164
files.addAll(getAllReadySnapshotsAndChains(srcDataStore, snapshotChains));
151165
files.addAll(getAllReadyVolumes(srcDataStore));
152166

@@ -159,8 +173,8 @@ protected List<DataObject> sortFilesOnSize(List<DataObject> files, Map<DataObjec
159173
Collections.sort(files, new Comparator<DataObject>() {
160174
@Override
161175
public int compare(DataObject o1, DataObject o2) {
162-
Long size1 = o1.getSize();
163-
Long size2 = o2.getSize();
176+
Long size1 = o1.getPhysicalSize();
177+
Long size2 = o2.getPhysicalSize();
164178
if (o1 instanceof SnapshotInfo) {
165179
size1 = snapshotChains.get(o1).second();
166180
}
@@ -173,19 +187,28 @@ public int compare(DataObject o1, DataObject o2) {
173187
return files;
174188
}
175189

176-
protected List<DataObject> getAllReadyTemplates(DataStore srcDataStore) {
190+
protected List<DataObject> getAllReadyTemplates(DataStore srcDataStore, Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates) {
177191

178-
List<DataObject> files = new LinkedList<>();
192+
List<TemplateInfo> files = new LinkedList<>();
179193
List<TemplateDataStoreVO> templates = templateDataStoreDao.listByStoreId(srcDataStore.getId());
180194
for (TemplateDataStoreVO template : templates) {
181195
VMTemplateVO templateVO = templateDao.findById(template.getTemplateId());
182196
if (template.getState() == ObjectInDataStoreStateMachine.State.Ready && templateVO != null &&
183197
(!templateVO.isPublicTemplate() || (templateVO.isPublicTemplate() && templateVO.getUrl() == null)) &&
184-
templateVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator) {
198+
templateVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator && templateVO.getParentTemplateId() == null) {
185199
files.add(templateFactory.getTemplate(template.getTemplateId(), srcDataStore));
186200
}
187201
}
188-
return files;
202+
for (TemplateInfo template: files) {
203+
List<VMTemplateVO> children = templateDao.listByParentTemplatetId(template.getId());
204+
List<TemplateInfo> temps = new ArrayList<>();
205+
temps.add(template);
206+
for(VMTemplateVO child : children) {
207+
temps.add(templateFactory.getTemplate(child.getId(), srcDataStore));
208+
}
209+
childTemplates.put(template, new Pair<>(temps, getTotalChainSize(temps)));
210+
}
211+
return (List<DataObject>) (List<?>) files;
189212
}
190213

191214
/** Returns parent snapshots and snapshots that do not have any children; snapshotChains comprises of the snapshot chain info
@@ -217,21 +240,20 @@ protected List<DataObject> getAllReadySnapshotsAndChains(DataStore srcDataStore,
217240
chain.addAll(children);
218241
}
219242
}
220-
snapshotChains.put(parent, new Pair<List<SnapshotInfo>, Long>(chain, getSizeForChain(chain)));
243+
snapshotChains.put(parent, new Pair<List<SnapshotInfo>, Long>(chain, getTotalChainSize(chain)));
221244
}
222245

223246
return (List<DataObject>) (List<?>) files;
224247
}
225248

226-
protected Long getSizeForChain(List<SnapshotInfo> chain) {
249+
protected Long getTotalChainSize(List<? extends DataObject> chain) {
227250
Long size = 0L;
228-
for (SnapshotInfo snapshot : chain) {
229-
size += snapshot.getSize();
251+
for (DataObject dataObject : chain) {
252+
size += dataObject.getPhysicalSize();
230253
}
231254
return size;
232255
}
233256

234-
235257
protected List<DataObject> getAllReadyVolumes(DataStore srcDataStore) {
236258
List<DataObject> files = new LinkedList<>();
237259
List<VolumeDataStoreVO> volumes = volumeDataStoreDao.listByStoreId(srcDataStore.getId());

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

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.apache.cloudstack.engine.subsystem.api.storage.SecondaryStorageService.DataObjectResult;
4545
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
4646
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
47+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
4748
import org.apache.cloudstack.framework.async.AsyncCallFuture;
4849
import org.apache.cloudstack.framework.config.ConfigKey;
4950
import org.apache.cloudstack.framework.config.Configurable;
@@ -144,18 +145,19 @@ public MigrationResponse migrateData(Long srcDataStoreId, List<Long> destDatasto
144145
migrationHelper.checkIfCompleteMigrationPossible(migrationPolicy, srcDataStoreId);
145146
DataStore srcDatastore = dataStoreManager.getDataStore(srcDataStoreId, DataStoreRole.Image);
146147
Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains = new HashMap<>();
147-
files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains);
148+
Map<DataObject, Pair<List<TemplateInfo>, Long>> childTemplates = new HashMap<>();
149+
files = migrationHelper.getSortedValidSourcesList(srcDatastore, snapshotChains, childTemplates);
148150

149151
if (files.isEmpty()) {
150-
return new MigrationResponse("No files in Image store "+srcDatastore.getId()+ " to migrate", migrationPolicy.toString(), true);
152+
return new MigrationResponse(String.format("No files in Image store: %s to migrate", srcDatastore.getId()), migrationPolicy.toString(), true);
151153
}
152154
Map<Long, Pair<Long, Long>> storageCapacities = new Hashtable<>();
153155
for (Long storeId : destDatastores) {
154156
storageCapacities.put(storeId, new Pair<>(null, null));
155157
}
156158
storageCapacities.put(srcDataStoreId, new Pair<>(null, null));
157159
if (migrationPolicy == MigrationPolicy.COMPLETE) {
158-
s_logger.debug("Setting source image store "+srcDatastore.getId()+ " to read-only");
160+
s_logger.debug(String.format("Setting source image store: %s to read-only", srcDatastore.getId()));
159161
storageService.updateImageStoreStatus(srcDataStoreId, true);
160162
}
161163

@@ -172,6 +174,7 @@ public MigrationResponse migrateData(Long srcDataStoreId, List<Long> destDatasto
172174
return response;
173175
}
174176

177+
int skipped = 0;
175178
List<Future<AsyncCallFuture<DataObjectResult>>> futures = new ArrayList<>();
176179
while (true) {
177180
DataObject chosenFileForMigration = null;
@@ -184,7 +187,7 @@ public MigrationResponse migrateData(Long srcDataStoreId, List<Long> destDatasto
184187
Long destDatastoreId = orderedDS.get(0);
185188

186189
if (chosenFileForMigration == null || destDatastoreId == null || (destDatastoreId == srcDatastore.getId() && migrationPolicy == MigrationPolicy.BALANCE) ) {
187-
Pair<String, Boolean> result = migrateCompleted(destDatastoreId, srcDatastore, files, migrationPolicy);
190+
Pair<String, Boolean> result = migrateCompleted(destDatastoreId, srcDatastore, files, migrationPolicy, skipped);
188191
message = result.first();
189192
success = result.second();
190193
break;
@@ -194,13 +197,14 @@ public MigrationResponse migrateData(Long srcDataStoreId, List<Long> destDatasto
194197
destDatastoreId = orderedDS.get(1);
195198
}
196199

197-
if (chosenFileForMigration.getSize() > storageCapacities.get(destDatastoreId).first()) {
198-
s_logger.debug("file: " + chosenFileForMigration.getId() + " too large to be migrated to " + destDatastoreId);
200+
if (chosenFileForMigration.getPhysicalSize() > storageCapacities.get(destDatastoreId).first()) {
201+
s_logger.debug(String.format("%s: %s too large to be migrated to %s", chosenFileForMigration.getType().name() , chosenFileForMigration.getUuid(), destDatastoreId));
202+
skipped += 1;
199203
continue;
200204
}
201205

202-
if (shouldMigrate(chosenFileForMigration, srcDatastore.getId(), destDatastoreId, storageCapacities, snapshotChains, migrationPolicy)) {
203-
storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, srcDatastore, destDatastoreId, executor, futures);
206+
if (shouldMigrate(chosenFileForMigration, srcDatastore.getId(), destDatastoreId, storageCapacities, snapshotChains, childTemplates, migrationPolicy)) {
207+
storageCapacities = migrateAway(chosenFileForMigration, storageCapacities, snapshotChains, childTemplates, srcDatastore, destDatastoreId, executor, futures);
204208
} else {
205209
if (migrationPolicy == MigrationPolicy.BALANCE) {
206210
continue;
@@ -215,7 +219,7 @@ public MigrationResponse migrateData(Long srcDataStoreId, List<Long> destDatasto
215219
return handleResponse(futures, migrationPolicy, message, success);
216220
}
217221

218-
protected Pair<String, Boolean> migrateCompleted(Long destDatastoreId, DataStore srcDatastore, List<DataObject> files, MigrationPolicy migrationPolicy) {
222+
protected Pair<String, Boolean> migrateCompleted(Long destDatastoreId, DataStore srcDatastore, List<DataObject> files, MigrationPolicy migrationPolicy, int skipped) {
219223
String message = "";
220224
boolean success = true;
221225
if (destDatastoreId == srcDatastore.getId() && !files.isEmpty()) {
@@ -233,14 +237,27 @@ protected Pair<String, Boolean> migrateCompleted(Long destDatastoreId, DataStore
233237
}
234238
} else {
235239
message = "Migration completed";
240+
if (migrationPolicy == MigrationPolicy.COMPLETE && skipped > 0) {
241+
message += ". Not all data objects were migrated. Some were probably skipped due to lack of storage capacity.";
242+
success = false;
243+
}
236244
}
237245
return new Pair<String, Boolean>(message, success);
238246
}
239247

240-
protected Map<Long, Pair<Long, Long>> migrateAway(DataObject chosenFileForMigration, Map<Long, Pair<Long, Long>> storageCapacities,
241-
Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains, DataStore srcDatastore, Long destDatastoreId, ThreadPoolExecutor executor,
242-
List<Future<AsyncCallFuture<DataObjectResult>>> futures) {
243-
Long fileSize = migrationHelper.getFileSize(chosenFileForMigration, snapshotChains);
248+
protected Map<Long, Pair<Long, Long>> migrateAway(
249+
DataObject chosenFileForMigration,
250+
Map<Long, Pair<Long, Long>> storageCapacities,
251+
Map<DataObject,
252+
Pair<List<SnapshotInfo>, Long>> snapshotChains,
253+
Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChains,
254+
DataStore srcDatastore,
255+
Long destDatastoreId,
256+
ThreadPoolExecutor executor,
257+
List<Future<AsyncCallFuture<DataObjectResult>>> futures) {
258+
259+
Long fileSize = migrationHelper.getFileSize(chosenFileForMigration, snapshotChains, templateChains);
260+
244261
storageCapacities = assumeMigrate(storageCapacities, srcDatastore.getId(), destDatastoreId, fileSize);
245262
long activeSsvms = migrationHelper.activeSSVMCount(srcDatastore);
246263
long totalJobs = activeSsvms * numConcurrentCopyTasksPerSSVM;
@@ -254,8 +271,11 @@ protected Map<Long, Pair<Long, Long>> migrateAway(DataObject chosenFileForMigrat
254271
if (chosenFileForMigration instanceof SnapshotInfo ) {
255272
task.setSnapshotChains(snapshotChains);
256273
}
274+
if (chosenFileForMigration instanceof TemplateInfo) {
275+
task.setTemplateChain(templateChains);
276+
}
257277
futures.add((executor.submit(task)));
258-
s_logger.debug("Migration of file " + chosenFileForMigration.getId() + " is initiated");
278+
s_logger.debug(String.format("Migration of %s: %s is initiated. ", chosenFileForMigration.getType().name(), chosenFileForMigration.getUuid()));
259279
return storageCapacities;
260280
}
261281

@@ -374,13 +394,19 @@ private Map<Long, Pair<Long, Long>> assumeMigrate(Map<Long, Pair<Long, Long>> st
374394
* @param migrationPolicy determines whether a "Balance" or "Complete" migration operation is to be performed
375395
* @return
376396
*/
377-
private boolean shouldMigrate(DataObject chosenFile, Long srcDatastoreId, Long destDatastoreId, Map<Long, Pair<Long, Long>> storageCapacities,
378-
Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains, MigrationPolicy migrationPolicy) {
397+
private boolean shouldMigrate(
398+
DataObject chosenFile,
399+
Long srcDatastoreId,
400+
Long destDatastoreId,
401+
Map<Long, Pair<Long, Long>> storageCapacities,
402+
Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChains,
403+
Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChains,
404+
MigrationPolicy migrationPolicy) {
379405

380406
if (migrationPolicy == MigrationPolicy.BALANCE) {
381407
double meanStdDevCurrent = getStandardDeviation(storageCapacities);
382408

383-
Long fileSize = migrationHelper.getFileSize(chosenFile, snapshotChains);
409+
Long fileSize = migrationHelper.getFileSize(chosenFile, snapshotChains, templateChains);
384410
Map<Long, Pair<Long, Long>> proposedCapacities = assumeMigrate(storageCapacities, srcDatastoreId, destDatastoreId, fileSize);
385411
double meanStdDevAfter = getStandardDeviation(proposedCapacities);
386412

@@ -426,6 +452,7 @@ private class MigrateDataTask implements Callable<AsyncCallFuture<DataObjectResu
426452
private DataStore srcDataStore;
427453
private DataStore destDataStore;
428454
private Map<DataObject, Pair<List<SnapshotInfo>, Long>> snapshotChain;
455+
private Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChain;
429456
public MigrateDataTask(DataObject file, DataStore srcDataStore, DataStore destDataStore) {
430457
this.file = file;
431458
this.srcDataStore = srcDataStore;
@@ -439,13 +466,19 @@ public void setSnapshotChains(Map<DataObject, Pair<List<SnapshotInfo>, Long>> sn
439466
public Map<DataObject, Pair<List<SnapshotInfo>, Long>> getSnapshotChain() {
440467
return snapshotChain;
441468
}
469+
public Map<DataObject, Pair<List<TemplateInfo>, Long>> getTemplateChain() {
470+
return templateChain;
471+
}
472+
public void setTemplateChain(Map<DataObject, Pair<List<TemplateInfo>, Long>> templateChain) {
473+
this.templateChain = templateChain;
474+
}
442475
public DataObject getFile() {
443476
return file;
444477
}
445478

446479
@Override
447480
public AsyncCallFuture<DataObjectResult> call() throws Exception {
448-
return secStgSrv.migrateData(file, srcDataStore, destDataStore, snapshotChain);
481+
return secStgSrv.migrateData(file, srcDataStore, destDataStore, snapshotChain, templateChain);
449482
}
450483
}
451484
}

0 commit comments

Comments
 (0)