Richard W.M. Jones
2022-Jul-01 11:01 UTC
[Libguestfs] [PATCH v2v 1/3] qemu-nbd: Implement output compression for qcow2 files
--- lib/qemuNBD.ml | 11 +++++++++-- lib/qemuNBD.mli | 5 +++++ output/output.ml | 38 +++++++++++++++++++++++++++++++++++--- output/output.mli | 1 + 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/qemuNBD.ml b/lib/qemuNBD.ml index ae21b17c7d..bbb65f4155 100644 --- a/lib/qemuNBD.ml +++ b/lib/qemuNBD.ml @@ -55,14 +55,16 @@ type cmd = { disk : string; mutable snapshot : bool; mutable format : string option; + mutable imgopts : bool; } -let create disk = { disk; snapshot = false; format = None } +let create disk = { disk; snapshot = false; format = None; imgopts = false } let set_snapshot cmd snap = cmd.snapshot <- snap let set_format cmd format = cmd.format <- format +let set_image_opts cmd imgopts = cmd.imgopts <- imgopts -let run_unix socket { disk; snapshot; format } +let run_unix socket { disk; snapshot; format; imgopts } assert (disk <> ""); (* Create a temporary directory where we place the PID file. *) @@ -85,6 +87,11 @@ let run_unix socket { disk; snapshot; format } (* -s adds a protective overlay. *) if snapshot then List.push_back args "-s"; + (* --image-opts reinterprets the filename parameter as a set of + * image options. + *) + if imgopts then List.push_back args "--image-opts"; + if have_selinux && qemu_nbd_has_selinux_label_option () then ( List.push_back args "--selinux-label"; List.push_back args "system_u:object_r:svirt_socket_t:s0" diff --git a/lib/qemuNBD.mli b/lib/qemuNBD.mli index e10d3106c7..afe9d944e1 100644 --- a/lib/qemuNBD.mli +++ b/lib/qemuNBD.mli @@ -43,6 +43,11 @@ val set_snapshot : cmd -> bool -> unit val set_format : cmd -> string option -> unit (** Set the format [--format] parameter. *) +val set_image_opts : cmd -> bool -> unit +(** Set whether the [--image-opts] parameter is used. This changes + the meaning of the [filename] parameter to a set of image options. + Consult the qemu-nbd man page for more details. *) + val run_unix : string -> cmd -> string * int (** Start qemu-nbd command listening on a Unix domain socket, waiting for the process to start up. diff --git a/output/output.ml b/output/output.ml index 5c6670b99c..5fe8555a7a 100644 --- a/output/output.ml +++ b/output/output.ml @@ -69,7 +69,7 @@ let error_if_disk_count_gt dir n if Sys.file_exists socket then error (f_"this output module doesn't support copying more than %d disks") n -let output_to_local_file ?(changeuid = fun f -> f ()) +let output_to_local_file ?(changeuid = fun f -> f ()) ?(compressed = false) output_alloc output_format filename size socket (* Check nbdkit is installed and has the required plugin. *) if not (Nbdkit.is_installed ()) then @@ -78,6 +78,10 @@ let output_to_local_file ?(changeuid = fun f -> f ()) error (f_"nbdkit-file-plugin is not installed or not working"); let nbdkit_config = Nbdkit.config () in + (* Only allow compressed with -of qcow2. *) + if compressed && output_format <> "qcow2" then + error (f_"?-oo compressed? is only allowed when the output format is a local qcow2-format file, ie ?-of qcow2?"); + let g = open_guestfs () in let preallocation match output_alloc with @@ -103,9 +107,37 @@ let output_to_local_file ?(changeuid = fun f -> f ()) On_exit.kill pid | "qcow2" -> - let cmd = QemuNBD.create filename in + let cmd + if not compressed then ( + let cmd = QemuNBD.create filename in + QemuNBD.set_format cmd (Some "qcow2"); + cmd + ) + else ( + (* Check nbdcopy is new enough. This assumes that + * the version of libnbd is the same as the version + * of nbdcopy, but parsing this is easier. We can + * remove this check when we build-depend on + * libnbd >= 1.14. + *) + let () + let version = NBD.create () |> NBD.get_version in + let version = String.nsplit "." version in + let version = List.map int_of_string version in + if version < [1; 13; 5] then + error (f_"-oo compressed option requires nbdcopy >= 1.13.5") in + + let qemu_quote str = String.replace str "," ",," in + let image_opts = [ "driver=compress"; + "file.driver=qcow2"; + "file.file.driver=file"; + "file.file.filename=" ^ qemu_quote filename ] in + let image_opts = String.concat "," image_opts in + let cmd = QemuNBD.create image_opts in + QemuNBD.set_image_opts cmd true; + cmd + ) in QemuNBD.set_snapshot cmd false; - QemuNBD.set_format cmd (Some "qcow2"); let _, pid = QemuNBD.run_unix socket cmd in On_exit.kill pid diff --git a/output/output.mli b/output/output.mli index 8d3d686594..c1f0f53d8e 100644 --- a/output/output.mli +++ b/output/output.mli @@ -84,6 +84,7 @@ val error_if_disk_count_gt : string -> int -> unit called. *) val output_to_local_file : ?changeuid:((unit -> unit) -> unit) -> + ?compressed:bool -> Types.output_allocation -> string -> string -> int64 -> string -> unit -- 2.37.0.rc2
Laszlo Ersek
2022-Jul-05 06:51 UTC
[Libguestfs] [PATCH v2v 1/3] qemu-nbd: Implement output compression for qcow2 files
On 07/01/22 13:01, Richard W.M. Jones wrote:> --- > lib/qemuNBD.ml | 11 +++++++++-- > lib/qemuNBD.mli | 5 +++++ > output/output.ml | 38 +++++++++++++++++++++++++++++++++++--- > output/output.mli | 1 + > 4 files changed, 50 insertions(+), 5 deletions(-)(Can you update your git diff order so that *.mli be put before *.ml?)> > diff --git a/lib/qemuNBD.ml b/lib/qemuNBD.ml > index ae21b17c7d..bbb65f4155 100644 > --- a/lib/qemuNBD.ml > +++ b/lib/qemuNBD.ml > @@ -55,14 +55,16 @@ type cmd = { > disk : string; > mutable snapshot : bool; > mutable format : string option; > + mutable imgopts : bool; > } > > -let create disk = { disk; snapshot = false; format = None } > +let create disk = { disk; snapshot = false; format = None; imgopts = false } > > let set_snapshot cmd snap = cmd.snapshot <- snap > let set_format cmd format = cmd.format <- format > +let set_image_opts cmd imgopts = cmd.imgopts <- imgopts > > -let run_unix socket { disk; snapshot; format } > +let run_unix socket { disk; snapshot; format; imgopts } > assert (disk <> ""); > > (* Create a temporary directory where we place the PID file. *) > @@ -85,6 +87,11 @@ let run_unix socket { disk; snapshot; format } > (* -s adds a protective overlay. *) > if snapshot then List.push_back args "-s"; > > + (* --image-opts reinterprets the filename parameter as a set of > + * image options. > + *) > + if imgopts then List.push_back args "--image-opts"; > + > if have_selinux && qemu_nbd_has_selinux_label_option () then ( > List.push_back args "--selinux-label"; > List.push_back args "system_u:object_r:svirt_socket_t:s0" > diff --git a/lib/qemuNBD.mli b/lib/qemuNBD.mli > index e10d3106c7..afe9d944e1 100644 > --- a/lib/qemuNBD.mli > +++ b/lib/qemuNBD.mli > @@ -43,6 +43,11 @@ val set_snapshot : cmd -> bool -> unit > val set_format : cmd -> string option -> unit > (** Set the format [--format] parameter. *) > > +val set_image_opts : cmd -> bool -> unit > +(** Set whether the [--image-opts] parameter is used. This changes > + the meaning of the [filename] parameter to a set of image options. > + Consult the qemu-nbd man page for more details. *) > + > val run_unix : string -> cmd -> string * int > (** Start qemu-nbd command listening on a Unix domain socket, > waiting for the process to start up. > diff --git a/output/output.ml b/output/output.ml > index 5c6670b99c..5fe8555a7a 100644 > --- a/output/output.ml > +++ b/output/output.ml > @@ -69,7 +69,7 @@ let error_if_disk_count_gt dir n > if Sys.file_exists socket then > error (f_"this output module doesn't support copying more than %d disks") n > > -let output_to_local_file ?(changeuid = fun f -> f ()) > +let output_to_local_file ?(changeuid = fun f -> f ()) ?(compressed = false) > output_alloc output_format filename size socket > (* Check nbdkit is installed and has the required plugin. *) > if not (Nbdkit.is_installed ()) then > @@ -78,6 +78,10 @@ let output_to_local_file ?(changeuid = fun f -> f ()) > error (f_"nbdkit-file-plugin is not installed or not working"); > let nbdkit_config = Nbdkit.config () in > > + (* Only allow compressed with -of qcow2. *) > + if compressed && output_format <> "qcow2" then > + error (f_"?-oo compressed? is only allowed when the output format is a local qcow2-format file, ie ?-of qcow2?"); > +Can you wrap this line? (Although I notice there's another quite long line (another error message) in this file, about nbdkit not being installed.) I suggest "i.e.," rather than "ie".> let g = open_guestfs () in > let preallocation > match output_alloc with > @@ -103,9 +107,37 @@ let output_to_local_file ?(changeuid = fun f -> f ()) > On_exit.kill pid > > | "qcow2" -> > - let cmd = QemuNBD.create filename in > + let cmd > + if not compressed then (IMO, whenever we have an "else" branch, it's better to simplify the condition by dropping "not", and swapping around the branches.> + let cmd = QemuNBD.create filename in > + QemuNBD.set_format cmd (Some "qcow2"); > + cmd > + ) > + else ( > + (* Check nbdcopy is new enough. This assumes that > + * the version of libnbd is the same as the version > + * of nbdcopy, but parsing this is easier. We can > + * remove this check when we build-depend on > + * libnbd >= 1.14. > + *) > + let ()What is the purpose of "let ()" here specifically? (I know what it's good for in general, from your earlier reference <https://baturin.org/docs/ocaml-faq/>, but I don't remember seeing it much outside of the outermost scope.)> + let version = NBD.create () |> NBD.get_version in > + let version = String.nsplit "." version in > + let version = List.map int_of_string version in > + if version < [1; 13; 5] then(Huh, didn't know such comparison existed, great!)> + error (f_"-oo compressed option requires nbdcopy >= 1.13.5") in > + > + let qemu_quote str = String.replace str "," ",," in > + let image_opts = [ "driver=compress"; > + "file.driver=qcow2"; > + "file.file.driver=file"; > + "file.file.filename=" ^ qemu_quote filename ] in > + let image_opts = String.concat "," image_opts in > + let cmd = QemuNBD.create image_opts in > + QemuNBD.set_image_opts cmd true; > + cmd > + ) in > QemuNBD.set_snapshot cmd false; > - QemuNBD.set_format cmd (Some "qcow2");Aha! This actually supports my suggestion to drop the "not" from the "if"'s condition, and swap the branches around -- that way the patch reads more easily. I first thought that the "set_format" call on the not-compressed branch above materialized from thin air.> let _, pid = QemuNBD.run_unix socket cmd in > On_exit.kill pid > > diff --git a/output/output.mli b/output/output.mli > index 8d3d686594..c1f0f53d8e 100644 > --- a/output/output.mli > +++ b/output/output.mli > @@ -84,6 +84,7 @@ val error_if_disk_count_gt : string -> int -> unit > called. *) > > val output_to_local_file : ?changeuid:((unit -> unit) -> unit) -> > + ?compressed:bool -> > Types.output_allocation -> > string -> string -> int64 -> string -> > unit >Only made some style comments; feel free to pick whatever you like (even none): Reviewed-by: Laszlo Ersek <lersek at redhat.com> Thanks Laszlo