[Libguestfs] [v2v PATCH 2/2] Consolidate handling of temporary files/dirs

Pino Toscano ptoscano at redhat.com
Mon Apr 6 15:40:45 UTC 2020


Create two temporary directories for all the files created during the
virt-v2v run:
1) tmpdir, created as $TMPDIR/virt-v2v.XXXXXX, for all the small files
2) cachedir, created as $LIBGUESTFS_CACHEDIR/virt-v2v.XXXXXX, for the
   big files (e.g. disks)
This way there is no need to manually schedule all the temporary files
and directories for removal when the application quits.
---
 v2v/input_ova.ml         |  5 ++---
 v2v/input_vmx.ml         |  5 -----
 v2v/measure_disk.ml      |  5 +++--
 v2v/output_glance.ml     | 18 ++++++------------
 v2v/output_libvirt.ml    |  3 +++
 v2v/output_null.ml       | 14 ++------------
 v2v/output_openstack.ml  |  3 +--
 v2v/output_rhv.ml        |  2 +-
 v2v/output_rhv_upload.ml |  7 -------
 v2v/parse_ova.ml         | 30 ++++++++++++++----------------
 v2v/utils.ml             | 25 +++++++++++++++++++++++--
 v2v/utils.mli            |  7 +++++++
 v2v/v2v.ml               | 15 ++++++---------
 13 files changed, 68 insertions(+), 71 deletions(-)

diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml
index 5d3bece1..5829c15e 100644
--- a/v2v/input_ova.ml
+++ b/v2v/input_ova.ml
@@ -26,6 +26,7 @@ open Types
 open Parse_ova
 open Parse_ovf_from_ova
 open Name_from_disk
