Skip to content

Commit 388a1de

Browse files
committed
<test>[imageStore]: clean bits after volume flatten
when flatten leaf node in reference tree, delete deleted backing volume vo bis. Resolves: ZSTAC-56100 Change-Id: I73676d7567726f6c7a7a6a766b63736b70797573
1 parent ffe5b13 commit 388a1de

File tree

4 files changed

+115
-57
lines changed

4 files changed

+115
-57
lines changed

core/src/main/java/org/zstack/core/trash/StorageRecycleImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,23 @@ public String makeSureInstallPathNotUsed(String installPath, String resourceType
260260
return null;
261261
}
262262

263+
@Override
264+
public String makeSurePrimaryStorageInstallPathNotUsed(String installPath) {
265+
String details = checkVolume(installPath);
266+
if (details != null) {
267+
return details;
268+
}
269+
details = checkImageCache(installPath);
270+
if (details != null) {
271+
return details;
272+
}
273+
details = checkVolumeSnapshot(installPath);
274+
if (details != null) {
275+
return details;
276+
}
277+
return null;
278+
}
279+
263280
@Override
264281
public Long getTrashId(String storageUuid, String installPath) {
265282
DebugUtils.Assert(installPath != null, "installPath is not allowed null here");

core/src/main/java/org/zstack/core/trash/StorageTrash.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public interface StorageTrash {
1616

1717
String makeSureInstallPathNotUsed(InstallPathRecycleInventory inventory);
1818
String makeSureInstallPathNotUsed(String installPath, String resourceType);
19+
String makeSurePrimaryStorageInstallPathNotUsed(String installPath);
1920
List<String> findTrashInstallPath(String installPath, String storageUuid);
2021
Long getTrashId(String storageUuid, String installPath);
2122

storage/src/main/java/org/zstack/storage/snapshot/reference/VolumeSnapshotReferenceTreeBase.java

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.zstack.core.thread.ChainTask;
1010
import org.zstack.core.thread.SyncTaskChain;
1111
import org.zstack.core.thread.ThreadFacade;
12+
import org.zstack.core.trash.StorageTrash;
1213
import org.zstack.core.workflow.FlowChainBuilder;
1314
import org.zstack.header.core.Completion;
1415
import org.zstack.header.core.workflow.*;
@@ -19,6 +20,7 @@
1920
import org.zstack.header.storage.primary.GetVolumeBackingChainFromPrimaryStorageMsg;
2021
import org.zstack.header.storage.primary.GetVolumeBackingChainFromPrimaryStorageReply;
2122
import org.zstack.header.storage.primary.PrimaryStorageConstant;
23+
import org.zstack.header.storage.snapshot.VolumeSnapshotVO;
2224
import org.zstack.header.storage.snapshot.reference.DeleteVolumeSnapshotReferenceLeafMsg;
2325
import org.zstack.header.storage.snapshot.reference.DeleteVolumeSnapshotReferenceLeafReply;
2426
import org.zstack.header.storage.snapshot.reference.VolumeSnapshotReferenceInventory;
@@ -44,6 +46,9 @@ public class VolumeSnapshotReferenceTreeBase {
4446
@Autowired
4547
private DatabaseFacade dbf;
4648

49+
@Autowired
50+
private StorageTrash trash;
51+
4752
private VolumeSnapshotReferenceTreeInventory self;
4853

4954
public VolumeSnapshotReferenceTreeBase(VolumeSnapshotReferenceTreeInventory self) {
@@ -98,7 +103,7 @@ private void deleteSnapshotRefLeaf(DeleteVolumeSnapshotReferenceLeafMsg msg, Com
98103
Set<String> otherLeafDirectBackingInstallUrls = msg.getOtherLeafs().stream()
99104
.map(VolumeSnapshotReferenceInventory::getDirectSnapshotInstallUrl)
100105
.collect(Collectors.toSet());
101-
if (otherLeafDirectBackingInstallUrls.contains(msg.getLeaf().getDirectSnapshotInstallUrl())) {
106+
if (hasSameVolumeResource(msg.getLeaf().getDirectSnapshotInstallUrl(), otherLeafDirectBackingInstallUrls)) {
102107
logger.debug(String.format("other leafs has the same direct backing, skip delete leaf: [%d]", msg.getLeaf().getId()));
103108
completion.success();
104109
return;
@@ -111,9 +116,12 @@ private void deleteSnapshotRefLeaf(DeleteVolumeSnapshotReferenceLeafMsg msg, Com
111116
logger.debug(String.format("no volume between leaf: [%d] and its parent, skip delete leaf", msg.getLeaf().getId()));
112117
completion.success();
113118
return;
119+
} else {
120+
logger.debug(String.format("it is going to delete untracked volume resource preliminarily: " +
121+
"startPath: [%s], endPath: [%s], contains end: %s", startPath, endPath, rootDeleted));
114122
}
115123

116-
Set<String> directBackingInstallUrls = new HashSet<>(otherLeafDirectBackingInstallUrls);
124+
List<String> directBackingInstallUrls = new ArrayList<>(otherLeafDirectBackingInstallUrls);
117125
directBackingInstallUrls.add(msg.getLeaf().getDirectSnapshotInstallUrl());
118126
final Map<String, List<String>> backingChains = new HashMap<>();
119127
final List<String> toDeletePaths = new ArrayList<>();
@@ -127,7 +135,7 @@ private void deleteSnapshotRefLeaf(DeleteVolumeSnapshotReferenceLeafMsg msg, Com
127135
public void run(FlowTrigger trigger, Map data) {
128136
GetVolumeBackingChainFromPrimaryStorageMsg gmsg = new GetVolumeBackingChainFromPrimaryStorageMsg();
129137
gmsg.setVolumeUuid(msg.getDeletedVolume().getUuid());
130-
gmsg.setRootInstallPaths(new ArrayList<>(directBackingInstallUrls));
138+
gmsg.setRootInstallPaths(directBackingInstallUrls);
131139
gmsg.setPrimaryStorageUuid(msg.getTree().getPrimaryStorageUuid());
132140
gmsg.setHostUuid(msg.getTree().getHostUuid());
133141
gmsg.setVolumeFormat(msg.getDeletedVolume().getFormat());
@@ -141,7 +149,12 @@ public void run(MessageReply reply) {
141149
}
142150

143151
GetVolumeBackingChainFromPrimaryStorageReply gr = reply.castReply();
144-
backingChains.putAll(gr.getBackingChainInstallPath());
152+
for (Map.Entry<String, List<String>> e : gr.getBackingChainInstallPath().entrySet()) {
153+
backingChains.put(e.getKey(), new ArrayList<>(e.getValue()));
154+
if (!e.getValue().contains(e.getKey())) {
155+
backingChains.get(e.getKey()).add(0, e.getKey());
156+
}
157+
}
145158
trigger.next();
146159
}
147160
});
@@ -156,12 +169,14 @@ public void rollback(FlowRollback trigger, Map data) {
156169

157170
@Override
158171
public void run(FlowTrigger trigger, Map data) {
159-
List<String> backingChain = backingChains.get(startPath);
160-
if (!backingChain.contains(startPath)) {
161-
toDeletePaths.add(startPath);
162-
}
172+
for (String path : backingChains.get(startPath)) {
173+
String usedDetails = trash.makeSurePrimaryStorageInstallPathNotUsed(path);
174+
if (usedDetails != null) {
175+
logger.error("[THIS IS A BUG NEEDED TO BE FIXED RIGHT NOW, PLEASE REPORT TO US ASAP]" +
176+
" install path[" + path + "] is still used, details: " + usedDetails);
177+
break;
178+
}
163179

164-
for (String path : backingChain) {
165180
if (path.contains(endPath)) {
166181
if (rootDeleted) {
167182
toDeletePaths.add(path);
@@ -172,12 +187,11 @@ public void run(FlowTrigger trigger, Map data) {
172187
toDeletePaths.add(path);
173188
}
174189

175-
for (Map.Entry<String, List<String>> e : backingChains.entrySet()) {
176-
if (!e.getKey().equals(startPath)) {
177-
toDeletePaths.removeAll(e.getValue());
178-
}
179-
}
190+
Set<String> usedByOthersPaths = backingChains.entrySet().stream()
191+
.filter(e -> !e.getKey().equals(startPath))
192+
.flatMap(e -> e.getValue().stream()).collect(Collectors.toSet());
180193

194+
filterDeletePaths(toDeletePaths, usedByOthersPaths);
181195
logger.debug("delete snapshot reference leafs: " + toDeletePaths);
182196
trigger.next();
183197
}
@@ -215,4 +229,39 @@ public void handle(Map data) {
215229
}
216230
}).start();
217231
}
232+
233+
private boolean hasSameVolumeResource(String snapshotInstallUrl, Set<String> snapshotInstallUrls) {
234+
if (!snapshotInstallUrl.contains("@")) {
235+
return snapshotInstallUrls.contains(snapshotInstallUrl);
236+
}
237+
238+
String volumeInstallUrl = getVolumeInstallUrl(snapshotInstallUrl);
239+
for (String url : snapshotInstallUrls) {
240+
if (getVolumeInstallUrl(url).equals(volumeInstallUrl)) {
241+
return true;
242+
}
243+
}
244+
245+
return false;
246+
}
247+
248+
private void filterDeletePaths(List<String> toDeletePaths, Set<String> protectedPaths) {
249+
Iterator<String> it = toDeletePaths.iterator();
250+
Set<String> protectedVolumePaths = protectedPaths.stream().map(this::getVolumeInstallUrl).collect(Collectors.toSet());
251+
while (it.hasNext()) {
252+
String path = getVolumeInstallUrl(it.next());
253+
if (protectedVolumePaths.contains(path)) {
254+
it.remove();
255+
}
256+
}
257+
}
258+
259+
// TODO hard code for internal snapshot, need to be refactored
260+
private String getVolumeInstallUrl(String snapshotInstallUrl) {
261+
if (!snapshotInstallUrl.contains("@")) {
262+
return snapshotInstallUrl;
263+
}
264+
265+
return snapshotInstallUrl.split("@")[0];
266+
}
218267
}

storage/src/main/java/org/zstack/storage/snapshot/reference/VolumeSnapshotReferenceUtils.java

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -97,29 +97,28 @@ public static void handleChainVolumeSnapshotReferenceAfterFlatten(VolumeInventor
9797
return;
9898
}
9999

100-
String currentSnapshotTreeUuid = Q.New(VolumeSnapshotTreeVO.class).select(VolumeSnapshotTreeVO_.uuid)
101-
.eq(VolumeSnapshotTreeVO_.volumeUuid, volume.getUuid())
102-
.eq(VolumeSnapshotTreeVO_.current, true)
103-
.findValue();
104-
if (currentSnapshotTreeUuid == null) {
105-
deleteSnapshotRef(ref);
100+
if (ref.getReferenceType().equals(VolumeVO.class.getSimpleName())) {
101+
// it means volume has no snapshot, so the reference is leaf node, just delete it.
102+
deleteSnapshotRefLeafInTree(null, ref);
106103
return;
107104
}
108105

109-
boolean volumeReferToOtherVols = !getVolumeReferenceSnapshotCurrently(volume.getUuid(), currentSnapshotTreeUuid).isEmpty();
110-
if (volumeReferToOtherVols) {
111-
return;
112-
}
106+
// it means volume has snapshot, reference type will be snapshot.
107+
String hasBackingSnapshotTreeUuid = Q.New(VolumeSnapshotVO.class).select(VolumeSnapshotVO_.treeUuid)
108+
.eq(VolumeSnapshotTreeVO_.uuid, ref.getReferenceUuid())
109+
.findValue();
113110

114-
if (!isVolumeCurrentBaseSnapshotReference(volume, ref, currentSnapshotTreeUuid)) {
111+
if (isVolumeSnapshotTreeReferOtherVolumes(volume.getUuid(), hasBackingSnapshotTreeUuid)) {
115112
return;
116113
}
117114

118115
SQL.New(VolumeSnapshotVO.class).set(VolumeSnapshotVO_.state, VolumeSnapshotState.Disabled)
119-
.eq(VolumeSnapshotVO_.treeUuid, currentSnapshotTreeUuid)
116+
.eq(VolumeSnapshotVO_.treeUuid, hasBackingSnapshotTreeUuid)
120117
.update();
121-
logger.warn(String.format("disable current snapshot tree[volumeUuid: %s] because of volume flatten.", volume.getUuid()));
118+
logger.warn(String.format("disable snapshot tree[volumeUuid: %s, uuid:%s] which is backing other volume[uuid:%s] " +
119+
"because of volume flatten.", volume.getUuid(), hasBackingSnapshotTreeUuid, ref.getVolumeUuid()));
122120

121+
// FIXME: mark reference deleted and delete ps backing bits after volume expunge
123122
deleteSnapshotRef(ref);
124123
}
125124

@@ -129,7 +128,7 @@ public static void handleStorageVolumeSnapshotReferenceAfterFlatten(VolumeInvent
129128
.eq(VolumeSnapshotReferenceVO_.referenceInstallUrl, volume.getInstallPath())
130129
.find();
131130
if (ref != null) {
132-
deleteSnapshotRef(ref);
131+
deleteSnapshotRefLeafInTree(null, ref);
133132
}
134133
}
135134

@@ -319,7 +318,7 @@ public static void handleSnapshotDeletion(VolumeSnapshotVO snapshot) {
319318
.eq(VolumeSnapshotReferenceVO_.referenceUuid, snapshot.getUuid())
320319
.find();
321320
if (ref != null) {
322-
deleteSnapshotRef(ref);
321+
deleteSnapshotRefLeafInTree(null, ref);
323322
}
324323
}
325324

@@ -347,6 +346,7 @@ protected void scripts() {
347346
.listValues();
348347

349348
if (!childrenRefIds.isEmpty()) {
349+
// redirect children refs to parent
350350
sql(VolumeSnapshotReferenceVO.class).in(VolumeSnapshotReferenceVO_.id, childrenRefIds)
351351
.set(VolumeSnapshotReferenceVO_.parentId, finalRef.getParentId())
352352
.set(VolumeSnapshotReferenceVO_.volumeUuid, finalRef.getVolumeUuid())
@@ -357,18 +357,13 @@ protected void scripts() {
357357
childrenRefIds, finalRef.getReferenceVolumeUuid(), finalRef.getVolumeUuid(), finalRef.getParentId()));
358358
deleteSnapshotRef(finalRef);
359359
} else {
360-
if (!finalRef.getDirectSnapshotUuid().equals(finalRef.getVolumeSnapshotUuid()) ||
361-
findByUuid(ref.getVolumeUuid(), VolumeVO.class) == null) {
362-
deleteBitsOnPs(tree, finalRef);
363-
}
364-
deleteSnapshotRef(finalRef);
360+
deleteSnapshotRefLeafInTree(tree, finalRef);
365361
}
366362

367363
}
368364
}.execute();
369365
}
370366

371-
//TODO
372367
private static void deleteBitsOnPs(VolumeSnapshotReferenceTreeVO treeVO, VolumeSnapshotReferenceVO ref) {
373368
List<VolumeSnapshotReferenceVO> treeRefs = Q.New(VolumeSnapshotReferenceVO.class).eq(VolumeSnapshotReferenceVO_.treeUuid, treeVO.getUuid()).list();
374369

@@ -403,37 +398,33 @@ private static void deleteSnapshotRef(VolumeSnapshotReferenceVO ref) {
403398
}
404399
}
405400

406-
// for chain snapshot
407-
private static List<String> getVolumeReferenceSnapshotCurrently(String volumeUuid, String currentTreeUuid) {
408-
List<String> refSnapshotUuids = Q.New(VolumeSnapshotReferenceVO.class).select(VolumeSnapshotReferenceVO_.volumeSnapshotUuid)
409-
.eq(VolumeSnapshotReferenceVO_.volumeUuid, volumeUuid)
410-
.listValues();
401+
private static void deleteSnapshotRefLeafInTree(VolumeSnapshotReferenceTreeVO tree, VolumeSnapshotReferenceVO ref) {
402+
boolean referenceRedirected = !ref.getDirectSnapshotUuid().equals(ref.getVolumeSnapshotUuid());
403+
if (referenceRedirected || !Q.New(VolumeVO.class).eq(VolumeVO_.uuid, ref.getVolumeUuid()).isExists()) {
404+
if (tree == null) {
405+
tree = Q.New(VolumeSnapshotReferenceTreeVO.class).eq(VolumeSnapshotReferenceTreeVO_.uuid, ref.getTreeUuid()).find();
406+
}
411407

412-
if (refSnapshotUuids.isEmpty()) {
413-
return Collections.emptyList();
408+
deleteBitsOnPs(tree, ref);
414409
}
415410

416-
return Q.New(VolumeSnapshotVO.class).select(VolumeSnapshotVO_.uuid)
417-
.eq(VolumeSnapshotVO_.treeUuid, currentTreeUuid)
418-
.in(VolumeSnapshotVO_.uuid, refSnapshotUuids)
419-
.listValues();
411+
deleteSnapshotRef(ref);
420412
}
421413

422414
// for chain snapshot
423-
private static boolean isVolumeCurrentBaseSnapshotReference(VolumeInventory volume, VolumeSnapshotReferenceVO ref, String currentTreeUuid) {
424-
if (ref.getReferenceType().equals(VolumeVO.class.getSimpleName()) ||
425-
volume.getInstallPath().equals(ref.getReferenceInstallUrl())) {
426-
return true;
427-
}
428-
429-
List<String> currentSnapshotUuids = Q.New(VolumeSnapshotVO.class).select(VolumeSnapshotVO_.uuid)
430-
.eq(VolumeSnapshotVO_.treeUuid, currentTreeUuid)
415+
private static boolean isVolumeSnapshotTreeReferOtherVolumes(String volumeUuid, String snapshotTreeUuid) {
416+
List<String> refSnapshotUuids = Q.New(VolumeSnapshotReferenceVO.class).select(VolumeSnapshotReferenceVO_.volumeSnapshotUuid)
417+
.eq(VolumeSnapshotReferenceVO_.volumeUuid, volumeUuid)
431418
.listValues();
432-
if (currentSnapshotUuids.stream().anyMatch(it -> it.equals(ref.getReferenceUuid()))) {
433-
return true;
419+
420+
if (refSnapshotUuids.isEmpty()) {
421+
return false;
434422
}
435423

436-
return false;
424+
return Q.New(VolumeSnapshotVO.class)
425+
.eq(VolumeSnapshotVO_.treeUuid, snapshotTreeUuid)
426+
.in(VolumeSnapshotVO_.uuid, refSnapshotUuids)
427+
.isExists();
437428
}
438429

439430
private static VolumeSnapshotReferenceVO getVolumeBackingRef(String volumeUuid) {

0 commit comments

Comments
 (0)