[Pkg-libvirt-commits] [libguestfs] 12/14: v2v: Add partial support for converting Windows guests.
Hilko Bengen
bengen at moszumanska.debian.org
Sat Aug 30 08:29:26 UTC 2014
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to annotated tag upstream/1.27.21
in repository libguestfs.
commit f296d34f3b30c82309b22b54bf5a2c133992bd93
Author: Richard W.M. Jones <rjones at redhat.com>
Date: Mon Jun 30 18:00:01 2014 +0100
v2v: Add partial support for converting Windows guests.
This is not yet complete.
---
mllib/config.ml.in | 3 +
run.in | 6 +
v2v/Makefile.am | 4 +
v2v/README | 1 -
v2v/convert_windows.ml | 339 +++++++++++++++++++++++++++++++++++++++++++++++-
v2v/convert_windows.mli | 2 +-
v2v/v2v.ml | 2 +-
v2v/virt-v2v.pod | 62 ++++++++-
8 files changed, 410 insertions(+), 9 deletions(-)
diff --git a/mllib/config.ml.in b/mllib/config.ml.in
index d18bf7b..b1c4a49 100644
--- a/mllib/config.ml.in
+++ b/mllib/config.ml.in
@@ -1,4 +1,5 @@
(* configuration for mllib.
+ * @configure_input@
* Copyright (C) 2013 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -18,3 +19,5 @@
let package_name = "@PACKAGE_NAME@"
let package_version = "@PACKAGE_VERSION@"
+let prefix = "@prefix@"
+let datadir = prefix ^ "/share"
diff --git a/run.in b/run.in
index f09b8fd..1fcd795 100755
--- a/run.in
+++ b/run.in
@@ -92,6 +92,12 @@ if [ -z "$XDG_CONFIG_DIRS" ]; then
export XDG_CONFIG_DIRS
fi
+# Data directory used by virt-v2v.
+if [ -z "$VIRT_TOOLS_DATA_DIR" ]; then
+ VIRT_TOOLS_DATA_DIR="$s/v2v"
+ export VIRT_TOOLS_DATA_DIR
+fi
+
# For Perl.
if [ -z "$PERL5LIB" ]; then
PERL5LIB="$b/perl/blib/lib:$b/perl/blib/arch"
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index b353fb4..a1dec52 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -112,6 +112,10 @@ virt_v2v_LINK = \
.ml.cmx:
$(OCAMLFIND) ocamlopt $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
+# Data directory.
+
+virttoolsdatadir = $(datadir)/virt-tools
+
# Manual pages and HTML files for the website.
man_MANS = virt-v2v.1
diff --git a/v2v/README b/v2v/README
index a4248ba..b12b3b1 100644
--- a/v2v/README
+++ b/v2v/README
@@ -1,7 +1,6 @@
Missing features compared to Perl version:
- user-custom in virt-v2v.conf to install custom packages (virt-customize?)
- - Windows support
- Fix configure_kernel on SUSE (see Mike Latimer's email)
- RHEV-M metadata
- testing
diff --git a/v2v/convert_windows.ml b/v2v/convert_windows.ml
index e5d1ea8..d8df3c8 100644
--- a/v2v/convert_windows.ml
+++ b/v2v/convert_windows.ml
@@ -16,7 +16,340 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
-(* Convert Windows guests. *)
+open Printf
-let convert verbose g inspect =
- assert false
+open Common_gettext.Gettext
+open Common_utils
+
+open Utils
+open Types
+
+module G = Guestfs
+
+(* Convert Windows guests.
+ *
+ * This only does a "pre-conversion", the steps needed to get the
+ * Windows guest to boot on KVM. Unlike the [Convert_linux] module,
+ * this is not a full conversion. Instead it just installs the
+ * [viostor] (Windows virtio block) driver, so that the Windows guest
+ * will be able to boot on the target. A [RunOnce] script is also
+ * added to the VM which does all the rest of the conversion the first
+ * time the Windows VM is booted on KVM.
+ *)
+
+type ('a, 'b) maybe = Either of 'a | Or of 'b
+
+let convert verbose (g : G.guestfs) inspect source =
+ (* Get the data directory. *)
+ let virt_tools_data_dir =
+ try Sys.getenv "VIRT_TOOLS_DATA_DIR"
+ with Not_found -> Config.datadir // "virt-tools" in
+ let virtio_win_dir = "/usr/share/virtio-win" in
+
+ (* Since this is a Windows guest, RHSrvAny must exist. (Check also
+ * that it's not a dangling symlink but a real file).
+ *)
+ let rhsrvany_exe = virt_tools_data_dir // "rhsrvany.exe" in
+ (try
+ let chan = open_in rhsrvany_exe in
+ close_in chan
+ with
+ Sys_error msg ->
+ error (f_"'%s' is missing. This file is required in order to do Windows conversions. You can get it by building rhsrvany (https://github.com/rwmjones/rhsrvany). Original error: %s")
+ rhsrvany_exe msg
+ );
+
+ let systemroot = g#inspect_get_windows_systemroot inspect.i_root in
+
+ (* This is a wrapper that handles opening and closing the hive
+ * properly around a function [f]. If [~write] is [true] then the
+ * hive is opened for writing and committed at the end if the
+ * function returned without error.
+ *)
+ let rec with_hive name ~write f =
+ let filename = sprintf "%s/system32/config/%s" systemroot name in
+ let filename = g#case_sensitive_path filename in
+ g#hivex_open ~write ~verbose ~debug:verbose filename;
+ let r =
+ try
+ let root = g#hivex_root () in
+ let ret = f root in
+ if write then g#hivex_commit None;
+ Either ret
+ with exn ->
+ Or exn in
+ g#hivex_close ();
+ match r with Either ret -> ret | Or exn -> raise exn
+
+ (* Find the given node in the current hive, relative to the starting
+ * point. Raises [Not_found] if the node is not found.
+ *)
+ and get_node node = function
+ | [] -> node
+ | x :: xs ->
+ let node = g#hivex_node_get_child node x in
+ if node = 0L then raise Not_found;
+ get_node node xs
+
+ (* Take a 7 bit ASCII string and encode it as UTF16LE. *)
+ and encode_utf16le str =
+ let len = String.length str in
+ let copy = String.make (len*2) '\000' in
+ for i = 0 to len-1 do
+ String.unsafe_set copy (i*2) (String.unsafe_get str i)
+ done;
+ copy
+
+ (* Take a UTF16LE string and decode it to UTF-8. Actually this
+ * fails if the string is not 7 bit ASCII. XXX Use iconv here.
+ *)
+ and decode_utf16le str =
+ let len = String.length str in
+ if len mod 2 <> 0 then
+ error (f_"decode_utf16le: Windows string does not appear to be in UTF16-LE encoding. This could be a bug in virt-v2v.");
+ let copy = String.create (len/2) in
+ for i = 0 to (len/2)-1 do
+ let cl = String.unsafe_get str (i*2) in
+ let ch = String.unsafe_get str ((i*2)+1) in
+ if ch != '\000' || Char.code cl >= 127 then
+ error (f_"decode_utf16le: Windows UTF16-LE string contains non-7-bit characters. This is a bug in virt-v2v, please report it.");
+ String.unsafe_set copy i cl
+ done;
+ copy
+ in
+
+ (*----------------------------------------------------------------------*)
+ (* Inspect the Windows guest. *)
+
+ let find_xenpv_uninst root =
+ try
+ let xenpvreg = "Red Hat Paravirtualized Xen Drivers for Windows(R)" in
+ let node =
+ get_node root
+ ["Microsoft"; "Windows"; "CurrentVersion"; "Uninstall"; xenpvreg] in
+ let uninstkey = "UninstallString" in
+ let valueh = g#hivex_node_get_value node uninstkey in
+ if valueh = 0L then (
+ warning ~prog (f_"cannot uninstall Xen PV drivers: registry key 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s' does not contain an '%s' key")
+ xenpvreg uninstkey;
+ raise Not_found
+ );
+ let data = g#hivex_value_value valueh in
+ let data = decode_utf16le data in
+
+ (* The uninstall program will be uninst.exe. This is a wrapper
+ * around _uninst.exe which prompts the user. As we don't want
+ * the user to be prompted, we run _uninst.exe explicitly.
+ *)
+ let len = String.length data in
+ let data =
+ if len >= 8 &&
+ String.lowercase (String.sub data (len-8) 8) = "uninst.exe" then
+ (String.sub data 0 (len-8)) ^ "_uninst.exe"
+ else
+ data in
+
+ Some data
+ with
+ Not_found -> None
+ in
+
+ (* Open the software hive (readonly) and find the Xen PV uninstaller,
+ * if it exists.
+ *)
+ let xenpv_uninst = with_hive "software" ~write:false find_xenpv_uninst in
+
+ (*----------------------------------------------------------------------*)
+ (* Perform the conversion of the Windows guest. *)
+
+ let rec update_system_hive root =
+ (* Update the SYSTEM hive. When this function is called the hive has
+ * already been opened as a hivex handle inside guestfs.
+ *)
+ (* Find the 'Current' ControlSet. *)
+ let current_cs =
+ let select = g#hivex_node_get_child root "Select" in
+ let valueh = g#hivex_node_get_value select "Current" in
+ let value = int_of_le32 (g#hivex_value_value valueh) in
+ sprintf "ControlSet%03Ld" value in
+
+ if verbose then printf "current ControlSet is %s\n%!" current_cs;
+
+ let firstboot = configure_firstboot root current_cs in
+ configure_rhev_apt root firstboot;
+ (match xenpv_uninst with
+ | None -> () (* nothing to be uninstalled *)
+ | Some uninst -> unconfigure_xenpv root firstboot uninst
+ );
+ close_firstboot root firstboot;
+ disable_services root current_cs;
+ let block_net_drivers = install_virtio_drivers root current_cs in
+
+ block_net_drivers
+
+ and configure_firstboot root current_cs =
+ ignore virt_tools_data_dir;
+ ()
+
+
+
+
+ and configure_rhev_apt root firstboot =
+ ()
+
+
+
+ and unconfigure_xenpv root firstboot uninst_exe =
+ ()
+
+
+
+
+
+ and close_firstboot root firstboot =
+ ()
+
+
+
+
+
+ and disable_services root current_cs =
+ ()
+
+
+
+
+
+ and install_virtio_drivers root current_cs =
+ ignore virtio_win_dir;
+ ("XXX", "XXX")
+
+
+
+
+
+ and update_software_hive root =
+ (* Update the SOFTWARE hive. When this function is called the
+ * hive has already been opened as a hivex handle inside
+ * guestfs.
+ *)
+
+ (* Find the node \Microsoft\Windows\CurrentVersion. If the node
+ * has a key called DevicePath then append the virtio driver
+ * path to this key.
+ *)
+ try
+ let node = get_node root ["Microsoft"; "Windows"; "CurrentVersion"] in
+ let append = encode_utf16le ";%SystemRoot%\\Drivers\\VirtIO" in
+ let values = Array.to_list (g#hivex_node_values node) in
+ let rec loop = function
+ | [] -> () (* DevicePath not found -- ignore this case *)
+ | { G.hivex_value_h = valueh } :: values ->
+ let key = g#hivex_value_key valueh in
+ if key <> "DevicePath" then
+ loop values
+ else (
+ let data = g#hivex_value_value valueh in
+ let len = String.length data in
+ let t = g#hivex_value_type valueh in
+
+ (* Only add the appended path if it doesn't exist already. *)
+ if string_find data append = -1 then (
+ (* Remove the explicit [\0\0] at the end of the string.
+ * This is the UTF-16LE NUL-terminator.
+ *)
+ let data =
+ if len >= 2 && String.sub data (len-2) 2 = "\000\000" then
+ String.sub data 0 (len-2)
+ else
+ data in
+
+ (* Append the path and the explicit NUL. *)
+ let data = data ^ append ^ "\000\000" in
+
+ g#hivex_node_set_value node key t data
+ )
+ )
+ in
+ loop values
+ with Not_found ->
+ warning ~prog (f_"could not find registry key HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion")
+
+ and fix_ntfs_heads () =
+ (* NTFS hardcodes the number of heads on the drive which created
+ it in the filesystem header. Modern versions of Windows
+ sensibly ignore it, but both Windows XP and Windows 2000
+ require it to be correct in order to boot from the drive. If it
+ isn't you get:
+
+ 'A disk read error occurred. Press Ctrl+Alt+Del to restart'
+
+ QEMU has some code in block.c:guess_disk_lchs() which on the face
+ of it appears to infer the drive geometry from the MBR if it's
+ valid. However, my tests have shown that a Windows XP guest
+ hosted on both RHEL 5 and F14 requires the heads field in NTFS to
+ be the following, based solely on drive size:
+
+ Range Heads
+ size < 2114445312 0x40
+ 2114445312 <= size < 4228374780 0x80
+ 4228374780 <= size 0xFF
+
+ I have not tested drive sizes less than 1G, which require fewer
+ heads, as this limitation applies only to the boot device and it
+ is not possible to install XP on a drive this size.
+
+ The following page has good information on the layout of NTFS in
+ Windows XP/2000:
+
+ http://mirror.href.com/thestarman/asm/mbr/NTFSBR.htm
+
+ Technet has this:
+
+ http://technet.microsoft.com/en-us/library/cc781134(WS.10).aspx#w2k3tr_ntfs_how_dhao
+
+ however, as this is specific to Windows 2003 it lists location
+ 0x1A as unused.
+ *)
+ let rootpart = inspect.i_root in
+
+ (* Check that the root device contains NTFS magic. *)
+ let magic = g#pread_device rootpart 8 3L in
+ if magic = "NTFS " then (
+ (* Get the size of the whole disk containing the root partition. *)
+ let rootdev = g#part_to_dev rootpart in (* eg. /dev/sda *)
+ let size = g#blockdev_getsize64 rootdev in
+
+ let heads = (* refer to the table above *)
+ if size < 2114445312L then 0x40
+ else if size < 4228374780L then 0x80
+ else 0xff in
+
+ (* Update NTFS's idea of the number of heads. This is an
+ * unsigned 16 bit little-endian integer, offset 0x1a from the
+ * beginning of the partition.
+ *)
+ let bytes = String.create 2 in
+ bytes.[0] <- Char.chr heads;
+ bytes.[1] <- '\000';
+ ignore (g#pwrite_device rootpart bytes 0x1a_L)
+ )
+ in
+
+ (* Open the system hive and update it. *)
+ let block_driver, net_driver =
+ with_hive "system" ~write:true update_system_hive in
+
+ (* Open the software hive and update it. *)
+ with_hive "software" ~write:true update_software_hive;
+
+ fix_ntfs_heads ();
+
+ (* Return guest capabilities. *)
+ let guestcaps = {
+ gcaps_block_bus = block_driver;
+ gcaps_net_bus = net_driver;
+ (* XXX display *)
+ } in
+
+ guestcaps
diff --git a/v2v/convert_windows.mli b/v2v/convert_windows.mli
index d1e60fe..1ea07c4 100644
--- a/v2v/convert_windows.mli
+++ b/v2v/convert_windows.mli
@@ -16,4 +16,4 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
-val convert : bool -> Guestfs.guestfs -> Types.inspect -> Types.guestcaps
+val convert : bool -> Guestfs.guestfs -> Types.inspect -> Types.source -> Types.guestcaps
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
index 292bb43..417b692 100644
--- a/v2v/v2v.ml
+++ b/v2v/v2v.ml
@@ -131,7 +131,7 @@ let rec main () =
error (f_"virt-v2v is unable to convert this guest type (linux/distro=%s)") distro
);
- | "windows" -> Convert_windows.convert verbose g inspect
+ | "windows" -> Convert_windows.convert verbose g inspect source
| typ ->
error (f_"virt-v2v is unable to convert this guest type (type=%s)") typ in
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
index c88df5d..48fa870 100644
--- a/v2v/virt-v2v.pod
+++ b/v2v/virt-v2v.pod
@@ -240,14 +240,17 @@ a kernel from the table below:
SLES 11+ i586: install 'kernel-pae'
x86-64: install 'kernel-default'
+ Windows (Does not apply, as there is no Xen PV Windows kernel)
+
=head1 ENABLING VIRTIO
"Virtio" is the name for a set of drivers which make disk (block
device), network and other guest operations work much faster on KVM.
Older versions of virt-v2v could install these drivers for certain
-guests. This version of virt-v2v does I<not> attempt to install these
-drivers, but will warn you if they are not installed already.
+Linux guests. This version of virt-v2v does I<not> attempt to install
+new Linux kernels or drivers, but will warn you if they are not
+installed already.
In order to enable virtio, and hence improve performance of the guest
after conversion, you should ensure that the B<minimum> versions of
@@ -274,6 +277,9 @@ below.
OpenSUSE 10 kernel >= 2.6.25.5-1.1
+ Windows Drivers are installed from /usr/share/virtio-win
+ if present
+
=head1 MACHINE READABLE OUTPUT
The I<--machine-readable> option can be used to make the output more
@@ -323,11 +329,25 @@ code if there was a fatal error.
Virt-v2v E<le> 0.9.1 did not support the I<--machine-readable>
option at all. The option was added when virt-v2v was rewritten in 2014.
+=head1 FILES
+
+=over 4
+
+=item C</usr/share/virtio-win>
+
+(Optional)
+
+If this directory is present, then virtio drivers for Windows guests
+will be found from this directory and installed in the guest during
+conversion.
+
+=back
+
=head1 ENVIRONMENT VARIABLES
=over 4
-=item TMPDIR
+=item C<TMPDIR>
Location of the temporary directory used for the potentially large
temporary overlay file.
@@ -346,6 +366,42 @@ size of the tmpfs mountpoint, eg:
mount -o remount,size=10G /tmp
+=item C<VIRT_TOOLS_DATA_DIR>
+
+This can point to the directory containing data files used for Windows
+conversion.
+
+Normally you do not need to set this. If not set, a compiled-in
+default will be used (something like C</usr/share/virt-tools>).
+
+This directory may contain the following files:
+
+=over 4
+
+=item C<rhsrvany.exe>
+
+(Required when doing conversions of Windows guests)
+
+This is the RHSrvAny Windows binary, used to install a "firstboot"
+script in the guest during conversion of Windows guests.
+
+See also: C<https://github.com/rwmjones/rhsrvany>
+
+=item C<rhev-apt.exe>
+
+(Optional)
+
+The RHEV Application Provisioning Tool (RHEV APT). If this file is
+present, then RHEV APT will be installed in the Windows guest during
+conversion. This tool is a guest agent which ensures that the virtio
+drivers remain up to date when the guest is running on Red Hat
+Enterprise Virtualization (RHEV).
+
+This file comes from Red Hat Enterprise Virtualization (RHEV), and is
+not distributed with virt-v2v.
+
+=back
+
=back
For other environment variables, see L<guestfs(3)/ENVIRONMENT VARIABLES>.
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-libvirt/libguestfs.git
More information about the Pkg-libvirt-commits
mailing list