On 11/22/22 16:47, Richard W.M. Jones wrote: > This tool can be used to estimate the disk space needed before doing a > virt-v2v conversion. > > It is a replacement for the old --print-estimate option which was > dropped in virt-v2v 2.0 (commit 5828c9c7d5 "v2v: Remove > --print-estimate option"). > --- > docs/Makefile.am | 15 ++ > docs/test-v2v-docs.sh | 9 + > docs/virt-v2v-inspector.pod | 252 +++++++++++++++++++ > docs/virt-v2v.pod | 4 + > configure.ac | 1 + > Makefile.am | 3 +- > inspector/Makefile.am | 129 ++++++++++ > tests/Makefile.am | 2 + > inspector/inspector.mli | 19 ++ > inspector/inspector.ml | 472 ++++++++++++++++++++++++++++++++++++ > inspector/dummy.c | 2 + > tests/test-v2v-inspector.sh | 76 ++++++ > .gitignore | 3 + > run.in | 3 +- > 14 files changed, 988 insertions(+), 2 deletions(-)
I've skimmed this. It seems very useful and mostly okay, where "mostly okay" qualifies *my level of understanding* of the code, and not the code itself. There is a bit of code duplication with other parts of the tree, but I think that's entirely OK: this is a new (experimental?) tool, for further feature enablement, so intrusive refactorings are not called for at this point, IMO. I suggest that we merge this ASAP and let people start testing it. One suggestion: migrate the nice info from the cover letter into this actual commit message. Acked-by: Laszlo Ersek <ler...@redhat.com> Laszlo > > diff --git a/docs/Makefile.am b/docs/Makefile.am > index 3668fd4f0c..012c672294 100644 > --- a/docs/Makefile.am > +++ b/docs/Makefile.am > @@ -24,6 +24,7 @@ EXTRA_DIST = \ > virt-v2v-in-place.pod \ > virt-v2v-input-vmware.pod \ > virt-v2v-input-xen.pod \ > + virt-v2v-inspector.pod \ > virt-v2v-output-local.pod \ > virt-v2v-output-openstack.pod \ > virt-v2v-output-rhv.pod \ > @@ -40,6 +41,7 @@ man_MANS = \ > virt-v2v-in-place.1 \ > virt-v2v-input-vmware.1 \ > virt-v2v-input-xen.1 \ > + virt-v2v-inspector.1 \ > virt-v2v-output-local.1 \ > virt-v2v-output-openstack.1 \ > virt-v2v-output-rhv.1 \ > @@ -53,6 +55,7 @@ noinst_DATA = \ > $(top_builddir)/website/virt-v2v-in-place.1.html \ > $(top_builddir)/website/virt-v2v-input-vmware.1.html \ > $(top_builddir)/website/virt-v2v-input-xen.1.html \ > + $(top_builddir)/website/virt-v2v-inspector.1.html \ > $(top_builddir)/website/virt-v2v-output-local.1.html \ > $(top_builddir)/website/virt-v2v-output-openstack.1.html \ > $(top_builddir)/website/virt-v2v-output-rhv.1.html \ > @@ -117,6 +120,18 @@ stamp-virt-v2v-input-xen.pod: virt-v2v-input-xen.pod > $< > touch $@ > > +virt-v2v-inspector.1 $(top_builddir)/website/virt-v2v-inspector.1.html: > stamp-virt-v2v-inspector.pod > + > +stamp-virt-v2v-inspector.pod: virt-v2v-inspector.pod > + $(PODWRAPPER) \ > + --man virt-v2v-inspector.1 \ > + --html $(top_builddir)/website/virt-v2v-inspector.1.html \ > + --path $(top_srcdir)/common/options \ > + --license GPLv2+ \ > + --warning safe \ > + $< > + touch $@ > + > virt-v2v-output-local.1 > $(top_builddir)/website/virt-v2v-output-local.1.html: > stamp-virt-v2v-output-local.pod > > stamp-virt-v2v-output-local.pod: virt-v2v-output-local.pod > diff --git a/docs/test-v2v-docs.sh b/docs/test-v2v-docs.sh > index 92ae39ee57..c0de5a20ce 100755 > --- a/docs/test-v2v-docs.sh > +++ b/docs/test-v2v-docs.sh > @@ -75,3 +75,12 @@ $srcdir/../podcheck.pl virt-v2v-in-place.pod > virt-v2v-in-place \ > --oo,\ > --op,\ > --os > + > +$srcdir/../podcheck.pl virt-v2v-inspector.pod virt-v2v-inspector \ > + --path $srcdir/../common/options \ > + --ignore=\ > +--ic,\ > +--if,\ > +--io,\ > +--ip,\ > +--it > diff --git a/docs/virt-v2v-inspector.pod b/docs/virt-v2v-inspector.pod > new file mode 100644 > index 0000000000..d2f0b66e4f > --- /dev/null > +++ b/docs/virt-v2v-inspector.pod > @@ -0,0 +1,252 @@ > +=head1 NAME > + > +virt-v2v-inspector - Estimate disk space needed before virt-v2v conversion > + > +=head1 SYNOPSIS > + > + virt-v2v-inspector [-i* options] guest > + > +=head1 DESCRIPTION > + > +Virt-v2v-inspector is a companion tool for L<virt-v2v(1)> which can be > +used before conversion to estimate the number of output disks and disk > +space that will be required to complete the virt-v2v conversion. The > +common use for this is to preallocate target disks on management > +systems that need this (like Kubevirt). > + > +This manual page only documents the estimation feature, not all of the > +I<-i*> options which are the same as virt-v2v. You should read > +L<virt-v2v(1)> first. > + > +=head2 Selecting the input guest > + > +You can run virt-v2v-inspector with the same I<-i*> options as > +virt-v2v. (Don't use any I<-o*> options). This will select the guest > +that you want to estimate. > + > +For example to estimate the space required for a guest in a stored > +local disk called F<filename.img> you could do: > + > + virt-v2v-inspector -i disk filename.img > + > +=head2 Output > + > +The output from this tool is an XML document (written to stdout). > + > +=over 4 > + > +=item * > + > +Fields which are annotated with an C<estimated='true'> attribute are > +estimated. Virt-v2v cannot always know exactly the final size of some > +things, such as the exact real size of the output disk, since there > +might be small perturbations between runs. Estimates are usually very > +close to the final values. > + > +=item * > + > +Elements (including sub-trees) which are annotated with an > +C<informational='true'> attribute are for information only. These > +elements might be changed or removed in future versions. If you would > +like to rely on this data in your program please contact the > +developers. > + > +=item * > + > +Numbers representing sizes are always given in bytes. > + > +=back > + > + <?xml version='1.0' encoding='utf-8'?> > + <v2v-inspection> > + <program>virt-v2v-inspector</program> > + <package>virt-v2v</package> > + <version>2.1.9</version> > + > +The E<lt>programE<gt>, E<lt>packageE<gt> and E<lt>versionE<gt> > +elements refer to the current version of virt-v2v-inspector and are > +useful for debugging. Make sure you use the same version of > +virt-v2v-inspector and virt-v2v. > + > + <disks> > + <disk index='0'> > + <virtual-size>6442450944</virtual-size> > + <allocated estimated='true'>1400897536</allocated> > + </disk> > + <disk index='1'> > + <virtual-size>6442450944</virtual-size> > + <allocated estimated='true'>45131520</allocated> > + </disk> > + </disks> > + > +The E<lt>disksE<gt> element lists information about each guest disk. > +The example virtual machine above has two disks. > +E<lt>virtual-sizeE<gt> describes the size of the disk as seen from > +inside the guest, while E<lt>allocatedE<gt> is an estimate of how much > +storage will be needed on the host after conversion. This is assuming > +you use S<I<-oa sparse>> - see the notes below. > + > + <operatingsystem> > + <name>linux</name> > + <distro>fedora</distro> > + <osinfo>fedora32</osinfo> > + <arch>x86_64</arch> > + [...] > + </operatingsystem> > + > +The E<lt>operatingsystemE<gt> element lists information about the > +guest operating system gleaned during conversion, in a manner similar > +to the L<virt-inspector(1)> tool from guestfs-tools. > + > +=head2 Output allocation mode and output format > + > +Virt-v2v supports selecting the output allocation mode (I<-oa> option) > +and output format (I<-of> option, eg. S<I<-of qcow2>>). Since it is > +difficult to predict the effect of these options on the actual space > +occupied by the final image this tool does not account for them. > + > +As a rule of thumb: > + > +=over 4 > + > +=item S<virt-v2v -oa preallocated> > + > +causes the disk images on the target to consume their full virtual > +size (excluding the effect of zero allocations will depends so much on > +the underlying storage that it is often hard even for experts to > +predict). > + > +=item S<virt-v2v -of qcow2> > + > +uses the QCOW2 format where supported which means that the apparent > +size of the file will be equal to its sparse size, but otherwise > +should not affect estimates very much. > + > +=back > + > +=head1 OPTIONS > + > +=over 4 > + > +=item B<--help> > + > +Display help. > + > +=item B<-v> > + > +=item B<--verbose> > + > +Enable verbose messages for debugging. > + > +=item B<-V> > + > +=item B<--version> > + > +Display version number and exit. > + > +=item B<-x> > + > +Enable tracing of libguestfs API calls. > + > +=item B<-i> ... > + > +=item B<-ic> ... > + > +=item B<-if> ... > + > +=item B<-io> ... > + > +=item B<-ip> ... > + > +=item B<-it> ... > + > +All of the I<-i*> options supported by virt-v2v and also supported by > +virt-v2v-inspector. > + > +=item B<-b> ... > + > +=item B<--bridge> ... > + > +=item B<--colors> > + > +=item B<--colours> > + > +=item B<--echo-keys> > + > +=item B<--key> ... > + > +=item B<--keys-from-stdin> > + > +=item B<--mac> ... > + > +=item B<--machine-readable> > + > +=item B<--machine-readable>=format > + > +=item B<-n> ... > + > +=item B<--network> ... > + > +=item B<-q> > + > +=item B<--quiet> > + > +=item B<--root> ... > + > +=item B<--wrap> > + > +These options work in the same way as the equivalent virt-v2v options. > + > +=back > + > +=head1 FILES > + > +Files used are the same as for virt-v2v. See L<virt-v2v(1)/FILES>. > + > +=head1 ENVIRONMENT VARIABLES > + > +Environment variables used are the same as for virt-v2v. See > +L<virt-v2v(1)/ENVIRONMENT VARIABLES>. > + > +=head1 SEE ALSO > + > +L<virt-v2v(1)>, > +L<virt-p2v(1)>, > +L<virt-inspector(1)>, > +L<guestfs(3)>, > +L<guestfish(1)>, > +L<qemu-img(1)>, > +L<nbdkit(1)>, > +L<http://libguestfs.org/>. > + > +=head1 AUTHORS > + > +Matthew Booth > + > +Cédric Bosdonnat > + > +Laszlo Ersek > + > +Tomáš Golembiovský > + > +Shahar Havivi > + > +Richard W.M. Jones > + > +Roman Kagan > + > +Mike Latimer > + > +Nir Soffer > + > +Pino Toscano > + > +Xiaodai Wang > + > +Ming Xie > + > +Tingting Zheng > + > +=head1 COPYRIGHT > + > +Copyright (C) 2009-2022 Red Hat Inc. > diff --git a/docs/virt-v2v.pod b/docs/virt-v2v.pod > index 4901c8407f..4f3d977a15 100644 > --- a/docs/virt-v2v.pod > +++ b/docs/virt-v2v.pod > @@ -21,6 +21,9 @@ There is also a companion front-end called L<virt-p2v(1)> > which comes > as an ISO, CD or PXE image that can be booted on physical machines to > virtualize those machines (physical to virtual, or p2v). > > +To estimate the disk space needed before conversion, see > +L<virt-v2v-inspector(1)>. > + > For in-place conversion, there is a separate tool called > L<virt-v2v-in-place(1)>. > > @@ -1624,6 +1627,7 @@ > L<https://rwmj.wordpress.com/2015/09/18/importing-kvm-guests-to-ovirt-or-rhev/#c > =head1 SEE ALSO > > L<virt-p2v(1)>, > +L<virt-v2v-inspector(1)>, > L<virt-v2v-in-place(1)>, > L<virt-customize(1)>, > L<virt-df(1)>, > diff --git a/configure.ac b/configure.ac > index b2396781d6..f8e2836551 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -146,6 +146,7 @@ AC_CONFIG_FILES([Makefile > gnulib/lib/Makefile > in-place/Makefile > input/Makefile > + inspector/Makefile > lib/Makefile > lib/config.ml > output/Makefile > diff --git a/Makefile.am b/Makefile.am > index cec68d76ce..16cd5f36d9 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -42,6 +42,7 @@ SUBDIRS += input > SUBDIRS += output > SUBDIRS += convert > SUBDIRS += v2v > +SUBDIRS += inspector > SUBDIRS += in-place > > SUBDIRS += tests > @@ -111,7 +112,7 @@ po/POTFILES: configure.ac > po/POTFILES-ml: configure.ac > rm -f $@ $@-t > cd $(srcdir); \ > - find common/ml* lib in-place input output v2v -name '*.ml' | \ > + find common/ml* lib in-place input inspector output v2v -name '*.ml' | \ > grep -v '^common/mlprogress/' | \ > grep -v '^common/mlvisit/' | \ > grep -v '^lib/config.ml$$' | \ > diff --git a/inspector/Makefile.am b/inspector/Makefile.am > new file mode 100644 > index 0000000000..30e6a297fa > --- /dev/null > +++ b/inspector/Makefile.am > @@ -0,0 +1,129 @@ > +# libguestfs virt-v2v-inspector tool > +# Copyright (C) 2009-2022 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. > + > +include $(top_srcdir)/subdir-rules.mk > + > +EXTRA_DIST = \ > + $(SOURCES_MLI) \ > + $(SOURCES_ML) \ > + $(SOURCES_C) > + > +SOURCES_MLI = \ > + inspector.mli > + > +SOURCES_ML = \ > + inspector.ml > + > +SOURCES_C = \ > + dummy.c > + > +bin_PROGRAMS = virt-v2v-inspector > + > +virt_v2v_inspector_SOURCES = $(SOURCES_C) > +virt_v2v_inspector_CPPFLAGS = \ > + -DCAML_NAME_SPACE \ > + -I. \ > + -I$(top_builddir) \ > + -I$(shell $(OCAMLC) -where) \ > + -I$(top_srcdir)/lib > +virt_v2v_inspector_CFLAGS = \ > + -pthread \ > + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ > + $(LIBGUESTFS_CFLAGS) \ > + $(LIBVIRT_CFLAGS) \ > + $(LIBOSINFO_CFLAGS) > + > +BOBJECTS = $(SOURCES_ML:.ml=.cmo) > +XOBJECTS = $(BOBJECTS:.cmo=.cmx) > + > +OCAMLPACKAGES = \ > + -package str,unix,guestfs,libvirt,nbd \ > + -I $(top_builddir)/common/utils/.libs \ > + -I $(top_builddir)/common/qemuopts/.libs \ > + -I $(top_builddir)/gnulib/lib/.libs \ > + -I $(top_builddir)/lib \ > + -I $(top_builddir)/input \ > + -I $(top_builddir)/convert \ > + -I $(top_builddir)/common/mlstdutils \ > + -I $(top_builddir)/common/mlutils \ > + -I $(top_builddir)/common/mlgettext \ > + -I $(top_builddir)/common/mlpcre \ > + -I $(top_builddir)/common/mlxml \ > + -I $(top_builddir)/common/mltools \ > + -I $(top_builddir)/common/mlcustomize \ > + -I $(top_builddir)/common/mlv2v > +if HAVE_OCAML_PKG_GETTEXT > +OCAMLPACKAGES += -package gettext-stub > +endif > + > +OCAMLCLIBS = \ > + -pthread \ > + -lqemuopts \ > + $(LIBGUESTFS_LIBS) \ > + $(LIBVIRT_LIBS) \ > + $(LIBXML2_LIBS) \ > + $(JANSSON_LIBS) \ > + $(LIBOSINFO_LIBS) \ > + $(LIBINTL) \ > + $(LIBNBD_LIBS) \ > + -lgnu > + > +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) -ccopt '$(CFLAGS)' > + > +if !HAVE_OCAMLOPT > +OBJECTS = $(BOBJECTS) > +else > +OBJECTS = $(XOBJECTS) > +endif > + > +OCAMLLINKFLAGS = \ > + mlstdutils.$(MLARCHIVE) \ > + mlgettext.$(MLARCHIVE) \ > + mlpcre.$(MLARCHIVE) \ > + mlxml.$(MLARCHIVE) \ > + mlcutils.$(MLARCHIVE) \ > + mltools.$(MLARCHIVE) \ > + mllibvirt.$(MLARCHIVE) \ > + mlcustomize.$(MLARCHIVE) \ > + mlv2v.$(MLARCHIVE) \ > + mlv2vlib.$(MLARCHIVE) \ > + mlconvert.$(MLARCHIVE) \ > + mlinput.$(MLARCHIVE) \ > + $(LINK_CUSTOM_OCAMLC_ONLY) > + > +virt_v2v_inspector_DEPENDENCIES = \ > + $(OBJECTS) \ > + $(top_builddir)/input/mlinput.$(MLARCHIVE) \ > + $(top_builddir)/convert/mlconvert.$(MLARCHIVE) \ > + $(top_builddir)/lib/mlv2vlib.$(MLARCHIVE) \ > + $(top_srcdir)/ocaml-link.sh > +virt_v2v_inspector_LINK = \ > + $(top_srcdir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \ > + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) > \ > + $(OBJECTS) -o $@ > + > +# Data directory. > + > +virttoolsdatadir = $(datadir)/virt-tools > + > +# Dependencies. > +.depend: \ > + $(srcdir)/*.mli \ > + $(srcdir)/*.ml \ > + $(filter %.ml,$(BUILT_SOURCES)) > + $(top_builddir)/ocaml-dep.sh $^ > +-include .depend > diff --git a/tests/Makefile.am b/tests/Makefile.am > index fb068624c7..de3f1fe9e2 100644 > --- a/tests/Makefile.am > +++ b/tests/Makefile.am > @@ -78,6 +78,7 @@ TESTS = \ > test-v2v-i-disk.sh \ > test-v2v-i-ova.sh \ > test-v2v-in-place.sh \ > + test-v2v-inspector.sh \ > test-v2v-mac.sh \ > test-v2v-machine-readable.sh \ > test-v2v-networks-and-bridges.sh \ > @@ -235,6 +236,7 @@ EXTRA_DIST += \ > test-v2v-i-vmx-6.vmx \ > test-v2v-i-vmx-7.vmx \ > test-v2v-in-place.sh \ > + test-v2v-inspector.sh \ > test-v2v-it-vddk-io-query.sh \ > test-v2v-machine-readable.sh \ > test-v2v-mac-expected.xml \ > diff --git a/inspector/inspector.mli b/inspector/inspector.mli > new file mode 100644 > index 0000000000..af7cc31cb3 > --- /dev/null > +++ b/inspector/inspector.mli > @@ -0,0 +1,19 @@ > +(* virt-v2v-in-place > + * Copyright (C) 2009-2022 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. > + *) > + > +(* Nothing is exported. *) > diff --git a/inspector/inspector.ml b/inspector/inspector.ml > new file mode 100644 > index 0000000000..0ded5d62ae > --- /dev/null > +++ b/inspector/inspector.ml > @@ -0,0 +1,472 @@ > +(* virt-v2v-inspector > + * Copyright (C) 2009-2022 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 Unix > + > +open Std_utils > +open Tools_utils > +open Unix_utils > +open Common_gettext.Gettext > +open Getopt.OptionName > + > +open Types > +open Utils > +open DOM > + > +(* Matches --mac command line parameters. *) > +let mac_re = PCRE.compile ~anchored:true > "([[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}):(network|bridge|ip):(.*)" > +let mac_ip_re = PCRE.compile ~anchored:true "([[:xdigit:]]|:|\\.)+" > + > +let rec main () = > + let set_string_option_once optname optref arg = > + match !optref with > + | Some _ -> > + error (f_"%s option used more than once on the command line") optname > + | None -> > + optref := Some arg > + in > + > + let bandwidth = ref None in > + let bandwidth_file = ref None in > + let input_conn = ref None in > + let input_format = ref None in > + let input_password = ref None in > + let input_transport = ref None in > + > + let input_options = ref [] in > + let io_query = ref false in > + let set_input_option_compat k v = > + List.push_back input_options (k, v) > + in > + let set_input_option option = > + if option = "?" then io_query := true > + else ( > + let k, v = String.split "=" option in > + set_input_option_compat k v > + ) > + in > + > + let network_map = Networks.create () in > + let static_ips = ref [] in > + let rec add_network str = > + match String.split ":" str with > + | "", "" -> > + error (f_"invalid -n/--network parameter") > + | out, "" | "", out -> > + Networks.add_default_network network_map out > + | in_, out -> > + Networks.add_network network_map in_ out > + and add_bridge str = > + match String.split ":" str with > + | "", "" -> > + error (f_"invalid -b/--bridge parameter") > + | out, "" | "", out -> > + Networks.add_default_bridge network_map out > + | in_, out -> > + Networks.add_bridge network_map in_ out > + and add_mac str = > + if not (PCRE.matches mac_re str) then > + error (f_"cannot parse --mac \"%s\" parameter") str; > + let mac = PCRE.sub 1 and out = PCRE.sub 3 in > + match PCRE.sub 2 with > + | "network" -> > + Networks.add_mac network_map mac Network out > + | "bridge" -> > + Networks.add_mac network_map mac Bridge out > + | "ip" -> > + (match String.nsplit "," out with > + | [] -> error (f_"invalid --mac ip option") > + | [ip] -> add_static_ip mac ip None None [] > + | [ip; gw] -> add_static_ip mac ip (Some gw) None [] > + | ip :: gw :: len :: nameservers -> > + add_static_ip mac ip (Some gw) (Some len) nameservers > + ) > + | _ -> assert false > + and add_static_ip if_mac_addr if_ip_address if_default_gateway > + if_prefix_length_str if_nameservers = > + (* Check the IP addresses and prefix length are sensible. This > + * is only a very simple test that they are sane, since IP addresses > + * come in too many valid forms to check thoroughly. > + *) > + let rec error_unless_ip_addr what addr = > + if not (PCRE.matches mac_ip_re addr) then > + error (f_"cannot parse --mac ip %s: doesn’t look like “%s” is an IP > address") what addr > + in > + error_unless_ip_addr "ipaddr" if_ip_address; > + Option.may (error_unless_ip_addr "gw") if_default_gateway; > + List.iter (error_unless_ip_addr "nameserver") if_nameservers; > + let if_prefix_length = > + match if_prefix_length_str with > + | None -> None > + | Some len -> > + let len = > + try int_of_string len with > + | Failure _ -> error (f_"cannot parse --mac ip prefix length > field as an integer: %s") len in > + if len < 0 || len > 128 then > + error (f_"--mac ip prefix length field is out of range"); > + Some len in > + List.push_back static_ips > + { if_mac_addr; if_ip_address; if_default_gateway; > + if_prefix_length; if_nameservers } > + in > + > + let root_choice = ref AskRoot in > + let set_root_choice = function > + | "ask" -> root_choice := AskRoot > + | "single" -> root_choice := SingleRoot > + | "first" -> root_choice := FirstRoot > + | dev when String.is_prefix dev "/dev/" -> root_choice := RootDev dev > + | s -> > + error (f_"unknown --root option: %s") s > + in > + > + let input_mode = ref `Not_set in > + let set_input_mode mode = > + if !input_mode <> `Not_set then > + error (f_"%s option used more than once on the command line") "-i"; > + match mode with > + | "disk" | "local" -> input_mode := `Disk > + | "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 > + > + let argspec = [ > + [ S 'b'; L"bridge" ], Getopt.String ("in:out", add_bridge), > + s_"Map bridge ‘in’ to ‘out’"; > + [ S 'i' ], Getopt.String ("disk|libvirt|libvirtxml|ova|vmx", > set_input_mode), > + s_"Set input mode (default: libvirt)"; > + [ M"ic" ], Getopt.String ("uri", set_string_option_once "-ic" > input_conn), > + s_"Libvirt URI"; > + [ M"if" ], Getopt.String ("format", set_string_option_once "-if" > input_format), > + s_"Input format"; > + [ M"io" ], Getopt.String ("option[=value]", set_input_option), > + s_"Set option for input mode"; > + [ M"ip" ], Getopt.String ("filename", set_string_option_once "-ip" > input_password), > + s_"Use password from file to connect to > input hypervisor"; > + [ M"it" ], Getopt.String ("transport", set_string_option_once > "-it" input_transport), > + s_"Input transport"; > + [ L"mac" ], Getopt.String ("mac:network|bridge|ip:out", add_mac), > + s_"Map NIC to network or bridge or > assign static IP"; > + [ S 'n'; L"network" ], Getopt.String ("in:out", add_network), > + s_"Map network ‘in’ to ‘out’"; > + [ L"root" ], Getopt.String ("ask|... ", set_root_choice), > + s_"How to choose root filesystem"; > + ] in > + let args = ref [] in > + let anon_fun s = List.push_front s args in > + let usage_msg = > + sprintf (f_"\ > +%s: estimate disk space needed before virt-v2v conversion > + > +virt-v2v-inspector -i disk disk.img > + > +A short summary of the options is given below. For detailed help please > +read the man page virt-v2v-inspector(1). > +") > + prog in > + let opthandle = create_standard_options argspec ~anon_fun ~key_opts:true > ~machine_readable:true usage_msg in > + Getopt.parse opthandle.getopt; > + > + (* Print the version, easier than asking users to tell us. *) > + debug "%s: %s %s (%s)" > + prog Config.package_name Config.package_version_full > + Config.host_cpu; > + > + (* Print the libvirt version if debugging. *) > + if verbose () then ( > + let major, minor, release = Libvirt_utils.libvirt_get_version () in > + debug "libvirt version: %d.%d.%d" major minor release > + ); > + > + (* Create the v2v directory to control conversion. *) > + let v2vdir = create_v2v_directory () in > + > + (* Dereference the arguments. *) > + let args = List.rev !args in > + let input_conn = !input_conn in > + let input_mode = !input_mode in > + let input_transport = > + match !input_transport with > + | None -> None > + | Some "ssh" -> Some `SSH > + | Some "vddk" -> Some `VDDK > + | Some transport -> > + error (f_"unknown input transport ‘-it %s’") transport in > + let root_choice = !root_choice in > + let static_ips = !static_ips in > + > + (* No arguments and machine-readable mode? Print out some facts > + * about what this binary supports. > + *) > + (match args, machine_readable () with > + | [], Some { pr } -> > + pr "virt-v2v-inspector\n"; > + pr "libguestfs-rewrite\n"; > + pr "colours-option\n"; > + pr "io\n"; > + pr "mac-option\n"; > + pr "mac-ip-option\n"; > + pr "input:disk\n"; > + pr "input:libvirt\n"; > + pr "input:libvirtxml\n"; > + pr "input:ova\n"; > + pr "input:vmx\n"; > + pr "convert:linux\n"; > + pr "convert:windows\n"; > + List.iter (pr "ovf:%s\n") Create_ovf.ovf_flavours; > + exit 0 > + | _, _ -> () > + ); > + > + (* Get the input module. *) > + let (module Input_module) = > + match input_mode with > + | `Disk -> (module Input_disk.Disk : Input.INPUT) > + | `LibvirtXML -> (module Input_libvirt.LibvirtXML) > + | `OVA -> (module Input_ova.OVA) > + | `VMX -> (module Input_vmx.VMX) > + | `Not_set | `Libvirt -> > + match input_conn with > + | None -> (module Input_libvirt.Libvirt_) > + | Some orig_uri -> > + let { Xml.uri_server = server; uri_scheme = scheme } = > + try Xml.parse_uri orig_uri > + with Invalid_argument msg -> > + error (f_"could not parse '-ic %s'. Original error message > was: %s") > + orig_uri msg in > + > + match server, scheme, input_transport with > + | None, _, _ > + | Some "", _, _ (* Not a remote URI. *) > + > + | Some _, None, _ (* No scheme? *) > + | Some _, Some "", _ -> > + (module Input_libvirt.Libvirt_) > + > + (* vCenter over https. *) > + | Some server, Some ("esx"|"gsx"|"vpx"), None -> > + (module Input_vcenter_https.VCenterHTTPS) > + > + (* vCenter or ESXi using nbdkit vddk plugin *) > + | Some server, Some ("esx"|"gsx"|"vpx"), Some `VDDK -> > + (module Input_vddk.VDDK) > + > + (* Xen over SSH *) > + | Some server, Some "xen+ssh", _ -> > + (module Input_xen_ssh.XenSSH) > + > + (* Old virt-v2v also supported qemu+ssh://. However I am > + * deliberately not supporting this in new virt-v2v. Don't > + * use virt-v2v if a guest already runs on KVM. > + *) > + > + (* Unknown remote scheme. *) > + | Some _, Some _, _ -> > + warning (f_"no support for remote libvirt connections to '-ic > %s'. The conversion may fail when it tries to read the source disks.") > orig_uri; > + (module Input_libvirt.Libvirt_) in > + > + let input_options = { > + Input.bandwidth = > + (match !bandwidth, !bandwidth_file with > + | None, None -> None > + | Some rate, None -> Some (StaticBandwidth rate) > + | rate, Some filename -> Some (DynamicBandwidth (rate, filename))); > + input_conn = input_conn; > + input_format = !input_format; > + input_options = !input_options; > + input_password = !input_password; > + input_transport = input_transport; > + (* This must always be true so that we do not modify the > + * source. This is set to [false] by in-place mode. > + *) > + read_only = true; > + } in > + > + (* If -io ? then we want to query input options supported in this mode. *) > + if !io_query then ( > + Input_module.query_input_options (); > + exit 0 > + ); > + > + (* Get the conversion options. *) > + let conv_options = { > + Convert.keep_serial_console = true; > + ks = opthandle.ks; > + network_map; > + root_choice; > + static_ips; > + } in > + > + (* Before starting the input module, check there is sufficient > + * free space in the temporary directory on the host. > + *) > + check_host_free_space (); > + > + (* Start the input module (runs an NBD server in the background). *) > + message (f_"Setting up the source: %s") > + (Input_module.to_string input_options args); > + let source = Input_module.setup v2vdir input_options args in > + > + (* Do the conversion. *) > + with_open_out (v2vdir // "convert") (fun _ -> ()); > + let inspect, _ = Convert.convert v2vdir conv_options source in > + unlink (v2vdir // "convert"); > + > + (* Debug the v2vdir. *) > + if verbose () then ( > + let cmd = sprintf "ls -alZ %s 1>&2" (quote v2vdir) in > + ignore (Sys.command cmd) > + ); > + > + (* Dump out the information. *) > + let doc = inspector_xml v2vdir inspect in > + DOM.doc_to_chan Stdlib.stdout doc; > + > + message (f_"Finishing off"); > + (* As the last thing, write a file indicating success before > + * we exit (so before we kill the helpers). The helpers may > + * use the presence or absence of the file to determine if > + * on-success or on-fail cleanup is required. > + *) > + with_open_out (v2vdir // "done") (fun _ -> ()) > + > +(* Conversion can fail or hang if there is insufficient free space in > + * the large temporary directory. Some input modules use large_tmpdir > + * to unpack OVAs or store qcow2 overlays and some output modules > + * use it to store temporary files. In addition the 500 MB guestfs > + * appliance may be created there. (RHBZ#1316479, RHBZ#2051394) > + *) > +and check_host_free_space () = > + let free_space = StatVFS.free_space (StatVFS.statvfs large_tmpdir) in > + debug "check_host_free_space: large_tmpdir=%s free_space=%Ld" > + large_tmpdir free_space; > + if free_space < 1_073_741_824L then > + error (f_"insufficient free space in the conversion server temporary > directory %s (%s).\n\nEither free up space in that directory, or set the > LIBGUESTFS_CACHEDIR environment variable to point to another directory with > more than 1GB of free space.\n\nSee also the virt-v2v(1) manual, section > \"Minimum free space check in the host\".") > + large_tmpdir (human_size free_space) > + > +(* This is a copy of {!Output.get_disks}. *) > +and get_disks dir = > + let rec loop acc i = > + let socket = sprintf "%s/in%d" dir i in > + if Sys.file_exists socket then ( > + let size = Utils.with_nbd_connect_unix ~socket NBD.get_size in > + loop ((i, size) :: acc) (i+1) > + ) > + else > + List.rev acc > + in > + loop [] 0 > + > +(* This is like {!Utils.get_disk_allocated} but works on the input disks. *) > +and get_input_disk_allocated dir i = > + let socket = sprintf "%s/in%d" dir i > + and alloc_ctx = "base:allocation" in > + with_nbd_connect_unix ~socket ~meta_contexts:[alloc_ctx] > + (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 = entries.(i * 2) > + and typ = entries.(i * 2 + 1) in > + assert (len > 0_L); > + if typ &^ 1_L = 0_L then > + allocated := !allocated +^ len; > + fetch_offset := !fetch_offset +^ len > + done; > + 0 > + ) > + done; > + Some !allocated > + ) else None > + ) > + > +(* This is where we construct the final XML document based on > + * these inputs: > + * - Global configuration like the version of v2v etc. > + * - The NBD input sockets: v2vdir // "in0", "in1", etc > + * - The inspection data (Types.inspect) > + *) > +and inspector_xml v2vdir inspect = > + let body = ref [] in > + > + (* Record the version of virt-v2v etc, mainly for debugging. *) > + List.push_back_list body [ > + Comment generated_by; > + e "program" [] [PCData "virt-v2v-inspector"]; > + e "package" [] [PCData Config.package_name]; > + e "version" [] [PCData Config.package_version]; > + ]; > + > + (* The disks. *) > + let disks = ref [] in > + > + List.iter ( > + fun (i, virtual_size) -> > + let elems = ref [] in > + List.push_back elems (e "virtual-size" [] > + [PCData (Int64.to_string virtual_size)]); > + (match get_input_disk_allocated v2vdir i with > + | None -> () > + | Some real_size -> > + List.push_back elems (e "allocated" [ "estimated", "true" ] > + [PCData (Int64.to_string real_size)]) > + ); > + > + List.push_back disks (e "disk" [ "index", string_of_int i ] !elems) > + ) (get_disks v2vdir); > + List.push_back body (e "disks" [] !disks); > + > + (* The inspection data. *) > + (* NB: Keep these field names compatible with virt-inspector! *) > + let os = ref [] in > + List.push_back os (e "name" [] [PCData inspect.i_type]); > + List.push_back os (e "distro" [] [PCData inspect.i_distro]); > + List.push_back os (e "osinfo" [] [PCData inspect.i_osinfo]); > + List.push_back os (e "arch" [] [PCData inspect.i_arch]); > + List.push_back os (e "major_version" [] > + [PCData (string_of_int inspect.i_major_version)]); > + List.push_back os (e "minor_version" [] > + [PCData (string_of_int inspect.i_minor_version)]); > + if inspect.i_package_format <> "" then > + List.push_back os (e "package_format" [] > + [PCData inspect.i_package_format]); > + if inspect.i_package_management <> "" then > + List.push_back os (e "package_management" [] > + [PCData inspect.i_package_management]); > + if inspect.i_product_name <> "" then > + List.push_back os (e "product_name" [] [PCData inspect.i_product_name]); > + List.push_back body (e "operatingsystem" [] !os); > + > + (* Construct the final document. *) > + (doc "v2v-inspection" [] !body : DOM.doc) > + > +let () = run_main_and_handle_errors main > diff --git a/inspector/dummy.c b/inspector/dummy.c > new file mode 100644 > index 0000000000..ebab6198cd > --- /dev/null > +++ b/inspector/dummy.c > @@ -0,0 +1,2 @@ > +/* Dummy source, to be used for OCaml-based tools with no C sources. */ > +enum { foo = 1 }; > diff --git a/tests/test-v2v-inspector.sh b/tests/test-v2v-inspector.sh > new file mode 100755 > index 0000000000..52406ddee1 > --- /dev/null > +++ b/tests/test-v2v-inspector.sh > @@ -0,0 +1,76 @@ > +#!/bin/bash - > +# libguestfs virt-v2v test script > +# Copyright (C) 2014-2022 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 virt-v2v-inspector. > + > +unset CDPATH > +export LANG=C > +set -e > + > +source ./functions.sh > +set -e > +set -x > + > +skip_if_skipped > +requires test -f ../test-data/phony-guests/windows.img > + > +img="$abs_top_builddir/test-data/phony-guests/windows.img" > + > +export VIRT_TOOLS_DATA_DIR="$srcdir/../test-data/fake-virt-tools" > +export VIRTIO_WIN="$srcdir/../test-data/fake-virtio-win" > + > +d=$PWD/test-v2v-inspector.d > +rm -rf $d > +cleanup_fn rm -r $d > +mkdir $d > + > +out="$d/out" > + > +libvirt_xml="$d/test.xml" > +rm -f $libvirt_xml > +n=windows > +cat > $libvirt_xml <<EOF > +<node> > + <domain type='test'> > + <name>$n</name> > + <memory>1048576</memory> > + <os> > + <type>hvm</type> > + <boot dev='hd'/> > + </os> > + <devices> > + <disk type='file' device='disk'> > + <driver name='qemu' type='raw'/> > + <source file='$img'/> > + <target dev='vda' bus='virtio'/> > + </disk> > + </devices> > + </domain> > +</node> > +EOF > + > +$VG virt-v2v-inspector --quiet --debug-gc -i libvirt -ic > "test://$libvirt_xml" $n > $out > +cat $out > + > +# Expect certain elements to be present. > +grep '^<v2v-inspection' $out > +grep '<program>virt-v2v-inspector</program>' $out > +grep '<disks>' $out > +grep "<disk index='0'>" $out > +grep '<distro>windows</distro>' $out > +grep '<osinfo>win7</osinfo>' $out > diff --git a/.gitignore b/.gitignore > index 62541b8980..655c794ee7 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -51,6 +51,7 @@ Makefile.in > /docs/virt-v2v-in-place.1 > /docs/virt-v2v-input-vmware.1 > /docs/virt-v2v-input-xen.1 > +/docs/virt-v2v-inspector.1 > /docs/virt-v2v-output-local.1 > /docs/virt-v2v-output-openstack.1 > /docs/virt-v2v-output-rhv.1 > @@ -62,6 +63,8 @@ Makefile.in > /in-place/.depend > /in-place/virt-v2v-in-place > /input/.depend > +/inspector/.depend > +/inspector/virt-v2v-inspector > /installcheck.sh > /install-sh > /libtool > diff --git a/run.in b/run.in > index 69936a6e2b..c75e4e0f0f 100755 > --- a/run.in > +++ b/run.in > @@ -67,9 +67,10 @@ export LIBGUESTFS_CACHEDIR="$b/tmp" > mkdir -p "$b/tmp" > chcon --reference=/tmp "$b/tmp" 2>/dev/null ||: > > -# Set the PATH to contain the virt-v2v and virt-v2v-in-place binaries. > +# Set the PATH to contain the virt-v2v and other binaries. > prepend PATH "$b/v2v" > prepend PATH "$b/in-place" > +prepend PATH "$b/inspector" > export PATH > > # This is a cheap way to find some use-after-free and uninitialized > _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs