diff --git a/ocaml/idl/datamodel.ml b/ocaml/idl/datamodel.ml index 4372877b995..227d5d9153f 100644 --- a/ocaml/idl/datamodel.ml +++ b/ocaml/idl/datamodel.ml @@ -4998,6 +4998,17 @@ module SR = struct end module SM = struct + let formats = + [ + ("raw", "Plain disk image") + ; ("vhd", "Virtual Hard Disk") + ; ("qcow2", "Qemu Copy-On-Write version 2") + ] + + let formats_desc = formats |> List.map fst |> String.concat ", " + + let image_format_type = Enum ("image_format_type", formats) + (** XXX: just make this a field and be done with it. Cowardly refusing to change the schema for now. *) let get_driver_filename = call ~name:"get_driver_filename" ~in_oss_since:None @@ -5118,6 +5129,10 @@ module SM = struct ~ty:(Set String) "required_cluster_stack" "The storage plugin requires that one of these cluster stacks is \ configured and running." + ; field ~lifecycle:[] ~qualifier:DynamicRO + ~default_value:(Some (VSet [])) ~ty:(Set image_format_type) + "supported_image_formats" + (Printf.sprintf "Image formats supported by the SR: %s" formats_desc) ] () end @@ -5439,6 +5454,10 @@ module VDI = struct [ (Ref _vdi, "vdi", "The VDI to migrate") ; (Ref _sr, "sr", "The destination SR") + ; ( String + , "dest_img_format" + , "The image format to use on destination SR: raw, vhd, qcow2" + ) ; (Map (String, String), "options", "Other parameters") ] ~result:(Ref _vdi, "The new reference of the migrated VDI.") diff --git a/ocaml/idl/datamodel_common.ml b/ocaml/idl/datamodel_common.ml index 12c548580b1..2d51f5b5714 100644 --- a/ocaml/idl/datamodel_common.ml +++ b/ocaml/idl/datamodel_common.ml @@ -10,7 +10,7 @@ open Datamodel_roles to leave a gap for potential hotfixes needing to increment the schema version.*) let schema_major_vsn = 5 -let schema_minor_vsn = 791 +let schema_minor_vsn = 792 (* Historical schema versions just in case this is useful later *) let rio_schema_major_vsn = 5 diff --git a/ocaml/idl/datamodel_vm.ml b/ocaml/idl/datamodel_vm.ml index f0d0856f9e4..a32cf2b36e6 100644 --- a/ocaml/idl/datamodel_vm.ml +++ b/ocaml/idl/datamodel_vm.ml @@ -1714,6 +1714,13 @@ let migrate_send = ; param_release= inverness_release ; param_default= Some (VMap []) } + ; { + param_type= Map (Ref _vdi, String) + ; param_name= "vdi_format_map" + ; param_doc= "Map of source VDI to it's expected type on destination" + ; param_release= numbered_release "25.33.0-next" + ; param_default= Some (VMap []) + } ] ~result: (Ref _vm, "The reference of the newly created VM in the destination pool") @@ -1781,6 +1788,13 @@ let assert_can_migrate = ; param_release= inverness_release ; param_default= Some (VMap []) } + ; { + param_type= Map (Ref _vdi, String) + ; param_name= "vdi_format_map" + ; param_doc= "Map of source VDI to it's expected type on destination" + ; param_release= numbered_release "25.33.0-next" + ; param_default= Some (VMap []) + } ] ~allowed_roles:_R_VM_POWER_ADMIN ~errs:[Api_errors.license_restriction] diff --git a/ocaml/idl/schematest.ml b/ocaml/idl/schematest.ml index 9411d1c3b42..7c1a5cbe832 100644 --- a/ocaml/idl/schematest.ml +++ b/ocaml/idl/schematest.ml @@ -3,7 +3,7 @@ let hash x = Digest.string x |> Digest.to_hex (* BEWARE: if this changes, check that schema has been bumped accordingly in ocaml/idl/datamodel_common.ml, usually schema_minor_vsn *) -let last_known_schema_hash = "3b20f4304cfaaa7b6213af91ae632e64" +let last_known_schema_hash = "957db9d8442259d5888ac88c0ae01a7b" let current_schema_hash : string = let open Datamodel_types in diff --git a/ocaml/tests/common/test_common.ml b/ocaml/tests/common/test_common.ml index c0a77d21716..f266934a1f8 100644 --- a/ocaml/tests/common/test_common.ml +++ b/ocaml/tests/common/test_common.ml @@ -354,11 +354,12 @@ let make_sm ~__context ?(ref = Ref.make ()) ?(uuid = make_uuid ()) ?(copyright = "") ?(version = "") ?(required_api_version = "") ?(capabilities = []) ?(features = default_sm_features) ?(host_pending_features = []) ?(configuration = []) ?(other_config = []) - ?(driver_filename = "/dev/null") ?(required_cluster_stack = []) () = + ?(driver_filename = "/dev/null") ?(required_cluster_stack = []) + ?(supported_image_formats = []) () = Db.SM.create ~__context ~ref ~uuid ~_type ~name_label ~name_description ~vendor ~copyright ~version ~required_api_version ~capabilities ~features ~host_pending_features ~configuration ~other_config ~driver_filename - ~required_cluster_stack ; + ~required_cluster_stack ~supported_image_formats ; ref let make_sr ~__context ?(ref = Ref.make ()) ?(uuid = make_uuid ()) diff --git a/ocaml/tests/test_sm_features.ml b/ocaml/tests/test_sm_features.ml index 6b7ef99502d..79799d9ba66 100644 --- a/ocaml/tests/test_sm_features.ml +++ b/ocaml/tests/test_sm_features.ml @@ -249,6 +249,7 @@ module CreateSMObject = Generic.MakeStateful (struct ; features ; configuration= [] ; required_cluster_stack= [] + ; supported_image_formats= [] ; smapi_version= SMAPIv2 } diff --git a/ocaml/tests/test_vdi_cbt.ml b/ocaml/tests/test_vdi_cbt.ml index 54ae411ac97..4387784f8c9 100644 --- a/ocaml/tests/test_vdi_cbt.ml +++ b/ocaml/tests/test_vdi_cbt.ml @@ -30,6 +30,7 @@ let register_smapiv2_server (module S : Storage_interface.Server_impl) sr_ref = ; features= [] ; configuration= [] ; required_cluster_stack= [] + ; supported_image_formats= [] ; smapi_version= SMAPIv2 } in diff --git a/ocaml/xapi-cli-server/cli_frontend.ml b/ocaml/xapi-cli-server/cli_frontend.ml index 8f8117fde0d..43755f17510 100644 --- a/ocaml/xapi-cli-server/cli_frontend.ml +++ b/ocaml/xapi-cli-server/cli_frontend.ml @@ -1613,6 +1613,7 @@ let rec cmdtable_data : (string * cmd_spec) list = ; "compress" ; "vif:" ; "vdi:" + ; "image-format:" ] ; help= "Migrate the selected VM(s). The parameter '--live' will migrate the \ @@ -1626,7 +1627,9 @@ let rec cmdtable_data : (string * cmd_spec) list = 'copy=true' will enable the copy mode so that a stopped vm can be \ copied, instead of migrating, to the destination pool. The vif and \ vdi mapping parameters take the form 'vif:=' and 'vdi:='. \ + network uuid>' and 'vdi:='. You can \ + also specify the destination image format of the VDI using \ + 'image-format:='. \ Unfortunately, destination uuids cannot be tab-completed." ; implementation= No_fd Cli_operations.vm_migrate ; flags= [Standard; Vm_selectors] @@ -2468,10 +2471,10 @@ let rec cmdtable_data : (string * cmd_spec) list = ; ( "vdi-pool-migrate" , { reqd= ["uuid"; "sr-uuid"] - ; optn= [] + ; optn= ["dest-img-format"] ; help= - "Migrate a VDI to a specified SR, while the VDI is attached to a \ - running guest." + "Migrate a VDI to a specified SR, while it is attached to a running \ + guest. You can specify the image format for the destination." ; implementation= No_fd Cli_operations.vdi_pool_migrate ; flags= [] } diff --git a/ocaml/xapi-cli-server/cli_operations.ml b/ocaml/xapi-cli-server/cli_operations.ml index 3296affb650..8480bdc8b21 100644 --- a/ocaml/xapi-cli-server/cli_operations.ml +++ b/ocaml/xapi-cli-server/cli_operations.ml @@ -2064,8 +2064,12 @@ let vdi_pool_migrate printer rpc session_id params = Client.VDI.get_by_uuid ~rpc ~session_id ~uuid:(List.assoc "uuid" params) and sr = Client.SR.get_by_uuid ~rpc ~session_id ~uuid:(List.assoc "sr-uuid" params) + and dest_img_format = + List.assoc_opt "dest-img-format" params |> Option.value ~default:"" and options = [] (* no options implemented yet *) in - let newvdi = Client.VDI.pool_migrate ~rpc ~session_id ~vdi ~sr ~options in + let newvdi = + Client.VDI.pool_migrate ~rpc ~session_id ~vdi ~sr ~dest_img_format ~options + in let newuuid = Client.VDI.get_uuid ~rpc ~session_id ~self:newvdi in printer (Cli_printer.PList [newuuid]) @@ -4592,6 +4596,7 @@ let vm_migrate_sxm_params = ; "remote-network" ; "vdi" ; "vgpu" + ; "image-format" ] let vm_migrate printer rpc session_id params = @@ -4896,12 +4901,23 @@ let vm_migrate printer rpc session_id params = let token = remote Client.Host.migrate_receive ~host ~network ~options in + let vdi_format_map = + List.map + (fun (vdi_uuid, vdi_fmt) -> + let vdi = + Client.VDI.get_by_uuid ~rpc ~session_id ~uuid:vdi_uuid + in + (vdi, vdi_fmt) + ) + (read_map_params "image-format" params) + in let new_vm = do_vm_op ~include_control_vms:false ~include_template_vms:true printer rpc session_id (fun vm -> Client.VM.migrate_send ~rpc ~session_id ~vm:(vm.getref ()) - ~dest:token ~live:true ~vdi_map ~vif_map ~options ~vgpu_map + ~dest:token ~live:true ~vdi_map ~vdi_format_map ~vif_map + ~options ~vgpu_map ) params (["host"; "host-uuid"; "host-name"; "live"; "force"; "copy"] diff --git a/ocaml/xapi-cli-server/records.ml b/ocaml/xapi-cli-server/records.ml index ee68f272eb8..f355211a202 100644 --- a/ocaml/xapi-cli-server/records.ml +++ b/ocaml/xapi-cli-server/records.ml @@ -3865,6 +3865,12 @@ let sm_record rpc session_id sm = ; make_field ~name:"required-cluster-stack" ~get:(fun () -> concat_with_comma (x ()).API.sM_required_cluster_stack) () + ; make_field ~name:"supported-image-formats" + ~get:(fun () -> + map_and_concat Record_util.image_format_type_to_string + (x ()).API.sM_supported_image_formats + ) + () ] } diff --git a/ocaml/xapi-idl/storage/storage_interface.ml b/ocaml/xapi-idl/storage/storage_interface.ml index eaabacc9e8f..567dafa9a43 100644 --- a/ocaml/xapi-idl/storage/storage_interface.ml +++ b/ocaml/xapi-idl/storage/storage_interface.ml @@ -264,6 +264,8 @@ let string_of_vdi_info (x : vdi_info) = Jsonrpc.to_string (rpc_of vdi_info x) "datapaths". *) type dp = string [@@deriving rpcty] +type image_format_t = string [@@deriving rpcty] + type sock_path = string [@@deriving rpcty] type dp_stat_t = { @@ -476,6 +478,7 @@ type query_result = { ; features: string list ; configuration: (string * string) list ; required_cluster_stack: string list + ; supported_image_formats: string list ; smapi_version: smapi_version } [@@deriving rpcty] @@ -1034,6 +1037,8 @@ module StorageAPI (R : RPC) = struct declare "get_by_name" [] (dbg_p @-> name_p @-> returning result_p err) module DATA = struct + let image_format_p = Param.mk ~name:"image_format" image_format_t + let url_p = Param.mk ~name:"url" Types.string let dest_p = Param.mk ~name:"dest" Sr.t @@ -1065,6 +1070,7 @@ module StorageAPI (R : RPC) = struct (dbg_p @-> sr_p @-> vdi_p + @-> image_format_p @-> vm_p @-> url_p @-> returning operation_p err @@ -1108,6 +1114,8 @@ module StorageAPI (R : RPC) = struct ) module MIRROR = struct + let image_format_p = Param.mk ~name:"image_format" image_format_t + let mirror_vm_p = Param.mk ~name:"mirror_vm" Vm.t let copy_vm_p = Param.mk ~name:"copy_vm" Vm.t @@ -1116,7 +1124,7 @@ module StorageAPI (R : RPC) = struct let id_p = Param.mk ~name:"id" Mirror.id - (** [send_start dbg dp task src_sr vdi mirror_vm mirror_id local_vdi copy_vm + (** [send_start dbg dp task src_sr vdi image_format mirror_vm mirror_id local_vdi copy_vm live_vm url remote_mirror dest_sr verify_dest] takes the remote mirror [remote_mirror] prepared by the destination host and initiates the mirroring of [vdi] from the source *) @@ -1133,6 +1141,7 @@ module StorageAPI (R : RPC) = struct @-> task_id_p @-> src_sr_p @-> vdi_p + @-> image_format_p @-> mirror_vm_p @-> id_p @-> local_vdi_p @@ -1158,6 +1167,7 @@ module StorageAPI (R : RPC) = struct @-> sr_p @-> VDI.vdi_info_p @-> id_p + @-> image_format_p @-> similar_p @-> returning result err ) @@ -1175,6 +1185,7 @@ module StorageAPI (R : RPC) = struct @-> sr_p @-> VDI.vdi_info_p @-> id_p + @-> image_format_p @-> similar_p @-> vm_p @-> returning result err @@ -1190,6 +1201,7 @@ module StorageAPI (R : RPC) = struct @-> sr_p @-> VDI.vdi_info_p @-> id_p + @-> image_format_p @-> similar_p @-> vm_p @-> url_p @@ -1319,6 +1331,7 @@ module type MIRROR = sig -> dp:dp -> sr:sr -> vdi:vdi + -> image_format:string -> mirror_vm:vm -> mirror_id:Mirror.id -> local_vdi:vdi_info @@ -1336,6 +1349,7 @@ module type MIRROR = sig -> sr:sr -> vdi_info:vdi_info -> id:Mirror.id + -> image_format:string -> similar:Mirror.similars -> Mirror.mirror_receive_result @@ -1345,6 +1359,7 @@ module type MIRROR = sig -> sr:sr -> vdi_info:vdi_info -> id:Mirror.id + -> image_format:string -> similar:Mirror.similars -> vm:vm -> Mirror.mirror_receive_result @@ -1355,6 +1370,7 @@ module type MIRROR = sig -> sr:sr -> vdi_info:vdi_info -> mirror_id:Mirror.id + -> image_format:string -> similar:Mirror.similars -> vm:vm -> url:string @@ -1656,6 +1672,7 @@ module type Server_impl = sig -> dbg:debug_info -> sr:sr -> vdi:vdi + -> image_format:string -> vm:vm -> dest:string -> operation @@ -1843,8 +1860,8 @@ module Server (Impl : Server_impl) () = struct S.DATA.copy (fun dbg sr vdi vm url dest verify_dest -> Impl.DATA.copy () ~dbg ~sr ~vdi ~vm ~url ~dest ~verify_dest ) ; - S.DATA.mirror (fun dbg sr vdi vm dest -> - Impl.DATA.mirror () ~dbg ~sr ~vdi ~vm ~dest + S.DATA.mirror (fun dbg sr vdi image_format vm dest -> + Impl.DATA.mirror () ~dbg ~sr ~vdi ~image_format ~vm ~dest ) ; S.DATA.stat (fun dbg sr vdi vm key -> Impl.DATA.stat () ~dbg ~sr ~vdi ~vm ~key @@ -1856,6 +1873,7 @@ module Server (Impl : Server_impl) () = struct dp sr vdi + image_format mirror_vm mirror_id local_vdi @@ -1866,20 +1884,23 @@ module Server (Impl : Server_impl) () = struct dest_sr verify_dest -> - Impl.DATA.MIRROR.send_start () ~dbg ~task_id ~dp ~sr ~vdi ~mirror_vm - ~mirror_id ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror ~dest_sr - ~verify_dest + Impl.DATA.MIRROR.send_start () ~dbg ~task_id ~dp ~sr ~vdi ~image_format + ~mirror_vm ~mirror_id ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror + ~dest_sr ~verify_dest ) ; - S.DATA.MIRROR.receive_start (fun dbg sr vdi_info id similar -> - Impl.DATA.MIRROR.receive_start () ~dbg ~sr ~vdi_info ~id ~similar + S.DATA.MIRROR.receive_start (fun dbg sr vdi_info id image_format similar -> + Impl.DATA.MIRROR.receive_start () ~dbg ~sr ~vdi_info ~id ~image_format + ~similar ) ; - S.DATA.MIRROR.receive_start2 (fun dbg sr vdi_info id similar vm -> - Impl.DATA.MIRROR.receive_start2 () ~dbg ~sr ~vdi_info ~id ~similar ~vm + S.DATA.MIRROR.receive_start2 + (fun dbg sr vdi_info id image_format similar vm -> + Impl.DATA.MIRROR.receive_start2 () ~dbg ~sr ~vdi_info ~id ~image_format + ~similar ~vm ) ; S.DATA.MIRROR.receive_start3 - (fun dbg sr vdi_info mirror_id similar vm url verify_dest -> + (fun dbg sr vdi_info mirror_id image_format similar vm url verify_dest -> Impl.DATA.MIRROR.receive_start3 () ~dbg ~sr ~vdi_info ~mirror_id - ~similar ~vm ~url ~verify_dest + ~image_format ~similar ~vm ~url ~verify_dest ) ; S.DATA.MIRROR.receive_cancel (fun dbg id -> Impl.DATA.MIRROR.receive_cancel () ~dbg ~id diff --git a/ocaml/xapi-idl/storage/storage_skeleton.ml b/ocaml/xapi-idl/storage/storage_skeleton.ml index a2d2d04ab08..fcbb6527c60 100644 --- a/ocaml/xapi-idl/storage/storage_skeleton.ml +++ b/ocaml/xapi-idl/storage/storage_skeleton.ml @@ -183,7 +183,7 @@ module DATA = struct let copy ctx ~dbg ~sr ~vdi ~vm ~url ~dest = Storage_interface.unimplemented __FUNCTION__ - let mirror ctx ~dbg ~sr ~vdi ~vm ~dest = + let mirror ctx ~dbg ~sr ~vdi ~image_format ~vm ~dest = Storage_interface.unimplemented __FUNCTION__ let stat ctx ~dbg ~sr ~vdi ~vm ~key = @@ -198,18 +198,19 @@ module DATA = struct module MIRROR = struct type context = unit - let send_start ctx ~dbg ~task_id ~dp ~sr ~vdi ~mirror_vm ~mirror_id - ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror ~dest_sr ~verify_dest = + let send_start ctx ~dbg ~task_id ~dp ~sr ~vdi ~image_format ~mirror_vm + ~mirror_id ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror ~dest_sr + ~verify_dest = Storage_interface.unimplemented __FUNCTION__ - let receive_start ctx ~dbg ~sr ~vdi_info ~id ~similar = + let receive_start ctx ~dbg ~sr ~vdi_info ~id ~image_format ~similar = Storage_interface.unimplemented __FUNCTION__ - let receive_start2 ctx ~dbg ~sr ~vdi_info ~id ~similar ~vm = + let receive_start2 ctx ~dbg ~sr ~vdi_info ~id ~image_format ~similar ~vm = Storage_interface.unimplemented __FUNCTION__ - let receive_start3 ctx ~dbg ~sr ~vdi_info ~mirror_id ~similar ~vm ~url - ~verify_dest = + let receive_start3 ctx ~dbg ~sr ~vdi_info ~mirror_id ~image_format ~similar + ~vm ~url ~verify_dest = Storage_interface.unimplemented __FUNCTION__ let receive_finalize ctx ~dbg ~id = diff --git a/ocaml/xapi-storage-cli/main.ml b/ocaml/xapi-storage-cli/main.ml index f581d6b6b48..5a26ef2215f 100644 --- a/ocaml/xapi-storage-cli/main.ml +++ b/ocaml/xapi-storage-cli/main.ml @@ -317,16 +317,19 @@ let copy_vm = Vm.of_string "SXM_copy" let live_vm = Vm.of_string "live_vm" -let mirror_start common_opts sr vdi dp url dest verify_dest = +let mirror_start common_opts sr vdi dp url dest verify_dest dest_img_format = on_vdi' (fun sr vdi -> let get_opt x err = match x with Some y -> y | None -> failwith err in let dp = get_opt dp "Need a local data path" in let url = get_opt url "Need a URL" in let dest = get_opt dest "Need a destination SR" in + let image_format = + match dest_img_format with Some s -> s | None -> "" + in let task = - Storage_migrate.start ~dbg ~sr ~vdi ~dp ~mirror_vm ~copy_vm ~live_vm - ~url + Storage_migrate.start ~dbg ~sr ~vdi ~image_format ~dp ~mirror_vm + ~copy_vm ~live_vm ~url ~dest:(Storage_interface.Sr.of_string dest) ~verify_dest in @@ -531,6 +534,10 @@ let mirror_start_cmd = let doc = "Verify certicate of remote server" in Arg.(value & pos 5 bool false & info [] ~docv:"VERIFYDEST" ~doc) in + let dest_img_format = + let doc = "Specify the image format on the destination SR" in + Arg.(value & pos 6 (some string) None & info [] ~docv:"IMAGEFORMAT" ~doc) + in ( Term.( ret (const mirror_start @@ -541,6 +548,7 @@ let mirror_start_cmd = $ url $ dest $ verify_dest + $ dest_img_format ) ) , Cmd.info "mirror-start" ~sdocs:_common_options ~doc ~man diff --git a/ocaml/xapi-storage-script/main.ml b/ocaml/xapi-storage-script/main.ml index 1eccd3867fd..771f618fd4c 100644 --- a/ocaml/xapi-storage-script/main.ml +++ b/ocaml/xapi-storage-script/main.ml @@ -936,6 +936,8 @@ module QueryImpl (M : META) = struct ; configuration= response.Xapi_storage.Plugin.configuration ; required_cluster_stack= response.Xapi_storage.Plugin.required_cluster_stack + ; supported_image_formats= + response.Xapi_storage.Plugin.supported_image_formats ; smapi_version= SMAPIv3 } in @@ -1815,7 +1817,9 @@ module DATAImpl (M : META) = struct let stat_impl dbg sr vdi vm key = wrap @@ stat dbg sr vdi vm key - let mirror dbg sr vdi' vm' remote = + let mirror dbg sr vdi' image_format vm' remote = + let _ = image_format in + (* TODO: really use image format *) let vdi = Storage_interface.Vdi.string_of vdi' in let domain = Storage_interface.Vm.string_of vm' in Attached_SRs.find sr >>>= fun sr -> @@ -1839,7 +1843,8 @@ module DATAImpl (M : META) = struct | MirrorV1 v -> return (Storage_interface.Mirror.MirrorV1 v) - let mirror_impl dbg sr vdi vm remote = wrap @@ mirror dbg sr vdi vm remote + let mirror_impl dbg sr vdi image_format vm remote = + wrap @@ mirror dbg sr vdi image_format vm remote let data_import_activate_impl dbg _dp sr vdi' vm' = wrap diff --git a/ocaml/xapi-storage-script/python-self-test.t b/ocaml/xapi-storage-script/python-self-test.t index 9ac59bed953..7be4876c6a7 100644 --- a/ocaml/xapi-storage-script/python-self-test.t +++ b/ocaml/xapi-storage-script/python-self-test.t @@ -6,7 +6,7 @@ pids and uuids $ export PYTHONPATH=../xapi-storage/python/; ./main.exe --root=$PWD/test --self-test-only=true 2>&1 >/dev/null | sed -E 's/\[[0-9]+\]/[PID]/g' | sed -E 's/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/UUID/g' [INFO] {"method":"Plugin.query","params":[{"dbg":"debug"}],"id":2} - [INFO] $TESTCASE_ROOT/test/volume/org.xen.xapi.storage.dummyv5/Plugin.Query[PID] succeeded: {"plugin": "dummy", "name": "dummy SR plugin", "description": "Dummy v5 SR for unit tests.", "vendor": "Citrix Systems Inc", "copyright": "(C) 2018 Citrix Inc", "version": "1.0", "required_api_version": "5.0", "features": ["SR_ATTACH", "SR_DETACH", "SR_CREATE", "SR_PROBE", "VDI_CREATE", "VDI_DESTROY"], "configuration": {}, "required_cluster_stack": []} + [INFO] $TESTCASE_ROOT/test/volume/org.xen.xapi.storage.dummyv5/Plugin.Query[PID] succeeded: {"plugin": "dummy", "name": "dummy SR plugin", "description": "Dummy v5 SR for unit tests.", "vendor": "Citrix Systems Inc", "copyright": "(C) 2018 Citrix Inc", "version": "1.0", "required_api_version": "5.0", "features": ["SR_ATTACH", "SR_DETACH", "SR_CREATE", "SR_PROBE", "VDI_CREATE", "VDI_DESTROY"], "configuration": {}, "required_cluster_stack": [], "supported_image_formats": []} [INFO] {"method":"Plugin.diagnostics","params":[{"dbg":"debug"}],"id":4} [INFO] $TESTCASE_ROOT/test/volume/org.xen.xapi.storage.dummyv5/Plugin.diagnostics[PID] succeeded: "Dummy diagnostics" diff --git a/ocaml/xapi-storage-script/test/volume/org.xen.xapi.storage.dummyv5/plugin.py b/ocaml/xapi-storage-script/test/volume/org.xen.xapi.storage.dummyv5/plugin.py index bf54820cdc4..6618ee2f1c4 100755 --- a/ocaml/xapi-storage-script/test/volume/org.xen.xapi.storage.dummyv5/plugin.py +++ b/ocaml/xapi-storage-script/test/volume/org.xen.xapi.storage.dummyv5/plugin.py @@ -31,7 +31,8 @@ def query(self, dbg): # pylint: disable=unused-argument "VDI_CREATE", "VDI_DESTROY"], "configuration": {}, - "required_cluster_stack": []} + "required_cluster_stack": [], + "supported_image_formats": []} if __name__ == "__main__": diff --git a/ocaml/xapi-storage/generator/lib/plugin.ml b/ocaml/xapi-storage/generator/lib/plugin.ml index 4fc4d6d3fa0..168a5f9aeae 100644 --- a/ocaml/xapi-storage/generator/lib/plugin.ml +++ b/ocaml/xapi-storage/generator/lib/plugin.ml @@ -18,6 +18,8 @@ type query_result = { (** Key/description pairs describing required device_config parameters. *) ; required_cluster_stack: string list (** The plugin requires one of these cluster stacks to be active. *) + ; supported_image_formats: string list + (** List of image formats (VHD, raw, Qcow2...) supported. *) } [@@deriving rpcty] diff --git a/ocaml/xapi/message_forwarding.ml b/ocaml/xapi/message_forwarding.ml index fe815ebe0df..2adf3a71363 100644 --- a/ocaml/xapi/message_forwarding.ml +++ b/ocaml/xapi/message_forwarding.ml @@ -2545,26 +2545,26 @@ functor assuming it can ignore this check." let assert_can_migrate ~__context ~vm ~dest ~live ~vdi_map ~vif_map - ~options ~vgpu_map = + ~options ~vgpu_map ~vdi_format_map = info "VM.assert_can_migrate: VM = '%s'" (vm_uuid ~__context vm) ; (* Run the checks that can be done using just the DB directly on the master *) - Local.VM.assert_can_migrate ~__context ~vm ~dest ~live ~vdi_map ~vif_map - ~vgpu_map ~options ; + Local.VM.assert_can_migrate ~__context ~vm ~dest ~live ~vdi_map + ~vdi_format_map ~vif_map ~vgpu_map ~options ; (* Run further checks on the sending host *) assert_can_migrate_sender ~__context ~vm ~dest ~live ~vdi_map ~vif_map ~vgpu_map ~options let migrate_send ~__context ~vm ~dest ~live ~vdi_map ~vif_map ~options - ~vgpu_map = + ~vgpu_map ~vdi_format_map = info "VM.migrate_send: VM = '%s'" (vm_uuid ~__context vm) ; let source_host = Db.VM.get_resident_on ~__context ~self:vm in let local_fn = - Local.VM.migrate_send ~vm ~dest ~live ~vdi_map ~vif_map ~vgpu_map - ~options + Local.VM.migrate_send ~vm ~dest ~live ~vdi_map ~vdi_format_map + ~vif_map ~vgpu_map ~options in let remote_fn = - Client.VM.migrate_send ~vm ~dest ~live ~vdi_map ~vif_map ~options - ~vgpu_map + Client.VM.migrate_send ~vm ~dest ~live ~vdi_map ~vdi_format_map + ~vif_map ~options ~vgpu_map in let migration_type = if Xapi_vm_lifecycle_helpers.is_live ~__context ~self:vm then @@ -2585,7 +2585,8 @@ functor Helpers.try_internal_async ~__context API.ref_VM_of_rpc (fun () -> Client.InternalAsync.VM.migrate_send ~rpc ~session_id ~vm - ~dest ~live ~vdi_map ~vif_map ~options ~vgpu_map + ~dest ~live ~vdi_map ~vdi_format_map ~vif_map ~options + ~vgpu_map ) (fun () -> remote_fn ~session_id ~rpc) ) @@ -2624,7 +2625,7 @@ functor Server_helpers.exec_with_subtask ~__context "VM.assert_can_migrate" (fun ~__context -> assert_can_migrate ~__context ~vm ~dest ~live ~vdi_map - ~vif_map ~vgpu_map ~options + ~vdi_format_map ~vif_map ~vgpu_map ~options ) ; if Db.VM.get_VGPUs ~__context ~self:vm <> [] then Xapi_stats.incr_pool_vgpu_migration_count () ; @@ -5349,7 +5350,7 @@ functor ~prefer_slaves:true ~remote_fn ) - let pool_migrate ~__context ~vdi ~sr ~options = + let pool_migrate ~__context ~vdi ~sr ~dest_img_format ~options = let vbds = let expr = Xapi_database.Db_filter_types.( @@ -5369,7 +5370,9 @@ functor ("__internal__vm", Ref.string_of vm) :: List.remove_assoc "__internal__vm" options in - let local_fn = Local.VDI.pool_migrate ~vdi ~sr ~options in + let local_fn = + Local.VDI.pool_migrate ~vdi ~sr ~dest_img_format ~options + in let force = try bool_of_string (List.assoc "force" options) with _ -> false in @@ -5404,11 +5407,12 @@ functor in let remote_fn ~rpc ~session_id = let sync_op () = - Client.VDI.pool_migrate ~rpc ~session_id ~vdi ~sr ~options + Client.VDI.pool_migrate ~rpc ~session_id ~vdi ~sr + ~dest_img_format ~options in let async_op () = Client.InternalAsync.VDI.pool_migrate ~rpc ~session_id ~vdi ~sr - ~options + ~dest_img_format ~options in Helpers.try_internal_async ~__context API.ref_VDI_of_rpc async_op sync_op diff --git a/ocaml/xapi/sm_exec.ml b/ocaml/xapi/sm_exec.ml index c4e2c46a1a9..544c263dbae 100644 --- a/ocaml/xapi/sm_exec.ml +++ b/ocaml/xapi/sm_exec.ml @@ -570,6 +570,13 @@ let parse_sr_get_driver_info driver (xml : Xml.xml) = ) (XMLRPC.From.array XMLRPC.From.structure (safe_assoc "configuration" info)) in + let image_formats = + match List.assoc_opt "supported_image_formats" info with + | None -> + [] + | Some lst -> + XMLRPC.From.array XMLRPC.From.string lst + in { sr_driver_filename= driver ; sr_driver_name= name @@ -582,6 +589,7 @@ let parse_sr_get_driver_info driver (xml : Xml.xml) = ; sr_driver_configuration= configuration ; sr_driver_text_features= text_features ; sr_driver_required_cluster_stack= [] + ; sr_driver_supported_image_formats= image_formats ; sr_smapi_version= SMAPIv1 } diff --git a/ocaml/xapi/smint.ml b/ocaml/xapi/smint.ml index 1b4e4d45e47..d5059eee76b 100644 --- a/ocaml/xapi/smint.ml +++ b/ocaml/xapi/smint.ml @@ -192,6 +192,7 @@ type sr_driver_info = { ; sr_driver_text_features: string list ; sr_driver_configuration: (string * string) list ; sr_driver_required_cluster_stack: string list + ; sr_driver_supported_image_formats: string list ; sr_smapi_version: Storage_interface.smapi_version } @@ -207,6 +208,7 @@ let query_result_of_sr_driver_info x = ; features= x.sr_driver_text_features ; configuration= x.sr_driver_configuration ; required_cluster_stack= x.sr_driver_required_cluster_stack + ; supported_image_formats= x.sr_driver_supported_image_formats ; smapi_version= x.sr_smapi_version } diff --git a/ocaml/xapi/storage_migrate.ml b/ocaml/xapi/storage_migrate.ml index 1ff03c3d7ed..3d977bb825d 100644 --- a/ocaml/xapi/storage_migrate.ml +++ b/ocaml/xapi/storage_migrate.ml @@ -112,28 +112,29 @@ module MigrateLocal = struct | e -> raise e - let prepare ~dbg ~sr ~vdi ~dest ~local_vdi ~mirror_id ~mirror_vm ~url - ~verify_dest = + let prepare ~dbg ~sr ~vdi ~image_format ~dest ~local_vdi ~mirror_id ~mirror_vm + ~url ~verify_dest = try let (module Migrate_Backend) = choose_backend dbg sr in let similars = similar_vdis ~dbg ~sr ~vdi in Migrate_Backend.receive_start3 () ~dbg ~sr:dest ~vdi_info:local_vdi - ~mirror_id ~similar:similars ~vm:mirror_vm ~url ~verify_dest + ~image_format ~mirror_id ~similar:similars ~vm:mirror_vm ~url + ~verify_dest with e -> error "%s Caught error %s while preparing for SXM" __FUNCTION__ (Printexc.to_string e) ; raise (Storage_error (Migration_preparation_failure (Printexc.to_string e))) - let start ~task_id ~dbg ~sr ~vdi ~dp ~mirror_vm ~copy_vm ~live_vm ~url ~dest - ~verify_dest = + let start ~task_id ~dbg ~sr ~vdi ~image_format ~dp ~mirror_vm ~copy_vm + ~live_vm ~url ~dest ~verify_dest = SXM.info - "%s sr:%s vdi:%s dp: %s mirror_vm: %s copy_vm: %s url:%s dest:%s \ - verify_dest:%B" + "%s sr:%s vdi:%s image_format:%s dp:%s mirror_vm:%s copy_vm:%s url:%s \ + dest:%s verify_dest:%B" __FUNCTION__ (Storage_interface.Sr.string_of sr) (Storage_interface.Vdi.string_of vdi) - dp + image_format dp (Storage_interface.Vm.string_of mirror_vm) (Storage_interface.Vm.string_of copy_vm) url @@ -168,11 +169,11 @@ module MigrateLocal = struct let (module Migrate_Backend) = choose_backend dbg sr in try let remote_mirror = - prepare ~dbg ~sr ~vdi ~dest ~local_vdi ~mirror_id ~mirror_vm ~url - ~verify_dest + prepare ~dbg ~sr ~vdi ~image_format ~dest ~local_vdi ~mirror_id + ~mirror_vm ~url ~verify_dest in - Migrate_Backend.send_start () ~dbg ~task_id ~dp ~sr ~vdi ~mirror_vm - ~mirror_id ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror + Migrate_Backend.send_start () ~dbg ~task_id ~dp ~sr ~vdi ~image_format + ~mirror_vm ~mirror_id ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror ~dest_sr:dest ~verify_dest ; Some (Mirror_id mirror_id) with @@ -427,14 +428,14 @@ let copy ~dbg ~sr ~vdi ~vm ~url ~dest ~verify_dest = ~sr ~vdi ~vm ~url ~dest ~verify_dest ) -let start ~dbg ~sr ~vdi ~dp ~mirror_vm ~copy_vm ~live_vm ~url ~dest ~verify_dest - = +let start ~dbg ~sr ~vdi ~image_format ~dp ~mirror_vm ~copy_vm ~live_vm ~url + ~dest ~verify_dest = with_dbg ~name:__FUNCTION__ ~dbg @@ fun dbg -> with_task_and_thread ~dbg (fun task -> MigrateLocal.start ~task_id:(Storage_task.id_of_handle task) - ~dbg:dbg.Debug_info.log ~sr ~vdi ~dp ~mirror_vm ~copy_vm ~live_vm ~url - ~dest ~verify_dest + ~dbg:dbg.Debug_info.log ~sr ~vdi ~image_format ~dp ~mirror_vm ~copy_vm + ~live_vm ~url ~dest ~verify_dest ) (* XXX: PR-1255: copy the xenopsd 'raise Exception' pattern *) diff --git a/ocaml/xapi/storage_mux.ml b/ocaml/xapi/storage_mux.ml index 0427f76ca54..303690656ba 100644 --- a/ocaml/xapi/storage_mux.ml +++ b/ocaml/xapi/storage_mux.ml @@ -76,6 +76,7 @@ module Mux = struct ; features= [] ; configuration= [] ; required_cluster_stack= [] + ; supported_image_formats= [] ; smapi_version= SMAPIv2 } @@ -812,14 +813,15 @@ module Mux = struct let copy () ~dbg = with_dbg ~name:"DATA.copy" ~dbg @@ fun dbg -> Storage_migrate.copy ~dbg - let mirror () ~dbg ~sr ~vdi ~vm ~dest = + let mirror () ~dbg ~sr ~vdi ~image_format ~vm ~dest = with_dbg ~name:"DATA.mirror" ~dbg @@ fun di -> - info "%s dbg:%s sr: %s vdi: %s vm:%s remote:%s" __FUNCTION__ dbg - (s_of_sr sr) (s_of_vdi vdi) (s_of_vm vm) dest ; + info "%s dbg:%s sr: %s vdi: %s image_format: %s vm:%s remote:%s" + __FUNCTION__ dbg (s_of_sr sr) (s_of_vdi vdi) image_format (s_of_vm vm) + dest ; let module C = StorageAPI (Idl.Exn.GenClient (struct let rpc = of_sr sr end)) in - C.DATA.mirror (Debug_info.to_string di) sr vdi vm dest + C.DATA.mirror (Debug_info.to_string di) sr vdi image_format vm dest let stat () ~dbg ~sr ~vdi ~vm ~key = with_dbg ~name:"DATA.stat" ~dbg @@ fun di -> @@ -851,41 +853,45 @@ module Mux = struct module MIRROR = struct type context = unit - let send_start _ctx ~dbg:_ ~task_id:_ ~dp:_ ~sr:_ ~vdi:_ ~mirror_vm:_ - ~mirror_id:_ ~local_vdi:_ ~copy_vm:_ ~live_vm:_ ~url:_ + let send_start _ctx ~dbg:_ ~task_id:_ ~dp:_ ~sr:_ ~vdi:_ ~image_format:_ + ~mirror_vm:_ ~mirror_id:_ ~local_vdi:_ ~copy_vm:_ ~live_vm:_ ~url:_ ~remote_mirror:_ ~dest_sr:_ ~verify_dest:_ = Storage_interface.unimplemented __FUNCTION__ (* see storage_smapi{v1,v3}_migrate.ml *) - let receive_start () ~dbg ~sr ~vdi_info ~id ~similar = + let receive_start () ~dbg ~sr ~vdi_info ~id ~image_format ~similar = with_dbg ~name:"DATA.MIRROR.receive_start" ~dbg @@ fun _di -> - info "%s dbg: %s sr: %s vdi_info: %s mirror_id: %s similar: %s" + info + "%s dbg: %s sr: %s vdi_info: %s mirror_id: %s image_format: %s \ + similar: %s" __FUNCTION__ dbg (s_of_sr sr) (string_of_vdi_info vdi_info) - id + id image_format (String.concat ";" similar) ; (* This goes straight to storage_smapiv1_migrate for backwards compatability reasons, new code should not call receive_start any more *) Storage_smapiv1_migrate.MIRROR.receive_start () ~dbg ~sr ~vdi_info ~id - ~similar + ~image_format ~similar - let receive_start2 () ~dbg ~sr ~vdi_info ~id ~similar ~vm = + let receive_start2 () ~dbg ~sr ~vdi_info ~id ~image_format ~similar ~vm = with_dbg ~name:"DATA.MIRROR.receive_start2" ~dbg @@ fun _di -> - info "%s dbg: %s sr: %s vdi_info: %s mirror_id: %s similar: %s vm: %s" + info + "%s dbg: %s sr: %s vdi_info: %s mirror_id: %s image_format: %s \ + similar: %s vm: %s" __FUNCTION__ dbg (s_of_sr sr) (string_of_vdi_info vdi_info) - id + id image_format (String.concat ";" similar) (s_of_vm vm) ; info "%s dbg:%s" __FUNCTION__ dbg ; (* This goes straight to storage_smapiv1_migrate for backwards compatability reasons, new code should not call receive_start any more *) Storage_smapiv1_migrate.MIRROR.receive_start2 () ~dbg ~sr ~vdi_info ~id - ~similar ~vm + ~image_format ~similar ~vm (** see storage_smapiv{1,3}_migrate.receive_start3 *) - let receive_start3 () ~dbg:_ ~sr:_ ~vdi_info:_ ~mirror_id:_ ~similar:_ - ~vm:_ = + let receive_start3 () ~dbg:_ ~sr:_ ~vdi_info:_ ~mirror_id:_ + ~image_format:_ ~similar:_ ~vm:_ = Storage_interface.unimplemented __FUNCTION__ let receive_finalize () ~dbg ~id = diff --git a/ocaml/xapi/storage_smapiv1.ml b/ocaml/xapi/storage_smapiv1.ml index 0995edc35c4..ff9d4626095 100644 --- a/ocaml/xapi/storage_smapiv1.ml +++ b/ocaml/xapi/storage_smapiv1.ml @@ -125,6 +125,7 @@ module SMAPIv1 : Server_impl = struct ; features= [] ; configuration= [] ; required_cluster_stack= [] + ; supported_image_formats= [] ; smapi_version= SMAPIv1 } @@ -1129,7 +1130,8 @@ module SMAPIv1 : Server_impl = struct let copy _context ~dbg:_ ~sr:_ ~vdi:_ ~vm:_ ~url:_ ~dest:_ ~verify_dest:_ = assert false - let mirror _context ~dbg:_ ~sr:_ ~vdi:_ ~vm:_ ~dest:_ = assert false + let mirror _context ~dbg:_ ~sr:_ ~vdi:_ ~image_format:_ ~vm:_ ~dest:_ = + assert false let stat _context ~dbg:_ ~sr:_ ~vdi:_ ~vm:_ ~key:_ = assert false @@ -1140,20 +1142,21 @@ module SMAPIv1 : Server_impl = struct module MIRROR = struct type context = unit - let send_start _ctx ~dbg:_ ~task_id:_ ~dp:_ ~sr:_ ~vdi:_ ~mirror_vm:_ - ~mirror_id:_ ~local_vdi:_ ~copy_vm:_ ~live_vm:_ ~url:_ + let send_start _ctx ~dbg:_ ~task_id:_ ~dp:_ ~sr:_ ~vdi:_ ~image_format:_ + ~mirror_vm:_ ~mirror_id:_ ~local_vdi:_ ~copy_vm:_ ~live_vm:_ ~url:_ ~remote_mirror:_ ~dest_sr:_ ~verify_dest:_ = assert false - let receive_start _context ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~similar:_ = + let receive_start _context ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~image_format:_ + ~similar:_ = assert false - let receive_start2 _context ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~similar:_ - ~vm:_ = + let receive_start2 _context ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~image_format:_ + ~similar:_ ~vm:_ = assert false let receive_start3 _context ~dbg:_ ~sr:_ ~vdi_info:_ ~mirror_id:_ - ~similar:_ ~vm:_ ~url:_ ~verify_dest:_ = + ~image_format:_ ~similar:_ ~vm:_ ~url:_ ~verify_dest:_ = assert false let receive_finalize _context ~dbg:_ ~id:_ = assert false diff --git a/ocaml/xapi/storage_smapiv1_migrate.ml b/ocaml/xapi/storage_smapiv1_migrate.ml index c850d61f842..27b59df561c 100644 --- a/ocaml/xapi/storage_smapiv1_migrate.ml +++ b/ocaml/xapi/storage_smapiv1_migrate.ml @@ -493,14 +493,22 @@ let mirror_pass_fds ~dbg ~dp ~sr ~vdi ~mirror_vm ~live_vm ~mirror_id ~url mirror_id ; tapdev -let mirror_snapshot ~dbg ~sr ~dp ~mirror_id ~local_vdi = - D.debug "%s dbg:%s sr:%s dp:%s mirror_id:%s local_vdi:%s" __FUNCTION__ dbg - (s_of_sr sr) dp mirror_id - (string_of_vdi_info local_vdi) ; +let mirror_snapshot ~dbg ~sr ~dp ~mirror_id ~local_vdi ~image_format = + D.debug "%s dbg:%s sr:%s dp:%s mirror_id:%s local_vdi:%s image_format:%s" + __FUNCTION__ dbg (s_of_sr sr) dp mirror_id + (string_of_vdi_info local_vdi) + image_format ; SXM.info "%s About to snapshot VDI = %s" __FUNCTION__ (string_of_vdi_info local_vdi) ; let local_vdi = add_to_sm_config local_vdi "mirror" ("nbd:" ^ dp) in let local_vdi = add_to_sm_config local_vdi "base_mirror" mirror_id in + let local_vdi = + match image_format with + | "" -> + local_vdi + | fmt -> + add_to_sm_config local_vdi "image-format" fmt + in let snapshot = try Local.VDI.snapshot dbg sr local_vdi with | Storage_interface.Storage_error (Backend_error (code, _)) @@ -567,13 +575,15 @@ let mirror_cleanup ~dbg ~sr ~snapshot = module MIRROR : SMAPIv2_MIRROR = struct type context = unit - let send_start _ctx ~dbg ~task_id ~dp ~sr ~vdi ~mirror_vm ~mirror_id - ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror ~dest_sr ~verify_dest = + let send_start _ctx ~dbg ~task_id ~dp ~sr ~vdi ~image_format ~mirror_vm + ~mirror_id ~local_vdi ~copy_vm ~live_vm ~url ~remote_mirror ~dest_sr + ~verify_dest = D.debug - "%s dbg: %s dp: %s sr: %s vdi:%s mirror_vm:%s mirror_id: %s live_vm: %s \ - url:%s dest_sr:%s verify_dest:%B" - __FUNCTION__ dbg dp (s_of_sr sr) (s_of_vdi vdi) (s_of_vm mirror_vm) - mirror_id (s_of_vm live_vm) url (s_of_sr dest_sr) verify_dest ; + "%s dbg: %s dp: %s sr: %s vdi:%s image_format:%s mirror_vm:%s mirror_id: \ + %s live_vm: %s url:%s dest_sr:%s verify_dest:%B" + __FUNCTION__ dbg dp (s_of_sr sr) (s_of_vdi vdi) image_format + (s_of_vm mirror_vm) mirror_id (s_of_vm live_vm) url (s_of_sr dest_sr) + verify_dest ; let (module Remote) = Storage_migrate_helper.get_remote_backend url verify_dest in @@ -598,7 +608,9 @@ module MIRROR : SMAPIv2_MIRROR = struct ~dest_sr ~verify_dest ~remote_mirror:mirror_res in - let snapshot = mirror_snapshot ~dbg ~sr ~dp ~mirror_id ~local_vdi in + let snapshot = + mirror_snapshot ~dbg ~sr ~dp ~mirror_id ~local_vdi ~image_format + in mirror_checker mirror_id tapdev ; let task = Storage_task.(handle_of_id tasks) task_id in @@ -615,7 +627,7 @@ module MIRROR : SMAPIv2_MIRROR = struct (Storage_interface.Vdi.string_of mirror_res.Mirror.mirror_vdi.vdi) ; mirror_cleanup ~dbg ~sr ~snapshot - let receive_start_common ~dbg ~sr ~vdi_info ~id ~similar ~vm + let receive_start_common ~dbg ~sr ~vdi_info ~id ~image_format ~similar ~vm (module SMAPI : SMAPIv2) = let on_fail : (unit -> unit) list ref = ref [] in let vdis = SMAPI.SR.scan dbg sr in @@ -624,6 +636,13 @@ module MIRROR : SMAPIv2_MIRROR = struct let leaf_dp = SMAPI.DP.create dbg Uuidx.(to_string (make ())) in try let vdi_info = {vdi_info with sm_config= [("base_mirror", id)]} in + let vdi_info = + match image_format with + | "" -> + vdi_info + | fmt -> + add_to_sm_config vdi_info "image-format" fmt + in let leaf = SMAPI.VDI.create dbg sr vdi_info in D.info "Created leaf VDI for mirror receive: %s" (string_of_vdi_info leaf) ; on_fail := (fun () -> SMAPI.VDI.destroy dbg sr leaf.vdi) :: !on_fail ; @@ -677,9 +696,19 @@ module MIRROR : SMAPIv2_MIRROR = struct vdi_info.virtual_size new_size ) ; vdi_clone - | None -> + | None -> ( D.debug "Creating a blank remote VDI" ; - SMAPI.VDI.create dbg sr vdi_info + D.debug "image_format is set to <%s>" image_format ; + match image_format with + | "" -> + SMAPI.VDI.create dbg sr vdi_info + | _ -> + SMAPI.VDI.create dbg sr + { + vdi_info with + sm_config= ("type", image_format) :: vdi_info.sm_config + } + ) in D.debug "Parent disk content_id=%s" parent.content_id ; (* The state tracking here does not need to be changed, however, it will be @@ -722,29 +751,36 @@ module MIRROR : SMAPIv2_MIRROR = struct !on_fail ; raise e - let receive_start _ctx ~dbg ~sr ~vdi_info ~id ~similar = - D.debug "%s dbg: %s sr: %s vdi: %s id: %s" __FUNCTION__ dbg (s_of_sr sr) + let receive_start _ctx ~dbg ~sr ~vdi_info ~id ~image_format ~similar = + D.debug "%s dbg: %s sr: %s vdi: %s id: %s image_format: %s" __FUNCTION__ dbg + (s_of_sr sr) (string_of_vdi_info vdi_info) - id ; - receive_start_common ~dbg ~sr ~vdi_info ~id ~similar ~vm:(Vm.of_string "0") + id image_format ; + receive_start_common ~dbg ~sr ~vdi_info ~id ~image_format ~similar + ~vm:(Vm.of_string "0") (module Local) - let receive_start2 _ctx ~dbg ~sr ~vdi_info ~id ~similar ~vm = - D.debug "%s dbg: %s sr: %s vdi: %s id: %s" __FUNCTION__ dbg (s_of_sr sr) + let receive_start2 _ctx ~dbg ~sr ~vdi_info ~id ~image_format ~similar ~vm = + D.debug "%s dbg: %s sr: %s vdi: %s id: %s image_format: %s" __FUNCTION__ dbg + (s_of_sr sr) (string_of_vdi_info vdi_info) - id ; - receive_start_common ~dbg ~sr ~vdi_info ~id ~similar ~vm (module Local) + id image_format ; + receive_start_common ~dbg ~sr ~vdi_info ~id ~image_format ~similar ~vm + (module Local) - let receive_start3 _ctx ~dbg ~sr ~vdi_info ~mirror_id ~similar ~vm ~url - ~verify_dest = - D.debug "%s dbg: %s sr: %s vdi: %s id: %s vm: %s url: %s verify_dest: %B" + let receive_start3 _ctx ~dbg ~sr ~vdi_info ~mirror_id ~image_format ~similar + ~vm ~url ~verify_dest = + D.debug + "%s dbg: %s sr: %s vdi: %s id: %s image_format: %s vm: %s url: %s \ + verify_dest: %B" __FUNCTION__ dbg (s_of_sr sr) (string_of_vdi_info vdi_info) - mirror_id (s_of_vm vm) url verify_dest ; + mirror_id image_format (s_of_vm vm) url verify_dest ; let (module Remote) = Storage_migrate_helper.get_remote_backend url verify_dest in - receive_start_common ~dbg ~sr ~vdi_info ~id:mirror_id ~similar ~vm + receive_start_common ~dbg ~sr ~vdi_info ~id:mirror_id ~image_format ~similar + ~vm (module Remote) let receive_finalize _ctx ~dbg ~id = diff --git a/ocaml/xapi/storage_smapiv1_migrate.mli b/ocaml/xapi/storage_smapiv1_migrate.mli index a1021858e46..ab376f0cc04 100644 --- a/ocaml/xapi/storage_smapiv1_migrate.mli +++ b/ocaml/xapi/storage_smapiv1_migrate.mli @@ -70,6 +70,7 @@ val mirror_snapshot : -> dp:string -> mirror_id:string -> local_vdi:Storage_interface.vdi_info + -> image_format:string -> Storage_interface.vdi_info val mirror_checker : string -> Tapctl.tapdev -> unit diff --git a/ocaml/xapi/storage_smapiv1_wrapper.ml b/ocaml/xapi/storage_smapiv1_wrapper.ml index 86879780fba..dbbc1066869 100644 --- a/ocaml/xapi/storage_smapiv1_wrapper.ml +++ b/ocaml/xapi/storage_smapiv1_wrapper.ml @@ -1142,7 +1142,7 @@ functor (s_of_vdi vdi) url (s_of_sr dest) ; Impl.DATA.copy context ~dbg ~sr ~vdi ~vm ~url ~dest - let mirror _context ~dbg:_ ~sr:_ ~vdi:_ ~vm:_ ~dest:_ = + let mirror _context ~dbg:_ ~sr:_ ~vdi:_ ~image_format:_ ~vm:_ ~dest:_ = Storage_interface.unimplemented __FUNCTION__ let stat _context ~dbg:_ ~sr:_ ~vdi:_ ~vm:_ ~key:_ = @@ -1192,28 +1192,34 @@ functor module MIRROR = struct type context = unit - let send_start _ctx ~dbg:_ ~task_id:_ ~dp:_ ~sr:_ ~vdi:_ ~mirror_vm:_ - ~mirror_id:_ ~local_vdi:_ ~copy_vm:_ ~live_vm:_ ~url:_ + let send_start _ctx ~dbg:_ ~task_id:_ ~dp:_ ~sr:_ ~vdi:_ ~image_format:_ + ~mirror_vm:_ ~mirror_id:_ ~local_vdi:_ ~copy_vm:_ ~live_vm:_ ~url:_ ~remote_mirror:_ ~dest_sr:_ ~verify_dest:_ = Storage_interface.unimplemented __FUNCTION__ - let receive_start context ~dbg ~sr ~vdi_info ~id ~similar = - info "DATA.MIRROR.receive_start dbg:%s sr:%s id:%s similar:[%s]" dbg - (s_of_sr sr) id + let receive_start context ~dbg ~sr ~vdi_info ~id ~image_format ~similar + = + info + "DATA.MIRROR.receive_start dbg:%s sr:%s id:%s image_format:%s \ + similar:[%s]" + dbg (s_of_sr sr) id image_format (String.concat "," similar) ; - Impl.DATA.MIRROR.receive_start context ~dbg ~sr ~vdi_info ~id ~similar + Impl.DATA.MIRROR.receive_start context ~dbg ~sr ~vdi_info ~id + ~image_format ~similar - let receive_start2 context ~dbg ~sr ~vdi_info ~id ~similar ~vm = + let receive_start2 context ~dbg ~sr ~vdi_info ~id ~image_format ~similar + ~vm = info - "DATA.MIRROR.receive_start2 dbg:%s sr:%s id:%s similar:[%s] vm:%s" - dbg (s_of_sr sr) id + "DATA.MIRROR.receive_start2 dbg:%s sr:%s id:%s image_format:%s \ + similar:[%s] vm:%s" + dbg (s_of_sr sr) id image_format (String.concat "," similar) (s_of_vm vm) ; Impl.DATA.MIRROR.receive_start2 context ~dbg ~sr ~vdi_info ~id - ~similar ~vm + ~image_format ~similar ~vm let receive_start3 _context ~dbg:_ ~sr:_ ~vdi_info:_ ~mirror_id:_ - ~similar:_ ~vm:_ = + ~image_format:_ ~similar:_ ~vm:_ = (* See Storage_smapiv1_migrate.receive_start3 *) Storage_interface.unimplemented __FUNCTION__ diff --git a/ocaml/xapi/storage_smapiv3_migrate.ml b/ocaml/xapi/storage_smapiv3_migrate.ml index 774239c0804..c15d2eb52d6 100644 --- a/ocaml/xapi/storage_smapiv3_migrate.ml +++ b/ocaml/xapi/storage_smapiv3_migrate.ml @@ -108,14 +108,15 @@ let mirror_wait ~dbg ~sr ~vdi ~vm ~mirror_id mirror_key = module MIRROR : SMAPIv2_MIRROR = struct type context = unit - let send_start _ctx ~dbg ~task_id:_ ~dp ~sr ~vdi ~mirror_vm ~mirror_id - ~local_vdi:_ ~copy_vm:_ ~live_vm ~url ~remote_mirror ~dest_sr ~verify_dest - = + let send_start _ctx ~dbg ~task_id:_ ~dp ~sr ~vdi ~image_format ~mirror_vm + ~mirror_id ~local_vdi:_ ~copy_vm:_ ~live_vm ~url ~remote_mirror ~dest_sr + ~verify_dest = D.debug - "%s dbg: %s dp: %s sr: %s vdi:%s mirror_vm:%s mirror_id: %s live_vm: %s \ - url:%s dest_sr:%s verify_dest:%B" - __FUNCTION__ dbg dp (s_of_sr sr) (s_of_vdi vdi) (s_of_vm mirror_vm) - mirror_id (s_of_vm live_vm) url (s_of_sr dest_sr) verify_dest ; + "%s dbg: %s dp: %s sr: %s vdi:%s image_format:%s mirror_vm:%s mirror_id: \ + %s live_vm: %s url:%s dest_sr:%s verify_dest:%B" + __FUNCTION__ dbg dp (s_of_sr sr) (s_of_vdi vdi) image_format + (s_of_vm mirror_vm) mirror_id (s_of_vm live_vm) url (s_of_sr dest_sr) + verify_dest ; ignore (Local.VDI.attach3 dbg dp sr vdi (Vm.of_string "0") true) ; (* TODO we are not activating the VDI here because SMAPIv3 does not support activating the VDI again on dom 0 when it is already activated on the live_vm. @@ -151,7 +152,7 @@ module MIRROR : SMAPIv2_MIRROR = struct D.info "%s nbd_proxy_path: %s nbd_url %s" __FUNCTION__ nbd_proxy_path nbd_uri ; - let mk = Local.DATA.mirror dbg sr vdi live_vm nbd_uri in + let mk = Local.DATA.mirror dbg sr vdi image_format live_vm nbd_uri in D.debug "%s Updating active local mirrors: id=%s" __FUNCTION__ mirror_id ; let alm = @@ -184,18 +185,22 @@ module MIRROR : SMAPIv2_MIRROR = struct ) ) - let receive_start _ctx ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~similar:_ = + let receive_start _ctx ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~image_format:_ + ~similar:_ = Storage_interface.unimplemented __FUNCTION__ - let receive_start2 _ctx ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~similar:_ ~vm:_ = + let receive_start2 _ctx ~dbg:_ ~sr:_ ~vdi_info:_ ~id:_ ~image_format:_ + ~similar:_ ~vm:_ = Storage_interface.unimplemented __FUNCTION__ - let receive_start3 _ctx ~dbg ~sr ~vdi_info ~mirror_id ~similar:_ ~vm ~url - ~verify_dest = - D.debug "%s dbg: %s sr: %s vdi: %s id: %s vm: %s url: %s verify_dest: %B" + let receive_start3 _ctx ~dbg ~sr ~vdi_info ~mirror_id ~image_format ~similar:_ + ~vm ~url ~verify_dest = + D.debug + "%s dbg: %s sr: %s vdi: %s id: %s image_format: %s vm: %s url: %s \ + verify_dest: %B" __FUNCTION__ dbg (s_of_sr sr) (string_of_vdi_info vdi_info) - mirror_id (s_of_vm vm) url verify_dest ; + mirror_id image_format (s_of_vm vm) url verify_dest ; let module Remote = StorageAPI (Idl.Exn.GenClient (struct let rpc = Storage_utils.rpc ~srcstr:"smapiv2" ~dststr:"dst_smapiv2" diff --git a/ocaml/xapi/xapi_services.ml b/ocaml/xapi/xapi_services.ml index ca9e3d729ca..24f31ce1cc6 100644 --- a/ocaml/xapi/xapi_services.ml +++ b/ocaml/xapi/xapi_services.ml @@ -255,6 +255,7 @@ let get_handler (req : Http.Request.t) s _ = ; features= List.map (fun x -> path [_services; x]) [_SM] ; configuration= [] ; required_cluster_stack= [] + ; supported_image_formats= [] ; smapi_version= SMAPIv2 } in diff --git a/ocaml/xapi/xapi_sm.ml b/ocaml/xapi/xapi_sm.ml index 769484ddd7f..3014b481f8a 100644 --- a/ocaml/xapi/xapi_sm.ml +++ b/ocaml/xapi/xapi_sm.ml @@ -49,6 +49,10 @@ let create_from_query_result ~__context q = ~host_pending_features:[] ~configuration:q.configuration ~other_config:[] ~driver_filename:(Sm_exec.cmd_name q.driver) ~required_cluster_stack:q.required_cluster_stack + ~supported_image_formats: + (List.map Record_util.image_format_type_of_string + q.supported_image_formats + ) ) let find_pending_features existing_features features = @@ -112,6 +116,10 @@ let update_from_query_result ~__context (self, r) q_result = |> addto_pending_hosts_features ~__context self |> valid_hosts_pending_features ~__context in + let supported_image_formats = + List.map Record_util.image_format_type_of_string + q_result.supported_image_formats + in remove_valid_features_from_pending ~__context ~self new_features ; let features = existing_features @ new_features in List.iter @@ -143,7 +151,10 @@ let update_from_query_result ~__context (self, r) q_result = if r.API.sM_configuration <> q_result.configuration then Db.SM.set_configuration ~__context ~self ~value:q_result.configuration ; if r.API.sM_driver_filename <> driver_filename then - Db.SM.set_driver_filename ~__context ~self ~value:driver_filename + Db.SM.set_driver_filename ~__context ~self ~value:driver_filename ; + if r.API.sM_supported_image_formats <> supported_image_formats then + Db.SM.set_supported_image_formats ~__context ~self + ~value:supported_image_formats ) let is_v1 x = version_of_string x < [2; 0] diff --git a/ocaml/xapi/xapi_vm_migrate.ml b/ocaml/xapi/xapi_vm_migrate.ml index 814ed70acea..ec8a3a0dab6 100644 --- a/ocaml/xapi/xapi_vm_migrate.ml +++ b/ocaml/xapi/xapi_vm_migrate.ml @@ -216,12 +216,73 @@ let assert_sr_support_operations ~__context ~vdi_map ~remote ~local_ops op_supported_on_dest_sr sr remote_ops sm_record remote ) +(** [check_supported_image_format] checks that the [image_format] string + corresponds to valid image format type listed in [sm_formats]. + If [sm_formats] is an empty list or [image_format] is an empty string + there function does nothing. Otherwise, if [image_format] is not found + in [sm_formats], an exception is raised. *) +let check_supported_image_format ~image_format ~sm_formats ~sr_uuid = + if image_format = "" || sm_formats = [] then + () + else + let ty = Record_util.image_format_type_of_string image_format in + if not (List.mem ty sm_formats) then + let msg = + Printf.sprintf "Image format %s is not supported by %s" image_format + sr_uuid + in + raise Api_errors.(Server_error (vdi_incompatible_type, [msg])) + +(** [assert_vdi_format_is_supported] checks that all VDIs in [vdi_map] are included in the list of + supported image format of their corresponding SM. The type of the VDI is found in [vdi_format_map]. + - If no VDI type is specified we just returned so no error is raised. + - If an SM reports an empty list of supported formats, we cannot verify compatibility and no error + is raised. So if the format is not actually supported, the failure will be detected later when + attempting to create the VDI using that image format. *) +let assert_vdi_format_is_supported ~__context ~vdi_map ~vdi_format_map = + List.iter + (fun (vdi_ref, sr_ref) -> + let vdi_uuid = Db.VDI.get_uuid ~__context ~self:vdi_ref in + let sr_uuid = Db.SR.get_uuid ~__context ~self:sr_ref in + match List.assoc_opt vdi_ref vdi_format_map with + | None -> + debug "read vdi %s, sr %s. No type specified for the VDI" vdi_uuid + sr_uuid + | Some image_format -> ( + (* To get the supported image format from SM we need the SR type because both have + the same type. *) + let sr_type = Db.SR.get_type ~__context ~self:sr_ref in + let sm_refs = + Db.SM.get_refs_where ~__context + ~expr:(Eq (Field "type", Literal sr_type)) + in + (* We expect that one sr_type matches one sm_ref *) + match sm_refs with + | [sm_ref] -> + debug "read vdi %s, sr %s. Type is %s" vdi_uuid sr_uuid + image_format ; + let sm_formats = + Db.SM.get_supported_image_formats ~__context ~self:sm_ref + in + check_supported_image_format ~image_format ~sm_formats ~sr_uuid + | _ -> + let msg = + Printf.sprintf + "Found more than one SM ref (%d) when checking type (%s) of \ + VDI." + (List.length sm_refs) image_format + in + raise Api_errors.(Server_error (vdi_incompatible_type, [msg])) + ) + ) + vdi_map + (** Check that none of the VDIs that are mapped to a different SR have CBT or encryption 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_can_migrate_vdis ~__context ~vdi_map = +let assert_can_migrate_vdis ~__context ~vdi_map ~vdi_format_map = let assert_cbt_not_enabled vdi = if Db.VDI.get_cbt_enabled ~__context ~self:vdi then raise Api_errors.(Server_error (vdi_cbt_enabled, [Ref.string_of vdi])) @@ -231,6 +292,7 @@ let assert_can_migrate_vdis ~__context ~vdi_map = if List.exists (fun (key, _value) -> key = "key_hash") sm_config then raise Api_errors.(Server_error (vdi_is_encrypted, [Ref.string_of vdi])) in + assert_vdi_format_is_supported ~__context ~vdi_map ~vdi_format_map ; List.iter (fun (vdi, target_sr) -> if target_sr <> Db.VDI.get_SR ~__context ~self:vdi then ( @@ -239,6 +301,12 @@ let assert_can_migrate_vdis ~__context ~vdi_map = ) vdi_map +(** [get_vdi_type vdi_ref vdi_format_map] returns the vdi type found in the + [vdi_format_map] mapping for a given [vdi_ref]. If no type is found None + is returned. *) +let get_vdi_type ~vdi_ref ~vdi_format_map = + List.assoc_opt vdi_ref vdi_format_map + let assert_licensed_storage_motion ~__context = Pool_features.assert_enabled ~__context ~f:Features.Storage_motion @@ -725,25 +793,22 @@ let update_snapshot_info ~__context ~dbg ~url ~vdi_map ~snapshots_map debug "Remote SMAPI doesn't implement update_snapshot_info_src - ignoring" 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: Storage_interface.Vdi.t - ; (* The location of the VDI in the current SR *) - sr: Storage_interface.Sr.t - ; (* 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 *) + vdi: [`VDI] API.Ref.t (** The API reference of the local VDI *) + ; format: string + (** The image format of the VDI that must be used during its creation *) + ; dp: string (** The datapath the VDI will be using if the VM is running *) + ; location: Storage_interface.Vdi.t + (** The location of the VDI in the current SR *) + ; sr: Storage_interface.Sr.t (** 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 *) ; mirror_vm: Vm.t - (* The domain slice to which SMAPI calls should be made when mirroring this vdi *) + (** The domain slice to which SMAPI calls should be made when mirroring this vdi *) ; copy_vm: Vm.t - (* The domain slice to which SMAPI calls should be made when copying this vdi *) + (** The domain slice to which SMAPI calls should be made when copying this vdi *) } (* For VMs (not snapshots) xenopsd does not allow remapping, so we @@ -823,8 +888,11 @@ let get_vdi_mirror __context vm vdi do_mirror = |> ( ^ ) "MIR" |> Storage_interface.Vm.of_string in + (* format of VDI will be updated in migrate_send *) + let format = "" in { vdi + ; format ; dp ; location ; sr @@ -1049,8 +1117,9 @@ let vdi_copy_fun __context dbg vdi_map remote is_intra_pool remote_vdis so_far (* Layering violation!! *) ignore (Storage_access.register_mirror __context id) ; Storage_migrate.start ~dbg ~sr:vconf.sr ~vdi:vconf.location ~dp:new_dp - ~mirror_vm:vconf.mirror_vm ~copy_vm:vconf.copy_vm ~live_vm - ~url:remote.sm_url ~dest:dest_sr ~verify_dest:is_intra_pool + ~image_format:vconf.format ~mirror_vm:vconf.mirror_vm + ~copy_vm:vconf.copy_vm ~live_vm ~url:remote.sm_url ~dest:dest_sr + ~verify_dest:is_intra_pool in let mapfn x = let total = Int64.to_float total_size in @@ -1213,8 +1282,8 @@ let check_vdi_map ~__context vms_vdis vdi_map = vms_vdis ) -let migrate_send' ~__context ~vm ~dest ~live:_ ~vdi_map ~vif_map ~vgpu_map - ~options = +let migrate_send' ~__context ~vm ~dest ~live:_ ~vdi_map ~vdi_format_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 @@ -1392,10 +1461,21 @@ let migrate_send' ~__context ~vm ~dest ~live:_ ~vdi_map ~vif_map ~vgpu_map extra_vdis in let vdi_map = vdi_map @ extra_vdi_map in - let all_vdis = vms_vdis @ extra_vdis in + let all_vdis = + List.map + (fun vm -> + match get_vdi_type ~vdi_ref:vm.vdi ~vdi_format_map with + | None -> + vm + | Some vdi_ty -> + {vm with format= vdi_ty} + ) + vms_vdis + @ extra_vdis + in (* This is a good time to check our VDIs, because the vdi_map should be complete at this point; it should include all the VDIs in the all_vdis list. *) - assert_can_migrate_vdis ~__context ~vdi_map ; + assert_can_migrate_vdis ~__context ~vdi_map ~vdi_format_map ; let dbg = Context.string_of_task_and_tracing __context in let open Xapi_xenops_queue in let queue_name = queue_of_vm ~__context ~self:vm in @@ -1774,7 +1854,7 @@ let migration_type ~__context ~remote = `cross_pool let assert_can_migrate ~__context ~vm ~dest ~live:_ ~vdi_map ~vif_map ~options - ~vgpu_map = + ~vgpu_map ~vdi_format_map = Xapi_vm_helpers.assert_no_legacy_hardware ~__context ~vm ; assert_licensed_storage_motion ~__context ; let remote = remote_of_dest ~__context dest in @@ -1935,7 +2015,7 @@ let assert_can_migrate ~__context ~vm ~dest ~live:_ ~vdi_map ~vif_map ~options (* Previously there was also a check that none of the VDIs have CBT enabled. This is unnecessary, we only need to check that none of the VDIs that *will be moved* have CBT enabled. *) - assert_can_migrate_vdis ~__context ~vdi_map + assert_can_migrate_vdis ~__context ~vdi_map ~vdi_format_map let assert_can_migrate_sender ~__context ~vm ~dest ~live:_ ~vdi_map:_ ~vif_map:_ ~vgpu_map ~options:_ = @@ -1954,13 +2034,13 @@ let assert_can_migrate_sender ~__context ~vm ~dest ~live:_ ~vdi_map:_ ~vif_map:_ ~vm ~vgpu_map ~host:remote.dest_host ?remote:remote_for_migration_type () let migrate_send ~__context ~vm ~dest ~live ~vdi_map ~vif_map ~options ~vgpu_map - = + ~vdi_format_map = with_migrate (fun () -> - migrate_send' ~__context ~vm ~dest ~live ~vdi_map ~vif_map ~vgpu_map - ~options + migrate_send' ~__context ~vm ~dest ~live ~vdi_map ~vdi_format_map ~vif_map + ~vgpu_map ~options ) -let vdi_pool_migrate ~__context ~vdi ~sr ~options = +let vdi_pool_migrate ~__context ~vdi ~sr ~dest_img_format ~options = if Db.VDI.get_type ~__context ~self:vdi = `cbt_metadata then ( error "VDI.pool_migrate: the specified VDI has type cbt_metadata (at %s)" __LOC__ ; @@ -2046,13 +2126,15 @@ let vdi_pool_migrate ~__context ~vdi ~sr ~options = XenAPI.Host.migrate_receive ~rpc ~session_id ~host:dest_host ~network ~options in - assert_can_migrate ~__context ~vm ~dest ~live:true ~vdi_map ~vif_map:[] - ~vgpu_map:[] ~options:[] ; + assert_can_migrate ~__context ~vm ~dest ~live:true ~vdi_map + ~vdi_format_map:[(vdi, dest_img_format)] + ~vif_map:[] ~vgpu_map:[] ~options:[] ; assert_can_migrate_sender ~__context ~vm ~dest ~live:true ~vdi_map ~vif_map:[] ~vgpu_map:[] ~options:[] ; ignore - (migrate_send ~__context ~vm ~dest ~live:true ~vdi_map ~vif_map:[] - ~vgpu_map:[] ~options:[] + (migrate_send ~__context ~vm ~dest ~live:true ~vdi_map + ~vdi_format_map:[(vdi, dest_img_format)] + ~vif_map:[] ~vgpu_map:[] ~options:[] ) ) ; Db.VBD.get_VDI ~__context ~self:vbd