-
Notifications
You must be signed in to change notification settings - Fork 288
/
Copy pathxapi_vm_migrate.ml
1349 lines (1200 loc) · 59.9 KB
/
xapi_vm_migrate.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(*
* Copyright (C) 2006-2009 Citrix Systems Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*)
(**
* @group Virtual-Machine Management
*)
(** We only currently support within-pool live or dead migration.
Unfortunately in the cross-pool case, two hosts must share the same SR and
co-ordinate tapdisk locking. We have not got code for this.
*)
open Stdext
open Pervasiveext
open Printf
open Threadext
module DD=Debug.Make(struct let name="xapi" end)
open DD
module SMPERF=Debug.Make(struct let name="SMPERF" end)
open Client
open Xmlrpc_client
exception VGPU_mapping of string
let _sm = "SM"
let _xenops = "xenops"
let _host = "host"
let _session_id = "session_id"
let _master = "master"
type remote = {
rpc : Rpc.call -> Rpc.response;
session : API.ref_session;
sm_url : string;
xenops_url : string;
master_url : string;
remote_ip : string; (* IP address *)
remote_master_ip : string; (* IP address *)
dest_host : API.ref_host;
}
let get_ip_from_url url =
match Http.Url.of_string url with
| Http.Url.Http { Http.Url.host = host }, _ -> host
| _, _ -> failwith (Printf.sprintf "Cannot extract foreign IP address from: %s" url)
let remote_of_dest dest =
let master_url = List.assoc _master dest in
let xenops_url = List.assoc _xenops dest in
let session_id = Ref.of_string (List.assoc _session_id dest) in
let remote_ip = get_ip_from_url xenops_url in
let remote_master_ip = get_ip_from_url master_url in
let dest_host_string = List.assoc _host dest in
let dest_host = Ref.of_string dest_host_string in
let rpc = Helpers.make_remote_rpc remote_master_ip in
let sm_url = List.assoc _sm dest in
{
rpc = rpc;
session = session_id;
sm_url = sm_url;
xenops_url = xenops_url;
master_url = master_url;
remote_ip = remote_ip;
remote_master_ip = remote_master_ip;
dest_host = dest_host;
}
let number = ref 0
let nmutex = Mutex.create ()
let with_migrate f =
Mutex.execute nmutex (fun () ->
if !number = 3 then raise (Api_errors.Server_error (Api_errors.too_many_storage_migrates,["3"]));
incr number);
finally f (fun () ->
Mutex.execute nmutex (fun () ->
decr number))
module XenAPI = Client
module SMAPI = Storage_interface.Client(struct let rpc call = Storage_migrate.rpc ~srcstr:"xapi" ~dststr:"smapiv2" (Storage_migrate.local_url ()) call end)
open Storage_interface
open Listext
open Fun
let assert_sr_support_operations ~__context ~vdi_map ~remote ~ops =
let op_supported_on_source_sr vdi ops =
(* Check VDIs must not be present on SR which doesn't have required capability *)
let source_sr = Db.VDI.get_SR ~__context ~self:vdi in
let sr_record = Db.SR.get_record_internal ~__context ~self:source_sr in
let sr_features = Xapi_sr_operations.features_of_sr ~__context sr_record in
if not (List.for_all (fun op -> Smint.(has_capability op sr_features)) ops) then
raise (Api_errors.Server_error(Api_errors.sr_does_not_support_migration, [Ref.string_of source_sr]));
in
let op_supported_on_dest_sr sr ops sm_record remote =
(* Check VDIs must not be mirrored to SR which doesn't have required capability *)
let sr_type = XenAPI.SR.get_type remote.rpc remote.session sr in
let sm_capabilities =
match List.filter (fun (_, r) -> r.API.sM_type = sr_type) sm_record with
| [ _, plugin ] -> plugin.API.sM_capabilities
| _ -> []
in
if not (List.for_all (fun op -> List.mem Smint.(string_of_capability op) sm_capabilities) ops) then
raise (Api_errors.Server_error(Api_errors.sr_does_not_support_migration, [Ref.string_of sr]))
in
let is_sr_matching local_vdi_ref remote_sr_ref =
let source_sr_ref = Db.VDI.get_SR ~__context ~self:local_vdi_ref in
(* relax_xsm_sr_check is used to enable XSM to out-of-pool SRs with matching UUID *)
if !Xapi_globs.relax_xsm_sr_check then
begin
let source_sr_uuid = Db.SR.get_uuid ~__context ~self:source_sr_ref in
let dest_sr_uuid = XenAPI.SR.get_uuid remote.rpc remote.session remote_sr_ref in
dest_sr_uuid = source_sr_uuid
end
else
(* Don't fail if source and destination SR for all VDIs are same *)
source_sr_ref = remote_sr_ref
in
(* Get destination host SM record *)
let sm_record = XenAPI.SM.get_all_records remote.rpc remote.session in
List.filter (fun (vdi,sr) -> not (is_sr_matching vdi sr)) vdi_map
|> List.iter (fun (vdi, sr) ->
op_supported_on_source_sr vdi ops;
op_supported_on_dest_sr sr ops sm_record remote;
)
(** Check that none of the VDIs that are mapped to a different SR have CBT
enabled. This function must be called with the complete [vdi_map],
which contains all the VDIs of the VM.
[check_vdi_map] should be called before this function to verify that this
is the case. *)
let assert_no_cbt_enabled_vdi_migrated ~__context ~vdi_map =
List.iter (fun (vdi, target_sr) ->
if (Db.VDI.get_cbt_enabled ~__context ~self:vdi) then begin
if (target_sr <> (Db.VDI.get_SR ~__context ~self:vdi)) then
raise Api_errors.(Server_error(vdi_cbt_enabled, [Ref.string_of vdi]))
end
) vdi_map
let assert_licensed_storage_motion ~__context =
Pool_features.assert_enabled ~__context ~f:Features.Storage_motion
let rec migrate_with_retries ~__context queue_name max try_no dbg vm_uuid xenops_vdi_map xenops_vif_map xenops_vgpu_map xenops =
let open Xapi_xenops_queue in
let module Client = (val make_client queue_name: XENOPS) in
let progress = ref "(none yet)" in
let f () =
progress := "Client.VM.migrate";
let t1 = Client.VM.migrate dbg vm_uuid xenops_vdi_map xenops_vif_map xenops_vgpu_map xenops in
progress := "sync_with_task";
ignore (Xapi_xenops.sync_with_task __context queue_name t1)
in
if try_no >= max then
f ()
else begin
try f ()
(* CA-86347 Handle the excn if the VM happens to reboot during migration.
* Such a reboot causes Xenops_interface.Cancelled the first try, then
* Xenops_interface.Internal_error("End_of_file") the second, then success. *)
with
(* User cancelled migration *)
| Xenops_interface.Cancelled _ as e when TaskHelper.is_cancelling ~__context ->
debug "xenops: Migration cancelled by user.";
raise e
(* VM rebooted during migration - first raises Cancelled, then Internal_error "End_of_file" *)
| Xenops_interface.Cancelled _
| Xenops_interface.Internal_error "End_of_file" as e ->
debug "xenops: will retry migration: caught %s from %s in attempt %d of %d."
(Printexc.to_string e) !progress try_no max;
migrate_with_retries ~__context queue_name max (try_no + 1) dbg vm_uuid xenops_vdi_map xenops_vif_map xenops_vgpu_map xenops
(* Something else went wrong *)
| e ->
debug "xenops: not retrying migration: caught %s from %s in attempt %d of %d."
(Printexc.to_string e) !progress try_no max;
raise e
end
let migrate_with_retry ~__context queue_name =
migrate_with_retries ~__context queue_name 3 1
(** detach the network of [vm] if it is migrating away to [destination] *)
let detach_local_network_for_vm ~__context ~vm ~destination =
let src, dst = Helpers.get_localhost ~__context, destination in
let ref = Ref.string_of in
if src <> dst then begin
info "VM %s migrated from %s to %s - detaching VM's network at source"
(ref vm) (ref src) (ref dst);
Xapi_network.detach_for_vm ~__context ~host:src ~vm;
end (* else: localhost migration - nothing to do *)
(** Return a map of vGPU device to PCI address of the destination pGPU.
* This only works _after_ resources on the destination have been reserved
* by a call to Message_forwarding.VM.allocate_vm_to_host (through
* Host.allocate_resources_for_vm for cross-pool migrations).
*
* We are extra careful to check that the VM has a valid pGPU assigned.
* During migration, a VM may suspend or shut down and if this happens
* the pGPU is released and getting the PCI will fail.
* *)
let infer_vgpu_map ~__context ?remote vm =
match remote with
| None ->
let vgpus = Db.VM.get_VGPUs ~__context ~self:vm in
List.map (fun self ->
let vgpu = Db.VGPU.get_record ~__context ~self in
let device = vgpu.API.vGPU_device in
let pci () =
vgpu.API.vGPU_scheduled_to_be_resident_on
|> fun self -> Db.PGPU.get_PCI ~__context ~self
|> fun self -> Db.PCI.get_pci_id ~__context ~self
|> Xenops_interface.Pci.address_of_string
in
try
device, pci ()
with e -> raise (VGPU_mapping(Printexc.to_string e))
) vgpus
| Some {rpc; session} ->
let vgpus = XenAPI.VM.get_VGPUs rpc session vm in
List.map (fun self ->
let vgpu = XenAPI.VGPU.get_record rpc session self in
let device = vgpu.API.vGPU_device in
let pci () =
vgpu.API.vGPU_scheduled_to_be_resident_on
|> fun self -> XenAPI.PGPU.get_PCI rpc session self
|> fun self -> XenAPI.PCI.get_pci_id rpc session self
|> Xenops_interface.Pci.address_of_string
in
try
device, pci ()
with e -> raise (VGPU_mapping(Printexc.to_string e))
) vgpus
let pool_migrate ~__context ~vm ~host ~options =
Pool_features.assert_enabled ~__context ~f:Features.Xen_motion;
let dbg = Context.string_of_task __context in
let open Xapi_xenops_queue in
let queue_name = queue_of_vm ~__context ~self:vm in
let module XenopsAPI = (val make_client queue_name : XENOPS) in
let session_id = Ref.string_of (Context.get_session_id __context) in
let ip = Http.Url.maybe_wrap_IPv6_literal (Db.Host.get_address ~__context ~self:host) in
let xenops_url = Printf.sprintf "http://%s/services/xenops?session_id=%s" ip session_id in
let vm_uuid = Db.VM.get_uuid ~__context ~self:vm in
let xenops_vgpu_map = infer_vgpu_map ~__context vm in
(* Check pGPU compatibility for Nvidia vGPUs - at this stage we already know
* the vgpu <-> pgpu mapping. *)
Db.VM.get_VGPUs ~__context ~self:vm
|> List.map
(fun vgpu ->
vgpu, Db.VGPU.get_scheduled_to_be_resident_on ~__context ~self:vgpu)
|> List.iter (fun (vgpu, pgpu) ->
Xapi_pgpu_helpers.assert_destination_pgpu_is_compatible_with_vm ~__context
~vm ~host ~vgpu ~pgpu ()
);
Xapi_xenops.Events_from_xenopsd.with_suppressed queue_name dbg vm_uuid (fun () ->
try
Xapi_network.with_networks_attached_for_vm ~__context ~vm ~host (fun () ->
(* XXX: PR-1255: the live flag *)
info "xenops: VM.migrate %s to %s" vm_uuid xenops_url;
migrate_with_retry ~__context queue_name dbg vm_uuid [] [] xenops_vgpu_map xenops_url;
(* Delete all record of this VM locally (including caches) *)
Xapi_xenops.Xenopsd_metadata.delete ~__context vm_uuid;
);
Rrdd_proxy.migrate_rrd ~__context ~vm_uuid ~host_uuid:(Ref.string_of host) ();
detach_local_network_for_vm ~__context ~vm ~destination:host;
Helpers.call_api_functions ~__context (fun rpc session_id ->
XenAPI.VM.pool_migrate_complete rpc session_id vm host
);
with exn ->
error "xenops: VM.migrate %s: caught %s" vm_uuid (Printexc.to_string exn);
(* We do our best to tidy up the state left behind *)
begin
try
let _, state = XenopsAPI.VM.stat dbg vm_uuid in
if Xenops_interface.(state.Vm.power_state = Suspended) then begin
debug "xenops: %s: shutting down suspended VM" vm_uuid;
Xapi_xenops.shutdown ~__context ~self:vm None;
end;
with _ -> ()
end;
match exn with
| Xenops_interface.Failed_to_acknowledge_shutdown_request ->
raise Api_errors.(Server_error (vm_failed_shutdown_ack, [Ref.string_of vm]))
| Xenops_interface.Cancelled _ ->
TaskHelper.raise_cancelled ~__context
| Xenops_interface.Storage_backend_error (code, _) ->
raise Api_errors.(Server_error (sr_backend_failure, [code]))
| Xenops_interface.Ballooning_timeout_before_migration ->
raise Api_errors.(Server_error (ballooning_timeout_before_migration, [Ref.string_of vm]))
| _ -> raise exn
)
let pool_migrate_complete ~__context ~vm ~host =
let id = Db.VM.get_uuid ~__context ~self:vm in
debug "VM.pool_migrate_complete %s" id;
let dbg = Context.string_of_task __context in
let queue_name = Xapi_xenops_queue.queue_of_vm ~__context ~self:vm in
if Xapi_xenops.vm_exists_in_xenopsd queue_name dbg id then begin
Cpuid_helpers.update_cpu_flags ~__context ~vm ~host;
Xapi_xenops.set_resident_on ~__context ~self:vm;
Xapi_xenops.add_caches id;
Xapi_xenops.refresh_vm ~__context ~self:vm;
Monitor_dbcalls_cache.clear_cache_for_vm ~vm_uuid:id
end
type mirror_record = {
mr_mirrored : bool;
mr_dp : Storage_interface.dp option;
mr_local_sr : Storage_interface.sr;
mr_local_vdi : Storage_interface.vdi;
mr_remote_sr : Storage_interface.sr;
mr_remote_vdi : Storage_interface.vdi;
mr_local_xenops_locator : string;
mr_remote_xenops_locator : string;
mr_remote_vdi_reference : API.ref_VDI;
mr_local_vdi_reference : API.ref_VDI;
}
type vdi_transfer_record = {
local_vdi_reference : API.ref_VDI;
remote_vdi_reference : API.ref_VDI option;
}
type vif_transfer_record = {
local_vif_reference : API.ref_VIF;
remote_network_reference : API.ref_network;
}
type vgpu_transfer_record = {
local_vgpu_reference : API.ref_VGPU;
remote_gpu_group_reference : API.ref_GPU_group;
}
(* If VM's suspend_SR is set to the local SR, it won't be visible to
the destination host after an intra-pool storage migrate *)
let intra_pool_fix_suspend_sr ~__context host vm =
let sr = Db.VM.get_suspend_SR ~__context ~self:vm in
if not (Helpers.host_has_pbd_for_sr ~__context ~host ~sr)
then Db.VM.set_suspend_SR ~__context ~self:vm ~value:Ref.null
let intra_pool_vdi_remap ~__context vm vdi_map =
let vbds = Db.VM.get_VBDs ~__context ~self:vm in
let vdis_and_callbacks = List.map (fun vbd ->
let vdi = Db.VBD.get_VDI ~__context ~self:vbd in
let callback mapto =
Db.VBD.set_VDI ~__context ~self:vbd ~value:mapto;
let other_config_record = Db.VDI.get_other_config ~__context ~self:vdi in
List.iter (fun key ->
Db.VDI.remove_from_other_config ~__context ~self:mapto ~key;
try Db.VDI.add_to_other_config ~__context ~self:mapto ~key ~value:(List.assoc key other_config_record) with Not_found -> ()
) Xapi_globs.vdi_other_config_sync_keys in
vdi, callback
) vbds in
let suspend_vdi = Db.VM.get_suspend_VDI ~__context ~self:vm in
let vdis_and_callbacks =
if suspend_vdi = Ref.null then vdis_and_callbacks
else (suspend_vdi, fun v -> Db.VM.set_suspend_VDI ~__context ~self:vm ~value:v) :: vdis_and_callbacks in
List.iter
(fun (vdi,callback) ->
try
let mirror_record = List.find (fun mr -> mr.mr_local_vdi_reference = vdi) vdi_map in
callback mirror_record.mr_remote_vdi_reference
with
Not_found -> ())
vdis_and_callbacks
let inter_pool_metadata_transfer ~__context ~remote ~vm ~vdi_map ~vif_map ~vgpu_map ~dry_run ~live ~copy =
List.iter (fun vdi_record ->
let vdi = vdi_record.local_vdi_reference in
Db.VDI.remove_from_other_config ~__context ~self:vdi
~key:Constants.storage_migrate_vdi_map_key;
Opt.iter (fun remote_vdi_reference ->
Db.VDI.add_to_other_config ~__context ~self:vdi
~key:Constants.storage_migrate_vdi_map_key
~value:(Ref.string_of remote_vdi_reference))
vdi_record.remote_vdi_reference) vdi_map;
List.iter (fun vif_record ->
let vif = vif_record.local_vif_reference in
Db.VIF.remove_from_other_config ~__context ~self:vif
~key:Constants.storage_migrate_vif_map_key;
Db.VIF.add_to_other_config ~__context ~self:vif
~key:Constants.storage_migrate_vif_map_key
~value:(Ref.string_of vif_record.remote_network_reference)) vif_map;
List.iter (fun vgpu_record ->
let vgpu = vgpu_record.local_vgpu_reference in
Db.VGPU.remove_from_other_config ~__context ~self:vgpu
~key:Constants.storage_migrate_vgpu_map_key;
Db.VGPU.add_to_other_config ~__context ~self:vgpu
~key:Constants.storage_migrate_vgpu_map_key
~value:(Ref.string_of vgpu_record.remote_gpu_group_reference)) vgpu_map;
let vm_export_import = {
Importexport.vm = vm; dry_run = dry_run; live = live; send_snapshots = not copy;
} in
finally
(fun () ->
Importexport.remote_metadata_export_import ~__context
~rpc:remote.rpc ~session_id:remote.session ~remote_address:remote.remote_ip ~restore:(not copy) (`Only vm_export_import))
(fun () ->
(* Make sure we clean up the remote VDI and VIF mapping keys. *)
List.iter
(fun vdi_record ->
Db.VDI.remove_from_other_config ~__context
~self:vdi_record.local_vdi_reference
~key:Constants.storage_migrate_vdi_map_key)
vdi_map;
List.iter
(fun vif_record ->
Db.VIF.remove_from_other_config ~__context
~self:vif_record.local_vif_reference
~key:Constants.storage_migrate_vif_map_key)
vif_map;
List.iter
(fun vgpu_record ->
Db.VGPU.remove_from_other_config ~__context
~self:vgpu_record.local_vgpu_reference
~key:Constants.storage_migrate_vgpu_map_key)
vgpu_map;)
module VDIMap = Map.Make(struct type t = API.ref_VDI let compare = compare end)
let update_snapshot_info ~__context ~dbg ~url ~vdi_map ~snapshots_map =
(* Construct a map of type:
* API.ref_VDI -> (mirror_record, (API.ref_VDI * mirror_record) list)
*
* Each VDI is mapped to its own mirror record, as well as a list of
* all its snapshots and their mirror records. *)
let empty_vdi_map =
(* Add the VDIs to the map along with empty lists of snapshots. *)
List.fold_left
(fun acc mirror -> VDIMap.add mirror.mr_local_vdi_reference (mirror, []) acc)
VDIMap.empty vdi_map
in
let vdi_to_snapshots_map =
(* Add the snapshots to the map. *)
List.fold_left
(fun acc snapshot_mirror ->
let snapshot_ref = snapshot_mirror.mr_local_vdi_reference in
let snapshot_of = Db.VDI.get_snapshot_of ~__context ~self:snapshot_ref in
try
let (mirror, snapshots) = VDIMap.find snapshot_of acc in
VDIMap.add
snapshot_of
(mirror, (snapshot_ref, snapshot_mirror) :: snapshots)
acc
with Not_found -> begin
warn
"Snapshot %s is in the snapshot_map; corresponding VDI %s is not in the vdi_map"
(Ref.string_of snapshot_ref) (Ref.string_of snapshot_of);
acc
end)
empty_vdi_map snapshots_map
in
(* Build the snapshot chain for each leaf VDI.
* Best-effort in case we're talking to an old SMAPI. *)
try
VDIMap.iter
(fun vdi_ref (mirror, snapshots) ->
let sr = mirror.mr_local_sr in
let vdi = mirror.mr_local_vdi in
let dest = mirror.mr_remote_sr in
let dest_vdi = mirror.mr_remote_vdi in
let snapshot_pairs =
List.map
(fun (local_snapshot_ref, snapshot_mirror) ->
Db.VDI.get_uuid ~__context ~self:local_snapshot_ref,
snapshot_mirror.mr_remote_vdi)
snapshots
in
SMAPI.SR.update_snapshot_info_src
~dbg ~sr ~vdi ~url ~dest ~dest_vdi ~snapshot_pairs)
vdi_to_snapshots_map
with Storage_interface.Unknown_RPC call ->
debug "Remote SMAPI doesn't implement %s - ignoring" call
type vdi_mirror = {
vdi : [ `VDI ] API.Ref.t; (* The API reference of the local VDI *)
dp : string; (* The datapath the VDI will be using if the VM is running *)
location : string; (* The location of the VDI in the current SR *)
sr : string; (* The VDI's current SR uuid *)
xenops_locator : string; (* The 'locator' xenops uses to refer to the VDI on the current host *)
size : Int64.t; (* Size of the VDI *)
snapshot_of : [ `VDI ] API.Ref.t; (* API's snapshot_of reference *)
do_mirror : bool; (* Whether we should mirror or just copy the VDI *)
}
(* For VMs (not snapshots) xenopsd does not allow remapping, so we
eject CDs where possible. This function takes a set of VBDs,
and filters to find those that should be ejected prior to the
SXM operation *)
let find_cds_to_eject __context vdi_map vbds =
(* Only consider CDs *)
let cd_vbds = List.filter (fun vbd -> Db.VBD.get_type ~__context ~self:vbd = `CD) vbds in
(* Only consider VMs (not snapshots) *)
let vm_cds = List.filter (fun vbd ->
let vm = Db.VBD.get_VM ~__context ~self:vbd in
not (Db.VM.get_is_a_snapshot ~__context ~self:vm)) cd_vbds in
(* Only consider moving CDs - no need to eject if they're staying in the same SR *)
let moving_cds = List.filter (fun vbd ->
let vdi = Db.VBD.get_VDI ~__context ~self:vbd in
try
let current_sr = Db.VDI.get_SR ~__context ~self:vdi in
let dest_sr = try List.assoc vdi vdi_map with _ -> Ref.null in
current_sr <> dest_sr
with Db_exn.DBCache_NotFound _ -> (* Catch the case where the VDI reference is invalid (e.g. empty CD) *)
false) vm_cds in
(* Only consider VMs that aren't suspended - we can't eject a suspended VM's CDs at the API level *)
let ejectable_cds = List.filter (fun vbd ->
let vm = Db.VBD.get_VM ~__context ~self:vbd in
Db.VM.get_power_state ~__context ~self:vm <> `Suspended) moving_cds in
ejectable_cds
let eject_cds __context cd_vbds =
Helpers.call_api_functions
~__context
(fun rpc session_id ->
List.iter (fun vbd -> XenAPI.VBD.eject ~rpc ~session_id ~vbd) cd_vbds)
(* Gather together some important information when mirroring VDIs *)
let get_vdi_mirror __context vm vdi do_mirror =
let snapshot_of = Db.VDI.get_snapshot_of ~__context ~self:vdi in
let size = Db.VDI.get_virtual_size ~__context ~self:vdi in
let xenops_locator = Xapi_xenops.xenops_vdi_locator ~__context ~self:vdi in
let location = Db.VDI.get_location ~__context ~self:vdi in
let dp = Storage_access.presentative_datapath_of_vbd ~__context ~vm ~vdi in
let sr = Db.SR.get_uuid ~__context ~self:(Db.VDI.get_SR ~__context ~self:vdi) in
{vdi; dp; location; sr; xenops_locator; size; snapshot_of; do_mirror}
(* We ignore empty or CD VBDs - nothing to do there. Possible redundancy here:
I don't think any VBDs other than CD VBDs can be 'empty' *)
let vdi_filter __context allow_mirror vbd =
if Db.VBD.get_empty ~__context ~self:vbd || Db.VBD.get_type ~__context ~self:vbd = `CD
then None
else
let do_mirror = allow_mirror && (Db.VBD.get_mode ~__context ~self:vbd = `RW) in
let vm = Db.VBD.get_VM ~__context ~self:vbd in
let vdi = Db.VBD.get_VDI ~__context ~self:vbd in
Some (get_vdi_mirror __context vm vdi do_mirror)
let vdi_copy_fun __context dbg vdi_map remote is_intra_pool remote_vdis so_far total_size copy vconf continuation =
TaskHelper.exn_if_cancelling ~__context;
let open Storage_access in
let dest_sr_ref = List.assoc vconf.vdi vdi_map in
let dest_sr_uuid = XenAPI.SR.get_uuid remote.rpc remote.session dest_sr_ref in
(* Plug the destination shared SR into destination host and pool master if unplugged.
Plug the local SR into destination host only if unplugged *)
let dest_pool = List.hd (XenAPI.Pool.get_all remote.rpc remote.session) in
let master_host = XenAPI.Pool.get_master remote.rpc remote.session dest_pool in
let pbds = XenAPI.SR.get_PBDs remote.rpc remote.session dest_sr_ref in
let pbd_host_pair = List.map (fun pbd -> (pbd, XenAPI.PBD.get_host remote.rpc remote.session pbd)) pbds in
let hosts_to_be_attached = [master_host; remote.dest_host] in
let pbds_to_be_plugged = List.filter (fun (_, host) ->
(List.mem host hosts_to_be_attached) && (XenAPI.Host.get_enabled remote.rpc remote.session host)) pbd_host_pair in
List.iter (fun (pbd, _) ->
if not (XenAPI.PBD.get_currently_attached remote.rpc remote.session pbd) then
XenAPI.PBD.plug remote.rpc remote.session pbd) pbds_to_be_plugged;
let rec dest_vdi_exists_on_sr vdi_uuid sr_ref retry =
try
let dest_vdi_ref = XenAPI.VDI.get_by_uuid remote.rpc remote.session vdi_uuid in
let dest_vdi_sr_ref = XenAPI.VDI.get_SR remote.rpc remote.session dest_vdi_ref in
if dest_vdi_sr_ref = sr_ref then
true
else
false
with _ ->
if retry then
begin
XenAPI.SR.scan remote.rpc remote.session sr_ref;
dest_vdi_exists_on_sr vdi_uuid sr_ref false
end
else
false
in
(* CP-4498 added an unsupported mode to use cross-pool shared SRs - the initial
use case is for a shared raw iSCSI SR (same uuid, same VDI uuid) *)
let vdi_uuid = Db.VDI.get_uuid ~__context ~self:vconf.vdi in
let mirror = if !Xapi_globs.relax_xsm_sr_check then
if (dest_sr_uuid = vconf.sr) then
begin
(* Check if the VDI uuid already exists in the target SR *)
if (dest_vdi_exists_on_sr vdi_uuid dest_sr_ref true) then
false
else
failwith ("SR UUID matches on destination but VDI does not exist")
end
else
true
else
(not is_intra_pool) || (dest_sr_uuid <> vconf.sr)
in
let with_new_dp cont =
let dp = Printf.sprintf (if vconf.do_mirror then "mirror_%s" else "copy_%s") vconf.dp in
try cont dp
with e ->
(try SMAPI.DP.destroy ~dbg ~dp ~allow_leak:false with _ -> info "Failed to cleanup datapath: %s" dp);
raise e in
let with_remote_vdi remote_vdi cont =
debug "Executing remote scan to ensure VDI is known to xapi";
XenAPI.SR.scan remote.rpc remote.session dest_sr_ref;
let query = Printf.sprintf "(field \"location\"=\"%s\") and (field \"SR\"=\"%s\")" remote_vdi (Ref.string_of dest_sr_ref) in
let vdis = XenAPI.VDI.get_all_records_where remote.rpc remote.session query in
let remote_vdi_ref = match vdis with
| [] -> raise (Api_errors.Server_error(Api_errors.vdi_location_missing, [Ref.string_of dest_sr_ref; remote_vdi]))
| h :: [] -> debug "Found remote vdi reference: %s" (Ref.string_of (fst h)); fst h
| _ -> raise (Api_errors.Server_error(Api_errors.location_not_unique, [Ref.string_of dest_sr_ref; remote_vdi])) in
try cont remote_vdi_ref
with e ->
(try XenAPI.VDI.destroy remote.rpc remote.session remote_vdi_ref with _ -> error "Failed to destroy remote VDI");
raise e in
let get_mirror_record ?new_dp remote_vdi remote_vdi_reference =
{ mr_dp = new_dp;
mr_mirrored = mirror;
mr_local_sr = vconf.sr;
mr_local_vdi = vconf.location;
mr_remote_sr = dest_sr_uuid;
mr_remote_vdi = remote_vdi;
mr_local_xenops_locator = vconf.xenops_locator;
mr_remote_xenops_locator = Xapi_xenops.xenops_vdi_locator_of_strings dest_sr_uuid remote_vdi;
mr_local_vdi_reference = vconf.vdi;
mr_remote_vdi_reference = remote_vdi_reference } in
let mirror_to_remote new_dp =
let task =
if not vconf.do_mirror then
SMAPI.DATA.copy ~dbg ~sr:vconf.sr ~vdi:vconf.location ~dp:new_dp ~url:remote.sm_url ~dest:dest_sr_uuid
else begin
(* Though we have no intention of "write", here we use the same mode as the
associated VBD on a mirrored VDIs (i.e. always RW). This avoids problem
when we need to start/stop the VM along the migration. *)
let read_write = true in
(* DP set up is only essential for MIRROR.start/stop due to their open ended pattern.
It's not necessary for copy which will take care of that itself. *)
ignore(SMAPI.VDI.attach2 ~dbg ~dp:new_dp ~sr:vconf.sr ~vdi:vconf.location ~read_write);
SMAPI.VDI.activate ~dbg ~dp:new_dp ~sr:vconf.sr ~vdi:vconf.location;
ignore(Storage_access.register_mirror __context vconf.location);
SMAPI.DATA.MIRROR.start ~dbg ~sr:vconf.sr ~vdi:vconf.location ~dp:new_dp ~url:remote.sm_url ~dest:dest_sr_uuid
end in
let mapfn x =
let total = Int64.to_float total_size in
let done_ = Int64.to_float !so_far /. total in
let remaining = Int64.to_float vconf.size /. total in
done_ +. x *. remaining in
let open Storage_access in
let task_result =
task |> register_task __context
|> add_to_progress_map mapfn
|> wait_for_task dbg
|> remove_from_progress_map
|> unregister_task __context
|> success_task dbg in
let mirror_id, remote_vdi =
if not vconf.do_mirror then
let vdi = task_result |> vdi_of_task dbg in
remote_vdis := vdi.vdi :: !remote_vdis;
None, vdi.vdi
else
let mirrorid = task_result |> mirror_of_task dbg in
let m = SMAPI.DATA.MIRROR.stat ~dbg ~id:mirrorid in
Some mirrorid, m.Mirror.dest_vdi in
so_far := Int64.add !so_far vconf.size;
debug "Local VDI %s %s to %s" vconf.location (if vconf.do_mirror then "mirrored" else "copied") remote_vdi;
mirror_id, remote_vdi in
let post_mirror mirror_id mirror_record =
try
let result = continuation mirror_record in
(match mirror_id with
| Some mid -> ignore(Storage_access.unregister_mirror mid);
| None -> ());
if mirror && not (Xapi_fist.storage_motion_keep_vdi () || copy) then
Helpers.call_api_functions ~__context (fun rpc session_id ->
XenAPI.VDI.destroy rpc session_id vconf.vdi);
result
with e ->
let mirror_failed =
match mirror_id with
| Some mid ->
ignore(Storage_access.unregister_mirror mid);
let m = SMAPI.DATA.MIRROR.stat ~dbg ~id:mid in
(try SMAPI.DATA.MIRROR.stop ~dbg ~id:mid with _ -> ());
m.Mirror.failed
| None -> false in
if mirror_failed then raise (Api_errors.Server_error(Api_errors.mirror_failed,[Ref.string_of vconf.vdi]))
else raise e in
if mirror then
with_new_dp (fun new_dp ->
let mirror_id, remote_vdi = mirror_to_remote new_dp in
with_remote_vdi remote_vdi (fun remote_vdi_ref ->
let mirror_record = get_mirror_record ~new_dp remote_vdi remote_vdi_ref in
post_mirror mirror_id mirror_record))
else
let mirror_record = get_mirror_record vconf.location (XenAPI.VDI.get_by_uuid remote.rpc remote.session vdi_uuid) in
continuation mirror_record
let wait_for_fist __context fistpoint name =
if fistpoint () then begin
TaskHelper.add_to_other_config ~__context "fist" name;
while fistpoint () do
debug "Sleeping while fistpoint exists";
Thread.delay 5.0;
done;
TaskHelper.operate_on_db_task ~__context
(fun self ->
Db_actions.DB_Action.Task.remove_from_other_config ~__context ~self ~key:"fist")
end
(* Helper function to apply a 'with_x' function to a list *)
let rec with_many withfn many fn =
let rec inner l acc =
match l with
| [] -> fn acc
| x::xs -> withfn x (fun y -> inner xs (y::acc))
in inner many []
(* Generate a VIF->Network map from vif_map and implicit mappings *)
let infer_vif_map ~__context vifs vif_map =
let mapped_macs =
List.map (fun (v, n) -> (v, Db.VIF.get_MAC ~__context ~self:v), n) vif_map in
List.fold_left (fun map vif ->
let vif_uuid = Db.VIF.get_uuid ~__context ~self:vif in
let log_prefix =
Printf.sprintf "Resolving VIF->Network map for VIF %s:" vif_uuid in
match List.filter (fun (v, _) -> v = vif) vif_map with
| (_, network)::_ ->
debug "%s VIF has been specified in map" log_prefix;
(vif, network)::map
| [] -> (* Check if another VIF with same MAC address has been mapped *)
let mac = Db.VIF.get_MAC ~__context ~self:vif in
match List.filter (fun ((_, m), _) -> m = mac) mapped_macs with
| ((similar, _), network)::_ ->
debug "%s VIF has same MAC as mapped VIF %s; inferring mapping"
log_prefix (Db.VIF.get_uuid ~__context ~self:similar);
(vif, network)::map
| [] ->
error "%s VIF not specified in map and cannot be inferred" log_prefix;
raise (Api_errors.Server_error(Api_errors.vif_not_in_map, [ Ref.string_of vif ]))
) [] vifs
(* Assert that every VDI is specified in the VDI map *)
let check_vdi_map ~__context vms_vdis vdi_map =
List.(iter (fun vconf ->
if not (mem_assoc vconf.vdi vdi_map)
then
let vdi_uuid = Db.VDI.get_uuid ~__context ~self:vconf.vdi in
error "VDI:SR map not fully specified for VDI %s" vdi_uuid ;
raise (Api_errors.Server_error(Api_errors.vdi_not_in_map, [ Ref.string_of vconf.vdi ]))) vms_vdis)
let migrate_send' ~__context ~vm ~dest ~live ~vdi_map ~vif_map ~vgpu_map ~options =
SMPERF.debug "vm.migrate_send called vm:%s" (Db.VM.get_uuid ~__context ~self:vm);
let open Xapi_xenops in
let localhost = Helpers.get_localhost ~__context in
let remote = remote_of_dest dest in
(* Copy mode means we don't destroy the VM on the source host. We also don't
copy over the RRDs/messages *)
let copy = try bool_of_string (List.assoc "copy" options) with _ -> false in
(* The first thing to do is to create mirrors of all the disks on the remote.
We look through the VM's VBDs and all of those of the snapshots. We then
compile a list of all of the associated VDIs, whether we mirror them or not
(mirroring means we believe the VDI to be active and new writes should be
mirrored to the destination - otherwise we just copy it)
We look at the VDIs of the VM, the VDIs of all of the snapshots, and any
suspend-image VDIs. *)
let vm_uuid = Db.VM.get_uuid ~__context ~self:vm in
let vbds = Db.VM.get_VBDs ~__context ~self:vm in
let vifs = Db.VM.get_VIFs ~__context ~self:vm in
let snapshots = Db.VM.get_snapshots ~__context ~self:vm in
let vm_and_snapshots = vm :: snapshots in
let snapshots_vbds = List.flatten (List.map (fun self -> Db.VM.get_VBDs ~__context ~self) snapshots) in
let snapshot_vifs = List.flatten (List.map (fun self -> Db.VM.get_VIFs ~__context ~self) snapshots) in
let is_intra_pool = try ignore(Db.Host.get_uuid ~__context ~self:remote.dest_host); true with _ -> false in
let is_same_host = is_intra_pool && remote.dest_host == localhost in
if copy && is_intra_pool then raise (Api_errors.Server_error(Api_errors.operation_not_allowed, [ "Copy mode is disallowed on intra pool storage migration, try efficient alternatives e.g. VM.copy/clone."]));
let vms_vdis = List.filter_map (vdi_filter __context true) vbds in
check_vdi_map ~__context vms_vdis vdi_map;
let vif_map =
if is_intra_pool then vif_map
else infer_vif_map ~__context (vifs @ snapshot_vifs) vif_map
in
(* Block SXM when VM has a VDI with on_boot=reset *)
List.(iter (fun vconf ->
let vdi = vconf.vdi in
if (Db.VDI.get_on_boot ~__context ~self:vdi ==`reset) then
raise (Api_errors.Server_error(Api_errors.vdi_on_boot_mode_incompatible_with_operation, [Ref.string_of vdi]))) vms_vdis) ;
let snapshots_vdis = List.filter_map (vdi_filter __context false) snapshots_vbds in
let suspends_vdis =
List.fold_left
(fun acc vm ->
if Db.VM.get_power_state ~__context ~self:vm = `Suspended
then
let vdi = Db.VM.get_suspend_VDI ~__context ~self:vm in
let sr = Db.VDI.get_SR ~__context ~self:vdi in
if is_intra_pool && Helpers.host_has_pbd_for_sr ~__context ~host:remote.dest_host ~sr
then acc
else (get_vdi_mirror __context vm vdi false):: acc
else acc)
[] vm_and_snapshots in
(* Double check that all of the suspend VDIs are all visible on the source *)
List.iter (fun vdi_mirror ->
let sr = Db.VDI.get_SR ~__context ~self:vdi_mirror.vdi in
if not (Helpers.host_has_pbd_for_sr ~__context ~host:localhost ~sr)
then raise (Api_errors.Server_error (Api_errors.suspend_image_not_accessible, [ Ref.string_of vdi_mirror.vdi ]))) suspends_vdis;
let dest_pool = List.hd (XenAPI.Pool.get_all remote.rpc remote.session) in
let default_sr_ref =
XenAPI.Pool.get_default_SR remote.rpc remote.session dest_pool in
let suspend_sr_ref =
let pool_suspend_SR = XenAPI.Pool.get_suspend_image_SR remote.rpc remote.session dest_pool
and host_suspend_SR = XenAPI.Host.get_suspend_image_sr remote.rpc remote.session remote.dest_host in
if pool_suspend_SR <> Ref.null then pool_suspend_SR else host_suspend_SR in
(* Resolve placement of unspecified VDIs here - unspecified VDIs that
are 'snapshot_of' a specified VDI go to the same place. suspend VDIs
that are unspecified go to the suspend_sr_ref defined above *)
let extra_vdis = suspends_vdis @ snapshots_vdis in
let extra_vdi_map =
List.map
(fun vconf ->
let dest_sr_ref =
let is_mapped = List.mem_assoc vconf.vdi vdi_map
and snapshot_of_is_mapped = List.mem_assoc vconf.snapshot_of vdi_map
and is_suspend_vdi = List.mem vconf suspends_vdis
and remote_has_suspend_sr = suspend_sr_ref <> Ref.null
and remote_has_default_sr = default_sr_ref <> Ref.null in
let log_prefix =
Printf.sprintf "Resolving VDI->SR map for VDI %s:" (Db.VDI.get_uuid ~__context ~self:vconf.vdi) in
if is_mapped then begin
debug "%s VDI has been specified in the map" log_prefix;
List.assoc vconf.vdi vdi_map
end else if snapshot_of_is_mapped then begin
debug "%s Snapshot VDI has entry in map for it's snapshot_of link" log_prefix;
List.assoc vconf.snapshot_of vdi_map
end else if is_suspend_vdi && remote_has_suspend_sr then begin
debug "%s Mapping suspend VDI to remote suspend SR" log_prefix;
suspend_sr_ref
end else if is_suspend_vdi && remote_has_default_sr then begin
debug "%s Remote suspend SR not set, mapping suspend VDI to remote default SR" log_prefix;
default_sr_ref
end else if remote_has_default_sr then begin
debug "%s Mapping unspecified VDI to remote default SR" log_prefix;
default_sr_ref
end else begin
error "%s VDI not in VDI->SR map and no remote default SR is set" log_prefix;
raise (Api_errors.Server_error(Api_errors.vdi_not_in_map, [ Ref.string_of vconf.vdi ]))
end in
(vconf.vdi, dest_sr_ref))
extra_vdis in
let vdi_map = vdi_map @ extra_vdi_map in
let all_vdis = vms_vdis @ extra_vdis in
(* The vdi_map should be complete at this point - it should include all the
VDIs in the all_vdis list. *)
assert_no_cbt_enabled_vdi_migrated ~__context ~vdi_map;
let dbg = Context.string_of_task __context in
let open Xapi_xenops_queue in
let queue_name = queue_of_vm ~__context ~self:vm in
let module XenopsAPI = (val make_client queue_name : XENOPS) in
let remote_vdis = ref [] in
let ha_always_run_reset = not is_intra_pool && Db.VM.get_ha_always_run ~__context ~self:vm in
let cd_vbds = find_cds_to_eject __context vdi_map vbds in
eject_cds __context cd_vbds;
try
(* Sort VDIs by size in principle and then age secondly. This gives better
chances that similar but smaller VDIs would arrive comparatively
earlier, which can serve as base for incremental copying the larger
ones. *)
let compare_fun v1 v2 =
let r = Int64.compare v1.size v2.size in
if r = 0 then
let t1 = Date.to_float (Db.VDI.get_snapshot_time ~__context ~self:v1.vdi) in
let t2 = Date.to_float (Db.VDI.get_snapshot_time ~__context ~self:v2.vdi) in
compare t1 t2
else r in
let all_vdis = all_vdis |> List.sort compare_fun in
let total_size = List.fold_left (fun acc vconf -> Int64.add acc vconf.size) 0L all_vdis in
let so_far = ref 0L in
let new_vm =
with_many (vdi_copy_fun __context dbg vdi_map remote is_intra_pool remote_vdis so_far total_size copy) all_vdis @@ fun all_map ->
let was_from vmap = List.exists (fun vconf -> vconf.vdi = vmap.mr_local_vdi_reference) in
let suspends_map, snapshots_map, vdi_map = List.fold_left (fun (suspends, snapshots, vdis) vmap ->
if was_from vmap suspends_vdis then vmap :: suspends, snapshots, vdis
else if was_from vmap snapshots_vdis then suspends, vmap :: snapshots, vdis
else suspends, snapshots, vmap :: vdis
) ([],[],[]) all_map in
let all_map = List.concat [suspends_map; snapshots_map; vdi_map] in
(* All the disks and snapshots have been created in the remote SR(s),
* so update the snapshot links if there are any snapshots. *)
if snapshots_map <> [] then
update_snapshot_info ~__context ~dbg ~url:remote.sm_url ~vdi_map ~snapshots_map;
let xenops_vdi_map = List.map (fun mirror_record -> (mirror_record.mr_local_xenops_locator, mirror_record.mr_remote_xenops_locator)) all_map in
(* Wait for delay fist to disappear *)
wait_for_fist __context Xapi_fist.pause_storage_migrate "pause_storage_migrate";
TaskHelper.exn_if_cancelling ~__context;
let new_vm =
if is_intra_pool
then vm
else
(* Make sure HA replaning cycle won't occur right during the import process or immediately after *)
let () = if ha_always_run_reset then XenAPI.Pool.ha_prevent_restarts_for ~rpc:remote.rpc ~session_id:remote.session ~seconds:(Int64.of_float !Xapi_globs.ha_monitor_interval) in
(* Move the xapi VM metadata to the remote pool. *)
let vms =
let vdi_map =
List.map (fun mirror_record -> {
local_vdi_reference = mirror_record.mr_local_vdi_reference;
remote_vdi_reference = Some mirror_record.mr_remote_vdi_reference;
})
all_map in
let vif_map =
List.map (fun (vif, network) -> {
local_vif_reference = vif;
remote_network_reference = network;
})
vif_map in
let vgpu_map =
List.map (fun (vgpu, gpu_group) -> {
local_vgpu_reference = vgpu;
remote_gpu_group_reference = gpu_group;
})
vgpu_map
in
inter_pool_metadata_transfer ~__context ~remote ~vm ~vdi_map
~vif_map ~vgpu_map ~dry_run:false ~live:true ~copy
in
let vm = List.hd vms in
let () = if ha_always_run_reset then XenAPI.VM.set_ha_always_run ~rpc:remote.rpc ~session_id:remote.session ~self:vm ~value:false in
(* Reserve resources for the new VM on the destination pool's host *)
let () = XenAPI.Host.allocate_resources_for_vm remote.rpc remote.session remote.dest_host vm true in
vm in
wait_for_fist __context Xapi_fist.pause_storage_migrate2 "pause_storage_migrate2";
(* Attach networks on remote *)