Mixing the XML parsing with the other functions of this module made it
very hard to understand. Splitting the XML parsing into another
module simplifies the flow considerably.
This is just code refactoring and should not affect the semantics.
---
v2v/Makefile.am | 2 +
v2v/input_ova.ml | 323 +++++++++++----------------------------------
v2v/parse_ovf_from_ova.ml | 226 +++++++++++++++++++++++++++++++
v2v/parse_ovf_from_ova.mli | 36 +++++
4 files changed, 343 insertions(+), 244 deletions(-)
create mode 100644 v2v/parse_ovf_from_ova.ml
create mode 100644 v2v/parse_ovf_from_ova.mli
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index a6a9899..f065654 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -53,6 +53,7 @@ SOURCES_MLI = \
output_rhv.mli \
output_vdsm.mli \
OVF.mli \
+ parse_ovf_from_ova.mli \
parse_libvirt_xml.mli \
qemu_command.mli \
target_bus_assignment.mli \
@@ -73,6 +74,7 @@ SOURCES_ML = \
DOM.ml \
changeuid.ml \
OVF.ml \
+ parse_ovf_from_ova.ml \
linux.ml \
windows.ml \
windows_virtio.ml \
diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml
index 9a6a615..a0a42a7 100644
--- a/v2v/input_ova.ml
+++ b/v2v/input_ova.ml
@@ -24,7 +24,7 @@ open Unix_utils
open Types
open Utils
-open Xpath_helpers
+open Parse_ovf_from_ova
open Name_from_disk
(* Return true if [libvirt] supports ["json:"] pseudo-URLs and accepts the
@@ -211,262 +211,97 @@ object
disk mf mode disk actual mode disk expected;
)
else
- warning (f_"unable to parse line from manifest file: %S") line
- ;
+ warning (f_"unable to parse line from manifest file: %S") line;
loop ()
in
(try loop () with End_of_file -> ());
close_in chan
) mf;
- (* Parse the ovf file. *)
let ovf_folder = Filename.dirname ovf in
- let xml = read_whole_file ovf in
- let doc = Xml.parse_memory xml in
-
- (* Handle namespaces. *)
- let xpathctx = Xml.xpath_new_context doc in
- Xml.xpath_register_ns xpathctx
- "ovf" "http://schemas.dmtf.org/ovf/envelope/1";
- Xml.xpath_register_ns xpathctx
- "rasd"
"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData";
- Xml.xpath_register_ns xpathctx
- "vmw" "http://www.vmware.com/schema/ovf";
- Xml.xpath_register_ns xpathctx
- "vssd"
"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData";
- let xpath_string = xpath_string xpathctx
- and xpath_int = xpath_int xpathctx
- and xpath_string_default = xpath_string_default xpathctx
- and xpath_int_default = xpath_int_default xpathctx
- and xpath_int64_default = xpath_int64_default xpathctx in
+ (* Parse the ovf file. *)
+ let name, memory, vcpu, firmware, disks, removables, nics =
+ parse_ovf_from_ova ovf in
- (* Search for vm name. *)
let name =
- match xpath_string "/ovf:Envelope/ovf:VirtualSystem/ovf:Name/text()"
with
- | None | Some "" ->
- warning (f_"could not parse ovf:Name from OVF document");
- name_from_disk ova
- | Some name -> name in
-
- (* Search for memory. *)
- let memory = xpath_int64_default
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=4]/rasd:VirtualQuantity/text()"
(1024L *^ 1024L) in
- let memory = memory *^ 1024L *^ 1024L in
-
- (* Search for number of vCPUs. *)
- let vcpu = xpath_int_default
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=3]/rasd:VirtualQuantity/text()"
1 in
-
- (* BIOS or EFI firmware? *)
- let firmware = xpath_string_default
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/vmw:Config[@vmw:key=\"firmware\"]/@vmw:value"
"bios" in
- let firmware =
- match firmware with
- | "bios" -> BIOS
- | "efi" -> UEFI
- | s ->
- error (f_"unknown Config:firmware value %s (expected \"bios\" or
\"efi\")") s in
-
- (* Helper function to return the parent controller of a disk. *)
- let parent_controller id =
- let expr = sprintf
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:InstanceID/text()=%d]/rasd:ResourceType/text()"
id in
- let controller = xpath_int expr in
-
- (* 6: iscsi controller, 5: ide *)
- match controller with
- | Some 6 -> Some Source_SCSI
- | Some 5 -> Some Source_IDE
+ match name with
| None ->
- warning (f_"ova disk has no parent controller, please report this as a bug
supplying the *.ovf file extracted from the ova");
- None
- | Some controller ->
- warning (f_"ova disk has an unknown VMware controller type (%d), please
report this as a bug supplying the *.ovf file extracted from the ova")
- controller;
- None
- in
-
- (* Hard disks (ResourceType = 17). *)
- let disks = ref [] in
- let () =
- let expr =
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=17]"
in
- let obj = Xml.xpath_eval_expression xpathctx expr in
- let nr_nodes = Xml.xpathobj_nr_nodes obj in
- for i = 0 to nr_nodes-1 do
- let n = Xml.xpathobj_node obj i in
- Xml.xpathctx_set_current_context xpathctx n;
-
- (* XXX We assume the OVF lists these in order.
- let address = xpath_int "rasd:AddressOnParent/text()" in
- *)
-
- (* Find the parent controller. *)
- let parent_id = xpath_int "rasd:Parent/text()" in
- let controller =
- match parent_id with
- | None -> None
- | Some id -> parent_controller id in
-
- Xml.xpathctx_set_current_context xpathctx n;
- let file_id = xpath_string_default "rasd:HostResource/text()"
"" in
- let rex = Str.regexp "^\\(ovf:\\)?/disk/\\(.*\\)" in
- if Str.string_match rex file_id 0 then (
- (* Chase the references through to the actual file name. *)
- let file_id = Str.matched_group 2 file_id in
- let expr = sprintf
"/ovf:Envelope/ovf:DiskSection/ovf:Disk[@ovf:diskId='%s']/@ovf:fileRef"
file_id in
- let file_ref =
- match xpath_string expr with
- | None -> error (f_"error parsing disk fileRef")
- | Some s -> s in
- let expr = sprintf
"/ovf:Envelope/ovf:References/ovf:File[@ovf:id='%s']/@ovf:href" file_ref
in
- let filename =
- match xpath_string expr with
- | None -> error (f_"no href in ovf:File (id=%s)") file_ref
- | Some s -> s in
-
- let expr = sprintf
"/ovf:Envelope/ovf:References/ovf:File[@ovf:id='%s']/@ovf:compression"
file_ref in
- let compressed =
- match xpath_string expr with
- | None | Some "identity" -> false
- | Some "gzip" -> true
- | Some s -> error (f_"unsupported compression in OVF: %s") s in
-
- let partial =
- if compressed && partial then (
- (* We cannot access compressed disk inside the tar;
- * we have to extract it.
- *)
- untar ~paths:[(subdirectory exploded ovf_folder) // filename]
- ova tmpdir;
- false
- )
- else
- partial in
-
- let filename =
- if partial then
- (subdirectory exploded ovf_folder) // filename
- else (
- (* Does the file exist and is it readable? *)
- Unix.access (ovf_folder // filename) [Unix.R_OK];
- ovf_folder // filename
- ) in
+ warning (f_"could not parse ovf:Name from OVF document");
+ name_from_disk ova
+ | Some name -> name in
- (* The spec allows the file to be gzip-compressed, in which case
- * we must uncompress it into the tmpdir.
- *)
- let filename =
- if compressed then (
- let new_filename = tmpdir // String.random8 () ^ ".vmdk" in
- let cmd =
- sprintf "zcat %s > %s" (quote filename) (quote new_filename)
in
- if shell_command cmd <> 0 then
- error (f_"error uncompressing %s, see earlier error messages")
- filename;
- new_filename
- )
- else filename in
+ let disks = List.map (
+ fun ({ href = href; compressed = compressed } as disk) ->
+ let partial =
+ if compressed && partial then (
+ (* We cannot access compressed disk inside the tar;
+ * we have to extract it.
+ *)
+ untar ~paths:[(subdirectory exploded ovf_folder) // href]
+ ova tmpdir;
+ false
+ )
+ else
+ partial in
+
+ let filename =
+ if partial then
+ (subdirectory exploded ovf_folder) // href
+ else (
+ (* Does the file exist and is it readable? *)
+ Unix.access (ovf_folder // href) [Unix.R_OK];
+ ovf_folder // href
+ ) in
+
+ (* The spec allows the file to be gzip-compressed, in which case
+ * we must uncompress it into the tmpdir.
+ *)
+ let filename =
+ if compressed then (
+ let new_filename = tmpdir // String.random8 () ^ ".vmdk" in
+ let cmd =
+ sprintf "zcat %s > %s" (quote filename) (quote new_filename)
in
+ if shell_command cmd <> 0 then
+ error (f_"error uncompressing %s, see earlier error messages")
+ filename;
+ new_filename
+ )
+ else filename in
- let qemu_uri =
- if not partial then (
- filename
- )
- else (
- let offset, size =
- try find_file_in_tar ova filename
- with
- | Not_found ->
- error (f_"file '%s' not found in the ova") filename
- | Failure msg -> error (f_"%s") msg in
- (* QEMU requires size aligned to 512 bytes. This is safe because
- * tar also works with 512 byte blocks.
- *)
- let size = roundup64 size 512L in
- let doc = [
+ let qemu_uri =
+ if not partial then (
+ filename
+ )
+ else (
+ let offset, size =
+ try find_file_in_tar ova filename
+ with
+ | Not_found ->
+ error (f_"file '%s' not found in the ova") filename
+ | Failure msg -> error (f_"%s") msg in
+ (* QEMU requires size aligned to 512 bytes. This is safe because
+ * tar also works with 512 byte blocks.
+ *)
+ let size = roundup64 size 512L in
+ let doc = [
"file", JSON.Dict [
- "driver", JSON.String "raw";
- "offset", JSON.Int64 offset;
- "size", JSON.Int64 size;
- "file", JSON.Dict [
- "driver", JSON.String "file";
- "filename", JSON.String ova]
- ]
- ] in
- let uri =
- sprintf "json:%s" (JSON.string_of_doc ~fmt:JSON.Compact doc)
in
- debug "json: %s" uri;
- uri
- ) in
-
- let disk = {
- s_disk_id = i;
- s_qemu_uri = qemu_uri;
- s_format = Some "vmdk";
- s_controller = controller;
- } in
- push_front disk disks;
- ) else
- error (f_"could not parse disk rasd:HostResource from OVF document")
- done in
- let disks = List.rev !disks in
-
- (* Floppies (ResourceType = 14), CDs (ResourceType = 15) and
- * CDROMs (ResourceType = 16). (What is the difference?) Try hard
- * to preserve the original ordering from the OVF.
- *)
- let removables = ref [] in
- let () =
- let expr =
-
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=14
or rasd:ResourceType/text()=15 or rasd:ResourceType/text()=16]" in
- let obj = Xml.xpath_eval_expression xpathctx expr in
- let nr_nodes = Xml.xpathobj_nr_nodes obj in
- for i = 0 to nr_nodes-1 do
- let n = Xml.xpathobj_node obj i in
- Xml.xpathctx_set_current_context xpathctx n;
- let id =
- match xpath_int "rasd:ResourceType/text()" with
- | None -> assert false
- | Some (14|15|16 as i) -> i
- | Some _ -> assert false in
-
- let slot = xpath_int "rasd:AddressOnParent/text()" in
-
- (* Find the parent controller. *)
- let parent_id = xpath_int "rasd:Parent/text()" in
- let controller =
- match parent_id with
- | None -> None
- | Some id -> parent_controller id in
-
- let typ =
- match id with
- | 14 -> Floppy
- | 15 | 16 -> CDROM
- | _ -> assert false in
- let disk = {
- s_removable_type = typ;
- s_removable_controller = controller;
- s_removable_slot = slot;
- } in
- push_front disk removables;
- done in
- let removables = List.rev !removables in
-
- (* Search for networks ResourceType: 10 *)
- let nics = ref [] in
- let obj = Xml.xpath_eval_expression xpathctx
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=10]"
in
- let nr_nodes = Xml.xpathobj_nr_nodes obj in
- for i = 0 to nr_nodes-1 do
- let n = Xml.xpathobj_node obj i in
- Xml.xpathctx_set_current_context xpathctx n;
- let vnet =
- xpath_string_default "rasd:ElementName/text()"
(sprintf"eth%d" i) in
- let nic = {
- s_mac = None;
- s_nic_model = None;
- s_vnet = vnet;
- s_vnet_orig = vnet;
- s_vnet_type = Network;
- } in
- push_front nic nics
- done;
+ "driver", JSON.String "raw";
+ "offset", JSON.Int64 offset;
+ "size", JSON.Int64 size;
+ "file", JSON.Dict [
+ "driver", JSON.String
"file";
+ "filename", JSON.String ova]
+ ]
+ ] in
+ let uri =
+ sprintf "json:%s" (JSON.string_of_doc ~fmt:JSON.Compact doc) in
+ debug "json: %s" uri;
+ uri
+ ) in
+
+ { disk.source_disk with s_qemu_uri = qemu_uri }
+ ) disks in
let source = {
s_hypervisor = VMware;
@@ -481,7 +316,7 @@ object
s_sound = None;
s_disks = disks;
s_removables = removables;
- s_nics = List.rev !nics;
+ s_nics = nics;
} in
source
diff --git a/v2v/parse_ovf_from_ova.ml b/v2v/parse_ovf_from_ova.ml
new file mode 100644
index 0000000..989483e
--- /dev/null
+++ b/v2v/parse_ovf_from_ova.ml
@@ -0,0 +1,226 @@
+(* virt-v2v
+ * Copyright (C) 2009-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.
+ *)
+
+(* Parse OVF from an externally produced OVA file. *)
+
+open Common_gettext.Gettext
+open Common_utils
+open Unix_utils
+
+open Types
+open Utils
+open Xpath_helpers
+
+open Printf
+
+type ovf_disk = {
+ source_disk : Types.source_disk;
+ href : string; (* The <File href> from the OVF file. *)
+ compressed : bool; (* If the file is gzip compressed. *)
+}
+
+let parse_ovf_from_ova ovf_filename =
+ let xml = read_whole_file ovf_filename in
+ let doc = Xml.parse_memory xml in
+
+ (* Handle namespaces. *)
+ let xpathctx = Xml.xpath_new_context doc in
+ Xml.xpath_register_ns xpathctx
+ "ovf"
"http://schemas.dmtf.org/ovf/envelope/1";
+ Xml.xpath_register_ns xpathctx
+ "rasd"
"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData";
+ Xml.xpath_register_ns xpathctx
+ "vmw" "http://www.vmware.com/schema/ovf";
+ Xml.xpath_register_ns xpathctx
+ "vssd"
"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData";
+
+ let xpath_string = xpath_string xpathctx
+ and xpath_int = xpath_int xpathctx
+ and xpath_string_default = xpath_string_default xpathctx
+ and xpath_int_default = xpath_int_default xpathctx
+ and xpath_int64_default = xpath_int64_default xpathctx in
+
+ let rec parse_top () =
+ (* Search for vm name. *)
+ let name =
+ match xpath_string "/ovf:Envelope/ovf:VirtualSystem/ovf:Name/text()"
with
+ | None | Some "" -> None
+ | Some _ as name -> name in
+
+ (* Search for memory. *)
+ let memory = xpath_int64_default
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=4]/rasd:VirtualQuantity/text()"
(1024L *^ 1024L) in
+ let memory = memory *^ 1024L *^ 1024L in
+
+ (* Search for number of vCPUs. *)
+ let vcpu = xpath_int_default
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=3]/rasd:VirtualQuantity/text()"
1 in
+
+ (* BIOS or EFI firmware? *)
+ let firmware = xpath_string_default
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/vmw:Config[@vmw:key=\"firmware\"]/@vmw:value"
"bios" in
+ let firmware =
+ match firmware with
+ | "bios" -> BIOS
+ | "efi" -> UEFI
+ | s ->
+ error (f_"unknown Config:firmware value %s (expected \"bios\" or
\"efi\")") s in
+
+ name, memory, vcpu, firmware,
+ parse_disks (), parse_removables (), parse_nics ()
+
+ (* Helper function to return the parent controller of a disk. *)
+ and parent_controller id =
+ let expr = sprintf
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:InstanceID/text()=%d]/rasd:ResourceType/text()"
id in
+ let controller = xpath_int expr in
+
+ (* 6: iscsi controller, 5: ide *)
+ match controller with
+ | Some 6 -> Some Source_SCSI
+ | Some 5 -> Some Source_IDE
+ | None ->
+ warning (f_"ova disk has no parent controller, please report this as a bug
supplying the *.ovf file extracted from the ova");
+ None
+ | Some controller ->
+ warning (f_"ova disk has an unknown VMware controller type (%d), please
report this as a bug supplying the *.ovf file extracted from the ova")
+ controller;
+ None
+
+ (* Hard disks (ResourceType = 17). *)
+ and parse_disks () =
+ let disks = ref [] in
+ let expr =
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=17]"
in
+ let obj = Xml.xpath_eval_expression xpathctx expr in
+ let nr_nodes = Xml.xpathobj_nr_nodes obj in
+ for i = 0 to nr_nodes-1 do
+ let n = Xml.xpathobj_node obj i in
+ Xml.xpathctx_set_current_context xpathctx n;
+
+ (* XXX We assume the OVF lists these in order.
+ let address = xpath_int "rasd:AddressOnParent/text()" in
+ *)
+
+ (* Find the parent controller. *)
+ let parent_id = xpath_int "rasd:Parent/text()" in
+ let controller =
+ match parent_id with
+ | None -> None
+ | Some id -> parent_controller id in
+
+ Xml.xpathctx_set_current_context xpathctx n;
+ let file_id = xpath_string_default "rasd:HostResource/text()"
"" in
+ let rex = Str.regexp "^\\(ovf:\\)?/disk/\\(.*\\)" in
+ if Str.string_match rex file_id 0 then (
+ (* Chase the references through to the actual file name. *)
+ let file_id = Str.matched_group 2 file_id in
+ let expr = sprintf
"/ovf:Envelope/ovf:DiskSection/ovf:Disk[@ovf:diskId='%s']/@ovf:fileRef"
file_id in
+ let file_ref =
+ match xpath_string expr with
+ | None -> error (f_"error parsing disk fileRef")
+ | Some s -> s in
+ let expr = sprintf
"/ovf:Envelope/ovf:References/ovf:File[@ovf:id='%s']/@ovf:href" file_ref
in
+ let href =
+ match xpath_string expr with
+ | None -> error (f_"no href in ovf:File (id=%s)") file_ref
+ | Some s -> s in
+
+ let expr = sprintf
"/ovf:Envelope/ovf:References/ovf:File[@ovf:id='%s']/@ovf:compression"
file_ref in
+ let compressed =
+ match xpath_string expr with
+ | None | Some "identity" -> false
+ | Some "gzip" -> true
+ | Some s -> error (f_"unsupported compression in OVF: %s") s in
+
+ let disk = {
+ source_disk = {
+ s_disk_id = i;
+ s_qemu_uri = "";
+ s_format = Some "vmdk";
+ s_controller = controller;
+ };
+ href = href;
+ compressed = compressed
+ } in
+ push_front disk disks;
+ ) else
+ error (f_"could not parse disk rasd:HostResource from OVF document")
+ done;
+ List.rev !disks
+
+ (* Floppies (ResourceType = 14), CDs (ResourceType = 15) and
+ * CDROMs (ResourceType = 16). (What is the difference?) Try hard
+ * to preserve the original ordering from the OVF.
+ *)
+ and parse_removables () =
+ let removables = ref [] in
+ let expr =
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=14
or rasd:ResourceType/text()=15 or rasd:ResourceType/text()=16]" in
+ let obj = Xml.xpath_eval_expression xpathctx expr in
+ let nr_nodes = Xml.xpathobj_nr_nodes obj in
+ for i = 0 to nr_nodes-1 do
+ let n = Xml.xpathobj_node obj i in
+ Xml.xpathctx_set_current_context xpathctx n;
+ let id =
+ match xpath_int "rasd:ResourceType/text()" with
+ | None -> assert false
+ | Some (14|15|16 as i) -> i
+ | Some _ -> assert false in
+
+ let slot = xpath_int "rasd:AddressOnParent/text()" in
+
+ (* Find the parent controller. *)
+ let parent_id = xpath_int "rasd:Parent/text()" in
+ let controller =
+ match parent_id with
+ | None -> None
+ | Some id -> parent_controller id in
+
+ let typ =
+ match id with
+ | 14 -> Floppy
+ | 15 | 16 -> CDROM
+ | _ -> assert false in
+ let disk = {
+ s_removable_type = typ;
+ s_removable_controller = controller;
+ s_removable_slot = slot;
+ } in
+ push_front disk removables;
+ done;
+ List.rev !removables
+
+ (* Search for networks ResourceType: 10 *)
+ and parse_nics () =
+ let nics = ref [] in
+ let expr =
"/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=10]"
in
+ let obj = Xml.xpath_eval_expression xpathctx expr in
+ let nr_nodes = Xml.xpathobj_nr_nodes obj in
+ for i = 0 to nr_nodes-1 do
+ let n = Xml.xpathobj_node obj i in
+ Xml.xpathctx_set_current_context xpathctx n;
+ let vnet =
+ xpath_string_default "rasd:ElementName/text()"
(sprintf"eth%d" i) in
+ let nic = {
+ s_mac = None;
+ s_nic_model = None;
+ s_vnet = vnet;
+ s_vnet_orig = vnet;
+ s_vnet_type = Network;
+ } in
+ push_front nic nics
+ done;
+ List.rev !nics
+ in
+
+ parse_top ()
diff --git a/v2v/parse_ovf_from_ova.mli b/v2v/parse_ovf_from_ova.mli
new file mode 100644
index 0000000..3f60abc
--- /dev/null
+++ b/v2v/parse_ovf_from_ova.mli
@@ -0,0 +1,36 @@
+(* virt-v2v
+ * Copyright (C) 2009-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.
+ *)
+
+(** Parse OVF from an externally produced OVA file.
+
+ This is used by [-i ova] only. OVA files are not a real standard
+ so we must make some assumptions here, eg. about disk format
+ being VMDK, which would not be true for oVirt. *)
+
+type ovf_disk = {
+ source_disk : Types.source_disk;
+ href : string; (** The <File href> from the OVF file. *)
+ compressed : bool; (** If the href is gzip compressed. *)
+}
+(** A VMDK disk from a parsed OVF. *)
+
+val parse_ovf_from_ova : string -> string option * int64 * int * Types.source_firmware
* ovf_disk list * Types.source_removable list * Types.source_nic list
+(** Parse an OVF file.
+
+ The returned tuple is
+ [name, memory, vcpu, firmware, disks, removables, nics] *)
--
2.9.3