Skip to content

Commit 01d2f46

Browse files
committed
CP-29521: VDI import/export in TAR format
Signed-off-by: Gabor Igloi <[email protected]>
1 parent 61ed233 commit 01d2f46

File tree

6 files changed

+60
-28
lines changed

6 files changed

+60
-28
lines changed

ocaml/quicktest/quicktest_vdi_ops_data_integrity.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ let export_import_vdi rpc session_id ~exportformat sR vdi =
9494

9595
let export_import_raw = export_import_vdi ~exportformat:"raw"
9696
let export_import_vhd = export_import_vdi ~exportformat:"vhd"
97+
let export_import_tar = export_import_vdi ~exportformat:"tar"
9798

9899
let f test_case =
99100
let open Qt_filter in
@@ -117,4 +118,5 @@ let tests () =
117118
(data_integrity_tests copy_vdi "VDI.copy") @
118119
(large_data_integrity_tests copy_vdi "VDI.copy") @
119120
(data_integrity_tests export_import_raw "VDI export/import to/from raw file") @
120-
(data_integrity_tests export_import_vhd "VDI export/import to/from VHD file")
121+
(data_integrity_tests export_import_vhd "VDI export/import to/from VHD file") @
122+
(data_integrity_tests export_import_tar "VDI export/import to/from TAR file")

ocaml/xapi/export_raw_vdi.ml

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,6 @@ let localhost_handler rpc session_id vdi (req: Http.Request.t) (s: Unix.file_des
3636
debug "export_raw_vdi task_id = %s; vdi = %s; format = %s; content-type = %s; filename = %s"
3737
(Ref.string_of task_id) (Ref.string_of vdi) (Importexport.Format.to_string format) content_type filename;
3838
let copy base_path path =
39-
let headers = Http.http_200_ok ~keep_alive:false () @ [
40-
Http.Hdr.task_id ^ ":" ^ (Ref.string_of task_id);
41-
Http.Hdr.content_type ^ ":" ^ content_type;
42-
Http.Hdr.content_disposition ^ ": attachment; filename=\"" ^ filename ^ "\""
43-
] in
44-
Http_svr.headers s headers;
4539
try
4640
debug "Copying VDI contents...";
4741
Vhd_tool_wrapper.send ?relative_to:base_path (Vhd_tool_wrapper.update_task_progress __context)
@@ -51,14 +45,29 @@ let localhost_handler rpc session_id vdi (req: Http.Request.t) (s: Unix.file_des
5145
raise (Api_errors.Server_error (Api_errors.vdi_io_error, ["Device I/O errors"])) in
5246
begin
5347
try
54-
Sm_fs_ops.with_block_attached_device __context rpc session_id vdi `RO
55-
(fun path ->
56-
match Importexport.base_vdi_of_req ~__context req with
57-
| Some base_vdi ->
58-
Sm_fs_ops.with_block_attached_device __context rpc session_id base_vdi `RO
59-
(fun base_path -> copy (Some base_path) path)
60-
| None -> copy None path
61-
)
48+
let headers = Http.http_200_ok ~keep_alive:false () @ [
49+
Http.Hdr.task_id ^ ":" ^ (Ref.string_of task_id);
50+
Http.Hdr.content_type ^ ":" ^ content_type;
51+
Http.Hdr.content_disposition ^ ": attachment; filename=\"" ^ filename ^ "\""
52+
] in
53+
Http_svr.headers s headers;
54+
match format with
55+
| Raw | Vhd ->
56+
Sm_fs_ops.with_block_attached_device __context rpc session_id vdi `RO
57+
(fun path ->
58+
match Importexport.base_vdi_of_req ~__context req with
59+
| Some base_vdi ->
60+
Sm_fs_ops.with_block_attached_device __context rpc session_id base_vdi `RO
61+
(fun base_path -> copy (Some base_path) path)
62+
| None -> copy None path
63+
)
64+
| Tar ->
65+
(* We need to keep refreshing the session to avoid session timeout *)
66+
let refresh_session = Xapi_session.consider_touching_session rpc session_id in
67+
(* The third element of the tuple, the size, is currently ignored by the function *)
68+
let size = Client.Client.VDI.get_virtual_size ~rpc ~session_id ~self:vdi in
69+
Stream_vdi.send_all refresh_session s __context rpc session_id [(Xapi_globs.vdi_tar_export_dir, vdi, size)];
70+
Tar_unix.write_end s;
6271
with e ->
6372
Backtrace.is_important e;
6473
TaskHelper.failed ~__context e;

