mrc0mmand / rpms / libguestfs

Forked from rpms/libguestfs 3 years ago
Clone

Blame SOURCES/0042-v2v-Copy-static-IP-address-information-over-for-Wind.patch

498672
From 77606e831f8891b65350effb6502d233d74f8cfc Mon Sep 17 00:00:00 2001
10436e
From: "Richard W.M. Jones" <rjones@redhat.com>
10436e
Date: Tue, 4 Dec 2018 16:09:42 +0000
10436e
Subject: [PATCH] v2v: Copy static IP address information over for Windows
10436e
 guests (RHBZ#1626503).
10436e
10436e
For Linux the guest itself remembers the IP address associated with
10436e
each MAC address.  Thus it doesn't matter if the interface type
10436e
changes (ie. to virtio-net), because as long as we preserve the MAC
10436e
address the guest will use the same IP address or the same DHCP
10436e
configuration.
10436e
10436e
However on Windows this association is not maintained by MAC address.
10436e
In fact the MAC address isn't saved anywhere in the guest registry.
10436e
(It seems instead this is likely done through PCI device type and
10436e
address which we don't record at the moment and is almost impossible
10436e
to preserve.)  When a guest which doesn't use DHCP is migrated, the
10436e
guest sees the brand new virtio-net devices and doesn't know what to
10436e
do with them, and meanwhile the right static IPs are still associated
10436e
with the old and now-defunct interfaces in the registry.
10436e
10436e
We cannot collect the required information from within the guest.
10436e
However we can collect it outside the tool by some other means
10436e
(eg. using VMware Tools APIs) and present this information to virt-v2v
10436e
which then writes it into the Windows guest at firstboot time.
10436e
10436e
This commit adds the --mac ..:ip:.. sub-option which creates a
10436e
Powershell script to set network adapters at firstboot.  An option
10436e
such as:
10436e
10436e
  --mac 00:0c:29:e6:3d:9d:ip:192.168.0.89,192.168.0.1,24,192.168.0.254
10436e
10436e
approximately turns into this script:
10436e
10436e
  # Wait for the netkvm (virtio-net) driver to become active.
10436e
  $adapters = @()
10436e
  While (-Not $adapters) {
10436e
      Start-Sleep -Seconds 5
10436e
      $adapters = Get-NetAdapter -Physical |
10436e
                     Where DriverFileName -eq "netkvm.sys"
10436e
  }
10436e
  $mac_address = '00-0c-29-e6-3d-9d'
10436e
  $ifindex = (Get-NetAdapter -Physical |
10436e
                 Where MacAddress -eq $mac_address).ifIndex
10436e
  if ($ifindex) {
10436e
      New-NetIPAddress -InterfaceIndex $ifindex
10436e
                       -IPAddress '192.168.0.89'
10436e
                       -DefaultGateway '192.168.0.1'
10436e
                       -PrefixLength 24
10436e
      Set-DnsClientServerAddress -InterfaceIndex $ifindex
10436e
                       -ServerAddresses ('192.168.0.254')
10436e
  }
10436e
10436e
Thanks: Brett Thurber for diagnosing the problem and suggesting paths
10436e
towards a fix.
10436e
10436e
(cherry picked from commit dfd9fac7435cf2f9293961b6cc1fe316af4feebc)
10436e
---
10436e
 v2v/cmdline.ml         | 41 ++++++++++++++++++-----
10436e
 v2v/cmdline.mli        |  1 +
10436e
 v2v/convert_linux.ml   |  2 +-
10436e
 v2v/convert_windows.ml | 76 +++++++++++++++++++++++++++++++++++++++++-
10436e
 v2v/modules_list.ml    |  2 +-
10436e
 v2v/modules_list.mli   |  2 +-
10436e
 v2v/types.ml           |  8 +++++
10436e
 v2v/types.mli          |  9 +++++
10436e
 v2v/v2v.ml             |  7 ++--
10436e
 v2v/virt-v2v.pod       | 18 ++++++++++
10436e
 10 files changed, 150 insertions(+), 16 deletions(-)
10436e
10436e
diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml
10436e
index 4d390f249..686631271 100644
10436e
--- a/v2v/cmdline.ml
10436e
+++ b/v2v/cmdline.ml
10436e
@@ -40,11 +40,12 @@ type cmdline = {
10436e
   print_estimate : bool;
10436e
   print_source : bool;
10436e
   root_choice : root_choice;
10436e
+  static_ips : static_ip list;
10436e
   ks : Tools_utils.key_store;
10436e
 }
10436e
 
10436e
 (* Matches --mac command line parameters. *)
10436e
-let mac_re = PCRE.compile ~anchored:true "([[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}):(network|bridge):(.*)"
10436e
+let mac_re = PCRE.compile ~anchored:true "([[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}):(network|bridge|ip):(.*)"
10436e
 
10436e
 let parse_cmdline () =
10436e
   let compressed = ref false in
10436e
@@ -97,6 +98,7 @@ let parse_cmdline () =
10436e
   in
10436e
 
10436e
   let network_map = Networks.create () in
10436e
+  let static_ips = ref [] in
10436e
   let add_network str =
10436e
     match String.split ":" str with
10436e
     | "", "" ->
10436e
@@ -119,11 +121,30 @@ let parse_cmdline () =
10436e
     if not (PCRE.matches mac_re str) then
10436e
       error (f_"cannot parse --mac \"%s\" parameter") str;
10436e
     let mac = PCRE.sub 1 and out = PCRE.sub 3 in
10436e
-    let vnet_type =
10436e
-      match PCRE.sub 2 with
10436e
-      | "network" -> Network | "bridge" -> Bridge
10436e
-      | _ -> assert false in
10436e
-    Networks.add_mac network_map mac vnet_type out
10436e
+    match PCRE.sub 2 with
10436e
+    | "network" ->
10436e
+       Networks.add_mac network_map mac Network out
10436e
+    | "bridge" ->
10436e
+       Networks.add_mac network_map mac Bridge out
10436e
+    | "ip" ->
10436e
+       let add if_mac_addr if_ip_address if_default_gateway
10436e
+               if_prefix_length if_nameservers =
10436e
+         List.push_back static_ips
10436e
+                        { if_mac_addr; if_ip_address; if_default_gateway;
10436e
+                          if_prefix_length; if_nameservers }
10436e
+       in
10436e
+       (match String.nsplit "," out with
10436e
+        | [] ->
10436e
+           error (f_"invalid --mac ip option")
10436e
+        | [ip] -> add mac ip None None []
10436e
+        | [ip; gw] -> add mac ip (Some gw) None []
10436e
+        | ip :: gw :: len :: nameservers ->
10436e
+           let len =
10436e
+             try int_of_string len with
10436e
+             | Failure _ -> error (f_"cannot parse --mac ip prefix length field as an integer: %s") len in
10436e
+           add mac ip (Some gw) (Some len) nameservers
10436e
+       );
10436e
+    | _ -> assert false
10436e
   in
10436e
 
10436e
   let no_trim_warning _ =
10436e
@@ -211,8 +232,8 @@ let parse_cmdline () =
10436e
                                     s_"Input transport";
10436e
     [ L"in-place" ], Getopt.Set in_place,
10436e
                                     s_"Only tune the guest in the input VM";
10436e
-    [ L"mac" ],      Getopt.String ("mac:network|bridge:out", add_mac),
10436e
-                                    s_"Map NIC to network or bridge";
10436e
+    [ L"mac" ],      Getopt.String ("mac:network|bridge|ip:out", add_mac),
10436e
+                                    s_"Map NIC to network or bridge or assign static IP";
10436e
     [ S 'n'; L"network" ], Getopt.String ("in:out", add_network),
10436e
                                     s_"Map network ‘in’ to ‘out’";
10436e
     [ L"no-copy" ],  Getopt.Clear do_copy,
10436e
@@ -335,6 +356,7 @@ read the man page virt-v2v(1).
10436e
   let print_source = !print_source in
10436e
   let qemu_boot = !qemu_boot in
10436e
   let root_choice = !root_choice in
10436e
+  let static_ips = !static_ips in
10436e
 
10436e
   (* No arguments and machine-readable mode?  Print out some facts
10436e
    * about what this binary supports.
10436e
@@ -351,6 +373,7 @@ read the man page virt-v2v(1).
10436e
     pr "in-place\n";
10436e
     pr "io/oo\n";
10436e
     pr "mac-option\n";
10436e
+    pr "mac-ip-option\n";
10436e
     List.iter (pr "input:%s\n") (Modules_list.input_modules ());
10436e
     List.iter (pr "output:%s\n") (Modules_list.output_modules ());
10436e
     List.iter (pr "convert:%s\n") (Modules_list.convert_modules ());
10436e
@@ -685,7 +708,7 @@ read the man page virt-v2v(1).
10436e
   {
10436e
     compressed; debug_overlays; do_copy; in_place; network_map;
10436e
     output_alloc; output_format; output_name;
10436e
-    print_estimate; print_source; root_choice;
10436e
+    print_estimate; print_source; root_choice; static_ips;
10436e
     ks = opthandle.ks;
10436e
   },
10436e
   input, output
10436e
diff --git a/v2v/cmdline.mli b/v2v/cmdline.mli
10436e
index 78601e191..a009e9888 100644
10436e
--- a/v2v/cmdline.mli
10436e
+++ b/v2v/cmdline.mli
10436e
@@ -30,6 +30,7 @@ type cmdline = {
10436e
   print_estimate : bool;
10436e
   print_source : bool;
10436e
   root_choice : Types.root_choice;
10436e
+  static_ips : Types.static_ip list;
10436e
   ks : Tools_utils.key_store;
10436e
 }
10436e
 
10436e
diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml
10436e
index f9e811c8d..1ada36115 100644
10436e
--- a/v2v/convert_linux.ml
10436e
+++ b/v2v/convert_linux.ml
10436e
@@ -34,7 +34,7 @@ open Linux_kernels
10436e
 module G = Guestfs
10436e
 
10436e
 (* The conversion function. *)
10436e
-let convert (g : G.guestfs) inspect source output rcaps =
10436e
+let convert (g : G.guestfs) inspect source output rcaps _ =
10436e
   (*----------------------------------------------------------------------*)
10436e
   (* Inspect the guest first.  We already did some basic inspection in
10436e
    * the common v2v.ml code, but that has to deal with generic guests
10436e
diff --git a/v2v/convert_windows.ml b/v2v/convert_windows.ml
10436e
index 1db3c0ea6..75e609d61 100644
10436e
--- a/v2v/convert_windows.ml
10436e
+++ b/v2v/convert_windows.ml
10436e
@@ -38,7 +38,7 @@ module G = Guestfs
10436e
  * time the Windows VM is booted on KVM.
10436e
  *)
10436e
 
10436e
-let convert (g : G.guestfs) inspect source output rcaps =
10436e
+let convert (g : G.guestfs) inspect source output rcaps static_ips =
10436e
   (*----------------------------------------------------------------------*)
10436e
   (* Inspect the Windows guest. *)
10436e
 
10436e
@@ -228,6 +228,8 @@ let convert (g : G.guestfs) inspect source output rcaps =
10436e
     Registry.with_hive_write g inspect.i_windows_software_hive
10436e
                              update_software_hive;
10436e
 
10436e
+    configure_network_interfaces net_driver;
10436e
+
10436e
     fix_ntfs_heads ();
10436e
 
10436e
     fix_win_esp ();
10436e
@@ -603,6 +605,78 @@ if errorlevel 3010 exit /b 0
10436e
     | None ->
10436e
        warning (f_"could not find registry key HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion")
10436e
 
10436e
+  and configure_network_interfaces net_driver =
10436e
+    (* If we were asked to force network interfaces to have particular
10436e
+     * static IP addresses then it is done here by installing a
10436e
+     * Powershell script which runs at boot.
10436e
+     *)
10436e
+    if static_ips <> [] then (
10436e
+      let psh_filename = "v2vnetcf.ps1" in
10436e
+      let psh = ref [] in
10436e
+      let add = List.push_back psh in
10436e
+
10436e
+      add "# Uncomment this line for lots of debug output.";
10436e
+      add "# Set-PSDebug -Trace 1";
10436e
+      add "";
10436e
+
10436e
+      (* If virtio-net was added to the registry, we must wait for
10436e
+       * it to be installed at runtime.
10436e
+       *)
10436e
+      if net_driver = Virtio_net then (
10436e
+        add "# Wait for the netkvm (virtio-net) driver to become active.";
10436e
+        add "$adapters = @()";
10436e
+        add "While (-Not $adapters) {";
10436e
+        add "    Start-Sleep -Seconds 5";
10436e
+        add "    $adapters = Get-NetAdapter -Physical | Where DriverFileName -eq \"netkvm.sys\"";
10436e
+        add "    Write-Host \"adapters = '$adapters'\"";
10436e
+        add "}";
10436e
+        add ""
10436e
+      );
10436e
+
10436e
+      List.iter (
10436e
+        fun { if_mac_addr; if_ip_address; if_default_gateway;
10436e
+              if_prefix_length; if_nameservers } ->
10436e
+          add (sprintf "$mac_address = '%s'"
10436e
+                       (String.replace if_mac_addr ":" "-"));
10436e
+          add "$ifindex = (Get-NetAdapter -Physical | Where MacAddress -eq $mac_address).ifIndex";
10436e
+          add "if ($ifindex) {";
10436e
+
10436e
+          add "    Write-Host \"setting IP address of adapter at $ifindex\"";
10436e
+
10436e
+          (* New-NetIPAddress command *)
10436e
+          let args = ref [] in
10436e
+          List.push_back args "-InterfaceIndex";
10436e
+          List.push_back args "$ifindex";
10436e
+          List.push_back args "-IPAddress";
10436e
+          List.push_back args (sprintf "'%s'" if_ip_address);
10436e
+          (match if_default_gateway with
10436e
+           | None -> ()
10436e
+           | Some gw ->
10436e
+              List.push_back args "-DefaultGateway";
10436e
+              List.push_back args (sprintf "'%s'" gw)
10436e
+          );
10436e
+          (match if_prefix_length with
10436e
+           | None -> ()
10436e
+           | Some len ->
10436e
+              List.push_back args "-PrefixLength";
10436e
+              List.push_back args (string_of_int len)
10436e
+          );
10436e
+          let cmd1 = "New-NetIPAddress " ^ String.concat " " !args in
10436e
+          add ("    " ^ cmd1);
10436e
+
10436e
+          (* Set-DnsClientServerAddress command *)
10436e
+          if if_nameservers <> [] then (
10436e
+            add (sprintf "    Set-DnsClientServerAddress -InterfaceIndex $ifindex -ServerAddresses (%s)"
10436e
+                         (String.concat "," (List.map (sprintf "'%s'") if_nameservers)))
10436e
+          );
10436e
+          add "}";
10436e
+          add ""
10436e
+      ) static_ips;
10436e
+
10436e
+      (* Install the Powershell script to run at firstboot. *)
10436e
+      Windows.install_firstboot_powershell g inspect psh_filename !psh
10436e
+    ) (* static_ips <> [] *)
10436e
+
10436e
   and fix_ntfs_heads () =
10436e
     (* NTFS hardcodes the number of heads on the drive which created
10436e
        it in the filesystem header. Modern versions of Windows
10436e
diff --git a/v2v/modules_list.ml b/v2v/modules_list.ml
10436e
index a0a74aaf2..76b3def5d 100644
10436e
--- a/v2v/modules_list.ml
10436e
+++ b/v2v/modules_list.ml
10436e
@@ -38,7 +38,7 @@ type inspection_fn = Types.inspect -> bool
10436e
 
10436e
 type conversion_fn =
10436e
   Guestfs.guestfs -> Types.inspect -> Types.source -> Types.output_settings ->
10436e
-  Types.requested_guestcaps -> Types.guestcaps
10436e
+  Types.requested_guestcaps -> Types.static_ip list -> Types.guestcaps
10436e
 
10436e
 let convert_modules = ref []
10436e
 
10436e
diff --git a/v2v/modules_list.mli b/v2v/modules_list.mli
10436e
index 3e80d3e23..ad2024755 100644
10436e
--- a/v2v/modules_list.mli
10436e
+++ b/v2v/modules_list.mli
10436e
@@ -34,7 +34,7 @@ type inspection_fn = Types.inspect -> bool
10436e
 
10436e
 type conversion_fn =
10436e
   Guestfs.guestfs -> Types.inspect -> Types.source -> Types.output_settings ->
10436e
-  Types.requested_guestcaps -> Types.guestcaps
10436e
+  Types.requested_guestcaps -> Types.static_ip list -> Types.guestcaps
10436e
 
10436e
 val register_convert_module : inspection_fn -> string -> conversion_fn -> unit
10436e
 (** [register_convert_module inspect_fn name fn] registers a
10436e
diff --git a/v2v/types.ml b/v2v/types.ml
10436e
index 714b30014..4ba6117fd 100644
10436e
--- a/v2v/types.ml
10436e
+++ b/v2v/types.ml
10436e
@@ -506,6 +506,14 @@ type root_choice = AskRoot | SingleRoot | FirstRoot | RootDev of string
10436e
 
10436e
 type output_allocation = Sparse | Preallocated
10436e
 
10436e
+type static_ip = {
10436e
+  if_mac_addr : string;
10436e
+  if_ip_address : string;
10436e
+  if_default_gateway : string option;
10436e
+  if_prefix_length : int option;
10436e
+  if_nameservers : string list;
10436e
+}
10436e
+
10436e
 class virtual input = object
10436e
   method precheck () = ()
10436e
   method virtual as_options : string
10436e
diff --git a/v2v/types.mli b/v2v/types.mli
10436e
index f595ab0ef..528d77965 100644
10436e
--- a/v2v/types.mli
10436e
+++ b/v2v/types.mli
10436e
@@ -361,6 +361,15 @@ type root_choice = AskRoot | SingleRoot | FirstRoot | RootDev of string
10436e
 type output_allocation = Sparse | Preallocated
10436e
 (** Type of [-oa] (output allocation) option. *)
10436e
 
10436e
+type static_ip = {
10436e
+  if_mac_addr : string;
10436e
+  if_ip_address : string;
10436e
+  if_default_gateway : string option;
10436e
+  if_prefix_length : int option;
10436e
+  if_nameservers : string list;
10436e
+}
10436e
+(** [--mac ..:ip:..] option. *)
10436e
+
10436e
 (** {2 Input object}
10436e
 
10436e
     This is subclassed for the various input [-i] options.
10436e
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
10436e
index 63e809030..d7a868659 100644
10436e
--- a/v2v/v2v.ml
10436e
+++ b/v2v/v2v.ml
10436e
@@ -133,7 +133,7 @@ let rec main () =
10436e
       | In_place ->
10436e
          rcaps_from_source source in
10436e
 
10436e
-    do_convert g inspect source output rcaps in
10436e
+    do_convert g inspect source output rcaps cmdline.static_ips in
10436e
 
10436e
   g#umount_all ();
10436e
 
10436e
@@ -556,7 +556,7 @@ and estimate_target_size mpstats overlays =
10436e
   )
10436e
 
10436e
 (* Conversion. *)
10436e
-and do_convert g inspect source output rcaps =
10436e
+and do_convert g inspect source output rcaps interfaces =
10436e
   (match inspect.i_product_name with
10436e
   | "unknown" ->
10436e
     message (f_"Converting the guest to run on KVM")
10436e
@@ -572,7 +572,8 @@ and do_convert g inspect source output rcaps =
10436e
   debug "picked conversion module %s" conversion_name;
10436e
   debug "requested caps: %s" (string_of_requested_guestcaps rcaps);
10436e
   let guestcaps =
10436e
-    convert g inspect source (output :> Types.output_settings) rcaps in
10436e
+    convert g inspect source (output :> Types.output_settings) rcaps
10436e
+            interfaces in
10436e
   debug "%s" (string_of_guestcaps guestcaps);
10436e
 
10436e
   (* Did we manage to install virtio drivers? *)
10436e
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
10436e
index 9a555c3be..0642d158f 100644
10436e
--- a/v2v/virt-v2v.pod
10436e
+++ b/v2v/virt-v2v.pod
10436e
@@ -368,6 +368,24 @@ Map source NIC MAC address to a network or bridge.
10436e
 
10436e
 See L</Networks and bridges> below.
10436e
 
10436e
+=item B<--mac> aa:bb:cc:dd:ee:ffB<:ip:>ipaddr[,gw[,len[,ns,ns,...]]]
10436e
+
10436e
+Force a particular interface (controlled by its MAC address) to have a
10436e
+static IP address after boot.
10436e
+
10436e
+The fields in the parameter are: C<ipaddr> is the IP address.  C<gw>
10436e
+is the optional gateway IP address.  C<len> is the subnet mask length
10436e
+(an integer).  The final parameters are zero or more nameserver IP
10436e
+addresses.
10436e
+
10436e
+This option can be supplied zero or more times.
10436e
+
10436e
+You only need to use this option for certain broken guests such as
10436e
+Windows which are unable to preserve MAC to static IP address mappings
10436e
+automatically.  You don't need to use it if Windows is using DHCP.  It
10436e
+is currently ignored for Linux guests since they do not have this
10436e
+problem.
10436e
+
10436e
 =item B<--machine-readable>
10436e
 
10436e
 =item B<--machine-readable>=format
10436e
-- 
498672
2.18.4
10436e