Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 00/11] v2v: Change virt-v2v to use nbdkit for input in several modes.
This series (except the last one) changes virt-v2v to use nbdkit for several input modes: -i vmx -it vddk: No change in functionality, as this already uses nbdkit-vddk-plugin, but the code is refactored for the other modes to use. -i libvirtxml: Use nbdkit-curl-plugin instead of qemu curl. vCenter: Use nbdkit-curl-plugin instead of qemu curl. xen: Use nbdkit-ssh-plugin instead of qemu ssh. -i vmx -it ssh: Use nbdkit-ssh-plugin instead of qemu ssh. I was hoping to post this patch series when it was longer and showed some benefits. However those patches aren't ready quite yet, but these ones are ready for review. However some of the future benefits will be: * using nbdkit-rate-plugin to bandwidth limit the input side * better support for modern SSH encryption * possibility of SSH password auth (instead of requiring ssh-agent) I have tested this locally. Unfortunately none of the existing tests cover any of these input cases. The last patch reduces the minimum required version of nbdkit from 1.12 (not yet available) to 1.11, so this patch can be tested in Fedora 30. It is obviously not meant to be applied. Rich.
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 01/11] v2v: Move have_selinux to utils.
This is not quite a neutral refactoring, because it means we now run the getenforce command every time virt-v2v starts up. However it's a trivial command that reads a single /sys file and it can't fail even if the command is missing or on platforms that know nothing about SELinux. --- v2v/input_libvirt_vddk.ml | 4 ---- v2v/output_rhv_upload.ml | 4 ---- v2v/utils.ml | 4 ++++ v2v/utils.mli | 3 +++ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/v2v/input_libvirt_vddk.ml b/v2v/input_libvirt_vddk.ml index 97c7cb532..e2efef842 100644 --- a/v2v/input_libvirt_vddk.ml +++ b/v2v/input_libvirt_vddk.ml @@ -108,10 +108,6 @@ class input_libvirt_vddk input_conn input_password vddk_options parsed_uri (* Compute the LD_LIBRARY_PATH that we may have to pass to nbdkit. *) let library_path = Option.map (fun libdir -> libdir // libNN) libdir in - (* Is SELinux enabled and enforcing on the host? *) - let have_selinux - 0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing" in - (* Check that the VDDK path looks reasonable. *) let error_unless_vddk_libdir () (match libdir with diff --git a/v2v/output_rhv_upload.ml b/v2v/output_rhv_upload.ml index 77c39107e..0709c8152 100644 --- a/v2v/output_rhv_upload.ml +++ b/v2v/output_rhv_upload.ml @@ -105,10 +105,6 @@ class output_rhv_upload output_alloc output_conn Python_script.create ~name:"rhv-upload-createvm.py" Output_rhv_upload_createvm_source.code in - (* Is SELinux enabled and enforcing on the host? *) - let have_selinux - 0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing" in - (* Check that the 'ovirtsdk4' Python module is available. *) let error_unless_ovirtsdk4_module_available () let res = run_command [ Python_script.python; "-c"; "import ovirtsdk4" ] in diff --git a/v2v/utils.ml b/v2v/utils.ml index 74b501f81..9be4d2dc4 100644 --- a/v2v/utils.ml +++ b/v2v/utils.ml @@ -24,6 +24,10 @@ open Std_utils open Tools_utils open Common_gettext.Gettext +(* Is SELinux enabled and enforcing on the host? *) +let have_selinux + 0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing" + (* URI quoting. *) let uri_quote str let len = String.length str in diff --git a/v2v/utils.mli b/v2v/utils.mli index 4635d4808..61cba1bbd 100644 --- a/v2v/utils.mli +++ b/v2v/utils.mli @@ -18,6 +18,9 @@ (** Utilities used in virt-v2v only. *) +val have_selinux : bool +(** True if SELinux is enabled and enforcing on the host. *) + val uri_quote : string -> string (** Take a string and perform %xx escaping as used in some parts of URLs. *) -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 02/11] v2v: Factor out the nbdkit VDDK code into a new module.
This refactoring takes the nbdkit-specific code from the ‘-it vddk’ mode and puts it into a separate module. The idea is to reuse this module (in future commits) to support ssh, curl and rate limiting. This refactoring is not quite neutral because it moves some of the prechecking into the Nbdkit.create function. This means it will happen a little bit later in the cycle (in input#source instead of input#precheck - refer to the diagram in v2v/types.mli). However it's still almost in the same place and importantly still happens before we start copying. --- v2v/Makefile.am | 2 + v2v/input_libvirt_vddk.ml | 289 ++++++-------------------------------- v2v/nbdkit.ml | 280 ++++++++++++++++++++++++++++++++++++ v2v/nbdkit.mli | 47 +++++++ 4 files changed, 373 insertions(+), 245 deletions(-) diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 39511022e..b391382f3 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -73,6 +73,7 @@ SOURCES_MLI = \ measure_disk.mli \ modules_list.mli \ name_from_disk.mli \ + nbdkit.mli \ networks.mli \ openstack_image_properties.mli \ output_glance.mli \ @@ -112,6 +113,7 @@ SOURCES_ML = \ var_expander.ml \ python_script.ml \ name_from_disk.ml \ + nbdkit.ml \ vCenter.ml \ libvirt_utils.ml \ DOM.ml \ diff --git a/v2v/input_libvirt_vddk.ml b/v2v/input_libvirt_vddk.ml index e2efef842..a553a1d10 100644 --- a/v2v/input_libvirt_vddk.ml +++ b/v2v/input_libvirt_vddk.ml @@ -18,8 +18,6 @@ (** [-i libvirt] when the source is VMware via nbdkit vddk plugin *) -open Unix - open Common_gettext.Gettext open Tools_utils open Std_utils @@ -95,115 +93,16 @@ let parse_input_options options (* Subclass specialized for handling VMware via nbdkit vddk plugin. *) class input_libvirt_vddk input_conn input_password vddk_options parsed_uri guest - (* The VDDK path. *) - let libdir - try Some (List.assoc "libdir" vddk_options) - with Not_found -> None in - - (* VDDK libraries are located under lib32/ or lib64/ relative to the - * libdir. Note this is unrelated to Linux multilib or multiarch. - *) - let libNN = sprintf "lib%d" Sys.word_size in - - (* Compute the LD_LIBRARY_PATH that we may have to pass to nbdkit. *) - let library_path = Option.map (fun libdir -> libdir // libNN) libdir in - - (* Check that the VDDK path looks reasonable. *) - let error_unless_vddk_libdir () - (match libdir with - | None -> () - | Some libdir -> - if not (is_directory libdir) then - error (f_"‘-io vddk-libdir=%s’ does not point to a directory. See the virt-v2v-input-vmware(1) manual.") libdir - ); - - (match library_path with - | None -> () - | Some library_path -> - if not (is_directory library_path) then - error (f_"VDDK library path %s not found or not a directory. See the virt-v2v-input-vmware(1) manual.") library_path - ) - in - - (* 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. It is required to use ‘-it vddk’. See the virt-v2v-input-vmware(1) manual."); - - (* 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") - in - - (* Check that the VDDK plugin is installed and working *) - let error_unless_nbdkit_vddk_working () - let set_ld_library_path - match library_path with - | None -> "" - | Some library_path -> - sprintf "LD_LIBRARY_PATH=%s " (quote library_path) in - - let cmd - sprintf "%snbdkit vddk --dump-plugin >/dev/null" - set_ld_library_path in - if Sys.command cmd <> 0 then ( - (* See if we can diagnose why ... *) - let cmd - sprintf "LANG=C %snbdkit vddk --dump-plugin 2>&1 | - grep -sq \"cannot open shared object file\"" - set_ld_library_path 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. - -The VDDK plugin is not enabled by default when you compile nbdkit. You have to read the instructions in the nbdkit sources under ‘plugins/vddk/README.VDDK’ to find out how to enable the VDDK plugin. - -See also the virt-v2v-input-vmware(1) manual.") - else - error (f_"nbdkit VDDK plugin is not installed or not working. It is required if you want to use VDDK. - -It looks like you did not set the right path in the ‘-io vddk-libdir’ option, or your copy of the VDDK directory is incomplete. There should be a library called ’<libdir>/%s/libvixDiskLib.so.?’. - -See also the virt-v2v-input-vmware(1) manual.") libNN - ) - in - let error_unless_thumbprint () if not (List.mem_assoc "thumbprint" vddk_options) then error (f_"You must pass the ‘-io vddk-thumbprint’ option with the SSL thumbprint of the VMware server. To find the thumbprint, see the nbdkit-vddk-plugin(1) manual. See also the virt-v2v-input-vmware(1) manual.") in - (* Check that nbdkit was compiled with SELinux support (for the - * --selinux-label option). - *) - let error_unless_nbdkit_compiled_with_selinux () - 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 - 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.") - in - object inherit input_libvirt input_conn input_password guest as super method precheck () - error_unless_vddk_libdir (); - error_unless_nbdkit_working (); - error_unless_nbdkit_vddk_working (); - error_unless_thumbprint (); - if have_selinux then - error_unless_nbdkit_compiled_with_selinux () + error_unless_thumbprint () method as_options let pt_options @@ -236,79 +135,40 @@ object | None -> error (f_"<vmware:moref> was not found in the output of ‘virsh dumpxml \"%s\"’. The most likely reason is that libvirt is too old, try upgrading libvirt to ≥ 3.7.") guest in - (* Create a temporary directory where we place the sockets. *) - let tmpdir - let base_dir = (open_guestfs ())#get_cachedir () in - let t = Mkdtemp.temp_dir ~base_dir "vddk." in - (* tmpdir must be readable (but not writable) by "other" so that - * qemu can open the sockets. - *) - chmod t 0o755; - rmdir_on_exit t; - t in - - (* Start constructing the parts of the incredibly long nbdkit - * command line which don't change between disks. + (* It probably never happens that the server name can be missing + * from the libvirt URI, but we need a server name to pass to + * nbdkit, so ... *) - let args - let add_arg, get_args - let args = ref [] in - let add_arg a = List.push_front a args in - let get_args () = List.rev !args in - add_arg, get_args in + let server + match parsed_uri.Xml.uri_server with + | Some server -> server + | None -> + match input_conn with + | Some input_conn -> + error (f_"‘-ic %s’ URL does not contain a host name field") + input_conn + | None -> + error (f_"you must use the ‘-ic’ parameter. See the virt-v2v-input-vmware(1) manual.") in - (* It probably never happens that the server name can be missing - * from the libvirt URI, but we need a server name to pass to - * nbdkit, so ... - *) - let server - match parsed_uri.Xml.uri_server with - | Some server -> server - | None -> - match input_conn with - | Some input_conn -> - error (f_"‘-ic %s’ URL does not contain a host name field") - input_conn - | None -> - error (f_"you must use the ‘-ic’ parameter. See the virt-v2v-input-vmware(1) manual.") in + let user = parsed_uri.Xml.uri_user in - (* Similar to above, we also need a username to pass. *) - let user - match parsed_uri.Xml.uri_user with - | Some user -> user - | None -> "root" (* ? *) in - - add_arg "nbdkit"; - if verbose () then add_arg "--verbose"; - add_arg "--readonly"; (* important! readonly mode *) - add_arg "--foreground"; (* run in foreground *) - add_arg "--exit-with-parent"; (* exit when virt-v2v exits *) - add_arg "--newstyle"; (* use newstyle NBD protocol *) - add_arg "--exportname"; add_arg "/"; - if have_selinux then ( (* label the socket so qemu can open it *) - add_arg "--selinux-label"; add_arg "system_u:object_r:svirt_t:s0" - ); - - (* Name of the plugin. Everything following is a plugin parameter. *) - add_arg "vddk"; - - let password_param - match input_password with - | None -> - (* nbdkit asks for the password interactively *) - "password=-" - | Some password_file -> - (* nbdkit reads the password from the file *) - "password=+" ^ password_file in - add_arg (sprintf "server=%s" server); - add_arg (sprintf "user=%s" user); - add_arg password_param; - add_arg (sprintf "vm=moref=%s" moref); - - (* The passthrough parameters. *) - List.iter (fun (k, v) -> add_arg (sprintf "%s=%s" k v)) vddk_options; - - get_args () in + let config + try Some (List.assoc "config" vddk_options) with Not_found -> None in + let cookie + try Some (List.assoc "cookie" vddk_options) with Not_found -> None in + let libdir + try Some (List.assoc "libdir" vddk_options) with Not_found -> None in + let nfchostport + try Some (List.assoc "nfchostport" vddk_options) with Not_found -> None in + let port + try Some (List.assoc "port" vddk_options) with Not_found -> None in + let snapshot + try Some (List.assoc "snapshot" vddk_options) with Not_found -> None in + let thumbprint + try List.assoc "thumbprint" vddk_options + with Not_found -> assert false (* checked in precheck method *) in + let transports + try Some (List.assoc "transports" vddk_options) with Not_found -> None in (* Create an nbdkit instance for each disk and rewrite the source * paths to point to the NBD socket. @@ -327,79 +187,18 @@ object * directly as the nbdkit file= parameter, and it is passed * directly in this form to VDDK. *) - - let sock = tmpdir // sprintf "nbdkit%d.sock" disk.s_disk_id in - let qemu_uri = sprintf "nbd:unix:%s:exportname=/" sock in - - let pidfile = tmpdir // sprintf "nbdkit%d.pid" disk.s_disk_id in - - (* Construct the final command line with the "static" args - * above plus the args which vary for each disk. - *) - let args - args @ [ "--pidfile"; pidfile; - "--unix"; sock; - sprintf "file=%s" path ] in - - (* Print the full command we are about to run when debugging. *) - if verbose () then ( - eprintf "running nbdkit:\n"; - Option.may (eprintf "LD_LIBRARY_PATH=%s") library_path; - List.iter (fun arg -> eprintf " %s" (quote arg)) args; - prerr_newline () - ); - - (* Start an nbdkit instance in the background. By using - * --exit-with-parent we don't have to worry about cleaning - * it up, hopefully. - *) - let args = Array.of_list args in - let pid = fork () in - if pid = 0 then ( - (* Child process (nbdkit). *) - Option.may (putenv "LD_LIBRARY_PATH") library_path; - execvp "nbdkit" args - ); - - (* Wait for the pidfile to appear so we know that nbdkit - * is listening for requests. - *) - if not (wait_for_file pidfile 30) then ( - if verbose () then - error (f_"nbdkit did not start up. See previous debugging messages for problems.") - else - error (f_"nbdkit did not start up. There may be errors printed by nbdkit above. - -If the messages above are not sufficient to diagnose the problem then add the ‘virt-v2v -v -x’ options and examine the debugging output carefully.") - ); - - if have_selinux then ( - (* Note that Unix domain sockets have both a file label and - * a socket/process label. Using --selinux-label above - * only set the socket label, but we must also set the file - * label. - *) - ignore ( - run_command ["chcon"; "system_u:object_r:svirt_image_t:s0"; - sock] - ); - ); - (* ... and the regular Unix permissions, in case qemu is - * running as another user. - *) - chmod sock 0o777; - - (* nbdkit from a vddk source always presents us with the raw - * disk blocks from the guest, so force the format to raw here. - *) - { disk with s_qemu_uri = qemu_uri; - s_format = Some "raw" } - ) disks in - - if verbose () then ( - eprintf "vddk: tmpdir %s:\n%!" tmpdir; - ignore (Sys.command (sprintf "ls -laZ %s" (quote tmpdir))) - ); + let nbdkit + Nbdkit.create_vddk ?config ?cookie ?libdir ~moref + ?nfchostport ?password_file:input_password ?port + ~server ?snapshot ~thumbprint ?transports ?user + path in + let qemu_uri = Nbdkit.run nbdkit in + + (* nbdkit always presents us with the raw disk blocks from + * the guest, so force the format to raw here. + *) + { disk with s_qemu_uri = qemu_uri; s_format = Some "raw" } + ) disks in { source with s_disks = disks } end diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml new file mode 100644 index 000000000..f682d742a --- /dev/null +++ b/v2v/nbdkit.ml @@ -0,0 +1,280 @@ +(* virt-v2v + * Copyright (C) 2009-2019 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *) + +open Unix +open Printf + +open Common_gettext.Gettext +open Std_utils +open Tools_utils +open Unix_utils + +open Utils + +type t = { + (* The nbdkit plugin name. *) + plugin_name : string; + + (* Parameters (includes the plugin name). *) + args : string list; + + (* Environment variables that may be needed for nbdkit to work. *) + env : (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"); + + (* 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 was compiled with SELinux support (for the + * --selinux-label option). + *) +let error_unless_nbdkit_compiled_with_selinux () + 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 + 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 (); + + (* Start constructing the parts of the incredibly long nbdkit + * command line which don't change between disks. + *) + let add_arg, get_args + let args = ref [] in + let add_arg a = List.push_front a args in + let get_args () = List.rev !args in + add_arg, get_args in + + add_arg "nbdkit"; + if verbose () then add_arg "--verbose"; + add_arg "--readonly"; (* important! readonly mode *) + add_arg "--foreground"; (* run in foreground *) + add_arg "--exit-with-parent"; (* exit when virt-v2v exits *) + add_arg "--newstyle"; (* use newstyle NBD protocol *) + add_arg "--exportname"; add_arg "/"; + if have_selinux then ( (* label the socket so qemu can open it *) + add_arg "--selinux-label"; add_arg "system_u:object_r:svirt_t:s0" + ); + 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 } + +(* VDDK libraries are located under lib32/ or lib64/ relative to the + * libdir. Note this is unrelated to Linux multilib or multiarch. + *) +let libNN = sprintf "lib%d" Sys.word_size + +(* Create an nbdkit module specialized for reading from VDDK sources. *) +let create_vddk ?config ?cookie ?libdir ~moref + ?nfchostport ?password_file ?port + ~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 + + (* Check that the VDDK path looks reasonable. *) + let error_unless_vddk_libdir () + (match libdir with + | None -> () + | Some libdir -> + if not (is_directory libdir) then + error (f_"‘-io vddk-libdir=%s’ does not point to a directory. See the virt-v2v-input-vmware(1) manual.") libdir + ); + + (match ld_library_path with + | None -> () + | Some ld_library_path -> + if not (is_directory ld_library_path) then + error (f_"VDDK library path %s not found or not a directory. See the virt-v2v-input-vmware(1) manual.") ld_library_path + ) + in + + (* Check that the VDDK plugin is installed and working *) + 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 cmd + sprintf "%snbdkit vddk --dump-plugin >/dev/null" + set_ld_library_path in + if Sys.command cmd <> 0 then ( + (* See if we can diagnose why ... *) + let cmd + sprintf "LANG=C %snbdkit vddk --dump-plugin 2>&1 | + grep -sq \"cannot open shared object file\"" + set_ld_library_path 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. + +The VDDK plugin is not enabled by default when you compile nbdkit. You have to read the instructions in the nbdkit sources under ‘plugins/vddk/README.VDDK’ to find out how to enable the VDDK plugin. + +See also the virt-v2v-input-vmware(1) manual.") + else + error (f_"nbdkit VDDK plugin is not installed or not working. It is required if you want to use VDDK. + +It looks like you did not set the right path in the ‘-io vddk-libdir’ option, or your copy of the VDDK directory is incomplete. There should be a library called ’<libdir>/%s/libvixDiskLib.so.?’. + +See also the virt-v2v-input-vmware(1) manual.") libNN + ) + in + + error_unless_vddk_libdir (); + error_unless_nbdkit_vddk_working (); + + (* For VDDK we require some user. If it's not supplied, assume root. *) + let user = Option.default "root" user in + + let add_arg, get_args + let args = ref [] in + let add_arg a = List.push_front a args in + let get_args () = List.rev !args in + add_arg, get_args in + + let password_param + match password_file with + | None -> + (* nbdkit asks for the password interactively *) + "password=-" + | Some password_file -> + (* nbdkit reads the password from the file *) + "password=+" ^ password_file in + add_arg (sprintf "server=%s" server); + add_arg (sprintf "user=%s" user); + add_arg password_param; + add_arg (sprintf "vm=moref=%s" moref); + add_arg (sprintf "file=%s" path); + + (* The passthrough parameters. *) + Option.may (fun s -> add_arg (sprintf "config=%s" s)) config; + Option.may (fun s -> add_arg (sprintf "cookie=%s" s)) cookie; + Option.may (fun s -> add_arg (sprintf "libdir=%s" s)) libdir; + Option.may (fun s -> add_arg (sprintf "nfchostport=%s" s)) nfchostport; + Option.may (fun s -> add_arg (sprintf "port=%s" s)) port; + Option.may (fun s -> add_arg (sprintf "snapshot=%s" s)) snapshot; + 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 } + (* Create a temporary directory where we place the sockets. *) + let tmpdir + let base_dir = (open_guestfs ())#get_cachedir () in + let t = Mkdtemp.temp_dir ~base_dir "v2vnbdkit." in + (* tmpdir must be readable (but not writable) by "other" so that + * qemu can open the sockets. + *) + chmod t 0o755; + rmdir_on_exit t; + t in + + let id = unique () in + let sock = tmpdir // sprintf "nbdkit%d.sock" id in + let qemu_uri = sprintf "nbd:unix:%s:exportname=/" sock in + let pidfile = tmpdir // sprintf "nbdkit%d.pid" id in + + (* Construct the final command line with the "static" args + * above plus the pidfile and socket which vary for each run. + *) + let args = args @ [ "--pidfile"; pidfile; "--unix"; sock ] in + + (* Print the full command we are about to run when debugging. *) + if verbose () then ( + eprintf "running nbdkit:\n"; + List.iter (fun (k, v) -> eprintf " %s=%s" k v) env; + List.iter (fun arg -> eprintf " %s" (quote arg)) args; + prerr_newline () + ); + + (* Start an nbdkit instance in the background. By using + * --exit-with-parent we don't have to worry about cleaning + * it up, hopefully. + *) + let args = Array.of_list args in + let pid = fork () in + if pid = 0 then ( + (* Child process (nbdkit). *) + List.iter (fun (k, v) -> putenv k v) env; + execvp "nbdkit" args + ); + + (* Wait for the pidfile to appear so we know that nbdkit + * is listening for requests. + *) + if not (wait_for_file pidfile 30) then ( + if verbose () then + error (f_"nbdkit did not start up. See previous debugging messages for problems.") + else + error (f_"nbdkit did not start up. There may be errors printed by nbdkit above. + +If the messages above are not sufficient to diagnose the problem then add the ‘virt-v2v -v -x’ options and examine the debugging output carefully.") + ); + + if have_selinux then ( + (* Note that Unix domain sockets have both a file label and + * a socket/process label. Using --selinux-label above + * only set the socket label, but we must also set the file + * label. + *) + ignore ( + run_command ["chcon"; "system_u:object_r:svirt_image_t:s0"; sock] + ); + ); + (* ... and the regular Unix permissions, in case qemu is + * running as another user. + *) + chmod sock 0o777; + + if verbose () then ( + eprintf "nbdkit: tmpdir %s:\n%!" tmpdir; + ignore (Sys.command (sprintf "ls -laZ %s" (quote tmpdir))) + ); + + qemu_uri diff --git a/v2v/nbdkit.mli b/v2v/nbdkit.mli new file mode 100644 index 000000000..3bdec1b56 --- /dev/null +++ b/v2v/nbdkit.mli @@ -0,0 +1,47 @@ +(* virt-v2v + * Copyright (C) 2009-2019 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *) + +(** nbdkit when used as a source. *) + +type t + +val create_vddk : ?config:string -> + ?cookie:string -> + ?libdir:string -> + moref:string -> + ?nfchostport:string -> + ?password_file:string -> + ?port:string -> + server:string -> + ?snapshot:string -> + thumbprint:string -> + ?transports:string -> + ?user:string -> + string -> t +(** Create a nbdkit object using the VDDK plugin. The required + string parameter is the disk remote path. + + This can fail (calling [error]) for a variety of reasons, such + as nbdkit not being available, wrong version, missing plugin, etc. + + Note this doesn't run nbdkit yet, it just creates the object. *) + +val run : t -> string +(** Start running nbdkit. + + Returns the QEMU URI that you can use to connect to this instance. *) -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 03/11] v2v: Generic code for querying nbdkit version and plugin.
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
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 04/11] v2v: nbdkit: Add support for nbdkit-ssh-plugin.
--- v2v/nbdkit.ml | 26 ++++++++++++++++++++++++++ v2v/nbdkit.mli | 18 ++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml index 8042d72f2..67e0d363a 100644 --- a/v2v/nbdkit.ml +++ b/v2v/nbdkit.ml @@ -29,6 +29,11 @@ open Utils let nbdkit_min_version = (1, 2) let nbdkit_min_version_string = "1.2" +type password +| NoPassword (* no password option at all *) +| AskForPassword (* password=- *) +| PasswordFile of string (* password=+file *) + type t = { (* The nbdkit plugin name. *) plugin_name : string; @@ -235,6 +240,27 @@ See also the virt-v2v-input-vmware(1) manual.") libNN common_create "vddk" (get_args ()) env +(* Create an nbdkit module specialized for reading from SSH sources. *) +let create_ssh ~password ?port ~server ?user path + let add_arg, get_args + let args = ref [] in + let add_arg a = List.push_front a args in + let get_args () = List.rev !args in + add_arg, get_args in + + add_arg (sprintf "host=%s" server); + Option.may (fun s -> add_arg (sprintf "port=%s" s)) port; + Option.may (fun s -> add_arg (sprintf "user=%s" s)) user; + (match password with + | NoPassword -> () + | AskForPassword -> add_arg "password=-" + | PasswordFile password_file -> + add_arg (sprintf "password=+%s" password_file) + ); + add_arg (sprintf "path=%s" path); + + common_create "ssh" (get_args ()) [] + let run { args; env } (* Create a temporary directory where we place the sockets. *) let tmpdir diff --git a/v2v/nbdkit.mli b/v2v/nbdkit.mli index 3bdec1b56..36faff03b 100644 --- a/v2v/nbdkit.mli +++ b/v2v/nbdkit.mli @@ -41,6 +41,24 @@ val create_vddk : ?config:string -> Note this doesn't run nbdkit yet, it just creates the object. *) +type password +| NoPassword +| AskForPassword +| PasswordFile of string + +val create_ssh : password:password -> + ?port:string -> + server:string -> + ?user:string -> + string -> t +(** Create a nbdkit object using the SSH plugin. The required + string parameter is the remote path. + + This can fail (calling [error]) for a variety of reasons, such + as nbdkit not being available, wrong version, missing plugin, etc. + + Note this doesn't run nbdkit yet, it just creates the object. *) + val run : t -> string (** Start running nbdkit. -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 05/11] v2v: xen: Replace qemu block ssh driver with nbdkit-ssh-plugin.
Initially this is a like-for-like replacement, but in future commits this will allow us to implement: - password authentication (instead of SSH agent) - bandwidth throttling - readahead Note this requires nbdkit >= 1.12. --- docs/guestfs-building.pod | 5 +++- v2v/input_libvirt_xen_ssh.ml | 48 +++++++++--------------------------- v2v/nbdkit.ml | 4 +-- 3 files changed, 17 insertions(+), 40 deletions(-) diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod index 47efe34a5..c9693d277 100644 --- a/docs/guestfs-building.pod +++ b/docs/guestfs-building.pod @@ -268,7 +268,7 @@ Optional. Used only for testing. =item qemu-nbd -=item nbdkit +=item nbdkit E<ge> 1.12 Optional. qemu-nbd is used for testing. @@ -276,6 +276,9 @@ L<virt-p2v(1)> requires either qemu-nbd or nbdkit, but these only need to be present on the virt-p2v ISO, they do not need to be installed at compile time. +L<virt-v2v(1)> requires nbdkit E<ge> 1.12 for various input and output +modes. + =item uml_mkcow Optional. For the L<UML backend|guestfs(3)/BACKEND>. diff --git a/v2v/input_libvirt_xen_ssh.ml b/v2v/input_libvirt_xen_ssh.ml index 6b77db029..cae6dac4d 100644 --- a/v2v/input_libvirt_xen_ssh.ml +++ b/v2v/input_libvirt_xen_ssh.ml @@ -50,18 +50,15 @@ object ?conn:input_conn guest in let source, disks = parse_libvirt_xml ?conn:input_conn xml in + let port + match parsed_uri.uri_port with + | 0 | 22 -> None + | i -> Some (string_of_int i) in + + let user = parsed_uri.uri_user in + (* Map the <source/> filename (which is relative to the remote - * Xen server) to an ssh URI. This is a JSON URI looking something - * like this: - * - * json: { - * "file.driver": "ssh", - * "file.user": "username", - * "file.host": "xen-host", - * "file.port": 1022, - * "file.path": <remote-path>, - * "file.host_key_check": "no" - * } + * Xen server) to an ssh URI pointing to nbdkit. *) let disks = List.map ( function @@ -69,32 +66,9 @@ object disk | { p_source_disk = disk; p_source = P_source_dev path } | { p_source_disk = disk; p_source = P_source_file path } -> - (* Construct the JSON parameters. *) - let json_params = [ - "file.driver", JSON.String "ssh"; - "file.path", JSON.String path; - "file.host", JSON.String server; - "file.host_key_check", JSON.String "no"; - ] in - - let json_params - match parsed_uri.uri_port with - | 0 | 22 -> json_params - (* qemu will actually assert-fail if you send the port - * number as a string ... - *) - | i -> ("file.port", JSON.Int (Int64.of_int i)) :: json_params in - - let json_params - match parsed_uri.uri_user with - | None -> json_params - | Some user -> ("file.user", JSON.String user) :: json_params in - - debug "ssh: json parameters: %s" (JSON.string_of_doc json_params); - - (* Turn the JSON parameters into a 'json:' protocol string. *) - let qemu_uri = "json: " ^ JSON.string_of_doc json_params in - + let nbdkit = Nbdkit.create_ssh ~password:NoPassword + ?port ~server ?user path in + let qemu_uri = Nbdkit.run nbdkit in { disk with s_qemu_uri = qemu_uri } ) disks in diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml index 67e0d363a..e67a83b6a 100644 --- a/v2v/nbdkit.ml +++ b/v2v/nbdkit.ml @@ -26,8 +26,8 @@ open Unix_utils open Utils -let nbdkit_min_version = (1, 2) -let nbdkit_min_version_string = "1.2" +let nbdkit_min_version = (1, 12) +let nbdkit_min_version_string = "1.12" type password | NoPassword (* no password option at all *) -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 06/11] v2v: -i vmx -it ssh: Replace qemu block ssh driver with nbdkit-ssh-plugin.
One immediately advantage is we can use libvirt again. --- v2v/input_vmx.ml | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml index b169b2537..f521346e5 100644 --- a/v2v/input_vmx.ml +++ b/v2v/input_vmx.ml @@ -230,24 +230,14 @@ and qemu_uri_of_filename vmx_source filename if remote_file_exists uri flat_vmdk then (flat_vmdk, "raw") else (abs_path, format) in - let json_params = [ - "file.driver", JSON.String "ssh"; - "file.path", JSON.String abs_path; - "file.host", JSON.String (server_of_uri uri); - "file.host_key_check", JSON.String "no"; - ] in - let json_params - match uri.Xml.uri_user with - | None -> json_params - | Some user -> - ("file.user", JSON.String user) :: json_params in - let json_params - match port_of_uri uri with - | None -> json_params - | Some port -> - ("file.port", JSON.Int (Int64.of_int port)) :: json_params in + let server = server_of_uri uri in + let port = Option.map string_of_int (port_of_uri uri) in + let user = uri.Xml.uri_user in - "json:" ^ JSON.string_of_doc json_params, format + let nbdkit = Nbdkit.create_ssh ~password:NoPassword ~server + ?port ?user abs_path in + let qemu_uri = Nbdkit.run nbdkit in + qemu_uri, format and absolute_path_from_other_file other_filename filename if not (Filename.is_relative filename) then filename @@ -396,14 +386,6 @@ object method as_options = "-i vmx " ^ arg - method precheck () - match input_transport with - | None -> () - | Some `SSH -> - if backend_is_libvirt () then - error (f_"because libvirtd doesn't pass the SSH_AUTH_SOCK environment variable to qemu you must set this environment variable:\n\nexport LIBGUESTFS_BACKEND=direct\n\nand then rerun the virt-v2v command."); - error_if_no_ssh_agent () - method source () let vmx_source = vmx_source_of_arg input_transport arg in -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 07/11] v2v: nbdkit: Add support for nbdkit-curl-plugin.
--- v2v/nbdkit.ml | 23 +++++++++++++++++++++++ v2v/nbdkit.mli | 13 +++++++++++++ 2 files changed, 36 insertions(+) diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml index e67a83b6a..7237799a7 100644 --- a/v2v/nbdkit.ml +++ b/v2v/nbdkit.ml @@ -261,6 +261,29 @@ let create_ssh ~password ?port ~server ?user path common_create "ssh" (get_args ()) [] +(* Create an nbdkit module specialized for reading from Curl sources. *) +let create_curl ?cookie ~password ?(sslverify=true) ?user url + let add_arg, get_args + let args = ref [] in + let add_arg a = List.push_front a args in + let get_args () = List.rev !args in + add_arg, get_args in + + Option.may (fun s -> add_arg (sprintf "user=%s" s)) user; + (match password with + | NoPassword -> () + | AskForPassword -> add_arg "password=-" + | PasswordFile password_file -> + add_arg (sprintf "password=+%s" password_file) + ); + (* https://bugzilla.redhat.com/show_bug.cgi?id=1146007#c10 *) + add_arg "timeout=2000"; + Option.may (fun s -> add_arg (sprintf "cookie=%s" s)) cookie; + if not sslverify then add_arg "sslverify=false"; + add_arg (sprintf "url=%s" url); + + common_create "curl" (get_args ()) [] + let run { args; env } (* Create a temporary directory where we place the sockets. *) let tmpdir diff --git a/v2v/nbdkit.mli b/v2v/nbdkit.mli index 36faff03b..efbb62f98 100644 --- a/v2v/nbdkit.mli +++ b/v2v/nbdkit.mli @@ -59,6 +59,19 @@ val create_ssh : password:password -> Note this doesn't run nbdkit yet, it just creates the object. *) +val create_curl : ?cookie:string -> + password:password -> + ?sslverify:bool -> + ?user:string -> + string -> t +(** Create a nbdkit object using the Curl plugin. The required + string parameter is the URL. + + This can fail (calling [error]) for a variety of reasons, such + as nbdkit not being available, wrong version, missing plugin, etc. + + Note this doesn't run nbdkit yet, it just creates the object. *) + val run : t -> string (** Start running nbdkit. -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 08/11] v2v: nbdkit: Add the readahead filter unconditionally if it is available.
The readahead filter is a self-configuring filter that makes sequential reads faster when the plugin is slow (and all of the plugins we use here are always slow). I observed the behaviour of the readahead filter with our qcow2 overlay when converting a guest from a vCenter source. Even when doing random reads, qemu issues 64K reads which happen to also be the minimum request size of the readahead filter, so there is no extra overhead. When doing the sequential copy the readahead filter performed better than qemu curl’s readahead because it scaled the prefetched data appropriately on long contiguous stretches and then shrunk it back to 64K around fragmented parts of the base image. --- v2v/nbdkit.ml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml index 7237799a7..a9f8c1aeb 100644 --- a/v2v/nbdkit.ml +++ b/v2v/nbdkit.ml @@ -49,6 +49,9 @@ type t = { (* nbdkit plugin_name --dump-plugin output. *) dump_plugin : (string * string) list; + + (* nbdkit directory containing the filters. *) + filterdir : string; } (* Check that nbdkit is available and new enough. *) @@ -105,6 +108,12 @@ let common_create plugin_name plugin_args plugin_env error_unless_nbdkit_min_version dump_config; error_unless_nbdkit_compiled_with_selinux dump_config; + (* Get the filterdir. *) + let filterdir + try List.assoc "filterdir" dump_config + with Not_found -> + error (f_"nbdkit --dump-config output did not contain filterdir") in + (* Get the nbdkit plugin_name --dump-plugin output, which also * checks that the plugin is available and loadable. *) @@ -133,9 +142,17 @@ let common_create plugin_name plugin_args plugin_env if have_selinux then ( (* label the socket so qemu can open it *) add_arg "--selinux-label"; add_arg "system_u:object_r:svirt_t:s0" ); + + (* Add the readahead filter is always a win for our access patterns. + * However if it doesn't exist don't worry. + *) + if Sys.file_exists (filterdir // "nbdkit-readahead-filter.so") then ( + add_arg "--filter"; add_arg "readahead" + ); + let args = get_args () @ [ plugin_name ] @ plugin_args in - { plugin_name; args; env; dump_config; dump_plugin } + { plugin_name; args; env; dump_config; dump_plugin; filterdir } (* VDDK libraries are located under lib32/ or lib64/ relative to the * libdir. Note this is unrelated to Linux multilib or multiarch. -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 09/11] v2v: -i libvirtxml: Replace qemu block curl driver with nbdkit-curl-plugin.
‘virt-v2v -i libvirtxml’ has a little-known feature where it can read network disks over HTTP or HTTPS. This can be used to test VMware conversions without needing VMware, although as far as I can tell the current test suite does not use the feature. This commit changes this functionality to use nbdkit-curl-plugin instead of the qemu curl driver. This change shouldn't affect functionality. The readahead size is changed from a fixed 1M buffer to using the readahead filter which is self-configuring. See also commit 38bf2a0f97c2e814d28c447ff6856bdd2d68df36 which originally introduced this functionality in 2017. --- v2v/parse_libvirt_xml.ml | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml index d5d78d367..58749e036 100644 --- a/v2v/parse_libvirt_xml.ml +++ b/v2v/parse_libvirt_xml.ml @@ -46,31 +46,6 @@ let get_drive_slot str offset warning (f_"could not parse device name ‘%s’ from the source libvirt XML") str; None -(* Create a JSON URI for qemu referring to a remote CURL (http/https) - * resource. See also [v2v/vCenter.ml]. - *) -let create_curl_qemu_uri driver host port path - let url - let port - match driver, port with - | _, None -> "" - | "https", Some 443 -> "" - | "http", Some 80 -> "" - | _, Some port when port >= 1 -> ":" ^ string_of_int port - | _, Some port -> invalid_arg "invalid port number in libvirt XML" in - sprintf "%s://%s%s%s" driver host port (uri_quote path) in - - let json_params = [ - "file.driver", JSON.String driver; (* "http" or "https" *) - "file.url", JSON.String url; - "file.timeout", JSON.Int 2000_L; - "file.readahead", JSON.Int (1024_L *^ 1024_L); - (* "file.sslverify", JSON.String "off"; XXX *) - ] in - - (* Turn the JSON parameters into a 'json:' protocol string. *) - "json: " ^ JSON.string_of_doc json_params - let parse_libvirt_xml ?conn xml debug "libvirt xml is:\n%s" xml; @@ -323,7 +298,18 @@ let parse_libvirt_xml ?conn xml * without needing VMware around. *) let path = Option.default "" (xpath_string "source/@name") in - let qemu_uri = create_curl_qemu_uri driver host port path in + let url + let port + match driver, port with + | _, None -> "" + | "https", Some 443 -> "" + | "http", Some 80 -> "" + | _, Some port when port >= 1 -> ":" ^ string_of_int port + | _, Some port -> + invalid_arg "invalid port number in libvirt XML" in + sprintf "%s://%s%s%s" driver host port (uri_quote path) in + let nbdkit = Nbdkit.create_curl ~password:NoPassword url in + let qemu_uri = Nbdkit.run nbdkit in add_disk qemu_uri format controller P_dont_rewrite | Some protocol, _, _ -> warning (f_"<disk type='network'> with <source protocol='%s'> was ignored") -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 10/11] v2v: vCenter: Replace qemu block curl driver with nbdkit-curl-plugin.
Because of the self-configuring readahead plugin we can entirely get rid of input#adjust_overlay_parameters, which is definitely a good thing. --- v2v/Makefile.am | 1 + v2v/input_libvirt_other.mli | 1 - v2v/input_libvirt_vcenter_https.ml | 41 +--------------------------- v2v/types.ml | 1 - v2v/types.mli | 7 ----- v2v/v2v.ml | 7 ----- v2v/vCenter.ml | 43 +++++++----------------------- v2v/vCenter.mli | 4 +-- 8 files changed, 14 insertions(+), 91 deletions(-) diff --git a/v2v/Makefile.am b/v2v/Makefile.am index b391382f3..de84bf996 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -268,6 +268,7 @@ COPY_TO_LOCAL_BOBJECTS = \ uefi.cmo \ utils.cmo \ libvirt_utils.cmo \ + nbdkit.cmo \ vCenter.cmo \ copy_to_local.cmo COPY_TO_LOCAL_XOBJECTS = $(COPY_TO_LOCAL_BOBJECTS:.cmo=.cmx) diff --git a/v2v/input_libvirt_other.mli b/v2v/input_libvirt_other.mli index e6671a76b..886e32d29 100644 --- a/v2v/input_libvirt_other.mli +++ b/v2v/input_libvirt_other.mli @@ -24,7 +24,6 @@ class virtual input_libvirt : string option -> string option -> string -> object method precheck : unit -> unit method as_options : string method virtual source : unit -> Types.source - method adjust_overlay_parameters : Types.overlay -> unit end val input_libvirt_other : string option -> string option -> string -> Types.input diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml index 96f009771..fac3cc5b8 100644 --- a/v2v/input_libvirt_vcenter_https.ml +++ b/v2v/input_libvirt_vcenter_https.ml @@ -30,17 +30,12 @@ open Input_libvirt_other open Printf -(* See RHBZ#1151033 and RHBZ#1153589. *) -let readahead_for_conversion = None -let readahead_for_copying = Some (64 * 1024 * 1024) - (* Subclass specialized for handling VMware vCenter over https. *) class input_libvirt_vcenter_https input_conn input_password parsed_uri server guest object inherit input_libvirt input_conn input_password guest - val saved_source_paths = Hashtbl.create 13 val mutable dcPath = "" method precheck () @@ -82,28 +77,13 @@ object error (f_"vcenter: <vmware:datacenterpath> was not found in the XML. You need to upgrade to libvirt ≥ 1.2.20.") ); - (* Save the original source paths, so that we can remap them again - * in [#adjust_overlay_parameters]. - *) - List.iter ( - function - | { p_source = P_source_dev _ } -> - (* Should never happen ... *) - error (f_"source disk has <source dev=...> attribute in XML") - | { p_source_disk = { s_disk_id = id }; p_source = P_dont_rewrite } -> - Hashtbl.add saved_source_paths id None - | { p_source_disk = { s_disk_id = id }; p_source = P_source_file path } -> - Hashtbl.add saved_source_paths id (Some path) - ) disks; - - let readahead = readahead_for_conversion in let disks = List.map ( function | { p_source = P_source_dev _ } -> assert false | { p_source_disk = disk; p_source = P_dont_rewrite } -> disk | { p_source_disk = disk; p_source = P_source_file path } -> let { VCenter.qemu_uri } - VCenter.map_source ?readahead ?password_file:input_password + VCenter.map_source ?password_file:input_password dcPath parsed_uri server path in (* The libvirt ESX driver doesn't normally specify a format, but @@ -113,25 +93,6 @@ object ) disks in { source with s_disks = disks } - - (* See RHBZ#1151033 and RHBZ#1153589 for why this is necessary. *) - method adjust_overlay_parameters overlay - let orig_path - try Hashtbl.find saved_source_paths overlay.ov_source.s_disk_id - with Not_found -> failwith "internal error in adjust_overlay_parameters" in - match orig_path with - | None -> () - | Some orig_path -> - let readahead = readahead_for_copying in - let { VCenter.qemu_uri = backing_qemu_uri } - VCenter.map_source ?readahead ?password_file:input_password - dcPath parsed_uri server orig_path in - - (* Rebase the qcow2 overlay to adjust the readahead parameter. *) - let cmd = [ "qemu-img"; "rebase"; "-u"; "-b"; backing_qemu_uri; - overlay.ov_overlay_file ] in - if run_command cmd <> 0 then - warning (f_"qemu-img rebase failed (ignored)") end let input_libvirt_vcenter_https = new input_libvirt_vcenter_https diff --git a/v2v/types.ml b/v2v/types.ml index 77f879200..581f5466f 100644 --- a/v2v/types.ml +++ b/v2v/types.ml @@ -510,7 +510,6 @@ class virtual input = object method precheck () = () method virtual as_options : string method virtual source : unit -> source - method adjust_overlay_parameters (_ : overlay) = () end class virtual output = object diff --git a/v2v/types.mli b/v2v/types.mli index be9406100..1441c0109 100644 --- a/v2v/types.mli +++ b/v2v/types.mli @@ -384,10 +384,6 @@ type output_allocation = Sparse | Preallocated │ │ ▼ - input#adjust_overlay_parameters Optional method for adjusting - │ QEMU overlay parameters ready for copying - │ (eg. using a larger readahead setting). - ▼ copying Guest data is copied to the target disks by running ‘qemu-img convert’. v} @@ -404,9 +400,6 @@ class virtual input : object This is just used for pretty-printing log messages. *) method virtual source : unit -> source (** Examine the source hypervisor and create a source struct. *) - method adjust_overlay_parameters : overlay -> unit - (** Called just before copying to allow the input module to adjust - parameters of the overlay disk. *) end (** Encapsulates all [-i], etc input arguments as an object. *) diff --git a/v2v/v2v.ml b/v2v/v2v.ml index 277d8f2c7..be812d5f8 100644 --- a/v2v/v2v.ml +++ b/v2v/v2v.ml @@ -706,13 +706,6 @@ and copy_targets cmdline targets input output if not ((open_guestfs ())#disk_has_backing_file overlay_file) then error (f_"internal error: qemu corrupted the overlay file"); - (* Give the input module a chance to adjust the parameters - * of the overlay/backing file. This allows us to increase - * the readahead parameter when copying (see RHBZ#1151033 and - * RHBZ#1153589 for the gruesome details). - *) - input#adjust_overlay_parameters t.target_overlay; - (match t.target_file with | TargetFile filename -> (* It turns out that libguestfs's disk creation code is diff --git a/v2v/vCenter.ml b/v2v/vCenter.ml index b1b9f9b15..2563ad0ed 100644 --- a/v2v/vCenter.ml +++ b/v2v/vCenter.ml @@ -35,7 +35,7 @@ type remote_resource = { let source_re = PCRE.compile "^\\[(.*)\\] (.*)\\.vmdk$" let snapshot_re = PCRE.compile "^(.*)-\\d{6}(\\.vmdk)$" -let rec map_source ?readahead ?password_file dcPath uri server path +let rec map_source ?password_file dcPath uri server path (* If no_verify=1 was passed in the libvirt URI, then we have to * turn off certificate verification here too. *) @@ -72,38 +72,15 @@ let rec map_source ?readahead ?password_file dcPath uri server path let session_cookie get_session_cookie password_file uri sslverify https_url in - let qemu_uri - (* Construct the JSON parameters for the qemu URI. *) - let json_params = [ - "file.driver", JSON.String "https"; - "file.url", JSON.String https_url; - (* https://bugzilla.redhat.com/show_bug.cgi?id=1146007#c10 *) - "file.timeout", JSON.Int 2000_L; - ] in - - let json_params - match readahead with - | None -> json_params - | Some readahead -> - ("file.readahead", JSON.Int (Int64.of_int readahead)) :: json_params in - - let json_params - if sslverify then json_params - else ("file.sslverify", JSON.String "off") :: json_params in - - let json_params - match session_cookie with - | None -> json_params - | Some cookie -> ("file.cookie", JSON.String cookie) :: json_params in - - debug "vcenter: json parameters: %s" (JSON.string_of_doc json_params); - - (* Turn the JSON parameters into a 'json:' protocol string. - * Note this requires qemu-img >= 2.1.0. - *) - let qemu_uri = "json: " ^ JSON.string_of_doc json_params in - - qemu_uri in + let password + match password_file with + | None -> Nbdkit.NoPassword + | Some password_file -> Nbdkit.PasswordFile password_file in + + let nbdkit + Nbdkit.create_curl ?cookie:session_cookie ~password ~sslverify + https_url in + let qemu_uri = Nbdkit.run nbdkit in (* Return the struct. *) { https_url = https_url; diff --git a/v2v/vCenter.mli b/v2v/vCenter.mli index 840e0a09e..d72d5686e 100644 --- a/v2v/vCenter.mli +++ b/v2v/vCenter.mli @@ -54,8 +54,8 @@ type remote_resource = { (** The "remote resource" is the structure returned by the {!map_source} function. *) -val map_source : ?readahead:int -> ?password_file:string -> string -> Xml.uri -> string -> string -> remote_resource -(** [map_source ?readahead ?password_file dcPath uri server path] +val map_source : ?password_file:string -> string -> Xml.uri -> string -> string -> remote_resource +(** [map_source ?password_file dcPath uri server path] maps the [<source path=...>] string to a {!remote_resource} structure containing both an [https://] URL and a qemu URI, both pointing the guest disk. -- 2.20.1
Richard W.M. Jones
2019-Apr-08 11:10 UTC
[Libguestfs] [PATCH 11/11] TEMPORARY RELAX RESTRICTION TO NBDKIT 1.11
--- v2v/nbdkit.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml index a9f8c1aeb..49af265e9 100644 --- a/v2v/nbdkit.ml +++ b/v2v/nbdkit.ml @@ -26,7 +26,7 @@ open Unix_utils open Utils -let nbdkit_min_version = (1, 12) +let nbdkit_min_version = (1, 11) let nbdkit_min_version_string = "1.12" type password -- 2.20.1
Pino Toscano
2019-May-10 14:12 UTC
Re: [Libguestfs] [PATCH 01/11] v2v: Move have_selinux to utils.
On Monday, April 8, 2019 1:10:07 PM CEST Richard W.M. Jones wrote:> This is not quite a neutral refactoring, because it means we now run > the getenforce command every time virt-v2v starts up. However it's a > trivial command that reads a single /sys file and it can't fail even > if the command is missing or on platforms that know nothing about > SELinux. > ---Alternative approach to retain the current behaviour: - add the proposed "have_selinux" statement as function in Utils, maybe with a better name such as "is_selinux_enforcing" - assigning to the have_selinux variables the result of is_selinux_enforcing () I see this patch was already pushed though ... -- Pino Toscano
Possibly Parallel Threads
- [PATCH v4 00/12] v2v: Change virt-v2v to use nbdkit for input in several modes.
- [PATCH v3 00/12] v2v: Change virt-v2v to use nbdkit for input in several modes.
- [PATCH v2 00/11] v2v: Change virt-v2v to use nbdkit for input in several modes.
- [PATCH] v2v: Implement the --bandwidth* options to control network bandwidth.
- [PATCH v2v 0/4] v2v: vcenter: Implement cookie scripts.