Blame SOURCES/0035-v2v-Implement-input-from-nbdkit-vddk-plugin-RHBZ-147.patch

a30de4
From 9542f12a306e05b73f5c4ecd4e4e61e03797098a Mon Sep 17 00:00:00 2001
a30de4
From: "Richard W.M. Jones" <rjones@redhat.com>
a30de4
Date: Wed, 30 Aug 2017 17:14:03 +0100
a30de4
Subject: [PATCH] v2v: Implement input from nbdkit vddk plugin (RHBZ#1477912).
a30de4
a30de4
(cherry picked from commit bbda5a6a68846acf971e0f4cbf83cacfa083d6e9)
a30de4
---
a30de4
 v2v/Makefile.am            |   2 +
a30de4
 v2v/cmdline.ml             |  53 +++++++-
a30de4
 v2v/input_libvirt.ml       |  16 ++-
a30de4
 v2v/input_libvirt.mli      |   8 +-
a30de4
 v2v/input_libvirt_vddk.ml  | 314 +++++++++++++++++++++++++++++++++++++++++++++
a30de4
 v2v/input_libvirt_vddk.mli |  24 ++++
a30de4
 v2v/types.ml               |  12 ++
a30de4
 v2v/types.mli              |  13 ++
a30de4
 v2v/virt-v2v.pod           | 155 ++++++++++++++++++++++
a30de4
 9 files changed, 588 insertions(+), 9 deletions(-)
a30de4
 create mode 100644 v2v/input_libvirt_vddk.ml
a30de4
 create mode 100644 v2v/input_libvirt_vddk.mli
a30de4
a30de4
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
a30de4
index 0df759eca..87776a509 100644
a30de4
--- a/v2v/Makefile.am
a30de4
+++ b/v2v/Makefile.am
a30de4
@@ -35,6 +35,7 @@ SOURCES_MLI = \
a30de4
 	input_libvirt.mli \
a30de4
 	input_libvirt_other.mli \
a30de4
 	input_libvirt_vcenter_https.mli \
a30de4
+	input_libvirt_vddk.mli \
a30de4
 	input_libvirt_xen_ssh.mli \
a30de4
 	input_libvirtxml.mli \
a30de4
 	input_ova.mli \
a30de4
@@ -89,6 +90,7 @@ SOURCES_ML = \
a30de4
 	input_libvirtxml.ml \
a30de4
 	input_libvirt_other.ml \
a30de4
 	input_libvirt_vcenter_https.ml \
a30de4
+	input_libvirt_vddk.ml \
a30de4
 	input_libvirt_xen_ssh.ml \
a30de4
 	input_libvirt.ml \
a30de4
 	input_ova.ml \
a30de4
diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml
a30de4
index 6b105886c..3050104d0 100644
a30de4
--- a/v2v/cmdline.ml
a30de4
+++ b/v2v/cmdline.ml
a30de4
@@ -63,6 +63,15 @@ let parse_cmdline () =
a30de4
   let output_name = ref None in
a30de4
   let output_storage = ref None in
a30de4
   let password_file = ref None in
a30de4
+  let vddk = ref None in
a30de4
+  let vddk_config = ref None in
a30de4
+  let vddk_cookie = ref None in
a30de4
+  let vddk_nfchostport = ref None in
a30de4
+  let vddk_port = ref None in
a30de4
+  let vddk_snapshot = ref None in
a30de4
+  let vddk_thumbprint = ref None in
a30de4
+  let vddk_transports = ref None in
a30de4
+  let vddk_vimapiver = ref None in
a30de4
   let vdsm_vm_uuid = ref None in
a30de4
   let vdsm_ovf_output = ref None in (* default "." *)
a30de4
 
a30de4
@@ -201,6 +210,24 @@ let parse_cmdline () =
a30de4
                                             s_"Use password from file";
a30de4
     [ L"print-source" ], Getopt.Set print_source, s_"Print source and stop";
a30de4
     [ L"root" ],    Getopt.String ("ask|... ", set_root_choice), s_"How to choose root filesystem";
a30de4
+    [ L"vddk" ],     Getopt.String ("libpath", set_string_option_once "--vddk" vddk),
a30de4
+                                            s_"Use nbdkit VDDK plugin";
a30de4
+    [ L"vddk-config" ], Getopt.String ("filename", set_string_option_once "--vddk-config" vddk_config),
a30de4
+                                            s_"Set VDDK config file";
a30de4
+    [ L"vddk-cookie" ], Getopt.String ("cookie", set_string_option_once "--vddk-cookie" vddk_cookie),
a30de4
+                                            s_"Set VDDK cookie";
a30de4
+    [ L"vddk-nfchostport" ], Getopt.String ("nfchostport", set_string_option_once "--vddk-nfchostport" vddk_nfchostport),
a30de4
+                                            s_"Set VDDK nfchostport";
a30de4
+    [ L"vddk-port" ], Getopt.String ("port", set_string_option_once "--vddk-port" vddk_port),
a30de4
+                                            s_"Set VDDK port";
a30de4
+    [ L"vddk-snapshot" ], Getopt.String ("snapshot-moref", set_string_option_once "--vddk-snapshot" vddk_snapshot),
a30de4
+                                            s_"Set VDDK snapshot";
a30de4
+    [ L"vddk-thumbprint" ], Getopt.String ("thumbprint", set_string_option_once "--vddk-thumbprint" vddk_thumbprint),
a30de4
+                                            s_"Set VDDK thumbprint";
a30de4
+    [ L"vddk-transports" ], Getopt.String ("transports", set_string_option_once "--vddk-transports" vddk_transports),
a30de4
+                                            s_"Set VDDK transports";
a30de4
+    [ L"vddk-vimapiver" ], Getopt.String ("apiver", set_string_option_once "--vddk-vimapiver" vddk_vimapiver),
a30de4
+                                            s_"Set VDDK vimapiver";
a30de4
     [ L"vdsm-compat" ], Getopt.Symbol ("0.10|1.1", ["0.10"; "1.1"], set_vdsm_compat), s_"Write qcow2 with compat=0.10|1.1";
a30de4
     [ L"vdsm-image-uuid" ], Getopt.String ("uuid", add_vdsm_image_uuid), s_"Output image UUID(s)";
a30de4
     [ L"vdsm-vol-uuid" ], Getopt.String ("uuid", add_vdsm_vol_uuid), s_"Output vol UUID(s)";
a30de4
@@ -263,6 +290,29 @@ read the man page virt-v2v(1).
a30de4
   let print_source = !print_source in
a30de4
   let qemu_boot = !qemu_boot in
a30de4
   let root_choice = !root_choice in
a30de4
+  let vddk_options =
a30de4
+    match !vddk with
a30de4
+    | Some libdir ->
a30de4
+      Some { vddk_libdir = libdir;
a30de4
+             vddk_config = !vddk_config;
a30de4
+             vddk_cookie = !vddk_cookie;
a30de4
+             vddk_nfchostport = !vddk_nfchostport;
a30de4
+             vddk_port = !vddk_port;
a30de4
+             vddk_snapshot = !vddk_snapshot;
a30de4
+             vddk_thumbprint = !vddk_thumbprint;
a30de4
+             vddk_transports = !vddk_transports;
a30de4
+             vddk_vimapiver = !vddk_vimapiver }
a30de4
+    | None ->
a30de4
+      if !vddk_config <> None ||
a30de4
+         !vddk_cookie <> None ||
a30de4
+         !vddk_nfchostport <> None ||
a30de4
+         !vddk_port <> None ||
a30de4
+         !vddk_snapshot <> None ||
a30de4
+         !vddk_thumbprint <> None ||
a30de4
+         !vddk_transports <> None ||
a30de4
+         !vddk_vimapiver <> None then
a30de4
+        error (f_"‘--vddk-*’ options should only be used when conversion via the nbdkit VDDK plugin has been enabled, ie. using ‘--vddk’.");
a30de4
+      None in
a30de4
   let vdsm_compat = !vdsm_compat in
a30de4
   let vdsm_image_uuids = List.rev !vdsm_image_uuids in
a30de4
   let vdsm_vol_uuids = List.rev !vdsm_vol_uuids in
a30de4
@@ -278,6 +328,7 @@ read the man page virt-v2v(1).
a30de4
     printf "libguestfs-rewrite\n";
a30de4
     printf "vcenter-https\n";
a30de4
     printf "xen-ssh\n";
a30de4
+    printf "vddk\n";
a30de4
     printf "colours-option\n";
a30de4
     printf "vdsm-compat-option\n";
a30de4
     List.iter (printf "input:%s\n") (Modules_list.input_modules ());
a30de4
@@ -316,7 +367,7 @@ read the man page virt-v2v(1).
a30de4
         | [guest] -> guest
a30de4
         | _ ->
a30de4
           error (f_"expecting a libvirt guest name on the command line") in
a30de4
-      Input_libvirt.input_libvirt dcpath password input_conn guest
a30de4
+      Input_libvirt.input_libvirt dcpath vddk_options password input_conn guest
a30de4
 
a30de4
     | `LibvirtXML ->
a30de4
       (* -i libvirtxml: Expecting a filename (XML file). *)
a30de4
diff --git a/v2v/input_libvirt.ml b/v2v/input_libvirt.ml
a30de4
index 0bc386430..e8143b6ad 100644
a30de4
--- a/v2v/input_libvirt.ml
a30de4
+++ b/v2v/input_libvirt.ml
a30de4
@@ -27,7 +27,7 @@ open Types
a30de4
 open Utils
a30de4
 
a30de4
 (* Choose the right subclass based on the URI. *)
a30de4
-let input_libvirt dcpath password libvirt_uri guest =
a30de4
+let input_libvirt dcpath vddk_options password libvirt_uri guest =
a30de4
   match libvirt_uri with
a30de4
   | None ->
a30de4
     Input_libvirt_other.input_libvirt_other password libvirt_uri guest
a30de4
@@ -47,10 +47,18 @@ let input_libvirt dcpath password libvirt_uri guest =
a30de4
     | Some _, Some "" ->
a30de4
       Input_libvirt_other.input_libvirt_other password libvirt_uri guest
a30de4
 
a30de4
-    (* vCenter over https *)
a30de4
+    (* vCenter over https, or
a30de4
+     * vCenter or ESXi using nbdkit vddk plugin
a30de4
+     *)
a30de4
     | Some server, Some ("esx"|"gsx"|"vpx" as scheme) ->
a30de4
-      Input_libvirt_vcenter_https.input_libvirt_vcenter_https
a30de4
-        dcpath password libvirt_uri parsed_uri scheme server guest
a30de4
+       (match vddk_options with
a30de4
+        | None ->
a30de4
+           Input_libvirt_vcenter_https.input_libvirt_vcenter_https
a30de4
+             dcpath password libvirt_uri parsed_uri scheme server guest
a30de4
+        | Some vddk_options ->
a30de4
+           Input_libvirt_vddk.input_libvirt_vddk vddk_options password
a30de4
+                                                 libvirt_uri parsed_uri guest
a30de4
+       )
a30de4
 
a30de4
     (* Xen over SSH *)
a30de4
     | Some server, Some ("xen+ssh" as scheme) ->
a30de4
diff --git a/v2v/input_libvirt.mli b/v2v/input_libvirt.mli
a30de4
index f5e6314d9..0a6aa3c54 100644
a30de4
--- a/v2v/input_libvirt.mli
a30de4
+++ b/v2v/input_libvirt.mli
a30de4
@@ -18,7 +18,7 @@
a30de4
 
a30de4
 (** [-i libvirt] source. *)
a30de4
 
a30de4
-val input_libvirt : string option -> string option -> string option -> string -> Types.input
a30de4
-(** [input_libvirt dcpath password libvirt_uri guest] creates and returns a
a30de4
-    new {!Types.input} object specialized for reading input from
a30de4
-    libvirt sources. *)
a30de4
+val input_libvirt : string option -> Types.vddk_options option -> string option -> string option -> string -> Types.input
a30de4
+(** [input_libvirt dcpath vddk_options password libvirt_uri guest] creates
a30de4
+    and returns a new {!Types.input} object specialized for reading input
a30de4
+    from libvirt sources. *)
a30de4
diff --git a/v2v/input_libvirt_vddk.ml b/v2v/input_libvirt_vddk.ml
a30de4
new file mode 100644
a30de4
index 000000000..89d2552f6
a30de4
--- /dev/null
a30de4
+++ b/v2v/input_libvirt_vddk.ml
a30de4
@@ -0,0 +1,314 @@
a30de4
+(* virt-v2v
a30de4
+ * Copyright (C) 2009-2017 Red Hat Inc.
a30de4
+ *
a30de4
+ * This program is free software; you can redistribute it and/or modify
a30de4
+ * it under the terms of the GNU General Public License as published by
a30de4
+ * the Free Software Foundation; either version 2 of the License, or
a30de4
+ * (at your option) any later version.
a30de4
+ *
a30de4
+ * This program is distributed in the hope that it will be useful,
a30de4
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
a30de4
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a30de4
+ * GNU General Public License for more details.
a30de4
+ *
a30de4
+ * You should have received a copy of the GNU General Public License along
a30de4
+ * with this program; if not, write to the Free Software Foundation, Inc.,
a30de4
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
a30de4
+ *)
a30de4
+
a30de4
+(** [-i libvirt] when the source is VMware via nbdkit vddk plugin *)
a30de4
+
a30de4
+open Unix
a30de4
+
a30de4
+open Common_gettext.Gettext
a30de4
+open Common_utils
a30de4
+open Unix_utils
a30de4
+
a30de4
+open Types
a30de4
+open Utils
a30de4
+open Input_libvirt_other
a30de4
+open Parse_libvirt_xml
a30de4
+open Xpath_helpers
a30de4
+
a30de4
+open Printf
a30de4
+
a30de4
+(* Subclass specialized for handling VMware via nbdkit vddk plugin. *)
a30de4
+class input_libvirt_vddk vddk_options password libvirt_uri parsed_uri guest =
a30de4
+
a30de4
+  (* The VDDK path. *)
a30de4
+  let libdir = vddk_options.vddk_libdir in
a30de4
+  (* Compute the LD_LIBRARY_PATH that we must pass to nbdkit. *)
a30de4
+  let library_path = libdir // sprintf "lib%d" Sys.word_size in
a30de4
+
a30de4
+  (* Is SELinux enabled and enforcing on the host? *)
a30de4
+  let have_selinux =
a30de4
+    0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing" in
a30de4
+
a30de4
+  (* Check that the VDDK path looks reasonable. *)
a30de4
+  let error_unless_vddk_libdir () =
a30de4
+    if not (is_directory libdir) then
a30de4
+      error (f_"‘--vddk %s’ does not point to a directory.  See \"INPUT FROM VDDK\" in the virt-v2v(1) manual.") libdir;
a30de4
+
a30de4
+    if not (is_directory library_path) then
a30de4
+      error (f_"VDDK library path %s not found or not a directory.  See \"INPUT FROM VDDK\" in the virt-v2v(1) manual.")
a30de4
+            library_path
a30de4
+  in
a30de4
+
a30de4
+  (* Check that nbdkit is available and new enough. *)
a30de4
+  let error_unless_nbdkit_working () =
a30de4
+    if 0 <> Sys.command "nbdkit --version >/dev/null" then
a30de4
+      error (f_"nbdkit is not installed or not working.  It is required to use ‘--vddk’.  See \"INPUT FROM VDDK\" in the virt-v2v(1) manual.");
a30de4
+
a30de4
+    (* Check it's a new enough version.  The latest features we
a30de4
+     * require are ‘--exit-with-parent’ and ‘--selinux-label’, both
a30de4
+     * added in 1.1.14.
a30de4
+     *)
a30de4
+    let lines = external_command "nbdkit --help" in
a30de4
+    let lines = String.concat " " lines in
a30de4
+    if String.find lines "exit-with-parent" == -1 ||
a30de4
+       String.find lines "selinux-label" == -1 then
a30de4
+      error (f_"nbdkit is not new enough, you need to upgrade to nbdkit ≥ 1.1.14")
a30de4
+  in
a30de4
+
a30de4
+  (* Check that the VDDK plugin is installed and working *)
a30de4
+  let error_unless_nbdkit_vddk_working () =
a30de4
+    let cmd =
a30de4
+      sprintf "LD_LIBRARY_PATH=%s nbdkit vddk --dump-plugin >/dev/null"
a30de4
+              (quote library_path) in
a30de4
+    if Sys.command cmd <> 0 then (
a30de4
+      (* See if we can diagnose why ... *)
a30de4
+      let cmd =
a30de4
+        sprintf "LD_LIBRARY_PATH=%s LANG=C nbdkit vddk --dump-plugin 2>&1 | grep -sq libvixDiskLib.so"
a30de4
+                (quote library_path) in
a30de4
+      let needs_library = Sys.command cmd = 0 in
a30de4
+      if not needs_library then
a30de4
+        error (f_"nbdkit VDDK plugin is not installed or not working.  It is required if you want to use VDDK.
a30de4
+
a30de4
+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.
a30de4
+
a30de4
+See also \"INPUT FROM VDDK\" in the virt-v2v(1) manual.")
a30de4
+      else
a30de4
+        error (f_"nbdkit VDDK plugin is not installed or not working.  It is required if you want to use VDDK.
a30de4
+
a30de4
+It looks like you did not set the right path in the ‘--vddk’ option, or your copy of the VDDK directory is incomplete.  There should be a library called ’%s/libvixDiskLib.so.?’.
a30de4
+
a30de4
+See also \"INPUT FROM VDDK\" in the virt-v2v(1) manual.") library_path
a30de4
+    )
a30de4
+  in
a30de4
+
a30de4
+  let error_unless_thumbprint () =
a30de4
+    if vddk_options.vddk_thumbprint = None then
a30de4
+      error (f_"You must pass the ‘--vddk-thumbprint’ option with the SSL thumbprint of the VMware server.  To find the thumbprint, see the nbdkit-vddk-plugin(1) manual.  See also \"INPUT FROM VDDK\" in the virt-v2v(1) manual.")
a30de4
+  in
a30de4
+
a30de4
+object
a30de4
+  inherit input_libvirt password libvirt_uri guest
a30de4
+
a30de4
+  method source () =
a30de4
+    error_unless_vddk_libdir ();
a30de4
+    error_unless_nbdkit_working ();
a30de4
+    error_unless_nbdkit_vddk_working ();
a30de4
+    error_unless_thumbprint ();
a30de4
+
a30de4
+    (* Get the libvirt XML.  This also checks (as a side-effect)
a30de4
+     * that the domain is not running.  (RHBZ#1138586)
a30de4
+     *)
a30de4
+    let xml = Libvirt_utils.dumpxml ?password ?conn:libvirt_uri guest in
a30de4
+    let source, disks = parse_libvirt_xml ?conn:libvirt_uri xml in
a30de4
+
a30de4
+    (* Find the <vmware:moref> element from the XML.  This was added
a30de4
+     * in libvirt >= 3.7 and is required.
a30de4
+     *)
a30de4
+    let moref =
a30de4
+      let doc = Xml.parse_memory xml in
a30de4
+      let xpathctx = Xml.xpath_new_context doc in
a30de4
+      Xml.xpath_register_ns xpathctx
a30de4
+        "vmware" "http://libvirt.org/schemas/domain/vmware/1.0";
a30de4
+      let xpath_string = xpath_string xpathctx in
a30de4
+      match xpath_string "/domain/vmware:moref" with
a30de4
+      | Some moref -> moref
a30de4
+      | None ->
a30de4
+         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
a30de4
+
a30de4
+    (* Create a temporary directory where we place the sockets and
a30de4
+     * password file.
a30de4
+     *)
a30de4
+    let tmpdir =
a30de4
+      let base_dir = (open_guestfs ())#get_cachedir () in
a30de4
+      let t = Mkdtemp.temp_dir ~base_dir "vddk." in
a30de4
+      (* tmpdir must be readable (but not writable) by "other" so that
a30de4
+       * qemu can open the sockets.  If we place a password file in
a30de4
+       * this directory then we'll chmod that to 0600 below.
a30de4
+       *)
a30de4
+      chmod t 0o755;
a30de4
+      rmdir_on_exit t;
a30de4
+      t in
a30de4
+
a30de4
+    (* Start constructing the parts of the incredibly long nbdkit
a30de4
+     * command line which don't change between disks.
a30de4
+     *)
a30de4
+    let args =
a30de4
+      let add_arg, get_args =
a30de4
+        let args = ref [] in
a30de4
+        let add_arg a = push_front a args in
a30de4
+        let get_args () = List.rev !args in
a30de4
+        add_arg, get_args in
a30de4
+
a30de4
+      (* It probably never happens that the server name can be missing
a30de4
+       * from the libvirt URI, but we need a server name to pass to
a30de4
+       * nbdkit, so ...
a30de4
+       *)
a30de4
+      let server =
a30de4
+        match parsed_uri.Xml.uri_server with
a30de4
+        | Some server -> server
a30de4
+        | None ->
a30de4
+           match libvirt_uri with
a30de4
+           | Some libvirt_uri ->
a30de4
+              error (f_"‘-ic %s’ URL does not contain a host name field")
a30de4
+                    libvirt_uri
a30de4
+           | None ->
a30de4
+              error (f_"you must use the ‘-ic’ parameter.  See \"INPUT FROM VDDK\" in the virt-v2v(1) manual.") in
a30de4
+
a30de4
+      (* Similar to above, we also need a username to pass. *)
a30de4
+      let user =
a30de4
+        match parsed_uri.Xml.uri_user with
a30de4
+        | Some user -> user
a30de4
+        | None -> "root" (* ? *) in
a30de4
+
a30de4
+      add_arg "nbdkit";
a30de4
+      if verbose () then add_arg "--verbose";
a30de4
+      add_arg "--readonly";         (* important! readonly mode *)
a30de4
+      add_arg "--foreground";       (* run in foreground *)
a30de4
+      add_arg "--exit-with-parent"; (* exit when virt-v2v exits *)
a30de4
+      add_arg "--newstyle";         (* use newstyle NBD protocol *)
a30de4
+      add_arg "--exportname"; add_arg "/";
a30de4
+      if have_selinux then (        (* label the socket so qemu can open it *)
a30de4
+        add_arg "--selinux-label"; add_arg "system_u:object_r:svirt_t:s0"
a30de4
+      );
a30de4
+
a30de4
+      (* Name of the plugin.  Everything following is a plugin parameter. *)
a30de4
+      add_arg "vddk";
a30de4
+
a30de4
+      let password_param =
a30de4
+        match password with
a30de4
+        | None ->
a30de4
+           (* nbdkit asks for the password interactively *)
a30de4
+           "password=-"
a30de4
+        | Some password ->
a30de4
+           let password_file = tmpdir // "password" in
a30de4
+           let chan = open_out password_file in
a30de4
+           chmod password_file 0o600;
a30de4
+           output_string chan password;
a30de4
+           close_out chan;
a30de4
+           (* nbdkit reads the password from the file *)
a30de4
+           "password=+" ^ password_file in
a30de4
+      add_arg (sprintf "server=%s" server);
a30de4
+      add_arg (sprintf "user=%s" user);
a30de4
+      add_arg password_param;
a30de4
+      add_arg (sprintf "vm=moref=%s" moref);
a30de4
+      add_arg (sprintf "libdir=%s" libdir);
a30de4
+
a30de4
+      (* The passthrough parameters. *)
a30de4
+      let pt name = may (fun field -> add_arg (sprintf "%s=%s" name field)) in
a30de4
+      pt "config" vddk_options.vddk_config;
a30de4
+      pt "cookie" vddk_options.vddk_cookie;
a30de4
+      pt "nfchostport" vddk_options.vddk_nfchostport;
a30de4
+      pt "port" vddk_options.vddk_port;
a30de4
+      pt "snapshot" vddk_options.vddk_snapshot;
a30de4
+      pt "thumbprint" vddk_options.vddk_thumbprint;
a30de4
+      pt "transports" vddk_options.vddk_transports;
a30de4
+      pt "vimapiver" vddk_options.vddk_vimapiver;
a30de4
+
a30de4
+      get_args () in
a30de4
+
a30de4
+    (* Create an nbdkit instance for each disk and rewrite the source
a30de4
+     * paths to point to the NBD socket.
a30de4
+     *)
a30de4
+    let disks = List.map (
a30de4
+      function
a30de4
+      | { p_source_disk = disk; p_source = P_dont_rewrite } ->
a30de4
+         disk
a30de4
+
a30de4
+      | { p_source = P_source_dev _ } -> (* Should never happen. *)
a30de4
+         error (f_"source disk has <source dev=...> attribute in XML")
a30de4
+
a30de4
+      | { p_source_disk = disk; p_source = P_source_file path } ->
a30de4
+         (* The <source file=...> attribute returned by the libvirt
a30de4
+          * VMX driver looks like "[datastore] path".  We can use it
a30de4
+          * directly as the nbdkit file= parameter, and it is passed
a30de4
+          * directly in this form to VDDK.
a30de4
+          *)
a30de4
+
a30de4
+         let sock = tmpdir // sprintf "nbdkit%d.sock" disk.s_disk_id in
a30de4
+         let qemu_uri = sprintf "nbd:unix:%s:exportname=/" sock in
a30de4
+
a30de4
+         let pidfile = tmpdir // sprintf "nbdkit%d.pid" disk.s_disk_id in
a30de4
+
a30de4
+         (* Construct the final command line with the "static" args
a30de4
+          * above plus the args which vary for each disk.
a30de4
+          *)
a30de4
+         let args =
a30de4
+           args @ [ "--pidfile"; pidfile;
a30de4
+                    "--unix"; sock;
a30de4
+                    sprintf "file=%s" path ] in
a30de4
+         let args = Array.of_list args in
a30de4
+
a30de4
+         (* Start an nbdkit instance in the background.  By using
a30de4
+          * --exit-with-parent we don't have to worry about cleaning
a30de4
+          * it up, hopefully.
a30de4
+          *)
a30de4
+         let pid = fork () in
a30de4
+         if pid = 0 then (
a30de4
+           (* Child process (nbdkit). *)
a30de4
+           putenv "LD_LIBRARY_PATH" library_path;
a30de4
+           execvp "nbdkit" args
a30de4
+         );
a30de4
+
a30de4
+         (* Wait for the pidfile to appear so we know that nbdkit
a30de4
+          * is listening for requests.
a30de4
+          *)
a30de4
+         let rec loop i =
a30de4
+           if i = 0 then false
a30de4
+           else if Sys.file_exists pidfile then true
a30de4
+           else (
a30de4
+             sleep 1;
a30de4
+             loop (i-1)
a30de4
+           )
a30de4
+         in
a30de4
+         if not (loop 30) then (
a30de4
+           if verbose () then
a30de4
+             error (f_"nbdkit did not start up.  See previous debugging messages for problems.")
a30de4
+           else
a30de4
+             error (f_"nbdkit did not start up.  There may be errors printed by nbdkit above.
a30de4
+
a30de4
+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.")
a30de4
+         );
a30de4
+
a30de4
+         if have_selinux then (
a30de4
+           (* Note that Unix domain sockets have both a file label and
a30de4
+            * a socket/process label.  Using --selinux-label above
a30de4
+            * only set the socket label, but we must also set the file
a30de4
+            * label.
a30de4
+            *)
a30de4
+           ignore (
a30de4
+               run_command ["chcon"; "system_u:object_r:svirt_image_t:s0";
a30de4
+                            sock]
a30de4
+           );
a30de4
+         );
a30de4
+         (* ... and the regular Unix permissions, in case qemu is
a30de4
+          * running as another user.
a30de4
+          *)
a30de4
+         chmod sock 0o777;
a30de4
+
a30de4
+         { disk with s_qemu_uri = qemu_uri }
a30de4
+     ) disks in
a30de4
+
a30de4
+    if verbose () then (
a30de4
+      eprintf "vddk: tmpdir %s:\n%!" tmpdir;
a30de4
+      ignore (Sys.command (sprintf "ls -laZ %s" (quote tmpdir)))
a30de4
+    );
a30de4
+
a30de4
+    { source with s_disks = disks }
a30de4
+end
a30de4
+
a30de4
+let input_libvirt_vddk = new input_libvirt_vddk
a30de4
diff --git a/v2v/input_libvirt_vddk.mli b/v2v/input_libvirt_vddk.mli
a30de4
new file mode 100644
a30de4
index 000000000..19a34c202
a30de4
--- /dev/null
a30de4
+++ b/v2v/input_libvirt_vddk.mli
a30de4
@@ -0,0 +1,24 @@
a30de4
+(* virt-v2v
a30de4
+ * Copyright (C) 2017 Red Hat Inc.
a30de4
+ *
a30de4
+ * This program is free software; you can redistribute it and/or modify
a30de4
+ * it under the terms of the GNU General Public License as published by
a30de4
+ * the Free Software Foundation; either version 2 of the License, or
a30de4
+ * (at your option) any later version.
a30de4
+ *
a30de4
+ * This program is distributed in the hope that it will be useful,
a30de4
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
a30de4
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a30de4
+ * GNU General Public License for more details.
a30de4
+ *
a30de4
+ * You should have received a copy of the GNU General Public License along
a30de4
+ * with this program; if not, write to the Free Software Foundation, Inc.,
a30de4
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
a30de4
+ *)
a30de4
+
a30de4
+(** [-i libvirt] when the source is VMware via nbdkit vddk plugin *)
a30de4
+
a30de4
+val input_libvirt_vddk : Types.vddk_options -> string option -> string option -> Xml.uri -> string -> Types.input
a30de4
+(** [input_libvirt_vddk vddk_options password libvirt_uri parsed_uri guest]
a30de4
+    creates and returns a {!Types.input} object specialized for reading
a30de4
+    the guest disks using the nbdkit vddk plugin. *)
a30de4
diff --git a/v2v/types.ml b/v2v/types.ml
a30de4
index 3efb9ff46..4f9205aa0 100644
a30de4
--- a/v2v/types.ml
a30de4
+++ b/v2v/types.ml
a30de4
@@ -472,6 +472,18 @@ type root_choice = AskRoot | SingleRoot | FirstRoot | RootDev of string
a30de4
 
a30de4
 type output_allocation = Sparse | Preallocated
a30de4
 
a30de4
+type vddk_options = {
a30de4
+    vddk_libdir : string;
a30de4
+    vddk_config : string option;
a30de4
+    vddk_cookie : string option;
a30de4
+    vddk_nfchostport : string option;
a30de4
+    vddk_port : string option;
a30de4
+    vddk_snapshot : string option;
a30de4
+    vddk_thumbprint : string option;
a30de4
+    vddk_transports : string option;
a30de4
+    vddk_vimapiver : string option;
a30de4
+}
a30de4
+
a30de4
 class virtual input = object
a30de4
   method precheck () = ()
a30de4
   method virtual as_options : string
a30de4
diff --git a/v2v/types.mli b/v2v/types.mli
a30de4
index 2880546ae..087a03702 100644
a30de4
--- a/v2v/types.mli
a30de4
+++ b/v2v/types.mli
a30de4
@@ -328,6 +328,19 @@ type root_choice = AskRoot | SingleRoot | FirstRoot | RootDev of string
a30de4
 type output_allocation = Sparse | Preallocated
a30de4
 (** Type of [-oa] (output allocation) option. *)
a30de4
 
a30de4
+type vddk_options = {
a30de4
+    vddk_libdir : string;
a30de4
+    vddk_config : string option;
a30de4
+    vddk_cookie : string option;
a30de4
+    vddk_nfchostport : string option;
a30de4
+    vddk_port : string option;
a30de4
+    vddk_snapshot : string option;
a30de4
+    vddk_thumbprint : string option;
a30de4
+    vddk_transports : string option;
a30de4
+    vddk_vimapiver : string option;
a30de4
+}
a30de4
+(** Various options passed through to the nbdkit vddk plugin unmodified. *)
a30de4
+
a30de4
 (** {2 Input object}
a30de4
 
a30de4
     There is one of these used for the [-i] option. *)
a30de4
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
a30de4
index 77b6f514f..d713d0b1f 100644
a30de4
--- a/v2v/virt-v2v.pod
a30de4
+++ b/v2v/virt-v2v.pod
a30de4
@@ -514,6 +514,35 @@ boot an operating system from the first VirtIO disk.  Specifically,
a30de4
 F</boot> must be on the first VirtIO disk, and it cannot chainload an
a30de4
 OS which is not in the first VirtIO disk.
a30de4
 
a30de4
+=item B<--vddk> LIBDIR
a30de4
+
a30de4
+Enable VDDK input from VMware vCenter or ESXi.  C<LIBDIR> is the top
a30de4
+directory of the VDDK library.  This directory should I<contain>
a30de4
+subdirectories called F<include>, F<lib64> etc., but do not include
a30de4
+F<lib64> actually in the parameter.
a30de4
+
a30de4
+See L</INPUT FROM VDDK> below for details.
a30de4
+
a30de4
+=item B<--vddk-config> FILENAME
a30de4
+
a30de4
+=item B<--vddk-cookie> COOKIE
a30de4
+
a30de4
+=item B<--vddk-nfchostport> PORT
a30de4
+
a30de4
+=item B<--vddk-port> PORT
a30de4
+
a30de4
+=item B<--vddk-snapshot> SNAPSHOT-MOREF
a30de4
+
a30de4
+=item B<--vddk-thumbprint> xx:xx:xx:...
a30de4
+
a30de4
+=item B<--vddk-transports> MODE:MODE:...
a30de4
+
a30de4
+=item B<--vddk-vimapiver> APIVER
a30de4
+
a30de4
+When using VDDK mode, these options are passed unmodified to the
a30de4
+L<nbdkit(1)> VDDK plugin.  Please refer to L<nbdkit-vddk-plugin(1)>.
a30de4
+Only I<--vddk-thumbprint> is required, the others are optional.
a30de4
+
a30de4
 =item B<--vdsm-compat=0.10>
a30de4
 
a30de4
 =item B<--vdsm-compat=1.1>
a30de4
@@ -1270,6 +1299,130 @@ Perform the conversion of the guest using virt-v2v:
a30de4
 
a30de4
 Remove the F<guest.xml> and F<guest-disk*> files.
a30de4
 
a30de4
+=head1 INPUT FROM VDDK
a30de4
+
a30de4
+Virt-v2v is able to import guests using VMware’s proprietary VDDK
a30de4
+library (a.k.a. VixDiskLib).
a30de4
+
a30de4
+=head2 VDDK: PREREQUISITES
a30de4
+
a30de4
+=over 4
a30de4
+
a30de4
+=item 1.
a30de4
+
a30de4
+As the VDDK library is not open source, and the license of this
a30de4
+library does not permit redistribution or commercial use, you must
a30de4
+obtain VDDK yourself and satisfy yourself that your usage of the
a30de4
+library is permitted by the license.
a30de4
+
a30de4
+=item 2.
a30de4
+
a30de4
+You must also compile nbdkit, enabling the VDDK plugin.  At least
a30de4
+nbdkit E<ge> 1.1.14 is required, but it is usually best to compile
a30de4
+from the git tree.
a30de4
+
a30de4
+=over 4
a30de4
+
a30de4
+=item *
a30de4
+
a30de4
+L<https://github.com/libguestfs/nbdkit>
a30de4
+
a30de4
+=item *
a30de4
+
a30de4
+L<https://github.com/libguestfs/nbdkit/tree/master/plugins/vddk>
a30de4
+
a30de4
+=back
a30de4
+
a30de4
+=item 3.
a30de4
+
a30de4
+You can run nbdkit from its source directory without needing to
a30de4
+install it.  Set C<$PATH> to include the nbdkit top build directory
a30de4
+(the directory containing a shell script called F<nbdkit>):
a30de4
+
a30de4
+ export PATH=/path/to/nbdkit:$PATH
a30de4
+
a30de4
+=item 4.
a30de4
+
a30de4
+You must find the SSL "thumbprint" of your VMware server.  How to do
a30de4
+this is explained in L<nbdkit-vddk-plugin(1)>, also available at the
a30de4
+link given in item 2 above.
a30de4
+
a30de4
+=item 5.
a30de4
+
a30de4
+VDDK imports require a feature added in libvirt E<ge> 3.7.
a30de4
+
a30de4
+=back
a30de4
+
a30de4
+=head2 VDDK: URI
a30de4
+
a30de4
+Construct the correct C<vpx://> (for vCenter) or C<esx://> (for ESXi)
a30de4
+URL.  It will look something like these:
a30de4
+
a30de4
+ vpx://root@vcenter.example.com/Datacenter/esxi
a30de4
+
a30de4
+ esx://root@esxi.example.com
a30de4
+
a30de4
+To verify that you have the correct URL, use the L<virsh(1)> command
a30de4
+to list the guests on the server:
a30de4
+
a30de4
+ $ virsh -c 'vpx://root@vcenter.example.com/Datacenter/esxi' list --all
a30de4
+ Enter root's password for vcenter.example.com: ***
a30de4
+ 
a30de4
+  Id    Name                           State
a30de4
+ ----------------------------------------------------
a30de4
+  -     Fedora 20                      shut off
a30de4
+  -     Windows 2003                   shut off
a30de4
+
a30de4
+If you get an error "Peer certificate cannot be authenticated with
a30de4
+given CA certificates" or similar, then you can either import the
a30de4
+vCenter host’s certificate, or bypass signature verification by adding
a30de4
+the C flag:
a30de4
+
a30de4
+ $ virsh -c 'vpx://root@vcenter.example.com/Datacenter/esxi?no_verify=1' list --all
a30de4
+
a30de4
+You should also try dumping the metadata from any guest on your
a30de4
+server, like this:
a30de4
+
a30de4
+ $ virsh -c 'vpx://root@vcenter.example.com/Datacenter/esxi' dumpxml "Windows 2003"
a30de4
+ <domain type='vmware'>
a30de4
+   <name>Windows 2003</name>
a30de4
+   [...]
a30de4
+   <vmware:moref>vm-123</vmware:moref>
a30de4
+ </domain>
a30de4
+
a30de4
+If C<E<lt>vmware:morefE<gt>> does not appear in the metadata, then you
a30de4
+need to upgrade libvirt.
a30de4
+
a30de4
+B
a30de4
+work either>.  Fix your URI and/or your VMware server before
a30de4
+continuing.
a30de4
+
a30de4
+=head2 VDDK: IMPORTING A GUEST
a30de4
+
a30de4
+To import a particular guest from vCenter server or ESXi hypervisor,
a30de4
+use a command like the following, substituting the URI, guest name and
a30de4
+SSL thumbprint:
a30de4
+
a30de4
+ $ export PATH=/path/to/nbdkit:$PATH
a30de4
+ $ virt-v2v \
a30de4
+     -ic 'vpx://root@vcenter.example.com/Datacenter/esxi?no_verify=1' \
a30de4
+     --vddk /path/to/vmware-vix-disklib-distrib \
a30de4
+     --vddk-thumbprint xx:xx:xx:... \
a30de4
+     "Windows 2003" \
a30de4
+     -o local -os /var/tmp
a30de4
+
a30de4
+Other options that you might need to add in rare circumstances include
a30de4
+I<--vddk-config>, I<--vddk-cookie>, I<--vddk-nfchostport>,
a30de4
+I<--vddk-port>, I<--vddk-snapshot>, I<--vddk-transports> and
a30de4
+I<--vddk-vimapiver>, which are all explained in the
a30de4
+L<nbdkit-vddk-plugin(1)> documentation.
a30de4
+
a30de4
+=head2 VDDK: DEBUGGING VDDK FAILURES
a30de4
+
a30de4
+The VDDK library can be operated in a verbose mode where it gives
a30de4
+(very) verbose messages.  Use ‘virt-v2v -v -x’ as usual to enable
a30de4
+verbose messages.
a30de4
+
a30de4
 =head1 INPUT FROM XEN
a30de4
 
a30de4
 Virt-v2v is able to import Xen guests from RHEL 5 Xen hosts.
a30de4
@@ -2032,6 +2185,8 @@ L<virt-v2v-copy-to-local(1)>,
a30de4
 L<virt-v2v-test-harness(1)>,
a30de4
 L<engine-image-uploader(8)>,
a30de4
 L<import-to-ovirt.pl|http://git.annexia.org/?p=import-to-ovirt.git>,
a30de4
+L<nbdkit(1)>,
a30de4
+L<nbdkit-vddk-plugin(1)>,
a30de4
 L<http://libguestfs.org/>.
a30de4
 
a30de4
 =head1 AUTHORS
a30de4
-- 
a30de4
2.14.3
a30de4