[Libguestfs] [PATCH virt-v2v] v2v: Allow temporary directory to be set on a global basis.

Richard W.M. Jones rjones at redhat.com
Thu Apr 2 12:49:18 UTC 2020


Previously we placed large files in g#get_cachedir () (usually
/var/tmp).  However the problem is this ties the libguestfs appliance
and the virt-v2v overlay files to the same location.

When virt-v2v is run in a container, or any other situation where
local storage is limited, it's helpful to be able to put the overlay
files on an externally mounted PVC, which might be using NFS and
shared between containers.  But putting the libguestfs appliance on
NFS in a shared location is certainly not recommended.

This allows the two locations to be set separately:

  VIRT_V2V_TMPDIR - location of large temporary files, can use NFS
                    and may be shared

  LIBGUESTFS_CACHEDIR - location of libguestfs appliance

Another motivation for this patch is to allow more reliable cleanup of
temporary files by an external process, as described in the updated
documentation.
---
 docs/virt-v2v.pod        | 21 +++++++++++++++++----
 v2v/input_ova.ml         |  4 ++--
 v2v/input_vmx.ml         |  3 +--
 v2v/nbdkit.ml            |  3 +--
 v2v/output_glance.ml     |  3 +--
 v2v/output_null.ml       |  5 ++---
 v2v/output_rhv_upload.ml |  3 +--
 v2v/parse_ova.ml         |  5 ++---
 v2v/python_script.ml     |  3 +--
 v2v/utils.ml             |  8 ++++++--
 v2v/utils.mli            |  4 ++++
 v2v/v2v.ml               | 15 ++++++---------
 12 files changed, 44 insertions(+), 33 deletions(-)

diff --git a/docs/virt-v2v.pod b/docs/virt-v2v.pod
index ed95fdc8e..dbfd10cad 100644
--- a/docs/virt-v2v.pod
+++ b/docs/virt-v2v.pod
@@ -1206,8 +1206,9 @@ possible.
 
 =head3 Disk space
 
-Virt-v2v places potentially large temporary files in C<$TMPDIR> (which
-is F</var/tmp> if you don't set it).  Using tmpfs is a bad idea.
+Virt-v2v places potentially large temporary files in
+C<$VIRT_V2V_TMPDIR> (or F</var/tmp>, see also L</ENVIRONMENT VARIBLES>
+below).  Using tmpfs is a bad idea.
 
 For each guest disk, an overlay is stored temporarily.  This stores
 the changes made during conversion, and is used as a cache.  The
@@ -1554,10 +1555,22 @@ conversion.
 
 =over 4
 
+=item C<VIRT_V2V_TMPDIR>
+
+=item C<LIBGUESTFS_CACHEDIR>
+
 =item C<TMPDIR>
 
-Location of the temporary directory used for the potentially large
-temporary overlay file.
+Location of the temporary directory.  This is used for the potentially
+large temporary overlay files.  Also for miscellaneous temporary
+files.  These environment variables are checked in order:
+C<VIRT_V2V_TMPDIR>, C<LIBGUESTFS_CACHEDIR>, C<TMPDIR>.  If none are
+set then F</var/tmp> is used.
+
+To reliably ensure temporary files are cleaned up (for example in case
+virt-v2v crashes) you should create a randomly named directory under
+F</var/tmp>, set C<VIRT_V2V_TMPDIR> to point to this directory, then
+when virt-v2v exits remove the directory.
 
 See the L</Disk space> section above.
 
diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml
index 5d3bece18..86f81e1f9 100644
--- a/v2v/input_ova.ml
+++ b/v2v/input_ova.ml
@@ -132,8 +132,8 @@ class input_ova ova = object
            (* The spec allows the file to be gzip-compressed, in
             * which case we must uncompress it into a temporary.
             *)
-           let temp_dir = (open_guestfs ())#get_cachedir () in
-           let new_filename = Filename.temp_file ~temp_dir "ova" ".vmdk" in
+           let new_filename =
+             Filename.temp_file ~temp_dir:Utils.tmpdir "ova" ".vmdk" in
            unlink_on_exit new_filename;
            let cmd =
              sprintf "zcat %s > %s" (quote filename) (quote new_filename) in
diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml
index f1d143e97..5a1804182 100644
--- a/v2v/input_vmx.ml
+++ b/v2v/input_vmx.ml
@@ -389,8 +389,7 @@ and find_nics vmx =
 
 class input_vmx input_password input_transport arg =
   let tmpdir =
-    let base_dir = (open_guestfs ())#get_cachedir () in
-    let t = Mkdtemp.temp_dir ~base_dir "vmx." in
+    let t = Mkdtemp.temp_dir ~base_dir:tmpdir "vmx." in
     rmdir_on_exit t;
     t in
 object
diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml
index 65317f9b9..188647f65 100644
--- a/v2v/nbdkit.ml
+++ b/v2v/nbdkit.ml
@@ -105,8 +105,7 @@ let add_filter_if_available cmd filter =
 let run_unix cmd =
   (* Create a temporary directory where we place the socket and PID file. *)
   let tmpdir =
-    let base_dir = (open_guestfs ())#get_cachedir () in
-    let t = Mkdtemp.temp_dir ~base_dir "v2vnbdkit." in
+    let t = Mkdtemp.temp_dir ~base_dir:tmpdir "v2vnbdkit." in
     (* tmpdir must be readable (but not writable) by "other" so that
      * qemu can open the sockets.
      *)
diff --git a/v2v/output_glance.ml b/v2v/output_glance.ml
index 0a9e91818..fce223333 100644
--- a/v2v/output_glance.ml
+++ b/v2v/output_glance.ml
@@ -33,8 +33,7 @@ class output_glance () =
    * to write to a temporary file.  XXX
    *)
   let tmpdir =
-    let base_dir = (open_guestfs ())#get_cachedir () in
-    let t = Mkdtemp.temp_dir ~base_dir "glance." in
+    let t = Mkdtemp.temp_dir ~base_dir:tmpdir "glance." in
     rmdir_on_exit t;
     t in
 object
diff --git a/v2v/output_null.ml b/v2v/output_null.ml
index 3528da50a..5ce30d557 100644
--- a/v2v/output_null.ml
+++ b/v2v/output_null.ml
@@ -50,7 +50,7 @@ let can_use_qemu_null_co_device () =
   (* We actually attempt to convert a raw file to the null-co device
    * using a JSON URL.
    *)
-  let tmp = Filename.temp_file "v2vqemunullcotst" ".img" in
+  let tmp = Filename.temp_file ~temp_dir:tmpdir "v2vqemunullcotst" ".img" in
   Unix.truncate tmp 1024;
 
   let json = [
@@ -75,8 +75,7 @@ class output_null =
    * the null-co device w/ a JSON URL.
    *)
   let tmpdir =
-    let base_dir = (open_guestfs ())#get_cachedir () in
-    let t = Mkdtemp.temp_dir ~base_dir "null." in
+    let t = Mkdtemp.temp_dir ~base_dir:tmpdir "null." in
     rmdir_on_exit t;
     t in
 object
diff --git a/v2v/output_rhv_upload.ml b/v2v/output_rhv_upload.ml
index 5c6c26110..4922a2040 100644
--- a/v2v/output_rhv_upload.ml
+++ b/v2v/output_rhv_upload.ml
@@ -155,8 +155,7 @@ class output_rhv_upload output_alloc output_conn
                         rhv_options =
   (* Create a temporary directory which will be deleted on exit. *)
   let tmpdir =
-    let base_dir = (open_guestfs ())#get_cachedir () in
-    let t = Mkdtemp.temp_dir ~base_dir "rhvupload." in
+    let t = Mkdtemp.temp_dir ~base_dir:tmpdir "rhvupload." in
     rmdir_on_exit t;
     t in
 
diff --git a/v2v/parse_ova.ml b/v2v/parse_ova.ml
index 0b939ac43..1f332a82c 100644
--- a/v2v/parse_ova.ml
+++ b/v2v/parse_ova.ml
@@ -71,8 +71,7 @@ let rec parse_ova ova =
     if is_directory ova then ova, Directory
     else (
       let tmpdir =
-        let base_dir = (open_guestfs ())#get_cachedir () in
-        let t = Mkdtemp.temp_dir ~base_dir "ova." in
+        let t = Mkdtemp.temp_dir ~base_dir:tmpdir "ova." in
         rmdir_on_exit t;
         t in
 
@@ -222,7 +221,7 @@ and uncompress_head format file =
 and uncompressed_type format file =
   let head, headlen = uncompress_head format file in
   let tmpfile, chan =
-    Filename.open_temp_file "ova.file." "" in
+    Filename.open_temp_file ~temp_dir:tmpdir "ova.file." "" in
   output chan head 0 headlen;
   close_out chan;
   let ret = detect_file_type tmpfile in
diff --git a/v2v/python_script.ml b/v2v/python_script.ml
index 33c5e9a21..2d95f4bd5 100644
--- a/v2v/python_script.ml
+++ b/v2v/python_script.ml
@@ -35,8 +35,7 @@ let create ?(name = "script.py") ?tmpdir code =
   let tmpdir =
     match tmpdir with
     | None ->
-      let base_dir = (open_guestfs ())#get_cachedir () in
-      let t = Mkdtemp.temp_dir ~base_dir "v2v." in
+      let t = Mkdtemp.temp_dir ~base_dir:Utils.tmpdir "v2v." in
       rmdir_on_exit t;
       t
     | Some dir -> dir in
diff --git a/v2v/utils.ml b/v2v/utils.ml
index ccbb9d68a..8cc24d6a2 100644
--- a/v2v/utils.ml
+++ b/v2v/utils.ml
@@ -24,6 +24,10 @@ open Std_utils
 open Tools_utils
 open Common_gettext.Gettext
 
+let tmpdir =
+  try Sys.getenv "VIRT_V2V_TMPDIR"
+  with Not_found -> (open_guestfs ())#get_cachedir ()
+
 (* Is SELinux enabled and enforcing on the host? *)
 let have_selinux =
   0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing"
@@ -112,7 +116,8 @@ let qemu_img_supports_offset_and_size () =
   (* We actually attempt to create a qcow2 file with a raw backing
    * file that has an offset and size.
    *)
-  let tmp = Filename.temp_file "v2vqemuimgtst" ".img" in
+  let tmp = Filename.temp_file ~temp_dir:tmpdir "v2vqemuimgtst" ".img" in
+  unlink_on_exit tmp;
   Unix.truncate tmp 1024;
 
   let json = [
@@ -132,7 +137,6 @@ let qemu_img_supports_offset_and_size () =
             (if verbose () then "" else " 2>&1") in
   debug "%s" cmd;
   let r = 0 = Sys.command cmd in
-  Unix.unlink tmp;
   debug "qemu-img supports \"offset\" and \"size\" in json URLs: %b" r;
   r
 
diff --git a/v2v/utils.mli b/v2v/utils.mli
index 937e2b9b8..5afe2954f 100644
--- a/v2v/utils.mli
+++ b/v2v/utils.mli
@@ -18,6 +18,10 @@
 
 (** Utilities used in virt-v2v only. *)
 
+val tmpdir : string
+(** [VIRT_V2V_TMPDIR] or [/var/tmp].  Create all temporary files
+    under this directory. *)
+
 val have_selinux : bool
 (** True if SELinux is enabled and enforcing on the host. *)
 
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
index 73edff2c4..6dd5fc693 100644
--- a/v2v/v2v.ml
+++ b/v2v/v2v.ml
@@ -264,8 +264,6 @@ and set_source_networks_and_bridges cmdline source =
   let nics = List.map (Networks.map cmdline.network_map) source.s_nics in
   { source with s_nics = nics }
 
-and overlay_dir = (open_guestfs ())#get_cachedir ()
-
 (* Conversion can fail or hang if there is insufficient free space in
  * the temporary directory used to store overlays on the host
  * (RHBZ#1316479).  Although only a few hundred MB is actually
@@ -273,12 +271,12 @@ and overlay_dir = (open_guestfs ())#get_cachedir ()
  * guestfs appliance which is also stored here.
  *)
 and check_host_free_space () =
-  let free_space = StatVFS.free_space (StatVFS.statvfs overlay_dir) in
-  debug "check_host_free_space: overlay_dir=%s free_space=%Ld"
-        overlay_dir free_space;
+  let free_space = StatVFS.free_space (StatVFS.statvfs tmpdir) in
+  debug "check_host_free_space: tmpdir=%s free_space=%Ld"
+        tmpdir free_space;
   if free_space < 1_073_741_824L then
     error (f_"insufficient free space in the conversion server temporary directory %s (%s).\n\nEither free up space in that directory, or set the LIBGUESTFS_CACHEDIR environment variable to point to another directory with more than 1GB of free space.\n\nSee also the virt-v2v(1) manual, section \"Minimum free space check in the host\".")
-          overlay_dir (human_size free_space)
+          tmpdir (human_size free_space)
 
 (* Create a qcow2 v3 overlay to protect the source image(s). *)
 and create_overlays source_disks =
@@ -286,7 +284,7 @@ and create_overlays source_disks =
   List.mapi (
     fun i ({ s_qemu_uri = qemu_uri; s_format = format } as source) ->
       let overlay_file =
-        Filename.temp_file ~temp_dir:overlay_dir "v2vovl" ".qcow2" in
+        Filename.temp_file ~temp_dir:tmpdir "v2vovl" ".qcow2" in
       unlink_on_exit overlay_file;
 
       (* There is a specific reason to use the newer qcow2 variant:
@@ -822,8 +820,7 @@ and actual_target_size target_file disk_stats =
 and preserve_overlays overlays src_name =
   List.iter (
     fun ov ->
-      let saved_filename =
-        sprintf "%s/%s-%s.qcow2" overlay_dir src_name ov.ov_sd in
+      let saved_filename = sprintf "%s/%s-%s.qcow2" tmpdir src_name ov.ov_sd in
       rename ov.ov_overlay_file saved_filename;
       info (f_"Overlay saved as %s [--debug-overlays]") saved_filename
   ) overlays
-- 
2.25.0




More information about the Libguestfs mailing list