From ca9f904b221b0812a7b22c86f619a2dd04367ed5 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 4 Apr 2019 15:13:25 +0100 Subject: [PATCH] v2v: Generic code for querying nbdkit version and plugin. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 6bf38daa0..8ae6549e9 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 common_create, 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 } =