+open Utils
 
 (* RHBZ#1570407: VMware-generated OVA files found in the wild can
  * contain hrefs referencing snapshots.  The href will be something
@@ -132,9 +133,7 @@ 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
-           unlink_on_exit new_filename;
+           let new_filename = Filename.temp_file ~temp_dir:cachedir "ova" ".vmdk" in
            let cmd =
              sprintf "zcat %s > %s" (quote filename) (quote new_filename) in
            if shell_command cmd <> 0 then
diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml
index f1d143e9..6fd60f94 100644
--- a/v2v/input_vmx.ml
+++ b/v2v/input_vmx.ml
@@ -388,11 +388,6 @@ and find_nics vmx =
   nics
 
 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
-    rmdir_on_exit t;
-    t in
 object
   inherit input
 
diff --git a/v2v/measure_disk.ml b/v2v/measure_disk.ml
index 5c01eaac..6a1212f0 100644
--- a/v2v/measure_disk.ml
+++ b/v2v/measure_disk.ml
@@ -21,6 +21,8 @@ open Tools_utils
 open JSON_parser
 open Common_gettext.Gettext
 
+open Utils
+
 (* Run qemu-img measure on a disk. *)
 
 let measure ?format filename =
@@ -38,8 +40,7 @@ let measure ?format filename =
   List.push_back cmd "--output=json";
   List.push_back cmd filename;
 
-  let json, chan = Filename.open_temp_file "v2vmeasure" ".json" in
-  unlink_on_exit json;
+  let json, chan = Filename.open_temp_file ~temp_dir:tmpdir "v2vmeasure" ".json" in
   let fd = Unix.descr_of_out_channel chan in
   if run_command ~stdout_fd:fd !cmd <> 0 then
     error (f_"qemu-img measure failed, see earlier errors");
diff --git a/v2v/output_glance.ml b/v2v/output_glance.ml
index 0a9e9181..f2468055 100644
--- a/v2v/output_glance.ml
+++ b/v2v/output_glance.ml
@@ -27,16 +27,6 @@ open Types
 open Utils
 
 class output_glance () =
-  (* Although glance can slurp in a stream from stdin, unfortunately
-   * 'qemu-img convert' cannot write to a stream (although I guess
-   * it could be implemented at least for raw).  Therefore we have
-   * 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
-    rmdir_on_exit t;
-    t in
 object
   inherit output
 
@@ -60,8 +50,12 @@ object
   method supported_firmware = [ TargetBIOS; TargetUEFI ]
 
   method prepare_targets _ overlays _ =
-    (* Write targets to a temporary local file - see above for reason. *)
-    List.map (fun (_, ov) -> TargetFile (tmpdir // ov.ov_sd)) overlays
+    (* Although glance can slurp in a stream from stdin, unfortunately
+     * 'qemu-img convert' cannot write to a stream (although I guess
+     * it could be implemented at least for raw).  Therefore we have
+     * to write to a temporary file.  XXX
+     *)
+    List.map (fun (_, ov) -> TargetFile (cachedir // ov.ov_sd)) overlays
 
   method create_metadata source targets
                          target_buses guestcaps inspect target_firmware =
diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml
index 033e26b1..9c189b9d 100644
--- a/v2v/output_libvirt.ml
+++ b/v2v/output_libvirt.ml
@@ -176,6 +176,9 @@ object (self)
       create_libvirt_xml ~pool:pool_name source targets target_buses
                          guestcaps target_features target_firmware inspect in
 
+    (* Do not store the temporary file inside our [tmpdir], as we preserve it
+     * for the user in case Domain.define_xml fails.
+     *)
     let tmpfile, chan = Filename.open_temp_file "v2vlibvirt" ".xml" in
     DOM.doc_to_chan chan doc;
     close_out chan;
diff --git a/v2v/output_null.ml b/v2v/output_null.ml
index 3528da50..493c9f0e 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 = [
@@ -65,20 +65,10 @@ let can_use_qemu_null_co_device () =
             (if verbose () then "" else " 2>&1") in
   debug "%s" cmd;
   let r = 0 = Sys.command cmd in
-  Unix.unlink tmp;
   debug "qemu-img supports the null-co device: %b" r;
   r
 
 class output_null =
-  (* Create a temporary directory which is always deleted at exit,
-   * so we can put the drives there in case qemu does not support
-   * 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
-    rmdir_on_exit t;
-    t in
 object
   inherit output
 
@@ -99,7 +89,7 @@ object
 
       List.map (fun _ -> target_file) overlays
     ) else (
-      List.map (fun (_, ov) -> TargetFile (tmpdir // ov.ov_sd)) overlays
+      List.map (fun (_, ov) -> TargetFile (cachedir // ov.ov_sd)) overlays
     )
 
   method create_metadata _ _ _ _ _ _ = ()
diff --git a/v2v/output_openstack.ml b/v2v/output_openstack.ml
index 179b0edf..39f30c9e 100644
--- a/v2v/output_openstack.ml
+++ b/v2v/output_openstack.ml
@@ -191,8 +191,7 @@ class output_openstack output_conn output_password output_storage
   let run_openstack_command_capture_json args =
     let cmd = [ openstack_binary ] @ extra_args @ args in
 
-    let json, chan = Filename.open_temp_file "v2vopenstack" ".json" in
-    unlink_on_exit json;
+    let json, chan = Filename.open_temp_file ~temp_dir:tmpdir "v2vopenstack" ".json" in
     let fd = descr_of_out_channel chan in
 
     (* Note that Tools_utils.run_command closes fd.
diff --git a/v2v/output_rhv.ml b/v2v/output_rhv.ml
index 49cc0381..49bfaaa8 100644
--- a/v2v/output_rhv.ml
+++ b/v2v/output_rhv.ml
@@ -40,7 +40,7 @@ let rec mount_and_check_storage_domain domain_class os =
     (* Create a mountpoint.  Default mode is too restrictive for us
      * when we need to write into the directory as 36:36.
      *)
-    let mp = Mkdtemp.temp_dir "v2v." in
+    let mp = Mkdtemp.temp_dir ~base_dir:tmpdir "v2v." in
     chmod mp 0o755;
 
     (* Try mounting it. *)
diff --git a/v2v/output_rhv_upload.ml b/v2v/output_rhv_upload.ml
index 5c6c2611..40498ba3 100644
--- a/v2v/output_rhv_upload.ml
+++ b/v2v/output_rhv_upload.ml
@@ -153,13 +153,6 @@ let json_optstring = function
 class output_rhv_upload output_alloc output_conn
                         output_password output_storage
                         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
-    rmdir_on_exit t;
-    t in
-
   let diskid_file_of_id id = tmpdir // sprintf "diskid.%d" id in
 
   (* Create Python scripts for precheck, vmcheck, plugin and create VM. *)
diff --git a/v2v/parse_ova.ml b/v2v/parse_ova.ml
index 0b939ac4..5c8d349f 100644
--- a/v2v/parse_ova.ml
+++ b/v2v/parse_ova.ml
@@ -70,11 +70,10 @@ let rec parse_ova ova =
   let top_dir, ova_type =
     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
-        rmdir_on_exit t;
-        t in
+      let ovatmpdir =
+        let d = cachedir // "ova" in
+        Unix.mkdir d 0o700;
+        d in
 
       match detect_file_type ova with
       | `Tar ->
@@ -85,15 +84,15 @@ let rec parse_ova ova =
           *)
          if qemu_img_supports_offset_and_size () &&
             libvirt_supports_json_raw_driver () &&
-            (untar_metadata ova tmpdir;
-             no_disks_are_compressed ova tmpdir) then
-           tmpdir, TarOptimized ova
+            (untar_metadata ova ovatmpdir;
+             no_disks_are_compressed ova ovatmpdir) then
+           ovatmpdir, TarOptimized ova
          else (
            (* If qemu/libvirt is too old or any disk is compressed
             * then we must fall back on the slow path.
             *)
-           untar ova tmpdir;
-           tmpdir, Directory
+           untar ova ovatmpdir;
+           ovatmpdir, Directory
          )
 
       | `Zip ->
@@ -102,16 +101,16 @@ let rec parse_ova ova =
           *)
          let cmd =
            [ "unzip" ] @ (if verbose () then [] else [ "-q" ]) @
-           [ "-j"; "-d"; tmpdir; ova ] in
+           [ "-j"; "-d"; ovatmpdir; ova ] in
          if run_command cmd <> 0 then
            error (f_"error unpacking %s, see earlier error messages") ova;
-         tmpdir, Directory
+         ovatmpdir, Directory
 
       | (`GZip|`XZ) as format ->
          (match uncompressed_type format ova with
           | `Tar ->
-             untar ~format ova tmpdir;
-             tmpdir, Directory
+             untar ~format ova ovatmpdir;
+             ovatmpdir, Directory
           | `Zip | `GZip | `XZ | `Unknown ->
              error (f_"%s: unsupported file format\n\nFormats which we currently understand for '-i ova' are: tar (uncompressed, compress with gzip or xz), zip") ova
          )
@@ -222,11 +221,10 @@ 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
-  Sys.remove tmpfile;
   ret
 
 (* Find files in [dir] ending with [ext]. *)
diff --git a/v2v/utils.ml b/v2v/utils.ml
index ccbb9d68..3dbb4da9 100644
--- a/v2v/utils.ml
+++ b/v2v/utils.ml
@@ -22,8 +22,30 @@ open Printf
 
 open Std_utils
 open Tools_utils
+open Unix_utils
 open Common_gettext.Gettext
 
+let lazy_tmpdir =
+  lazy (
+    let tmpdir = Mkdtemp.temp_dir "virt-v2v." in
+    rmdir_on_exit tmpdir;
+    tmpdir
+  )
+
+let lazy_cachedir =
+  lazy (
+    let base_dir = (open_guestfs ())#get_cachedir () in
+    let cachedir = Mkdtemp.temp_dir ~base_dir "virt-v2v." in
+    rmdir_on_exit cachedir;
+    cachedir
+  )
+
+let tmpdir =
+  Lazy.force lazy_tmpdir
+
+let cachedir =
+  Lazy.force lazy_cachedir
+
 (* Is SELinux enabled and enforcing on the host? *)
 let have_selinux =
   0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing"
@@ -112,7 +134,7 @@ 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
   Unix.truncate tmp 1024;
 
   let json = [
@@ -132,7 +154,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 937e2b9b..8f279263 100644
--- a/v2v/utils.mli
+++ b/v2v/utils.mli
@@ -18,6 +18,13 @@
 
 (** Utilities used in virt-v2v only. *)
 
+val tmpdir : string
+(** The single temporary directory of virt-v2v for small files. *)
+
+val cachedir : string
+(** The single temporary directory of virt-v2v for big files,
+    such as disks. *)
+
 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 73edff2c..6e88d978 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 cachedir) in
+  debug "check_host_free_space: cachedir=%s free_space=%Ld"
+        cachedir 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)
+          cachedir (human_size free_space)
 
 (* Create a qcow2 v3 overlay to protect the source image(s). *)
 and create_overlays source_disks =
@@ -286,8 +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
-      unlink_on_exit overlay_file;
+        Filename.temp_file ~temp_dir:cachedir "v2vovl" ".qcow2" in
 
       (* There is a specific reason to use the newer qcow2 variant:
        * Because the L2 table can store zero clusters efficiently, and
@@ -823,7 +820,7 @@ 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
+        sprintf "%s/%s-%s.qcow2" cachedir 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.1




More information about the Libguestfs mailing list