[Libguestfs] [PATCH v4 10/12] v2v: Implement the --bandwidth* options to control network bandwidth.

Richard W.M. Jones rjones at redhat.com
Fri Sep 20 09:28:21 UTC 2019


For input methods which use nbdkit, we can cheaply add
nbdkit-rate-filter to control input-side network bandwidth.  These
options control that filter.  We can choose to set the bandwidth
statically and optionally change it dynamically:

  --bandwidth 10M
    # static bandwidth of 10 Mbps, no dynamic adjustment possible

  --bandwidth 5M --bandwidth-file /tmp/bw
    # initial static bandwidth of 5 Mbps, adjustable by writing to /tmp/bw

  --bandwidth-file /tmp/bw
    # no initial bandwidth cap, can be added later by writing to /tmp/bw

It only makes sense to control the input side since virt-v2v writes a
lot less data than it reads.
---
 v2v/Makefile.am                    |  1 +
 v2v/cmdline.ml                     | 17 ++++++++++--
 v2v/cmdline.mli                    |  1 +
 v2v/input_disk.ml                  |  2 +-
 v2v/input_libvirt_other.ml         |  4 +--
 v2v/input_libvirt_other.mli        |  2 +-
 v2v/input_libvirt_vcenter_https.ml |  6 ++--
 v2v/input_libvirt_vddk.ml          |  6 ++--
 v2v/input_libvirt_xen_ssh.ml       |  6 ++--
 v2v/input_libvirtxml.ml            |  2 +-
 v2v/input_ova.ml                   |  2 +-
 v2v/input_vmx.ml                   | 26 ++++++++++--------
 v2v/nbdkit.ml                      | 34 +++++++++++++++++------
 v2v/nbdkit.mli                     |  9 ++++--
 v2v/parse_libvirt_xml.ml           |  9 +++---
 v2v/parse_libvirt_xml.mli          |  4 +--
 v2v/types.ml                       |  6 +++-
 v2v/types.mli                      |  7 ++++-
 v2v/v2v.ml                         |  3 +-
 v2v/vCenter.ml                     |  4 +--
 v2v/vCenter.mli                    |  3 +-
 v2v/virt-v2v.pod                   | 44 ++++++++++++++++++++++++++++++
 22 files changed, 146 insertions(+), 52 deletions(-)

diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index 55b966efa..63d9b963a 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -281,6 +281,7 @@ virt_v2v_copy_to_local_CFLAGS = \
 	$(LIBVIRT_CFLAGS)
 
 COPY_TO_LOCAL_BOBJECTS = \
+	types.cmo \
 	uefi.cmo \
 	utils.cmo \
 	libvirt_utils.cmo \
diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml
index 4d390f249..641eed017 100644
--- a/v2v/cmdline.ml
+++ b/v2v/cmdline.ml
@@ -29,6 +29,7 @@ open Types
 open Utils
 
 type cmdline = {
+  bandwidth : bandwidth option;
   compressed : bool;
   debug_overlays : bool;
   do_copy : bool;
@@ -47,6 +48,8 @@ type cmdline = {
 let mac_re = PCRE.compile ~anchored:true "([[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}):(network|bridge):(.*)"
 
 let parse_cmdline () =
+  let bandwidth = ref None in
+  let bandwidth_file = ref None in
   let compressed = ref false in
   let debug_overlays = ref false in
   let do_copy = ref true in
@@ -191,6 +194,10 @@ let parse_cmdline () =
   and ovf_flavours_str = String.concat "|" Create_ovf.ovf_flavours in
 
   let argspec = [
+    [ L"bandwidth" ], Getopt.String ("bps", set_string_option_once "--bandwidth" bandwidth),
+                                    s_"Set bandwidth to bits per sec";
+    [ L"bandwidth-file" ], Getopt.String ("filename", set_string_option_once "--bandwidth-file" bandwidth_file),
+                                    s_"Set bandwidth dynamically from file";
     [ S 'b'; L"bridge" ], Getopt.String ("in:out", add_bridge),
                                     s_"Map bridge ‘in’ to ‘out’";
     [ L"compressed" ], Getopt.Set compressed,
@@ -304,6 +311,11 @@ read the man page virt-v2v(1).
 
   (* Dereference the arguments. *)
   let args = List.rev !args in
+  let bandwidth =
+    match !bandwidth, !bandwidth_file with
+    | None, None -> None
+    | Some rate, None -> Some (StaticBandwidth rate)
+    | rate, Some filename -> Some (DynamicBandwidth (rate, filename)) in
   let compressed = !compressed in
   let debug_overlays = !debug_overlays in
   let do_copy = !do_copy in
@@ -351,6 +363,7 @@ read the man page virt-v2v(1).
     pr "in-place\n";
     pr "io/oo\n";
     pr "mac-option\n";
+    pr "bandwidth-option\n";
     List.iter (pr "input:%s\n") (Modules_list.input_modules ());
     List.iter (pr "output:%s\n") (Modules_list.output_modules ());
     List.iter (pr "convert:%s\n") (Modules_list.convert_modules ());
@@ -683,8 +696,8 @@ read the man page virt-v2v(1).
       output_format, output_alloc in
 
   {
-    compressed; debug_overlays; do_copy; in_place; network_map;
-    output_alloc; output_format; output_name;
+    bandwidth; compressed; debug_overlays; do_copy; in_place;
+    network_map; output_alloc; output_format; output_name;
     print_estimate; print_source; root_choice;
     ks = opthandle.ks;
   },
diff --git a/v2v/cmdline.mli b/v2v/cmdline.mli
index 78601e191..1c9e6c258 100644
--- a/v2v/cmdline.mli
+++ b/v2v/cmdline.mli
@@ -19,6 +19,7 @@
 (** Command line argument parsing. *)
 
 type cmdline = {
+  bandwidth : Types.bandwidth option;
   compressed : bool;
   debug_overlays : bool;
   do_copy : bool;
diff --git a/v2v/input_disk.ml b/v2v/input_disk.ml
index 8321a2a8c..52f40a31b 100644
--- a/v2v/input_disk.ml
+++ b/v2v/input_disk.ml
@@ -36,7 +36,7 @@ class input_disk input_format disk = object
       | Some fmt -> " -if " ^ fmt)
       disk
 
-  method source () =
+  method source ?bandwidth () =
     (* Check the input file exists and is readable. *)
     Unix.access disk [Unix.R_OK];
 
diff --git a/v2v/input_libvirt_other.ml b/v2v/input_libvirt_other.ml
index 5ff3cfc43..504c72600 100644
--- a/v2v/input_libvirt_other.ml
+++ b/v2v/input_libvirt_other.ml
@@ -58,10 +58,10 @@ class input_libvirt_other libvirt_conn guest =
 object (self)
   inherit input_libvirt libvirt_conn guest
 
-  method source () =
+  method source ?bandwidth () =
     debug "input_libvirt_other: source ()";
 
-    let source, disks, _ = parse_libvirt_domain self#conn guest in
+    let source, disks, _ = parse_libvirt_domain ?bandwidth self#conn guest in
     let disks = List.map (fun { p_source_disk = disk } -> disk) disks in
     { source with s_disks = disks }
 end
diff --git a/v2v/input_libvirt_other.mli b/v2v/input_libvirt_other.mli
index d48987e46..1aac92ad6 100644
--- a/v2v/input_libvirt_other.mli
+++ b/v2v/input_libvirt_other.mli
@@ -23,7 +23,7 @@ val error_if_libvirt_does_not_support_json_backingfile : unit -> unit
 class virtual input_libvirt : Libvirt.rw Libvirt.Connect.t Lazy.t -> string -> object
   method precheck : unit -> unit
   method as_options : string
-  method virtual source : unit -> Types.source
+  method virtual source : ?bandwidth:Types.bandwidth -> unit -> Types.source
   method private conn : Libvirt.rw Libvirt.Connect.t
 end
 
diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml
index bfe5e8e9d..280febf98 100644
--- a/v2v/input_libvirt_vcenter_https.ml
+++ b/v2v/input_libvirt_vcenter_https.ml
@@ -41,7 +41,7 @@ object (self)
   method precheck () =
     error_if_libvirt_does_not_support_json_backingfile ()
 
-  method source () =
+  method source ?bandwidth () =
     debug "input_libvirt_vcenter_https: source: server %s" server;
 
     (* Remove proxy environment variables so curl doesn't try to use
@@ -56,7 +56,7 @@ object (self)
     unsetenv "ALL_PROXY";
     unsetenv "NO_PROXY";
 
-    let source, disks, xml = parse_libvirt_domain self#conn guest in
+    let source, disks, xml = parse_libvirt_domain ?bandwidth self#conn guest in
 
     (* Find the <vmware:datacenterpath> element from the XML.  This
      * was added in libvirt >= 1.2.20.
@@ -78,7 +78,7 @@ object (self)
       | { 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 ?password_file:input_password
+          VCenter.map_source ?bandwidth ?password_file:input_password
                              dcPath parsed_uri server path in
 
         (* The libvirt ESX driver doesn't normally specify a format, but
diff --git a/v2v/input_libvirt_vddk.ml b/v2v/input_libvirt_vddk.ml
index 1f54ee511..19b4166c7 100644
--- a/v2v/input_libvirt_vddk.ml
+++ b/v2v/input_libvirt_vddk.ml
@@ -113,8 +113,8 @@ object (self)
             super#as_options (* superclass prints "-i libvirt etc" *)
             pt_options
 
-  method source () =
-    let source, disks, xml = parse_libvirt_domain self#conn guest in
+  method source ?bandwidth () =
+    let source, disks, xml = parse_libvirt_domain ?bandwidth self#conn guest in
 
     (* Find the <vmware:moref> element from the XML.  This was added
      * in libvirt >= 3.7 and is required.
@@ -183,7 +183,7 @@ object (self)
           * directly in this form to VDDK.
           *)
          let nbdkit =
-           Nbdkit.create_vddk ?config ?cookie ?libdir ~moref
+           Nbdkit.create_vddk ?bandwidth ?config ?cookie ?libdir ~moref
                               ?nfchostport ?password_file:input_password ?port
                               ~server ?snapshot ~thumbprint ?transports ?user
                               path in
diff --git a/v2v/input_libvirt_xen_ssh.ml b/v2v/input_libvirt_xen_ssh.ml
index 9a941d070..f5877b054 100644
--- a/v2v/input_libvirt_xen_ssh.ml
+++ b/v2v/input_libvirt_xen_ssh.ml
@@ -40,10 +40,10 @@ object (self)
     error_if_libvirt_does_not_support_json_backingfile ();
     error_if_no_ssh_agent ()
 
-  method source () =
+  method source ?bandwidth () =
     debug "input_libvirt_xen_ssh: source: server %s" server;
 
-    let source, disks, _ = parse_libvirt_domain self#conn guest in
+    let source, disks, _ = parse_libvirt_domain ?bandwidth self#conn guest in
 
     let port =
       match parsed_uri.uri_port with
@@ -61,7 +61,7 @@ object (self)
         disk
       | { p_source_disk = disk; p_source = P_source_dev path }
       | { p_source_disk = disk; p_source = P_source_file path } ->
-         let nbdkit = Nbdkit.create_ssh ~password:NoPassword
+         let nbdkit = Nbdkit.create_ssh ?bandwidth ~password:NoPassword
                                         ?port ~server ?user path in
          let qemu_uri = Nbdkit.run nbdkit in
         { disk with s_qemu_uri = qemu_uri }
diff --git a/v2v/input_libvirtxml.ml b/v2v/input_libvirtxml.ml
index a44b41fce..efffb28b0 100644
--- a/v2v/input_libvirtxml.ml
+++ b/v2v/input_libvirtxml.ml
@@ -31,7 +31,7 @@ object
 
   method as_options = "-i libvirtxml " ^ file
 
-  method source () =
+  method source ?bandwidth () =
     let xml = read_whole_file file in
 
     let source, disks = parse_libvirt_xml xml in
diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml
index 6309ff9a5..872796137 100644
--- a/v2v/input_ova.ml
+++ b/v2v/input_ova.ml
@@ -75,7 +75,7 @@ class input_ova ova = object
 
   method as_options = "-i ova " ^ ova
 
-  method source () =
+  method source ?bandwidth () =
     (* Extract ova file. *)
     let ova_t = parse_ova ova in
 
diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml
index 0db1945ea..1a7b331ee 100644
--- a/v2v/input_vmx.ml
+++ b/v2v/input_vmx.ml
@@ -112,8 +112,9 @@ let remote_file_exists uri path =
     eprintf "%s\n%!" cmd;
   Sys.command cmd = 0
 
-let rec find_disks vmx vmx_source =
-  find_scsi_disks vmx vmx_source @ find_ide_disks vmx vmx_source
+let rec find_disks ?bandwidth vmx vmx_source =
+  find_scsi_disks ?bandwidth vmx vmx_source
+  @ find_ide_disks ?bandwidth vmx vmx_source
 
 (* Find all SCSI hard disks.
  *
@@ -123,7 +124,7 @@ let rec find_disks vmx vmx_source =
  *                        | omitted
  *   scsi0:0.fileName = "guest.vmdk"
  *)
-and find_scsi_disks vmx vmx_source =
+and find_scsi_disks ?bandwidth vmx vmx_source =
   let get_scsi_controller_target ns =
     sscanf ns "scsi%d:%d" (fun c t -> c, t)
   in
@@ -135,7 +136,7 @@ and find_scsi_disks vmx vmx_source =
                             Some "scsi-harddisk"; None ] in
   let scsi_controller = Source_SCSI in
 
-  find_hdds vmx vmx_source
+  find_hdds ?bandwidth vmx vmx_source
             get_scsi_controller_target is_scsi_controller_target
             scsi_device_types scsi_controller
 
@@ -145,7 +146,7 @@ and find_scsi_disks vmx vmx_source =
  *   ide0:0.deviceType = "ata-hardDisk"
  *   ide0:0.fileName = "guest.vmdk"
  *)
-and find_ide_disks vmx vmx_source =
+and find_ide_disks ?bandwidth vmx vmx_source =
   let get_ide_controller_target ns =
     sscanf ns "ide%d:%d" (fun c t -> c, t)
   in
@@ -156,11 +157,11 @@ and find_ide_disks vmx vmx_source =
   let ide_device_types = [ Some "ata-harddisk" ] in
   let ide_controller = Source_IDE in
 
-  find_hdds vmx vmx_source
+  find_hdds ?bandwidth vmx vmx_source
             get_ide_controller_target is_ide_controller_target
             ide_device_types ide_controller
 
-and find_hdds vmx vmx_source
+and find_hdds ?bandwidth vmx vmx_source
               get_controller_target is_controller_target
               device_types controller =
   (* Find namespaces matching '(ide|scsi)X:Y' with suitable deviceType. *)
@@ -186,7 +187,8 @@ and find_hdds vmx vmx_source
         match path, v with
         | [ns; "filename"], Some filename ->
            let c, t = get_controller_target ns in
-           let uri, format = qemu_uri_of_filename vmx_source filename in
+           let uri, format = qemu_uri_of_filename ?bandwidth
+                                                  vmx_source filename in
            let s = { s_disk_id = (-1);
                      s_qemu_uri = uri; s_format = Some format;
                      s_controller = Some controller } in
@@ -213,7 +215,7 @@ and find_hdds vmx vmx_source
  * This constructs a QEMU URI of the filename relative to the
  * vmx file (which might be remote over SSH).
  *)
-and qemu_uri_of_filename vmx_source filename =
+and qemu_uri_of_filename ?bandwidth vmx_source filename =
   match vmx_source with
   | File vmx_filename ->
      (* Always ensure this returns an absolute path to avoid
@@ -240,7 +242,7 @@ and qemu_uri_of_filename vmx_source filename =
      let port = Option.map string_of_int (port_of_uri uri) in
      let user = uri.Xml.uri_user in
 
-     let nbdkit = Nbdkit.create_ssh ~password:NoPassword ~server
+     let nbdkit = Nbdkit.create_ssh ?bandwidth ~password:NoPassword ~server
                                     ?port ?user abs_path in
      let qemu_uri = Nbdkit.run nbdkit in
      qemu_uri, format
@@ -392,7 +394,7 @@ object
 
   method as_options = "-i vmx " ^ arg
 
-  method source () =
+  method source ?bandwidth () =
     let vmx_source = vmx_source_of_arg input_transport arg in
 
     (* If the transport is SSH, fetch the file from remote, else
@@ -486,7 +488,7 @@ object
          None
       | None -> None in
 
-    let disks = find_disks vmx vmx_source in
+    let disks = find_disks ?bandwidth vmx vmx_source in
     let removables = find_removables vmx in
     let nics = find_nics vmx in
 
diff --git a/v2v/nbdkit.ml b/v2v/nbdkit.ml
index b2d77f963..776eedce0 100644
--- a/v2v/nbdkit.ml
+++ b/v2v/nbdkit.ml
@@ -24,6 +24,7 @@ open Std_utils
 open Tools_utils
 open Unix_utils
 
+open Types
 open Utils
 
 let nbdkit_min_version = (1, 12)
@@ -88,7 +89,7 @@ let error_unless_nbdkit_compiled_with_selinux dump_config =
       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 =
+let common_create ?bandwidth plugin_name plugin_args plugin_env =
   error_unless_nbdkit_working ();
 
   (* Environment.  We always add LANG=C. *)
@@ -150,7 +151,24 @@ let common_create plugin_name plugin_args plugin_env =
     add_arg "--filter"; add_arg "readahead"
   );
 
-  let args = get_args () @ [ plugin_name ] @ plugin_args in
+  (* Add the rate filter. *)
+  let rate_args =
+    if Sys.file_exists (filterdir // "nbdkit-rate-filter.so") then (
+      match bandwidth with
+      | None -> []
+      | Some bandwidth ->
+         add_arg "--filter"; add_arg "rate";
+         match bandwidth with
+         | StaticBandwidth rate ->
+            [ "rate=" ^ rate ]
+         | DynamicBandwidth (None, filename) ->
+            [ "rate-file=" ^ filename ]
+         | DynamicBandwidth (Some rate, filename) ->
+            [ "rate=" ^ rate; "rate-file=" ^ filename ]
+    )
+    else [] in
+
+  let args = get_args () @ [ plugin_name ] @ plugin_args @ rate_args in
 
   { plugin_name; args; env; dump_config; dump_plugin; filterdir }
 
@@ -160,7 +178,7 @@ let common_create plugin_name plugin_args plugin_env =
 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
+let create_vddk ?bandwidth ?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. *)
@@ -255,10 +273,10 @@ 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;
 
-  common_create "vddk" (get_args ()) env
+  common_create ?bandwidth "vddk" (get_args ()) env
 
 (* Create an nbdkit module specialized for reading from SSH sources. *)
-let create_ssh ~password ?port ~server ?user path =
+let create_ssh ?bandwidth ~password ?port ~server ?user path =
   let add_arg, get_args =
     let args = ref [] in
     let add_arg a = List.push_front a args in
@@ -276,10 +294,10 @@ let create_ssh ~password ?port ~server ?user path =
   );
   add_arg (sprintf "path=%s" path);
 
-  common_create "ssh" (get_args ()) []
+  common_create ?bandwidth "ssh" (get_args ()) []
 
 (* Create an nbdkit module specialized for reading from Curl sources. *)
-let create_curl ?cookie ~password ?(sslverify=true) ?user url =
+let create_curl ?bandwidth ?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
@@ -299,7 +317,7 @@ let create_curl ?cookie ~password ?(sslverify=true) ?user url =
   if not sslverify then add_arg "sslverify=false";
   add_arg (sprintf "url=%s" url);
 
-  common_create "curl" (get_args ()) []
+  common_create ?bandwidth "curl" (get_args ()) []
 
 let run { args; env } =
   (* Create a temporary directory where we place the sockets. *)
diff --git a/v2v/nbdkit.mli b/v2v/nbdkit.mli
index efbb62f98..627c78c11 100644
--- a/v2v/nbdkit.mli
+++ b/v2v/nbdkit.mli
@@ -20,7 +20,8 @@
 
 type t
 
-val create_vddk : ?config:string ->
+val create_vddk : ?bandwidth:Types.bandwidth ->
+                  ?config:string ->
                   ?cookie:string ->
                   ?libdir:string ->
                   moref:string ->
@@ -46,7 +47,8 @@ type password =
 | AskForPassword
 | PasswordFile of string
 
-val create_ssh : password:password ->
+val create_ssh : ?bandwidth:Types.bandwidth ->
+                 password:password ->
                  ?port:string ->
                  server:string ->
                  ?user:string ->
@@ -59,7 +61,8 @@ val create_ssh : password:password ->
 
     Note this doesn't run nbdkit yet, it just creates the object. *)
 
-val create_curl : ?cookie:string ->
+val create_curl : ?bandwidth:Types.bandwidth ->
+                  ?cookie:string ->
                   password:password ->
                   ?sslverify:bool ->
                   ?user:string ->
diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml
index 97d8a5cd8..86990aeb3 100644
--- a/v2v/parse_libvirt_xml.ml
+++ b/v2v/parse_libvirt_xml.ml
@@ -46,7 +46,7 @@ let get_drive_slot str offset =
        warning (f_"could not parse device name ‘%s’ from the source libvirt XML") str;
        None
 
-let parse_libvirt_xml ?conn xml =
+let parse_libvirt_xml ?bandwidth ?conn xml =
   debug "libvirt xml is:\n%s" xml;
 
   (* Create a default libvirt connection on request, to not open one
@@ -319,7 +319,8 @@ let parse_libvirt_xml ?conn xml =
                | _, 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 nbdkit = Nbdkit.create_curl ?bandwidth ~password:NoPassword
+                                           url in
            let qemu_uri = Nbdkit.run nbdkit in
            add_disk qemu_uri format controller P_dont_rewrite
         | Some protocol, _, _ ->
@@ -537,9 +538,9 @@ let parse_libvirt_xml ?conn xml =
    },
    disks)
 
-let parse_libvirt_domain conn guest =
+let parse_libvirt_domain ?bandwidth conn guest =
   let dom = Libvirt_utils.get_domain conn guest in
   (* Use XmlSecure to get passwords (RHBZ#1174123). *)
   let xml = Libvirt.Domain.get_xml_desc_flags dom [Libvirt.Domain.XmlSecure] in
-  let source, disks = parse_libvirt_xml ~conn xml in
+  let source, disks = parse_libvirt_xml ?bandwidth ~conn xml in
   source, disks, xml
diff --git a/v2v/parse_libvirt_xml.mli b/v2v/parse_libvirt_xml.mli
index 2d81e0d99..658ebc5eb 100644
--- a/v2v/parse_libvirt_xml.mli
+++ b/v2v/parse_libvirt_xml.mli
@@ -27,7 +27,7 @@ and parsed_source =
 | P_source_file of string            (** <source file> *)
 | P_dont_rewrite                     (** s_qemu_uri is already set. *)
 
-val parse_libvirt_domain : Libvirt.rw Libvirt.Connect.t -> string -> Types.source * parsed_disk list * string
+val parse_libvirt_domain : ?bandwidth:Types.bandwidth -> Libvirt.rw Libvirt.Connect.t -> string -> Types.source * parsed_disk list * string
 (** [parse_libvirt_domain conn dom] loads the XML of the domain [dom]
     from the libvirt connection [conn].
     The result is a tuple with a {!Types.source} structure, a list of
@@ -36,7 +36,7 @@ val parse_libvirt_domain : Libvirt.rw Libvirt.Connect.t -> string -> Types.sourc
     {b Note} the [source.s_disks] field is an empty list.  The caller
     must map over the parsed disks and update the [source.s_disks] field. *)
 
-val parse_libvirt_xml : ?conn:Libvirt.rw Libvirt.Connect.t -> string -> Types.source * parsed_disk list
+val parse_libvirt_xml : ?bandwidth:Types.bandwidth -> ?conn:Libvirt.rw Libvirt.Connect.t -> string -> Types.source * parsed_disk list
 (** Take libvirt XML and parse it into a {!Types.source} structure and a
     list of source disks.
 
diff --git a/v2v/types.ml b/v2v/types.ml
index 1100dd2a6..d406caeb9 100644
--- a/v2v/types.ml
+++ b/v2v/types.ml
@@ -506,10 +506,14 @@ type root_choice = AskRoot | SingleRoot | FirstRoot | RootDev of string
 
 type output_allocation = Sparse | Preallocated
 
+type bandwidth =
+| StaticBandwidth of string
+| DynamicBandwidth of string option * string
+
 class virtual input = object
   method precheck () = ()
   method virtual as_options : string
-  method virtual source : unit -> source
+  method virtual source : ?bandwidth:bandwidth -> unit -> source
 end
 
 class virtual output = object
diff --git a/v2v/types.mli b/v2v/types.mli
index d605b1ad4..556f6930f 100644
--- a/v2v/types.mli
+++ b/v2v/types.mli
@@ -361,6 +361,11 @@ type root_choice = AskRoot | SingleRoot | FirstRoot | RootDev of string
 type output_allocation = Sparse | Preallocated
 (** Type of [-oa] (output allocation) option. *)
 
+type bandwidth =
+| StaticBandwidth of string
+| DynamicBandwidth of string option * string
+(** [--bandwidth] and [--bandwidth-file] options. *)
+
 (** {2 Input object}
 
     This is subclassed for the various input [-i] options.
@@ -398,7 +403,7 @@ class virtual input : object
   method virtual as_options : string
   (** Converts the input object back to the equivalent command line options.
       This is just used for pretty-printing log messages. *)
-  method virtual source : unit -> source
+  method virtual source : ?bandwidth:bandwidth -> unit -> source
   (** Examine the source hypervisor and create a source struct. *)
 end
 (** Encapsulates all [-i], etc input arguments as an object. *)
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
index afe46cd9b..e4b4dfe37 100644
--- a/v2v/v2v.ml
+++ b/v2v/v2v.ml
@@ -198,7 +198,8 @@ let rec main () =
 
 and open_source cmdline input =
   message (f_"Opening the source %s") input#as_options;
-  let source = input#source () in
+  let bandwidth = cmdline.bandwidth in
+  let source = input#source ?bandwidth () in
 
   (* Print source and stop. *)
   if cmdline.print_source then (
diff --git a/v2v/vCenter.ml b/v2v/vCenter.ml
index 2563ad0ed..89c5579b9 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 ?password_file dcPath uri server path =
+let rec map_source ?bandwidth ?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.
    *)
@@ -78,7 +78,7 @@ let rec map_source ?password_file dcPath uri server path =
     | Some password_file -> Nbdkit.PasswordFile password_file in
 
   let nbdkit =
-    Nbdkit.create_curl ?cookie:session_cookie ~password ~sslverify
+    Nbdkit.create_curl ?bandwidth ?cookie:session_cookie ~password ~sslverify
                        https_url in
   let qemu_uri = Nbdkit.run nbdkit in
 
diff --git a/v2v/vCenter.mli b/v2v/vCenter.mli
index d72d5686e..5620cad45 100644
--- a/v2v/vCenter.mli
+++ b/v2v/vCenter.mli
@@ -54,7 +54,8 @@ type remote_resource = {
 (** The "remote resource" is the structure returned by the {!map_source}
     function. *)
 
-val map_source : ?password_file:string -> string -> Xml.uri -> string -> string -> remote_resource
+val map_source : ?bandwidth:Types.bandwidth -> ?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,
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
index 9a555c3be..8ba141be9 100644
--- a/v2v/virt-v2v.pod
+++ b/v2v/virt-v2v.pod
@@ -155,6 +155,50 @@ qemu, do:
 
 Display help.
 
+=item B<--bandwidth> bps
+
+=item B<--bandwidth-file> filename
+
+Some input methods are able to limit the network bandwidth they will
+use statically or dynamically.  In the first variant this sets the
+bandwidth limit statically in bits per second.  Formats like C<10M>
+may be used (meaning 10 megabits per second).
+
+In the second variant the bandwidth is limited dynamically from the
+content of the file (also in bits per second, in the same formats
+supported by the first variant).  You may use both parameters
+together, meaning: first limit to a static rate, then you can create
+the file while virt-v2v is running to adjust the rate dynamically.
+
+This is only supported for:
+
+=over 4
+
+=item *
+
+L<input from Xen|virt-v2v-input-xen(1)>
+
+=item *
+
+L<input from VMware VMX|virt-v2v-input-vmware(1)/INPUT FROM VMWARE VMX>
+when using the SSH transport method
+
+=item *
+
+L<input from VDDK|virt-v2v-input-vmware(1)/INPUT FROM VDDK>
+
+=item *
+
+I<-i libvirtxml> when using HTTP or HTTPS disks
+
+=item *
+
+L<input from VMware vCenter server|virt-v2v-input-vmware(1)/INPUT FROM VMWARE VCENTER SERVER>
+
+=back
+
+The options are silently ignored for other input methods.
+
 =item B<-b> ...
 
 =item B<--bridge> ...
-- 
2.23.0




More information about the Libguestfs mailing list