ocaml/xapi/import_raw_vdi.ml

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,26 @@ let localhost_handler rpc session_id vdi_opt (req: Request.t) (s: Unix.file_desc
7171
error "Not enough info supplied to import";
7272
raise (HandleError(Api_errors.Server_error(Api_errors.internal_error, ["Not enough info supplied to import"]),Http.http_400_badrequest ~version:"1.0" ()))
7373
in
74-
let prezeroed = not (Sm_fs_ops.must_write_zeroes_into_new_vdi ~__context vdi) in
75-
Sm_fs_ops.with_block_attached_device __context rpc session_id vdi `RW
76-
(fun path ->
77-
let headers = Http.http_200_ok ~keep_alive:false () @
78-
[ Http.Hdr.task_id ^ ":" ^ (Ref.string_of task_id);
79-
content_type ] in
80-
Http_svr.headers s headers;
81-
if chunked
82-
then Vhd_tool_wrapper.receive (Vhd_tool_wrapper.update_task_progress __context) "raw" "chunked" s None path "" prezeroed
83-
else Vhd_tool_wrapper.receive (Vhd_tool_wrapper.update_task_progress __context) (Importexport.Format.to_string format) "none" s req.Request.content_length path "" prezeroed
84-
);
74+
let headers = Http.http_200_ok ~keep_alive:false () @
75+
[ Http.Hdr.task_id ^ ":" ^ (Ref.string_of task_id);
76+
content_type ] in
77+
Http_svr.headers s headers;
78+
begin match format with
79+
| Raw | Vhd ->
80+
let prezeroed = not (Sm_fs_ops.must_write_zeroes_into_new_vdi ~__context vdi) in
81+
Sm_fs_ops.with_block_attached_device __context rpc session_id vdi `RW
82+
(fun path ->
83+
if chunked
84+
then Vhd_tool_wrapper.receive (Vhd_tool_wrapper.update_task_progress __context) "raw" "chunked" s None path "" prezeroed
85+
else Vhd_tool_wrapper.receive (Vhd_tool_wrapper.update_task_progress __context) (Importexport.Format.to_string format) "none" s req.Request.content_length path "" prezeroed
86+
);
87+
| Tar ->
88+
(* We need to keep refreshing the session to avoid session timeout *)
89+
let refresh_session = Xapi_session.consider_touching_session rpc session_id in
90+
let size = Client.VDI.get_virtual_size ~rpc ~session_id ~self:vdi in
91+
(* VDIs exported as TAR archives will always have inline checksums *)
92+
Stream_vdi.recv_all_vdi refresh_session s __context rpc session_id ~has_inline_checksums:true ~force:false [(Xapi_globs.vdi_tar_export_dir, vdi, size)] |> ignore;
93+
end;
8594
TaskHelper.complete ~__context (Some (API.rpc_of_ref_VDI vdi));
8695
Some vdi
8796
with

ocaml/xapi/importexport.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,14 +322,17 @@ module Format = struct
322322
type t =
323323
| Raw
324324
| Vhd
325+
| Tar
325326

326327
let to_string = function
327328
| Raw -> "raw"
328329
| Vhd -> "vhd"
330+
| Tar -> "tar"
329331

330332
let of_string x = match String.lowercase_ascii x with
331333
| "raw" -> Some Raw
332334
| "vhd" -> Some Vhd
335+
| "tar" -> Some Tar
333336
| _ -> None
334337

335338
let filename ~__context vdi format =
@@ -340,6 +343,7 @@ module Format = struct
340343
let content_type = function
341344
| Raw -> "application/octet-stream"
342345
| Vhd -> "application/vhd"
346+
| Tar -> "application/x-tar"
343347

344348
let _key = "format"
345349

ocaml/xapi/stream_vdi.ml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ let verify_inline_checksum ifd checksum_table =
193193
raise e
194194

195195
(** Receive a set of VDIs split into chunks in a tar format in a defined order *)
196-
let recv_all refresh_session ifd (__context:Context.t) rpc session_id vsn force prefix_vdis =
196+
let recv_all_vdi refresh_session ifd (__context:Context.t) rpc session_id ~has_inline_checksums ~force prefix_vdis =
197197
TaskHelper.set_cancellable ~__context;
198198

199199
let progress = new_progress_record __context prefix_vdis in
@@ -273,7 +273,7 @@ let recv_all refresh_session ifd (__context:Context.t) rpc session_id vsn force
273273
made_progress __context progress (Int64.add skipped_size length);
274274

275275

276-
if vsn.Importexport.export_vsn > 0 then
276+
if has_inline_checksums then
277277
begin
278278
try
279279
verify_inline_checksum ifd checksum_table;
@@ -293,6 +293,10 @@ let recv_all refresh_session ifd (__context:Context.t) rpc session_id vsn force
293293
end;
294294
!checksum_table
295295

296+
let recv_all refresh_session ifd (__context:Context.t) rpc session_id vsn force =
297+
let has_inline_checksums = vsn.Importexport.export_vsn > 0 in
298+
recv_all_vdi refresh_session ifd __context rpc session_id ~has_inline_checksums ~force
299+
296300

297301
(** Receive a set of VDIs split into chunks in a tar format created out of a Zurich/Geneva
298302
exported VM. Each chunk has been independently compressed.*)

ocaml/xapi/xapi_globs.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ let default_platform_version = "0.0.0"
120120
and Miami GA (2) [inline checksums, no end-of-tar checksum table] *)
121121
let export_vsn = 2
122122

123+
(** When exporting a VDI in TAR format, the VDI's data will be stored under
124+
this directory in the archive. *)
125+
let vdi_tar_export_dir = "vdi"
126+
123127
let software_version () =
124128
(* In the case of XCP, all product_* fields will be blank. *)
125129
List.filter (fun (_, value) -> value <> "")

0 commit comments

Comments
 (0)