[Pkg-libvirt-commits] [libguestfs] 200/266: v2v: esx: Add / fix ability to import guests from vCenter Server.
Hilko Bengen
bengen at moszumanska.debian.org
Fri Oct 3 14:42:01 UTC 2014
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to annotated tag debian/1%1.27.35-1
in repository libguestfs.
commit 155a6f0482e5eeb6a763d4d31354edc5f48a1f43
Author: Richard W.M. Jones <rjones at redhat.com>
Date: Tue Aug 26 19:34:37 2014 +0100
v2v: esx: Add / fix ability to import guests from vCenter Server.
See additional information in the manual page for details.
---
README | 3 +-
v2v/input_libvirt.ml | 12 +++-
v2v/lib_esx.ml | 186 ++++++++++++++++++++++++++++++++++++++++++++-------
v2v/lib_esx.mli | 2 +-
v2v/utils.ml | 13 ++++
v2v/virt-v2v.pod | 90 +++++++++++++++++++++++--
6 files changed, 272 insertions(+), 34 deletions(-)
diff --git a/README b/README
index aa03ee8..b88a67d 100644
--- a/README
+++ b/README
@@ -52,7 +52,8 @@ The full requirements are described below.
+--------------+-------------+---+-----------------------------------------+
| qemu | 1.2.0 | R | 1.1 may work, but has broken virtio-scsi|
+--------------+-------------+---+-----------------------------------------+
-| qemu-img | | R | |
+| qemu-img | | R | >= 2.2.0 is required for virt-v2v but |
+| | | | optional elsewhere |
+--------------+-------------+---+-----------------------------------------+
| kernel | 2.6.34 | R | Make sure the following are enabled |
| | | | compiled in or as a module: |
diff --git a/v2v/input_libvirt.ml b/v2v/input_libvirt.ml
index 6449dc3..f75df00 100644
--- a/v2v/input_libvirt.ml
+++ b/v2v/input_libvirt.ml
@@ -61,8 +61,16 @@ object
| Some _, Some "" ->
None, None
- | Some _, Some "esx" -> (* esx://... *)
- let f = Lib_esx.map_path_to_uri uri in
+ | Some server, Some ("esx"|"gsx"|"vpx" as scheme) -> (* ESX *)
+ (* Check the backend is not libvirt. Works around a libvirt bug
+ * (RHBZ#1134592).
+ *)
+ let libguestfs_backend = (new Guestfs.guestfs ())#get_backend () in
+ if libguestfs_backend = "libvirt" then (
+ error (f_"ESX: because of libvirt bug https://bugzilla.redhat.com/show_bug.cgi?id=1134592 you must set this environment variable:\n\nexport LIBGUESTFS_BACKEND=direct\n\nand then rerun the virt-v2v command.")
+ );
+
+ let f = Lib_esx.map_path_to_uri verbose uri scheme server in
Some f, Some f
(* XXX Missing: Look for qemu+ssh://, xen+ssh:// and use an ssh
diff --git a/v2v/lib_esx.ml b/v2v/lib_esx.ml
index feef090..7966358 100644
--- a/v2v/lib_esx.ml
+++ b/v2v/lib_esx.ml
@@ -18,6 +18,9 @@
(** Functions for dealing with ESX. *)
+open Common_gettext.Gettext
+open Common_utils
+
open Xml
open Utils
@@ -25,6 +28,8 @@ open Printf
let esx_re = Str.regexp "^\\[\\(.*\\)\\] \\(.*\\)\\.vmdk$"
+let session_cookie = ref ""
+
(* Map an ESX <source/> to a qemu URI using the cURL driver
* in qemu. The 'path' will be something like
*
@@ -32,48 +37,183 @@ let esx_re = Str.regexp "^\\[\\(.*\\)\\] \\(.*\\)\\.vmdk$"
*
* including those literal spaces in the string.
*
- * We want to convert that into the following URL:
- * "https://user:password@server/folder/Fedora 20/Fedora 20-flat.vmdk" ^
- * "?dcPath=ha-datacenter&dsName=datastore1"
- *
- * Note that the URL we create here is passed to qemu-img and is
- * ultimately parsed by curl_easy_setopt (CURLOPT_URL).
- *
* XXX Old virt-v2v could also handle snapshots, ie:
*
* "[datastore1] Fedora 20/Fedora 20-NNNNNN.vmdk"
*
- * However this requires access to the server which we don't necessarily
- * have here.
+ * XXX Need to handle templates. The file is called "-delta.vmdk" in
+ * place of "-flat.vmdk".
*)
-let map_path_to_uri uri path format =
+let rec map_path_to_uri verbose uri scheme server path format =
if not (Str.string_match esx_re path 0) then
path, format
else (
let datastore = Str.matched_group 1 path
- and vmdk = Str.matched_group 2 path in
-
- let user =
- match uri.uri_user with
- | None -> ""
- | Some user -> user ^ "@" (* No need to quote it, see RFC 2617. *) in
- let server =
- match uri.uri_server with
- | None -> assert false (* checked by caller *)
- | Some server -> server in
+ and path = Str.matched_group 2 path in
+
+ (* Get the datacenter. *)
+ let datacenter = get_datacenter uri scheme in
+
let port =
match uri.uri_port with
| 443 -> ""
| n when n >= 1 -> ":" ^ string_of_int n
| _ -> "" in
- let qemu_uri =
+ let url =
sprintf
- "https://%s%s%s/folder/%s-flat.vmdk?dcPath=ha-datacenter&dsName=%s"
- user server port vmdk (uri_quote datastore) in
+ "https://%s%s/folder/%s-flat.vmdk?dcPath=%s&dsName=%s"
+ server port
+ (uri_quote path) (uri_quote datacenter) (uri_quote datastore) in
+
+ (* If no_verify=1 was passed in the libvirt URI, then we have to
+ * turn off certificate verification here too.
+ *)
+ let sslverify =
+ match uri.uri_query_raw with
+ | None -> true
+ | Some query ->
+ (* XXX only works if the query string is not URI-quoted *)
+ string_find query "no_verify=1" = -1 in
+
+ (* Now we have to query the server to get the session cookie. *)
+ let session_cookie = get_session_cookie verbose uri sslverify url in
+
+ (* Construct the JSON parameters. *)
+ let json_params = [
+ "file.driver", "https";
+ "file.url", url;
+ "file.timeout", "60";
+ ] in
+
+ let json_params =
+ if sslverify then json_params
+ else ("file.sslverify", "off") :: json_params in
+
+ let json_params =
+ match session_cookie with
+ | None -> json_params
+ | Some cookie -> ("file.cookie", cookie) :: json_params in
+
+ if verbose then (
+ printf "esx: json parameters:\n";
+ List.iter (fun (n, v) -> printf " %s : %s\n" n v) json_params
+ );
+
+ (* Turn the JSON parameters into a 'json:' protocol string.
+ *
+ * Notes:
+ *
+ * (1) This requires qemu-img >= 2.1.0.
+ *
+ * (2) We don't 'quote' the commas, because in {!V2v} we pass the
+ * option to qemu-img create -b (not -o backing_file=...) and so
+ * extra quoting of commas is not required. However if we changed
+ * things in future then we might requiring quoting of commas to be
+ * added somewhere.
+ *)
+ let qemu_uri =
+ sprintf "json: { %s }"
+ (String.concat " , " (
+ List.map
+ (fun (n, v) -> sprintf "\"%s\" : \"%s\"" n (json_quote v))
+ json_params
+ )
+ ) in
(* The libvirt ESX driver doesn't normally specify a format, but
* the format of the -flat file is *always* raw, so force it here.
*)
qemu_uri, Some "raw"
)
+
+and get_datacenter uri scheme =
+ let default_dc = "ha-datacenter" in
+ match scheme with
+ | "vpx" -> (* Hopefully the first part of the path. *)
+ (match uri.uri_path with
+ | None ->
+ warning ~prog (f_"esx: URI (-ic parameter) contains no path, so we cannot determine the datacenter name");
+ default_dc
+ | Some path ->
+ let path =
+ let len = String.length path in
+ if len > 0 && path.[0] = '/' then
+ String.sub path 1 (len-1)
+ else path in
+ let len =
+ try String.index path '/' with Not_found -> String.length path in
+ String.sub path 0 len
+ );
+ | "esx" -> (* Connecting to an ESXi hypervisor directly, so it's fixed. *)
+ default_dc
+ | _ -> (* Don't know, so guess. *)
+ default_dc
+
+and get_session_cookie verbose uri sslverify url =
+ (* Memoize the session cookie. *)
+ if !session_cookie <> "" then
+ Some !session_cookie
+ else (
+ let cmd =
+ sprintf "curl -s%s%s%s -I %s ||:"
+ (if not sslverify then " --insecure" else "")
+ (match uri.uri_user with Some _ -> " -u" | None -> "")
+ (match uri.uri_user with Some user -> " " ^ quote user | None -> "")
+ (quote url) in
+ let lines = external_command ~prog cmd in
+
+ let dump_response chan =
+ fprintf chan "%s\n" cmd;
+ List.iter (fun x -> fprintf chan "%s\n" x) lines
+ in
+
+ if verbose then dump_response stdout;
+
+ (* Look for the last HTTP/x.y NNN status code in the output. *)
+ let status = ref "" in
+ List.iter (
+ fun line ->
+ let len = String.length line in
+ if len >= 12 && String.sub line 0 5 = "HTTP/" then
+ status := String.sub line 9 3
+ ) lines;
+ let status = !status in
+ if status = "" then (
+ dump_response stderr;
+ error (f_"esx: no status code in output of 'curl' command. Is 'curl' installed?")
+ );
+
+ if status = "401" then (
+ dump_response stderr;
+ error (f_"esx: incorrect username or password")
+ );
+
+ if status = "404" then (
+ dump_response stderr;
+ error (f_"esx: URL not found: %s") url
+ );
+
+ if status <> "200" then (
+ dump_response stderr;
+ error (f_"esx: invalid response from server")
+ );
+
+ (* Get the cookie. *)
+ List.iter (
+ fun line ->
+ let len = String.length line in
+ if len >= 12 && String.sub line 0 12 = "Set-Cookie: " then (
+ let line = String.sub line 12 (len-12) in
+ let cookie, _ = string_split ";" line in
+ session_cookie := cookie
+ )
+ ) lines;
+ if !session_cookie = "" then (
+ dump_response stderr;
+ warning ~prog (f_"esx: could not read session cookie from the vCenter Server, conversion may consume all sessions on the server and fail part way through");
+ None
+ )
+ else
+ Some !session_cookie
+ )
diff --git a/v2v/lib_esx.mli b/v2v/lib_esx.mli
index 626ebd9..e504098 100644
--- a/v2v/lib_esx.mli
+++ b/v2v/lib_esx.mli
@@ -18,6 +18,6 @@
(** Functions for dealing with ESX. *)
-val map_path_to_uri : Xml.uri -> string -> string option -> string * string option
+val map_path_to_uri : bool -> Xml.uri -> string -> string -> string -> string option -> string * string option
(** Map a VMware path like "[datastore1] guest/guest.vmdk" to the
URL where we can fetch the data. *)
diff --git a/v2v/utils.ml b/v2v/utils.ml
index dd46835..38443fa 100644
--- a/v2v/utils.ml
+++ b/v2v/utils.ml
@@ -61,6 +61,19 @@ let uri_quote str =
done;
String.concat "" (List.rev !xs)
+(* JSON quoting. *)
+let json_quote str =
+ let str = replace_str str "\\" "\\\\" in
+ let str = replace_str str "\"" "\\\"" in
+ let str = replace_str str "'" "\\'" in
+ let str = replace_str str "\008" "\\b" in
+ let str = replace_str str "\012" "\\f" in
+ let str = replace_str str "\n" "\\n" in
+ let str = replace_str str "\r" "\\r" in
+ let str = replace_str str "\t" "\\t" in
+ let str = replace_str str "\011" "\\v" in
+ str
+
external drive_name : int -> string = "v2v_utils_drive_name"
let compare_app2_versions app1 app2 =
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
index 5b3393e..90160ea 100644
--- a/v2v/virt-v2v.pod
+++ b/v2v/virt-v2v.pod
@@ -4,9 +4,9 @@ virt-v2v - Convert a guest to use KVM
=head1 SYNOPSIS
- virt-v2v -ic esx://esx.example.com/ esx_guest
+ virt-v2v -ic vpx://esx.example.com/Datacenter/esxi esx_guest
- virt-v2v -ic esx://esx.example.com/ \
+ virt-v2v -ic vpx://esx.example.com/Datacenter/esxi \
-o rhev -os rhev.nfs:/export_domain --network rhevm esx_guest
virt-v2v -i libvirtxml -o local -os /var/tmp guest-domain.xml
@@ -63,17 +63,21 @@ I<-o rhev> is used to write to a RHEV-M / oVirt target.
=head1 EXAMPLES
-=head2 Convert from VMware ESX server to local libvirt
+=head2 Convert from VMware vCenter server to local libvirt
-You have a VMware ESX server called C<esx.example.com>. You want to
-convert a guest called C<esx_guest> to run locally under libvirt.
+You have a VMware vCenter server called C<esx.example.com>, a
+datacenter called C<Datacenter>, and an ESXi hypervisor called
+C<esxi>. You want to convert a guest called C<esx_guest> to run
+locally under libvirt.
- virt-v2v -ic esx://example.com esx_guest
+ virt-v2v -ic vpx://example.com/Datacenter/esxi esx_guest
In this case you will most likely have to run virt-v2v as C<root>,
since it needs to talk to the system libvirt daemon and copy the guest
disks to C</var/lib/libvirt/images>.
+For more information see L</INPUT FROM VMWARE VCENTER SERVER> below.
+
=head2 Convert from ESX to RHEV-M/oVirt
This is the same as the previous example, except you want to send the
@@ -83,7 +87,7 @@ the location of the Export Storage Domain you should check the
settings on your RHEV-M management console. Guest network
interface(s) are connected to the target network called C<rhevm>.
- virt-v2v -ic esx://esx.example.com/ \
+ virt-v2v -ic vpx://esx.example.com/Datacenter/esxi \
-o rhev -os rhev.nfs:/export_domain --network rhevm esx_guest
In this case the host running virt-v2v acts as a B<conversion server>.
@@ -169,6 +173,8 @@ is only used when S<I<-i libvirt>>.
Only local libvirt connections and ESX connections can be used.
Remote libvirt connections will not work in general.
+See also L</INPUT FROM VMWARE VCENTER SERVER> below.
+
=item B<-if> format
For I<-i disk> only, this specifies the format of the input disk
@@ -530,6 +536,76 @@ I<--bridge> option instead. For example:
$ virt-v2v [...] --bridge br0:targetbr
+=head1 INPUT FROM VMWARE VCENTER SERVER
+
+Virt-v2v is able to import guests from VMware vCenter Server.
+
+Note that virt-v2v B<cannot> import guests directly from an ESXi
+hypervisor.
+
+Virt-v2v uses libvirt for access to vCenter, and therefore the input
+mode should be I<-i libvirt>. As this is the default, you don't need
+to specify it on the command line.
+
+The libvirt URI of a vCenter server looks something like this:
+
+ vpx://user@server/Datacenter/esxi
+
+where C<user@> is the (optional, but recommended) user to connect as,
+C<server> is the vCenter Server (I<not> hypervisor), C<Datacenter> is
+the name of the datacenter, and C<esxi> is the name of the ESXi
+hypervisor running the guest.
+
+For full details of libvirt URIs, see: L<http://libvirt.org/drvesx.html>
+
+=head2 ESX: TEST LIBVIRT CONNECTION TO VCENTER
+
+Use the L<virsh(1)> command to list the guests on the vCenter Server
+like this:
+
+ $ virsh -c 'vpx://root@esx.example.com/Datacenter/esxi' list --all
+ Enter root's password for esx.example.com: ***
+
+ Id Name State
+ ----------------------------------------------------
+ - Fedora 20 shut off
+ - Windows 2003 shut off
+
+If you get an error "Peer certificate cannot be authenticated with
+given CA certificates" or similar, then you can either import the ESX
+host's certificate, or bypass signature verification by adding the
+C<?no_verify=1> flag:
+
+ $ virsh -c 'vpx://root@esx.example.com/Datacenter/esxi?no_verify=1' list --all
+
+You should also try dumping the metadata from any guest on your
+server, like this:
+
+ $ virsh -c 'vpx://root@esx.example.com/Datacenter/esxi' dumpxml "Windows 2003"
+ <domain type='vmware'>
+ <name>Windows 2003</name>
+ [...]
+ </domain>
+
+B<If the above commands do not work, then virt-v2v is not going to
+work either>. Fix your libvirt configuration and/or your VMware
+vCenter Server before continuing.
+
+=head2 ESX: IMPORTING A GUEST
+
+To import a particular guest from vCenter Server, do:
+
+ $ virt-v2v -ic 'esx://root@esx.example.com/Datacenter/esxi?no_verify=1' \
+ "Windows 2003" \
+ -o local -os /var/tmp
+
+where C<Windows 2003> is the name of the guest (which must be shut
+down).
+
+In this case the output flags are set to write the converted guest to
+a temporary directory as this is just an example, but you can also
+write to libvirt or RHEV.
+
=head1 OUTPUT TO LIBVIRT
The I<-o libvirt> option lets you upload the converted guest to
--
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