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