@@ -211,61 +211,155 @@ let with_vdis_on_error ~vdis f = try f () with e -> Error (e, vdis)
211211
212212let ( let@ ) f x = f x
213213
214- (* Copy the VBDs and VIFs from a source VM to a dest VM and then delete the old
215- disks. This operation destroys the data of the dest VM. *)
214+ (* Revert the VBDs of a VM to have the contents of the snapshot and copy the
215+ VIFs from a snapshot VM to the VM. This operation destroys the data of the
216+ dest VM. *)
217+
216218type cloned = {
217219 disks : ([`VBD ] Ref .t * API .ref_VDI * bool ) list
218220 ; cds : ([`VBD ] Ref .t * API .ref_VDI * bool ) list
219221 ; suspend_VDI : [`VDI ] Ref .t
220222}
221223
224+ module VDISet = Set. Make (struct
225+ type t = [`VDI ] Ref .t
226+
227+ let compare = Ref. compare
228+ end )
229+
230+ module VBDSet = Set. Make (struct
231+ type t = [`VBD ] Ref .t
232+
233+ let compare = Ref. compare
234+ end )
235+
222236let revert_vbds ~__context ~rpc ~session_id ~snapshot ~vm =
223- let snap_VBDs = Db.VM. get_VBDs ~__context ~self: snapshot in
224- let snap_VBDs_disk, snap_VBDs_CD =
225- List. partition
226- (fun vbd -> Db.VBD. get_type ~__context ~self: vbd = `Disk )
227- snap_VBDs
228- in
229- let snap_disks =
230- List. map (fun vbd -> Db.VBD. get_VDI ~__context ~self: vbd) snap_VBDs_disk
237+ let get_snapshot_of vdi = Db.VDI. get_snapshot_of ~__context ~self: vdi in
238+
239+ let disks_of_vbds vbds =
240+ vbds
241+ |> VBDSet. to_seq
242+ |> Seq. map (fun vbd -> Db.VBD. get_VDI ~__context ~self: vbd)
243+ |> VDISet. of_seq
231244 in
232- let snap_disks_snapshot_of =
233- List. map (fun vdi -> Db.VDI. get_snapshot_of ~__context ~self: vdi) snap_disks
245+
246+ let snap_VBDs_disk, snap_VBDs_CD =
247+ Db.VM. get_VBDs ~__context ~self: snapshot
248+ |> VBDSet. of_list
249+ |> VBDSet. partition (fun vbd -> Db.VBD. get_type ~__context ~self: vbd = `Disk )
234250 in
235251 let snap_suspend_VDI = Db.VM. get_suspend_VDI ~__context ~self: snapshot in
252+ let snap_disks_all = disks_of_vbds snap_VBDs_disk in
253+ let snap_disks_snapshot_of = VDISet. map get_snapshot_of snap_disks_all in
236254
237- let vm_VBDs = Db.VM. get_VBDs ~__context ~self: vm in
238- (* Filter VBDs to ensure that we don't read empty CDROMs *)
255+ let vm_VBDs_all = Db.VM. get_VBDs ~__context ~self: vm |> VBDSet. of_list in
239256 let vm_VBDs_disk =
240- List. filter
257+ (* Filter VBDs to ensure that we don't read empty CDROMs *)
258+ VBDSet. filter
241259 (fun vbd -> Db.VBD. get_type ~__context ~self: vbd = `Disk )
242- vm_VBDs
260+ vm_VBDs_all
261+ in
262+ let vm_disks_all = disks_of_vbds vm_VBDs_disk in
263+
264+ let vm_suspend_VDI =
265+ Db.VM. get_suspend_VDI ~__context ~self: vm |> VDISet. singleton
243266 in
244- (* Filter out VM disks for which the snapshot does not have a corresponding
245- disk - these disks will be left unattached after the revert is complete. *)
246- let vm_disks =
247- List. map (fun vbd -> Db.VBD. get_VDI ~__context ~self: vbd) vm_VBDs_disk
267+
268+ if VDISet. cardinal snap_disks_all <> 0 then
269+ debug " %s: trying to revert VDIs using VDI.revert" __FUNCTION__ ;
270+
271+ let snap_disks_reverted =
272+ VDISet. filter
273+ (fun snapshot ->
274+ try
275+ Client.VDI. revert ~rpc ~session_id ~snapshot ;
276+ true
277+ with _ -> false
278+ )
279+ snap_disks_all
248280 in
249- let vm_disks_with_snapshot =
250- List. filter (fun vdi -> List. mem vdi snap_disks_snapshot_of) vm_disks
281+
282+ let vm_disks_already_reverted =
283+ VDISet. map get_snapshot_of snap_disks_reverted
251284 in
252- let vm_suspend_VDI = Db.VM. get_suspend_VDI ~__context ~self: vm in
253285
254- debug " Cleaning up the old VBDs and VDIs to have more free space" ;
255- List. iter (safe_destroy_vbd ~__context ~rpc ~session_id ) vm_VBDs ;
256- List. iter
286+ let vm_disks_to_be_destroyed =
287+ let ( --- ) = VDISet. diff in
288+ let ( +++ ) = VDISet. union in
289+
290+ (* Disks without snapshot are left unattached after the revert is complete. *)
291+ let vm_disks_without_snapshot = vm_disks_all --- snap_disks_snapshot_of in
292+
293+ vm_disks_all
294+ --- vm_disks_without_snapshot
295+ --- vm_disks_already_reverted
296+ +++ vm_suspend_VDI
297+ in
298+
299+ let filter_vbds_from_vdis vbds vdis =
300+ vbds
301+ |> VBDSet. filter (fun vbd ->
302+ VDISet. mem (Db.VBD. get_VDI ~__context ~self: vbd) vdis
303+ )
304+ in
305+
306+ let vm_vbds_to_be_destroyed =
307+ filter_vbds_from_vdis vm_VBDs_all vm_disks_to_be_destroyed
308+ in
309+
310+ let snap_VBDs_reverted =
311+ filter_vbds_from_vdis snap_VBDs_disk snap_disks_reverted
312+ in
313+
314+ (*
315+ snap VBDs (disk)
316+ - snap VBDs from reverted disks
317+ = snap VBDs to be cloned
318+ *)
319+ let snap_VBDs_to_be_cloned =
320+ let ( --- ) = VBDSet. diff in
321+ snap_VBDs_disk --- snap_VBDs_reverted
322+ in
323+ if
324+ VBDSet. cardinal vm_vbds_to_be_destroyed <> 0
325+ || VDISet. cardinal vm_disks_to_be_destroyed <> 0
326+ then
327+ debug " %s: Cleaning up the old VBDs and VDIs to have more free space"
328+ __FUNCTION__ ;
329+ VBDSet. iter
330+ (safe_destroy_vbd ~__context ~rpc ~session_id )
331+ vm_vbds_to_be_destroyed ;
332+ VDISet. iter
257333 (safe_destroy_vdi ~__context ~rpc ~session_id )
258- (vm_suspend_VDI :: vm_disks_with_snapshot) ;
334+ vm_disks_to_be_destroyed ;
259335 TaskHelper. set_progress ~__context 0.2 ;
260- debug " Cloning the snapshotted disks" ;
336+ if VBDSet. cardinal snap_VBDs_to_be_cloned <> 0 then
337+ debug " %s: Cloning the snapshotted disks" __FUNCTION__ ;
338+
261339 let driver_params = Xapi_vm_clone. make_driver_params () in
262340 let cloned_disks =
263- Xapi_vm_clone. safe_clone_disks rpc session_id Xapi_vm_clone. Disk_op_clone
264- ~__context snap_VBDs_disk driver_params
341+ (* the list of cloned VDIs maintains the order of the VBDs given, use this
342+ to correlate the VDI with the original VDI before the clone *)
343+ let snap_VBDs_to_be_cloned =
344+ snap_VBDs_to_be_cloned |> VBDSet. to_seq |> List. of_seq
345+ in
346+ let snap_disks_previous_snapshot_of =
347+ snap_VBDs_to_be_cloned
348+ |> List. map (fun vbd ->
349+ let vdi = Db.VBD. get_VDI ~__context ~self: vbd in
350+ Db.VDI. get_snapshot_of ~__context ~self: vdi
351+ )
352+ in
353+
354+ let cloned =
355+ Xapi_vm_clone. safe_clone_disks rpc session_id Xapi_vm_clone. Disk_op_clone
356+ ~__context snap_VBDs_to_be_cloned driver_params
357+ in
358+ List. combine cloned snap_disks_previous_snapshot_of
265359 in
266360 let destroy_vdis_on_error =
267361 List. filter_map
268- (fun (_ , vdi , on_error_delete ) ->
362+ (fun (( _ , vdi , on_error_delete ), _ ) ->
269363 if on_error_delete then
270364 Some vdi
271365 else
@@ -275,20 +369,22 @@ let revert_vbds ~__context ~rpc ~session_id ~snapshot ~vm =
275369 in
276370 let @ () = with_vdis_on_error ~vdis: destroy_vdis_on_error in
277371 let cloned_CDs =
372+ let snap_VBDs_CD = snap_VBDs_CD |> VBDSet. to_seq |> List. of_seq in
278373 Xapi_vm_clone. safe_clone_disks rpc session_id Xapi_vm_clone. Disk_op_clone
279374 ~__context snap_VBDs_CD driver_params
280375 in
281376 TaskHelper. set_progress ~__context 0.5 ;
282- debug " Updating the snapshot_of fields for relevant VDIs" ;
283- List. iter2
284- (fun snap_disk (_ , cloned_disk , _ ) ->
377+ if cloned_disks <> [] then
378+ debug " %s: Updating the snapshot_of fields for relevant VDIs" __FUNCTION__ ;
379+
380+ List. iter
381+ (fun ((_ , cloned_disk , _ ), snapshot_of ) ->
285382 (* For each snapshot disk which was just cloned:
286383 1) Find the value of snapshot_of
287384 2) Find all snapshots with the same snapshot_of
288385 3) Update each of these snapshots so that their snapshot_of points
289386 to the new cloned disk. *)
290387 let open Xapi_database.Db_filter_types in
291- let snapshot_of = Db.VDI. get_snapshot_of ~__context ~self: snap_disk in
292388 let all_snaps_in_tree =
293389 Db.VDI. get_refs_where ~__context
294390 ~expr: (Eq (Field " snapshot_of" , Literal (Ref. string_of snapshot_of)))
@@ -299,7 +395,7 @@ let revert_vbds ~__context ~rpc ~session_id ~snapshot ~vm =
299395 )
300396 all_snaps_in_tree
301397 )
302- snap_disks cloned_disks ;
398+ cloned_disks ;
303399 debug " Cloning the suspend VDI if needed" ;
304400 let cloned_suspend_VDI =
305401 if snap_suspend_VDI = Ref. null then
@@ -310,12 +406,13 @@ let revert_vbds ~__context ~rpc ~session_id ~snapshot ~vm =
310406 in
311407 let destroy_vdis_on_error = cloned_suspend_VDI :: destroy_vdis_on_error in
312408 let @ () = with_vdis_on_error ~vdis: destroy_vdis_on_error in
409+ let vbds_to_copy = List. map fst cloned_disks @ cloned_CDs in
313410 TaskHelper. set_progress ~__context 0.6 ;
314- debug " Copying the VBDs" ;
411+ if vbds_to_copy <> [] then debug " %s: Copying the VBDs" __FUNCTION__ ;
315412 let (_ : [`VBD] Ref.t list ) =
316413 List. map
317414 (fun (vbd , vdi , _ ) -> Xapi_vbd_helpers. copy ~__context ~vm ~vdi vbd)
318- (cloned_disks @ cloned_CDs)
415+ vbds_to_copy
319416 in
320417 debug " Update the suspend_VDI" ;
321418 Db.VM. set_suspend_VDI ~__context ~self: vm ~value: cloned_suspend_VDI ;
0 commit comments