mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
v2v: Add a new tool virt-v2v-copy-to-local.
This allows certain guests which virt-v2v cannot access to be copied off the remote hypervisor and converted. Essentially this just automates the process of copying the guest's disks and adjusting the libvirt XML. This also adds a test of the new tool.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -562,6 +562,7 @@ Makefile.in
|
||||
/v2v/rhel-6.5.img
|
||||
/v2v/rhel-7.0.img
|
||||
/v2v/stamp-virt-v2v.pod
|
||||
/v2v/stamp-virt-v2v-copy-to-local.pod
|
||||
/v2v/test-harness/.depend
|
||||
/v2v/test-harness/META
|
||||
/v2v/test-harness/dllv2v_test_harness.so
|
||||
@@ -570,6 +571,8 @@ Makefile.in
|
||||
/v2v/v2v_unit_tests
|
||||
/v2v/virt-v2v
|
||||
/v2v/virt-v2v.1
|
||||
/v2v/virt-v2v-copy-to-local
|
||||
/v2v/virt-v2v-copy-to-local.1
|
||||
/website/*.html
|
||||
/website/README.txt
|
||||
/website/TODO.txt
|
||||
|
||||
@@ -101,6 +101,7 @@ v2v/changeuid.ml
|
||||
v2v/cmdline.ml
|
||||
v2v/convert_linux.ml
|
||||
v2v/convert_windows.ml
|
||||
v2v/copy_to_local.ml
|
||||
v2v/curl.ml
|
||||
v2v/domainxml.ml
|
||||
v2v/input_disk.ml
|
||||
|
||||
@@ -19,6 +19,7 @@ include $(top_srcdir)/subdir-rules.mk
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(SOURCES_MLI) $(SOURCES_ML) $(SOURCES_C) \
|
||||
copy_to_local.ml \
|
||||
v2v_unit_tests.ml \
|
||||
$(TESTS) $(SLOW_TESTS) \
|
||||
HACKING \
|
||||
@@ -35,7 +36,8 @@ EXTRA_DIST = \
|
||||
test-v2v-networks-and-bridges-expected.xml \
|
||||
test-v2v-networks-and-bridges.xml \
|
||||
test-v2v-sound.xml \
|
||||
virt-v2v.pod
|
||||
virt-v2v.pod \
|
||||
virt-v2v-copy-to-local.pod
|
||||
|
||||
CLEANFILES = *~ *.annot *.cmi *.cmo *.cmx *.cmxa *.o virt-v2v
|
||||
|
||||
@@ -113,7 +115,7 @@ SOURCES_C = \
|
||||
|
||||
if HAVE_OCAML
|
||||
|
||||
bin_PROGRAMS = virt-v2v
|
||||
bin_PROGRAMS = virt-v2v virt-v2v-copy-to-local
|
||||
|
||||
virt_v2v_SOURCES = $(SOURCES_C)
|
||||
virt_v2v_CPPFLAGS = \
|
||||
@@ -177,6 +179,46 @@ virt_v2v_LINK = \
|
||||
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
|
||||
$(OBJECTS) -o $@
|
||||
|
||||
virt_v2v_copy_to_local_SOURCES = \
|
||||
domainxml-c.c \
|
||||
utils-c.c \
|
||||
xml-c.c
|
||||
virt_v2v_copy_to_local_CPPFLAGS = \
|
||||
-I. \
|
||||
-I$(top_builddir) \
|
||||
-I$(shell $(OCAMLC) -where) \
|
||||
-I$(top_srcdir)/src
|
||||
virt_v2v_copy_to_local_CFLAGS = \
|
||||
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
|
||||
$(LIBXML2_CFLAGS) \
|
||||
$(LIBVIRT_CFLAGS)
|
||||
|
||||
COPY_TO_LOCAL_BOBJECTS = \
|
||||
$(top_builddir)/mllib/guestfs_config.cmo \
|
||||
$(top_builddir)/mllib/common_gettext.cmo \
|
||||
$(top_builddir)/mllib/common_utils.cmo \
|
||||
$(top_builddir)/mllib/JSON.cmo \
|
||||
xml.cmo \
|
||||
utils.cmo \
|
||||
curl.cmo \
|
||||
vCenter.cmo \
|
||||
domainxml.cmo \
|
||||
copy_to_local.cmo
|
||||
COPY_TO_LOCAL_XOBJECTS = $(COPY_TO_LOCAL_BOBJECTS:.cmo=.cmx)
|
||||
|
||||
if !HAVE_OCAMLOPT
|
||||
COPY_TO_LOCAL_OBJECTS = $(COPY_TO_LOCAL_BOBJECTS)
|
||||
else
|
||||
COPY_TO_LOCAL_OBJECTS = $(COPY_TO_LOCAL_XOBJECTS)
|
||||
endif
|
||||
|
||||
virt_v2v_copy_to_local_DEPENDENCIES = \
|
||||
$(COPY_TO_LOCAL_OBJECTS) $(top_srcdir)/ocaml-link.sh
|
||||
virt_v2v_copy_to_local_LINK = \
|
||||
$(top_srcdir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \
|
||||
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
|
||||
$(COPY_TO_LOCAL_OBJECTS) -o $@
|
||||
|
||||
.mli.cmi:
|
||||
$(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
|
||||
.ml.cmo:
|
||||
@@ -192,9 +234,11 @@ virttoolsdatadir = $(datadir)/virt-tools
|
||||
|
||||
# Manual pages and HTML files for the website.
|
||||
|
||||
man_MANS = virt-v2v.1
|
||||
man_MANS = virt-v2v.1 virt-v2v-copy-to-local.1
|
||||
|
||||
noinst_DATA = $(top_builddir)/website/virt-v2v.1.html
|
||||
noinst_DATA = \
|
||||
$(top_builddir)/website/virt-v2v.1.html \
|
||||
$(top_builddir)/website/virt-v2v-copy-to-local.1.html
|
||||
|
||||
virt-v2v.1 $(top_builddir)/website/virt-v2v.1.html: stamp-virt-v2v.pod
|
||||
|
||||
@@ -206,9 +250,21 @@ stamp-virt-v2v.pod: virt-v2v.pod
|
||||
$<
|
||||
touch $@
|
||||
|
||||
virt-v2v-copy-to-local.1 $(top_builddir)/website/virt-v2v-copy-to-local.1.html: stamp-virt-v2v-copy-to-local.pod
|
||||
|
||||
stamp-virt-v2v-copy-to-local.pod: virt-v2v-copy-to-local.pod
|
||||
$(PODWRAPPER) \
|
||||
--man virt-v2v-copy-to-local.1 \
|
||||
--html $(top_builddir)/website/virt-v2v-copy-to-local.1.html \
|
||||
--license GPLv2+ \
|
||||
$<
|
||||
touch $@
|
||||
|
||||
CLEANFILES += \
|
||||
stamp-virt-v2v.pod \
|
||||
virt-v2v.1
|
||||
stamp-virt-v2v-copy-to-local.pod \
|
||||
virt-v2v.1 \
|
||||
virt-v2v-copy-to-local.1
|
||||
|
||||
# Tests.
|
||||
|
||||
@@ -231,7 +287,8 @@ TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||
TESTS = \
|
||||
test-v2v-i-ova-formats.sh \
|
||||
test-v2v-i-ova-gz.sh \
|
||||
test-v2v-i-ova-two-disks.sh
|
||||
test-v2v-i-ova-two-disks.sh \
|
||||
test-v2v-copy-to-local.sh
|
||||
|
||||
if HAVE_OCAML_PKG_OUNIT
|
||||
TESTS += v2v_unit_tests
|
||||
|
||||
332
v2v/copy_to_local.ml
Normal file
332
v2v/copy_to_local.ml
Normal file
@@ -0,0 +1,332 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2015 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.
|
||||
*)
|
||||
|
||||
(* The separate virt-v2v-copy-to-local tool. *)
|
||||
|
||||
open Printf
|
||||
|
||||
open Common_gettext.Gettext
|
||||
open Common_utils
|
||||
|
||||
open Utils
|
||||
|
||||
type source_t = Xen_ssh of string | ESXi of string | Test
|
||||
|
||||
let rec main () =
|
||||
let input_conn = ref None in
|
||||
let password_file = ref None in
|
||||
|
||||
let set_string_option_once optname optref arg =
|
||||
match !optref with
|
||||
| Some _ ->
|
||||
error (f_"%s option used more than once on the command line") optname
|
||||
| None ->
|
||||
optref := Some arg
|
||||
in
|
||||
|
||||
(* Handle the command line. *)
|
||||
let argspec = [
|
||||
"-ic", Arg.String (set_string_option_once "-ic" input_conn),
|
||||
"uri " ^ s_"Libvirt URI";
|
||||
"--password-file", Arg.String (set_string_option_once "--password-file" password_file),
|
||||
"file " ^ s_"Use password from file";
|
||||
] in
|
||||
let argspec = set_standard_options argspec in
|
||||
let args = ref [] in
|
||||
let anon_fun s = args := s :: !args in
|
||||
let usage_msg =
|
||||
sprintf (f_"\
|
||||
%s: copy a remote guest to the local machine
|
||||
|
||||
Copy the remote guest:
|
||||
|
||||
virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com guest
|
||||
|
||||
virt-v2v-copy-to-local -ic esx://esxi.example.com guest
|
||||
|
||||
Then perform the conversion step:
|
||||
|
||||
virt-v2v -i libvirtxml guest.xml -o local -os /var/tmp
|
||||
|
||||
To clean up:
|
||||
|
||||
rm guest.xml guest-disk*
|
||||
|
||||
A short summary of the options is given below. For detailed help please
|
||||
read the man page virt-v2v-copy-to-local(1).
|
||||
")
|
||||
prog in
|
||||
Arg.parse argspec anon_fun usage_msg;
|
||||
|
||||
let args = !args in
|
||||
let input_conn = !input_conn in
|
||||
let password_file = !password_file in
|
||||
|
||||
let input_conn =
|
||||
match input_conn with
|
||||
| None ->
|
||||
error (f_"the -ic parameter is required") (* at the moment *)
|
||||
| Some ic -> ic in
|
||||
|
||||
(* Parse out the password from the password file. *)
|
||||
let password =
|
||||
match password_file with
|
||||
| None -> None
|
||||
| Some filename ->
|
||||
let password = read_first_line_from_file filename in
|
||||
Some password in
|
||||
|
||||
(* Check this is a libvirt URI we can understand. *)
|
||||
let parsed_uri =
|
||||
try Xml.parse_uri input_conn
|
||||
with Invalid_argument msg ->
|
||||
error (f_"could not parse '-ic %s'. Original error message was: %s")
|
||||
input_conn msg in
|
||||
let source =
|
||||
match parsed_uri.Xml.uri_server, parsed_uri.Xml.uri_scheme with
|
||||
| Some server, Some "xen+ssh" -> (* Xen over SSH *)
|
||||
Xen_ssh server
|
||||
| Some server, Some "esx" -> (* esxi over https *)
|
||||
ESXi server
|
||||
|
||||
(* This is just for testing, and is not documented. *)
|
||||
| None, Some "test" ->
|
||||
Test
|
||||
|
||||
(* We can probably extend this list in future. *)
|
||||
| _ ->
|
||||
error (f_"only copies from VMware ESXi or Xen over SSH are supported. See the virt-v2v-copy-to-local(1) manual page.") in
|
||||
|
||||
(* We expect a single extra argument, which is the guest name. *)
|
||||
let guest_name =
|
||||
match args with
|
||||
| [] ->
|
||||
error (f_"missing guest name. See the virt-v2v-copy-to-local(1) manual page.")
|
||||
| [arg] -> arg
|
||||
| _ ->
|
||||
error (f_"too many command line parameters. See the virt-v2v-copy-to-local(1) manual page.") in
|
||||
|
||||
(* Print the version, easier than asking users to tell us. *)
|
||||
if verbose () then
|
||||
printf "%s: %s %s (%s)\n%!"
|
||||
prog Guestfs_config.package_name Guestfs_config.package_version Guestfs_config.host_cpu;
|
||||
|
||||
(* Get the remote libvirt XML. *)
|
||||
message (f_"Fetching the remote libvirt XML metadata ...");
|
||||
let xml = Domainxml.dumpxml ?password ~conn:input_conn guest_name in
|
||||
|
||||
if verbose () then
|
||||
printf "libvirt XML from remote server:\n%s\n" xml;
|
||||
|
||||
(* Get the disk remote paths from the XML. *)
|
||||
message (f_"Parsing the remote libvirt XML metadata ...");
|
||||
let disks, dcpath, xml = parse_libvirt_xml guest_name xml in
|
||||
|
||||
if verbose () then
|
||||
printf "libvirt XML after modifying for local disks:\n%s\n" xml;
|
||||
|
||||
(* For VMware ESXi source, we have to massage the disk path. *)
|
||||
let disks =
|
||||
match source with
|
||||
| ESXi server ->
|
||||
List.map (
|
||||
fun (remote_disk, local_disk) ->
|
||||
let url, sslverify =
|
||||
VCenter.map_source_to_https dcpath parsed_uri
|
||||
server remote_disk in
|
||||
if verbose () then
|
||||
printf "esxi: source disk %s (sslverify=%b)\n" url sslverify;
|
||||
let cookie =
|
||||
VCenter.get_session_cookie password "esx"
|
||||
parsed_uri sslverify url in
|
||||
(url, local_disk, sslverify, cookie)
|
||||
) disks
|
||||
| Test | Xen_ssh _ ->
|
||||
List.map (fun (remote_disk, local_disk) ->
|
||||
(remote_disk, local_disk, false, None)) disks in
|
||||
|
||||
(* Delete the disks on exit, unless we finish everything OK. *)
|
||||
let delete_on_exit = ref true in
|
||||
at_exit (
|
||||
fun () ->
|
||||
if !delete_on_exit then (
|
||||
List.iter (
|
||||
fun (_, local_disk, _, _) ->
|
||||
try Unix.unlink local_disk with _ -> ()
|
||||
) disks
|
||||
)
|
||||
);
|
||||
|
||||
(* Copy the disks. *)
|
||||
let n = List.length disks in
|
||||
iteri (
|
||||
fun i (remote_disk, local_disk, sslverify, cookie) ->
|
||||
message (f_"Copying remote disk %d/%d to %s")
|
||||
(i+1) n local_disk;
|
||||
|
||||
(* How we copy it depends on the source. *)
|
||||
match source with
|
||||
| Xen_ssh server ->
|
||||
let { Xml.uri_user = user; uri_port = port } = parsed_uri in
|
||||
|
||||
let cmd =
|
||||
sprintf "set -o pipefail; ssh%s %s%s dd bs=1M if=%s | dd%s conv=sparse bs=1M of=%s"
|
||||
(match port with
|
||||
| n when n >= 1 -> sprintf " -p %d" n
|
||||
| _ -> "")
|
||||
(match user with
|
||||
| None -> ""
|
||||
| Some u -> sprintf "%s@" (quote u))
|
||||
server
|
||||
(quote remote_disk)
|
||||
(if quiet () then ""
|
||||
else " status=progress")
|
||||
(quote local_disk) in
|
||||
if verbose () then
|
||||
printf "%s\n%!" cmd;
|
||||
if Sys.command cmd <> 0 then
|
||||
error (f_"ssh copy command failed, see earlier errors");
|
||||
|
||||
| ESXi _ ->
|
||||
let curl_args = [
|
||||
"url", Some remote_disk;
|
||||
"output", Some local_disk;
|
||||
] in
|
||||
let curl_args =
|
||||
if sslverify then curl_args
|
||||
else ("insecure", None) :: curl_args in
|
||||
let curl_args =
|
||||
match cookie with
|
||||
| None -> curl_args
|
||||
| Some cookie -> ("cookie", Some cookie) :: curl_args in
|
||||
let curl_args =
|
||||
if quiet () then ("silent", None) :: curl_args
|
||||
else curl_args in
|
||||
|
||||
if verbose () then
|
||||
Curl.print_curl_command stdout curl_args;
|
||||
ignore (Curl.run curl_args)
|
||||
|
||||
| Test ->
|
||||
let cmd = sprintf "cp %s %s" (quote remote_disk) (quote local_disk) in
|
||||
if verbose () then
|
||||
printf "%s\n%!" cmd;
|
||||
if Sys.command cmd <> 0 then
|
||||
error (f_"copy command failed, see earlier errors");
|
||||
) disks;
|
||||
|
||||
let guest_xml = guest_name ^ ".xml" in
|
||||
message (f_"Writing libvirt XML metadata to %s ...") guest_xml;
|
||||
let chan = open_out guest_xml in
|
||||
output_string chan xml;
|
||||
close_out chan;
|
||||
|
||||
(* Finished, so don't delete the disks on exit. *)
|
||||
message (f_"Finishing off");
|
||||
delete_on_exit := false
|
||||
|
||||
(* This is a greatly simplified version of the parsing function
|
||||
* in virt-v2v input_libvirtxml.ml:parse_libvirt_xml
|
||||
* It also modifies the XML <disk> elements to point to local disks.
|
||||
*)
|
||||
and parse_libvirt_xml guest_name xml =
|
||||
(* Parse the XML. *)
|
||||
let doc = Xml.parse_memory xml in
|
||||
let xpathctx = Xml.xpath_new_context doc in
|
||||
Xml.xpath_register_ns xpathctx
|
||||
"vmware" "http://libvirt.org/schemas/domain/vmware/1.0";
|
||||
let xpath_string = xpath_string xpathctx
|
||||
and xpath_string_default = xpath_string_default xpathctx in
|
||||
|
||||
(* Get the dcpath, only present for libvirt >= 1.2.20 so use a
|
||||
* sensible default for older versions.
|
||||
*)
|
||||
let dcpath =
|
||||
xpath_string_default "/domain/vmware:datacenterpath" "ha-datacenter" in
|
||||
|
||||
(* Parse the disks. *)
|
||||
let get_disks, add_disk =
|
||||
let disks = ref [] and i = ref 0 in
|
||||
let get_disks () = List.rev !disks in
|
||||
let add_disk remote_disk =
|
||||
(* Generate a unique name for each output disk. *)
|
||||
incr i;
|
||||
let local_disk = sprintf "%s-disk%d" guest_name !i in
|
||||
|
||||
disks := (remote_disk, local_disk) :: !disks;
|
||||
local_disk
|
||||
in
|
||||
get_disks, add_disk
|
||||
in
|
||||
|
||||
(* node is a <disk> node, containing a <source> element. Update the
|
||||
* node to point to a local file.
|
||||
*)
|
||||
let update_disk_node node local_disk =
|
||||
Xml.set_prop node "type" "file";
|
||||
let obj = Xml.xpath_eval_expression xpathctx "source" in
|
||||
let nr_nodes = Xml.xpathobj_nr_nodes obj in
|
||||
assert (nr_nodes >= 1);
|
||||
for i = 0 to nr_nodes-1 do
|
||||
let source_node = Xml.xpathobj_node obj i in
|
||||
ignore (Xml.unset_prop source_node "dev");
|
||||
Xml.set_prop source_node "file" local_disk
|
||||
done
|
||||
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 obj i in
|
||||
Xml.xpathctx_set_current_context xpathctx node;
|
||||
|
||||
(* The <disk type='...'> attribute may be 'block' or 'file'.
|
||||
* We ignore any other types.
|
||||
*)
|
||||
match xpath_string "@type" with
|
||||
| None ->
|
||||
warning (f_"<disk> element with no type attribute ignored")
|
||||
|
||||
| Some "block" ->
|
||||
(match xpath_string "source/@dev" with
|
||||
| Some path ->
|
||||
let local_disk = add_disk path in
|
||||
update_disk_node node local_disk
|
||||
| None -> ()
|
||||
);
|
||||
| Some "file" ->
|
||||
(match xpath_string "source/@file" with
|
||||
| Some path ->
|
||||
let local_disk = add_disk path in
|
||||
update_disk_node node local_disk
|
||||
| None -> ()
|
||||
);
|
||||
|
||||
| Some disk_type ->
|
||||
warning (f_"<disk type='%s'> was ignored") disk_type
|
||||
done;
|
||||
|
||||
let xml = Xml.to_string doc ~format:true in
|
||||
get_disks (), dcpath, xml
|
||||
|
||||
let () = run_main_and_handle_errors main
|
||||
53
v2v/test-v2v-copy-to-local.sh
Executable file
53
v2v/test-v2v-copy-to-local.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs virt-v2v test script
|
||||
# Copyright (C) 2015 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
# Test virt-v2v-copy-to-local command.
|
||||
|
||||
unset CDPATH
|
||||
export LANG=C
|
||||
set -e
|
||||
|
||||
if [ -n "$SKIP_TEST_V2V_COPY_TO_LOCAL_SH" ]; then
|
||||
echo "$0: test skipped because environment variable is set"
|
||||
exit 77
|
||||
fi
|
||||
|
||||
abs_top_builddir="$(cd ..; pwd)"
|
||||
libvirt_uri="test://$abs_top_builddir/test-data/phony-guests/guests.xml"
|
||||
|
||||
f=../test-data/phony-guests/fedora.img
|
||||
if ! test -f $f || ! test -s $f; then
|
||||
echo "$0: test skipped because phony Fedora image was not created"
|
||||
exit 77
|
||||
fi
|
||||
|
||||
d=test-v2v-copy-to-local.d
|
||||
rm -rf $d
|
||||
mkdir $d
|
||||
|
||||
pushd $d
|
||||
$VG virt-v2v-copy-to-local --debug-gc -ic "$libvirt_uri" fedora
|
||||
popd
|
||||
|
||||
# Test the libvirt XML metadata was created.
|
||||
test -f $d/fedora.xml
|
||||
|
||||
# Test the disk was created.
|
||||
test -f $d/fedora-disk1
|
||||
|
||||
rm -r $d
|
||||
216
v2v/virt-v2v-copy-to-local.pod
Normal file
216
v2v/virt-v2v-copy-to-local.pod
Normal file
@@ -0,0 +1,216 @@
|
||||
=head1 NAME
|
||||
|
||||
virt-v2v-copy-to-local - Copy a remote guest to the local machine
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
virt-v2v-copy-to-local -ic LIBVIRT_URI GUEST
|
||||
|
||||
virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com xen_guest
|
||||
|
||||
virt-v2v-copy-to-local -ic esx://root@esxi.example.com vmware_guest
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
C<virt-v2v-copy-to-local> copies a guest from a remote hypervisor to
|
||||
the local machine, in preparation for conversion by L<virt-v2v(1)>.
|
||||
Note this tool alone B<does not> do the virt-v2v conversion.
|
||||
|
||||
=head2 When to use this tool
|
||||
|
||||
This tool is not usually necessary, but there are a few special cases
|
||||
(see list below) where you might need it.
|
||||
|
||||
If your case does not fit one of these special cases, then ignore this
|
||||
tool and read L<virt-v2v(1)> instead. The virt-v2v-copy-to-local
|
||||
process is slower than using virt-v2v directly, because it has to copy
|
||||
unused parts of the guest disk.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
You have a Xen guest using host block devices. Virt-v2v cannot
|
||||
convert such guests directly.
|
||||
|
||||
See L<virt-v2v(1)/XEN OR SSH CONVERSIONS FROM BLOCK DEVICES>.
|
||||
|
||||
=item *
|
||||
|
||||
You have VMware ESXi hypervisors, and are not using VMware vCenter to
|
||||
manage them. Virt-v2v cannot directly access ESXi hypervisor, so you
|
||||
either have to export the guest as an OVA (eg. using VMware's
|
||||
C<ovftool>); or you can use this tool to copy the guest to a local
|
||||
file on the conversion server, from where virt-v2v will be able to
|
||||
access it.
|
||||
|
||||
=back
|
||||
|
||||
=head2 How this tool works
|
||||
|
||||
This tool uses libvirt to get the libvirt XML (metadata) of the remote
|
||||
guest, essentially equivalent to running C<virsh dumpxml guest>.
|
||||
|
||||
It then uses the XML to locate the remote guest disks, which are
|
||||
copied over using a hypervisor-specific method. It uses ssh for
|
||||
remote Xen hypervisors, and HTTPS (curl) for remote ESXi hypervisors.
|
||||
|
||||
It then modifies the libvirt XML so that it points at the local copies
|
||||
of the guest disks.
|
||||
|
||||
The libvirt XML is output to a file called F<guest.xml> (where
|
||||
I<guest> is the name of the guest). The disk(s) are output to file(s)
|
||||
called F<guest-disk1>, F<guest-disk2> and so on.
|
||||
|
||||
After copying the guest locally, you can convert it using:
|
||||
|
||||
virt-v2v -i libvirtxml guest.xml [-o options ...]
|
||||
|
||||
Virt-v2v finds the local copies of the disks by looking in the XML.
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
=head2 Copy and convert from Xen hypervisor that uses host block devices
|
||||
|
||||
virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com xen_guest
|
||||
virt-v2v -i libvirtxml xen_guest.xml -o local -os /var/tmp
|
||||
rm xen_guest.xml xen_guest-disk*
|
||||
|
||||
=head2 Copy and convert from ESXi hypervisor
|
||||
|
||||
virt-v2v-copy-to-local -ic esx://root@esxi.example.com?no_verify=1 vmware_guest
|
||||
virt-v2v -i libvirtxml vmware_guest.xml -o local -os /var/tmp
|
||||
rm vmware_guest.xml vmware_guest-disk*
|
||||
|
||||
=head1 COPYING FROM XEN HYPERVISOR
|
||||
|
||||
=head2 XEN: LIBVIRT URI
|
||||
|
||||
The libvirt URI for remote Xen hosts will look something like this:
|
||||
|
||||
xen+ssh://root@xen.example.com
|
||||
|
||||
The remote Xen server must allow root logins over ssh.
|
||||
|
||||
To test it and list the remote guests available, use L<virsh(1)>:
|
||||
|
||||
$ virsh -c xen+ssh://root@xen.example.com list --all
|
||||
Id Name State
|
||||
----------------------------------------------------
|
||||
0 Domain-0 running
|
||||
- guest shut off
|
||||
|
||||
=head2 XEN: COPY THE GUEST
|
||||
|
||||
Using the libvirt URI as the I<-ic> option, copy one of the guests to
|
||||
the local machine:
|
||||
|
||||
$ virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com guest
|
||||
|
||||
This creates F<guest.xml>, F<guest-disk1>, ...
|
||||
|
||||
=head2 XEN: DO THE CONVERSION
|
||||
|
||||
Perform the conversion of the guest using virt-v2v:
|
||||
|
||||
$ virt-v2v -i libvirtxml guest.xml -o local -os /var/tmp
|
||||
|
||||
=head2 XEN: CLEAN UP
|
||||
|
||||
Remove the F<guest.xml> and F<guest-disk*> files.
|
||||
|
||||
=head1 COPYING FROM VMware ESXi HYPERVISOR
|
||||
|
||||
=head2 ESXi: LIBVIRT URI
|
||||
|
||||
The libvirt URI for VMware ESXi hypervisors will look something like this:
|
||||
|
||||
esx://root@esxi.example.com?no_verify=1
|
||||
|
||||
The C<?no_verify=1> parameter disables TLS certificate checking.
|
||||
|
||||
To test it and list the remote guests available, use L<virsh(1)>:
|
||||
|
||||
$ virsh -c esx://root@esxi.example.com?no_verify=1 list --all
|
||||
Enter root's password for esxi.example.com: ***
|
||||
Id Name State
|
||||
----------------------------------------------------
|
||||
- guest shut off
|
||||
|
||||
=head2 ESXi: COPY THE GUEST
|
||||
|
||||
Using the libvirt URI as the I<-ic> option, copy one of the guests to
|
||||
the local machine:
|
||||
|
||||
$ virt-v2v-copy-to-local -ic esx://root@esxi.example.com?no_verify=1 guest
|
||||
|
||||
This creates F<guest.xml>, F<guest-disk1>, ...
|
||||
|
||||
=head2 ESXi: DO THE CONVERSION
|
||||
|
||||
Perform the conversion of the guest using virt-v2v:
|
||||
|
||||
$ virt-v2v -i libvirtxml guest.xml -o local -os /var/tmp
|
||||
|
||||
=head2 ESXi: CLEAN UP
|
||||
|
||||
Remove the F<guest.xml> and F<guest-disk*> files.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Display help.
|
||||
|
||||
=item B<-ic> libvirtURI
|
||||
|
||||
Specify a libvirt connection URI
|
||||
|
||||
=item B<--password-file> file
|
||||
|
||||
Instead of asking for password(s) interactively, pass the password
|
||||
through a file. Note the file should contain the whole password,
|
||||
B<without any trailing newline>, and for security the file should have
|
||||
mode C<0600> so that others cannot read it.
|
||||
|
||||
Currently this option does not have any effect on xen+ssh transfers,
|
||||
but that is a bug.
|
||||
|
||||
=item B<-q>
|
||||
|
||||
=item B<--quiet>
|
||||
|
||||
This disables progress bars and other unnecessary output.
|
||||
|
||||
=item B<-v>
|
||||
|
||||
=item B<--verbose>
|
||||
|
||||
Enable verbose messages for debugging.
|
||||
|
||||
=item B<-V>
|
||||
|
||||
=item B<--version>
|
||||
|
||||
Display version number and exit.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<virt-v2v(1)>,
|
||||
L<virsh(1)>,
|
||||
L<http://libguestfs.org/>,
|
||||
L<https://libvirt.org/uri.html>,
|
||||
L<https://libvirt.org/remote.html>,
|
||||
L<https://libvirt.org/drvesx.html>.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (C) 2009-2015 Red Hat Inc.
|
||||
@@ -1177,6 +1177,19 @@ directory containing the files:
|
||||
|
||||
$ virt-v2v -i ova /path/to/files -o local -os /var/tmp
|
||||
|
||||
=head1 INPUT FROM VMWARE ESXi HYPERVISOR
|
||||
|
||||
Virt-v2v cannot access an ESXi hypervisor directly.
|
||||
|
||||
However you can use the L<virt-v2v-copy-to-local(1)> tool to copy the
|
||||
guest off the hypervisor into a local file, and then convert it:
|
||||
|
||||
virt-v2v-copy-to-local -ic esx://esxi.example.com vmware_guest
|
||||
virt-v2v -i libvirtxml vmware_guest.xml [-o options ...]
|
||||
rm vmware_guest.xml vmware_guest-disk*
|
||||
|
||||
See L<virt-v2v-copy-to-local(1)> for further information.
|
||||
|
||||
=head1 INPUT FROM RHEL 5 XEN
|
||||
|
||||
Virt-v2v is able to import Xen guests from RHEL 5 Xen hosts.
|
||||
@@ -1269,53 +1282,9 @@ The workaround is to copy the guest over to the conversion server.
|
||||
You will need sufficient space on the conversion server to store a
|
||||
full copy of the guest.
|
||||
|
||||
=over 4
|
||||
|
||||
=item 1.
|
||||
|
||||
From the conversion host, dump the guest XML to a local file:
|
||||
|
||||
virsh -c xen+ssh://root@xen.example.com dumpxml guest > guest.xml
|
||||
|
||||
=item 2.
|
||||
|
||||
From the conversion host, copy the guest disk(s) over. You may need
|
||||
to read the C<E<lt>diskE<gt>> sections from the guest XML to find the
|
||||
names of the guest disks.
|
||||
|
||||
ssh root@xen.example.com 'dd if=/dev/VG/guest' | dd conv=sparse of=guest-disk1
|
||||
|
||||
Notice C<conv=sparse> which adds sparseness, so the copied disk will
|
||||
use as little space as possible.
|
||||
|
||||
=item 3.
|
||||
|
||||
Edit the guest XML file, replacing C<E<lt>diskE<gt>> section(s) that
|
||||
refer to the remote disks with references to the local copies.
|
||||
|
||||
Three changes have to be made. Firstly in:
|
||||
|
||||
<disk type='block' device='disk'>
|
||||
|
||||
the C<type> must be changed to C<file>:
|
||||
|
||||
<disk type='file' device='disk'>
|
||||
|
||||
The last two changes are in the C<E<lt>sourceE<gt>> line where:
|
||||
|
||||
<source dev='/dev/VG/guest'/>
|
||||
|
||||
C<source dev=> becomes C<source file=> pointing at the local file:
|
||||
|
||||
<source file='guest-disk1'/>
|
||||
|
||||
=item 4.
|
||||
|
||||
Convert the guest using C<virt-v2v -i libvirtxml> mode like this:
|
||||
|
||||
virt-v2v -i libvirtxml guest.xml [-o options as usual ...]
|
||||
|
||||
=back
|
||||
virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com guest
|
||||
virt-v2v -i libvirtxml guest.xml [-o options ...]
|
||||
rm guest.xml guest-disk*
|
||||
|
||||
=head1 OUTPUT TO LIBVIRT
|
||||
|
||||
@@ -1786,6 +1755,14 @@ For other environment variables, see L<guestfs(3)/ENVIRONMENT VARIABLES>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item L<virt-v2v-copy-to-local(1)>
|
||||
|
||||
There are some special cases where virt-v2v cannot directly access the
|
||||
remote hypervisor. In that case you have to use
|
||||
L<virt-v2v-copy-to-local(1)> to make a local copy of the guest first,
|
||||
followed by running C<virt-v2v -i libvirtxml> to perform the
|
||||
conversion.
|
||||
|
||||
=item L<engine-image-uploader(8)>
|
||||
|
||||
Variously called C<engine-image-uploader>, C<ovirt-image-uploader> or
|
||||
@@ -1816,6 +1793,7 @@ L<guestfs(3)>,
|
||||
L<guestfish(1)>,
|
||||
L<qemu-img(1)>,
|
||||
L<fstrim(8)>,
|
||||
L<virt-v2v-copy-to-local(1)>,
|
||||
L<virt-v2v-test-harness(1)>,
|
||||
L<engine-image-uploader(8)>,
|
||||
L<import-to-ovirt.pl|http://git.annexia.org/?p=import-to-ovirt.git>,
|
||||
|
||||
Reference in New Issue
Block a user