99import org .zstack .core .thread .ChainTask ;
1010import org .zstack .core .thread .SyncTaskChain ;
1111import org .zstack .core .thread .ThreadFacade ;
12+ import org .zstack .core .trash .StorageTrash ;
1213import org .zstack .core .workflow .FlowChainBuilder ;
1314import org .zstack .header .core .Completion ;
1415import org .zstack .header .core .workflow .*;
1920import org .zstack .header .storage .primary .GetVolumeBackingChainFromPrimaryStorageMsg ;
2021import org .zstack .header .storage .primary .GetVolumeBackingChainFromPrimaryStorageReply ;
2122import org .zstack .header .storage .primary .PrimaryStorageConstant ;
23+ import org .zstack .header .storage .snapshot .VolumeSnapshotVO ;
2224import org .zstack .header .storage .snapshot .reference .DeleteVolumeSnapshotReferenceLeafMsg ;
2325import org .zstack .header .storage .snapshot .reference .DeleteVolumeSnapshotReferenceLeafReply ;
2426import 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}
0 commit comments