Skip to content

Commit ccff5f6

Browse files
committed
CP-312874 Reproduce a VM's snapshot chains one VDI at a time
A SMAPIv1 destination gets a VM's snapshot chains for free: each copy is based on whichever VDI already in the destination SR has the most similar content, which is the copy of the snapshot before it. SMAPIv3 backends report no similar content, so every VDI arrives as a full, unrelated copy and the snapshot structure is lost. Migration already copies a VM's VDIs one at a time, in a with_many fold, and DATA.copy_into now takes the destination VDI a copy should be based on. with_many hands each element the results of the elements before it, so naming the destination copy of the VDI a VDI follows in its disk's snapshot chain is enough to reproduce the chain. That parent comes from the VM snapshot tree: the disk of the nearest ancestor of the VM that has one, so snapshots on a branch the VM has reverted away from start chains of their own. It only works if a VDI is copied after the VDI it is based on, so same-sized VDIs are now ordered by a depth-first walk of the snapshot trees rather than by snapshot time. A disk's tree stays in one piece whatever else is attached to the VM, siblings go oldest first so that the branches a revert abandoned are copied before the branch the VM is still on, and the disk itself, the only child that is not a snapshot, comes last. Signed-off-by: Lunfan Zhang[Lunfan.Zhang] <Lunfan.Zhang@cloud.com>
1 parent a15649f commit ccff5f6

2 files changed

Lines changed: 283 additions & 38 deletions

File tree

ocaml/tests/test_vm_migrate.ml

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,134 @@ let test_infer_vif_map () =
7272
)
7373
)
7474

