File: vCenter.ml

package info (click to toggle)
libguestfs 1%3A1.28.1-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 65,676 kB
  • ctags: 42,708
  • sloc: ansic: 374,827; ml: 40,236; sh: 19,721; java: 8,493; perl: 8,244; makefile: 5,740; cs: 5,602; haskell: 5,088; python: 2,591; erlang: 2,197; xml: 1,494; ruby: 271; pascal: 218; yacc: 123; lex: 110; cpp: 10
file content (207 lines) | stat: -rw-r--r-- 6,632 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
(* virt-v2v
 * Copyright (C) 2009-2014 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 ESX. *)

open Common_gettext.Gettext
open Common_utils

open Xml
open Utils

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
 *
 *   "[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 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 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 url =
      sprintf
        "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 scheme uri sslverify url in

    (* Construct the JSON parameters. *)
    let json_params = [
      "file.driver", JSON.String "https";
      "file.url", JSON.String url;
      "file.timeout", JSON.Int 60;
      (* Choose a large readahead.  See: RHBZ#1151033 *)
      "file.readahead", JSON.Int (64 * 1024 * 1024);
    ] 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 "esx: 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

    (* 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 scheme 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;
      if uri.uri_user <> None then
        error (f_"esx: incorrect username or password")
      else
        error (f_"esx: 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_"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
  )