In forthcoming commits we will be adding support for ssh, curl and
other features that require nbdkit >= 1.12.
As a prelude to that work, add generic code for querying ‘nbdkit
--dump-config’ and ‘nbdkit plugin --dump-plugin’ and checking the
minimum version number.
This changes the minimum version from 1.1.16 to 1.2, although that was
released about a year ago and is widely available, and in any case
we're going to require 1.12 in the next commit.
---
v2v/nbdkit.ml | 112 ++++++++++++++++++++++++++++++++------------------
1 file changed, 72 insertions(+), 40 deletions(-)
diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml
index f682d742a..8042d72f2 100644
--- a/v2v/nbdkit.ml
+++ b/v2v/nbdkit.ml
@@ -26,6 +26,9 @@ open Unix_utils
open Utils
+let nbdkit_min_version = (1, 2)
+let nbdkit_min_version_string = "1.2"
+
type t = {
(* The nbdkit plugin name. *)
plugin_name : string;
@@ -35,42 +38,76 @@ type t = {
(* Environment variables that may be needed for nbdkit to work. *)
env : (string * string) list;
+
+ (* nbdkit --dump-config output. *)
+ dump_config : (string * string) list;
+
+ (* nbdkit plugin_name --dump-plugin output. *)
+ dump_plugin : (string * string) list;
}
(* Check that nbdkit is available and new enough. *)
let error_unless_nbdkit_working () =
if 0 <> Sys.command "nbdkit --version >/dev/null" then
- error (f_"nbdkit is not installed or not working");
+ error (f_"nbdkit is not installed or not working")
- (* Check it's a new enough version. The latest features we
- * require are ‘--exit-with-parent’ and ‘--selinux-label’, both
- * added in 1.1.14. (We use 1.1.16 as the minimum here because
- * it also adds the selinux=yes|no flag in --dump-config).
- *)
- let lines = external_command "nbdkit --help" in
- let lines = String.concat " " lines in
- if String.find lines "exit-with-parent" == -1 ||
- String.find lines "selinux-label" == -1 then
- error (f_"nbdkit is not new enough, you need to upgrade to nbdkit ≥
1.1.16")
+(* Check that nbdkit is at or above the minimum version. *)
+let re_major_minor = PCRE.compile "(\\d+)\\.(\\d+)"
+
+let error_unless_nbdkit_min_version dump_config =
+ let version =
+ let version =
+ try List.assoc "version" dump_config
+ with Not_found ->
+ error (f_"nbdkit --dump-config did not print version. This might be a very
old or broken nbdkit binary.") in
+ debug "nbdkit version: %s" version;
+ if PCRE.matches re_major_minor version then
+ (int_of_string (PCRE.sub 1), int_of_string (PCRE.sub 2))
+ else
+ error (f_"nbdkit --dump-config: could not parse version: %s") version in
+
+ if version < nbdkit_min_version then
+ error (f_"nbdkit is too old. nbdkit >= %s is required.")
+ nbdkit_min_version_string
(* Check that nbdkit was compiled with SELinux support (for the
* --selinux-label option).
*)
-let error_unless_nbdkit_compiled_with_selinux () =
+let error_unless_nbdkit_compiled_with_selinux dump_config =
if have_selinux then (
- let lines = external_command "nbdkit --dump-config" in
- (* In nbdkit <= 1.1.15 the selinux attribute was not present
- * at all in --dump-config output so there was no way to tell.
- * Ignore this case because there will be an error later when
- * we try to use the --selinux-label parameter.
- *)
- if List.mem "selinux=no" (List.map String.trim lines) then
+ let selinux = try List.assoc "selinux" dump_config with Not_found ->
"no" in
+ if selinux = "no" then
error (f_"nbdkit was compiled without SELinux support. You will have to
recompile nbdkit with libselinux-devel installed, or else set SELinux to Permissive mode
while doing the conversion.")
)
let common_create plugin_name plugin_args plugin_env =
error_unless_nbdkit_working ();
- error_unless_nbdkit_compiled_with_selinux ();
+
+ (* Environment. We always add LANG=C. *)
+ let env = ("LANG", "C") :: plugin_env in
+ let env_as_string =
+ String.concat " " (List.map (fun (k, v) -> sprintf "%s=%s" k
(quote v))
+ env) in
+
+ (* Get the nbdkit --dump-config output and check minimum
+ * required version of nbdkit.
+ *)
+ let dump_config =
+ let lines =
+ external_command (sprintf "%s nbdkit --dump-config" env_as_string) in
+ List.map (String.split "=") lines in
+
+ error_unless_nbdkit_min_version dump_config;
+ error_unless_nbdkit_compiled_with_selinux dump_config;
+
+ (* Get the nbdkit plugin_name --dump-plugin output, which also
+ * checks that the plugin is available and loadable.
+ *)
+ let dump_plugin =
+ let lines =
+ external_command (sprintf "%s nbdkit %s --dump-plugin"
+ env_as_string plugin_name) in
+ List.map (String.split "=") lines in
(* Start constructing the parts of the incredibly long nbdkit
* command line which don't change between disks.
@@ -93,10 +130,7 @@ let common_create plugin_name plugin_args plugin_env =
);
let args = get_args () @ [ plugin_name ] @ plugin_args in
- (* Environment. We always add LANG=C. *)
- let env = ("LANG", "C") :: plugin_env in
-
- { plugin_name; args; env }
+ { plugin_name; args; env; dump_config; dump_plugin }
(* VDDK libraries are located under lib32/ or lib64/ relative to the
* libdir. Note this is unrelated to Linux multilib or multiarch.
@@ -109,6 +143,10 @@ let create_vddk ?config ?cookie ?libdir ~moref
~server ?snapshot ~thumbprint ?transports ?user path =
(* Compute the LD_LIBRARY_PATH that we may have to pass to nbdkit. *)
let ld_library_path = Option.map (fun libdir -> libdir // libNN) libdir in
+ let env =
+ match ld_library_path with
+ | None -> []
+ | Some ld_library_path -> ["LD_LIBRARY_PATH", ld_library_path] in
(* Check that the VDDK path looks reasonable. *)
let error_unless_vddk_libdir () =
@@ -127,23 +165,22 @@ let create_vddk ?config ?cookie ?libdir ~moref
)
in
- (* Check that the VDDK plugin is installed and working *)
+ (* Check that the VDDK plugin is installed and working. We also
+ * check this later when calling create_process, but this version
+ * has better troubleshooting output.
+ *)
let error_unless_nbdkit_vddk_working () =
- let set_ld_library_path =
- match ld_library_path with
- | None -> ""
- | Some ld_library_path ->
- sprintf "LD_LIBRARY_PATH=%s " (quote ld_library_path) in
-
+ let env_as_string =
+ String.concat " " (List.map (fun (k, v) -> sprintf "%s=%s" k
(quote v))
+ env) in
let cmd =
- sprintf "%snbdkit vddk --dump-plugin >/dev/null"
- set_ld_library_path in
+ sprintf "%s nbdkit vddk --dump-plugin >/dev/null" env_as_string in
if Sys.command cmd <> 0 then (
(* See if we can diagnose why ... *)
let cmd =
- sprintf "LANG=C %snbdkit vddk --dump-plugin 2>&1 |
+ sprintf "LANG=C %s nbdkit vddk --dump-plugin 2>&1 |
grep -sq \"cannot open shared object file\""
- set_ld_library_path in
+ env_as_string in
let needs_library = Sys.command cmd = 0 in
if not needs_library then
error (f_"nbdkit VDDK plugin is not installed or not working. It is
required if you want to use VDDK.
@@ -196,11 +233,6 @@ See also the virt-v2v-input-vmware(1) manual.") libNN
add_arg (sprintf "thumbprint=%s" thumbprint);
Option.may (fun s -> add_arg (sprintf "transports=%s" s)) transports;
- let env =
- match ld_library_path with
- | None -> []
- | Some ld_library_path -> ["LD_LIBRARY_PATH", ld_library_path] in
-
common_create "vddk" (get_args ()) env
let run { args; env } =
--
2.20.1