Skip to content

Commit d5285c0

Browse files
committed
vhd_tool_wrapper: Add parse_header to determine allocated blocks
Split common code used by {Vhd,Qcow}_tool_wrapper into a new vhd_qcow_parsing module. Since Vhd_tool_wrapper.run_vhd_tool is hardcoded to read the progress percentage printed by vhd-tool, we have to use the more generic Vhd_qcow_parsing.run_qcow_tool to run vhd-tool. Since VHD and QCOW follow the same format of JSON, use the same parse_header function. Signed-off-by: Andrii Sultanov <[email protected]>
1 parent e413177 commit d5285c0

File tree

4 files changed

+105
-45
lines changed

4 files changed

+105
-45
lines changed

ocaml/xapi/qcow_tool_wrapper.ml

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,14 @@
1212
* GNU Lesser General Public License for more details.
1313
*)
1414

15-
module D = Debug.Make (struct let name = __MODULE__ end)
16-
17-
open D
18-
19-
let run_qcow_tool qcow_tool ?(replace_fds = []) ?input_fd ?output_fd
20-
(_progress_cb : int -> unit) (args : string list) =
21-
info "Executing %s %s" qcow_tool (String.concat " " args) ;
22-
let open Forkhelpers in
23-
match
24-
with_logfile_fd "qcow-tool" (fun log_fd ->
25-
let pid =
26-
safe_close_and_exec input_fd output_fd (Some log_fd) replace_fds
27-
qcow_tool args
28-
in
29-
let _, status = waitpid pid in
30-
if status <> Unix.WEXITED 0 then (
31-
error "qcow-tool failed, returning VDI_IO_ERROR" ;
32-
raise
33-
(Api_errors.Server_error
34-
(Api_errors.vdi_io_error, ["Device I/O errors"])
35-
)
36-
)
37-
)
38-
with
39-
| Success (out, _) ->
40-
debug "qcow-tool successful export (%s)" out
41-
| Failure (out, _e) ->
42-
error "qcow-tool output: %s" out ;
43-
raise (Api_errors.Server_error (Api_errors.vdi_io_error, [out]))
44-
4515
let update_task_progress (__context : Context.t) (x : int) =
4616
TaskHelper.set_progress ~__context (float_of_int x /. 100.)
4717

4818
let receive (progress_cb : int -> unit) (unix_fd : Unix.file_descr)
4919
(path : string) =
5020
let args = ["stream_decode"; path] in
5121
let qcow_tool = !Xapi_globs.qcow_stream_tool in
52-
run_qcow_tool qcow_tool progress_cb args ~input_fd:unix_fd
22+
Vhd_qcow_parsing.run_tool qcow_tool progress_cb args ~input_fd:unix_fd
5323

5424
let read_header qcow_path =
5525
let args = ["read_headers"; qcow_path] in
@@ -58,23 +28,16 @@ let read_header qcow_path =
5828

5929
let progress_cb _ = () in
6030
Xapi_stdext_pervasives.Pervasiveext.finally
61-
(fun () -> run_qcow_tool qcow_tool progress_cb args ~output_fd:pipe_writer)
31+
(fun () ->
32+
Vhd_qcow_parsing.run_tool qcow_tool progress_cb args
33+
~output_fd:pipe_writer
34+
)
6235
(fun () -> Unix.close pipe_writer) ;
6336
pipe_reader
6437

6538
let parse_header qcow_path =
6639
let pipe_reader = read_header qcow_path in
67-
let ic = Unix.in_channel_of_descr pipe_reader in
68-
let buf = Buffer.create 4096 in
69-
let json = Yojson.Basic.from_channel ~buf ~fname:"qcow_header.json" ic in
70-
In_channel.close ic ;
71-
let cluster_size =
72-
1 lsl Yojson.Basic.Util.(member "cluster_bits" json |> to_int)
73-
in
74-
let cluster_list =
75-
Yojson.Basic.Util.(member "data_clusters" json |> to_list |> List.map to_int)
76-
in
77-
(cluster_size, cluster_list)
40+
Vhd_qcow_parsing.parse_header pipe_reader
7841

