mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
New tool: virt-v2v.
This is a rewrite of the original virt-v2v tool. The original was written by Matt Booth et al in Perl between 2009 and 2013.
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -252,6 +252,7 @@ Makefile.in
|
||||
/html/virt-tar.1.html
|
||||
/html/virt-tar-in.1.html
|
||||
/html/virt-tar-out.1.html
|
||||
/html/virt-v2v.1.html
|
||||
/html/virt-win-reg.1.html
|
||||
/inspector/actual-*.xml
|
||||
/inspector/stamp-virt-inspector.pod
|
||||
@@ -523,3 +524,8 @@ Makefile.in
|
||||
/test-tool/libguestfs-test-tool-helper
|
||||
/test-tool/stamp-libguestfs-test-tool.pod
|
||||
/tools/virt-*.1
|
||||
/v2v/.depend
|
||||
/v2v/link.sh
|
||||
/v2v/stamp-virt-v2v.pod
|
||||
/v2v/virt-v2v
|
||||
/v2v/virt-v2v.1
|
||||
|
||||
@@ -132,7 +132,8 @@ SUBDIRS += \
|
||||
builder builder/website \
|
||||
resize \
|
||||
sparsify \
|
||||
sysprep
|
||||
sysprep \
|
||||
v2v
|
||||
endif
|
||||
|
||||
# Perl tools.
|
||||
@@ -257,6 +258,7 @@ HTMLFILES = \
|
||||
html/virt-tar.1.html \
|
||||
html/virt-tar-in.1.html \
|
||||
html/virt-tar-out.1.html \
|
||||
html/virt-v2v.1.html \
|
||||
html/virt-win-reg.1.html
|
||||
|
||||
HTMLSUPPORTFILES = \
|
||||
@@ -319,7 +321,7 @@ all-local:
|
||||
grep -v -E '^python/utils.c$$' | \
|
||||
LC_ALL=C sort > po/POTFILES
|
||||
cd $(srcdir); \
|
||||
find builder customize mllib resize sparsify sysprep -name '*.ml' | \
|
||||
find builder customize mllib resize sparsify sysprep v2v -name '*.ml' | \
|
||||
LC_ALL=C sort > po/POTFILES-ml
|
||||
|
||||
# Manual pages in top level directory.
|
||||
|
||||
@@ -1599,6 +1599,8 @@ AC_CONFIG_FILES([run],
|
||||
[chmod +x,-w run])
|
||||
AC_CONFIG_FILES([sparsify/link.sh],
|
||||
[chmod +x,-w sparsify/link.sh])
|
||||
AC_CONFIG_FILES([v2v/link.sh],
|
||||
[chmod +x,-w v2v/link.sh])
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
align/Makefile
|
||||
@@ -1702,7 +1704,8 @@ AC_CONFIG_FILES([Makefile
|
||||
tests/tmpdirs/Makefile
|
||||
tests/xfs/Makefile
|
||||
tests/xml/Makefile
|
||||
tools/Makefile])
|
||||
tools/Makefile
|
||||
v2v/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
dnl Produce summary.
|
||||
|
||||
@@ -1624,6 +1624,7 @@ L<virt-sysprep(1)>,
|
||||
L<virt-tar(1)>,
|
||||
L<virt-tar-in(1)>,
|
||||
L<virt-tar-out(1)>,
|
||||
L<virt-v2v(1)>,
|
||||
L<virt-win-reg(1)>,
|
||||
L<libguestfs-tools.conf(5)>,
|
||||
L<display(1)>,
|
||||
|
||||
@@ -320,3 +320,5 @@ src/test-utils.c
|
||||
src/tmpdirs.c
|
||||
src/utils.c
|
||||
test-tool/test-tool.c
|
||||
v2v/utils-c.c
|
||||
v2v/xml-c.c
|
||||
|
||||
@@ -81,3 +81,14 @@ sysprep/sysprep_operation_udev_persistent_net.ml
|
||||
sysprep/sysprep_operation_user_account.ml
|
||||
sysprep/sysprep_operation_utmp.ml
|
||||
sysprep/sysprep_operation_yum_uuid.ml
|
||||
v2v/cmdline.ml
|
||||
v2v/convert_linux_common.ml
|
||||
v2v/convert_linux_enterprise.ml
|
||||
v2v/convert_linux_grub.ml
|
||||
v2v/convert_windows.ml
|
||||
v2v/source_libvirt.ml
|
||||
v2v/target_local.ml
|
||||
v2v/types.ml
|
||||
v2v/utils.ml
|
||||
v2v/v2v.ml
|
||||
v2v/xml.ml
|
||||
|
||||
@@ -4420,6 +4420,10 @@ created by another.
|
||||
|
||||
Command line tools written in Perl (L<virt-win-reg(1)> and many others).
|
||||
|
||||
=item C<v2v>
|
||||
|
||||
L<virt-v2v(1)> command and documentation.
|
||||
|
||||
=item C<csharp>
|
||||
|
||||
=item C<erlang>
|
||||
@@ -4773,6 +4777,7 @@ L<virt-sysprep(1)>,
|
||||
L<virt-tar(1)>,
|
||||
L<virt-tar-in(1)>,
|
||||
L<virt-tar-out(1)>,
|
||||
L<virt-v2v(1)>,
|
||||
L<virt-win-reg(1)>,
|
||||
L<guestfs-faq(1)>,
|
||||
L<guestfs-performance(1)>,
|
||||
|
||||
163
v2v/Makefile.am
Normal file
163
v2v/Makefile.am
Normal file
@@ -0,0 +1,163 @@
|
||||
# libguestfs virt-v2v tool
|
||||
# Copyright (C) 2009-2014 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) \
|
||||
virt-v2v.pod
|
||||
|
||||
CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o virt-v2v
|
||||
|
||||
SOURCES_MLI = \
|
||||
convert_linux_common.mli \
|
||||
convert_linux_enterprise.mli \
|
||||
convert_linux_grub.mli \
|
||||
convert_windows.mli \
|
||||
source_libvirt.mli \
|
||||
target_local.mli \
|
||||
types.mli \
|
||||
xml.mli
|
||||
|
||||
SOURCES_ML = \
|
||||
types.ml \
|
||||
utils.ml \
|
||||
xml.ml \
|
||||
cmdline.ml \
|
||||
source_libvirt.ml \
|
||||
convert_linux_common.ml \
|
||||
convert_linux_grub.ml \
|
||||
convert_linux_enterprise.ml \
|
||||
convert_windows.ml \
|
||||
target_local.ml \
|
||||
v2v.ml
|
||||
|
||||
SOURCES_C = \
|
||||
$(top_builddir)/fish/progress.c \
|
||||
$(top_builddir)/mllib/tty-c.c \
|
||||
$(top_builddir)/mllib/progress-c.c \
|
||||
utils-c.c \
|
||||
xml-c.c
|
||||
|
||||
if HAVE_OCAML
|
||||
|
||||
bin_PROGRAMS = virt-v2v
|
||||
|
||||
virt_v2v_SOURCES = $(SOURCES_C)
|
||||
virt_v2v_CFLAGS = \
|
||||
-I. \
|
||||
-I$(top_builddir) \
|
||||
-I$(shell $(OCAMLC) -where) \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/fish \
|
||||
$(LIBXML2_CFLAGS)
|
||||
|
||||
BOBJECTS = \
|
||||
$(top_builddir)/mllib/common_gettext.cmo \
|
||||
$(top_builddir)/mllib/common_utils.cmo \
|
||||
$(top_builddir)/mllib/tTY.cmo \
|
||||
$(top_builddir)/mllib/progress.cmo \
|
||||
$(top_builddir)/mllib/config.cmo \
|
||||
$(SOURCES_ML:.ml=.cmo)
|
||||
XOBJECTS = $(BOBJECTS:.cmo=.cmx)
|
||||
|
||||
# -I $(top_builddir)/src/.libs is a hack which forces corresponding -L
|
||||
# option to be passed to gcc, so we don't try linking against an
|
||||
# installed copy of libguestfs.
|
||||
OCAMLPACKAGES = \
|
||||
-package str,unix \
|
||||
-I $(top_builddir)/src/.libs \
|
||||
-I ../gnulib/lib/.libs \
|
||||
-I $(top_builddir)/ocaml \
|
||||
-I $(top_builddir)/mllib
|
||||
if HAVE_OCAML_PKG_GETTEXT
|
||||
OCAMLPACKAGES += -package gettext-stub
|
||||
endif
|
||||
|
||||
OCAMLFLAGS = -g -warn-error CDEFLMPSUVYZX
|
||||
|
||||
if !HAVE_OCAMLOPT
|
||||
OBJECTS = $(BOBJECTS)
|
||||
BEST = c
|
||||
OCAMLLINKFLAGS = mlguestfs.cma -custom
|
||||
else
|
||||
OBJECTS = $(XOBJECTS)
|
||||
BEST = opt
|
||||
OCAMLLINKFLAGS = mlguestfs.cmxa
|
||||
endif
|
||||
|
||||
virt_v2v_DEPENDENCIES = $(OBJECTS)
|
||||
virt_v2v_LINK = \
|
||||
./link.sh \
|
||||
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
|
||||
$(OBJECTS) -o $@
|
||||
|
||||
.mli.cmi:
|
||||
$(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
|
||||
.ml.cmo:
|
||||
$(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
|
||||
.ml.cmx:
|
||||
$(OCAMLFIND) ocamlopt $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
|
||||
|
||||
# Manual pages and HTML files for the website.
|
||||
|
||||
man_MANS = virt-v2v.1
|
||||
|
||||
noinst_DATA = $(top_builddir)/html/virt-v2v.1.html
|
||||
|
||||
virt-v2v.1 $(top_builddir)/html/virt-v2v.1.html: stamp-virt-v2v.pod
|
||||
|
||||
stamp-virt-v2v.pod: virt-v2v.pod
|
||||
$(PODWRAPPER) \
|
||||
--man virt-v2v.1 \
|
||||
--html $(top_builddir)/html/virt-v2v.1.html \
|
||||
--license GPLv2+ \
|
||||
$<
|
||||
touch $@
|
||||
|
||||
CLEANFILES += stamp-virt-v2v.pod
|
||||
|
||||
# Tests.
|
||||
|
||||
TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||
|
||||
if ENABLE_APPLIANCE
|
||||
TESTS =
|
||||
endif ENABLE_APPLIANCE
|
||||
|
||||
check-valgrind:
|
||||
$(MAKE) VG="$(top_builddir)/run @VG@" check
|
||||
|
||||
# Dependencies.
|
||||
depend: .depend
|
||||
|
||||
.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
|
||||
rm -f $@ $@-t
|
||||
$(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \
|
||||
$(SED) 's/ *$$//' | \
|
||||
$(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
|
||||
$(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
|
||||
sort > $@-t
|
||||
mv $@-t $@
|
||||
|
||||
-include .depend
|
||||
|
||||
endif
|
||||
|
||||
DISTCLEANFILES = .depend
|
||||
|
||||
.PHONY: depend docs
|
||||
23
v2v/README
Normal file
23
v2v/README
Normal file
@@ -0,0 +1,23 @@
|
||||
Missing features compared to Perl version:
|
||||
|
||||
- user-custom in virt-v2v.conf to install custom packages (virt-customize?)
|
||||
- Windows support
|
||||
- Fix configure_kernel on SUSE (see Mike Latimer's email)
|
||||
- RHEV-M metadata
|
||||
- testing
|
||||
|
||||
Notes on the support matrix for upstream virt-v2v:
|
||||
|
||||
- All these in 32- and 64-bit variants.
|
||||
|
||||
- All these as Xen HV and PV variants, and ESX guests.
|
||||
|
||||
- RHEL 3, 4, 5, 6, 7.
|
||||
- RHEL 4.5, 4.8, 5.2, 5.4 - tested particularly because virtio was added
|
||||
between these releases.
|
||||
|
||||
- Windows XP, 2000, 2003, 2008.
|
||||
|
||||
- SUSE: OpenSUSE 13.1, SLES11 SP3, SLES12.
|
||||
|
||||
- VirtualBox: Not tested.
|
||||
197
v2v/cmdline.ml
Normal file
197
v2v/cmdline.ml
Normal file
@@ -0,0 +1,197 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(* Command line argument parsing. *)
|
||||
|
||||
open Printf
|
||||
|
||||
open Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Types
|
||||
open Utils
|
||||
|
||||
let parse_cmdline () =
|
||||
let display_version () =
|
||||
printf "virt-v2v %s\n" Config.package_version;
|
||||
exit 0
|
||||
in
|
||||
|
||||
let debug_gc = ref false in
|
||||
let input_conn = ref "" in
|
||||
let output_conn = ref "" in
|
||||
let output_format = ref "" in
|
||||
let output_name = ref "" in
|
||||
let output_storage = ref "" in
|
||||
let machine_readable = ref false in
|
||||
let quiet = ref false in
|
||||
let verbose = ref false in
|
||||
let trace = ref false in
|
||||
|
||||
let input_mode = ref `Libvirt in
|
||||
let set_input_mode = function
|
||||
| "libvirt" -> input_mode := `Libvirt
|
||||
| "libvirtxml" -> input_mode := `LibvirtXML
|
||||
| s ->
|
||||
error (f_"unknown -i option: %s") s
|
||||
in
|
||||
|
||||
let output_mode = ref `Libvirt in
|
||||
let set_output_mode = function
|
||||
| "libvirt" -> output_mode := `Libvirt
|
||||
| "local" -> output_mode := `Local
|
||||
| "ovirt" | "rhev" -> output_mode := `RHEV
|
||||
| s ->
|
||||
error (f_"unknown -o option: %s") s
|
||||
in
|
||||
|
||||
let output_alloc = ref `Sparse in
|
||||
let set_output_alloc = function
|
||||
| "sparse" -> output_alloc := `Sparse
|
||||
| "preallocated" -> output_alloc := `Preallocated
|
||||
| s ->
|
||||
error (f_"unknown -oa option: %s") s
|
||||
in
|
||||
|
||||
let root_choice = ref `Ask in
|
||||
let set_root_choice = function
|
||||
| "ask" -> root_choice := `Ask
|
||||
| "single" -> root_choice := `Single
|
||||
| "first" -> root_choice := `First
|
||||
| dev when string_prefix dev "/dev/" -> root_choice := `Dev dev
|
||||
| s ->
|
||||
error (f_"unknown --root option: %s") s
|
||||
in
|
||||
|
||||
let ditto = " -\"-" in
|
||||
let argspec = Arg.align [
|
||||
"--debug-gc",Arg.Set debug_gc, " " ^ s_"Debug GC and memory allocations";
|
||||
"-i", Arg.String set_input_mode, "libvirtxml|libvirt " ^ s_"Set input mode (default: libvirt)";
|
||||
"-ic", Arg.Set_string input_conn, "uri " ^ s_"Libvirt URI";
|
||||
"--long-options", Arg.Unit display_long_options, " " ^ s_"List long options";
|
||||
"--machine-readable", Arg.Set machine_readable, " " ^ s_"Make output machine readable";
|
||||
"-o", Arg.String set_output_mode, "libvirt|local|rhev " ^ s_"Set output mode (default: libvirt)";
|
||||
"-oa", Arg.String set_output_alloc, "sparse|preallocated " ^ s_"Set output allocation mode";
|
||||
"-oc", Arg.Set_string output_conn, "uri " ^ s_"Libvirt URI";
|
||||
"-of", Arg.Set_string output_format, "raw|qcow2 " ^ s_"Set output format";
|
||||
"-on", Arg.Set_string output_name, "name " ^ s_"Rename guest when converting";
|
||||
"-os", Arg.Set_string output_storage, "storage " ^ s_"Set output storage location";
|
||||
"-q", Arg.Set quiet, " " ^ s_"Quiet output";
|
||||
"--quiet", Arg.Set quiet, ditto;
|
||||
"--root", Arg.String set_root_choice,"ask|... " ^ s_"How to choose root filesystem";
|
||||
"-v", Arg.Set verbose, " " ^ s_"Enable debugging messages";
|
||||
"--verbose", Arg.Set verbose, ditto;
|
||||
"-V", Arg.Unit display_version, " " ^ s_"Display version and exit";
|
||||
"--version", Arg.Unit display_version, ditto;
|
||||
"-x", Arg.Set trace, " " ^ s_"Enable tracing of libguestfs calls";
|
||||
] in
|
||||
long_options := argspec;
|
||||
let args = ref [] in
|
||||
let anon_fun s = args := s :: !args in
|
||||
let usage_msg =
|
||||
sprintf (f_"\
|
||||
%s: convert a guest to use KVM
|
||||
|
||||
virt-v2v -ic esx://esx.example.com/ -os imported esx_guest
|
||||
|
||||
virt-v2v -ic esx://esx.example.com/ \
|
||||
-o rhev -os rhev.nfs:/export_domain --network rhevm esx_guest
|
||||
|
||||
virt-v2v -i libvirtxml -o local -os /tmp guest-domain.xml
|
||||
|
||||
There is a companion front-end called \"virt-p2v\" which comes as an
|
||||
ISO or CD image that can be booted on physical machines.
|
||||
|
||||
A short summary of the options is given below. For detailed help please
|
||||
read the man page virt-v2v(1).
|
||||
")
|
||||
prog in
|
||||
Arg.parse argspec anon_fun usage_msg;
|
||||
|
||||
(* Dereference the arguments. *)
|
||||
let args = List.rev !args in
|
||||
let debug_gc = !debug_gc in
|
||||
let input_conn = match !input_conn with "" -> None | s -> Some s in
|
||||
let input_mode = !input_mode in
|
||||
let machine_readable = !machine_readable in
|
||||
let output_alloc = !output_alloc in
|
||||
let output_conn = match !output_conn with "" -> None | s -> Some s in
|
||||
let output_format = match !output_format with "" -> None | s -> Some s in
|
||||
let output_mode = !output_mode in
|
||||
let output_name = match !output_name with "" -> None | s -> Some s in
|
||||
let output_storage = !output_storage in
|
||||
let quiet = !quiet in
|
||||
let root_choice = !root_choice in
|
||||
let verbose = !verbose in
|
||||
let trace = !trace in
|
||||
|
||||
(* No arguments and machine-readable mode? Print out some facts
|
||||
* about what this binary supports.
|
||||
*)
|
||||
if args = [] && machine_readable then (
|
||||
printf "virt-v2v\n";
|
||||
printf "libguestfs-rewrite\n";
|
||||
exit 0
|
||||
);
|
||||
|
||||
(* Parsing of the argument(s) depends on the input mode. *)
|
||||
let input =
|
||||
match input_mode with
|
||||
| `Libvirt ->
|
||||
(* -i libvirt: Expecting a single argument which is the name
|
||||
* of the libvirt guest.
|
||||
*)
|
||||
let guest =
|
||||
match args with
|
||||
| [guest] -> guest
|
||||
| _ ->
|
||||
error (f_"expecting a libvirt guest name on the command line") in
|
||||
InputLibvirt (input_conn, guest)
|
||||
| `LibvirtXML ->
|
||||
(* -i libvirtxml: Expecting a filename (XML file). *)
|
||||
let filename =
|
||||
match args with
|
||||
| [filename] -> filename
|
||||
| _ ->
|
||||
error (f_"expecting a libvirt XML file name on the command line") in
|
||||
InputLibvirtXML filename in
|
||||
|
||||
(* Parse the output mode. *)
|
||||
let output =
|
||||
match output_mode with
|
||||
| `Libvirt ->
|
||||
if output_storage <> "" then
|
||||
error (f_"-o libvirt: do not use the -os option");
|
||||
OutputLibvirt output_conn
|
||||
| `Local ->
|
||||
if output_storage = "" then
|
||||
error (f_"-o local: output directory was not specified, use '-os /dir'");
|
||||
let dir_exists =
|
||||
try Sys.is_directory output_storage with Sys_error _ -> false in
|
||||
if not dir_exists then
|
||||
error (f_"-os %s: output directory does not exist or is not a directory")
|
||||
output_storage;
|
||||
OutputLocal output_storage
|
||||
| `RHEV ->
|
||||
if output_storage = "" then
|
||||
error (f_"-o local: output storage was not specified, use '-os'");
|
||||
OutputRHEV output_storage in
|
||||
|
||||
input, output,
|
||||
debug_gc, output_alloc, output_format, output_name,
|
||||
quiet, root_choice, trace, verbose
|
||||
236
v2v/convert_linux_common.ml
Normal file
236
v2v/convert_linux_common.ml
Normal file
@@ -0,0 +1,236 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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 Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Types
|
||||
open Utils
|
||||
|
||||
module StringMap = Map.Make (String)
|
||||
let keys map = StringMap.fold (fun k _ ks -> k :: ks) map []
|
||||
|
||||
(* Wrappers around aug_init & aug_load which can dump out full Augeas
|
||||
* parsing problems when debugging is enabled.
|
||||
*)
|
||||
let rec augeas_init verbose g =
|
||||
g#aug_init "/" 1;
|
||||
if verbose then augeas_debug_errors g
|
||||
|
||||
and augeas_reload verbose g =
|
||||
g#aug_load ();
|
||||
if verbose then augeas_debug_errors g
|
||||
|
||||
and augeas_debug_errors g =
|
||||
try
|
||||
let errors = g#aug_match "/augeas/files//error" in
|
||||
let errors = Array.to_list errors in
|
||||
let map =
|
||||
List.fold_left (
|
||||
fun map error ->
|
||||
let detail_paths = g#aug_match (error ^ "//*") in
|
||||
let detail_paths = Array.to_list detail_paths in
|
||||
List.fold_left (
|
||||
fun map path ->
|
||||
(* path is "/augeas/files/<filename>/error/<field>". Put
|
||||
* <filename>, <field> and the value of this Augeas field
|
||||
* into a map.
|
||||
*)
|
||||
let i = string_find path "/error/" in
|
||||
assert (i >= 0);
|
||||
let filename = String.sub path 13 (i-13) in
|
||||
let field = String.sub path (i+7) (String.length path - (i+7)) in
|
||||
|
||||
let detail = g#aug_get path in
|
||||
|
||||
let fmap : string StringMap.t =
|
||||
try StringMap.find filename map
|
||||
with Not_found -> StringMap.empty in
|
||||
let fmap = StringMap.add field detail fmap in
|
||||
StringMap.add filename fmap map
|
||||
) map detail_paths
|
||||
) StringMap.empty errors in
|
||||
|
||||
let filenames = keys map in
|
||||
let filenames = List.sort compare filenames in
|
||||
|
||||
List.iter (
|
||||
fun filename ->
|
||||
printf "augeas failed to parse %s:\n" filename;
|
||||
let fmap = StringMap.find filename map in
|
||||
(try
|
||||
let msg = StringMap.find "message" fmap in
|
||||
printf " error \"%s\"" msg
|
||||
with Not_found -> ()
|
||||
);
|
||||
(try
|
||||
let line = StringMap.find "line" fmap
|
||||
and char = StringMap.find "char" fmap in
|
||||
printf " at line %s char %s" line char
|
||||
with Not_found -> ()
|
||||
);
|
||||
(try
|
||||
let lens = StringMap.find "lens" fmap in
|
||||
printf " in lens %s" lens
|
||||
with Not_found -> ()
|
||||
);
|
||||
printf "\n"
|
||||
) filenames;
|
||||
|
||||
flush stdout
|
||||
with
|
||||
G.Error msg -> eprintf "%s: augeas: %s (ignored)\n" prog msg
|
||||
|
||||
let install verbose g inspect packages =
|
||||
assert false
|
||||
|
||||
let remove verbose g inspect packages =
|
||||
if packages <> [] then (
|
||||
let root = inspect.i_root in
|
||||
let package_format = g#inspect_get_package_format root in
|
||||
match package_format with
|
||||
| "rpm" ->
|
||||
let cmd = [ "rpm"; "-e" ] @ packages in
|
||||
let cmd = Array.of_list cmd in
|
||||
ignore (g#command cmd);
|
||||
|
||||
(* Reload Augeas in case anything changed. *)
|
||||
augeas_reload verbose g
|
||||
|
||||
| format ->
|
||||
error (f_"don't know how to remove packages using %s: packages: %s")
|
||||
format (String.concat " " packages)
|
||||
)
|
||||
|
||||
let file_owned verbose g inspect file =
|
||||
let root = inspect.i_root in
|
||||
let package_format = g#inspect_get_package_format root in
|
||||
match package_format with
|
||||
| "rpm" ->
|
||||
let cmd = [| "rpm"; "-qf"; file |] in
|
||||
(try ignore (g#command cmd); true with G.Error _ -> false)
|
||||
|
||||
| format ->
|
||||
error (f_"don't know how to find package owner using %s") format
|
||||
|
||||
type kernel_info = {
|
||||
base_package : string; (* base package, eg. "kernel-PAE" *)
|
||||
version : string; (* kernel version *)
|
||||
modules : string list; (* list of kernel modules *)
|
||||
arch : string; (* kernel arch *)
|
||||
}
|
||||
|
||||
(* There was some crazy SUSE stuff going on in the Perl version
|
||||
* of virt-v2v, which I have dropped from this as I couldn't
|
||||
* understand what on earth it was doing. - RWMJ
|
||||
*)
|
||||
let inspect_linux_kernel verbose (g : Guestfs.guestfs) inspect path =
|
||||
let root = inspect.i_root in
|
||||
|
||||
let base_package =
|
||||
let package_format = g#inspect_get_package_format root in
|
||||
match package_format with
|
||||
| "rpm" ->
|
||||
let cmd = [| "rpm"; "-qf"; "--qf"; "%{NAME}"; path |] in
|
||||
g#command cmd
|
||||
| format ->
|
||||
error (f_"don't know how to inspect kernel using %s") format in
|
||||
|
||||
(* Try to get kernel version by examination of the binary.
|
||||
* See supermin.git/src/kernel.ml
|
||||
*)
|
||||
let version =
|
||||
try
|
||||
let hdrS = g#pread path 4 514L in
|
||||
if hdrS <> "HdrS" then raise Not_found;
|
||||
let s = g#pread path 2 518L in
|
||||
let s = (Char.code s.[1] lsl 8) lor Char.code s.[0] in
|
||||
if s < 0x1ff then raise Not_found;
|
||||
let offset = g#pread path 2 526L in
|
||||
let offset = (Char.code offset.[1] lsl 8) lor Char.code offset.[0] in
|
||||
if offset < 0 then raise Not_found;
|
||||
let buf = g#pread path (offset + 0x200) 132L in
|
||||
let rec loop i =
|
||||
if i < 132 then (
|
||||
if buf.[i] = '\000' || buf.[i] = ' ' ||
|
||||
buf.[i] = '\t' || buf.[i] = '\n' then
|
||||
String.sub buf 0 i
|
||||
else
|
||||
loop (i+1)
|
||||
)
|
||||
else raise Not_found
|
||||
in
|
||||
let v = loop 0 in
|
||||
(* There must be a corresponding modules directory. *)
|
||||
let modpath = sprintf "/lib/modules/%s" v in
|
||||
if not (g#is_dir modpath) then
|
||||
raise Not_found;
|
||||
Some (v, modpath)
|
||||
with Not_found -> None in
|
||||
|
||||
(* Apparently Xen PV kernels don't contain a version number,
|
||||
* so try to guess the version from the filename.
|
||||
*)
|
||||
let version =
|
||||
match version with
|
||||
| Some v -> Some v
|
||||
| None ->
|
||||
let rex = Str.regexp "^/boot/vmlinuz-\\(.*\\)" in
|
||||
if Str.string_match rex path 0 then (
|
||||
let v = Str.matched_group 1 path in
|
||||
let modpath = sprintf "/lib/modules/%s" v in
|
||||
if g#is_dir modpath then Some (v, modpath) else None
|
||||
)
|
||||
else None in
|
||||
|
||||
(* If we sill didn't find a version, give up here. *)
|
||||
match version with
|
||||
| None -> None
|
||||
| Some (version, modpath) ->
|
||||
|
||||
(* List modules. *)
|
||||
let modules = g#find modpath in
|
||||
let modules = Array.to_list modules in
|
||||
let rex = Str.regexp ".*\\.k?o$" in
|
||||
let modules = List.filter (fun m -> Str.string_match rex m 0) modules in
|
||||
|
||||
assert (List.length modules > 0);
|
||||
|
||||
(* Determine the kernel architecture by looking at the architecture
|
||||
* of an arbitrary kernel module.
|
||||
*)
|
||||
let arch =
|
||||
let any_module = modpath ^ List.hd modules in
|
||||
g#file_architecture any_module in
|
||||
|
||||
(* Just return the module names, without path or extension. *)
|
||||
let rex = Str.regexp ".*/\\([^/]+\\)\\.k?o$/" in
|
||||
let modules = filter_map (
|
||||
fun m ->
|
||||
if Str.string_match rex m 0 then
|
||||
Some (Str.matched_group 1 m)
|
||||
else
|
||||
None
|
||||
) modules in
|
||||
|
||||
Some { base_package = base_package;
|
||||
version = version;
|
||||
modules = modules;
|
||||
arch = arch }
|
||||
45
v2v/convert_linux_common.mli
Normal file
45
v2v/convert_linux_common.mli
Normal file
@@ -0,0 +1,45 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(** Common Linux conversion code. *)
|
||||
|
||||
val augeas_init : bool -> Guestfs.guestfs -> unit
|
||||
val augeas_reload : bool -> Guestfs.guestfs -> unit
|
||||
(** Wrappers around [g#aug_init] and [g#aug_load], which (if verbose)
|
||||
provide additional debugging information about parsing problems
|
||||
that augeas found. *)
|
||||
|
||||
val install : bool -> Guestfs.guestfs -> Types.inspect -> string list -> unit
|
||||
(** Install package(s) from the list in the guest (or ensure they are
|
||||
installed). *)
|
||||
|
||||
val remove : bool -> Guestfs.guestfs -> Types.inspect -> string list -> unit
|
||||
(** Uninstall package(s). *)
|
||||
|
||||
val file_owned : bool -> Guestfs.guestfs -> Types.inspect -> string -> bool
|
||||
(** Returns true if the file is owned by an installed package. *)
|
||||
|
||||
type kernel_info = {
|
||||
base_package : string; (* base package, eg. "kernel-PAE" *)
|
||||
version : string; (* kernel version *)
|
||||
modules : string list; (* list of kernel modules *)
|
||||
arch : string; (* kernel arch *)
|
||||
}
|
||||
|
||||
val inspect_linux_kernel : bool -> Guestfs.guestfs -> Types.inspect -> string -> kernel_info option
|
||||
(** Inspect a Linux kernel (by path) and return various information. *)
|
||||
637
v2v/convert_linux_enterprise.ml
Normal file
637
v2v/convert_linux_enterprise.ml
Normal file
@@ -0,0 +1,637 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(* Convert various RPM-based Linux enterprise distros. This module
|
||||
* handles:
|
||||
*
|
||||
* - RHEL and derivatives like CentOS and ScientificLinux
|
||||
* - SUSE
|
||||
* - OpenSUSE and Fedora (not enterprisey, but similar enough to RHEL/SUSE)
|
||||
*)
|
||||
|
||||
open Printf
|
||||
|
||||
open Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Utils
|
||||
open Types
|
||||
|
||||
let rec convert ?(keep_serial_console = true) verbose (g : Guestfs.guestfs)
|
||||
({ i_root = root; i_apps = apps }
|
||||
as inspect) source =
|
||||
let typ = g#inspect_get_type root
|
||||
and distro = g#inspect_get_distro root
|
||||
and arch = g#inspect_get_arch root
|
||||
and major_version = g#inspect_get_major_version root
|
||||
and minor_version = g#inspect_get_minor_version root
|
||||
and package_format = g#inspect_get_package_format root
|
||||
and package_management = g#inspect_get_package_management root in
|
||||
|
||||
assert (typ = "linux");
|
||||
|
||||
let is_rhel_family =
|
||||
(distro = "rhel" || distro = "centos"
|
||||
|| distro = "scientificlinux" || distro = "redhat-based")
|
||||
|
||||
and is_suse_family =
|
||||
(distro = "sles" || distro = "suse-based" || distro = "opensuse") in
|
||||
|
||||
let rec clean_rpmdb () =
|
||||
(* Clean RPM database. *)
|
||||
assert (package_format = "rpm");
|
||||
let dbfiles = g#glob_expand "/var/lib/rpm/__db.00?" in
|
||||
let dbfiles = Array.to_list dbfiles in
|
||||
List.iter g#rm_f dbfiles
|
||||
|
||||
and autorelabel () =
|
||||
(* Only do autorelabel if load_policy binary exists. Actually
|
||||
* loading the policy is problematic.
|
||||
*)
|
||||
if g#is_file ~followsymlinks:true "/usr/sbin/load_policy" then
|
||||
g#touch "/.autorelabel";
|
||||
|
||||
and get_grub () =
|
||||
(* Detect if grub2 or grub1 is installed by trying to create
|
||||
* an object of each sort.
|
||||
*)
|
||||
try Convert_linux_grub.grub2 verbose g inspect
|
||||
with Failure grub2_error ->
|
||||
try Convert_linux_grub.grub1 verbose g inspect
|
||||
with Failure grub1_error ->
|
||||
error (f_"no grub configuration found in this guest.
|
||||
Grub2 error was: %s
|
||||
Grub1/grub-legacy error was: %s")
|
||||
grub2_error grub1_error
|
||||
|
||||
and unconfigure_xen () =
|
||||
(* Remove kmod-xenpv-* (RHEL 3). *)
|
||||
let xenmods =
|
||||
filter_map (
|
||||
fun { G.app2_name = name } ->
|
||||
if name = "kmod-xenpv" || string_prefix name "kmod-xenpv-" then
|
||||
Some name
|
||||
else
|
||||
None
|
||||
) apps in
|
||||
Convert_linux_common.remove verbose g inspect xenmods;
|
||||
|
||||
(* Undo related nastiness if kmod-xenpv was installed. *)
|
||||
if xenmods <> [] then (
|
||||
(* kmod-xenpv modules may have been manually copied to other kernels.
|
||||
* Hunt them down and destroy them.
|
||||
*)
|
||||
let dirs = g#find "/lib/modules" in
|
||||
let dirs = Array.to_list dirs in
|
||||
let dirs = List.filter (fun s -> string_find s "/xenpv" >= 0) dirs in
|
||||
let dirs = List.map ((^) "/lib/modules/") dirs in
|
||||
let dirs = List.filter g#is_dir dirs in
|
||||
|
||||
(* Check it's not owned by an installed application. *)
|
||||
let dirs = List.filter (
|
||||
fun d -> not (Convert_linux_common.file_owned verbose g inspect d)
|
||||
) dirs in
|
||||
|
||||
(* Remove any unowned xenpv directories. *)
|
||||
List.iter g#rm_rf dirs;
|
||||
|
||||
(* rc.local may contain an insmod or modprobe of the xen-vbd driver,
|
||||
* added by an installation script.
|
||||
*)
|
||||
(try
|
||||
let lines = g#read_lines "/etc/rc.local" in
|
||||
let lines = Array.to_list lines in
|
||||
let rex = Str.regexp ".*\\b\\(insmod|modprobe\\)\b.*\\bxen-vbd.*" in
|
||||
let lines = List.map (
|
||||
fun s ->
|
||||
if Str.string_match rex s 0 then
|
||||
"#" ^ s
|
||||
else
|
||||
s
|
||||
) lines in
|
||||
let file = String.concat "\n" lines ^ "\n" in
|
||||
g#write "/etc/rc.local" file
|
||||
with
|
||||
G.Error msg -> eprintf "%s: /etc/rc.local: %s (ignored)\n" prog msg
|
||||
);
|
||||
);
|
||||
|
||||
if is_suse_family then (
|
||||
(* Remove xen modules from INITRD_MODULES and DOMU_INITRD_MODULES. *)
|
||||
let variables = ["INITRD_MODULES"; "DOMU_INITRD_MODULES"] in
|
||||
let xen_modules = ["xennet"; "xen-vnif"; "xenblk"; "xen-vbd"] in
|
||||
let modified = ref false in
|
||||
List.iter (
|
||||
fun var ->
|
||||
List.iter (
|
||||
fun xen_mod ->
|
||||
let expr =
|
||||
sprintf "/file/etc/sysconfig/kernel/%s/value[. = '%s']"
|
||||
var xen_mod in
|
||||
let entries = g#aug_match expr in
|
||||
let entries = Array.to_list entries in
|
||||
if entries <> [] then (
|
||||
List.iter (fun e -> ignore (g#aug_rm e)) entries;
|
||||
modified := true
|
||||
)
|
||||
) xen_modules
|
||||
) variables;
|
||||
if !modified then g#aug_save ()
|
||||
);
|
||||
|
||||
and unconfigure_vbox () =
|
||||
(* Uninstall VirtualBox Guest Additions. *)
|
||||
let package_name = "virtualbox-guest-additions" in
|
||||
let has_guest_additions =
|
||||
List.exists (
|
||||
fun { G.app2_name = name } -> name = package_name
|
||||
) apps in
|
||||
if has_guest_additions then
|
||||
Convert_linux_common.remove verbose g inspect [package_name];
|
||||
|
||||
(* Guest Additions might have been installed from a tarball. The
|
||||
* above code won't detect this case. Look for the uninstall tool
|
||||
* and try running it.
|
||||
*
|
||||
* Note that it's important we do this early in the conversion
|
||||
* process, as this uninstallation script naively overwrites
|
||||
* configuration files with versions it cached prior to
|
||||
* installation.
|
||||
*)
|
||||
let vboxconfig = "/var/lib/VBoxGuestAdditions/config" in
|
||||
if g#is_file ~followsymlinks:true vboxconfig then (
|
||||
let lines = g#read_lines vboxconfig in
|
||||
let lines = Array.to_list lines in
|
||||
let rex = Str.regexp "^INSTALL_DIR=\\(.*\\)$" in
|
||||
let lines = filter_map (
|
||||
fun line ->
|
||||
if Str.string_match rex line 0 then (
|
||||
let vboxuninstall = Str.matched_group 1 line ^ "/uninstall.sh" in
|
||||
Some vboxuninstall
|
||||
)
|
||||
else None
|
||||
) lines in
|
||||
let lines = List.filter (g#is_file ~followsymlinks:true) lines in
|
||||
match lines with
|
||||
| [] -> ()
|
||||
| vboxuninstall :: _ ->
|
||||
try
|
||||
ignore (g#command [| vboxuninstall |]);
|
||||
|
||||
(* Reload Augeas to detect changes made by vbox tools uninst. *)
|
||||
Convert_linux_common.augeas_reload verbose g
|
||||
with
|
||||
G.Error msg ->
|
||||
eprintf (f_"%s: warning: VirtualBox Guest Additions were detected, but uninstallation failed. The error message was: %s (ignored)")
|
||||
prog msg
|
||||
)
|
||||
|
||||
and unconfigure_vmware () =
|
||||
(* Look for any configured VMware yum repos and disable them. *)
|
||||
let repos =
|
||||
g#aug_match "/files/etc/yum.repos.d/*/*[baseurl =~ regexp('https?://([^/]+\\.)?vmware\\.com/.*')]" in
|
||||
let repos = Array.to_list repos in
|
||||
List.iter (
|
||||
fun repo ->
|
||||
g#aug_set (repo ^ "/enabled") "0";
|
||||
g#aug_save ()
|
||||
) repos;
|
||||
|
||||
(* Uninstall VMware Tools. *)
|
||||
let remove = ref [] and libraries = ref [] in
|
||||
List.iter (
|
||||
fun { G.app2_name = name } ->
|
||||
if name = "open-vm-tools" then
|
||||
remove := name :: !remove
|
||||
else if string_prefix name "vmware-tools-libraries-" then
|
||||
libraries := name :: !libraries
|
||||
else if string_prefix name "vmware-tools-" then
|
||||
remove := name :: !remove
|
||||
) apps;
|
||||
let libraries = !libraries in
|
||||
|
||||
(* VMware tools includes 'libraries' packages which provide custom
|
||||
* versions of core functionality. We need to install non-custom
|
||||
* versions of everything provided by these packages before
|
||||
* attempting to uninstall them, or we'll hit dependency
|
||||
* issues.
|
||||
*)
|
||||
if libraries <> [] then (
|
||||
(* We only support removal of libraries on systems which use yum. *)
|
||||
if package_management = "yum" then (
|
||||
List.iter (
|
||||
fun library ->
|
||||
let provides =
|
||||
g#command_lines [| "rpm"; "-q"; "--provides"; library |] in
|
||||
let provides = Array.to_list provides in
|
||||
|
||||
(* The packages provide themselves, filter this out. *)
|
||||
let provides =
|
||||
List.filter (fun s -> string_find s library = -1) provides in
|
||||
|
||||
(* Trim whitespace. *)
|
||||
let rex = Str.regexp "^[ \\t]*\\([^ \\t]+\\)[ \\t]*$" in
|
||||
let provides = List.map (Str.replace_first rex "\\1") provides in
|
||||
|
||||
(* Install the dependencies with yum. Use yum explicitly
|
||||
* because we don't have package names and local install is
|
||||
* impractical. - RWMJ: Not convinced the original Perl code
|
||||
* would work, so I'm just installing the dependencies.
|
||||
*)
|
||||
let cmd = [ "yum"; "install"; "-y" ] @ provides in
|
||||
let cmd = Array.of_list cmd in
|
||||
(try
|
||||
ignore (g#command cmd);
|
||||
remove := library :: !remove
|
||||
with G.Error msg ->
|
||||
eprintf "%s: could not install replacement for %s. Error was: %s. %s was not removed.\n"
|
||||
prog library msg library
|
||||
);
|
||||
) libraries
|
||||
)
|
||||
);
|
||||
|
||||
let remove = !remove in
|
||||
Convert_linux_common.remove verbose g inspect remove;
|
||||
|
||||
(* VMware Tools may have been installed from a tarball, so the
|
||||
* above code won't remove it. Look for the uninstall tool and run
|
||||
* if present.
|
||||
*)
|
||||
let uninstaller = "/usr/bin/vmware-uninstall-tools.pl" in
|
||||
if g#is_file ~followsymlinks:true uninstaller then (
|
||||
try
|
||||
ignore (g#command [| uninstaller |]);
|
||||
|
||||
(* Reload Augeas to detect changes made by vbox tools uninst. *)
|
||||
Convert_linux_common.augeas_reload verbose g
|
||||
with
|
||||
G.Error msg ->
|
||||
eprintf (f_"%s: warning: VMware tools was detected, but uninstallation failed. The error message was: %s (ignored)")
|
||||
prog msg
|
||||
)
|
||||
|
||||
and unconfigure_citrix () =
|
||||
let pkgs =
|
||||
List.filter (
|
||||
fun { G.app2_name = name } -> string_prefix name "xe-guest-utilities"
|
||||
) apps in
|
||||
let pkgs = List.map (fun { G.app2_name = name } -> name) pkgs in
|
||||
|
||||
if pkgs <> [] then (
|
||||
Convert_linux_common.remove verbose g inspect pkgs;
|
||||
|
||||
(* Installing these guest utilities automatically unconfigures
|
||||
* ttys in /etc/inittab if the system uses it. We need to put
|
||||
* them back.
|
||||
*)
|
||||
let rex = Str.regexp "^\\([1-6]\\):\\([2-5]+\\):respawn:\\(.*\\)" in
|
||||
let updated = ref false in
|
||||
let rec loop () =
|
||||
let comments = g#aug_match "/files/etc/inittab/#comment" in
|
||||
let comments = Array.to_list comments in
|
||||
match comments with
|
||||
| [] -> ()
|
||||
| commentp :: _ ->
|
||||
let comment = g#aug_get commentp in
|
||||
if Str.string_match rex comment 0 then (
|
||||
let name = Str.matched_group 1 comment in
|
||||
let runlevels = Str.matched_group 2 comment in
|
||||
let process = Str.matched_group 3 comment in
|
||||
|
||||
if string_find process "getty" >= 0 then (
|
||||
updated := true;
|
||||
|
||||
(* Create a new entry immediately after the comment. *)
|
||||
g#aug_insert commentp name false;
|
||||
g#aug_set ("/files/etc/inittab/" ^ name ^ "/runlevels") runlevels;
|
||||
g#aug_set ("/files/etc/inittab/" ^ name ^ "/action") "respawn";
|
||||
g#aug_set ("/files/etc/inittab/" ^ name ^ "/process") process;
|
||||
|
||||
(* Delete the comment node. *)
|
||||
ignore (g#aug_rm commentp);
|
||||
|
||||
(* As the aug_rm invalidates the output of aug_match, we
|
||||
* now have to restart the whole loop.
|
||||
*)
|
||||
loop ()
|
||||
)
|
||||
)
|
||||
in
|
||||
loop ();
|
||||
if !updated then g#aug_save ();
|
||||
)
|
||||
|
||||
and install_virtio () =
|
||||
(* How you install virtio depends on the guest type. Note that most
|
||||
* modern guests already support virtio, so we do nothing for them.
|
||||
* In Perl virt-v2v this was done via a configuration database
|
||||
* (virt-v2v.db). This function returns true if virtio is supported
|
||||
* already or if we managed to install it.
|
||||
*)
|
||||
match distro, major_version, minor_version with
|
||||
(* RHEL 6+ has always supported virtio. *)
|
||||
| ("rhel"|"centos"|"scientificlinux"|"redhat-based"), v, _ when v >= 6 ->
|
||||
true
|
||||
| ("rhel"|"centos"|"scientificlinux"|"redhat-based"), 5, _ ->
|
||||
let kernel = upgrade_package "kernel" (0_l, "2.6.18", "128.el5") in
|
||||
let lvm2 = upgrade_package "lvm2" (0_l, "2.02.40", "6.el5") in
|
||||
let selinux =
|
||||
upgrade_package ~ifinstalled:true
|
||||
"selinux-policy-targeted" (0_l, "2.4.6", "203.el5") in
|
||||
kernel && lvm2 && selinux
|
||||
| ("rhel"|"centos"|"scientificlinux"|"redhat-based"), 4, _ ->
|
||||
upgrade_package "kernel" (0_l, "2.6.9", "89.EL")
|
||||
|
||||
(* All supported Fedora versions support virtio. *)
|
||||
| "fedora", _, _ -> true
|
||||
|
||||
(* SLES 11 supports virtio in the kernel. *)
|
||||
| ("sles"|"suse-based"), v, _ when v >= 11 -> true
|
||||
| ("sles"|"suse-based"), 10, _ ->
|
||||
upgrade_package "kernel" (0_l, "2.6.16.60", "0.85.1")
|
||||
|
||||
(* OpenSUSE. *)
|
||||
| "opensuse", v, _ when v >= 11 -> true
|
||||
| "opensuse", 10, _ ->
|
||||
upgrade_package "kernel" (0_l, "2.6.25.5", "1.1")
|
||||
|
||||
| _ ->
|
||||
eprintf (f_"%s: warning: don't know how to install virtio drivers for %s %d")
|
||||
prog distro major_version;
|
||||
false
|
||||
|
||||
and configure_kernel virtio grub =
|
||||
let kernels = grub#list_kernels () in
|
||||
|
||||
let bootable_kernel =
|
||||
let rec loop =
|
||||
function
|
||||
| [] -> None
|
||||
| path :: paths ->
|
||||
let kernel =
|
||||
Convert_linux_common.inspect_linux_kernel verbose g inspect path in
|
||||
match kernel with
|
||||
| None -> loop paths
|
||||
| Some kernel when is_hv_kernel kernel -> loop paths
|
||||
| Some kernel when virtio && not (supports_virtio kernel) ->
|
||||
loop paths
|
||||
| Some kernel -> Some kernel
|
||||
in
|
||||
loop kernels in
|
||||
|
||||
(* If virtio == true, then a virtio kernel should have been
|
||||
* installed. If we didn't find one, it indicates a bug in
|
||||
* virt-v2v.
|
||||
*)
|
||||
if virtio && bootable_kernel = None then
|
||||
error (f_"virtio configured, but no virtio kernel found");
|
||||
|
||||
(* No bootable kernel was found. Install one. *)
|
||||
let bootable_kernel =
|
||||
match bootable_kernel with
|
||||
| Some k -> k
|
||||
| None ->
|
||||
(* Find which kernel is currently used by the guest. *)
|
||||
let current_kernel =
|
||||
let rec loop = function
|
||||
| [] -> "kernel"
|
||||
| path :: paths ->
|
||||
let kernel =
|
||||
Convert_linux_common.inspect_linux_kernel verbose g inspect
|
||||
path in
|
||||
match kernel with
|
||||
| None -> loop paths
|
||||
| Some kernel -> kernel.Convert_linux_common.base_package
|
||||
in
|
||||
loop kernels in
|
||||
|
||||
(* Replace kernel-xen with a suitable kernel. *)
|
||||
let current_kernel =
|
||||
if string_find current_kernel "kernel-xen" >= 0 then
|
||||
xen_replacement_kernel ()
|
||||
else
|
||||
current_kernel in
|
||||
|
||||
(* Install the kernel. However we need a way to detect the
|
||||
* version of the kernel that has just been installed. A quick
|
||||
* way is to compare /lib/modules before and after.
|
||||
*)
|
||||
let files1 = g#ls "/lib/modules" in
|
||||
let files1 = Array.to_list files1 in
|
||||
Convert_linux_common.install verbose g inspect [current_kernel];
|
||||
let files2 = g#ls "/lib/modules" in
|
||||
let files2 = Array.to_list files2 in
|
||||
|
||||
(* Note that g#ls is guaranteed to return the strings in order. *)
|
||||
let rec loop files1 files2 =
|
||||
match files1, files2 with
|
||||
| [], [] ->
|
||||
error (f_"tried to install '%s', but no kernel package was installed") current_kernel
|
||||
| (v1 :: _), [] ->
|
||||
error (f_"tried to install '%s', but there are now fewer directories under /lib/modules!") current_kernel
|
||||
| [], (v2 :: _) -> v2
|
||||
| (v1 :: _), (v2 :: _) when v1 <> v2 -> v2
|
||||
| (_ :: v1s), (_ :: v2s) -> loop v1s v2s
|
||||
in
|
||||
let version = loop files1 files2 in
|
||||
|
||||
{ Convert_linux_common.base_package = current_kernel;
|
||||
version = version; modules = []; arch = "" } in
|
||||
|
||||
(* Set /etc/sysconfig/kernel DEFAULTKERNEL to point to the new
|
||||
* kernel package name.
|
||||
*)
|
||||
if g#is_file ~followsymlinks:true "/etc/sysconfig/kernel" then (
|
||||
let base_package = bootable_kernel.Convert_linux_common.base_package in
|
||||
let paths =
|
||||
g#aug_match "/files/etc/sysconfig/kernel/DEFAULTKERNEL/value" in
|
||||
let paths = Array.to_list paths in
|
||||
List.iter (fun path -> g#aug_set path base_package) paths;
|
||||
g#aug_save ()
|
||||
);
|
||||
|
||||
(* Return the installed kernel version. *)
|
||||
bootable_kernel.Convert_linux_common.version
|
||||
|
||||
and supports_virtio { Convert_linux_common.modules = modules } =
|
||||
List.mem "virtio_blk" modules && List.mem "virtio_net" modules
|
||||
|
||||
(* Is it a hypervisor-specific kernel? *)
|
||||
and is_hv_kernel { Convert_linux_common.modules = modules } =
|
||||
List.mem "xennet" modules (* Xen PV kernel. *)
|
||||
|
||||
(* Find a suitable replacement for kernel-xen. *)
|
||||
and xen_replacement_kernel () =
|
||||
if is_rhel_family then (
|
||||
match major_version, arch with
|
||||
| 5, ("i386"|"i486"|"i586"|"i686") -> "kernel-PAE"
|
||||
| 5, _ -> "kernel"
|
||||
| 4, ("i386"|"i486"|"i586"|"i686") ->
|
||||
(* If guest has >= 10GB of RAM, give it a hugemem kernel. *)
|
||||
if source.s_memory >= 10L *^ 1024L *^ 1024L *^ 1024L then
|
||||
"kernel-hugemem"
|
||||
(* SMP kernel for guests with > 1 vCPU. *)
|
||||
else if source.s_vcpu > 1 then
|
||||
"kernel-smp"
|
||||
else
|
||||
"kernel"
|
||||
| 4, _ ->
|
||||
if source.s_vcpu > 8 then "kernel-largesmp"
|
||||
else if source.s_vcpu > 1 then "kernel-smp"
|
||||
else "kernel"
|
||||
| _, _ -> "kernel"
|
||||
)
|
||||
else if is_suse_family then (
|
||||
match distro, major_version, arch with
|
||||
| "opensuse", _, _ -> "kernel-default"
|
||||
| _, v, ("i386"|"i486"|"i586"|"i686") when v >= 11 ->
|
||||
if source.s_memory >= 10L *^ 1024L *^ 1024L *^ 1024L then
|
||||
"kernel-pae"
|
||||
else
|
||||
"kernel"
|
||||
| _, v, _ when v >= 11 -> "kernel-default"
|
||||
| _, 10, ("i386"|"i486"|"i586"|"i686") ->
|
||||
if source.s_memory >= 10L *^ 1024L *^ 1024L *^ 1024L then
|
||||
"kernel-bigsmp"
|
||||
else if source.s_vcpu > 1 then
|
||||
"kernel-smp"
|
||||
else
|
||||
"kernel-default"
|
||||
| _, 10, _ ->
|
||||
if source.s_vcpu > 1 then
|
||||
"kernel-smp"
|
||||
else
|
||||
"kernel-default"
|
||||
| _ -> "kernel-default"
|
||||
)
|
||||
else
|
||||
"kernel" (* conservative default *)
|
||||
|
||||
(* We configure a console on ttyS0. Make sure existing console
|
||||
* references use it. N.B. Note that the RHEL 6 xen guest kernel
|
||||
* presents a console device called /dev/hvc0, whereas previous xen
|
||||
* guest kernels presented /dev/xvc0. The regular kernel running
|
||||
* under KVM also presents a virtio console device called /dev/hvc0,
|
||||
* so ideally we would just leave it alone. However, RHEL 6 libvirt
|
||||
* doesn't yet support this device so we can't attach to it. We
|
||||
* therefore use /dev/ttyS0 for RHEL 6 anyway.
|
||||
*)
|
||||
and configure_console () =
|
||||
(* Look for gettys using xvc0 or hvc0. RHEL 6 doesn't use inittab
|
||||
* but this still works.
|
||||
*)
|
||||
let paths = g#aug_match "/files/etc/inittab/*/process" in
|
||||
let paths = Array.to_list paths in
|
||||
let rex = Str.regexp "\\(.*\\)\\b\\([xh]vc0\\)\\b\\(.*\\)" in
|
||||
List.iter (
|
||||
fun path ->
|
||||
let proc = g#aug_get path in
|
||||
if Str.string_match rex proc 0 then (
|
||||
let proc = Str.global_replace rex "\\1ttyS0\\3" proc in
|
||||
g#aug_set path proc
|
||||
);
|
||||
) paths;
|
||||
|
||||
let paths = g#aug_match "/files/etc/securetty/*" in
|
||||
let paths = Array.to_list paths in
|
||||
List.iter (
|
||||
fun path ->
|
||||
let tty = g#aug_get path in
|
||||
if tty = "xvc0" || tty = "hvc0" then
|
||||
g#aug_set path "ttyS0"
|
||||
) paths;
|
||||
|
||||
g#aug_save ()
|
||||
|
||||
(* If the target doesn't support a serial console, we want to remove
|
||||
* all references to it instead.
|
||||
*)
|
||||
and remove_console () =
|
||||
(* Look for gettys using xvc0 or hvc0. RHEL 6 doesn't use inittab
|
||||
* but this still works.
|
||||
*)
|
||||
let paths = g#aug_match "/files/etc/inittab/*/process" in
|
||||
let paths = Array.to_list paths in
|
||||
let rex = Str.regexp ".*\\b\\([xh]vc0|ttyS0\\)\\b.*" in
|
||||
List.iter (
|
||||
fun path ->
|
||||
let proc = g#aug_get path in
|
||||
if Str.string_match rex proc 0 then
|
||||
ignore (g#aug_rm (path ^ "/.."))
|
||||
) paths;
|
||||
|
||||
let paths = g#aug_match "/files/etc/securetty/*" in
|
||||
let paths = Array.to_list paths in
|
||||
List.iter (
|
||||
fun path ->
|
||||
let tty = g#aug_get path in
|
||||
if tty = "xvc0" || tty = "hvc0" then
|
||||
ignore (g#aug_rm path)
|
||||
) paths;
|
||||
|
||||
g#aug_save ()
|
||||
|
||||
(* Upgrade 'pkg' to >= minversion. Returns true if that was possible. *)
|
||||
and upgrade_package ?(ifinstalled = false) name minversion =
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(* XXX *)
|
||||
true
|
||||
|
||||
|
||||
in
|
||||
|
||||
clean_rpmdb ();
|
||||
autorelabel ();
|
||||
Convert_linux_common.augeas_init verbose g;
|
||||
let grub = get_grub () in
|
||||
|
||||
unconfigure_xen ();
|
||||
unconfigure_vbox ();
|
||||
unconfigure_vmware ();
|
||||
unconfigure_citrix ();
|
||||
|
||||
let virtio = install_virtio () in
|
||||
let kernel_version = configure_kernel virtio grub in (*XXX*) ignore kernel_version;
|
||||
if keep_serial_console then (
|
||||
configure_console ();
|
||||
grub#configure_console ()
|
||||
) else (
|
||||
remove_console ();
|
||||
grub#remove_console ()
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let guestcaps = {
|
||||
gcaps_block_bus = if virtio then "virtio" else "ide";
|
||||
gcaps_net_bus = if virtio then "virtio" else "e1000";
|
||||
(* XXX display *)
|
||||
} in
|
||||
|
||||
guestcaps
|
||||
19
v2v/convert_linux_enterprise.mli
Normal file
19
v2v/convert_linux_enterprise.mli
Normal file
@@ -0,0 +1,19 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
val convert : ?keep_serial_console:bool -> bool -> Guestfs.guestfs -> Types.inspect -> Types.source -> Types.guestcaps
|
||||
330
v2v/convert_linux_grub.ml
Normal file
330
v2v/convert_linux_grub.ml
Normal file
@@ -0,0 +1,330 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
module G = Guestfs
|
||||
|
||||
open Printf
|
||||
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Utils
|
||||
open Types
|
||||
|
||||
(* Helper function for SUSE: remove (hdX,X) prefix from a path. *)
|
||||
let remove_hd_prefix =
|
||||
let rex = Str.regexp "^(hd.*)\\(.*\\)" in
|
||||
Str.replace_first rex "\\1"
|
||||
|
||||
(* Helper function to check if guest is EFI. *)
|
||||
let check_efi g =
|
||||
if Array.length (g#glob_expand "/boot/efi/EFI/*/grub.cfg") < 1 then
|
||||
raise Not_found;
|
||||
|
||||
(* Check the first partition of each device looking for an EFI
|
||||
* boot partition. We can't be sure which device is the boot
|
||||
* device, so we just check them all.
|
||||
*)
|
||||
let devs = g#list_devices () in
|
||||
let devs = Array.to_list devs in
|
||||
List.find (
|
||||
fun dev ->
|
||||
try g#part_get_gpt_type dev 1 = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
|
||||
with G.Error _ -> false
|
||||
) devs
|
||||
|
||||
(* Virtual grub superclass. *)
|
||||
class virtual grub verbose (g : Guestfs.guestfs) inspect config_file =
|
||||
object
|
||||
method virtual list_kernels : unit -> string list
|
||||
|
||||
method virtual configure_console : unit -> unit
|
||||
method virtual remove_console : unit -> unit
|
||||
|
||||
method private get_default_image () =
|
||||
let cmd =
|
||||
if g#exists "/sbin/grubby" then
|
||||
[| "grubby"; "--default-kernel" |]
|
||||
else
|
||||
[| "/usr/bin/perl"; "-MBootloader::Tools"; "-e"; "
|
||||
InitLibrary();
|
||||
my $default = Bootloader::Tools::GetDefaultSection();
|
||||
print $default->{image};
|
||||
" |] in
|
||||
match g#command cmd with
|
||||
| "" -> None
|
||||
| k ->
|
||||
let len = String.length k in
|
||||
let k =
|
||||
if len > 0 && k.[len-1] = '\n' then String.sub k 0 (len-1) else k in
|
||||
Some (remove_hd_prefix k)
|
||||
end
|
||||
|
||||
(* Concrete implementation for grub1. *)
|
||||
class grub1 verbose g inspect config_file grub_fs =
|
||||
object (self)
|
||||
inherit grub verbose g inspect config_file
|
||||
|
||||
method private grub_fs = grub_fs (* grub filesystem prefix *)
|
||||
|
||||
method list_kernels () =
|
||||
let paths =
|
||||
let expr = sprintf "/files%s/title/kernel" config_file in
|
||||
let paths = g#aug_match expr in
|
||||
let paths = Array.to_list paths in
|
||||
|
||||
(* Get the default kernel from grub if it's set. *)
|
||||
let default =
|
||||
let expr = sprintf "/files%s/default" config_file in
|
||||
try
|
||||
let idx = g#aug_get expr in
|
||||
let idx = int_of_string idx in
|
||||
(* Grub indices are zero-based, augeas is 1-based. *)
|
||||
let expr = sprintf "/files%s/title[%d]/kernel" config_file (idx+1) in
|
||||
Some expr
|
||||
with Not_found -> None in
|
||||
|
||||
(* If a default kernel was set, put it at the beginning of the paths
|
||||
* list.
|
||||
*)
|
||||
match default with
|
||||
| None -> paths
|
||||
| Some p -> p :: List.filter ((<>) p) paths in
|
||||
|
||||
(* Remove duplicates. *)
|
||||
let paths =
|
||||
let checked = Hashtbl.create 13 in
|
||||
let rec loop = function
|
||||
| [] -> []
|
||||
| p :: ps when Hashtbl.mem checked p -> ps
|
||||
| p :: ps -> Hashtbl.add checked p true; p :: loop ps
|
||||
in
|
||||
loop paths in
|
||||
|
||||
(* Resolve the Augeas paths to kernel filenames. *)
|
||||
let kernels = List.map g#aug_get paths in
|
||||
|
||||
(* Make sure kernel does not begin with (hdX,X). *)
|
||||
let kernels = List.map remove_hd_prefix kernels in
|
||||
|
||||
(* Prepend grub filesystem. *)
|
||||
let kernels = List.map ((^) grub_fs) kernels in
|
||||
|
||||
(* Check the actual file exists. *)
|
||||
let kernels = List.filter (g#is_file ~followsymlinks:true) kernels in
|
||||
|
||||
kernels
|
||||
|
||||
method configure_console () =
|
||||
let rex = Str.regexp "\\(.*\\)\\b\\([xh]vc0\\)\\b\\(.*\\)" in
|
||||
let expr = sprintf "/files%s/title/kernel/console" config_file in
|
||||
|
||||
let paths = g#aug_match expr in
|
||||
let paths = Array.to_list paths in
|
||||
List.iter (
|
||||
fun path ->
|
||||
let console = g#aug_get path in
|
||||
if Str.string_match rex console 0 then (
|
||||
let console = Str.global_replace rex "\\1ttyS0\\3" console in
|
||||
g#aug_set path console
|
||||
)
|
||||
) paths;
|
||||
|
||||
g#aug_save ()
|
||||
|
||||
method remove_console () =
|
||||
let rex = Str.regexp "\\(.*\\)\\b\\([xh]vc0\\)\\b\\(.*\\)" in
|
||||
let expr = sprintf "/files%s/title/kernel/console" config_file in
|
||||
|
||||
let rec loop = function
|
||||
| [] -> ()
|
||||
| path :: paths ->
|
||||
let console = g#aug_get path in
|
||||
if Str.string_match rex console 0 then (
|
||||
ignore (g#aug_rm path);
|
||||
(* All the paths are invalid, restart the loop. *)
|
||||
let paths = g#aug_match expr in
|
||||
let paths = Array.to_list paths in
|
||||
loop paths
|
||||
)
|
||||
else
|
||||
loop paths
|
||||
in
|
||||
let paths = g#aug_match expr in
|
||||
let paths = Array.to_list paths in
|
||||
loop paths;
|
||||
|
||||
g#aug_save ()
|
||||
|
||||
end
|
||||
|
||||
(* Create a grub1 object. *)
|
||||
let rec grub1 verbose (g : Guestfs.guestfs) inspect =
|
||||
let root = inspect.i_root in
|
||||
|
||||
(* Look for a grub configuration file. *)
|
||||
let config_file =
|
||||
try
|
||||
List.find (
|
||||
fun file -> g#is_file ~followsymlinks:true file
|
||||
) ["/boot/grub/menu.lst"; "/boot/grub/grub.conf"]
|
||||
with
|
||||
Not_found ->
|
||||
failwith (s_"no grub/grub1/grub-legacy configuration file was found") in
|
||||
|
||||
(* Check for EFI and convert if found. *)
|
||||
(try let dev = check_efi g in grub1_convert_from_efi verbose g dev
|
||||
with Not_found -> ()
|
||||
);
|
||||
|
||||
(* Find the path that has to be prepended to filenames in grub.conf
|
||||
* in order to make them absolute.
|
||||
*)
|
||||
let grub_fs =
|
||||
let mounts = g#inspect_get_mountpoints root in
|
||||
try
|
||||
List.find (
|
||||
fun path -> List.mem_assoc path mounts
|
||||
) [ "/boot/grub"; "/boot" ]
|
||||
with Not_found -> "" in
|
||||
|
||||
(* Ensure Augeas is reading the grub configuration file, and if not
|
||||
* then add it.
|
||||
*)
|
||||
let () =
|
||||
let incls = g#aug_match "/augeas/load/Grub/incl" in
|
||||
let incls = Array.to_list incls in
|
||||
let incls_contains_conf =
|
||||
List.exists (fun incl -> g#aug_get incl = config_file) incls in
|
||||
if not incls_contains_conf then (
|
||||
g#aug_set "/augeas/load/Grub/incl[last()+1]" config_file;
|
||||
Convert_linux_common.augeas_reload verbose g;
|
||||
) in
|
||||
|
||||
new grub1 verbose g inspect config_file grub_fs
|
||||
|
||||
(* Reinstall grub. *)
|
||||
and grub1_convert_from_efi verbose g dev =
|
||||
g#cp "/etc/grub.conf" "/boot/grub/grub.conf";
|
||||
g#ln_sf "/boot/grub/grub.conf" "/etc/grub.conf";
|
||||
|
||||
(* Reload Augeas to pick up new location of grub.conf. *)
|
||||
Convert_linux_common.augeas_reload verbose g;
|
||||
|
||||
ignore (g#command [| "grub-install"; dev |])
|
||||
|
||||
(* Concrete implementation for grub2. *)
|
||||
class grub2 verbose g inspect config_file =
|
||||
object (self)
|
||||
inherit grub verbose g inspect config_file
|
||||
|
||||
method list_kernels () =
|
||||
let files =
|
||||
(match self#get_default_image () with
|
||||
| None -> []
|
||||
| Some k -> [k]) @
|
||||
(* This is how the grub2 config generator enumerates kernels. *)
|
||||
Array.to_list (g#glob_expand "/boot/kernel-*") @
|
||||
Array.to_list (g#glob_expand "/boot/vmlinuz-*") @
|
||||
Array.to_list (g#glob_expand "/vmlinuz-*") in
|
||||
let rex = Str.regexp ".*\\.\\(dpkg-.*|rpmsave|rpmnew\\)$" in
|
||||
let files = List.filter (
|
||||
fun file -> not (Str.string_match rex file 0)
|
||||
) files in
|
||||
files
|
||||
|
||||
method private update_console ~remove =
|
||||
let rex = Str.regexp "\\(.*\\)\\bconsole=[xh]vc0\\b\\(.*\\)" in
|
||||
|
||||
let grub_cmdline_expr =
|
||||
if g#exists "/etc/sysconfig/grub" then
|
||||
"/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX"
|
||||
else
|
||||
"/files/etc/default/grub/GRUB_CMDLINE_LINUX_DEFAULT" in
|
||||
|
||||
(try
|
||||
let grub_cmdline = g#aug_get grub_cmdline_expr in
|
||||
let grub_cmdline =
|
||||
if Str.string_match rex grub_cmdline 0 then (
|
||||
if remove then
|
||||
Str.global_replace rex "\\1\\3" grub_cmdline
|
||||
else
|
||||
Str.global_replace rex "\\1console=ttyS0\\3" grub_cmdline
|
||||
)
|
||||
else grub_cmdline in
|
||||
g#aug_set grub_cmdline_expr grub_cmdline;
|
||||
g#aug_save ();
|
||||
|
||||
ignore (g#command [| "grub2-mkconfig"; "-o"; config_file |])
|
||||
with
|
||||
G.Error msg ->
|
||||
eprintf (f_"%s: warning: could not update grub2 console: %s (ignored)\n")
|
||||
prog msg
|
||||
)
|
||||
|
||||
method configure_console () = self#update_console ~remove:false
|
||||
method remove_console () = self#update_console ~remove:true
|
||||
end
|
||||
|
||||
let rec grub2 verbose (g : Guestfs.guestfs) inspect =
|
||||
(* Look for a grub2 configuration file. *)
|
||||
let config_file = "/boot/grub2/grub.cfg" in
|
||||
if not (g#is_file ~followsymlinks:true config_file) then (
|
||||
let msg =
|
||||
sprintf (f_"no grub2 configuration file was found (expecting %s)")
|
||||
config_file in
|
||||
failwith msg
|
||||
);
|
||||
|
||||
(* Check for EFI and convert if found. *)
|
||||
(try
|
||||
let dev = check_efi g in
|
||||
grub2_convert_from_efi verbose g inspect dev
|
||||
with Not_found -> ()
|
||||
);
|
||||
|
||||
new grub2 verbose g inspect config_file
|
||||
|
||||
(* For grub2:
|
||||
* - Turn the EFI partition into a BIOS Boot Partition
|
||||
* - Remove the former EFI partition from fstab
|
||||
* - Install the non-EFI version of grub
|
||||
* - Install grub2 in the BIOS Boot Partition
|
||||
* - Regenerate grub.cfg
|
||||
*)
|
||||
and grub2_convert_from_efi verbose g inspect dev =
|
||||
(* EFI systems boot using grub2-efi, and probably don't have the
|
||||
* base grub2 package installed.
|
||||
*)
|
||||
Convert_linux_common.install verbose g inspect ["grub2"];
|
||||
|
||||
(* Relabel the EFI boot partition as a BIOS boot partition. *)
|
||||
g#part_set_gpt_type dev 1 "21686148-6449-6E6F-744E-656564454649";
|
||||
|
||||
(* Delete the fstab entry for the EFI boot partition. *)
|
||||
let nodes = g#aug_match "/files/etc/fstab/*[file = '/boot/efi']" in
|
||||
let nodes = Array.to_list nodes in
|
||||
List.iter (fun node -> ignore (g#aug_rm node)) nodes;
|
||||
g#aug_save ();
|
||||
|
||||
(* Install grub2 in the BIOS boot partition. This overwrites the
|
||||
* previous contents of the EFI boot partition.
|
||||
*)
|
||||
ignore (g#command [| "grub2-install"; dev |]);
|
||||
|
||||
(* Re-generate the grub2 config, and put it in the correct place *)
|
||||
ignore (g#command [| "grub2-mkconfig"; "-o"; "/boot/grub2/grub.cfg" |])
|
||||
43
v2v/convert_linux_grub.mli
Normal file
43
v2v/convert_linux_grub.mli
Normal file
@@ -0,0 +1,43 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(** Common code handling grub1 (grub-legacy) and grub2 operations. *)
|
||||
|
||||
class type virtual grub = object
|
||||
method virtual list_kernels : unit -> string list
|
||||
(** Return a list of kernels from the grub configuration. The
|
||||
returned list is a list of filenames. *)
|
||||
method virtual configure_console : unit -> unit
|
||||
(** Reconfigure the grub console. *)
|
||||
method virtual remove_console : unit -> unit
|
||||
(** Remove the grub console configuration. *)
|
||||
end
|
||||
|
||||
val grub1 : bool -> Guestfs.guestfs -> Types.inspect -> grub
|
||||
(** Detect if grub1/grub-legacy is used by this guest and return a
|
||||
grub object if so.
|
||||
|
||||
This raises [Failure] if grub1 is not used by this guest or some
|
||||
other problem happens. *)
|
||||
|
||||
val grub2 : bool -> Guestfs.guestfs -> Types.inspect -> grub
|
||||
(** Detect if grub2 is used by this guest and return a grub object
|
||||
if so.
|
||||
|
||||
This raises [Failure] if grub2 is not used by this guest or some
|
||||
other problem happens. *)
|
||||
22
v2v/convert_windows.ml
Normal file
22
v2v/convert_windows.ml
Normal file
@@ -0,0 +1,22 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(* Convert Windows guests. *)
|
||||
|
||||
let convert verbose g inspect =
|
||||
assert false
|
||||
19
v2v/convert_windows.mli
Normal file
19
v2v/convert_windows.mli
Normal file
@@ -0,0 +1,19 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
val convert : bool -> Guestfs.guestfs -> Types.inspect -> Types.guestcaps
|
||||
22
v2v/link.sh.in
Normal file
22
v2v/link.sh.in
Normal file
@@ -0,0 +1,22 @@
|
||||
# libguestfs Makefile.am
|
||||
# @configure_input@
|
||||
# (C) Copyright 2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
# Hack automake to link binary properly. There is no other way to add
|
||||
# the -cclib parameter to the end of the command line.
|
||||
|
||||
exec "$@" -linkpkg -cclib '-lutils -lncurses @LIBXML2_LIBS@ -lgnu'
|
||||
149
v2v/source_libvirt.ml
Normal file
149
v2v/source_libvirt.ml
Normal file
@@ -0,0 +1,149 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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 Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Types
|
||||
open Utils
|
||||
|
||||
let create_xml xml =
|
||||
let doc = Xml.parse_memory xml in
|
||||
let xpathctx = Xml.xpath_new_context doc in
|
||||
|
||||
let xpath_to_string expr default =
|
||||
let obj = Xml.xpath_eval_expression xpathctx expr in
|
||||
if Xml.xpathobj_nr_nodes obj < 1 then default
|
||||
else (
|
||||
let node = Xml.xpathobj_node doc obj 0 in
|
||||
Xml.node_as_string node
|
||||
) in
|
||||
let xpath_to_int expr default =
|
||||
let obj = Xml.xpath_eval_expression xpathctx expr in
|
||||
if Xml.xpathobj_nr_nodes obj < 1 then default
|
||||
else (
|
||||
let node = Xml.xpathobj_node doc obj 0 in
|
||||
let str = Xml.node_as_string node in
|
||||
try int_of_string str
|
||||
with Failure "int_of_string" ->
|
||||
error (f_"expecting XML expression to return an integer (expression: %s)")
|
||||
expr
|
||||
) in
|
||||
|
||||
let dom_type = xpath_to_string "/domain/@type" "" in
|
||||
let name = xpath_to_string "/domain/name/text()" "" in
|
||||
let memory = xpath_to_int "/domain/memory/text()" 0 in
|
||||
let memory = Int64.of_int memory *^ 1024L in
|
||||
let vcpu = xpath_to_int "/domain/vcpu/text()" 0 in
|
||||
let arch = xpath_to_string "/domain/os/type/@arch" "" in
|
||||
|
||||
let features =
|
||||
let features = ref [] in
|
||||
let obj = Xml.xpath_eval_expression xpathctx "/domain/features/*" in
|
||||
let nr_nodes = Xml.xpathobj_nr_nodes obj in
|
||||
for i = 0 to nr_nodes-1 do
|
||||
let node = Xml.xpathobj_node doc obj i in
|
||||
features := Xml.node_name node :: !features
|
||||
done;
|
||||
!features in
|
||||
|
||||
(* Non-removable disk devices. *)
|
||||
let disks =
|
||||
let disks = ref [] in
|
||||
let obj =
|
||||
Xml.xpath_eval_expression xpathctx
|
||||
"/domain/devices/disk[@device='disk']" in
|
||||
let nr_nodes = Xml.xpathobj_nr_nodes obj in
|
||||
if nr_nodes < 1 then
|
||||
error (f_"this guest has no non-removable disks");
|
||||
for i = 0 to nr_nodes-1 do
|
||||
let node = Xml.xpathobj_node doc obj i in
|
||||
Xml.xpathctx_set_current_context xpathctx node;
|
||||
|
||||
let format =
|
||||
let format = xpath_to_string "driver/@type" "" in
|
||||
if format <> "" then Some format else None in
|
||||
|
||||
(* The <disk type='...'> attribute may be 'block', 'file' or
|
||||
* 'network'. We ignore any other types.
|
||||
*)
|
||||
match xpath_to_string "@type" "" with
|
||||
| "block" ->
|
||||
let path = xpath_to_string "source/@dev" "" in
|
||||
if path <> "" then disks := (path, format) :: !disks
|
||||
| "file" ->
|
||||
let path = xpath_to_string "source/@file" "" in
|
||||
if path <> "" then disks := (path, format) :: !disks
|
||||
| "network" ->
|
||||
(* We only handle <source protocol="nbd"> here, and that is
|
||||
* intended only for virt-p2v. Any other network disk is
|
||||
* currently ignored.
|
||||
*)
|
||||
(match xpath_to_string "source/@protocol" "" with
|
||||
| "nbd" ->
|
||||
let host = xpath_to_string "source/host/@name" "" in
|
||||
let port = xpath_to_int "source/host/@port" 0 in
|
||||
if host <> "" && port > 0 then (
|
||||
(* Generate a qemu nbd URL.
|
||||
* XXX Quoting, although it's not needed for virt-p2v.
|
||||
*)
|
||||
let path = sprintf "nbd:%s:%d" host port in
|
||||
disks := (path, format) :: !disks
|
||||
)
|
||||
| "" -> ()
|
||||
| protocol ->
|
||||
eprintf (f_"%s: warning: network <disk> with <source protocol='%s'> was ignored")
|
||||
prog protocol
|
||||
)
|
||||
| disk_type ->
|
||||
eprintf (f_"%s: warning: <disk type='%s'> was ignored") prog disk_type
|
||||
done;
|
||||
List.rev !disks in
|
||||
|
||||
(* XXX Much more metadata needs to be collected here:
|
||||
* - graphics
|
||||
* - cdroms
|
||||
* - floppies
|
||||
* - network interfaces
|
||||
* See: lib/Sys/VirtConvert/Connection/LibVirt.pm
|
||||
*)
|
||||
|
||||
{
|
||||
s_dom_type = dom_type;
|
||||
s_name = name;
|
||||
s_memory = memory;
|
||||
s_vcpu = vcpu;
|
||||
s_arch = arch;
|
||||
s_features = features;
|
||||
s_disks = disks;
|
||||
}
|
||||
|
||||
let create_from_xml file =
|
||||
let xml = read_whole_file file in
|
||||
create_xml xml
|
||||
|
||||
let create libvirt_uri guest =
|
||||
let cmd =
|
||||
match libvirt_uri with
|
||||
| None -> sprintf "virsh dumpxml %s" (quote guest)
|
||||
| Some uri -> sprintf "virsh -c %s dumpxml %s" (quote uri) (quote guest) in
|
||||
let lines = external_command ~prog cmd in
|
||||
let xml = String.concat "\n" lines in
|
||||
create_xml xml
|
||||
27
v2v/source_libvirt.mli
Normal file
27
v2v/source_libvirt.mli
Normal file
@@ -0,0 +1,27 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(** [-i libvirt] and [-i libvirtxml] sources. *)
|
||||
|
||||
val create : string option -> string -> Types.source
|
||||
(** [create libvirt_uri guest] reads the source metadata from the
|
||||
named libvirt guest. *)
|
||||
|
||||
val create_from_xml : string -> Types.source
|
||||
(** [create_from_xml filename] reads the source metadata from the
|
||||
libvirt XML file. *)
|
||||
86
v2v/target_local.ml
Normal file
86
v2v/target_local.ml
Normal file
@@ -0,0 +1,86 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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 Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Types
|
||||
open Utils
|
||||
|
||||
let initialize dir overlays =
|
||||
List.map (
|
||||
fun ov ->
|
||||
let target_file = dir // "disk-" ^ ov.ov_sd in
|
||||
{ ov with ov_target_file = target_file; ov_target_file_tmp = target_file }
|
||||
) overlays
|
||||
|
||||
let create_metadata dir source overlays guestcaps =
|
||||
let name = source.s_name in
|
||||
let file = dir // name ^ ".xml" in
|
||||
|
||||
let chan = open_out file in
|
||||
let p fs = fprintf chan fs in
|
||||
|
||||
p "<domain type='%s'>\n" "kvm"; (* Always assume target is kvm? *)
|
||||
p " <name>%s</name>\n" name;
|
||||
let memory_k = source.s_memory /^ 1024L in
|
||||
p " <memory unit='KiB'>%Ld</memory>\n" memory_k;
|
||||
p " <currentMemory unit='KiB'>%Ld</currentMemory>\n" memory_k;
|
||||
p " <vcpu>%d</vcpu>\n" source.s_vcpu;
|
||||
p " <os>\n";
|
||||
p " <type arch='%s'>hvm</type>\n" source.s_arch;
|
||||
p " </os>\n";
|
||||
p " <features>\n";
|
||||
List.iter (p " <%s/>\n") source.s_features;
|
||||
p " </features>\n";
|
||||
|
||||
p " <on_poweroff>destroy</on_poweroff>\n";
|
||||
p " <on_reboot>restart</on_reboot>\n";
|
||||
p " <on_crash>restart</on_crash>\n";
|
||||
p " <devices>\n";
|
||||
|
||||
let block_prefix =
|
||||
if guestcaps.gcaps_block_bus = "virtio" then "vd" else "hd" in
|
||||
iteri (
|
||||
fun i ov ->
|
||||
p " <disk type='file' device='disk'>\n";
|
||||
p " <driver name='qemu' type='%s' cache='none'/>\n"
|
||||
ov.ov_target_format;
|
||||
p " <source file='%s'/>\n" (xml_quote_attr ov.ov_target_file);
|
||||
p " <target dev='%s%s' bus='%s'/>\n"
|
||||
block_prefix (drive_name i) guestcaps.gcaps_block_bus;
|
||||
p " </disk>\n";
|
||||
) overlays;
|
||||
|
||||
p " <input type='tablet' bus='usb'/>\n";
|
||||
p " <input type='mouse' bus='ps2'/>\n";
|
||||
p " <console type='pty'/>\n";
|
||||
|
||||
(* XXX Missing here from old virt-v2v:
|
||||
<video/>
|
||||
<graphics/>
|
||||
cdroms and floppies
|
||||
network interfaces
|
||||
See: lib/Sys/VirtConvert/Connection/LibVirtTarget.pm
|
||||
*)
|
||||
|
||||
p "</domain>\n";
|
||||
|
||||
close_out chan
|
||||
21
v2v/target_local.mli
Normal file
21
v2v/target_local.mli
Normal file
@@ -0,0 +1,21 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
val initialize : string -> Types.overlay list -> Types.overlay list
|
||||
|
||||
val create_metadata : string -> Types.source -> Types.overlay list -> Types.guestcaps -> unit
|
||||
84
v2v/types.ml
Normal file
84
v2v/types.ml
Normal file
@@ -0,0 +1,84 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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
|
||||
|
||||
(* Types. See types.mli for documentation. *)
|
||||
|
||||
type input =
|
||||
| InputLibvirt of string option * string
|
||||
| InputLibvirtXML of string
|
||||
|
||||
type output =
|
||||
| OutputLibvirt of string option
|
||||
| OutputLocal of string
|
||||
| OutputRHEV of string
|
||||
|
||||
type source = {
|
||||
s_dom_type : string;
|
||||
s_name : string;
|
||||
s_memory : int64;
|
||||
s_vcpu : int;
|
||||
s_arch : string;
|
||||
s_features : string list;
|
||||
s_disks : source_disk list;
|
||||
}
|
||||
and source_disk = string * string option
|
||||
|
||||
type overlay = {
|
||||
ov_overlay : string;
|
||||
ov_target_file : string;
|
||||
ov_target_file_tmp : string;
|
||||
ov_target_format : string;
|
||||
ov_sd : string;
|
||||
ov_virtual_size : int64;
|
||||
ov_preallocation : string option;
|
||||
ov_source_file : string;
|
||||
ov_source_format : string option;
|
||||
}
|
||||
|
||||
let string_of_overlay ov =
|
||||
sprintf "\
|
||||
ov_overlay = %s
|
||||
ov_target_file = %s
|
||||
ov_target_file_tmp = %s
|
||||
ov_target_format = %s
|
||||
ov_sd = %s
|
||||
ov_virtual_size = %Ld
|
||||
ov_preallocation = %s
|
||||
ov_source_file = %s
|
||||
ov_source_format = %s
|
||||
"
|
||||
ov.ov_overlay
|
||||
ov.ov_target_file ov.ov_target_file_tmp
|
||||
ov.ov_target_format
|
||||
ov.ov_sd
|
||||
ov.ov_virtual_size
|
||||
(match ov.ov_preallocation with None -> "None" | Some s -> s)
|
||||
ov.ov_source_file
|
||||
(match ov.ov_source_format with None -> "None" | Some s -> s)
|
||||
|
||||
type inspect = {
|
||||
i_root : string;
|
||||
i_apps : Guestfs.application2 list;
|
||||
}
|
||||
|
||||
type guestcaps = {
|
||||
gcaps_block_bus : string;
|
||||
gcaps_net_bus : string;
|
||||
}
|
||||
77
v2v/types.mli
Normal file
77
v2v/types.mli
Normal file
@@ -0,0 +1,77 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(** Types. *)
|
||||
|
||||
type input =
|
||||
| InputLibvirt of string option * string (* -i libvirt: -ic + guest name *)
|
||||
| InputLibvirtXML of string (* -i libvirtxml: XML file name *)
|
||||
(** The input arguments as specified on the command line. *)
|
||||
|
||||
type output =
|
||||
| OutputLibvirt of string option (* -o libvirt: -oc *)
|
||||
| OutputLocal of string (* -o local: directory *)
|
||||
| OutputRHEV of string (* -o rhev: output storage *)
|
||||
(** The output arguments as specified on the command line. *)
|
||||
|
||||
type source = {
|
||||
s_dom_type : string; (** Source domain type, eg "kvm" *)
|
||||
s_name : string; (** Guest name. *)
|
||||
s_memory : int64; (** Memory size (bytes). *)
|
||||
s_vcpu : int; (** Number of CPUs. *)
|
||||
s_arch : string; (** Architecture. *)
|
||||
s_features : string list; (** Machine features. *)
|
||||
s_disks : source_disk list; (** Disk images. *)
|
||||
}
|
||||
(** The source: metadata, disk images. *)
|
||||
|
||||
and source_disk = string * string option
|
||||
(** A source file is a qemu URI and a format. *)
|
||||
|
||||
type overlay = {
|
||||
ov_overlay : string; (** Local overlay file (qcow2 format). *)
|
||||
ov_target_file : string; (** Destination file (real). *)
|
||||
ov_target_file_tmp : string; (** Destination file (temporary). *)
|
||||
ov_target_format : string; (** Destination format (eg. -of option). *)
|
||||
ov_sd : string; (** sdX libguestfs name of disk. *)
|
||||
ov_virtual_size : int64; (** Virtual disk size in bytes. *)
|
||||
ov_preallocation : string option; (** ?preallocation option. *)
|
||||
|
||||
(* Note: the next two fields are for information only and must not
|
||||
* be opened/copied/etc.
|
||||
*)
|
||||
ov_source_file : string; (** qemu URI for source file. *)
|
||||
ov_source_format : string option; (** Source file format, if known. *)
|
||||
}
|
||||
(** Disk overlays and destination disks. *)
|
||||
|
||||
val string_of_overlay : overlay -> string
|
||||
|
||||
type inspect = {
|
||||
i_root : string; (** Root device. *)
|
||||
i_apps : Guestfs.application2 list; (** Packages installed. *)
|
||||
}
|
||||
(** Inspection information. Only the applications list is stored here
|
||||
as that is the only one which is slow/inconvenient to fetch. *)
|
||||
|
||||
type guestcaps = {
|
||||
gcaps_block_bus : string; (** "virtio", "ide", possibly others *)
|
||||
gcaps_net_bus : string; (** "virtio", "e1000", possibly others *)
|
||||
(* XXX acpi, display *)
|
||||
}
|
||||
(** Guest capabilities after conversion. eg. Was virtio found or installed? *)
|
||||
43
v2v/utils-c.c
Normal file
43
v2v/utils-c.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/* virt-v2v
|
||||
* Copyright (C) 2009-2014 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 <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <caml/alloc.h>
|
||||
#include <caml/memory.h>
|
||||
#include <caml/mlvalues.h>
|
||||
|
||||
#include "guestfs.h"
|
||||
#include "guestfs-internal-frontend.h"
|
||||
|
||||
value
|
||||
v2v_utils_drive_name (value indexv)
|
||||
{
|
||||
CAMLparam1 (indexv);
|
||||
CAMLlocal1 (namev);
|
||||
char name[64];
|
||||
|
||||
guestfs___drive_name (Int_val (indexv), name);
|
||||
namev = caml_copy_string (name);
|
||||
|
||||
CAMLreturn (namev);
|
||||
}
|
||||
44
v2v/utils.ml
Normal file
44
v2v/utils.ml
Normal file
@@ -0,0 +1,44 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(* Utilities used in virt-v2v only. *)
|
||||
|
||||
open Printf
|
||||
|
||||
open Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Types
|
||||
|
||||
let prog = Filename.basename Sys.executable_name
|
||||
let error ?exit_code fs = error ~prog ?exit_code fs
|
||||
|
||||
let quote = Filename.quote
|
||||
|
||||
(* Quote XML <element attr='...'> content. Note you must use single
|
||||
* quotes around the attribute.
|
||||
*)
|
||||
let xml_quote_attr str =
|
||||
let str = Common_utils.replace_str str "&" "&" in
|
||||
let str = Common_utils.replace_str str "'" "'" in
|
||||
let str = Common_utils.replace_str str "<" "<" in
|
||||
let str = Common_utils.replace_str str ">" ">" in
|
||||
str
|
||||
|
||||
external drive_name : int -> string = "v2v_utils_drive_name"
|
||||
|
||||
353
v2v/v2v.ml
Normal file
353
v2v/v2v.ml
Normal file
@@ -0,0 +1,353 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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 Unix
|
||||
open Printf
|
||||
|
||||
open Common_gettext.Gettext
|
||||
|
||||
module G = Guestfs
|
||||
|
||||
open Common_utils
|
||||
open Types
|
||||
open Utils
|
||||
|
||||
let () = Random.self_init ()
|
||||
|
||||
let rec main () =
|
||||
(* Handle the command line. *)
|
||||
let input, output,
|
||||
debug_gc, output_alloc, output_format, output_name,
|
||||
quiet, root_choice, trace, verbose =
|
||||
Cmdline.parse_cmdline () in
|
||||
|
||||
let msg fs = make_message_function ~quiet fs in
|
||||
|
||||
let source =
|
||||
match input with
|
||||
| InputLibvirt (libvirt_uri, guest) ->
|
||||
Source_libvirt.create libvirt_uri guest
|
||||
| InputLibvirtXML filename ->
|
||||
Source_libvirt.create_from_xml filename in
|
||||
|
||||
(* Create a qcow2 v3 overlay to protect the source image(s). There
|
||||
* is a specific reason to use the newer qcow2 variant: Because the
|
||||
* L2 table can store zero clusters efficiently, and because
|
||||
* discarded blocks are stored as zero clusters, this should allow us
|
||||
* to fstrim/blkdiscard and avoid copying significant parts of the
|
||||
* data over the wire.
|
||||
*)
|
||||
msg (f_"Creating an overlay to protect the source from being modified");
|
||||
let overlays =
|
||||
List.map (
|
||||
fun (qemu_uri, format) ->
|
||||
let overlay = Filename.temp_file "v2vovl" ".qcow2" in
|
||||
unlink_on_exit overlay;
|
||||
|
||||
let options =
|
||||
"compat=1.1,lazy_refcounts=on" ^
|
||||
(match format with None -> ""
|
||||
| Some fmt -> ",backing_fmt=" ^ fmt) in
|
||||
let cmd =
|
||||
sprintf "qemu-img create -q -f qcow2 -b %s -o %s %s"
|
||||
(quote qemu_uri) (quote options) overlay in
|
||||
if Sys.command cmd <> 0 then
|
||||
error (f_"qemu-img command failed, see earlier errors");
|
||||
overlay, qemu_uri, format
|
||||
) source.s_disks in
|
||||
|
||||
(* Open the guestfs handle. *)
|
||||
msg (f_"Opening the overlay");
|
||||
let g = new G.guestfs () in
|
||||
g#set_trace trace;
|
||||
g#set_verbose verbose;
|
||||
g#set_network true;
|
||||
List.iter (
|
||||
fun (overlay, _, _) ->
|
||||
g#add_drive_opts overlay
|
||||
~format:"qcow2" ~cachemode:"unsafe" ~discard:"besteffort"
|
||||
) overlays;
|
||||
|
||||
g#launch ();
|
||||
|
||||
(* Work out where we will write the final output. Do this early
|
||||
* just so we can display errors to the user before doing too much
|
||||
* work.
|
||||
*)
|
||||
let overlays =
|
||||
initialize_target g output output_alloc output_format overlays in
|
||||
|
||||
(* Inspection - this also mounts up the filesystems. *)
|
||||
msg (f_"Inspecting the overlay");
|
||||
let inspect = inspect_source g root_choice in
|
||||
|
||||
(* Conversion. *)
|
||||
let guestcaps =
|
||||
let root = inspect.i_root in
|
||||
|
||||
(match g#inspect_get_product_name root with
|
||||
| "unknown" ->
|
||||
msg (f_"Converting the guest to run on KVM")
|
||||
| prod ->
|
||||
msg (f_"Converting %s to run on KVM") prod
|
||||
);
|
||||
|
||||
match g#inspect_get_type root with
|
||||
| "linux" ->
|
||||
(match g#inspect_get_distro root with
|
||||
| "fedora"
|
||||
| "rhel" | "centos" | "scientificlinux" | "redhat-based"
|
||||
| "sles" | "suse-based" | "opensuse" ->
|
||||
|
||||
(* RHEV doesn't support serial console so remove any on conversion. *)
|
||||
let keep_serial_console =
|
||||
match output with
|
||||
| OutputRHEV _ -> Some false
|
||||
| OutputLibvirt _ | OutputLocal _ -> None in
|
||||
|
||||
Convert_linux_enterprise.convert ?keep_serial_console
|
||||
verbose g inspect source
|
||||
|
||||
| distro ->
|
||||
error (f_"virt-v2v is unable to convert this guest type (linux/distro=%s)") distro
|
||||
);
|
||||
|
||||
| "windows" -> Convert_windows.convert verbose g inspect
|
||||
|
||||
| typ ->
|
||||
error (f_"virt-v2v is unable to convert this guest type (type=%s)") typ in
|
||||
|
||||
(* Trim the filesystems to reduce transfer size. *)
|
||||
msg (f_"Trimming filesystems to reduce amount of data to copy");
|
||||
let () =
|
||||
let mps = g#mountpoints () in
|
||||
List.iter (
|
||||
fun (_, mp) ->
|
||||
try g#fstrim mp
|
||||
with G.Error msg -> eprintf "%s: %s (ignored)\n" mp msg
|
||||
) mps in
|
||||
|
||||
msg (f_"Closing the overlay");
|
||||
g#umount_all ();
|
||||
g#shutdown ();
|
||||
g#close ();
|
||||
|
||||
(* Copy the source to the output. *)
|
||||
let delete_target_on_exit = ref true in
|
||||
at_exit (fun () ->
|
||||
if !delete_target_on_exit then (
|
||||
List.iter (
|
||||
fun ov -> try Unix.unlink ov.ov_target_file_tmp with _ -> ()
|
||||
) overlays
|
||||
)
|
||||
);
|
||||
let nr_overlays = List.length overlays in
|
||||
iteri (
|
||||
fun i ov ->
|
||||
msg (f_"Copying disk %d/%d to %s (%s)")
|
||||
(i+1) nr_overlays ov.ov_target_file ov.ov_target_format;
|
||||
if verbose then printf "%s\n%!" (string_of_overlay ov);
|
||||
|
||||
(* It turns out that libguestfs's disk creation code is
|
||||
* considerably more flexible and easier to use than qemu-img, so
|
||||
* create the disk explicitly using libguestfs then pass the
|
||||
* 'qemu-img convert -n' option so qemu reuses the disk.
|
||||
*)
|
||||
let preallocation = ov.ov_preallocation in
|
||||
let compat =
|
||||
match ov.ov_target_format with "qcow2" -> Some "1.1" | _ -> None in
|
||||
(new G.guestfs ())#disk_create ov.ov_target_file_tmp
|
||||
ov.ov_target_format ov.ov_virtual_size ?preallocation ?compat;
|
||||
|
||||
let cmd =
|
||||
sprintf "qemu-img convert -n -f qcow2 -O %s %s %s"
|
||||
(quote ov.ov_target_format) (quote ov.ov_overlay)
|
||||
(quote ov.ov_target_file_tmp) in
|
||||
if verbose then printf "%s\n%!" cmd;
|
||||
if Sys.command cmd <> 0 then
|
||||
error (f_"qemu-img command failed, see earlier errors");
|
||||
) overlays;
|
||||
|
||||
(* Create output metadata. *)
|
||||
msg (f_"Creating output metadata");
|
||||
let () =
|
||||
(* Are we going to rename the guest? *)
|
||||
let renamed_source =
|
||||
match output_name with
|
||||
| None -> source
|
||||
| Some name -> { source with s_name = name } in
|
||||
match output with
|
||||
| OutputLibvirt oc -> assert false
|
||||
| OutputLocal dir ->
|
||||
Target_local.create_metadata dir renamed_source overlays guestcaps
|
||||
| OutputRHEV os -> assert false in
|
||||
|
||||
(* If we wrote to a temporary file, rename to the real file. *)
|
||||
List.iter (
|
||||
fun ov ->
|
||||
if ov.ov_target_file_tmp <> ov.ov_target_file then
|
||||
rename ov.ov_target_file_tmp ov.ov_target_file
|
||||
) overlays;
|
||||
|
||||
delete_target_on_exit := false;
|
||||
|
||||
msg (f_"Finishing off");
|
||||
|
||||
if debug_gc then
|
||||
Gc.compact ()
|
||||
|
||||
and initialize_target g output output_alloc output_format overlays =
|
||||
let overlays =
|
||||
mapi (
|
||||
fun i (overlay, qemu_uri, backing_format) ->
|
||||
(* Grab the virtual size of each disk. *)
|
||||
let sd = "sd" ^ drive_name i in
|
||||
let dev = "/dev/" ^ sd in
|
||||
let vsize = g#blockdev_getsize64 dev in
|
||||
|
||||
(* What output format should we use? *)
|
||||
let format =
|
||||
match output_format, backing_format with
|
||||
| Some format, _ -> format (* -of overrides everything *)
|
||||
| None, Some format -> format (* same as backing format *)
|
||||
| None, None ->
|
||||
error (f_"disk %s (%s) has no defined format, you have to either define the original format in the source metadata, or use the '-of' option to force the output format") sd qemu_uri in
|
||||
|
||||
(* What output preallocation mode should we use? *)
|
||||
let preallocation =
|
||||
match format, output_alloc with
|
||||
| "raw", `Sparse -> Some "sparse"
|
||||
| "raw", `Preallocated -> Some "full"
|
||||
| "qcow2", `Sparse -> Some "off" (* ? *)
|
||||
| "qcow2", `Preallocated -> Some "metadata"
|
||||
| _ -> None (* ignore -oa flag for other formats *) in
|
||||
|
||||
{ ov_overlay = overlay;
|
||||
ov_target_file = ""; ov_target_file_tmp = "";
|
||||
ov_target_format = format;
|
||||
ov_sd = sd; ov_virtual_size = vsize; ov_preallocation = preallocation;
|
||||
ov_source_file = qemu_uri; ov_source_format = backing_format; }
|
||||
) overlays in
|
||||
let overlays =
|
||||
match output with
|
||||
| OutputLibvirt oc -> assert false
|
||||
| OutputLocal dir -> Target_local.initialize dir overlays
|
||||
| OutputRHEV os -> assert false in
|
||||
overlays
|
||||
|
||||
and inspect_source g root_choice =
|
||||
let roots = g#inspect_os () in
|
||||
let roots = Array.to_list roots in
|
||||
|
||||
let root =
|
||||
match roots with
|
||||
| [] ->
|
||||
error (f_"no root device found in this operating system image.");
|
||||
| [root] -> root
|
||||
| roots ->
|
||||
match root_choice with
|
||||
| `Ask ->
|
||||
(* List out the roots and ask the user to choose. *)
|
||||
printf "\n***\n";
|
||||
printf (f_"dual- or multi-boot operating system detected. Choose the root filesystem\nthat contains the main operating system from the list below:\n");
|
||||
printf "\n";
|
||||
iteri (
|
||||
fun i root ->
|
||||
let prod = g#inspect_get_product_name root in
|
||||
match prod with
|
||||
| "unknown" -> printf " [%d] %s\n" i root
|
||||
| prod -> printf " [%d] %s (%s)\n" i root prod
|
||||
) roots;
|
||||
printf "\n";
|
||||
let i = ref 0 in
|
||||
let n = List.length roots in
|
||||
while !i < 1 || !i > n do
|
||||
printf (f_"Enter number between 1 and %d: ") n;
|
||||
(try i := int_of_string (read_line ())
|
||||
with
|
||||
| End_of_file -> error (f_"connection closed")
|
||||
| Failure "int_of_string" -> ()
|
||||
)
|
||||
done;
|
||||
List.nth roots (!i - 1)
|
||||
|
||||
| `Single ->
|
||||
error (f_"multi-boot operating systems are not supported by virt-v2v. Use the --root option to change how virt-v2v handles this.")
|
||||
|
||||
| `First ->
|
||||
List.hd roots
|
||||
|
||||
| `Dev dev ->
|
||||
if List.mem dev roots then dev
|
||||
else
|
||||
error (f_"root device %s not found. Roots found were: %s")
|
||||
dev (String.concat " " roots) in
|
||||
|
||||
(* Reject this OS if it doesn't look like an installed image. *)
|
||||
let () =
|
||||
let fmt = g#inspect_get_format root in
|
||||
if fmt <> "installed" then
|
||||
error (f_"libguestfs thinks this is not an installed operating system (it might be, for example, an installer disk or live CD). If this is wrong, it is probably a bug in libguestfs. root=%s fmt=%s") root fmt in
|
||||
|
||||
(* Mount up the filesystems. *)
|
||||
let mps = g#inspect_get_mountpoints root in
|
||||
let cmp (a,_) (b,_) = compare (String.length a) (String.length b) in
|
||||
let mps = List.sort cmp mps in
|
||||
List.iter (
|
||||
fun (mp, dev) ->
|
||||
try g#mount dev mp
|
||||
with G.Error msg -> eprintf "%s (ignored)\n" msg
|
||||
) mps;
|
||||
|
||||
(* Get list of applications/packages installed. *)
|
||||
let apps = g#inspect_list_applications2 root in
|
||||
let apps = Array.to_list apps in
|
||||
|
||||
{ i_root = root; i_apps = apps; }
|
||||
|
||||
let () =
|
||||
try main ()
|
||||
with
|
||||
| Unix.Unix_error (code, fname, "") -> (* from a syscall *)
|
||||
eprintf (f_"%s: error: %s: %s\n") prog fname (Unix.error_message code);
|
||||
exit 1
|
||||
| Unix.Unix_error (code, fname, param) -> (* from a syscall *)
|
||||
eprintf (f_"%s: error: %s: %s: %s\n") prog fname (Unix.error_message code)
|
||||
param;
|
||||
exit 1
|
||||
| Sys_error msg -> (* from a syscall *)
|
||||
eprintf (f_"%s: error: %s\n") prog msg;
|
||||
exit 1
|
||||
| G.Error msg -> (* from libguestfs *)
|
||||
eprintf (f_"%s: libguestfs error: %s\n") prog msg;
|
||||
exit 1
|
||||
| Failure msg -> (* from failwith/failwithf *)
|
||||
eprintf (f_"%s: failure: %s\n") prog msg;
|
||||
exit 1
|
||||
| Invalid_argument msg -> (* probably should never happen *)
|
||||
eprintf (f_"%s: internal error: invalid argument: %s\n") prog msg;
|
||||
exit 1
|
||||
| Assert_failure (file, line, char) -> (* should never happen *)
|
||||
eprintf (f_"%s: internal error: assertion failed at %s, line %d, char %d\n") prog file line char;
|
||||
exit 1
|
||||
| Not_found -> (* should never happen *)
|
||||
eprintf (f_"%s: internal error: Not_found exception was thrown\n") prog;
|
||||
exit 1
|
||||
| exn -> (* something not matched above *)
|
||||
eprintf (f_"%s: exception: %s\n") prog (Printexc.to_string exn);
|
||||
exit 1
|
||||
306
v2v/virt-v2v.pod
Normal file
306
v2v/virt-v2v.pod
Normal file
@@ -0,0 +1,306 @@
|
||||
=head1 NAME
|
||||
|
||||
virt-v2v - Convert a guest to use KVM
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
virt-v2v -ic esx://esx.example.com/ -os imported esx_guest
|
||||
|
||||
virt-v2v -ic esx://esx.example.com/ \
|
||||
-o rhev -os rhev.nfs:/export_domain --network rhevm esx_guest
|
||||
|
||||
virt-v2v -i libvirtxml -o local -os /tmp guest-domain.xml
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Virt-v2v converts guests from a foreign hypervisor to run on KVM,
|
||||
managed by libvirt or Red Hat Enterprise Virtualisation (RHEV) version
|
||||
2.2 or later. It can currently convert Red Hat Enterprise Linux and
|
||||
Windows guests running on Xen and VMware ESX.
|
||||
|
||||
There is also a companion front-end called "virt-p2v" which comes as an
|
||||
ISO or CD image that can be booted on physical machines.
|
||||
|
||||
This manual page documents the rewritten virt-v2v included in
|
||||
libguestfs E<ge> 1.28.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Display help.
|
||||
|
||||
=item B<--debug-gc>
|
||||
|
||||
Debug garbage collection and memory allocation. This is only useful
|
||||
when debugging memory problems in virt-v2v or the OCaml libguestfs
|
||||
bindings.
|
||||
|
||||
=item B<-i libvirt>
|
||||
|
||||
Set the input method to I<libvirt>. This is the default.
|
||||
|
||||
In this mode you have to specify a libvirt guest name on the command
|
||||
line. You may also specify a libvirt connection URI (see I<-ic>).
|
||||
|
||||
=item B<-i libvirtxml>
|
||||
|
||||
Set the input method to I<libvirtxml>.
|
||||
|
||||
In this mode you have to pass a libvirt XML file on the command line.
|
||||
This file is read in order to get metadata about the source guest
|
||||
(such as its name, amount of memory), and also to locate the input
|
||||
disks.
|
||||
|
||||
=item B<-ic> libvirtURI
|
||||
|
||||
Specify a libvirt connection URI to use when reading the guest. This
|
||||
is only used when S<I<-i libvirt>>.
|
||||
|
||||
Only local libvirt connections and ESX connections can be used.
|
||||
Remote libvirt connections will not work in general.
|
||||
|
||||
=item B<--machine-readable>
|
||||
|
||||
This option is used to make the output more machine friendly
|
||||
when being parsed by other programs. See
|
||||
L</MACHINE READABLE OUTPUT> below.
|
||||
|
||||
=item B<-o libvirt>
|
||||
|
||||
Set the output method to I<libvirt>. This is the default.
|
||||
|
||||
In this mode, the converted guest is created as a libvirt guest. You
|
||||
may also specify a libvirt connection URI (see I<-oc>).
|
||||
|
||||
=item B<-o local>
|
||||
|
||||
Set the output method to I<local>.
|
||||
|
||||
In this mode, the converted guest is written to a local directory
|
||||
specified by I<-os /dir> (the directory must exist). The converted
|
||||
guest's disks are written as:
|
||||
|
||||
/dir/disk-sda
|
||||
/dir/disk-sdb
|
||||
[etc]
|
||||
|
||||
and a libvirt XML file is created containing guest metadata
|
||||
(C</dir/name.xml>, where C<name> is the guest name).
|
||||
|
||||
=item B<-o rhev>
|
||||
|
||||
Set the output method to I<rhev>.
|
||||
|
||||
The converted guest is written to a RHEV Export Storage Domain. The
|
||||
I<-os> parameter must also be used to specify the location of the
|
||||
Export Storage Domain. Note this does not actually import the guest
|
||||
into RHEV. You have to do that manually later using the UI.
|
||||
|
||||
=item B<-oa sparse>
|
||||
|
||||
=item B<-oa preallocated>
|
||||
|
||||
Set the output file allocation mode. The default is C<sparse>.
|
||||
|
||||
=item B<-oc> libvirtURI
|
||||
|
||||
Specify a libvirt connection to use when writing the converted guest.
|
||||
This is only used when S<I<-o libvirt>>.
|
||||
|
||||
Only local libvirt connections can be used. Remote libvirt
|
||||
connections will not work.
|
||||
|
||||
=item B<-of> format
|
||||
|
||||
When converting the guest, convert the disks to the given format.
|
||||
|
||||
If not specified, then the input format is used.
|
||||
|
||||
=item B<-on> name
|
||||
|
||||
Rename the guest when converting it. If this option is not used then
|
||||
the output name is the same as the input name.
|
||||
|
||||
=item B<-os> storage
|
||||
|
||||
The location of the storage for the converted guest.
|
||||
|
||||
For I<-o libvirt>, this is a libvirt pool (see S<C<virsh pool-list>>).
|
||||
|
||||
For I<-o local>, this is a directory name. The directory must exist.
|
||||
|
||||
For I<-o rhev>, this is an NFS path of the form
|
||||
C<E<lt>hostE<gt>:E<lt>pathE<gt>>, eg:
|
||||
|
||||
rhev-storage.example.com:/rhev/export
|
||||
|
||||
The NFS export must be mountable and writable by the user and host
|
||||
running virt-v2v, since the virt-v2v program has to actually mount it
|
||||
when it runs.
|
||||
|
||||
=item B<-q>
|
||||
|
||||
=item B<--quiet>
|
||||
|
||||
This disables progress bars and other unnecessary output.
|
||||
|
||||
=item B<--root ask>
|
||||
|
||||
=item B<--root single>
|
||||
|
||||
=item B<--root first>
|
||||
|
||||
=item B<--root> /dev/sdX
|
||||
|
||||
=item B<--root> /dev/VG/LV
|
||||
|
||||
Choose the root filesystem to be converted.
|
||||
|
||||
In the case where the virtual machine is dual-boot or multi-boot, or
|
||||
where the VM has other filesystems that look like operating systems,
|
||||
this option can be used to select the root filesystem (a.k.a. C<C:>
|
||||
drive or C</>) of the operating system that is to be converted. The
|
||||
Windows Recovery Console, certain attached DVD drives, and bugs in
|
||||
libguestfs inspection heuristics, can make a guest look like a
|
||||
multi-boot operating system.
|
||||
|
||||
The default in virt-v2v E<le> 0.7.1 was S<I<--root single>>, which
|
||||
causes virt-v2v to die if a multi-boot operating system is found.
|
||||
|
||||
Since virt-v2v E<ge> 0.7.2 the default is now S<I<--root ask>>: If the
|
||||
VM is found to be multi-boot, then virt-v2v will stop and list the
|
||||
possible root filesystems and ask the user which to use. This
|
||||
requires that virt-v2v is run interactively.
|
||||
|
||||
S<I<--root first>> means to choose the first root device in the case
|
||||
of a multi-boot operating system. Since this is a heuristic, it may
|
||||
sometimes choose the wrong one.
|
||||
|
||||
You can also name a specific root device, eg. S<I<--root /dev/sda2>>
|
||||
would mean to use the second partition on the first hard drive. If
|
||||
the named root device does not exist or was not detected as a root
|
||||
device, then virt-v2v will fail.
|
||||
|
||||
Note that there is a bug in grub which prevents it from successfully
|
||||
booting a multiboot system if VirtIO is enabled. Grub is only able to
|
||||
boot an operating system from the first VirtIO disk. Specifically,
|
||||
C</boot> must be on the first VirtIO disk, and it cannot chainload an
|
||||
OS which is not in the first VirtIO disk.
|
||||
|
||||
=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.
|
||||
|
||||
=back
|
||||
|
||||
=head1 MACHINE READABLE OUTPUT
|
||||
|
||||
The I<--machine-readable> option can be used to make the output more
|
||||
machine friendly, which is useful when calling virt-v2v from
|
||||
other programs, GUIs etc.
|
||||
|
||||
There are two ways to use this option.
|
||||
|
||||
Firstly use the option on its own to query the capabilities of the
|
||||
virt-v2v binary. Typical output looks like this:
|
||||
|
||||
$ virt-v2v --machine-readable
|
||||
virt-v2v
|
||||
libguestfs-rewrite
|
||||
|
||||
A list of features is printed, one per line, and the program exits
|
||||
with status 0.
|
||||
|
||||
Secondly use the option in conjunction with other options to make the
|
||||
regular program output more machine friendly.
|
||||
|
||||
At the moment this means:
|
||||
|
||||
=over 4
|
||||
|
||||
=item 1.
|
||||
|
||||
Progress bar messages can be parsed from stdout by looking for this
|
||||
regular expression:
|
||||
|
||||
^[0-9]+/[0-9]+$
|
||||
|
||||
=item 2.
|
||||
|
||||
The calling program should treat messages sent to stdout (except for
|
||||
progress bar messages) as status messages. They can be logged and/or
|
||||
displayed to the user.
|
||||
|
||||
=item 3.
|
||||
|
||||
The calling program should treat messages sent to stderr as error
|
||||
messages. In addition, virt-v2v exits with a non-zero status
|
||||
code if there was a fatal error.
|
||||
|
||||
=back
|
||||
|
||||
Virt-v2v E<le> 0.9.1 did not support the I<--machine-readable>
|
||||
option at all. The option was added when virt-v2v was rewritten in 2014.
|
||||
|
||||
=head1 ENVIRONMENT VARIABLES
|
||||
|
||||
=over 4
|
||||
|
||||
=item TMPDIR
|
||||
|
||||
Location of the temporary directory used for the potentially large
|
||||
temporary overlay file.
|
||||
|
||||
You should ensure there is enough free space in the worst case for a
|
||||
full copy of the source disk (I<virtual> size), or else set C<$TMPDIR>
|
||||
to point to another directory that has enough space.
|
||||
|
||||
This defaults to C</tmp>.
|
||||
|
||||
Note that if C<$TMPDIR> is a tmpfs (eg. if C</tmp> is on tmpfs, or if
|
||||
you use C<TMPDIR=/dev/shm>), tmpfs defaults to a maximum size of
|
||||
I<half> of physical RAM. If virt-v2v exceeds this, it will hang.
|
||||
The solution is either to use a real disk, or to increase the maximum
|
||||
size of the tmpfs mountpoint, eg:
|
||||
|
||||
mount -o remount,size=10G /tmp
|
||||
|
||||
=back
|
||||
|
||||
For other environment variables, see L<guestfs(3)/ENVIRONMENT VARIABLES>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<virt-df(1)>,
|
||||
L<virt-filesystems(1)>,
|
||||
L<guestfs(3)>,
|
||||
L<guestfish(1)>,
|
||||
L<qemu-img(1)>,
|
||||
L<http://libguestfs.org/>.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
||||
|
||||
Matthew Booth
|
||||
|
||||
Mike Latimer
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (C) 2009-2014 Red Hat Inc.
|
||||
244
v2v/xml-c.c
Normal file
244
v2v/xml-c.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*/
|
||||
|
||||
/* Mini interface to libxml2 for parsing libvirt XML. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <caml/alloc.h>
|
||||
#include <caml/custom.h>
|
||||
#include <caml/fail.h>
|
||||
#include <caml/memory.h>
|
||||
#include <caml/mlvalues.h>
|
||||
|
||||
#include <libxml/xpath.h>
|
||||
|
||||
#include "guestfs.h"
|
||||
#include "guestfs-internal-frontend.h"
|
||||
|
||||
/* xmlDocPtr type */
|
||||
#define Doc_val(v) (*((xmlDocPtr *)Data_custom_val(v)))
|
||||
|
||||
static void
|
||||
doc_finalize (value docv)
|
||||
{
|
||||
xmlDocPtr doc = Doc_val (docv);
|
||||
|
||||
if (doc)
|
||||
xmlFreeDoc (doc);
|
||||
}
|
||||
|
||||
static struct custom_operations doc_custom_operations = {
|
||||
(char *) "doc_custom_operations",
|
||||
doc_finalize,
|
||||
custom_compare_default,
|
||||
custom_hash_default,
|
||||
custom_serialize_default,
|
||||
custom_deserialize_default
|
||||
};
|
||||
|
||||
/* xmlXPathContextPtr type */
|
||||
#define Xpathctx_val(v) (*((xmlXPathContextPtr *)Data_custom_val(v)))
|
||||
|
||||
static void
|
||||
xpathctx_finalize (value xpathctxv)
|
||||
{
|
||||
xmlXPathContextPtr xpathctx = Xpathctx_val (xpathctxv);
|
||||
|
||||
if (xpathctx)
|
||||
xmlXPathFreeContext (xpathctx);
|
||||
}
|
||||
|
||||
static struct custom_operations xpathctx_custom_operations = {
|
||||
(char *) "xpathctx_custom_operations",
|
||||
xpathctx_finalize,
|
||||
custom_compare_default,
|
||||
custom_hash_default,
|
||||
custom_serialize_default,
|
||||
custom_deserialize_default
|
||||
};
|
||||
|
||||
/* xmlXPathObjectPtr type */
|
||||
#define Xpathobj_val(v) (*((xmlXPathObjectPtr *)Data_custom_val(v)))
|
||||
|
||||
static void
|
||||
xpathobj_finalize (value xpathobjv)
|
||||
{
|
||||
xmlXPathObjectPtr xpathobj = Xpathobj_val (xpathobjv);
|
||||
|
||||
if (xpathobj)
|
||||
xmlXPathFreeObject (xpathobj);
|
||||
}
|
||||
|
||||
static struct custom_operations xpathobj_custom_operations = {
|
||||
(char *) "xpathobj_custom_operations",
|
||||
xpathobj_finalize,
|
||||
custom_compare_default,
|
||||
custom_hash_default,
|
||||
custom_serialize_default,
|
||||
custom_deserialize_default
|
||||
};
|
||||
|
||||
value
|
||||
v2v_xml_parse_memory (value xmlv)
|
||||
{
|
||||
CAMLparam1 (xmlv);
|
||||
CAMLlocal1 (docv);
|
||||
xmlDocPtr doc;
|
||||
|
||||
/* For security reasons, call xmlReadMemory (not xmlParseMemory) and
|
||||
* pass XML_PARSE_NONET. See commit 845daded5fddc70f.
|
||||
*/
|
||||
doc = xmlReadMemory (String_val (xmlv), caml_string_length (xmlv),
|
||||
NULL, NULL, XML_PARSE_NONET);
|
||||
if (doc == NULL)
|
||||
caml_invalid_argument ("parse_memory: unable to parse XML from libvirt");
|
||||
|
||||
docv = caml_alloc_custom (&doc_custom_operations, sizeof (xmlDocPtr), 0, 1);
|
||||
Doc_val (docv) = doc;
|
||||
|
||||
CAMLreturn (docv);
|
||||
}
|
||||
|
||||
value
|
||||
v2v_xml_xpath_new_context (value docv)
|
||||
{
|
||||
CAMLparam1 (docv);
|
||||
CAMLlocal1 (xpathctxv);
|
||||
xmlDocPtr doc;
|
||||
xmlXPathContextPtr xpathctx;
|
||||
|
||||
doc = Doc_val (docv);
|
||||
xpathctx = xmlXPathNewContext (doc);
|
||||
if (xpathctx == NULL)
|
||||
caml_invalid_argument ("xpath_new_context: unable to create xmlXPathNewContext");
|
||||
|
||||
xpathctxv = caml_alloc_custom (&xpathctx_custom_operations,
|
||||
sizeof (xmlXPathContextPtr), 0, 1);
|
||||
Xpathctx_val (xpathctxv) = xpathctx;
|
||||
|
||||
CAMLreturn (xpathctxv);
|
||||
}
|
||||
|
||||
value
|
||||
v2v_xml_xpath_eval_expression (value xpathctxv, value exprv)
|
||||
{
|
||||
CAMLparam2 (xpathctxv, exprv);
|
||||
CAMLlocal1 (xpathobjv);
|
||||
xmlXPathContextPtr xpathctx;
|
||||
xmlXPathObjectPtr xpathobj;
|
||||
|
||||
xpathctx = Xpathctx_val (xpathctxv);
|
||||
xpathobj = xmlXPathEvalExpression (BAD_CAST String_val (exprv), xpathctx);
|
||||
if (xpathobj == NULL)
|
||||
caml_invalid_argument ("xpath_eval_expression: unable to evaluate XPath expression");
|
||||
|
||||
xpathobjv = caml_alloc_custom (&xpathobj_custom_operations,
|
||||
sizeof (xmlXPathObjectPtr), 0, 1);
|
||||
Xpathobj_val (xpathobjv) = xpathobj;
|
||||
|
||||
CAMLreturn (xpathobjv);
|
||||
}
|
||||
|
||||
value
|
||||
v2v_xml_xpathobj_nr_nodes (value xpathobjv)
|
||||
{
|
||||
CAMLparam1 (xpathobjv);
|
||||
xmlXPathObjectPtr xpathobj = Xpathobj_val (xpathobjv);
|
||||
|
||||
CAMLreturn (Val_int (xpathobj->nodesetval->nodeNr));
|
||||
}
|
||||
|
||||
value
|
||||
v2v_xml_xpathobj_get_node_ptr (value xpathobjv, value iv)
|
||||
{
|
||||
CAMLparam2 (xpathobjv, iv);
|
||||
xmlXPathObjectPtr xpathobj = Xpathobj_val (xpathobjv);
|
||||
int i = Int_val (iv);
|
||||
|
||||
/* Because xmlNodePtrs are owned by the document, we don't want to
|
||||
* wrap this up with a finalizer, so just pass the pointer straight
|
||||
* back to OCaml as a value. OCaml will ignore it because it's
|
||||
* outside the heap, and just pass it back to us when needed. This
|
||||
* relies on the xmlDocPtr not being freed, but we pair the node
|
||||
* pointer with the doc in the OCaml layer so the GC will not free
|
||||
* one without freeing the other.
|
||||
*/
|
||||
CAMLreturn ((value) xpathobj->nodesetval->nodeTab[i]);
|
||||
}
|
||||
|
||||
value
|
||||
v2v_xml_xpathctx_set_node_ptr (value xpathctxv, value nodev)
|
||||
{
|
||||
CAMLparam2 (xpathctxv, nodev);
|
||||
xmlXPathContextPtr xpathctx = Xpathctx_val (xpathctxv);
|
||||
xmlNodePtr node = (xmlNodePtr) nodev;
|
||||
|
||||
xpathctx->node = node;
|
||||
|
||||
CAMLreturn (Val_unit);
|
||||
}
|
||||
|
||||
value
|
||||
v2v_xml_node_ptr_name (value nodev)
|
||||
{
|
||||
CAMLparam1 (nodev);
|
||||
xmlNodePtr node = (xmlNodePtr) nodev;
|
||||
|
||||
switch (node->type) {
|
||||
case XML_ATTRIBUTE_NODE:
|
||||
case XML_ELEMENT_NODE:
|
||||
CAMLreturn (caml_copy_string ((char *) node->name));
|
||||
|
||||
default:
|
||||
caml_invalid_argument ("node_name: don't know how to get the name of this node");
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
v2v_xml_node_ptr_as_string (value docv, value nodev)
|
||||
{
|
||||
CAMLparam2 (docv, nodev);
|
||||
xmlDocPtr doc = Doc_val (docv);
|
||||
xmlNodePtr node = (xmlNodePtr) nodev;
|
||||
CLEANUP_FREE char *str = NULL;
|
||||
|
||||
switch (node->type) {
|
||||
case XML_TEXT_NODE:
|
||||
case XML_COMMENT_NODE:
|
||||
case XML_CDATA_SECTION_NODE:
|
||||
case XML_PI_NODE:
|
||||
CAMLreturn (caml_copy_string ((char *) node->content));
|
||||
|
||||
case XML_ATTRIBUTE_NODE:
|
||||
case XML_ELEMENT_NODE:
|
||||
str = (char *) xmlNodeListGetString (doc, node->children, 1);
|
||||
|
||||
if (str == NULL)
|
||||
caml_invalid_argument ("node_as_string: xmlNodeListGetString cannot convert node to string");
|
||||
|
||||
CAMLreturn (caml_copy_string (str));
|
||||
|
||||
default:
|
||||
caml_invalid_argument ("node_as_string: don't know how to convert this node to a string");
|
||||
}
|
||||
}
|
||||
50
v2v/xml.ml
Normal file
50
v2v/xml.ml
Normal file
@@ -0,0 +1,50 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(* Mini interface to libxml2 for parsing libvirt XML. *)
|
||||
|
||||
type doc
|
||||
type node_ptr
|
||||
type xpathctx
|
||||
type xpathobj
|
||||
|
||||
(* Since node is owned by doc, we have to make that explicit to the
|
||||
* garbage collector.
|
||||
*)
|
||||
type node = doc * node_ptr
|
||||
|
||||
external parse_memory : string -> doc = "v2v_xml_parse_memory"
|
||||
external xpath_new_context : doc -> xpathctx = "v2v_xml_xpath_new_context"
|
||||
external xpath_eval_expression : xpathctx -> string -> xpathobj = "v2v_xml_xpath_eval_expression"
|
||||
|
||||
external xpathobj_nr_nodes : xpathobj -> int = "v2v_xml_xpathobj_nr_nodes"
|
||||
external xpathobj_get_node_ptr : xpathobj -> int -> node_ptr = "v2v_xml_xpathobj_get_node_ptr"
|
||||
let xpathobj_node doc xpathobj i =
|
||||
let n = xpathobj_get_node_ptr xpathobj i in
|
||||
(doc, n)
|
||||
|
||||
external xpathctx_set_node_ptr : xpathctx -> node_ptr -> unit = "v2v_xml_xpathctx_set_node_ptr"
|
||||
let xpathctx_set_current_context xpathctx (_, node) =
|
||||
xpathctx_set_node_ptr xpathctx node
|
||||
|
||||
external node_ptr_name : node_ptr -> string = "v2v_xml_node_ptr_name"
|
||||
let node_name (_, node) = node_ptr_name node
|
||||
|
||||
external node_ptr_as_string : doc -> node_ptr -> string = "v2v_xml_node_ptr_as_string"
|
||||
let node_as_string (doc, node) =
|
||||
node_ptr_as_string doc node
|
||||
57
v2v/xml.mli
Normal file
57
v2v/xml.mli
Normal file
@@ -0,0 +1,57 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2014 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.
|
||||
*)
|
||||
|
||||
(** Mini interface to libxml2 for parsing libvirt XML. *)
|
||||
|
||||
type doc (** xmlDocPtr *)
|
||||
type node (** xmlNodePtr *)
|
||||
type xpathctx (** xmlXPathContextPtr *)
|
||||
type xpathobj (** xmlXPathObjectPtr *)
|
||||
|
||||
val parse_memory : string -> doc
|
||||
(** xmlParseMemory (for security reasons it actually calls xmlReadMemory) *)
|
||||
val xpath_new_context : doc -> xpathctx
|
||||
(** xmlXPathNewContext *)
|
||||
val xpath_eval_expression : xpathctx -> string -> xpathobj
|
||||
(** xmlXPathEvalExpression *)
|
||||
|
||||
val xpathobj_nr_nodes : xpathobj -> int
|
||||
(** Get the number of nodes in the node set of the xmlXPathObjectPtr. *)
|
||||
val xpathobj_node : doc -> xpathobj -> int -> node
|
||||
(** Get the number of nodes in the node set of the xmlXPathObjectPtr. *)
|
||||
|
||||
val xpathctx_set_current_context : xpathctx -> node -> unit
|
||||
(** Set the current context of an xmlXPathContextPtr to the node.
|
||||
Basically the same as the following C code:
|
||||
|
||||
{v
|
||||
xpathctx->node = node
|
||||
v}
|
||||
|
||||
It means the next expression you evaluate within this context will
|
||||
start at this node, when evaluating relative paths
|
||||
(eg. [./@attr]).
|
||||
*)
|
||||
|
||||
val node_name : node -> string
|
||||
(** Get the name of the node. Note that only things like elements and
|
||||
attributes have names. Other types of nodes will return an
|
||||
error. *)
|
||||
|
||||
val node_as_string : node -> string
|
||||
(** Converter to turn a node into a string *)
|
||||
Reference in New Issue
Block a user