diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 28f937985..af43aa9f1 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -327,6 +327,7 @@ TESTS = \ test-v2v-i-ova-gz.sh \ test-v2v-i-ova-invalid-manifest1.sh \ test-v2v-i-ova-invalid-manifest2.sh \ + test-v2v-i-ova-snapshots.sh \ test-v2v-i-ova-subfolders.sh \ test-v2v-i-ova-tar.sh \ test-v2v-i-ova-two-disks.sh \ @@ -466,6 +467,10 @@ EXTRA_DIST += \ test-v2v-i-ova-gz.sh \ test-v2v-i-ova-invalid-manifest1.sh \ test-v2v-i-ova-invalid-manifest2.sh \ + test-v2v-i-ova-snapshots.expected \ + test-v2v-i-ova-snapshots.expected2 \ + test-v2v-i-ova-snapshots.ovf \ + test-v2v-i-ova-snapshots.sh \ test-v2v-i-ova-subfolders.expected \ test-v2v-i-ova-subfolders.expected2 \ test-v2v-i-ova-subfolders.ovf \ diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml index 15c5b9171..c4711f902 100644 --- a/v2v/input_ova.ml +++ b/v2v/input_ova.ml @@ -27,6 +27,49 @@ open Parse_ova open Parse_ovf_from_ova open Name_from_disk +(* RHBZ#1570407: VMware-generated OVA files found in the wild can + * contain hrefs referencing snapshots. The href will be something + * like: but the actual disk will be a + * snapshot called something like "disk1.vmdk.000000000". + *) +let re_snapshot = PCRE.compile "\\.(\\d+)$" + +let rec find_file_or_snapshot ova_t href manifest = + match resolve_href ova_t href with + | Some f -> f + | None -> + (* Find all files in the OVA called [.\d+] *) + let files = get_file_list ova_t in + let snapshots = + List.filter_map ( + function + | LocalFile filename -> get_snapshot_if_matches href filename + | TarFile (_, filename) -> get_snapshot_if_matches href filename + ) files in + (* Pick highest. *) + let snapshots = List.sort (fun a b -> compare b a) snapshots in + match snapshots with + | [] -> error_missing_href href + | snapshot::_ -> + let href = sprintf "%s.%s" href snapshot in + match resolve_href ova_t href with + | None -> error_missing_href href + | Some f -> f + +(* If [filename] matches [.\d+] then return [Some snapshot]. *) +and get_snapshot_if_matches href filename = + if PCRE.matches re_snapshot filename then ( + let snapshot = PCRE.sub 1 in + if String.is_suffix filename (sprintf "%s.%s" href snapshot) then + Some snapshot + else + None + ) + else None + +and error_missing_href href = + error (f_"-i ova: OVF references file ā€˜%s’ which was not found in the OVA archive") href + class input_ova ova = object inherit input @@ -79,11 +122,7 @@ class input_ova ova = object (* Convert the disk hrefs into qemu URIs. *) let qemu_uris = List.map ( fun { href; compressed } -> - let file_ref = - match resolve_href ova_t href with - | Some f -> f - | None -> - error (f_"-i ova: OVF references file ā€˜%s’ which was not found in the OVA archive") href in + let file_ref = find_file_or_snapshot ova_t href manifest in match compressed, file_ref with | false, LocalFile filename -> diff --git a/v2v/test-v2v-i-ova-snapshots.expected b/v2v/test-v2v-i-ova-snapshots.expected new file mode 100644 index 000000000..97bce58ad --- /dev/null +++ b/v2v/test-v2v-i-ova-snapshots.expected @@ -0,0 +1,21 @@ +Source guest information (--print-source option): + + source name: 2K8R2EESP1_2_Medium +hypervisor type: vmware + memory: 1073741824 (bytes) + nr vCPUs: 1 + CPU vendor: + CPU model: + CPU topology: + CPU features: + firmware: uefi + display: + video: + sound: +disks: + disk1.vmdk (vmdk) [scsi] +removable media: + CD-ROM [ide] in slot 0 +NICs: + Bridge "PG-VLAN60" [e1000] + diff --git a/v2v/test-v2v-i-ova-snapshots.expected2 b/v2v/test-v2v-i-ova-snapshots.expected2 new file mode 100644 index 000000000..45be3cc46 --- /dev/null +++ b/v2v/test-v2v-i-ova-snapshots.expected2 @@ -0,0 +1,21 @@ +Source guest information (--print-source option): + + source name: 2K8R2EESP1_2_Medium +hypervisor type: vmware + memory: 1073741824 (bytes) + nr vCPUs: 1 + CPU vendor: + CPU model: + CPU topology: + CPU features: + firmware: uefi + display: + video: + sound: +disks: + json:{ "file": { "driver": "raw", "offset": x, "size": 12288, "file": { "driver": "file", "filename": "test-snapshots.ova" } } } (vmdk) [scsi] +removable media: + CD-ROM [ide] in slot 0 +NICs: + Bridge "PG-VLAN60" [e1000] + diff --git a/v2v/test-v2v-i-ova-snapshots.ovf b/v2v/test-v2v-i-ova-snapshots.ovf new file mode 100644 index 000000000..5e7c0d054 --- /dev/null +++ b/v2v/test-v2v-i-ova-snapshots.ovf @@ -0,0 +1,138 @@ + + + + + + + Virtual disk information + + + + The list of logical networks + + The PG-VLAN60 network + + + + A virtual machine + 2K8R2EESP1_2_Medium + + The kind of installed guest operating system + Microsoft Windows Server 2008 R2 (64-bit) + + + Virtual hardware requirements + + Virtual Hardware Family + 0 + 2K8R2EESP1_2_Medium + vmx-10 + + + hertz * 10^6 + Number of Virtual CPUs + 1 virtual CPU(s) + 1 + 3 + 1 + + + byte * 2^20 + Memory Size + 1024MB of memory + 2 + 4 + 1024 + + + 0 + SCSI Controller + SCSI controller 0 + 3 + lsilogicsas + 6 + + + + 1 + IDE Controller + IDE 1 + 4 + 5 + + + 0 + IDE Controller + IDE 0 + 5 + 5 + + + false + Video card + 6 + 24 + + + + + + + false + VMCI device + 7 + vmware.vmci + 1 + + + + + 0 + false + CD/DVD drive 1 + 8 + 4 + vmware.cdrom.atapi + 15 + + + 0 + Hard disk 1 + ovf:/disk/vmdisk1 + 9 + 3 + 17 + + + + 7 + true + PG-VLAN60 + E1000 ethernet adapter on "PG-VLAN60" + Network adapter 1 + 11 + E1000 + 10 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2v/test-v2v-i-ova-snapshots.sh b/v2v/test-v2v-i-ova-snapshots.sh new file mode 100755 index 000000000..7649d22f9 --- /dev/null +++ b/v2v/test-v2v-i-ova-snapshots.sh @@ -0,0 +1,78 @@ +#!/bin/bash - +# libguestfs virt-v2v test script +# Copyright (C) 2014-2018 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 with OVA file containing snapshots. +# https://bugzilla.redhat.com/show_bug.cgi?id=1570407 + +unset CDPATH +export LANG=C +set -e + +$TEST_FUNCTIONS +skip_if_skipped +skip_if_backend uml + +export VIRT_TOOLS_DATA_DIR="$top_srcdir/test-data/fake-virt-tools" + +d=test-v2v-i-ova-snapshots.d +rm -rf $d +mkdir $d + +pushd $d + +# Create a phony OVA. This is only a test of source parsing, not +# conversion, so the contents of the disks doesn't matter. +# In these weird OVAs, disk1.vmdk does not exist, but both the +# href and manifest reference it. virt-v2v should use the +# highest numbered snapshot instead. +guestfish disk-create disk1.vmdk.000000000 raw 10k +guestfish disk-create disk1.vmdk.000000001 raw 11k +guestfish disk-create disk1.vmdk.000000002 raw 12k +sha=`do_sha1 disk1.vmdk.000000002` +echo -e "SHA1(disk1.vmdk)= $sha\r" > disk1.mf +sha=`do_sha1 disk1.vmdk.000000000` +echo -e "SHA1(disk1.vmdk.000000000)= $sha\r" > disk1.mf +sha=`do_sha1 disk1.vmdk.000000001` +echo -e "SHA1(disk1.vmdk.000000001)= $sha\r" > disk1.mf +sha=`do_sha1 disk1.vmdk.000000002` +echo -e "SHA1(disk1.vmdk.000000002)= $sha\r" > disk1.mf +cp ../test-v2v-i-ova-snapshots.ovf . +tar -cf test-snapshots.ova test-v2v-i-ova-snapshots.ovf disk1.vmdk.00000000? disk1.mf + +popd + +# Run virt-v2v but only as far as the --print-source stage +$VG virt-v2v --debug-gc --quiet \ + -i ova $d/test-snapshots.ova \ + --print-source > $d/source + +# Check the parsed source is what we expect. +if grep -sq json: $d/source ; then + # Normalize the output. + # Remove directory prefix. + # Exact offset will vary because of tar. + sed -i -e "s,\"[^\"]*/$d/,\"," \ + -e "s|\"offset\": [0-9]*,|\"offset\": x,|" $d/source + diff -u test-v2v-i-ova-snapshots.expected2 $d/source +else + # normalize the output + sed -i -e 's,[^ \t]*\(disk.*.vmdk\),\1,' $d/source + diff -u test-v2v-i-ova-snapshots.expected $d/source +fi + +rm -rf $d