7942
let send ?relative_to (progress_cb : int -> unit) (unix_fd : Unix.file_descr)
8043
(path : string) (_size : Int64.t) =
@@ -107,8 +70,8 @@ let send ?relative_to (progress_cb : int -> unit) (unix_fd : Unix.file_descr)
10770
let replace_fds = Option.map (fun fd -> [(unique_string, fd)]) diff_fd in
10871
Xapi_stdext_pervasives.Pervasiveext.finally
10972
(fun () ->
110-
run_qcow_tool qcow_tool progress_cb args ?input_fd ~output_fd:unix_fd
111-
?replace_fds
73+
Vhd_qcow_parsing.run_tool qcow_tool progress_cb args ?input_fd
74+
~output_fd:unix_fd ?replace_fds
11275
)
11376
(fun () ->
11477
Option.iter Unix.close input_fd ;

ocaml/xapi/vhd_qcow_parsing.ml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
(*
2+
* Copyright (C) 2025 Vates.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published
6+
* by the Free Software Foundation; version 2.1 only. with the special
7+
* exception on linking described in file LICENSE.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*)
14+
15+
module D = Debug.Make (struct let name = __MODULE__ end)
16+
17+
open D
18+
19+
let run_tool tool ?(replace_fds = []) ?input_fd ?output_fd
20+
(_progress_cb : int -> unit) (args : string list) =
21+
info "Executing %s %s" tool (String.concat " " args) ;
22+
let open Forkhelpers in
23+
match
24+
with_logfile_fd tool (fun log_fd ->
25+
let pid =
26+
safe_close_and_exec input_fd output_fd (Some log_fd) replace_fds tool
27+
args
28+
in
29+
let _, status = waitpid pid in
30+
if status <> Unix.WEXITED 0 then (
31+
error "qcow-tool failed, returning VDI_IO_ERROR" ;
32+
raise
33+
(Api_errors.Server_error
34+
(Api_errors.vdi_io_error, ["Device I/O errors"])
35+
)
36+
)
37+
)
38+
with
39+
| Success (out, _) ->
40+
debug "%s successful export (%s)" tool out
41+
| Failure (out, _e) ->
42+
error "%s output: %s" tool out ;
43+
raise (Api_errors.Server_error (Api_errors.vdi_io_error, [out]))
44+
45+
let parse_header pipe_reader =
46+
let ic = Unix.in_channel_of_descr pipe_reader in
47+
let buf = Buffer.create 4096 in
48+
let json = Yojson.Basic.from_channel ~buf ~fname:"header.json" ic in
49+
In_channel.close ic ;
50+
let cluster_size =
51+
1 lsl Yojson.Basic.Util.(member "cluster_bits" json |> to_int)
52+
in
53+
let cluster_list =
54+
Yojson.Basic.Util.(member "data_clusters" json |> to_list |> List.map to_int)
55+
in
56+
(cluster_size, cluster_list)

ocaml/xapi/vhd_qcow_parsing.mli

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
(*
2+
* Copyright (C) 2025 Vates.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published
6+
* by the Free Software Foundation; version 2.1 only. with the special
7+
* exception on linking described in file LICENSE.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*)
14+
15+
val run_tool :
16+
string
17+
-> ?replace_fds:(string * Unix.file_descr) list
18+
-> ?input_fd:Unix.file_descr
19+
-> ?output_fd:Unix.file_descr
20+
-> (int -> unit)
21+
-> string list
22+
-> unit
23+
24+
val parse_header : Unix.file_descr -> int * int list

ocaml/xapi/vhd_tool_wrapper.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ let receive progress_cb format protocol (s : Unix.file_descr)
112112
in
113113
run_vhd_tool progress_cb args s s' path
114114

115+
let read_vhd_header path =
116+
let vhd_tool = !Xapi_globs.vhd_tool in
117+
let args = ["read_headers"; path] in
118+
let pipe_reader, pipe_writer = Unix.pipe ~cloexec:true () in
119+
120+
let progress_cb _ = () in
121+
Xapi_stdext_pervasives.Pervasiveext.finally
122+
(fun () ->
123+
Vhd_qcow_parsing.run_tool vhd_tool progress_cb args ~output_fd:pipe_writer
124+
)
125+
(fun () -> Unix.close pipe_writer) ;
126+
pipe_reader
127+
128+
let parse_header vhd_path =
129+
let pipe_reader = read_vhd_header vhd_path in
130+
Vhd_qcow_parsing.parse_header pipe_reader
131+
115132
let send progress_cb ?relative_to (protocol : string) (dest_format : string)
116133
(s : Unix.file_descr) (path : string) (size : Int64.t) (prefix : string) =
117134
let vhd_of_device =

0 commit comments

Comments
 (0)