75+
let check_lineage_parent =
76+
Alcotest.(check (option (Alcotest_comparators.ref ())))
77+
78+
let make_disk ~__context ~vm =
79+
let vDI = Test_common.make_vdi ~__context () in
80+
ignore (Test_common.make_vbd ~__context ~vM:vm ~vDI ~_type:`Disk ()) ;
81+
vDI
82+
83+
(* A snapshot of [live_vm] taken from [parent], holding one snapshot VDI per
84+
disk, linked the way VM.snapshot links them. *)
85+
let make_snapshot ~__context ~live_vm ~parent ~disks =
86+
let snapshot = Test_common.make_vm ~__context ~name_label:"snapshot" () in
87+
Db.VM.set_is_a_snapshot ~__context ~self:snapshot ~value:true ;
88+
Db.VM.set_snapshot_of ~__context ~self:snapshot ~value:live_vm ;
89+
Db.VM.set_parent ~__context ~self:snapshot ~value:parent ;
90+
let snapshot_disks =
91+
List.map
92+
(fun disk ->
93+
let vDI = make_disk ~__context ~vm:snapshot in
94+
Db.VDI.set_is_a_snapshot ~__context ~self:vDI ~value:true ;
95+
Db.VDI.set_snapshot_of ~__context ~self:vDI ~value:disk ;
96+
vDI
97+
)
98+
disks
99+
in
100+
(snapshot, snapshot_disks)
101+
102+
(* [make_snapshot] for a VM whose only disk is [disk]. *)
103+
let make_snapshot_of_disk ~__context ~live_vm ~parent ~disk =
104+
match make_snapshot ~__context ~live_vm ~parent ~disks:[disk] with
105+
| snapshot, [snapshot_disk] ->
106+
(snapshot, snapshot_disk)
107+
| _ ->
108+
Alcotest.fail "expected one snapshot VDI per disk"
109+
110+
let test_lineage_parent_of_no_snapshots () =
111+
let __context = Test_common.make_test_database () in
112+
let vm = Test_common.make_vm ~__context () in
113+
let disk = make_disk ~__context ~vm in
114+
check_lineage_parent "a disk that has never been snapshotted starts a chain"
115+
None
116+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm ~vdi:disk)
117+
118+
let test_lineage_parent_of_linear_chain () =
119+
let __context = Test_common.make_test_database () in
120+
let vm = Test_common.make_vm ~__context () in
121+
let disk = make_disk ~__context ~vm in
122+
let snap1, snap1_disk =
123+
make_snapshot_of_disk ~__context ~live_vm:vm ~parent:Ref.null ~disk
124+
in
125+
let snap2, snap2_disk =
126+
make_snapshot_of_disk ~__context ~live_vm:vm ~parent:snap1 ~disk
127+
in
128+
Db.VM.set_parent ~__context ~self:vm ~value:snap2 ;
129+
check_lineage_parent "the oldest snapshot starts the chain" None
130+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm:snap1 ~vdi:snap1_disk) ;
131+
check_lineage_parent "the newest snapshot follows the oldest" (Some snap1_disk)
132+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm:snap2 ~vdi:snap2_disk) ;
133+
check_lineage_parent "the disk in use follows the newest snapshot"
134+
(Some snap2_disk)
135+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm ~vdi:disk)
136+
137+
let test_lineage_parent_of_branch_after_revert () =
138+
(* snap1 <- snap2, then a revert to snap1 and snap3 taken from there, which
139+
leaves snap2 on a branch of its own. *)
140+
let __context = Test_common.make_test_database () in
141+
let vm = Test_common.make_vm ~__context () in
142+
let disk = make_disk ~__context ~vm in
143+
let snap1, snap1_disk =
144+
make_snapshot_of_disk ~__context ~live_vm:vm ~parent:Ref.null ~disk
145+
in
146+
let snap2, snap2_disk =
147+
make_snapshot_of_disk ~__context ~live_vm:vm ~parent:snap1 ~disk
148+
in
149+
let snap3, snap3_disk =
150+
make_snapshot_of_disk ~__context ~live_vm:vm ~parent:snap1 ~disk
151+
in
152+
Db.VM.set_parent ~__context ~self:vm ~value:snap3 ;
153+
check_lineage_parent "the abandoned branch follows the revert point"
154+
(Some snap1_disk)
155+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm:snap2 ~vdi:snap2_disk) ;
156+
check_lineage_parent "the branch taken after the revert follows it too"
157+
(Some snap1_disk)
158+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm:snap3 ~vdi:snap3_disk) ;
159+
check_lineage_parent
160+
"the disk in use skips the branch it was reverted away from"
161+
(Some snap3_disk)
162+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm ~vdi:disk)
163+
164+
let test_lineage_parent_of_several_disks () =
165+
(* Both disks are snapshotted by the same VM, and each follows its own. *)
166+
let __context = Test_common.make_test_database () in
167+
let vm = Test_common.make_vm ~__context () in
168+
let disk1 = make_disk ~__context ~vm in
169+
let disk2 = make_disk ~__context ~vm in
170+
let snap, snap_disks =
171+
make_snapshot ~__context ~live_vm:vm ~parent:Ref.null ~disks:[disk1; disk2]
172+
in
173+
Db.VM.set_parent ~__context ~self:vm ~value:snap ;
174+
match snap_disks with
175+
| [snap_disk1; snap_disk2] ->
176+
check_lineage_parent "the first disk follows its own snapshot"
177+
(Some snap_disk1)
178+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm ~vdi:disk1) ;
179+
check_lineage_parent "the second disk follows its own snapshot"
180+
(Some snap_disk2)
181+
(Xapi_vm_migrate.lineage_parent_of ~__context ~vm ~vdi:disk2)
182+
| _ ->
183+
Alcotest.fail "expected one snapshot VDI per disk"
184+
75185
let test =
76186
[
77187
("test_infer_vif_map_empty", `Quick, test_infer_vif_map_empty)
78188
; ("test_infer_vif_map", `Quick, test_infer_vif_map)
189+
; ( "test_lineage_parent_of_no_snapshots"
190+
, `Quick
191+
, test_lineage_parent_of_no_snapshots
192+
)
193+
; ( "test_lineage_parent_of_linear_chain"
194+
, `Quick
195+
, test_lineage_parent_of_linear_chain
196+
)
197+
; ( "test_lineage_parent_of_branch_after_revert"
198+
, `Quick
199+
, test_lineage_parent_of_branch_after_revert
200+
)
201+
; ( "test_lineage_parent_of_several_disks"
202+
, `Quick
203+
, test_lineage_parent_of_several_disks
204+
)
79205
]

0 commit comments

Comments
 (0)