mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
v2v: -i ova: Handle OVAs containing snapshots (RHBZ#1570407).
Also adds a regression test.
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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: <File href="disk1.vmdk"/> 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 [<href>.\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 [<href>.\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 ->
|
||||
|
||||
21
v2v/test-v2v-i-ova-snapshots.expected
Normal file
21
v2v/test-v2v-i-ova-snapshots.expected
Normal file
@@ -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]
|
||||
|
||||
21
v2v/test-v2v-i-ova-snapshots.expected2
Normal file
21
v2v/test-v2v-i-ova-snapshots.expected2
Normal file
@@ -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]
|
||||
|
||||
138
v2v/test-v2v-i-ova-snapshots.ovf
Normal file
138
v2v/test-v2v-i-ova-snapshots.ovf
Normal file
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Envelope vmw:buildId="build-1750787" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<References>
|
||||
<File ovf:href="disk1.vmdk" ovf:id="file1" ovf:size="12288"/>
|
||||
</References>
|
||||
<DiskSection>
|
||||
<Info>Virtual disk information</Info>
|
||||
<Disk ovf:capacity="50" ovf:capacityAllocationUnits="byte * 2^30" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="18975752192"/>
|
||||
</DiskSection>
|
||||
<NetworkSection>
|
||||
<Info>The list of logical networks</Info>
|
||||
<Network ovf:name="PG-VLAN60">
|
||||
<Description>The PG-VLAN60 network</Description>
|
||||
</Network>
|
||||
</NetworkSection>
|
||||
<VirtualSystem ovf:id="2K8R2EESP1_2_Medium">
|
||||
<Info>A virtual machine</Info>
|
||||
<Name>2K8R2EESP1_2_Medium</Name>
|
||||
<OperatingSystemSection ovf:id="103" vmw:osType="windows7Server64Guest">
|
||||
<Info>The kind of installed guest operating system</Info>
|
||||
<Description>Microsoft Windows Server 2008 R2 (64-bit)</Description>
|
||||
</OperatingSystemSection>
|
||||
<VirtualHardwareSection>
|
||||
<Info>Virtual hardware requirements</Info>
|
||||
<System>
|
||||
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
|
||||
<vssd:InstanceID>0</vssd:InstanceID>
|
||||
<vssd:VirtualSystemIdentifier>2K8R2EESP1_2_Medium</vssd:VirtualSystemIdentifier>
|
||||
<vssd:VirtualSystemType>vmx-10</vssd:VirtualSystemType>
|
||||
</System>
|
||||
<Item>
|
||||
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
|
||||
<rasd:Description>Number of Virtual CPUs</rasd:Description>
|
||||
<rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
|
||||
<rasd:InstanceID>1</rasd:InstanceID>
|
||||
<rasd:ResourceType>3</rasd:ResourceType>
|
||||
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
|
||||
<rasd:Description>Memory Size</rasd:Description>
|
||||
<rasd:ElementName>1024MB of memory</rasd:ElementName>
|
||||
<rasd:InstanceID>2</rasd:InstanceID>
|
||||
<rasd:ResourceType>4</rasd:ResourceType>
|
||||
<rasd:VirtualQuantity>1024</rasd:VirtualQuantity>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:Address>0</rasd:Address>
|
||||
<rasd:Description>SCSI Controller</rasd:Description>
|
||||
<rasd:ElementName>SCSI controller 0</rasd:ElementName>
|
||||
<rasd:InstanceID>3</rasd:InstanceID>
|
||||
<rasd:ResourceSubType>lsilogicsas</rasd:ResourceSubType>
|
||||
<rasd:ResourceType>6</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="160"/>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:Address>1</rasd:Address>
|
||||
<rasd:Description>IDE Controller</rasd:Description>
|
||||
<rasd:ElementName>IDE 1</rasd:ElementName>
|
||||
<rasd:InstanceID>4</rasd:InstanceID>
|
||||
<rasd:ResourceType>5</rasd:ResourceType>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:Address>0</rasd:Address>
|
||||
<rasd:Description>IDE Controller</rasd:Description>
|
||||
<rasd:ElementName>IDE 0</rasd:ElementName>
|
||||
<rasd:InstanceID>5</rasd:InstanceID>
|
||||
<rasd:ResourceType>5</rasd:ResourceType>
|
||||
</Item>
|
||||
<Item ovf:required="false">
|
||||
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
|
||||
<rasd:ElementName>Video card</rasd:ElementName>
|
||||
<rasd:InstanceID>6</rasd:InstanceID>
|
||||
<rasd:ResourceType>24</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="use3dRenderer" vmw:value="automatic"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="useAutoDetect" vmw:value="true"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="videoRamSizeInKB" vmw:value="4096"/>
|
||||
</Item>
|
||||
<Item ovf:required="false">
|
||||
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
|
||||
<rasd:ElementName>VMCI device</rasd:ElementName>
|
||||
<rasd:InstanceID>7</rasd:InstanceID>
|
||||
<rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
|
||||
<rasd:ResourceType>1</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="allowUnrestrictedCommunication" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="32"/>
|
||||
</Item>
|
||||
<Item ovf:required="false">
|
||||
<rasd:AddressOnParent>0</rasd:AddressOnParent>
|
||||
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
|
||||
<rasd:ElementName>CD/DVD drive 1</rasd:ElementName>
|
||||
<rasd:InstanceID>8</rasd:InstanceID>
|
||||
<rasd:Parent>4</rasd:Parent>
|
||||
<rasd:ResourceSubType>vmware.cdrom.atapi</rasd:ResourceSubType>
|
||||
<rasd:ResourceType>15</rasd:ResourceType>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:AddressOnParent>0</rasd:AddressOnParent>
|
||||
<rasd:ElementName>Hard disk 1</rasd:ElementName>
|
||||
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
|
||||
<rasd:InstanceID>9</rasd:InstanceID>
|
||||
<rasd:Parent>3</rasd:Parent>
|
||||
<rasd:ResourceType>17</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="backing.writeThrough" vmw:value="false"/>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:AddressOnParent>7</rasd:AddressOnParent>
|
||||
<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
|
||||
<rasd:Connection>PG-VLAN60</rasd:Connection>
|
||||
<rasd:Description>E1000 ethernet adapter on "PG-VLAN60"</rasd:Description>
|
||||
<rasd:ElementName>Network adapter 1</rasd:ElementName>
|
||||
<rasd:InstanceID>11</rasd:InstanceID>
|
||||
<rasd:ResourceSubType>E1000</rasd:ResourceSubType>
|
||||
<rasd:ResourceType>10</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="33"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/>
|
||||
</Item>
|
||||
<vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="efi"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.standbyAction" vmw:value="checkpoint"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="hard"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="tools.afterPowerOn" vmw:value="true"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="tools.afterResume" vmw:value="true"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="tools.beforeGuestShutdown" vmw:value="true"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="tools.beforeGuestStandby" vmw:value="true"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="tools.toolsUpgradePolicy" vmw:value="upgradeAtPowerCycle"/>
|
||||
</VirtualHardwareSection>
|
||||
</VirtualSystem>
|
||||
</Envelope>
|
||||
78
v2v/test-v2v-i-ova-snapshots.sh
Executable file
78
v2v/test-v2v-i-ova-snapshots.sh
Executable file
@@ -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
|
||||
Reference in New Issue
Block a user