From f9d9002616006a358821f3fd9a4c7356eefd62df Mon Sep 17 00:00:00 2001 From: Guillaume Date: Tue, 2 Sep 2025 15:34:15 +0200 Subject: [PATCH 1/4] Datamodel: add supported_image_format field to SM object When running `xe sm-list params=all` you will now have the info of supported image formats if the SM plugin specified it in its DRIVER_INFO. The field is called `supported-image-formats`. If the plugin doesn't provide the info the field will be empty. This patch modifies the datamodel and add a new field to store this information into the SM object. Signed-off-by: Guillaume --- ocaml/idl/datamodel.ml | 15 +++++++++++++++ ocaml/idl/schematest.ml | 2 +- ocaml/tests/common/test_common.ml | 5 +++-- ocaml/tests/test_sm_features.ml | 1 + ocaml/tests/test_vdi_cbt.ml | 1 + ocaml/xapi-cli-server/records.ml | 6 ++++++ ocaml/xapi-idl/storage/storage_interface.ml | 1 + ocaml/xapi-storage-script/main.ml | 2 ++ ocaml/xapi-storage-script/python-self-test.t | 2 +- .../volume/org.xen.xapi.storage.dummyv5/plugin.py | 3 ++- ocaml/xapi-storage/generator/lib/plugin.ml | 2 ++ ocaml/xapi/sm_exec.ml | 1 + ocaml/xapi/smint.ml | 2 ++ ocaml/xapi/storage_mux.ml | 1 + ocaml/xapi/storage_smapiv1.ml | 1 + ocaml/xapi/xapi_services.ml | 1 + ocaml/xapi/xapi_sm.ml | 13 ++++++++++++- 17 files changed, 53 insertions(+), 6 deletions(-) diff --git a/ocaml/idl/datamodel.ml b/ocaml/idl/datamodel.ml index c72f0d2e0d8..bac7660739c 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 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 09f6a3b465a..7dfdb5f47c5 100644 --- a/ocaml/tests/common/test_common.ml +++ b/ocaml/tests/common/test_common.ml @@ -356,11 +356,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/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..d3aea600f9e 100644 --- a/ocaml/xapi-idl/storage/storage_interface.ml +++ b/ocaml/xapi-idl/storage/storage_interface.ml @@ -476,6 +476,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] diff --git a/ocaml/xapi-storage-script/main.ml b/ocaml/xapi-storage-script/main.ml index 1eccd3867fd..98d4e24822e 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 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/sm_exec.ml b/ocaml/xapi/sm_exec.ml index c4e2c46a1a9..724fe0ac273 100644 --- a/ocaml/xapi/sm_exec.ml +++ b/ocaml/xapi/sm_exec.ml @@ -582,6 +582,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= [] ; 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_mux.ml b/ocaml/xapi/storage_mux.ml index 0427f76ca54..991ea12e5c9 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 } diff --git a/ocaml/xapi/storage_smapiv1.ml b/ocaml/xapi/storage_smapiv1.ml index 0995edc35c4..fbdc8c46d76 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 } 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] From d6668e04cdc057da16c1cc8a66b5b303a38dd46b Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 14 Aug 2025 16:28:32 +0200 Subject: [PATCH 2/4] Allow selection of image format during migration This patch allows specifying the destination format for individual VDIs mapped to a destination SR. It adds a new parameter to `VM.migrate_send` and `VM.assert_can_migrate` API. It also adds a new parameter to XE CLI. The format to specify the image format is `image-format:=`. If the given image format cannot be validated, an error is returned. It also adds a new parameter to `VDI.pool-migrate`. This new parameter allows to provide a string that is the destination format. This string is used to check whether the destination SR supports the expected format. If the check fails or cannot be performed due to missing information on the destination SR, an error is returned. Signed-off-by: Guillaume --- ocaml/idl/datamodel.ml | 4 + ocaml/idl/datamodel_vm.ml | 7 + ocaml/xapi-cli-server/cli_frontend.ml | 11 +- ocaml/xapi-cli-server/cli_operations.ml | 20 ++- ocaml/xapi-idl/storage/storage_interface.ml | 44 ++++-- ocaml/xapi-idl/storage/storage_skeleton.ml | 15 +- ocaml/xapi-storage-cli/main.ml | 14 +- ocaml/xapi-storage-script/main.ml | 7 +- ocaml/xapi/message_forwarding.ml | 24 +-- ocaml/xapi/sm_exec.ml | 9 +- ocaml/xapi/storage_migrate.ml | 33 ++-- ocaml/xapi/storage_mux.ml | 37 +++-- ocaml/xapi/storage_smapiv1.ml | 16 +- ocaml/xapi/storage_smapiv1_migrate.ml | 90 +++++++---- ocaml/xapi/storage_smapiv1_migrate.mli | 1 + ocaml/xapi/storage_smapiv1_wrapper.ml | 30 ++-- ocaml/xapi/storage_smapiv3_migrate.ml | 33 ++-- ocaml/xapi/xapi_vm_migrate.ml | 164 ++++++++++++++++---- 18 files changed, 398 insertions(+), 161 deletions(-) diff --git a/ocaml/idl/datamodel.ml b/ocaml/idl/datamodel.ml index bac7660739c..12fa18519ae 100644 --- a/ocaml/idl/datamodel.ml +++ b/ocaml/idl/datamodel.ml @@ -5454,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_vm.ml b/ocaml/idl/datamodel_vm.ml index f0d0856f9e4..0a0d3a2bbdf 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.36.0-next" + ; param_default= Some (VMap []) + } ] ~result: (Ref _vm, "The reference of the newly created VM in the destination pool") diff --git a/ocaml/xapi-cli-server/cli_frontend.ml b/ocaml/xapi-cli-server/cli_frontend.ml index 39e0c8ce51f..737a4258920 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 eb6a0eb3a80..7f714b71795 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-idl/storage/storage_interface.ml b/ocaml/xapi-idl/storage/storage_interface.ml index d3aea600f9e..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 = { @@ -1035,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 @@ -1066,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 @@ -1109,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 @@ -1117,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 *) @@ -1134,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 @@ -1159,6 +1167,7 @@ module StorageAPI (R : RPC) = struct @-> sr_p @-> VDI.vdi_info_p @-> id_p + @-> image_format_p @-> similar_p @-> returning result err ) @@ -1176,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 @@ -1191,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 @@ -1320,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 @@ -1337,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 @@ -1346,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 @@ -1356,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 @@ -1657,6 +1672,7 @@ module type Server_impl = sig -> dbg:debug_info -> sr:sr -> vdi:vdi + -> image_format:string -> vm:vm -> dest:string -> operation @@ -1844,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 @@ -1857,6 +1873,7 @@ module Server (Impl : Server_impl) () = struct dp sr vdi + image_format mirror_vm mirror_id local_vdi @@ -1867,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 98d4e24822e..771f618fd4c 100644 --- a/ocaml/xapi-storage-script/main.ml +++ b/ocaml/xapi-storage-script/main.ml @@ -1817,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 -> @@ -1841,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/message_forwarding.ml b/ocaml/xapi/message_forwarding.ml index af2b1aa1458..f6406fcf116 100644 --- a/ocaml/xapi/message_forwarding.ml +++ b/ocaml/xapi/message_forwarding.ml @@ -2555,16 +2555,16 @@ functor ~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) ) @@ -5360,7 +5361,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.( @@ -5380,7 +5381,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 @@ -5415,11 +5418,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 724fe0ac273..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,7 +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= [] + ; sr_driver_supported_image_formats= image_formats ; sr_smapi_version= SMAPIv1 } 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 991ea12e5c9..303690656ba 100644 --- a/ocaml/xapi/storage_mux.ml +++ b/ocaml/xapi/storage_mux.ml @@ -813,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 -> @@ -852,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 fbdc8c46d76..ff9d4626095 100644 --- a/ocaml/xapi/storage_smapiv1.ml +++ b/ocaml/xapi/storage_smapiv1.ml @@ -1130,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 @@ -1141,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_vm_migrate.ml b/ocaml/xapi/xapi_vm_migrate.ml index cba3d5f4966..13141f42f5a 100644 --- a/ocaml/xapi/xapi_vm_migrate.ml +++ b/ocaml/xapi/xapi_vm_migrate.ml @@ -216,6 +216,93 @@ 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 ~remote_opt ~vdi_map + ~vdi_format_map = + let get_uuid_sr sr_ref = + match remote_opt with + | None -> + Db.SR.get_uuid ~__context ~self:sr_ref + | Some r -> + XenAPI.SR.get_uuid ~rpc:r.rpc ~session_id:r.session ~self:sr_ref + in + let get_sr_type sr_ref = + match remote_opt with + | None -> + Db.SR.get_type ~__context ~self:sr_ref + | Some r -> + XenAPI.SR.get_type ~rpc:r.rpc ~session_id:r.session ~self:sr_ref + in + let get_sm_refs sr_type = + match remote_opt with + | None -> + Db.SM.get_refs_where ~__context + ~expr:(Eq (Field "type", Literal sr_type)) + | Some r -> + XenAPI.SM.get_all_where ~rpc:r.rpc ~session_id:r.session + ~expr:(Printf.sprintf {|(field "type"="%s")|} sr_type) + in + let get_sm_formats sm_ref = + match remote_opt with + | None -> + Db.SM.get_supported_image_formats ~__context ~self:sm_ref + | Some r -> + XenAPI.SM.get_supported_image_formats ~rpc:r.rpc ~session_id:r.session + ~self:sm_ref + in + List.iter + (fun (vdi_ref, sr_ref) -> + let vdi_uuid = Db.VDI.get_uuid ~__context ~self:vdi_ref in + let sr_uuid = get_uuid_sr sr_ref in + match List.assoc_opt vdi_ref vdi_format_map with + | None -> + debug "read vdi %s, sr %s. No type specified." 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 = get_sr_type sr_ref in + let sm_refs = get_sm_refs 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 = get_sm_formats 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. @@ -239,6 +326,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 +818,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 +913,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 +1142,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 @@ -1214,8 +1308,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 @@ -1393,10 +1487,23 @@ 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 ; + let remote_opt = if is_same_host then None else Some remote in + assert_vdi_format_is_supported ~__context ~remote_opt ~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 @@ -1955,13 +2062,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__ ; @@ -2052,8 +2159,9 @@ let vdi_pool_migrate ~__context ~vdi ~sr ~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 From 1d848afc238df936baaeb4369fd5c4c1652858bf Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 30 Oct 2025 10:47:49 +0100 Subject: [PATCH 3/4] Add new parameter to VM.migrate_send in GO SDK Update VM.MigrateSend call to include new VdiFormatMap parameter. Signed-off-by: Guillaume --- .../jsonrpc-client/go/main_test.go | 11 ++++++++ .../jsonrpc-client/go/vm_test.go | 27 ++++++++++++------- .../spec/xapi-24/vm_migrate_send.json | 19 ++++++------- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/ocaml/sdk-gen/component-test/jsonrpc-client/go/main_test.go b/ocaml/sdk-gen/component-test/jsonrpc-client/go/main_test.go index eda67d8b592..35aecfcb7e5 100644 --- a/ocaml/sdk-gen/component-test/jsonrpc-client/go/main_test.go +++ b/ocaml/sdk-gen/component-test/jsonrpc-client/go/main_test.go @@ -113,6 +113,17 @@ func ConvertMapToVgpuMap(input map[string]interface{}) (map[xenapi.VGPURef]xenap return output, nil } +func ConvertMapToVdiFormatMap(input map[string]interface{}) (map[xenapi.VDIRef]string, error) { + output := make(map[xenapi.VDIRef]string) + for key, value := range input { + strValue, ok := value.(string) + if !ok { + return nil, fmt.Errorf("non-string value found for key %s: %v", key, value) + } + output[xenapi.VDIRef(key)] = strValue + } + return output, nil +} func TestMain(m *testing.M) { flag.Parse() exitVal := m.Run() diff --git a/ocaml/sdk-gen/component-test/jsonrpc-client/go/vm_test.go b/ocaml/sdk-gen/component-test/jsonrpc-client/go/vm_test.go index e4ebd17cd30..8d34a8ea693 100644 --- a/ocaml/sdk-gen/component-test/jsonrpc-client/go/vm_test.go +++ b/ocaml/sdk-gen/component-test/jsonrpc-client/go/vm_test.go @@ -269,13 +269,14 @@ func TestVMMigrateSend(t *testing.T) { inputParams := spec.Key.Params["VM.migrate_send"] const ( - IndexVMRef = 1 - IndexDest = 2 - IndexLive = 3 - IndexVdiMap = 4 - IndexVifMap = 5 - IndexOptions = 6 - IndexVgpuMap = 7 + IndexVMRef = 1 + IndexDest = 2 + IndexLive = 3 + IndexVdiMap = 4 + IndexVifMap = 5 + IndexOptions = 6 + IndexVgpuMap = 7 + IndexVdiFormatMap = 8 ) vmRef, ok1 := inputParams[IndexVMRef].(string) destOrg, ok2 := inputParams[IndexDest].(map[string]interface{}) @@ -284,7 +285,8 @@ func TestVMMigrateSend(t *testing.T) { vifMapOrg, ok5 := inputParams[IndexVifMap].(map[string]interface{}) optionsOrg, ok6 := inputParams[IndexOptions].(map[string]interface{}) vgpuMapOrg, ok7 := inputParams[IndexVgpuMap].(map[string]interface{}) - if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 || !ok6 || !ok7 { + vdiFormatMapOrg, ok8 := inputParams[IndexVdiFormatMap].(map[string]interface{}) + if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 || !ok6 || !ok7 || !ok8 { t.Log("Parameter get error from json file") t.Fail() return @@ -319,8 +321,13 @@ func TestVMMigrateSend(t *testing.T) { t.Fail() return } - - result, err := xenapi.VM.MigrateSend(session, xenapi.VMRef(vmRef), dest, live, vdiMap, vifMap, options, vgpuMap) + vdiFormatMap, err := ConvertMapToVdiFormatMap(vdiFormatMapOrg) + if err != nil { + t.Log(err) + t.Fail() + return + } + result, err := xenapi.VM.MigrateSend(session, xenapi.VMRef(vmRef), dest, live, vdiMap, vifMap, options, vgpuMap, vdiFormatMap) if err != nil { t.Log(err) t.Fail() diff --git a/ocaml/sdk-gen/component-test/spec/xapi-24/vm_migrate_send.json b/ocaml/sdk-gen/component-test/spec/xapi-24/vm_migrate_send.json index ae6167dff85..2ee632cc311 100644 --- a/ocaml/sdk-gen/component-test/spec/xapi-24/vm_migrate_send.json +++ b/ocaml/sdk-gen/component-test/spec/xapi-24/vm_migrate_send.json @@ -5,14 +5,15 @@ ], "params": { "VM.migrate_send": [ - "", - "OpaqueRef:5vm2d621-7b62-f571-vm00-754341a973e5", - {"name_label": "host1"}, - true, - {"OpaqueRef:5vdid621-7b62-f571-vdi8-754341a973e5": "OpaqueRef:5sr2d621-7b62-f571-s38r-754341a973e5"}, - {"OpaqueRef:9vifb8a9-f6a3-69d2-vifb-7a7b7c81c49e": "OpaqueRef:9network-f6a3-69d2-netw-7a7b7c81c49e"}, - {}, - {"OpaqueRef:9vgpu8a9-f6a3-69d2-vgpu-7a7b7c81c49e": "OpaqueRef:9gpugroup-f6a3-69d2-grou-7a7b7c81c49e"} + "", + "OpaqueRef:5vm2d621-7b62-f571-vm00-754341a973e5", + {"name_label": "host1"}, + true, + {"OpaqueRef:5vdid621-7b62-f571-vdi8-754341a973e5": "OpaqueRef:5sr2d621-7b62-f571-s38r-754341a973e5"}, + {"OpaqueRef:9vifb8a9-f6a3-69d2-vifb-7a7b7c81c49e": "OpaqueRef:9network-f6a3-69d2-netw-7a7b7c81c49e"}, + {}, + {"OpaqueRef:9vgpu8a9-f6a3-69d2-vgpu-7a7b7c81c49e": "OpaqueRef:9gpugroup-f6a3-69d2-grou-7a7b7c81c49e"}, + {"OpaqueRef:5vdid621-7b62-f571-vdi8-754341a973e5": "vhd"} ] }, "expected_result": { @@ -21,4 +22,4 @@ } } } -} \ No newline at end of file +} From 98e3219bef707614b6f08654b730da16e195d664 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 14 Aug 2025 16:40:39 +0200 Subject: [PATCH 4/4] Bumping database schema version A new field supported_image_format and new parameters have been added for: - VM.migrate_send - VM.assert_can_migrate - VDI.pool_migrate Signed-off-by: Guillaume --- ocaml/idl/datamodel_common.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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