mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
v2v: Implement -i vmx to read VMware vmx files directly (RHBZ#1441197).
This is a mostly complete implementation of a VMX parser and input class for virt-v2v. It parses the name, memory size, CPU topology, firmware, video, sound, hard disks, removable disks and network interfaces from the VMX file. It only omits support for floppies and SCSI CD-ROMs. The input class is split into two major parts: a generic VMX file parser (Parse_vmx), and the Input_vmx module which translates the VMX tree into the source device model. This also contains tests. There are simple unit tests of the Parse_vmx module, and also some more complete parsing tests taken from real guests.
This commit is contained in:
@@ -39,6 +39,7 @@ SOURCES_MLI = \
|
||||
input_libvirt_xen_ssh.mli \
|
||||
input_libvirtxml.mli \
|
||||
input_ova.mli \
|
||||
input_vmx.mli \
|
||||
inspect_source.mli \
|
||||
libvirt_utils.mli \
|
||||
linux.mli \
|
||||
@@ -55,6 +56,7 @@ SOURCES_MLI = \
|
||||
output_vdsm.mli \
|
||||
parse_ovf_from_ova.mli \
|
||||
parse_libvirt_xml.mli \
|
||||
parse_vmx.mli \
|
||||
qemu_command.mli \
|
||||
target_bus_assignment.mli \
|
||||
types.mli \
|
||||
@@ -80,6 +82,7 @@ SOURCES_ML = \
|
||||
windows_virtio.ml \
|
||||
modules_list.ml \
|
||||
input_disk.ml \
|
||||
parse_vmx.ml \
|
||||
parse_libvirt_xml.ml \
|
||||
create_libvirt_xml.ml \
|
||||
qemu_command.ml \
|
||||
@@ -89,6 +92,7 @@ SOURCES_ML = \
|
||||
input_libvirt_xen_ssh.ml \
|
||||
input_libvirt.ml \
|
||||
input_ova.ml \
|
||||
input_vmx.ml \
|
||||
linux_bootloaders.ml \
|
||||
linux_kernels.ml \
|
||||
convert_linux.ml \
|
||||
@@ -268,6 +272,7 @@ TESTS = \
|
||||
test-v2v-i-ova-subfolders.sh \
|
||||
test-v2v-i-ova-tar.sh \
|
||||
test-v2v-i-ova-two-disks.sh \
|
||||
test-v2v-i-vmx.sh \
|
||||
test-v2v-bad-networks-and-bridges.sh
|
||||
|
||||
if HAVE_LIBVIRT
|
||||
@@ -412,6 +417,15 @@ EXTRA_DIST += \
|
||||
test-v2v-i-ova.ovf \
|
||||
test-v2v-i-ova.sh \
|
||||
test-v2v-i-ova.xml \
|
||||
test-v2v-i-vmx.sh \
|
||||
test-v2v-i-vmx-1.expected \
|
||||
test-v2v-i-vmx-2.expected \
|
||||
test-v2v-i-vmx-3.expected \
|
||||
test-v2v-i-vmx-4.expected \
|
||||
test-v2v-i-vmx-1.vmx \
|
||||
test-v2v-i-vmx-2.vmx \
|
||||
test-v2v-i-vmx-3.vmx \
|
||||
test-v2v-i-vmx-4.vmx \
|
||||
test-v2v-in-place.sh \
|
||||
test-v2v-machine-readable.sh \
|
||||
test-v2v-networks-and-bridges-expected.xml \
|
||||
@@ -452,6 +466,7 @@ v2v_unit_tests_BOBJECTS = \
|
||||
windows.cmo \
|
||||
windows_virtio.cmo \
|
||||
linux.cmo \
|
||||
parse_vmx.cmo \
|
||||
v2v_unit_tests.cmo
|
||||
v2v_unit_tests_XOBJECTS = $(v2v_unit_tests_BOBJECTS:.cmo=.cmx)
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ let parse_cmdline () =
|
||||
| "libvirt" -> input_mode := `Libvirt
|
||||
| "libvirtxml" -> input_mode := `LibvirtXML
|
||||
| "ova" -> input_mode := `OVA
|
||||
| "vmx" -> input_mode := `VMX
|
||||
| s ->
|
||||
error (f_"unknown -i option: %s") s
|
||||
in
|
||||
@@ -332,7 +333,16 @@ read the man page virt-v2v(1).
|
||||
| [filename] -> filename
|
||||
| _ ->
|
||||
error (f_"expecting an OVA file name on the command line") in
|
||||
Input_ova.input_ova filename in
|
||||
Input_ova.input_ova filename
|
||||
|
||||
| `VMX ->
|
||||
(* -i vmx: Expecting an vmx filename. *)
|
||||
let filename =
|
||||
match args with
|
||||
| [filename] -> filename
|
||||
| _ ->
|
||||
error (f_"expecting a VMX file name on the command line") in
|
||||
Input_vmx.input_vmx filename in
|
||||
|
||||
(* Common error message. *)
|
||||
let error_option_cannot_be_used_in_output_mode mode opt =
|
||||
|
||||
366
v2v/input_vmx.ml
Normal file
366
v2v/input_vmx.ml
Normal file
@@ -0,0 +1,366 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2017 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 Scanf
|
||||
|
||||
open Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Types
|
||||
open Utils
|
||||
open Name_from_disk
|
||||
|
||||
external identity : 'a -> 'a = "%identity"
|
||||
|
||||
let rec find_disks vmx vmx_filename =
|
||||
find_scsi_disks vmx vmx_filename @ find_ide_disks vmx vmx_filename
|
||||
|
||||
(* Find all SCSI hard disks.
|
||||
*
|
||||
* In the VMX file:
|
||||
* scsi0.virtualDev = "pvscsi" # or may be "lsilogic" etc.
|
||||
* scsi0:0.deviceType = "scsi-hardDisk"
|
||||
* scsi0:0.fileName = "guest.vmdk"
|
||||
*)
|
||||
and find_scsi_disks vmx vmx_filename =
|
||||
let get_scsi_controller_target ns =
|
||||
sscanf ns "scsi%d:%d" (fun c t -> c, t)
|
||||
in
|
||||
let is_scsi_controller_target ns =
|
||||
try ignore (get_scsi_controller_target ns); true
|
||||
with Scanf.Scan_failure _ | End_of_file | Failure _ -> false
|
||||
in
|
||||
let scsi_device_types = [ "scsi-harddisk" ] in
|
||||
let scsi_controller = Source_SCSI in
|
||||
|
||||
find_hdds vmx vmx_filename
|
||||
get_scsi_controller_target is_scsi_controller_target
|
||||
scsi_device_types scsi_controller
|
||||
|
||||
(* Find all IDE hard disks.
|
||||
*
|
||||
* In the VMX file:
|
||||
* ide0:0.deviceType = "ata-hardDisk"
|
||||
* ide0:0.fileName = "guest.vmdk"
|
||||
*)
|
||||
and find_ide_disks vmx vmx_filename =
|
||||
let get_ide_controller_target ns =
|
||||
sscanf ns "ide%d:%d" (fun c t -> c, t)
|
||||
in
|
||||
let is_ide_controller_target ns =
|
||||
try ignore (get_ide_controller_target ns); true
|
||||
with Scanf.Scan_failure _ | End_of_file | Failure _ -> false
|
||||
in
|
||||
let ide_device_types = [ "ata-harddisk" ] in
|
||||
let ide_controller = Source_IDE in
|
||||
|
||||
find_hdds vmx vmx_filename
|
||||
get_ide_controller_target is_ide_controller_target
|
||||
ide_device_types ide_controller
|
||||
|
||||
and find_hdds vmx vmx_filename
|
||||
get_controller_target is_controller_target
|
||||
device_types controller =
|
||||
(* Find namespaces matching '(ide|scsi)X:Y' with suitable deviceType. *)
|
||||
let hdds =
|
||||
Parse_vmx.select_namespaces (
|
||||
function
|
||||
| [ns] ->
|
||||
(* Check the namespace is '(ide|scsi)X:Y' *)
|
||||
if not (is_controller_target ns) then false
|
||||
else (
|
||||
(* Check the deviceType is one we are looking for. *)
|
||||
match Parse_vmx.get_string vmx [ns; "deviceType"] with
|
||||
| Some str ->
|
||||
let str = String.lowercase_ascii str in
|
||||
List.mem str device_types
|
||||
| None -> false
|
||||
)
|
||||
| _ -> false
|
||||
) vmx in
|
||||
|
||||
(* Map the subset to a list of disks. *)
|
||||
let hdds =
|
||||
Parse_vmx.map (
|
||||
fun path v ->
|
||||
match path, v with
|
||||
| [ns; "filename"], Some filename ->
|
||||
let c, t = get_controller_target ns in
|
||||
let s = { s_disk_id = (-1);
|
||||
s_qemu_uri = qemu_uri_of_filename vmx_filename filename;
|
||||
s_format = Some "vmdk";
|
||||
s_controller = Some controller } in
|
||||
Some (c, t, s)
|
||||
| _ -> None
|
||||
) hdds in
|
||||
let hdds = filter_map identity hdds in
|
||||
|
||||
(* We don't have a way to return the controllers and targets, so
|
||||
* just make sure the disks are sorted into order, since Parse_vmx
|
||||
* won't return them in any particular order.
|
||||
*)
|
||||
let hdds = List.sort compare hdds in
|
||||
let hdds = List.map (fun (_, _, source) -> source) hdds in
|
||||
|
||||
(* Set the s_disk_id field to an incrementing number. *)
|
||||
let hdds = mapi (fun i source -> { source with s_disk_id = i }) hdds in
|
||||
|
||||
hdds
|
||||
|
||||
(* The filename can be an absolute path, but is more often a
|
||||
* path relative to the location of the vmx file.
|
||||
*
|
||||
* Note that we always end up with an absolute path, which is
|
||||
* also useful because it means we won't have any paths that
|
||||
* could be misinterpreted by qemu.
|
||||
*)
|
||||
and qemu_uri_of_filename vmx_filename filename =
|
||||
if not (Filename.is_relative filename) then
|
||||
filename
|
||||
else (
|
||||
let dir = Filename.dirname (absolute_path vmx_filename) in
|
||||
dir // filename
|
||||
)
|
||||
|
||||
(* Find all removable disks.
|
||||
*
|
||||
* In the VMX file:
|
||||
* ide1:0.deviceType = "cdrom-image"
|
||||
* ide1:0.fileName = "boot.iso"
|
||||
*
|
||||
* XXX This only supports IDE CD-ROMs, but we could support SCSI
|
||||
* CD-ROMs and floppies in future.
|
||||
*)
|
||||
and find_removables vmx =
|
||||
let get_ide_controller_target ns =
|
||||
sscanf ns "ide%d:%d" (fun c t -> c, t)
|
||||
in
|
||||
let is_ide_controller_target ns =
|
||||
try ignore (get_ide_controller_target ns); true
|
||||
with Scanf.Scan_failure _ | End_of_file | Failure _ -> false
|
||||
in
|
||||
let device_types = [ "atapi-cdrom";
|
||||
"cdrom-image"; "cdrom-raw" ] in
|
||||
|
||||
(* Find namespaces matching 'ideX:Y' with suitable deviceType. *)
|
||||
let devs =
|
||||
Parse_vmx.select_namespaces (
|
||||
function
|
||||
| [ns] ->
|
||||
(* Check the namespace is 'ideX:Y' *)
|
||||
if not (is_ide_controller_target ns) then false
|
||||
else (
|
||||
(* Check the deviceType is one we are looking for. *)
|
||||
match Parse_vmx.get_string vmx [ns; "deviceType"] with
|
||||
| Some str ->
|
||||
let str = String.lowercase_ascii str in
|
||||
List.mem str device_types
|
||||
| None -> false
|
||||
)
|
||||
| _ -> false
|
||||
) vmx in
|
||||
|
||||
(* Map the subset to a list of CD-ROMs. *)
|
||||
let devs =
|
||||
Parse_vmx.map (
|
||||
fun path v ->
|
||||
match path, v with
|
||||
| [ns], None ->
|
||||
let c, t = get_ide_controller_target ns in
|
||||
let s = { s_removable_type = CDROM;
|
||||
s_removable_controller = Some Source_IDE;
|
||||
s_removable_slot = Some (ide_slot c t) } in
|
||||
Some s
|
||||
| _ -> None
|
||||
) devs in
|
||||
let devs = filter_map identity devs in
|
||||
|
||||
(* Sort by slot. *)
|
||||
let devs =
|
||||
List.sort
|
||||
(fun { s_removable_slot = s1 } { s_removable_slot = s2 } ->
|
||||
compare s1 s2)
|
||||
devs in
|
||||
|
||||
devs
|
||||
|
||||
and ide_slot c t =
|
||||
(* Assuming the old master/slave arrangement. *)
|
||||
c * 2 + t
|
||||
|
||||
(* Find all ethernet cards.
|
||||
*
|
||||
* In the VMX file:
|
||||
* ethernet0.virtualDev = "vmxnet3"
|
||||
* ethernet0.networkName = "VM Network"
|
||||
* ethernet0.generatedAddress = "00:01:02:03:04:05"
|
||||
* ethernet0.connectionType = "bridged" # also: "custom", "nat" or not present
|
||||
*)
|
||||
and find_nics vmx =
|
||||
let get_ethernet_port ns =
|
||||
sscanf ns "ethernet%d" (fun p -> p)
|
||||
in
|
||||
let is_ethernet_port ns =
|
||||
try ignore (get_ethernet_port ns); true
|
||||
with Scanf.Scan_failure _ | End_of_file | Failure _ -> false
|
||||
in
|
||||
|
||||
(* Find namespaces matching 'ethernetX'. *)
|
||||
let nics =
|
||||
Parse_vmx.select_namespaces (
|
||||
function
|
||||
| [ns] -> is_ethernet_port ns
|
||||
| _ -> false
|
||||
) vmx in
|
||||
|
||||
(* Map the subset to a list of NICs. *)
|
||||
let nics =
|
||||
Parse_vmx.map (
|
||||
fun path v ->
|
||||
match path, v with
|
||||
| [ns], None ->
|
||||
let port = get_ethernet_port ns in
|
||||
let mac = Parse_vmx.get_string vmx [ns; "generatedAddress"] in
|
||||
let model = Parse_vmx.get_string vmx [ns; "virtualDev"] in
|
||||
let model =
|
||||
match model with
|
||||
| Some m when String.lowercase_ascii m = "e1000" ->
|
||||
Some Source_e1000
|
||||
| Some model ->
|
||||
Some (Source_other_nic (String.lowercase_ascii model))
|
||||
| None -> None in
|
||||
let vnet = Parse_vmx.get_string vmx [ns; "networkName"] in
|
||||
let vnet =
|
||||
match vnet with
|
||||
| Some vnet -> vnet
|
||||
| None -> ns (* "ethernetX" *) in
|
||||
let vnet_type =
|
||||
match Parse_vmx.get_string vmx [ns; "connectionType"] with
|
||||
| Some b when String.lowercase_ascii b = "bridged" ->
|
||||
Bridge
|
||||
| Some _ | None -> Network in
|
||||
Some (port,
|
||||
{ s_mac = mac; s_nic_model = model;
|
||||
s_vnet = vnet; s_vnet_orig = vnet;
|
||||
s_vnet_type = vnet_type })
|
||||
| _ -> None
|
||||
) nics in
|
||||
let nics = filter_map identity nics in
|
||||
|
||||
(* Sort by port. *)
|
||||
let nics = List.sort compare nics in
|
||||
|
||||
let nics = List.map (fun (_, source) -> source) nics in
|
||||
nics
|
||||
|
||||
class input_vmx vmx_filename = object
|
||||
inherit input
|
||||
|
||||
method as_options = "-i vmx " ^ vmx_filename
|
||||
|
||||
method source () =
|
||||
(* Parse the VMX file. *)
|
||||
let vmx = Parse_vmx.parse_file vmx_filename in
|
||||
|
||||
let name =
|
||||
match Parse_vmx.get_string vmx ["displayName"] with
|
||||
| None ->
|
||||
warning (f_"no displayName key found in VMX file");
|
||||
name_from_disk vmx_filename
|
||||
| Some s -> s in
|
||||
|
||||
let memory_mb =
|
||||
match Parse_vmx.get_int64 vmx ["memSize"] with
|
||||
| None -> 32_L (* default is really 32 MB! *)
|
||||
| Some i -> i in
|
||||
let memory = memory_mb *^ 1024L *^ 1024L in
|
||||
|
||||
let vcpu =
|
||||
match Parse_vmx.get_int vmx ["numvcpus"] with
|
||||
| None -> 1
|
||||
| Some i -> i in
|
||||
|
||||
let cpu_sockets, cpu_cores =
|
||||
match Parse_vmx.get_int vmx ["cpuid"; "coresPerSocket"] with
|
||||
| None -> None, None
|
||||
| Some cores_per_socket ->
|
||||
let sockets = vcpu / cores_per_socket in
|
||||
if sockets <= 0 then (
|
||||
warning (f_"invalid cpuid.coresPerSocket < number of vCPUs");
|
||||
None, None
|
||||
)
|
||||
else
|
||||
Some sockets, Some cores_per_socket in
|
||||
|
||||
let firmware =
|
||||
match Parse_vmx.get_string vmx ["firmware"] with
|
||||
| None -> BIOS
|
||||
| Some "efi" -> UEFI
|
||||
(* Other values are not documented for this field ... *)
|
||||
| Some fw ->
|
||||
warning (f_"unknown firmware value '%s', assuming BIOS") fw;
|
||||
BIOS in
|
||||
|
||||
let video =
|
||||
if Parse_vmx.namespace_present vmx ["svga"] then
|
||||
(* We could also parse svga.vramSize. *)
|
||||
Some (Source_other_video "vmvga")
|
||||
else
|
||||
None in
|
||||
|
||||
let sound =
|
||||
match Parse_vmx.get_string vmx ["sound"; "virtualDev"] with
|
||||
| Some ("sb16") -> Some { s_sound_model = SB16 }
|
||||
| Some ("es1371") -> Some { s_sound_model = ES1370 (* hmmm ... *) }
|
||||
| Some "hdaudio" -> Some { s_sound_model = ICH6 (* intel-hda *) }
|
||||
| Some model ->
|
||||
warning (f_"unknown sound device '%s' ignored") model;
|
||||
None
|
||||
| None -> None in
|
||||
|
||||
let disks = find_disks vmx vmx_filename in
|
||||
let removables = find_removables vmx in
|
||||
let nics = find_nics vmx in
|
||||
|
||||
let source = {
|
||||
s_hypervisor = VMware;
|
||||
s_name = name;
|
||||
s_orig_name = name;
|
||||
s_memory = memory;
|
||||
s_vcpu = vcpu;
|
||||
s_cpu_vendor = None;
|
||||
s_cpu_model = None;
|
||||
s_cpu_sockets = cpu_sockets;
|
||||
s_cpu_cores = cpu_cores;
|
||||
s_cpu_threads = None;
|
||||
s_features = [];
|
||||
s_firmware = firmware;
|
||||
s_display = None;
|
||||
s_video = video;
|
||||
s_sound = sound;
|
||||
s_disks = disks;
|
||||
s_removables = removables;
|
||||
s_nics = nics;
|
||||
} in
|
||||
|
||||
source
|
||||
end
|
||||
|
||||
let input_vmx = new input_vmx
|
||||
let () = Modules_list.register_input_module "vmx"
|
||||
22
v2v/input_vmx.mli
Normal file
22
v2v/input_vmx.mli
Normal file
@@ -0,0 +1,22 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2017 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.
|
||||
*)
|
||||
|
||||
(** [-i vmx] source. *)
|
||||
|
||||
val input_vmx : string -> Types.input
|
||||
(** [input_vmx filename] sets up an input from vmware vmx file. *)
|
||||
@@ -24,7 +24,7 @@ let name_from_disk disk =
|
||||
(* Remove the extension (or suffix), only if it's one usually
|
||||
* used for disk images. *)
|
||||
let suffixes = [
|
||||
".img"; ".ova"; ".qcow2"; ".raw"; ".vmdk";
|
||||
".img"; ".ova"; ".qcow2"; ".raw"; ".vmdk"; ".vmx";
|
||||
"-sda";
|
||||
] in
|
||||
let rec loop = function
|
||||
|
||||
381
v2v/parse_vmx.ml
Normal file
381
v2v/parse_vmx.ml
Normal file
@@ -0,0 +1,381 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2017 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
|
||||
|
||||
(* As far as I can tell the VMX format is totally unspecified.
|
||||
* However libvirt has a useful selection of .vmx files in the
|
||||
* sources which explore some of the darker regions of this
|
||||
* format.
|
||||
*
|
||||
* So here are some facts about VMX derived from libvirt and
|
||||
* other places:
|
||||
*
|
||||
* - Keys are compared case insensitively. We assume here
|
||||
* that keys are 7-bit ASCII.
|
||||
*
|
||||
* - Multiple keys with the same name are not allowed.
|
||||
*
|
||||
* - Escaping in the value string is possible using a very weird
|
||||
* escape format: "|22" means the character '\x22'. To write
|
||||
* a pipe character you must use "|7C".
|
||||
*
|
||||
* - Boolean values are written "TRUE", "FALSE", "True", "true", etc.
|
||||
* Because of the quotes they cannot be distinguished from strings.
|
||||
*
|
||||
* - Comments (#...) and blank lines are ignored. Some files start
|
||||
* with a hash-bang path, but we ignore those as comments. This
|
||||
* parser also ignores any other line which it doesn't understand,
|
||||
* but will print a warning.
|
||||
*
|
||||
* - Multi-line values are not permitted.
|
||||
*
|
||||
* - Keys are namespaced using dots, eg. scsi0:0.deviceType has
|
||||
* the namespace "scsi0:0" and the key name "deviceType".
|
||||
*
|
||||
* - Using namespace.present = "FALSE" means that all other keys
|
||||
* in and under the namespace are ignored.
|
||||
*
|
||||
* - You cannot have a namespace and a key with the same name, eg.
|
||||
* this is not allowed:
|
||||
* namespace = "some value"
|
||||
* namespace.foo = "another value"
|
||||
*
|
||||
* - The Hashicorp packer VMX writer considers some special keys
|
||||
* as not requiring any quotes around their values, but I'm
|
||||
* ignoring that for now.
|
||||
*)
|
||||
|
||||
(* This VMX file:
|
||||
*
|
||||
* foo.a = "abc"
|
||||
* foo.b = "def"
|
||||
* foo.bar.c = "abc"
|
||||
* foo.bar.d = "def"
|
||||
*
|
||||
* would be represented by this structure:
|
||||
*
|
||||
* "foo" => Namespace ( # "foo" is a namespace
|
||||
* "a" => Key "abc"; # "foo.a" is a key with value "abc"
|
||||
* "b" => Key "def";
|
||||
* "bar" => Namespace ( # "foo.bar" is another namespace
|
||||
* "c" => Key "abc";
|
||||
* "d" => Key "def";
|
||||
* )
|
||||
* )
|
||||
* ‘( => )’s represent the StringMap type.
|
||||
*)
|
||||
type t = key StringMap.t
|
||||
|
||||
and key =
|
||||
| Key of string
|
||||
| Namespace of t
|
||||
|
||||
let empty = StringMap.empty
|
||||
|
||||
(* Compare two trees for equality. *)
|
||||
let rec equal vmx1 vmx2 =
|
||||
let cmp k1 k2 =
|
||||
match k1, k2 with
|
||||
| Key v1, Key v2 -> v1 = v2
|
||||
| Key _, Namespace _ -> false
|
||||
| Namespace _, Key _ -> false
|
||||
| Namespace vmx1, Namespace vmx2 -> equal vmx1 vmx2
|
||||
in
|
||||
StringMap.equal cmp vmx1 vmx2
|
||||
|
||||
(* Higher-order functions. *)
|
||||
let rec select_namespaces pred vmx =
|
||||
_select_namespaces [] pred vmx
|
||||
|
||||
and _select_namespaces path pred vmx =
|
||||
StringMap.fold (
|
||||
fun k v new_vmx ->
|
||||
let path = path @ [k] in
|
||||
match v with
|
||||
| Key _ -> new_vmx
|
||||
| Namespace _ when pred path ->
|
||||
StringMap.add k v new_vmx
|
||||
| Namespace t ->
|
||||
let t = _select_namespaces path pred t in
|
||||
if not (equal t empty) then
|
||||
StringMap.add k (Namespace t) new_vmx
|
||||
else
|
||||
new_vmx
|
||||
) vmx empty
|
||||
|
||||
let rec map f vmx =
|
||||
_map [] f vmx
|
||||
|
||||
and _map path f vmx =
|
||||
StringMap.fold (
|
||||
fun k v r ->
|
||||
let path = path @ [k] in
|
||||
match v with
|
||||
| Key v -> r @ [ f path (Some v) ]
|
||||
| Namespace t -> r @ [ f path None ] @ _map path f t
|
||||
) vmx []
|
||||
|
||||
let rec namespace_present vmx = function
|
||||
| [] -> false
|
||||
| [ns] ->
|
||||
let ns = String.lowercase_ascii ns in
|
||||
(try
|
||||
let v = StringMap.find ns vmx in
|
||||
match v with
|
||||
| Key _ -> false
|
||||
| Namespace _ -> true
|
||||
with
|
||||
Not_found -> false
|
||||
)
|
||||
| ns :: path ->
|
||||
let ns = String.lowercase_ascii ns in
|
||||
(try
|
||||
let v = StringMap.find ns vmx in
|
||||
match v with
|
||||
| Key _ -> false
|
||||
| Namespace vmx -> namespace_present vmx path
|
||||
with
|
||||
Not_found -> false
|
||||
)
|
||||
|
||||
(* Dump the vmx structure to [chan]. Used for debugging. *)
|
||||
let rec print chan indent vmx =
|
||||
StringMap.iter (print_key chan indent) vmx
|
||||
|
||||
and print_key chan indent k = function
|
||||
| Key v ->
|
||||
output_spaces chan indent;
|
||||
fprintf chan "%s = \"%s\"\n" k v
|
||||
| Namespace vmx ->
|
||||
output_spaces chan indent;
|
||||
fprintf chan "namespace '%s':\n" k;
|
||||
print chan (indent+4) vmx
|
||||
|
||||
(* As above, but creates a string instead. *)
|
||||
let rec to_string indent vmx =
|
||||
StringMap.fold (fun k v str -> str ^ to_string_key indent k v) vmx ""
|
||||
|
||||
and to_string_key indent k = function
|
||||
| Key v ->
|
||||
String.spaces indent ^ sprintf "%s = \"%s\"\n" k v
|
||||
| Namespace vmx ->
|
||||
String.spaces indent ^ sprintf "namespace '%s':\n" k ^
|
||||
to_string (indent+4) vmx
|
||||
|
||||
(* Access keys in the tree. *)
|
||||
let rec get_string vmx = function
|
||||
| [] -> None
|
||||
| [k] ->
|
||||
let k = String.lowercase_ascii k in
|
||||
(try
|
||||
let v = StringMap.find k vmx in
|
||||
match v with
|
||||
| Key v -> Some v
|
||||
| Namespace _ -> None
|
||||
with Not_found -> None
|
||||
)
|
||||
| ns :: path ->
|
||||
let ns = String.lowercase_ascii ns in
|
||||
(try
|
||||
let v = StringMap.find ns vmx in
|
||||
match v with
|
||||
| Key v -> None
|
||||
| Namespace vmx -> get_string vmx path
|
||||
with
|
||||
Not_found -> None
|
||||
)
|
||||
|
||||
let get_int64 vmx path =
|
||||
match get_string vmx path with
|
||||
| None -> None
|
||||
| Some i -> Some (Int64.of_string i)
|
||||
|
||||
let get_int vmx path =
|
||||
match get_string vmx path with
|
||||
| None -> None
|
||||
| Some i -> Some (int_of_string i)
|
||||
|
||||
let rec get_bool vmx path =
|
||||
match get_string vmx path with
|
||||
| None -> None
|
||||
| Some t -> Some (vmx_bool_of_string t)
|
||||
|
||||
and vmx_bool_of_string t =
|
||||
if String.lowercase_ascii t = "true" then true
|
||||
else if String.lowercase_ascii t = "false" then false
|
||||
else failwith "bool_of_string"
|
||||
|
||||
(* Regular expression used to match key = "value" in VMX file. *)
|
||||
let rex = Str.regexp "^\\([^ \t=]+\\)[ \t]*=[ \t]*\"\\(.*\\)\"$"
|
||||
|
||||
(* Remove the weird escapes used in value strings. See description above. *)
|
||||
let remove_vmx_escapes str =
|
||||
let len = String.length str in
|
||||
let out = Bytes.make len '\000' in
|
||||
let j = ref 0 in
|
||||
|
||||
let rec loop i =
|
||||
if i >= len then ()
|
||||
else (
|
||||
let c = String.unsafe_get str i in
|
||||
if i <= len-3 && c = '|' then (
|
||||
let c1 = str.[i+1] and c2 = str.[i+2] in
|
||||
if Char.isxdigit c1 && Char.isxdigit c2 then (
|
||||
let x = Char.hexdigit c1 * 0x10 + Char.hexdigit c2 in
|
||||
Bytes.set out !j (Char.chr x);
|
||||
incr j;
|
||||
loop (i+3)
|
||||
)
|
||||
else (
|
||||
Bytes.set out !j c;
|
||||
incr j;
|
||||
loop (i+1)
|
||||
)
|
||||
)
|
||||
else (
|
||||
Bytes.set out !j c;
|
||||
incr j;
|
||||
loop (i+1)
|
||||
)
|
||||
)
|
||||
in
|
||||
loop 0;
|
||||
|
||||
(* Truncate the output string to its real size and return it
|
||||
* as an immutable string.
|
||||
*)
|
||||
Bytes.sub_string out 0 !j
|
||||
|
||||
(* Parsing. *)
|
||||
let rec parse_file vmx_filename =
|
||||
(* Read the whole file as a list of lines. *)
|
||||
let str = read_whole_file vmx_filename in
|
||||
if verbose () then eprintf "VMX file:\n%s\n" str;
|
||||
parse_string str
|
||||
|
||||
and parse_string str =
|
||||
let lines = String.nsplit "\n" str in
|
||||
|
||||
(* I've never seen any VMX file with CR-LF endings, and VMware
|
||||
* itself is Linux-based, but to be on the safe side ...
|
||||
*)
|
||||
let lines = List.map (String.trimr ~test:((=) '\r')) lines in
|
||||
|
||||
(* Ignore blank lines and comments. *)
|
||||
let lines = List.filter (
|
||||
fun line ->
|
||||
let line = String.triml line in
|
||||
let len = String.length line in
|
||||
len > 0 && line.[0] != '#'
|
||||
) lines in
|
||||
|
||||
(* Parse the lines into key = "value". *)
|
||||
let lines = filter_map (
|
||||
fun line ->
|
||||
if Str.string_match rex line 0 then (
|
||||
let key = Str.matched_group 1 line in
|
||||
let key = String.lowercase_ascii key in
|
||||
let value = Str.matched_group 2 line in
|
||||
let value = remove_vmx_escapes value in
|
||||
Some (key, value)
|
||||
)
|
||||
else (
|
||||
warning (f_"vmx parser: cannot parse this line, ignoring: %s") line;
|
||||
None
|
||||
)
|
||||
) lines in
|
||||
|
||||
(* Split the keys into namespace paths. *)
|
||||
let lines =
|
||||
List.map (fun (key, value) -> String.nsplit "." key, value) lines in
|
||||
|
||||
(* Build a tree from the flat list and return it. This is horribly
|
||||
* inefficient, at least O(n²), possibly even O(n².log n). Hope
|
||||
* there are no large VMX files! (XXX)
|
||||
*)
|
||||
let vmx =
|
||||
List.fold_left (
|
||||
fun vmx (path, value) -> insert vmx value path
|
||||
) empty lines in
|
||||
|
||||
(* If we're verbose, dump the parsed VMX for debugging purposes. *)
|
||||
if verbose () then (
|
||||
eprintf "parsed VMX tree:\n";
|
||||
print stderr 0 vmx
|
||||
);
|
||||
|
||||
(* Drop all present = "FALSE" namespaces. *)
|
||||
let vmx = drop_not_present vmx in
|
||||
|
||||
vmx
|
||||
|
||||
and insert vmx value = function
|
||||
| [] -> assert false
|
||||
| [k] ->
|
||||
if StringMap.mem k vmx then (
|
||||
warning (f_"vmx parser: duplicate key '%s' ignored") k;
|
||||
vmx
|
||||
) else
|
||||
StringMap.add k (Key value) vmx
|
||||
| ns :: path ->
|
||||
let v =
|
||||
try
|
||||
(match StringMap.find ns vmx with
|
||||
| Namespace vmx -> Some vmx
|
||||
| Key _ -> None
|
||||
)
|
||||
with Not_found -> None in
|
||||
let v =
|
||||
match v with
|
||||
| None ->
|
||||
(* Completely new namespace. *)
|
||||
insert empty value path
|
||||
| Some v ->
|
||||
(* Insert the subkey into the previously created namespace. *)
|
||||
insert v value path in
|
||||
StringMap.add ns (Namespace v) vmx
|
||||
|
||||
(* Find any "present" keys. If we find present = "FALSE", then
|
||||
* drop the containing namespace and all subkeys and subnamespaces.
|
||||
*)
|
||||
and drop_not_present vmx =
|
||||
StringMap.fold (
|
||||
fun k v new_vmx ->
|
||||
match v with
|
||||
| Key _ ->
|
||||
StringMap.add k v new_vmx
|
||||
| Namespace vmx when contains_key_present_false vmx ->
|
||||
(* drop this namespace and all sub-spaces *)
|
||||
new_vmx
|
||||
| Namespace v ->
|
||||
(* recurse into sub-namespace and do the same check *)
|
||||
let v = drop_not_present v in
|
||||
StringMap.add k (Namespace v) new_vmx
|
||||
) vmx empty
|
||||
|
||||
and contains_key_present_false vmx =
|
||||
try
|
||||
match StringMap.find "present" vmx with
|
||||
| Key v when vmx_bool_of_string v = false -> true
|
||||
| Key _ | Namespace _ -> false
|
||||
with
|
||||
Failure _ | Not_found -> false
|
||||
89
v2v/parse_vmx.mli
Normal file
89
v2v/parse_vmx.mli
Normal file
@@ -0,0 +1,89 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2017 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.
|
||||
*)
|
||||
|
||||
(** A simple parser for VMware [.vmx] files. *)
|
||||
|
||||
type t
|
||||
|
||||
val parse_file : string -> t
|
||||
(** [parse_file filename] parses a VMX file. *)
|
||||
|
||||
val parse_string : string -> t
|
||||
(** [parse_string s] parses VMX from a string. *)
|
||||
|
||||
val get_string : t -> string list -> string option
|
||||
(** Find a key and return it as a string. If not present, returns [None].
|
||||
|
||||
Note that if [namespace.present = "FALSE"] is found in the file
|
||||
then all keys in [namespace] and below it are ignored. This
|
||||
applies to all [get_*] functions. *)
|
||||
|
||||
val get_int64 : t -> string list -> int64 option
|
||||
(** Find a key and return it as an [int64].
|
||||
If not present, returns [None].
|
||||
|
||||
Raises [Failure _] if the key is present but was not parseable
|
||||
as an integer. *)
|
||||
|
||||
val get_int : t -> string list -> int option
|
||||
(** Find a key and return it as an [int].
|
||||
If not present, returns [None].
|
||||
|
||||
Raises [Failure _] if the key is present but was not parseable
|
||||
as an integer. *)
|
||||
|
||||
val get_bool : t -> string list -> bool option
|
||||
(** Find a key and return it as a boolean.
|
||||
|
||||
You cannot return [namespace.present = "FALSE"] booleans this way.
|
||||
They are processed by the parser and the namespace and anything
|
||||
below it are removed from the tree.
|
||||
|
||||
Raises [Failure _] if the key is present but was not parseable
|
||||
as a boolean. *)
|
||||
|
||||
val namespace_present : t -> string list -> bool
|
||||
(** Returns true iff the namespace ({b note:} not key) is present. *)
|
||||
|
||||
val select_namespaces : (string list -> bool) -> t -> t
|
||||
(** Filter the VMX file, selecting exactly namespaces (and their
|
||||
keys) matching the predicate. The predicate is a function which
|
||||
is called on each {i namespace} path ({b note:} not on
|
||||
namespace + key paths). If the predicate matches a
|
||||
namespace, then all sub-namespaces under that namespace are
|
||||
selected implicitly. *)
|
||||
|
||||
val map : (string list -> string option -> 'a) -> t -> 'a list
|
||||
(** Map all the entries in the VMX file into a list using the
|
||||
map function. The map function takes two arguments. The
|
||||
first is the path to the namespace or key, and the second
|
||||
is the key value (or [None] if the path refers to a namespace). *)
|
||||
|
||||
val equal : t -> t -> bool
|
||||
(** Compare two VMX files for equality. This is mainly used for
|
||||
testing the parser. *)
|
||||
|
||||
val empty : t
|
||||
(** An empty VMX file. *)
|
||||
|
||||
val print : out_channel -> int -> t -> unit
|
||||
(** [print chan indent] prints the VMX file to the output channel.
|
||||
[indent] is the indentation applied to each line of output. *)
|
||||
|
||||
val to_string : int -> t -> string
|
||||
(** Same as {!print} but it creates a printable (multiline) string. *)
|
||||
42
v2v/test-v2v-i-vmx-1.expected
Normal file
42
v2v/test-v2v-i-vmx-1.expected
Normal file
@@ -0,0 +1,42 @@
|
||||
[ 0.0] Opening the source -i vmx test-v2v-i-vmx-1.vmx
|
||||
Source guest information (--print-source option):
|
||||
|
||||
source name: BZ1308535_21disks
|
||||
hypervisor type: vmware
|
||||
memory: 2147483648 (bytes)
|
||||
nr vCPUs: 1
|
||||
CPU vendor:
|
||||
CPU model:
|
||||
CPU topology: sockets: - cores/socket: - threads/core: -
|
||||
CPU features:
|
||||
firmware: bios
|
||||
display:
|
||||
video: vmvga
|
||||
sound:
|
||||
disks:
|
||||
/BZ1308535_21disks.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_1.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_2.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_3.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_4.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_5.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_6.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_7.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_8.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_9.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_10.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_11.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_12.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_13.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_14.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_15.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_16.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_17.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_18.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_19.vmdk (vmdk) [scsi]
|
||||
/BZ1308535_21disks_20.vmdk (vmdk) [scsi]
|
||||
removable media:
|
||||
CD-ROM [ide] in slot 2
|
||||
NICs:
|
||||
Network "VM Network" mac: 00:0c:29:36:ef:31 [vmxnet3]
|
||||
|
||||
172
v2v/test-v2v-i-vmx-1.vmx
Normal file
172
v2v/test-v2v-i-vmx-1.vmx
Normal file
@@ -0,0 +1,172 @@
|
||||
.encoding = "UTF-8"
|
||||
config.version = "8"
|
||||
virtualHW.version = "8"
|
||||
nvram = "BZ1308535_21disks.nvram"
|
||||
pciBridge0.present = "TRUE"
|
||||
svga.present = "TRUE"
|
||||
pciBridge4.present = "TRUE"
|
||||
pciBridge4.virtualDev = "pcieRootPort"
|
||||
pciBridge4.functions = "8"
|
||||
pciBridge5.present = "TRUE"
|
||||
pciBridge5.virtualDev = "pcieRootPort"
|
||||
pciBridge5.functions = "8"
|
||||
pciBridge6.present = "TRUE"
|
||||
pciBridge6.virtualDev = "pcieRootPort"
|
||||
pciBridge6.functions = "8"
|
||||
pciBridge7.present = "TRUE"
|
||||
pciBridge7.virtualDev = "pcieRootPort"
|
||||
pciBridge7.functions = "8"
|
||||
vmci0.present = "TRUE"
|
||||
hpet0.present = "TRUE"
|
||||
displayName = "BZ1308535_21disks"
|
||||
extendedConfigFile = "BZ1308535_21disks.vmxf"
|
||||
virtualHW.productCompatibility = "hosted"
|
||||
memSize = "2048"
|
||||
sched.cpu.units = "mhz"
|
||||
powerType.powerOff = "soft"
|
||||
powerType.suspend = "hard"
|
||||
powerType.reset = "soft"
|
||||
scsi0.virtualDev = "pvscsi"
|
||||
scsi0.present = "TRUE"
|
||||
scsi1.virtualDev = "pvscsi"
|
||||
scsi1.present = "TRUE"
|
||||
ide1:0.deviceType = "cdrom-image"
|
||||
ide1:0.fileName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/ISOs/RHEL-7.1-20150219.1-Server-x86_64-boot.iso"
|
||||
ide1:0.present = "TRUE"
|
||||
floppy0.startConnected = "FALSE"
|
||||
floppy0.clientDevice = "TRUE"
|
||||
floppy0.fileName = "vmware-null-remote-floppy"
|
||||
ethernet0.virtualDev = "vmxnet3"
|
||||
ethernet0.networkName = "VM Network"
|
||||
ethernet0.addressType = "generated"
|
||||
ethernet0.present = "TRUE"
|
||||
scsi0:0.deviceType = "scsi-hardDisk"
|
||||
scsi0:0.fileName = "BZ1308535_21disks.vmdk"
|
||||
scsi0:0.present = "TRUE"
|
||||
scsi0:1.deviceType = "scsi-hardDisk"
|
||||
scsi0:1.fileName = "BZ1308535_21disks_1.vmdk"
|
||||
scsi0:1.present = "TRUE"
|
||||
scsi0:2.deviceType = "scsi-hardDisk"
|
||||
scsi0:2.fileName = "BZ1308535_21disks_2.vmdk"
|
||||
scsi0:2.present = "TRUE"
|
||||
scsi0:3.deviceType = "scsi-hardDisk"
|
||||
scsi0:3.fileName = "BZ1308535_21disks_3.vmdk"
|
||||
scsi0:3.present = "TRUE"
|
||||
scsi0:4.deviceType = "scsi-hardDisk"
|
||||
scsi0:4.fileName = "BZ1308535_21disks_4.vmdk"
|
||||
scsi0:4.present = "TRUE"
|
||||
scsi0:5.deviceType = "scsi-hardDisk"
|
||||
scsi0:5.fileName = "BZ1308535_21disks_5.vmdk"
|
||||
scsi0:5.present = "TRUE"
|
||||
scsi0:6.deviceType = "scsi-hardDisk"
|
||||
scsi0:6.fileName = "BZ1308535_21disks_6.vmdk"
|
||||
scsi0:6.present = "TRUE"
|
||||
scsi0:8.deviceType = "scsi-hardDisk"
|
||||
scsi0:8.fileName = "BZ1308535_21disks_7.vmdk"
|
||||
scsi0:8.present = "TRUE"
|
||||
scsi0:9.deviceType = "scsi-hardDisk"
|
||||
scsi0:9.fileName = "BZ1308535_21disks_8.vmdk"
|
||||
scsi0:9.present = "TRUE"
|
||||
scsi0:10.deviceType = "scsi-hardDisk"
|
||||
scsi0:10.fileName = "BZ1308535_21disks_9.vmdk"
|
||||
scsi0:10.present = "TRUE"
|
||||
scsi0:11.deviceType = "scsi-hardDisk"
|
||||
scsi0:11.fileName = "BZ1308535_21disks_10.vmdk"
|
||||
scsi0:11.present = "TRUE"
|
||||
scsi0:12.deviceType = "scsi-hardDisk"
|
||||
scsi0:12.fileName = "BZ1308535_21disks_11.vmdk"
|
||||
scsi0:12.present = "TRUE"
|
||||
scsi0:13.deviceType = "scsi-hardDisk"
|
||||
scsi0:13.fileName = "BZ1308535_21disks_12.vmdk"
|
||||
scsi0:13.present = "TRUE"
|
||||
scsi0:14.deviceType = "scsi-hardDisk"
|
||||
scsi0:14.fileName = "BZ1308535_21disks_13.vmdk"
|
||||
scsi0:14.present = "TRUE"
|
||||
scsi0:15.deviceType = "scsi-hardDisk"
|
||||
scsi0:15.fileName = "BZ1308535_21disks_14.vmdk"
|
||||
scsi0:15.present = "TRUE"
|
||||
scsi1:0.deviceType = "scsi-hardDisk"
|
||||
scsi1:0.fileName = "BZ1308535_21disks_15.vmdk"
|
||||
scsi1:0.present = "TRUE"
|
||||
scsi1:1.deviceType = "scsi-hardDisk"
|
||||
scsi1:1.fileName = "BZ1308535_21disks_16.vmdk"
|
||||
scsi1:1.present = "TRUE"
|
||||
scsi1:2.deviceType = "scsi-hardDisk"
|
||||
scsi1:2.fileName = "BZ1308535_21disks_17.vmdk"
|
||||
scsi1:2.present = "TRUE"
|
||||
scsi1:3.deviceType = "scsi-hardDisk"
|
||||
scsi1:3.fileName = "BZ1308535_21disks_18.vmdk"
|
||||
scsi1:3.present = "TRUE"
|
||||
scsi1:4.deviceType = "scsi-hardDisk"
|
||||
scsi1:4.fileName = "BZ1308535_21disks_19.vmdk"
|
||||
scsi1:4.present = "TRUE"
|
||||
scsi1:5.deviceType = "scsi-hardDisk"
|
||||
scsi1:5.fileName = "BZ1308535_21disks_20.vmdk"
|
||||
scsi1:5.present = "TRUE"
|
||||
guestOS = "rhel6-64"
|
||||
toolScripts.afterPowerOn = "TRUE"
|
||||
toolScripts.afterResume = "TRUE"
|
||||
toolScripts.beforeSuspend = "TRUE"
|
||||
toolScripts.beforePowerOff = "TRUE"
|
||||
uuid.bios = "56 4d 96 af e6 46 bd 86-5c 4d 65 4e 77 36 ef 31"
|
||||
uuid.location = "56 4d 96 af e6 46 bd 86-5c 4d 65 4e 77 36 ef 31"
|
||||
vc.uuid = "52 31 cb fc c1 3f 96 32-83 c0 bb 70 6c 90 5c fd"
|
||||
chipset.onlineStandby = "FALSE"
|
||||
sched.cpu.min = "0"
|
||||
sched.cpu.shares = "normal"
|
||||
sched.mem.min = "0"
|
||||
sched.mem.minSize = "0"
|
||||
sched.mem.shares = "normal"
|
||||
svga.vramSize = "8388608"
|
||||
sched.swap.derivedName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/BZ1308535_21disks/BZ1308535_21disks-6a024f8a.vswp"
|
||||
replay.supported = "FALSE"
|
||||
replay.filename = ""
|
||||
scsi0:0.redo = ""
|
||||
scsi0:1.redo = ""
|
||||
scsi0:2.redo = ""
|
||||
scsi0:3.redo = ""
|
||||
scsi0:4.redo = ""
|
||||
scsi0:5.redo = ""
|
||||
scsi0:6.redo = ""
|
||||
scsi0:8.redo = ""
|
||||
scsi0:9.redo = ""
|
||||
scsi0:10.redo = ""
|
||||
scsi0:11.redo = ""
|
||||
scsi0:12.redo = ""
|
||||
scsi0:13.redo = ""
|
||||
scsi0:14.redo = ""
|
||||
scsi0:15.redo = ""
|
||||
scsi1:0.redo = ""
|
||||
scsi1:1.redo = ""
|
||||
scsi1:2.redo = ""
|
||||
scsi1:3.redo = ""
|
||||
scsi1:4.redo = ""
|
||||
scsi1:5.redo = ""
|
||||
pciBridge0.pciSlotNumber = "17"
|
||||
pciBridge4.pciSlotNumber = "21"
|
||||
pciBridge5.pciSlotNumber = "22"
|
||||
pciBridge6.pciSlotNumber = "23"
|
||||
pciBridge7.pciSlotNumber = "24"
|
||||
scsi0.pciSlotNumber = "160"
|
||||
scsi1.pciSlotNumber = "192"
|
||||
ethernet0.pciSlotNumber = "224"
|
||||
vmci0.pciSlotNumber = "32"
|
||||
scsi0.sasWWID = "50 05 05 6f e6 46 bd 80"
|
||||
scsi1.sasWWID = "50 05 05 6f e6 46 bc 80"
|
||||
ethernet0.generatedAddress = "00:0c:29:36:ef:31"
|
||||
ethernet0.generatedAddressOffset = "0"
|
||||
vmci0.id = "2000088881"
|
||||
hostCPUID.0 = "0000000d756e65476c65746e49656e69"
|
||||
hostCPUID.1 = "000206a700100800179ae3bfbfebfbff"
|
||||
hostCPUID.80000001 = "00000000000000000000000128100800"
|
||||
guestCPUID.0 = "0000000d756e65476c65746e49656e69"
|
||||
guestCPUID.1 = "000206a700010800969822030fabfbff"
|
||||
guestCPUID.80000001 = "00000000000000000000000128100800"
|
||||
userCPUID.0 = "0000000d756e65476c65746e49656e69"
|
||||
userCPUID.1 = "000206a700100800169822030fabfbff"
|
||||
userCPUID.80000001 = "00000000000000000000000128100800"
|
||||
evcCompatibilityMode = "FALSE"
|
||||
vmotion.checkpointFBSize = "8388608"
|
||||
cleanShutdown = "TRUE"
|
||||
softPowerOff = "TRUE"
|
||||
tools.remindInstall = "TRUE"
|
||||
22
v2v/test-v2v-i-vmx-2.expected
Normal file
22
v2v/test-v2v-i-vmx-2.expected
Normal file
@@ -0,0 +1,22 @@
|
||||
[ 0.0] Opening the source -i vmx test-v2v-i-vmx-2.vmx
|
||||
Source guest information (--print-source option):
|
||||
|
||||
source name: Fedora 20
|
||||
hypervisor type: vmware
|
||||
memory: 2147483648 (bytes)
|
||||
nr vCPUs: 1
|
||||
CPU vendor:
|
||||
CPU model:
|
||||
CPU topology: sockets: - cores/socket: - threads/core: -
|
||||
CPU features:
|
||||
firmware: bios
|
||||
display:
|
||||
video: vmvga
|
||||
sound:
|
||||
disks:
|
||||
/Fedora 20.vmdk (vmdk) [scsi]
|
||||
removable media:
|
||||
|
||||
NICs:
|
||||
Network "VM Network" mac: 00:50:56:9b:5f:0d [vmxnet3]
|
||||
|
||||
84
v2v/test-v2v-i-vmx-2.vmx
Normal file
84
v2v/test-v2v-i-vmx-2.vmx
Normal file
@@ -0,0 +1,84 @@
|
||||
.encoding = "UTF-8"
|
||||
config.version = "8"
|
||||
virtualHW.version = "10"
|
||||
nvram = "Fedora 20.nvram"
|
||||
pciBridge0.present = "TRUE"
|
||||
svga.present = "TRUE"
|
||||
pciBridge4.present = "TRUE"
|
||||
pciBridge4.virtualDev = "pcieRootPort"
|
||||
pciBridge4.functions = "8"
|
||||
pciBridge5.present = "TRUE"
|
||||
pciBridge5.virtualDev = "pcieRootPort"
|
||||
pciBridge5.functions = "8"
|
||||
pciBridge6.present = "TRUE"
|
||||
pciBridge6.virtualDev = "pcieRootPort"
|
||||
pciBridge6.functions = "8"
|
||||
pciBridge7.present = "TRUE"
|
||||
pciBridge7.virtualDev = "pcieRootPort"
|
||||
pciBridge7.functions = "8"
|
||||
vmci0.present = "TRUE"
|
||||
hpet0.present = "TRUE"
|
||||
displayName = "Fedora 20"
|
||||
extendedConfigFile = "Fedora 20.vmxf"
|
||||
virtualHW.productCompatibility = "hosted"
|
||||
svga.vramSize = "8388608"
|
||||
memSize = "2048"
|
||||
sched.cpu.units = "mhz"
|
||||
sched.cpu.affinity = "all"
|
||||
powerType.powerOff = "soft"
|
||||
powerType.suspend = "hard"
|
||||
powerType.reset = "soft"
|
||||
scsi0.virtualDev = "pvscsi"
|
||||
scsi0.present = "TRUE"
|
||||
sata0.present = "TRUE"
|
||||
scsi0:0.deviceType = "scsi-hardDisk"
|
||||
scsi0:0.fileName = "Fedora 20.vmdk"
|
||||
sched.scsi0:0.shares = "normal"
|
||||
sched.scsi0:0.throughputCap = "off"
|
||||
scsi0:0.present = "TRUE"
|
||||
ethernet0.virtualDev = "vmxnet3"
|
||||
ethernet0.networkName = "VM Network"
|
||||
ethernet0.addressType = "vpx"
|
||||
ethernet0.generatedAddress = "00:50:56:9b:5f:0d"
|
||||
ethernet0.present = "TRUE"
|
||||
sata0:0.startConnected = "FALSE"
|
||||
sata0:0.deviceType = "cdrom-image"
|
||||
sata0:0.fileName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/ISOs/Fedora-20-x86_64-netinst.iso"
|
||||
sata0:0.present = "TRUE"
|
||||
floppy0.startConnected = "FALSE"
|
||||
floppy0.clientDevice = "TRUE"
|
||||
floppy0.fileName = "vmware-null-remote-floppy"
|
||||
vmci.filter.enable = "TRUE"
|
||||
guestOS = "rhel7-64"
|
||||
toolScripts.afterPowerOn = "TRUE"
|
||||
toolScripts.afterResume = "TRUE"
|
||||
toolScripts.beforeSuspend = "TRUE"
|
||||
toolScripts.beforePowerOff = "TRUE"
|
||||
uuid.bios = "42 1b 4b 87 e6 b7 d8 81-07 a0 c9 d2 21 cd 3c 6b"
|
||||
vc.uuid = "50 1b 1f 1b 73 00 32 bf-93 a1 1c b2 b4 e6 17 d6"
|
||||
sched.cpu.min = "0"
|
||||
sched.cpu.shares = "normal"
|
||||
sched.mem.min = "0"
|
||||
sched.mem.minSize = "0"
|
||||
sched.mem.shares = "normal"
|
||||
sched.swap.derivedName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/Fedora 20/Fedora 20-c71e4118.vswp"
|
||||
uuid.location = "56 4d 0f 53 00 63 d5 55-41 01 4c f7 55 ce 03 0e"
|
||||
replay.supported = "TRUE"
|
||||
replay.filename = ""
|
||||
scsi0:0.redo = ""
|
||||
pciBridge0.pciSlotNumber = "17"
|
||||
pciBridge4.pciSlotNumber = "21"
|
||||
pciBridge5.pciSlotNumber = "22"
|
||||
pciBridge6.pciSlotNumber = "23"
|
||||
pciBridge7.pciSlotNumber = "24"
|
||||
scsi0.pciSlotNumber = "160"
|
||||
ethernet0.pciSlotNumber = "192"
|
||||
vmci0.pciSlotNumber = "32"
|
||||
sata0.pciSlotNumber = "33"
|
||||
scsi0.sasWWID = "50 05 05 67 e6 b7 d8 80"
|
||||
vmci0.id = "567098475"
|
||||
vmotion.checkpointFBSize = "8388608"
|
||||
cleanShutdown = "TRUE"
|
||||
softPowerOff = "TRUE"
|
||||
sata0:0.allowGuestConnectionControl = "TRUE"
|
||||
tools.syncTime = "FALSE"
|
||||
22
v2v/test-v2v-i-vmx-3.expected
Normal file
22
v2v/test-v2v-i-vmx-3.expected
Normal file
@@ -0,0 +1,22 @@
|
||||
[ 0.0] Opening the source -i vmx test-v2v-i-vmx-3.vmx
|
||||
Source guest information (--print-source option):
|
||||
|
||||
source name: RHEL 7.1 UEFI
|
||||
hypervisor type: vmware
|
||||
memory: 2147483648 (bytes)
|
||||
nr vCPUs: 1
|
||||
CPU vendor:
|
||||
CPU model:
|
||||
CPU topology: sockets: - cores/socket: - threads/core: -
|
||||
CPU features:
|
||||
firmware: uefi
|
||||
display:
|
||||
video: vmvga
|
||||
sound:
|
||||
disks:
|
||||
/RHEL 7.1 UEFI.vmdk (vmdk) [scsi]
|
||||
removable media:
|
||||
CD-ROM [ide] in slot 2
|
||||
NICs:
|
||||
Network "VM Network" mac: 00:0c:29:4b:2b:8c [vmxnet3]
|
||||
|
||||
91
v2v/test-v2v-i-vmx-3.vmx
Normal file
91
v2v/test-v2v-i-vmx-3.vmx
Normal file
@@ -0,0 +1,91 @@
|
||||
.encoding = "UTF-8"
|
||||
config.version = "8"
|
||||
virtualHW.version = "8"
|
||||
nvram = "RHEL 7.1 UEFI.nvram"
|
||||
pciBridge0.present = "TRUE"
|
||||
svga.present = "TRUE"
|
||||
pciBridge4.present = "TRUE"
|
||||
pciBridge4.virtualDev = "pcieRootPort"
|
||||
pciBridge4.functions = "8"
|
||||
pciBridge5.present = "TRUE"
|
||||
pciBridge5.virtualDev = "pcieRootPort"
|
||||
pciBridge5.functions = "8"
|
||||
pciBridge6.present = "TRUE"
|
||||
pciBridge6.virtualDev = "pcieRootPort"
|
||||
pciBridge6.functions = "8"
|
||||
pciBridge7.present = "TRUE"
|
||||
pciBridge7.virtualDev = "pcieRootPort"
|
||||
pciBridge7.functions = "8"
|
||||
vmci0.present = "TRUE"
|
||||
hpet0.present = "TRUE"
|
||||
displayName = "RHEL 7.1 UEFI"
|
||||
extendedConfigFile = "RHEL 7.1 UEFI.vmxf"
|
||||
virtualHW.productCompatibility = "hosted"
|
||||
memSize = "2048"
|
||||
firmware = "efi"
|
||||
sched.cpu.units = "mhz"
|
||||
powerType.powerOff = "soft"
|
||||
powerType.suspend = "hard"
|
||||
powerType.reset = "soft"
|
||||
scsi0.virtualDev = "pvscsi"
|
||||
scsi0.present = "TRUE"
|
||||
ide1:0.startConnected = "FALSE"
|
||||
ide1:0.deviceType = "cdrom-image"
|
||||
ide1:0.fileName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/ISOs/RHEL-7.1-20150219.1-Server-x86_64-boot.iso"
|
||||
ide1:0.present = "TRUE"
|
||||
floppy0.startConnected = "FALSE"
|
||||
floppy0.clientDevice = "TRUE"
|
||||
floppy0.fileName = "vmware-null-remote-floppy"
|
||||
ethernet0.virtualDev = "vmxnet3"
|
||||
ethernet0.networkName = "VM Network"
|
||||
ethernet0.addressType = "generated"
|
||||
ethernet0.present = "TRUE"
|
||||
scsi0:0.deviceType = "scsi-hardDisk"
|
||||
scsi0:0.fileName = "RHEL 7.1 UEFI.vmdk"
|
||||
scsi0:0.present = "TRUE"
|
||||
guestOS = "rhel6-64"
|
||||
toolScripts.afterPowerOn = "TRUE"
|
||||
toolScripts.afterResume = "TRUE"
|
||||
toolScripts.beforeSuspend = "TRUE"
|
||||
toolScripts.beforePowerOff = "TRUE"
|
||||
uuid.bios = "56 4d 99 89 a7 21 91 0d-cc 28 e2 db d5 4b 2b 8c"
|
||||
uuid.location = "56 4d 99 89 a7 21 91 0d-cc 28 e2 db d5 4b 2b 8c"
|
||||
vc.uuid = "52 3f 29 10 d3 81 16 43-fa b0 e3 af 3b ba 36 e5"
|
||||
chipset.onlineStandby = "FALSE"
|
||||
sched.cpu.min = "0"
|
||||
sched.cpu.shares = "normal"
|
||||
sched.mem.min = "0"
|
||||
sched.mem.minSize = "0"
|
||||
sched.mem.shares = "normal"
|
||||
svga.vramSize = "8388608"
|
||||
sched.swap.derivedName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/RHEL 7.1 UEFI/RHEL 7.1 UEFI-58ff6e6f.vswp"
|
||||
replay.supported = "FALSE"
|
||||
replay.filename = ""
|
||||
scsi0:0.redo = ""
|
||||
pciBridge0.pciSlotNumber = "17"
|
||||
pciBridge4.pciSlotNumber = "21"
|
||||
pciBridge5.pciSlotNumber = "22"
|
||||
pciBridge6.pciSlotNumber = "23"
|
||||
pciBridge7.pciSlotNumber = "24"
|
||||
scsi0.pciSlotNumber = "160"
|
||||
ethernet0.pciSlotNumber = "192"
|
||||
vmci0.pciSlotNumber = "32"
|
||||
scsi0.sasWWID = "50 05 05 69 a7 21 91 00"
|
||||
ethernet0.generatedAddress = "00:0c:29:4b:2b:8c"
|
||||
ethernet0.generatedAddressOffset = "0"
|
||||
vmci0.id = "-716493940"
|
||||
hostCPUID.0 = "0000000d756e65476c65746e49656e69"
|
||||
hostCPUID.1 = "000206a700100800179ae3bfbfebfbff"
|
||||
hostCPUID.80000001 = "00000000000000000000000128100800"
|
||||
guestCPUID.0 = "0000000d756e65476c65746e49656e69"
|
||||
guestCPUID.1 = "000206a700010800969822030fabfbff"
|
||||
guestCPUID.80000001 = "00000000000000000000000128100800"
|
||||
userCPUID.0 = "0000000d756e65476c65746e49656e69"
|
||||
userCPUID.1 = "000206a700100800169822030fabfbff"
|
||||
userCPUID.80000001 = "00000000000000000000000128100800"
|
||||
evcCompatibilityMode = "FALSE"
|
||||
vmotion.checkpointFBSize = "8388608"
|
||||
cleanShutdown = "TRUE"
|
||||
softPowerOff = "TRUE"
|
||||
ide1:0.allowGuestConnectionControl = "TRUE"
|
||||
tools.syncTime = "FALSE"
|
||||
22
v2v/test-v2v-i-vmx-4.expected
Normal file
22
v2v/test-v2v-i-vmx-4.expected
Normal file
@@ -0,0 +1,22 @@
|
||||
[ 0.0] Opening the source -i vmx test-v2v-i-vmx-4.vmx
|
||||
Source guest information (--print-source option):
|
||||
|
||||
source name: Windows 7 x64
|
||||
hypervisor type: vmware
|
||||
memory: 2147483648 (bytes)
|
||||
nr vCPUs: 1
|
||||
CPU vendor:
|
||||
CPU model:
|
||||
CPU topology: sockets: - cores/socket: - threads/core: -
|
||||
CPU features:
|
||||
firmware: bios
|
||||
display:
|
||||
video: vmvga
|
||||
sound:
|
||||
disks:
|
||||
/Windows 7 x64.vmdk (vmdk) [scsi]
|
||||
removable media:
|
||||
CD-ROM [ide] in slot 2
|
||||
NICs:
|
||||
Network "VM Network" mac: 00:0c:29:94:89:23 [e1000]
|
||||
|
||||
88
v2v/test-v2v-i-vmx-4.vmx
Normal file
88
v2v/test-v2v-i-vmx-4.vmx
Normal file
@@ -0,0 +1,88 @@
|
||||
.encoding = "UTF-8"
|
||||
config.version = "8"
|
||||
virtualHW.version = "8"
|
||||
nvram = "Windows 7 x64.nvram"
|
||||
pciBridge0.present = "TRUE"
|
||||
svga.present = "TRUE"
|
||||
pciBridge4.present = "TRUE"
|
||||
pciBridge4.virtualDev = "pcieRootPort"
|
||||
pciBridge4.functions = "8"
|
||||
pciBridge5.present = "TRUE"
|
||||
pciBridge5.virtualDev = "pcieRootPort"
|
||||
pciBridge5.functions = "8"
|
||||
pciBridge6.present = "TRUE"
|
||||
pciBridge6.virtualDev = "pcieRootPort"
|
||||
pciBridge6.functions = "8"
|
||||
pciBridge7.present = "TRUE"
|
||||
pciBridge7.virtualDev = "pcieRootPort"
|
||||
pciBridge7.functions = "8"
|
||||
vmci0.present = "TRUE"
|
||||
hpet0.present = "TRUE"
|
||||
displayName = "Windows 7 x64"
|
||||
extendedConfigFile = "Windows 7 x64.vmxf"
|
||||
virtualHW.productCompatibility = "hosted"
|
||||
memSize = "2048"
|
||||
sched.cpu.units = "mhz"
|
||||
powerType.powerOff = "soft"
|
||||
powerType.suspend = "hard"
|
||||
powerType.reset = "soft"
|
||||
scsi0.virtualDev = "lsisas1068"
|
||||
scsi0.present = "TRUE"
|
||||
ide1:0.deviceType = "cdrom-image"
|
||||
ide1:0.fileName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/ISOs/en_windows_7_ultimate_with_sp1_x64_dvd_u_677332.iso"
|
||||
ide1:0.present = "TRUE"
|
||||
floppy0.startConnected = "FALSE"
|
||||
floppy0.clientDevice = "TRUE"
|
||||
floppy0.fileName = "vmware-null-remote-floppy"
|
||||
ethernet0.virtualDev = "e1000"
|
||||
ethernet0.networkName = "VM Network"
|
||||
ethernet0.addressType = "generated"
|
||||
ethernet0.present = "TRUE"
|
||||
scsi0:0.deviceType = "scsi-hardDisk"
|
||||
scsi0:0.fileName = "Windows 7 x64.vmdk"
|
||||
scsi0:0.present = "TRUE"
|
||||
guestOS = "windows7-64"
|
||||
toolScripts.afterPowerOn = "TRUE"
|
||||
toolScripts.afterResume = "TRUE"
|
||||
toolScripts.beforeSuspend = "TRUE"
|
||||
toolScripts.beforePowerOff = "TRUE"
|
||||
uuid.bios = "56 4d 6f ca 63 a5 a8 3e-13 ec 73 89 1d 94 89 23"
|
||||
uuid.location = "56 4d 6f ca 63 a5 a8 3e-13 ec 73 89 1d 94 89 23"
|
||||
vc.uuid = "52 7a 63 e1 2c 2f 50 46-91 66 3a e8 fa f9 c4 65"
|
||||
chipset.onlineStandby = "FALSE"
|
||||
sched.cpu.min = "0"
|
||||
sched.cpu.shares = "normal"
|
||||
sched.mem.min = "0"
|
||||
sched.mem.minSize = "0"
|
||||
sched.mem.shares = "normal"
|
||||
svga.vramSize = "8388608"
|
||||
sched.swap.derivedName = "/vmfs/volumes/5458b680-34ec3500-9f36-001320f5f6ca/Windows 7 x64/Windows 7 x64-8e3b0929.vswp"
|
||||
replay.supported = "FALSE"
|
||||
replay.filename = ""
|
||||
scsi0:0.redo = ""
|
||||
pciBridge0.pciSlotNumber = "17"
|
||||
pciBridge4.pciSlotNumber = "21"
|
||||
pciBridge5.pciSlotNumber = "22"
|
||||
pciBridge6.pciSlotNumber = "23"
|
||||
pciBridge7.pciSlotNumber = "24"
|
||||
scsi0.pciSlotNumber = "160"
|
||||
ethernet0.pciSlotNumber = "32"
|
||||
vmci0.pciSlotNumber = "33"
|
||||
scsi0.sasWWID = "50 05 05 6a 63 a5 a8 30"
|
||||
ethernet0.generatedAddress = "00:0c:29:94:89:23"
|
||||
ethernet0.generatedAddressOffset = "0"
|
||||
vmci0.id = "496273699"
|
||||
hostCPUID.0 = "0000000b756e65476c65746e49656e69"
|
||||
hostCPUID.1 = "000206c220200800029ee3ffbfebfbff"
|
||||
hostCPUID.80000001 = "0000000000000000000000012c100800"
|
||||
guestCPUID.0 = "0000000b756e65476c65746e49656e69"
|
||||
guestCPUID.1 = "000206c200010800829822030fabfbff"
|
||||
guestCPUID.80000001 = "00000000000000000000000128100800"
|
||||
userCPUID.0 = "0000000b756e65476c65746e49656e69"
|
||||
userCPUID.1 = "000206c220200800029822030fabfbff"
|
||||
userCPUID.80000001 = "00000000000000000000000128100800"
|
||||
evcCompatibilityMode = "FALSE"
|
||||
vmotion.checkpointFBSize = "8388608"
|
||||
cleanShutdown = "TRUE"
|
||||
softPowerOff = "TRUE"
|
||||
tools.remindInstall = "TRUE"
|
||||
48
v2v/test-v2v-i-vmx.sh
Executable file
48
v2v/test-v2v-i-vmx.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs virt-v2v test script
|
||||
# Copyright (C) 2017 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.
|
||||
|
||||
# Test -i ova option.
|
||||
|
||||
set -e
|
||||
|
||||
$TEST_FUNCTIONS
|
||||
skip_if_skipped
|
||||
skip_if_backend uml
|
||||
|
||||
export VIRT_TOOLS_DATA_DIR="$top_srcdir/test-data/fake-virt-tools"
|
||||
export VIRTIO_WIN="$top_srcdir/test-data/fake-virtio-win"
|
||||
|
||||
rm -f test-v2v-i-vmx-*.actual
|
||||
|
||||
for i in 1 2 3 4; do
|
||||
$VG virt-v2v --debug-gc \
|
||||
-i vmx test-v2v-i-vmx-$i.vmx \
|
||||
--print-source > test-v2v-i-vmx-$i.actual
|
||||
|
||||
# Normalize the print-source output.
|
||||
mv test-v2v-i-vmx-$i.actual test-v2v-i-vmx-$i.actual.old
|
||||
sed \
|
||||
-e "s,$(pwd),," \
|
||||
< test-v2v-i-vmx-$i.actual.old > test-v2v-i-vmx-$i.actual
|
||||
rm test-v2v-i-vmx-$i.actual.old
|
||||
|
||||
# Check the output.
|
||||
diff -u test-v2v-i-vmx-$i.expected test-v2v-i-vmx-$i.actual
|
||||
done
|
||||
|
||||
rm test-v2v-i-vmx-*.actual
|
||||
@@ -796,6 +796,148 @@ let test_qemu_img_supports ctx =
|
||||
*)
|
||||
ignore (Utils.qemu_img_supports_offset_and_size ())
|
||||
|
||||
(* Test the VMX file parser in the Parse_vmx module. *)
|
||||
let test_vmx_parse_string ctx =
|
||||
let cmp = Parse_vmx.equal in
|
||||
let printer = Parse_vmx.to_string 0 in
|
||||
|
||||
(* This should be identical to the empty file. *)
|
||||
let t = Parse_vmx.parse_string "\
|
||||
test.foo = \"a\"
|
||||
test.bar = \"b\"
|
||||
test.present = \"FALSE\"
|
||||
" in
|
||||
assert_equal ~cmp ~printer Parse_vmx.empty t;
|
||||
|
||||
(* Test weird escapes. *)
|
||||
let t1 = Parse_vmx.parse_string "\
|
||||
foo = \"a|20|21b\"
|
||||
" in
|
||||
let t2 = Parse_vmx.parse_string "\
|
||||
foo = \"a !b\"
|
||||
" in
|
||||
assert_equal ~cmp ~printer t1 t2;
|
||||
|
||||
(* Test case insensitivity. *)
|
||||
let t1 = Parse_vmx.parse_string "\
|
||||
foo = \"abc\"
|
||||
" in
|
||||
let t2 = Parse_vmx.parse_string "\
|
||||
fOO = \"abc\"
|
||||
" in
|
||||
assert_equal ~cmp ~printer t1 t2;
|
||||
let t = Parse_vmx.parse_string "\
|
||||
flag = \"true\"
|
||||
" in
|
||||
assert_bool "parse_vmx: failed case insensitivity test for booleans #1"
|
||||
(Parse_vmx.get_bool t ["FLAG"] = Some true);
|
||||
let t = Parse_vmx.parse_string "\
|
||||
flag = \"TRUE\"
|
||||
" in
|
||||
assert_bool "parse_vmx: failed case insensitivity test for booleans #2"
|
||||
(Parse_vmx.get_bool t ["Flag"] = Some true);
|
||||
|
||||
(* Missing keys. *)
|
||||
let t = Parse_vmx.parse_string "\
|
||||
foo = \"a\"
|
||||
" in
|
||||
assert_bool "parse_vmx: failed missing key test"
|
||||
(Parse_vmx.get_string t ["bar"] = None);
|
||||
|
||||
(* namespace_present function *)
|
||||
let t = Parse_vmx.parse_string "\
|
||||
foo.bar.present = \"TRUE\"
|
||||
foo.baz.present = \"FALSE\"
|
||||
foo.a.b = \"abc\"
|
||||
foo.a.c = \"abc\"
|
||||
foo.b = \"abc\"
|
||||
foo.c.a = \"abc\"
|
||||
foo.c.b = \"abc\"
|
||||
" in
|
||||
assert_bool "parse_vmx: namespace_present #1"
|
||||
(Parse_vmx.namespace_present t ["foo"] = true);
|
||||
assert_bool "parse_vmx: namespace_present #2"
|
||||
(Parse_vmx.namespace_present t ["foo"; "bar"] = true);
|
||||
assert_bool "parse_vmx: namespace_present #3"
|
||||
(* this whole namespace should have been culled *)
|
||||
(Parse_vmx.namespace_present t ["foo"; "baz"] = false);
|
||||
assert_bool "parse_vmx: namespace_present #4"
|
||||
(Parse_vmx.namespace_present t ["foo"; "a"] = true);
|
||||
assert_bool "parse_vmx: namespace_present #5"
|
||||
(* this is a key, not a namespace *)
|
||||
(Parse_vmx.namespace_present t ["foo"; "a"; "b"] = false);
|
||||
assert_bool "parse_vmx: namespace_present #6"
|
||||
(Parse_vmx.namespace_present t ["foo"; "b"] = false);
|
||||
assert_bool "parse_vmx: namespace_present #7"
|
||||
(Parse_vmx.namespace_present t ["foo"; "c"] = true);
|
||||
assert_bool "parse_vmx: namespace_present #8"
|
||||
(Parse_vmx.namespace_present t ["foo"; "d"] = false);
|
||||
|
||||
(* map function *)
|
||||
let t = Parse_vmx.parse_string "\
|
||||
foo.bar.present = \"TRUE\"
|
||||
foo.baz.present = \"FALSE\"
|
||||
foo.a.b = \"abc\"
|
||||
foo.a.c = \"abc\"
|
||||
foo.b = \"abc\"
|
||||
foo.c.a = \"abc\"
|
||||
foo.c.b = \"abc\"
|
||||
" in
|
||||
let xs =
|
||||
Parse_vmx.map (
|
||||
fun path ->
|
||||
let path = String.concat "." path in
|
||||
function
|
||||
| None -> sprintf "%s.present = \"true\"\n" path
|
||||
| Some v -> sprintf "%s = \"%s\"\n" path v
|
||||
) t in
|
||||
let xs = List.sort compare xs in
|
||||
let s = String.concat "" xs in
|
||||
assert_equal ~printer:identity "\
|
||||
foo.a.b = \"abc\"
|
||||
foo.a.c = \"abc\"
|
||||
foo.a.present = \"true\"
|
||||
foo.b = \"abc\"
|
||||
foo.bar.present = \"TRUE\"
|
||||
foo.bar.present = \"true\"
|
||||
foo.c.a = \"abc\"
|
||||
foo.c.b = \"abc\"
|
||||
foo.c.present = \"true\"
|
||||
foo.present = \"true\"
|
||||
" s;
|
||||
|
||||
(* select_namespaces function *)
|
||||
let t1 = Parse_vmx.parse_string "\
|
||||
foo.bar.present = \"TRUE\"
|
||||
foo.a.b = \"abc\"
|
||||
foo.a.c = \"abc\"
|
||||
foo.b = \"abc\"
|
||||
foo.c.a = \"abc\"
|
||||
foo.c.b = \"abc\"
|
||||
" in
|
||||
let t2 =
|
||||
Parse_vmx.select_namespaces
|
||||
(function ["foo"] -> true | _ -> false) t1 in
|
||||
assert_equal ~cmp ~printer t1 t2;
|
||||
|
||||
let t1 = Parse_vmx.parse_string "\
|
||||
foo.bar.present = \"TRUE\"
|
||||
foo.a.b = \"abc\"
|
||||
foo.a.c = \"abc\"
|
||||
foo.b = \"abc\"
|
||||
foo.c.a = \"abc\"
|
||||
foo.c.b = \"abc\"
|
||||
foo.c.c.d.e.f = \"abc\"
|
||||
" in
|
||||
let t1 =
|
||||
Parse_vmx.select_namespaces
|
||||
(function ["foo"; "a"] -> true | _ -> false) t1 in
|
||||
let t2 = Parse_vmx.parse_string "\
|
||||
foo.a.b = \"abc\"
|
||||
foo.a.c = \"abc\"
|
||||
" in
|
||||
assert_equal ~cmp ~printer t2 t1
|
||||
|
||||
(* Suites declaration. *)
|
||||
let suite =
|
||||
"virt-v2v" >:::
|
||||
@@ -807,6 +949,7 @@ let suite =
|
||||
test_virtio_iso_path_matches_guest_os;
|
||||
"Utils.shell_unquote" >:: test_shell_unquote;
|
||||
"Utils.qemu_img_supports" >:: test_qemu_img_supports;
|
||||
"Parse_vmx.parse_string" >::test_vmx_parse_string;
|
||||
]
|
||||
|
||||
let () =
|
||||
|
||||
@@ -43,7 +43,8 @@ libguestfs E<ge> 1.28.
|
||||
... ───▶│ (default) │ │ │ ──┐ └────────────┘
|
||||
└────────────┘ │ │ ─┐└──────▶ -o glance
|
||||
-i libvirtxml ─────────▶ │ │ ┐└─────────▶ -o rhv
|
||||
└────────────┘ └──────────▶ -o vdsm
|
||||
-i vmx ────────────────▶ │ │ └──────────▶ -o vdsm
|
||||
└────────────┘
|
||||
|
||||
Virt-v2v has a number of possible input and output modes, selected
|
||||
using the I<-i> and I<-o> options. Only one input and output mode can
|
||||
@@ -62,6 +63,8 @@ method used by L<virt-p2v(1)> behind the scenes.
|
||||
|
||||
I<-i ova> is used for reading from a VMware ova source file.
|
||||
|
||||
I<-i vmx> is used for reading from a VMware vmx file.
|
||||
|
||||
I<-o glance> is used for writing to OpenStack Glance.
|
||||
|
||||
I<-o libvirt> is used for writing to any libvirt target. Libvirt can
|
||||
@@ -165,6 +168,10 @@ from ESXi is not supported.
|
||||
|
||||
OVAs from other hypervisors will not work.
|
||||
|
||||
=item VMX from VMware
|
||||
|
||||
VMX files generated by other hypervisors will not work.
|
||||
|
||||
=item RHEL 5 Xen
|
||||
|
||||
=item SUSE Xen
|
||||
@@ -335,6 +342,14 @@ ova manifest file and check the vmdk volumes for validity (checksums)
|
||||
as well as analyzing the ovf file, and then convert the guest. See
|
||||
L</INPUT FROM VMWARE OVA> below
|
||||
|
||||
=item B<-i> B<vmx>
|
||||
|
||||
Set the input method to I<vmx>.
|
||||
|
||||
In this mode you can read a VMware vmx file directly. This is useful
|
||||
when VMware VMs are stored on an NFS server which you can mount
|
||||
directly. See L</INPUT FROM VMWARE VMX> below
|
||||
|
||||
=item B<-ic> libvirtURI
|
||||
|
||||
Specify a libvirt connection URI to use when reading the guest. This
|
||||
@@ -985,8 +1000,9 @@ I<--bridge> option instead. For example:
|
||||
Virt-v2v is able to import guests from VMware vCenter Server.
|
||||
|
||||
vCenter E<ge> 5.0 is required. If you don’t have vCenter, using OVA
|
||||
is recommended instead (see L</INPUT FROM VMWARE OVA> below), or if
|
||||
that is not possible then see L</INPUT FROM VMWARE ESXi HYPERVISOR>.
|
||||
or VMX is recommended instead (see L</INPUT FROM VMWARE OVA> and/or
|
||||
L</INPUT FROM VMWARE VMX> below), or if that is not possible then see
|
||||
L</INPUT FROM VMWARE 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
|
||||
@@ -1257,12 +1273,58 @@ directory containing the files:
|
||||
|
||||
$ virt-v2v -i ova /path/to/files -o local -os /var/tmp
|
||||
|
||||
=head1 INPUT FROM VMWARE VMX
|
||||
|
||||
Virt-v2v is able to import guests from VMware’s vmx files. This is
|
||||
useful where VMware virtual machines are stored on a separate NFS
|
||||
server and you are able to mount the NFS storage directly.
|
||||
|
||||
If you find a folder of files called F<I<guest>.vmx>,
|
||||
F<I<guest>.vmxf>, F<I<guest>.nvram> and one or more F<.vmdk> disk
|
||||
images, then you can use this method.
|
||||
|
||||
=head2 VMX: REMOVE VMWARE TOOLS FROM WINDOWS GUESTS
|
||||
|
||||
For Windows guests, you should remove VMware tools before conversion.
|
||||
Although this is not strictly necessary, and the guest will still be
|
||||
able to run, if you don't do this then the converted guest will
|
||||
complain on every boot. The tools cannot be removed after conversion
|
||||
because the uninstaller checks if it is running on VMware and refuses
|
||||
to start (which is also the reason that virt-v2v cannot remove them).
|
||||
|
||||
This is not necessary for Linux guests, as virt-v2v is able to remove
|
||||
VMware tools.
|
||||
|
||||
=head2 VMX: GUEST MUST BE SHUT DOWN
|
||||
|
||||
B<The guest must be shut down before conversion starts>. If you don't
|
||||
shut it down, you will end up with a corrupted VM disk on the target.
|
||||
With other methods, virt-v2v tries to prevent concurrent access, but
|
||||
because the I<-i vmx> method works directly against the storage,
|
||||
checking for concurrent access is not possible.
|
||||
|
||||
=head2 VMX: MOUNT THE NFS STORAGE ON THE CONVERSION SERVER
|
||||
|
||||
Virt-v2v must be able to access the F<.vmx> file and any local
|
||||
F<.vmdk> disks. Normally this means you must mount the NFS storage
|
||||
containing these files.
|
||||
|
||||
=head2 VMX: IMPORTING A GUEST
|
||||
|
||||
To import a vmx file, do:
|
||||
|
||||
$ virt-v2v -i vmx guest.vmx -o local -os /var/tmp
|
||||
|
||||
Virt-v2v processes the vmx file and uses it to find the location of
|
||||
any vmdk disks.
|
||||
|
||||
=head1 INPUT FROM VMWARE ESXi HYPERVISOR
|
||||
|
||||
Virt-v2v cannot access an ESXi hypervisor directly. You should use
|
||||
the OVA method above (see L</INPUT FROM VMWARE OVA>) if possible, as
|
||||
it is much faster and requires much less disk space than the method
|
||||
described in this section.
|
||||
the OVA or VMX methods above (see L</INPUT FROM VMWARE OVA> and/or
|
||||
L</INPUT FROM VMWARE VMX>) if possible, as it is much faster and
|
||||
requires much less disk space than the method described in this
|
||||
section.
|
||||
|
||||
You can use the L<virt-v2v-copy-to-local(1)> tool to copy the guest
|
||||
off the hypervisor into a local file, and then convert it.
|
||||
|
||||
Reference in New Issue
Block a user