Laszlo Ersek
2021-Dec-10 11:35 UTC
[Libguestfs] [virt-v2v PATCH v2 0/3] lib/create_ovf: populate "actual size" attributes again
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2027598 v1: https://listman.redhat.com/archives/libguestfs/2021-December/msg00096.html In v2, move the new functions to the Utils module, from Nbdkit. Thanks, Laszlo Laszlo Ersek (3): lib/utils: add the "Utils.with_nbd_connect_unix" helper function lib/utils: add the "Utils.get_disk_allocated" helper function lib/create_ovf: populate "actual size" attributes again lib/Makefile.am | 2 +- lib/create_ovf.ml | 33 +++++++-------- lib/create_ovf.mli | 2 +- lib/utils.ml | 42 ++++++++++++++++++++ lib/utils.mli | 18 +++++++++ output/output_rhv.ml | 2 +- output/output_rhv_upload.ml | 2 +- output/output_vdsm.ml | 1 + tests/test-v2v-o-rhv.ovf.expected | 4 +- tests/test-v2v-o-vdsm-options.ovf.expected | 4 +- 10 files changed, 84 insertions(+), 26 deletions(-) base-commit: 9bb0e7f1d22936bf2c6c328c5ca48f32c7b9420a -- 2.19.1.3.g30247aa5d201
Laszlo Ersek
2021-Dec-10 11:35 UTC
[Libguestfs] [virt-v2v PATCH v2 1/3] lib/utils: add the "Utils.with_nbd_connect_unix" helper function
Connecting to an NBD server temporarily, for a "one-shot" operation,
is
quite similar to "Std_utils.with_open_in" and
"Std_utils.with_open_out",
as there are cleanup operations regardless of whether the "one-shot"
operation completes successfully or throws an exception.
Introduce the "Utils.with_nbd_connect_unix" function, which takes a
Unix
domain socket pathname, a list of metadata contexts to request from the
NBD server, and calls a function with the live NBD server connection.
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2027598
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
Notes:
v2:
- move the function to the Utils module, from Nbdkit [Rich]
- rename the function to "with_nbd_connect_unix" (from
"with_connect_unix")
- update the commit message
lib/Makefile.am | 2 +-
lib/utils.mli | 10 ++++++++++
lib/utils.ml | 12 ++++++++++++
3 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/lib/Makefile.am b/lib/Makefile.am
index c274b9ecf6c7..1fab25b326f5 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -80,7 +80,7 @@ BOBJECTS = config.cmo $(SOURCES_ML:.ml=.cmo)
XOBJECTS = $(BOBJECTS:.cmo=.cmx)
OCAMLPACKAGES = \
- -package str,unix \
+ -package str,unix,nbd \
-I $(builddir) \
-I $(top_builddir)/common/mlgettext \
-I $(top_builddir)/common/mlpcre \
diff --git a/lib/utils.mli b/lib/utils.mli
index c9b4bd631d65..f41e825e747d 100644
--- a/lib/utils.mli
+++ b/lib/utils.mli
@@ -77,3 +77,13 @@ val metaversion : string
Eventually we may switch to using an "open metadata" format
instead
(eg. XML). *)
+
+val with_nbd_connect_unix : socket:string ->
+ meta_contexts:string list ->
+ f:(NBD.t -> 'a) ->
+ 'a
+(** [with_nbd_connect_unix socket meta_contexts f] calls function [f] with the
+ NBD server at Unix domain socket [socket] connected, and the metadata
+ contexts in [meta_contexts] requested (each of which is not necessarily
+ supported by the server though). The connection is torn down either on
+ normal return or if the function [f] throws an exception. *)
diff --git a/lib/utils.ml b/lib/utils.ml
index c18a467cb57c..86967f342e6e 100644
--- a/lib/utils.ml
+++ b/lib/utils.ml
@@ -164,3 +164,15 @@ let rec wait_for_file filename timeout )
let metaversion = Digest.to_hex (Digest.string Config.package_version_full)
+
+let with_nbd_connect_unix ~socket ~meta_contexts ~f + let nbd = NBD.create ()
in
+ protect
+ ~f:(fun () ->
+ List.iter (NBD.add_meta_context nbd) meta_contexts;
+ NBD.connect_unix nbd socket;
+ protect
+ ~f:(fun () -> f nbd)
+ ~finally:(fun () -> NBD.shutdown nbd)
+ )
+ ~finally:(fun () -> NBD.close nbd)
--
2.19.1.3.g30247aa5d201
Laszlo Ersek
2021-Dec-10 11:35 UTC
[Libguestfs] [virt-v2v PATCH v2 2/3] lib/utils: add the "Utils.get_disk_allocated" helper function
Add the "Utils.get_disk_allocated" helper function, which calculates
the
number of allocated bytes in an output disk image, through a corresponding
NBD server connection.
Original "NBD.block_status" summation example code by Rich Jones
(thanks!), chunking, metadata context simplification, and virt-v2v
integration by myself. Chunking added because "NBD.block_status" is
not
64-bit clean just yet (Eric Blake is working on it).
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2027598
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
Notes:
v2:
- move the function to the Utils module, from Nbdkit [Rich]
- update the commit message
- replace with_connect_unix call with a call to with_nbd_connect_unix
lib/utils.mli | 8 ++++++
lib/utils.ml | 30 ++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/lib/utils.mli b/lib/utils.mli
index f41e825e747d..3096eb146650 100644
--- a/lib/utils.mli
+++ b/lib/utils.mli
@@ -87,3 +87,11 @@ val with_nbd_connect_unix : socket:string ->
contexts in [meta_contexts] requested (each of which is not necessarily
supported by the server though). The connection is torn down either on
normal return or if the function [f] throws an exception. *)
+
+val get_disk_allocated : dir:string -> disknr:int -> int64 option
+(** Callable only in the finalization step. [get_disk_allocated dir disknr]
+ examines output disk [disknr] through the corresponding NBD server socket
+ that resides in [dir]. Returns the number of bytes allocated in the disk
+ image, according to the "base:allocation" metadata context. If
the context
+ is not supported by the NBD server behind the socket, the function returns
+ None. *)
diff --git a/lib/utils.ml b/lib/utils.ml
index 86967f342e6e..d6861d08898f 100644
--- a/lib/utils.ml
+++ b/lib/utils.ml
@@ -176,3 +176,33 @@ let with_nbd_connect_unix ~socket ~meta_contexts ~f
~finally:(fun () -> NBD.shutdown nbd)
)
~finally:(fun () -> NBD.close nbd)
+
+let get_disk_allocated ~dir ~disknr + let socket = sprintf
"%s/out%d" dir disknr
+ and alloc_ctx = "base:allocation" in
+ with_nbd_connect_unix ~socket ~meta_contexts:[alloc_ctx]
+ ~f:(fun nbd ->
+ if NBD.can_meta_context nbd alloc_ctx then (
+ (* Get the list of extents, using a 2GiB chunk size as hint. *)
+ let size = NBD.get_size nbd
+ and allocated = ref 0_L
+ and fetch_offset = ref 0_L in
+ while !fetch_offset < size do
+ let remaining = size -^ !fetch_offset in
+ let fetch_size = min 0x8000_0000_L remaining in
+ NBD.block_status nbd fetch_size !fetch_offset
+ (fun ctx offset entries err ->
+ assert (ctx = alloc_ctx);
+ for i = 0 to Array.length entries / 2 - 1 do
+ let len = Int64.of_int32 entries.(i * 2)
+ and typ = entries.(i * 2 + 1) in
+ if Int32.logand typ 1_l = 0_l then
+ allocated := !allocated +^ len;
+ fetch_offset := !fetch_offset +^ len
+ done;
+ 0
+ )
+ done;
+ Some !allocated
+ ) else None
+ )
--
2.19.1.3.g30247aa5d201
Laszlo Ersek
2021-Dec-10 11:35 UTC
[Libguestfs] [virt-v2v PATCH v2 3/3] lib/create_ovf: populate "actual size" attributes again
Commit 255722cbf39a ("v2v: Modular virt-v2v", 2021-09-07) removed the
following attributes from the OVF output:
- ovf:Envelope/References/File/@ovf:size
-
ovf:Envelope/Section[@xsi:type='ovf:DiskSection_Type']/Disk/@ovf:actual_size
Unfortunately, ovirt-engine considers the second one mandatory; without
it, ovirt-engine refuses to import the OVF.
Restore both attributes, using the utility functions added to the Utils
module previously.
(If we do not have the information necessary to fill in @ovf:actual_size,
we still have to generate the attribute, only with empty contents.
Ovirt-engine does cope with that.)
Fixes: 255722cbf39afc0b012e2ac00d16fa6ba2f8c21f
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2027598
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
Notes:
v2:
- update commit message
- drop "Nbdkit." module selector (as get_disk_allocated is in
Utils now,
and "lib/create_ovf.ml" has Utils open anyway)
lib/create_ovf.mli | 2 +-
lib/create_ovf.ml | 33 +++++++++-----------
output/output_rhv.ml | 2 +-
output/output_rhv_upload.ml | 2 +-
output/output_vdsm.ml | 1 +
tests/test-v2v-o-rhv.ovf.expected | 4 +--
tests/test-v2v-o-vdsm-options.ovf.expected | 4 +--
7 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/lib/create_ovf.mli b/lib/create_ovf.mli
index 701152d93dd2..0d1cc5a9311a 100644
--- a/lib/create_ovf.mli
+++ b/lib/create_ovf.mli
@@ -46,7 +46,7 @@ val ovf_flavour_to_string : ovf_flavour -> string
val create_ovf : Types.source -> Types.inspect ->
Types.target_meta -> int64 list ->
Types.output_allocation -> string -> string -> string
list ->
- string list -> string -> ovf_flavour -> DOM.doc
+ string list -> string -> string -> ovf_flavour ->
DOM.doc
(** Create the OVF file.
Actually a {!DOM} document is created, not a file. It can be written
diff --git a/lib/create_ovf.ml b/lib/create_ovf.ml
index c9f14635363e..dbac3405989b 100644
--- a/lib/create_ovf.ml
+++ b/lib/create_ovf.ml
@@ -531,7 +531,7 @@ let rec create_ovf source inspect
{ output_name; guestcaps; target_firmware; target_nics }
sizes
output_alloc output_format
- sd_uuid image_uuids vol_uuids vm_uuid ovf_flavour + sd_uuid
image_uuids vol_uuids dir vm_uuid ovf_flavour assert (List.length sizes =
List.length vol_uuids);
let memsize_mb = source.s_memory /^ 1024L /^ 1024L in
@@ -745,7 +745,7 @@ let rec create_ovf source inspect
(* Add disks to the OVF XML. *)
add_disks sizes guestcaps output_alloc output_format
- sd_uuid image_uuids vol_uuids ovf_flavour ovf;
+ sd_uuid image_uuids vol_uuids dir ovf_flavour ovf;
(* Old virt-v2v ignored removable media. XXX *)
@@ -791,7 +791,7 @@ and get_flavoured_section ovf ovirt_path rhv_path
rhv_path_attr = function
(* This modifies the OVF DOM, adding a section for each disk. *)
and add_disks sizes guestcaps output_alloc output_format
- sd_uuid image_uuids vol_uuids ovf_flavour ovf + sd_uuid image_uuids
vol_uuids dir ovf_flavour ovf let references let nodes = path_to_nodes
ovf ["ovf:Envelope"; "References"] in
match nodes with
@@ -839,13 +839,7 @@ and add_disks sizes guestcaps output_alloc output_format
b /^ 1073741824L
in
let size_gb = bytes_to_gb size in
- (* virt-v2v 1.4x used to try to collect the actual size of the
- * sparse disk. It would be possible to get this information
- * accurately now by reading the extent map of the output disk
- * (this function is called during finalization), but we don't
- * yet do that. XXX
- *)
- let actual_size_gb = None in
+ let actual_size = get_disk_allocated ~dir ~disknr:i in
let format_for_rhv match output_format with
@@ -867,13 +861,11 @@ and add_disks sizes guestcaps output_alloc output_format
"ovf:id", vol_uuid;
"ovf:description", generated_by;
] in
- (* See note above about actual_size_gb
- (match t.target_overlay.ov_stats.target_actual_size with
+ (match actual_size with
| None -> ()
| Some actual_size ->
List.push_back attrs ("ovf:size", Int64.to_string
actual_size)
);
- *)
e "File" !attrs [] in
append_child disk references;
@@ -899,11 +891,16 @@ and add_disks sizes guestcaps output_alloc output_format
"ovf:disk-type", "System"; (* RHBZ#744538 *)
"ovf:boot", if is_bootable_drive then "True" else
"False";
] in
- (match actual_size_gb with
- | None -> ()
- | Some actual_size_gb ->
- List.push_back attrs ("ovf:actual_size", Int64.to_string
actual_size_gb)
- );
+ (* Ovirt-engine considers the "ovf:actual_size" attribute
mandatory. If
+ * we don't know the actual size, we must create the attribute with
+ * empty contents.
+ *)
+ List.push_back attrs
+ ("ovf:actual_size",
+ match actual_size with
+ | None -> ""
+ | Some actual_size -> Int64.to_string (bytes_to_gb actual_size)
+ );
e "Disk" !attrs [] in
append_child disk disk_section;
diff --git a/output/output_rhv.ml b/output/output_rhv.ml
index 4550dc4f306f..b902a7ee4619 100644
--- a/output/output_rhv.ml
+++ b/output/output_rhv.ml
@@ -183,7 +183,7 @@ and rhv_finalize dir source inspect target_meta
(* Create the metadata. *)
let ovf Create_ovf.create_ovf source inspect target_meta sizes
- output_alloc output_format esd_uuid image_uuids vol_uuids vm_uuid
+ output_alloc output_format esd_uuid image_uuids vol_uuids dir vm_uuid
Create_ovf.RHVExportStorageDomain in
(* Write it to the metadata file. *)
diff --git a/output/output_rhv_upload.ml b/output/output_rhv_upload.ml
index 7099ecefd076..7c77cace5308 100644
--- a/output/output_rhv_upload.ml
+++ b/output/output_rhv_upload.ml
@@ -419,7 +419,7 @@ and rhv_upload_finalize dir source inspect target_meta
(* Create the metadata. *)
let ovf Create_ovf.create_ovf source inspect target_meta disk_sizes
- Sparse output_format sd_uuid disk_uuids vol_uuids vm_uuid
+ Sparse output_format sd_uuid disk_uuids vol_uuids dir vm_uuid
OVirt in
let ovf = DOM.doc_to_string ovf in
diff --git a/output/output_vdsm.ml b/output/output_vdsm.ml
index c9b552805ccd..ce0d5b5e4b2a 100644
--- a/output/output_vdsm.ml
+++ b/output/output_vdsm.ml
@@ -201,6 +201,7 @@ and vdsm_finalize dir source inspect target_meta
output_alloc output_format dd_uuid
image_uuids
vol_uuids
+ dir
vm_uuid
ovf_flavour in
diff --git a/tests/test-v2v-o-rhv.ovf.expected
b/tests/test-v2v-o-rhv.ovf.expected
index 5fda41c9488e..25e492fdbabc 100644
--- a/tests/test-v2v-o-rhv.ovf.expected
+++ b/tests/test-v2v-o-rhv.ovf.expected
@@ -2,7 +2,7 @@
<ovf:Envelope
xmlns:rasd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData'
xmlns:vssd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:ovf='http://schemas.dmtf.org/ovf/envelope/1/'
xmlns:ovirt='http://www.ovirt.org/ovf' ovf:version='0.9'>
<!-- generated by virt-v2v -->
<References>
- <File ovf:href='#DISK_ID#/#VOL_ID#' ovf:id='#VOL_ID#'
ovf:description='generated by virt-v2v'/>
+ <File ovf:href='#DISK_ID#/#VOL_ID#' ovf:id='#VOL_ID#'
ovf:description='generated by virt-v2v' ovf:size='#SIZE#'/>
</References>
<Section xsi:type='ovf:NetworkSection_Type'>
<Info>List of networks</Info>
@@ -10,7 +10,7 @@
</Section>
<Section xsi:type='ovf:DiskSection_Type'>
<Info>List of Virtual Disks</Info>
- <Disk ovf:diskId='#VOL_ID#' ovf:size='1'
ovf:capacity='536870912' ovf:fileRef='#DISK_ID#/#VOL_ID#'
ovf:parentRef='' ovf:vm_snapshot_id='#UUID#'
ovf:volume-format='RAW' ovf:volume-type='Sparse'
ovf:format='http://en.wikipedia.org/wiki/Byte'
ovf:disk-interface='VirtIO' ovf:disk-type='System'
ovf:boot='True'/>
+ <Disk ovf:diskId='#VOL_ID#' ovf:size='1'
ovf:capacity='536870912' ovf:fileRef='#DISK_ID#/#VOL_ID#'
ovf:parentRef='' ovf:vm_snapshot_id='#UUID#'
ovf:volume-format='RAW' ovf:volume-type='Sparse'
ovf:format='http://en.wikipedia.org/wiki/Byte'
ovf:disk-interface='VirtIO' ovf:disk-type='System'
ovf:boot='True' ovf:actual_size='1'/>
</Section>
<Content ovf:id='out'
xsi:type='ovf:VirtualSystem_Type'>
<Name>windows</Name>
diff --git a/tests/test-v2v-o-vdsm-options.ovf.expected
b/tests/test-v2v-o-vdsm-options.ovf.expected
index 23ca180f4c2f..bd5b5e7d38ec 100644
--- a/tests/test-v2v-o-vdsm-options.ovf.expected
+++ b/tests/test-v2v-o-vdsm-options.ovf.expected
@@ -2,7 +2,7 @@
<ovf:Envelope
xmlns:rasd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData'
xmlns:vssd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:ovf='http://schemas.dmtf.org/ovf/envelope/1/'
xmlns:ovirt='http://www.ovirt.org/ovf' ovf:version='0.9'>
<!-- generated by virt-v2v -->
<References>
- <File ovf:href='VOL' ovf:id='VOL'
ovf:description='generated by virt-v2v'/>
+ <File ovf:href='VOL' ovf:id='VOL'
ovf:description='generated by virt-v2v' ovf:size='#SIZE#'/>
</References>
<NetworkSection>
<Info>List of networks</Info>
@@ -10,7 +10,7 @@
</NetworkSection>
<DiskSection>
<Info>List of Virtual Disks</Info>
- <Disk ovf:diskId='IMAGE' ovf:size='1'
ovf:capacity='536870912' ovf:fileRef='VOL'
ovf:parentRef='' ovf:vm_snapshot_id='#UUID#'
ovf:volume-format='COW' ovf:volume-type='Sparse'
ovf:format='http://en.wikipedia.org/wiki/Byte'
ovf:disk-interface='VirtIO' ovf:disk-type='System'
ovf:boot='True'/>
+ <Disk ovf:diskId='IMAGE' ovf:size='1'
ovf:capacity='536870912' ovf:fileRef='VOL'
ovf:parentRef='' ovf:vm_snapshot_id='#UUID#'
ovf:volume-format='COW' ovf:volume-type='Sparse'
ovf:format='http://en.wikipedia.org/wiki/Byte'
ovf:disk-interface='VirtIO' ovf:disk-type='System'
ovf:boot='True' ovf:actual_size='1'/>
</DiskSection>
<VirtualSystem ovf:id='VM'>
<Name>windows</Name>
--
2.19.1.3.g30247aa5d201