88802688a4
resolves: RHEL-58032
277 lines
9.4 KiB
Diff
277 lines
9.4 KiB
Diff
From 037a603c2d5cf9d2d5f8157116dbf14945277dc2 Mon Sep 17 00:00:00 2001
|
||
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||
Date: Mon, 2 Dec 2024 15:22:43 +0000
|
||
Subject: [PATCH] v2v: Implement --parallel=N for parallel disk copies
|
||
|
||
When set, run up to N copies of nbdcopy in parallel. This only
|
||
applies for guests with multiple disks.
|
||
|
||
The default, as for all older versions of virt-v2v, is to copy disks
|
||
one at a time.
|
||
|
||
(cherry picked from commit fd1148f79581b148525eb12154aef7603ccf0baa)
|
||
---
|
||
docs/virt-v2v.pod | 13 +++++++
|
||
lib/utils.ml | 6 ++++
|
||
lib/utils.mli | 4 +++
|
||
tests/Makefile.am | 2 ++
|
||
tests/test-v2v-i-disk-parallel.sh | 54 +++++++++++++++++++++++++++++
|
||
v2v/v2v.ml | 56 +++++++++++++++++++++++++------
|
||
6 files changed, 125 insertions(+), 10 deletions(-)
|
||
create mode 100755 tests/test-v2v-i-disk-parallel.sh
|
||
|
||
diff --git a/docs/virt-v2v.pod b/docs/virt-v2v.pod
|
||
index 74581463..216e617d 100644
|
||
--- a/docs/virt-v2v.pod
|
||
+++ b/docs/virt-v2v.pod
|
||
@@ -749,6 +749,19 @@ C<root>.
|
||
You will get an error if virt-v2v is unable to mount/write to the
|
||
Export Storage Domain.
|
||
|
||
+=item B<--parallel> N
|
||
+
|
||
+Enable parallel copying if the guest has multiple disks. I<N> is the
|
||
+maximum number of parallel L<nbdcopy(1)> instances to run.
|
||
+
|
||
+The default is to run at most one instance of nbdcopy
|
||
+(ie. I<--parallel=1>). All versions of virt-v2v E<le> 2.7.2 also did
|
||
+disk copies one at a time.
|
||
+
|
||
+Within each guest disk, nbdcopy tries to copy in parallel if the
|
||
+underlying endpoints support that. This is not affected by this
|
||
+command line option. See the L<nbdcopy(1)> manual page for details.
|
||
+
|
||
=item B<--print-source>
|
||
|
||
Print information about the source guest and stop. This option is
|
||
diff --git a/lib/utils.ml b/lib/utils.ml
|
||
index c4cfd89b..f2da9e80 100644
|
||
--- a/lib/utils.ml
|
||
+++ b/lib/utils.ml
|
||
@@ -29,6 +29,12 @@ let large_tmpdir =
|
||
try Sys.getenv "VIRT_V2V_TMPDIR"
|
||
with Not_found -> (open_guestfs ())#get_cachedir ()
|
||
|
||
+let string_of_process_status = function
|
||
+ | Unix.WEXITED 0 -> s_"success"
|
||
+ | WEXITED i -> sprintf (f_"exited with non-zero error code %d") i
|
||
+ | WSIGNALED i -> sprintf (f_"signalled by signal %d") i
|
||
+ | WSTOPPED i -> sprintf (f_"stopped by signal %d") i
|
||
+
|
||
(* Is SELinux enabled and enforcing on the host? *)
|
||
let have_selinux =
|
||
0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing"
|
||
diff --git a/lib/utils.mli b/lib/utils.mli
|
||
index afe61a4e..e7ee13d1 100644
|
||
--- a/lib/utils.mli
|
||
+++ b/lib/utils.mli
|
||
@@ -23,6 +23,10 @@ val large_tmpdir : string
|
||
such as overlays in this directory. Small temporary files can
|
||
use the default behaviour eg. of {!Filename.temp_file} *)
|
||
|
||
+val string_of_process_status : Unix.process_status -> string
|
||
+(** Convert a process status (such as returned by {!Unix.wait}) into
|
||
+ a printable string. *)
|
||
+
|
||
val have_selinux : bool
|
||
(** True if SELinux is enabled and enforcing on the host. *)
|
||
|
||
diff --git a/tests/Makefile.am b/tests/Makefile.am
|
||
index 8a710b99..fa5bb4f1 100644
|
||
--- a/tests/Makefile.am
|
||
+++ b/tests/Makefile.am
|
||
@@ -76,6 +76,7 @@ TESTS = \
|
||
test-v2v-cdrom.sh \
|
||
test-v2v-floppy.sh \
|
||
test-v2v-i-disk.sh \
|
||
+ test-v2v-i-disk-parallel.sh \
|
||
test-v2v-i-ova.sh \
|
||
test-v2v-inspector.sh \
|
||
test-v2v-mac.sh \
|
||
@@ -189,6 +190,7 @@ EXTRA_DIST += \
|
||
test-v2v-floppy.expected \
|
||
test-v2v-floppy.sh \
|
||
test-v2v-i-disk.sh \
|
||
+ test-v2v-i-disk-parallel.sh \
|
||
test-v2v-i-ova-as-root.ovf \
|
||
test-v2v-i-ova-as-root.sh \
|
||
test-v2v-i-ova-bad-sha1.sh \
|
||
diff --git a/tests/test-v2v-i-disk-parallel.sh b/tests/test-v2v-i-disk-parallel.sh
|
||
new file mode 100755
|
||
index 00000000..a6470fdd
|
||
--- /dev/null
|
||
+++ b/tests/test-v2v-i-disk-parallel.sh
|
||
@@ -0,0 +1,54 @@
|
||
+#!/bin/bash -
|
||
+# libguestfs virt-v2v test script
|
||
+# Copyright (C) 2014-2024 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.
|
||
+
|
||
+# Test --parallel option.
|
||
+
|
||
+set -e
|
||
+
|
||
+source ./functions.sh
|
||
+set -e
|
||
+set -x
|
||
+
|
||
+skip_if_skipped
|
||
+windows=../test-data/phony-guests/windows.img
|
||
+requires test -f $windows
|
||
+
|
||
+export VIRT_TOOLS_DATA_DIR="$srcdir/../test-data/fake-virt-tools"
|
||
+
|
||
+d=test-v2v-i-disk-parallel.d
|
||
+rm -rf $d
|
||
+cleanup_fn rm -rf $d
|
||
+mkdir $d
|
||
+
|
||
+truncate -s $((100*1024*1024)) $d/disk-2.img $d/disk-3.img $d/disk-4.img
|
||
+
|
||
+$VG virt-v2v --debug-gc \
|
||
+ --parallel=2 \
|
||
+ -i disk \
|
||
+ $windows \
|
||
+ $d/disk-2.img \
|
||
+ $d/disk-3.img \
|
||
+ $d/disk-4.img \
|
||
+ -o local -os $d
|
||
+
|
||
+# Test the libvirt XML metadata and output disks were created.
|
||
+test -f $d/windows.xml
|
||
+test -f $d/windows-sda
|
||
+test -f $d/windows-sdb
|
||
+test -f $d/windows-sdc
|
||
+test -f $d/windows-sdd
|
||
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
|
||
index 2fdaf40b..68502884 100644
|
||
--- a/v2v/v2v.ml
|
||
+++ b/v2v/v2v.ml
|
||
@@ -79,6 +79,7 @@ let rec main () =
|
||
)
|
||
in
|
||
|
||
+ let parallel = ref 1 in
|
||
let network_map = Networks.create () in
|
||
let static_ips = ref [] in
|
||
let rec add_network str =
|
||
@@ -263,6 +264,8 @@ let rec main () =
|
||
s_"Set output storage location";
|
||
[ L"password-file" ], Getopt.String ("filename", set_string_option_once "-ip" input_password),
|
||
s_"Same as ‘-ip filename’";
|
||
+ [ L"parallel" ], Getopt.Set_int ("N", parallel),
|
||
+ s_"Run up to N instances of nbdcopy in parallel";
|
||
[ L"print-source" ], Getopt.Set print_source,
|
||
s_"Print source and stop";
|
||
[ L"root" ], Getopt.String ("ask|... ", set_root_choice),
|
||
@@ -365,6 +368,7 @@ read the man page virt-v2v(1).
|
||
| `Preallocated -> Types.Preallocated in
|
||
let output_mode = !output_mode in
|
||
let output_name = !output_name in
|
||
+ let parallel = !parallel in
|
||
let print_source = !print_source in
|
||
let root_choice = !root_choice in
|
||
let static_ips = !static_ips in
|
||
@@ -386,6 +390,7 @@ read the man page virt-v2v(1).
|
||
pr "mac-option\n";
|
||
pr "bandwidth-option\n";
|
||
pr "mac-ip-option\n";
|
||
+ pr "parallel-option\n";
|
||
pr "customize-ops\n";
|
||
pr "input:disk\n";
|
||
pr "input:libvirt\n";
|
||
@@ -583,12 +588,15 @@ read the man page virt-v2v(1).
|
||
else
|
||
List.rev acc
|
||
in
|
||
- let disks = loop [] 0 in
|
||
- let nr_disks = List.length disks in
|
||
+ let disks = ref (loop [] 0) in
|
||
+ let nr_disks = List.length !disks in
|
||
|
||
(* Copy the disks. *)
|
||
- List.iter (
|
||
- fun (i, input_socket, output_socket) ->
|
||
+ let nbdcopy_pids = ref [] in
|
||
+ let rec copy_loop () =
|
||
+ if List.length !nbdcopy_pids < parallel && !disks <> [] then (
|
||
+ (* Schedule another nbdcopy process. *)
|
||
+ let i, input_socket, output_socket = List.pop_front disks in
|
||
message (f_"Copying disk %d/%d") (i+1) nr_disks;
|
||
|
||
let request_size = Output_module.request_size
|
||
@@ -608,8 +616,33 @@ read the man page virt-v2v(1).
|
||
flush Stdlib.stderr
|
||
);
|
||
|
||
- nbdcopy ?request_size output_alloc input_uri output_uri
|
||
- ) disks;
|
||
+ let pid = nbdcopy ?request_size output_alloc input_uri output_uri in
|
||
+ List.push_front pid nbdcopy_pids;
|
||
+
|
||
+ copy_loop ();
|
||
+ )
|
||
+ else if !nbdcopy_pids <> [] then (
|
||
+ (* Wait for one nbdcopy instance to exit. *)
|
||
+ let pid, status = wait () in
|
||
+ (* If this internal error turns up in real world scenarios then
|
||
+ * we may need to change the [wait] above so it only waits on
|
||
+ * the nbdcopy PIDs.
|
||
+ *)
|
||
+ if not (List.mem pid !nbdcopy_pids) then
|
||
+ error (f_"internal error: wait returned unexpected \
|
||
+ process ID %d status \"%s\"")
|
||
+ pid (string_of_process_status status);
|
||
+ nbdcopy_pids := List.filter ((<>) pid) !nbdcopy_pids;
|
||
+ (match status with
|
||
+ | WEXITED 0 -> copy_loop ()
|
||
+ | WEXITED _ | WSIGNALED _ | WSTOPPED _ ->
|
||
+ error "nbdcopy %s" (string_of_process_status status)
|
||
+ );
|
||
+ )
|
||
+ in
|
||
+ copy_loop ();
|
||
+ assert (!disks == []);
|
||
+ assert (!nbdcopy_pids == []);
|
||
|
||
(* End of copying phase. *)
|
||
unlink (v2vdir // "copy");
|
||
@@ -647,6 +680,7 @@ and check_host_free_space () =
|
||
\"Minimum free space check in the host\".")
|
||
large_tmpdir (human_size free_space)
|
||
|
||
+(* Start nbdcopy as a background process, returning the PID. *)
|
||
and nbdcopy ?request_size output_alloc input_uri output_uri =
|
||
(* XXX It's possible that some output modes know whether
|
||
* --target-is-zero which would be a useful optimization.
|
||
@@ -674,10 +708,12 @@ and nbdcopy ?request_size output_alloc input_uri output_uri =
|
||
if not (quiet ()) then List.push_back cmd "--progress";
|
||
if output_alloc = Types.Preallocated then List.push_back cmd "--allocated";
|
||
|
||
- let cmd = !cmd in
|
||
-
|
||
- if run_command cmd <> 0 then
|
||
- error (f_"nbdcopy command failed, see earlier error messages")
|
||
+ let args = Array.of_list !cmd in
|
||
+ match fork () with
|
||
+ | 0 ->
|
||
+ (* Child process (nbdcopy). *)
|
||
+ execvp "nbdcopy" args
|
||
+ | pid -> pid
|
||
|
||
(* Run nbdinfo on a URI and collect the information. However don't
|
||
* fail if nbdinfo is not installed since this is just used for debugging.
|