Richard W.M. Jones
2015-Oct-09 11:53 UTC
[Libguestfs] [PATCH 0/4] v2v: Use libvirt-supplied <vmware:datacenterpath> if available.
See earlier thread on libvir-list: https://www.redhat.com/archives/libvir-list/2015-September/thread.html#00201 Libvirt >= 1.2.20 supplies the correct dcPath parameter. If it is available in the libvirt XML, use it, otherwise fall back to the old method of trying to guess it from the vpx:// path. Patches 1, 2 and 4 are just refactoring around this change. Rich.
Richard W.M. Jones
2015-Oct-09 11:53 UTC
[Libguestfs] [PATCH 1/4] v2v: Move curl functions to library module.
This refactors the curl functions used to talk to vCenter into a library module. --- po/POTFILES-ml | 1 + v2v/Makefile.am | 2 ++ v2v/curl.ml | 71 ++++++++++++++++++++++++++++++++++++++ v2v/curl.mli | 38 ++++++++++++++++++++ v2v/input_libvirt_vcenter_https.ml | 54 +++-------------------------- 5 files changed, 116 insertions(+), 50 deletions(-) create mode 100644 v2v/curl.ml create mode 100644 v2v/curl.mli diff --git a/po/POTFILES-ml b/po/POTFILES-ml index 437926f..239e586 100644 --- a/po/POTFILES-ml +++ b/po/POTFILES-ml @@ -100,6 +100,7 @@ v2v/OVF.ml v2v/cmdline.ml v2v/convert_linux.ml v2v/convert_windows.ml +v2v/curl.ml v2v/detect_antivirus.ml v2v/domainxml.ml v2v/input_disk.ml diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 7f30240..6bfdb62 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -44,6 +44,7 @@ CLEANFILES = *~ *.annot *.cmi *.cmo *.cmx *.cmxa *.o virt-v2v SOURCES_MLI = \ convert_linux.mli \ convert_windows.mli \ + curl.mli \ detect_antivirus.ml \ detect_antivirus.mli \ DOM.mli \ @@ -75,6 +76,7 @@ SOURCES_ML = \ types.ml \ xml.ml \ utils.ml \ + curl.ml \ domainxml.ml \ DOM.ml \ kvmuid.ml \ diff --git a/v2v/curl.ml b/v2v/curl.ml new file mode 100644 index 0000000..29315b4 --- /dev/null +++ b/v2v/curl.ml @@ -0,0 +1,71 @@ +(* virt-v2v + * Copyright (C) 2009-2015 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. + *) + +open Printf + +open Common_utils + +type curl_args = (string * string option) list + +let run curl_args + let config_file, chan = Filename.open_temp_file "v2vcurl" ".conf" in + List.iter ( + function + | name, None -> fprintf chan "%s\n" name + | name, Some value -> + fprintf chan "%s = \"" name; + (* Write the quoted value. See 'curl' man page for what is + * allowed here. + *) + let len = String.length value in + for i = 0 to len-1 do + match value.[i] with + | '\\' -> output_string chan "\\\\" + | '"' -> output_string chan "\\\"" + | '\t' -> output_string chan "\\t" + | '\n' -> output_string chan "\\n" + | '\r' -> output_string chan "\\r" + | '\x0b' -> output_string chan "\\v" + | c -> output_char chan c + done; + fprintf chan "\"\n" + ) curl_args; + close_out chan; + + let cmd = sprintf "curl -q --config %s" (Filename.quote config_file) in + let lines = external_command cmd in + Unix.unlink config_file; + lines + +let print_curl_command chan curl_args + (* Don't print passwords in the debug output. *) + let curl_args + List.map ( + function + | ("user", Some _) -> ("user", Some "<hidden>") + | x -> x + ) curl_args in + + (* Dump out the approximate curl command that was run. *) + fprintf chan "curl -q"; + List.iter ( + function + | name, None -> fprintf chan " --%s" name + | name, Some value -> fprintf chan " --%s %s" name (Filename.quote value) + ) curl_args; + fprintf chan "\n"; diff --git a/v2v/curl.mli b/v2v/curl.mli new file mode 100644 index 0000000..eb89e23 --- /dev/null +++ b/v2v/curl.mli @@ -0,0 +1,38 @@ +(* virt-v2v + * Copyright (C) 2009-2015 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. + *) + +(** Functions for dealing with [curl]. *) + +type curl_args = (string * string option) list + +val run : curl_args -> string list +(** [run curl_args] runs the [curl] command. + + It actually uses the [curl --config] option to pass the arguments + securely to curl through an external file. Thus passwords etc are + not exposed to other users on the same machine. + + The curl arguments are a list of key, value pairs corresponding + to curl command line parameters, without leading dashes, + eg. [("user", Some "user:password")]. + + The result is the output of curl as a list of lines. *) + +val print_curl_command : out_channel -> curl_args -> unit +(** Print the curl command line. This elides any arguments that + might contain passwords, so is useful for debugging. *) diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml index 99f3ee1..13108c7 100644 --- a/v2v/input_libvirt_vcenter_https.ml +++ b/v2v/input_libvirt_vcenter_https.ml @@ -36,7 +36,7 @@ let readahead_for_copying = Some (64 * 1024 * 1024) (* Return the session cookie. It is memoized, so you can call this * as often as required. *) -let rec get_session_cookie +let get_session_cookie let session_cookie = ref "" in fun password scheme uri sslverify url -> if !session_cookie <> "" then @@ -60,24 +60,11 @@ let rec get_session_cookie let curl_args if not sslverify then ("insecure", None) :: curl_args else curl_args in - let lines = run_curl_get_lines curl_args in + let lines = Curl.run curl_args in let dump_response chan - (* Don't print passwords in the debug output. *) - let curl_args - List.map ( - function - | ("user", Some _) -> ("user", Some "<hidden>") - | x -> x - ) curl_args in - (* Dump out the approximate curl command that was run. *) - fprintf chan "curl -q"; - List.iter ( - function - | name, None -> fprintf chan " --%s" name - | name, Some value -> fprintf chan " --%s %s" name (quote value) - ) curl_args; - fprintf chan "\n"; + Curl.print_curl_command chan curl_args; + (* Dump out the output of the command. *) List.iter (fun x -> fprintf chan "%s\n" x) lines; flush chan @@ -137,39 +124,6 @@ let rec get_session_cookie Some !session_cookie ) -(* Run 'curl' and pass the arguments securely through the --config - * option and an external file. - *) -and run_curl_get_lines curl_args - let config_file, chan = Filename.open_temp_file "v2vcurl" ".conf" in - List.iter ( - function - | name, None -> fprintf chan "%s\n" name - | name, Some value -> - fprintf chan "%s = \"" name; - (* Write the quoted value. See 'curl' man page for what is - * allowed here. - *) - let len = String.length value in - for i = 0 to len-1 do - match value.[i] with - | '\\' -> output_string chan "\\\\" - | '"' -> output_string chan "\\\"" - | '\t' -> output_string chan "\\t" - | '\n' -> output_string chan "\\n" - | '\r' -> output_string chan "\\r" - | '\x0b' -> output_string chan "\\v" - | c -> output_char chan c - done; - fprintf chan "\"\n" - ) curl_args; - close_out chan; - - let cmd = sprintf "curl -q --config %s" (quote config_file) in - let lines = external_command cmd in - Unix.unlink config_file; - lines - let multiple_slash = Str.regexp "/+" (* Helper function to extract the dcPath from a URI. *) -- 2.5.0
Richard W.M. Jones
2015-Oct-09 11:53 UTC
[Libguestfs] [PATCH 2/4] v2v: Move VCenter functions to library module.
This refactors useful VCenter functions out of the large 'input_libvirt_vcenter_https.ml' file. --- po/POTFILES-ml | 1 + v2v/Makefile.am | 2 + v2v/input_libvirt_vcenter_https.ml | 144 +-------------------------------- v2v/vCenter.ml | 160 +++++++++++++++++++++++++++++++++++++ v2v/vCenter.mli | 40 ++++++++++ 5 files changed, 205 insertions(+), 142 deletions(-) create mode 100644 v2v/vCenter.ml create mode 100644 v2v/vCenter.mli diff --git a/po/POTFILES-ml b/po/POTFILES-ml index 239e586..f794f1d 100644 --- a/po/POTFILES-ml +++ b/po/POTFILES-ml @@ -126,4 +126,5 @@ v2v/types.ml v2v/utils.ml v2v/v2v.ml v2v/v2v_unit_tests.ml +v2v/vCenter.ml v2v/xml.ml diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 6bfdb62..da31aaf 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -69,6 +69,7 @@ SOURCES_MLI = \ OVF.mli \ stringMap.mli \ types.mli \ + vCenter.mli \ xml.mli SOURCES_ML = \ @@ -77,6 +78,7 @@ SOURCES_ML = \ xml.ml \ utils.ml \ curl.ml \ + vCenter.ml \ domainxml.ml \ DOM.ml \ kvmuid.ml \ diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml index 13108c7..0f1a648 100644 --- a/v2v/input_libvirt_vcenter_https.ml +++ b/v2v/input_libvirt_vcenter_https.ml @@ -33,146 +33,6 @@ open Printf let readahead_for_conversion = None let readahead_for_copying = Some (64 * 1024 * 1024) -(* Return the session cookie. It is memoized, so you can call this - * as often as required. - *) -let get_session_cookie - let session_cookie = ref "" in - fun password scheme uri sslverify url -> - if !session_cookie <> "" then - Some !session_cookie - else ( - let curl_args = [ - "head", None; - "silent", None; - "url", Some url; - ] in - let curl_args - match uri.uri_user, password with - | None, None -> curl_args - | None, Some _ -> - warning (f_"--password-file parameter ignored because 'user@' was not given in the URL"); - curl_args - | Some user, None -> - ("user", Some user) :: curl_args - | Some user, Some password -> - ("user", Some (user ^ ":" ^ password)) :: curl_args in - let curl_args - if not sslverify then ("insecure", None) :: curl_args else curl_args in - - let lines = Curl.run curl_args in - - let dump_response chan - Curl.print_curl_command chan curl_args; - - (* Dump out the output of the command. *) - List.iter (fun x -> fprintf chan "%s\n" x) lines; - flush chan - 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_"vcenter: no status code in output of 'curl' command. Is 'curl' installed?") - ); - - if status = "401" then ( - dump_response stderr; - if uri.uri_user <> None then - error (f_"vcenter: incorrect username or password") - else - error (f_"vcenter: incorrect username or password. You might need to specify the username in the URI like this: %s://USERNAME@[etc]") - scheme - ); - - if status = "404" then ( - dump_response stderr; - error (f_"vcenter: URL not found: %s\n\nThe '--dcpath' parameter may be useful. See the explanation in the virt-v2v(1) man page OPTIONS section.") url - ); - - if status <> "200" then ( - dump_response stderr; - error (f_"vcenter: 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 (f_"vcenter: 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 - ) - -let multiple_slash = Str.regexp "/+" - -(* Helper function to extract the dcPath from a URI. *) -let get_dcPath uri scheme - let default_dc = "ha-datacenter" in - match scheme with - | "vpx" -> - (match uri.uri_path with - | None -> - warning (f_"vcenter: URI (-ic parameter) contains no path, so we cannot determine the dcPath (datacenter name)"); - default_dc - | Some path -> - (* vCenter: URIs are *usually* '/Folder/Datacenter/esxi' so we can - * just chop off the first '/' and final '/esxi' to get the dcPath. - * - * The libvirt driver allows things like '/DC///esxi////' so we also - * have to handle trailing slashes and collapse multiple slashes into - * single (RHBZ#1258342). - * - * However if there is a cluster involved then the URI may be - * /Folder/Datacenter/Cluster/esxi but dcPath=Folder/Datacenter/Cluster - * won't work. In this case the user has to adjust the path to - * remove the Cluster name (which still works in libvirt). There - * should be a way to ask the libvirt vpx driver for the correct - * path, but there isn't. XXX See also RHBZ#1256823. - *) - (* Collapse multiple slashes to single slash. *) - let path = Str.global_replace multiple_slash "/" path in - (* Chop off the first and trailing '/' (if found). *) - let path - let len = String.length path in - if len > 0 && path.[0] = '/' then - String.sub path 1 (len-1) - else path in - let path - let len = String.length path in - if len > 0 && path.[len-1] = '/' then - String.sub path 0 (len-1) - else path in - (* Chop off the final element (ESXi hostname). *) - let len - try String.rindex 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 - (* Map the <source/> string to a qemu URI using the cURL driver * in qemu. The 'path' will be something like * @@ -200,7 +60,7 @@ let map_source_to_uri ?readahead dcPath password uri scheme server path let dcPath match dcPath with | None -> - let dcPath = get_dcPath uri scheme in + let dcPath = VCenter.guess_dcPath uri scheme in if verbose () then printf "vcenter: calculated dcPath as: %s\n" dcPath; dcPath @@ -233,7 +93,7 @@ let map_source_to_uri ?readahead dcPath password uri scheme server path (* Now we have to query the server to get the session cookie. *) let session_cookie - get_session_cookie password scheme uri sslverify url in + VCenter.get_session_cookie password scheme uri sslverify url in (* Construct the JSON parameters. *) let json_params = [ diff --git a/v2v/vCenter.ml b/v2v/vCenter.ml new file mode 100644 index 0000000..20dd964 --- /dev/null +++ b/v2v/vCenter.ml @@ -0,0 +1,160 @@ +(* virt-v2v + * Copyright (C) 2009-2015 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. + *) + +open Printf + +open Common_utils +open Common_gettext.Gettext + +open Xml + +(* Memoized session cookie. *) +let session_cookie = ref "" + +let get_session_cookie password scheme uri sslverify url + if !session_cookie <> "" then + Some !session_cookie + else ( + let curl_args = [ + "head", None; + "silent", None; + "url", Some url; + ] in + let curl_args + match uri.uri_user, password with + | None, None -> curl_args + | None, Some _ -> + warning (f_"--password-file parameter ignored because 'user@' was not given in the URL"); + curl_args + | Some user, None -> + ("user", Some user) :: curl_args + | Some user, Some password -> + ("user", Some (user ^ ":" ^ password)) :: curl_args in + let curl_args + if not sslverify then ("insecure", None) :: curl_args else curl_args in + + let lines = Curl.run curl_args in + + let dump_response chan + Curl.print_curl_command chan curl_args; + + (* Dump out the output of the command. *) + List.iter (fun x -> fprintf chan "%s\n" x) lines; + flush chan + 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_"vcenter: no status code in output of 'curl' command. Is 'curl' installed?") + ); + + if status = "401" then ( + dump_response stderr; + if uri.uri_user <> None then + error (f_"vcenter: incorrect username or password") + else + error (f_"vcenter: incorrect username or password. You might need to specify the username in the URI like this: %s://USERNAME@[etc]") + scheme + ); + + if status = "404" then ( + dump_response stderr; + error (f_"vcenter: URL not found: %s\n\nThe '--dcpath' parameter may be useful. See the explanation in the virt-v2v(1) man page OPTIONS section.") url + ); + + if status <> "200" then ( + dump_response stderr; + error (f_"vcenter: 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 (f_"vcenter: 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 + ) + +let multiple_slash = Str.regexp "/+" +let default_dc = "ha-datacenter" + +let guess_dcPath uri = function + | "vpx" -> + (match uri.uri_path with + | None -> + warning (f_"vcenter: URI (-ic parameter) contains no path, so we cannot determine the dcPath (datacenter name)"); + default_dc + | Some path -> + (* vCenter: URIs are *usually* '/Folder/Datacenter/esxi' so we can + * just chop off the first '/' and final '/esxi' to get the dcPath. + * + * The libvirt driver allows things like '/DC///esxi////' so we also + * have to handle trailing slashes and collapse multiple slashes into + * single (RHBZ#1258342). + * + * However if there is a cluster involved then the URI may be + * /Folder/Datacenter/Cluster/esxi but dcPath=Folder/Datacenter/Cluster + * won't work. In this case the user has to adjust the path to + * remove the Cluster name (which still works in libvirt). There + * should be a way to ask the libvirt vpx driver for the correct + * path, but there isn't. XXX See also RHBZ#1256823. + *) + (* Collapse multiple slashes to single slash. *) + let path = Str.global_replace multiple_slash "/" path in + (* Chop off the first and trailing '/' (if found). *) + let path + let len = String.length path in + if len > 0 && path.[0] = '/' then + String.sub path 1 (len-1) + else path in + let path + let len = String.length path in + if len > 0 && path.[len-1] = '/' then + String.sub path 0 (len-1) + else path in + (* Chop off the final element (ESXi hostname). *) + let len + try String.rindex 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 diff --git a/v2v/vCenter.mli b/v2v/vCenter.mli new file mode 100644 index 0000000..10a9657 --- /dev/null +++ b/v2v/vCenter.mli @@ -0,0 +1,40 @@ +(* virt-v2v + * Copyright (C) 2009-2015 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. + *) + +(** Functions for dealing with VMware vCenter. *) + +val get_session_cookie : string option -> string -> Xml.uri -> bool -> string -> string option +(** [get_session_cookie password scheme uri sslverify url] + contacts the vCenter server, logs in, and gets the session cookie, + which can later be passed back to the server instead of having to + log in each time (this is also more efficient since it avoids + vCenter running out of authentication sessions). + + Returns [None] if the session cookie could not be read (but + authentication was successful). You can proceed without the + session cookie in this case, but there is an unavoidable + danger of running out of authentication sessions. If the + session cookie could not be read, this function prints a + warning. + + The session cookie is memoized so you can call this function as + often as you want, and only a single log in is made. *) + +val guess_dcPath : Xml.uri -> string -> string +(** Try to guess the dcPath parameter from a URI. The mapping is + not precise. *) -- 2.5.0
Richard W.M. Jones
2015-Oct-09 11:53 UTC
[Libguestfs] [PATCH 3/4] v2v: Use libvirt-supplied <vmware:datacenterpath> if available.
In libvirt >= 1.2.20, the VMware libvirt driver supplies the correct dcPath to use via <vmware:datacenterpath> in the libvirt XML. If libvirt passes us this element, use it. This code still allows the user to override dcPath using the --dcPath option on the command line, but that's mainly for safety so we can fix any problems in virt-v2v or libvirt in the field. As we get more confident in libvirt and as libvirt 1.2.20 is more widely adopted, we will be able to deprecate this parameter entirely. Thanks: Matthias Bolte for adding the <vmware:datacenterpath> element to libvirt in https://libvirt.org/git/?p=libvirt.git;a=commit;h=636a99058758a0447482f3baad94de8de3ab1151 --- v2v/input_libvirt_vcenter_https.ml | 48 +++++++++++++++++++++++++++----------- v2v/vCenter.ml | 4 +--- v2v/vCenter.mli | 6 ++++- v2v/virt-v2v.pod | 9 +++---- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml index 0f1a648..a5b3c8b 100644 --- a/v2v/input_libvirt_vcenter_https.ml +++ b/v2v/input_libvirt_vcenter_https.ml @@ -56,19 +56,6 @@ let map_source_to_uri ?readahead dcPath password uri scheme server path let datastore = Str.matched_group 1 path and path = Str.matched_group 2 path in - (* Get the dcPath. *) - let dcPath - match dcPath with - | None -> - let dcPath = VCenter.guess_dcPath uri scheme in - if verbose () then - printf "vcenter: calculated dcPath as: %s\n" dcPath; - dcPath - | Some dcPath -> - if verbose () then - printf "vcenter: using --dcpath from the command line: %s\n" dcPath; - dcPath in - let port match uri.uri_port with | 443 -> "" @@ -131,11 +118,12 @@ let map_source_to_uri ?readahead dcPath password uri scheme server path (* Subclass specialized for handling VMware vCenter over https. *) class input_libvirt_vcenter_https - dcPath password libvirt_uri parsed_uri scheme server guest + cmdline_dcPath password libvirt_uri parsed_uri scheme server guest object inherit input_libvirt password libvirt_uri guest val saved_source_paths = Hashtbl.create 13 + val mutable dcPath = "" method source () if verbose () then @@ -150,6 +138,38 @@ object let xml = Domainxml.dumpxml ?password ?conn:libvirt_uri guest in let source, disks = parse_libvirt_xml ?conn:libvirt_uri xml in + (* Find the <vmware:datacenterpath> element from the XML, if it + * exists. This was added in libvirt >= 1.2.20. + *) + let xml_dcPath + let doc = Xml.parse_memory xml in + let xpathctx = Xml.xpath_new_context doc in + Xml.xpath_register_ns xpathctx + "vmware" "http://libvirt.org/schemas/domain/vmware/1.0"; + let xpath_string = xpath_string xpathctx in + xpath_string "/domain/vmware:datacenterpath" in + + (* Calculate the dcPath we're going to use. *) + dcPath <- ( + match cmdline_dcPath, xml_dcPath with + (* Command line --dcpath parameter overrides everything, allowing + * users to correct any mistakes in v2v or libvirt. + *) + | Some p, (None|Some _) -> + if verbose () then + printf "vcenter: using --dcpath from the command line: %s\n" p; + p + | None, Some p -> + if verbose () then + printf "vcenter: using <vmware:datacenterpath> from libvirt: %s\n" p; + p + | None, None -> + let p = VCenter.guess_dcPath parsed_uri scheme in + if verbose () then + printf "vcenter: guessed dcPath from URI: %s\n" p; + p + ); + (* Save the original source paths, so that we can remap them again * in [#adjust_overlay_parameters]. *) diff --git a/v2v/vCenter.ml b/v2v/vCenter.ml index 20dd964..9ff9415 100644 --- a/v2v/vCenter.ml +++ b/v2v/vCenter.ml @@ -132,9 +132,7 @@ let guess_dcPath uri = function * However if there is a cluster involved then the URI may be * /Folder/Datacenter/Cluster/esxi but dcPath=Folder/Datacenter/Cluster * won't work. In this case the user has to adjust the path to - * remove the Cluster name (which still works in libvirt). There - * should be a way to ask the libvirt vpx driver for the correct - * path, but there isn't. XXX See also RHBZ#1256823. + * remove the Cluster name (which still works in libvirt). *) (* Collapse multiple slashes to single slash. *) let path = Str.global_replace multiple_slash "/" path in diff --git a/v2v/vCenter.mli b/v2v/vCenter.mli index 10a9657..87583c0 100644 --- a/v2v/vCenter.mli +++ b/v2v/vCenter.mli @@ -37,4 +37,8 @@ val get_session_cookie : string option -> string -> Xml.uri -> bool -> string -> val guess_dcPath : Xml.uri -> string -> string (** Try to guess the dcPath parameter from a URI. The mapping is - not precise. *) + not precise. + + This function is only used with [libvirt < 1.2.20] because later + versions of libvirt provide the dcPath (see + https://bugzilla.redhat.com/1263574). *) diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod index 09eb3ea..ae87986 100644 --- a/v2v/virt-v2v.pod +++ b/v2v/virt-v2v.pod @@ -252,6 +252,9 @@ See I<--network> below. =item B<--dcpath> Folder/Datacenter +B<NB:> You don't need to use this parameter if you have +S<libvirt E<ge> 1.2.20>. + For VMware vCenter, override the C<dcPath=...> parameter used to select the datacenter. Virt-v2v can usually calculate this from the C<vpx://> URI, but if it gets it wrong, then you can override it using @@ -981,12 +984,6 @@ added to the URI, eg: vpx://user@server/Folder/Datacenter/esxi -Virt-v2v needs to calculate the C<dcPath> parameter from the URI, and -it does this by removing the final C</esxi> element, so in the above -example C<dcPath=Folder/Datacenter>. As it is not always possible to -correctly calculate C<dcPath> from the URI, you can override this -using the I<--dcpath> parameter. - For full details of libvirt URIs, see: L<http://libvirt.org/drvesx.html> Typical errors from libvirt / virsh when the URI is wrong include: -- 2.5.0
Richard W.M. Jones
2015-Oct-09 11:53 UTC
[Libguestfs] [PATCH 4/4] v2v: Refactor the map_source_to_uri function.
Move this function to the VCenter module. This is easier since it doesn't have to do dcPath calculation (see previous commit). The readhead parameter is no longer labelled. --- v2v/input_libvirt_vcenter_https.ml | 94 ++------------------------------------ v2v/vCenter.ml | 74 ++++++++++++++++++++++++++++++ v2v/vCenter.mli | 10 ++++ 3 files changed, 89 insertions(+), 89 deletions(-) diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml index a5b3c8b..8310dd5 100644 --- a/v2v/input_libvirt_vcenter_https.ml +++ b/v2v/input_libvirt_vcenter_https.ml @@ -33,89 +33,6 @@ open Printf let readahead_for_conversion = None let readahead_for_copying = Some (64 * 1024 * 1024) -(* Map the <source/> string to a qemu URI using the cURL driver - * in qemu. The 'path' will be something like - * - * "[datastore1] Fedora 20/Fedora 20.vmdk" - * - * including those literal spaces in the string. - * - * XXX Old virt-v2v could also handle snapshots, ie: - * - * "[datastore1] Fedora 20/Fedora 20-NNNNNN.vmdk" - * - * XXX Need to handle templates. The file is called "-delta.vmdk" in - * place of "-flat.vmdk". - *) -let source_re = Str.regexp "^\\[\\(.*\\)\\] \\(.*\\)\\.vmdk$" - -let map_source_to_uri ?readahead dcPath password uri scheme server path - if not (Str.string_match source_re path 0) then - path - else ( - let datastore = Str.matched_group 1 path - and path = Str.matched_group 2 path in - - let port - match uri.uri_port with - | 443 -> "" - | n when n >= 1 -> ":" ^ string_of_int n - | _ -> "" in - - let url - sprintf - "https://%s%s/folder/%s-flat.vmdk?dcPath=%s&dsName=%s" - server port - (uri_quote path) (uri_quote dcPath) (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 - VCenter.get_session_cookie password scheme uri sslverify url in - - (* Construct the JSON parameters. *) - let json_params = [ - "file.driver", JSON.String "https"; - "file.url", JSON.String url; - (* https://bugzilla.redhat.com/show_bug.cgi?id=1146007#c10 *) - "file.timeout", JSON.Int 2000; - ] in - - let json_params - match readahead with - | None -> json_params - | Some readahead -> - ("file.readahead", JSON.Int readahead) :: json_params in - - let json_params - if sslverify then json_params - else ("file.sslverify", JSON.String "off") :: json_params in - - let json_params - match session_cookie with - | None -> json_params - | Some cookie -> ("file.cookie", JSON.String cookie) :: json_params in - - if verbose () then - printf "vcenter: json parameters: %s\n" (JSON.string_of_doc json_params); - - (* Turn the JSON parameters into a 'json:' protocol string. - * Note this requires qemu-img >= 2.1.0. - *) - let qemu_uri = "json: " ^ JSON.string_of_doc json_params in - - qemu_uri - ) - (* Subclass specialized for handling VMware vCenter over https. *) class input_libvirt_vcenter_https cmdline_dcPath password libvirt_uri parsed_uri scheme server guest @@ -190,9 +107,9 @@ object | { p_source = P_source_dev _ } -> assert false | { p_source_disk = disk; p_source = P_dont_rewrite } -> disk | { p_source_disk = disk; p_source = P_source_file path } -> - let qemu_uri = map_source_to_uri ?readahead - dcPath password - parsed_uri scheme server path in + let qemu_uri + VCenter.map_source_to_uri readahead dcPath password + parsed_uri scheme server path 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. @@ -212,9 +129,8 @@ object | Some orig_path -> let readahead = readahead_for_copying in let backing_qemu_uri - map_source_to_uri ?readahead - dcPath password - parsed_uri scheme server orig_path in + VCenter.map_source_to_uri readahead dcPath password + parsed_uri scheme server orig_path in (* Rebase the qcow2 overlay to adjust the readahead parameter. *) let cmd diff --git a/v2v/vCenter.ml b/v2v/vCenter.ml index 9ff9415..49b3f32 100644 --- a/v2v/vCenter.ml +++ b/v2v/vCenter.ml @@ -21,6 +21,7 @@ open Printf open Common_utils open Common_gettext.Gettext +open Utils open Xml (* Memoized session cookie. *) @@ -156,3 +157,76 @@ let guess_dcPath uri = function default_dc | _ -> (* Don't know, so guess. *) default_dc + +let source_re = Str.regexp "^\\[\\(.*\\)\\] \\(.*\\)\\.vmdk$" + +let map_source_to_uri readahead dcPath password uri scheme server path + if not (Str.string_match source_re path 0) then + path + else ( + let datastore = Str.matched_group 1 path + and path = Str.matched_group 2 path in + + let port + match uri.uri_port with + | 443 -> "" + | n when n >= 1 -> ":" ^ string_of_int n + | _ -> "" in + + (* XXX Old virt-v2v could also handle snapshots, ie: + * "[datastore1] Fedora 20/Fedora 20-NNNNNN.vmdk" + * XXX Need to handle templates. The file is called "-delta.vmdk" in + * place of "-flat.vmdk". + *) + let url + sprintf + "https://%s%s/folder/%s-flat.vmdk?dcPath=%s&dsName=%s" + server port + (uri_quote path) (uri_quote dcPath) (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 password scheme uri sslverify url in + + (* Construct the JSON parameters. *) + let json_params = [ + "file.driver", JSON.String "https"; + "file.url", JSON.String url; + (* https://bugzilla.redhat.com/show_bug.cgi?id=1146007#c10 *) + "file.timeout", JSON.Int 2000; + ] in + + let json_params + match readahead with + | None -> json_params + | Some readahead -> + ("file.readahead", JSON.Int readahead) :: json_params in + + let json_params + if sslverify then json_params + else ("file.sslverify", JSON.String "off") :: json_params in + + let json_params + match session_cookie with + | None -> json_params + | Some cookie -> ("file.cookie", JSON.String cookie) :: json_params in + + if verbose () then + printf "vcenter: json parameters: %s\n" (JSON.string_of_doc json_params); + + (* Turn the JSON parameters into a 'json:' protocol string. + * Note this requires qemu-img >= 2.1.0. + *) + let qemu_uri = "json: " ^ JSON.string_of_doc json_params in + + qemu_uri + ) diff --git a/v2v/vCenter.mli b/v2v/vCenter.mli index 87583c0..15b5143 100644 --- a/v2v/vCenter.mli +++ b/v2v/vCenter.mli @@ -42,3 +42,13 @@ val guess_dcPath : Xml.uri -> string -> string This function is only used with [libvirt < 1.2.20] because later versions of libvirt provide the dcPath (see https://bugzilla.redhat.com/1263574). *) + +val map_source_to_uri : int option -> string -> string option -> Xml.uri -> string -> string -> string -> string +(** [map_source_to_uri readahead dcPath password uri scheme server path] + maps the [<source path=...>] string to a qemu URI. + + The [path] will be something like: + + ["[datastore1] Fedora 20/Fedora 20.vmdk"] + + including those literal spaces in the string. *) -- 2.5.0
Reasonably Related Threads
- [PATCH 0/5] v2v: Handle disks with snapshots (RHBZ#1172425).
- [PATCH 0/2] v2v: Add --password-file parameter (RHBZ#1158526).
- [PATCH v2v 0/4] v2v: vcenter: Implement cookie scripts.
- [PATCH 0/2] v2v: vcenter: Calculate dcPath correctly (RHBZ#1256823).
- [PATCH 0/4] v2v: Add a new tool virt-v2v-copy-to-local to handle Xen and ESXi