[Pkg-libvirt-commits] [libguestfs] 58/266: v2v: Add RHEV target.
Hilko Bengen
bengen at moszumanska.debian.org
Fri Oct 3 14:41:41 UTC 2014
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to annotated tag debian/1%1.27.35-1
in repository libguestfs.
commit 40cecb4b9cb4354902149f68918fe1d24a534c41
Author: Richard W.M. Jones <rjones at redhat.com>
Date: Mon Aug 4 17:20:41 2014 +0100
v2v: Add RHEV target.
Use:
virt-v2v [..] -o rhev -os nfs:/export
or:
virt-v2v [..] -o rhev -os /mountpoint
to write to a RHEV-M / oVirt Export Storage Domain.
---
po/POTFILES-ml | 2 +
v2v/DOM.ml | 97 +++++++++
v2v/DOM.mli | 52 +++++
v2v/Makefile.am | 6 +
v2v/cmdline.ml | 15 +-
v2v/target_RHEV.ml | 568 ++++++++++++++++++++++++++++++++++++++++++++++++++++
v2v/target_RHEV.mli | 24 +++
v2v/types.ml | 5 +-
v2v/types.mli | 7 +-
v2v/utils.ml | 6 +
v2v/v2v.ml | 14 +-
v2v/virt-v2v.pod | 21 +-
12 files changed, 805 insertions(+), 12 deletions(-)
diff --git a/po/POTFILES-ml b/po/POTFILES-ml
index fe6f169..2c2b71b 100644
--- a/po/POTFILES-ml
+++ b/po/POTFILES-ml
@@ -82,12 +82,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/DOM.ml
v2v/cmdline.ml
v2v/convert_linux.ml
v2v/convert_windows.ml
v2v/lib_linux.ml
v2v/source_libvirt.ml
v2v/stringMap.ml
+v2v/target_RHEV.ml
v2v/target_local.ml
v2v/types.ml
v2v/utils.ml
diff --git a/v2v/DOM.ml b/v2v/DOM.ml
new file mode 100644
index 0000000..d828db4
--- /dev/null
+++ b/v2v/DOM.ml
@@ -0,0 +1,97 @@
+(* 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.
+ *)
+
+(* Poor man's XML DOM, mutable for easy of modification. *)
+
+open Common_utils
+open Utils
+
+open Printf
+
+type node =
+ | PCData of string
+ | Element of element
+and element = {
+ e_name : string; (* Name of element. *)
+ mutable e_attrs : attr list; (* Attributes. *)
+ mutable e_children : node list; (* Child elements. *)
+}
+and attr = string * string
+and doc = element
+
+let doc name attrs children =
+ { e_name = name; e_attrs = attrs; e_children = children }
+
+let e name attrs children =
+ Element { e_name = name; e_attrs = attrs; e_children = children }
+
+let rec node_to_chan chan = function
+ | PCData str -> output_string chan (xml_quote_pcdata str)
+ | Element e -> element_to_chan chan e
+and element_to_chan chan
+ { e_name = name; e_attrs = attrs; e_children = children } =
+ fprintf chan "<%s" name;
+ List.iter (fun (n, v) -> fprintf chan " %s='%s'" n (xml_quote_attr v)) attrs;
+ output_char chan '>';
+ List.iter (node_to_chan chan) children;
+ fprintf chan "</%s>" name
+
+let doc_to_chan chan doc =
+ fprintf chan "<?xml version='1.0' encoding='utf-8'?>\n";
+ element_to_chan chan doc
+
+let path_to_nodes doc path =
+ match path with
+ | [] -> invalid_arg "path_to_nodes: empty path"
+ | top_name :: path ->
+ if doc.e_name <> top_name then []
+ else (
+ let rec loop nodes path =
+ match path with
+ | [] -> []
+ | [p] ->
+ List.filter (
+ function
+ | PCData _ -> false
+ | Element e when e.e_name = p -> true
+ | Element _ -> false
+ ) nodes
+ | p :: ps ->
+ let children =
+ filter_map (
+ function
+ | PCData _ -> None
+ | Element e when e.e_name = p -> Some e.e_children
+ | Element _ -> None
+ ) nodes in
+ List.concat (List.map (fun nodes -> loop nodes ps) children)
+ in
+ loop doc.e_children path
+ )
+
+let filter_node_list_by_attr nodes attr =
+ List.filter (
+ function
+ | Element { e_attrs = attrs } when List.mem attr attrs -> true
+ | Element _ | PCData _ -> false
+ ) nodes
+
+let find_node_by_attr nodes attr =
+ match filter_node_list_by_attr nodes attr with
+ | [] -> raise Not_found
+ | x::_ -> x
diff --git a/v2v/DOM.mli b/v2v/DOM.mli
new file mode 100644
index 0000000..2c97f5c
--- /dev/null
+++ b/v2v/DOM.mli
@@ -0,0 +1,52 @@
+(* 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.
+ *)
+
+(** Poor man's XML DOM, mutable for easy of modification. *)
+
+type node =
+ | PCData of string
+ | Element of element
+and element = {
+ e_name : string; (** Name of element. *)
+ mutable e_attrs : attr list; (** Attributes. *)
+ mutable e_children : node list; (** Child elements. *)
+}
+and attr = string * string
+and doc = element
+
+val doc : string -> attr list -> node list -> doc
+(** A quick way to create a document. *)
+
+val e : string -> attr list -> node list -> node
+(** A quick way to create elements. *)
+
+val doc_to_chan : out_channel -> doc -> unit
+(** Write the XML document to an output channel. *)
+
+val path_to_nodes : doc -> string list -> node list
+(** Search down the path and return a list of all matching elements.
+ Returns an empty list if none were found. *)
+
+val filter_node_list_by_attr : node list -> attr -> node list
+(** Find DOM elements which have a particular attribute name=value (not
+ recursively). If not found, returns an empty list. *)
+
+val find_node_by_attr : node list -> attr -> node
+(** Find the first DOM element which has a particular attribute
+ name=value (not recursively). If not found, raises
+ [Not_found]. *)
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index 0d8ef09..97d6d3e 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -26,9 +26,11 @@ CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o virt-v2v
SOURCES_MLI = \
convert_linux.mli \
convert_windows.mli \
+ DOM.mli \
lib_linux.mli \
source_libvirt.mli \
target_local.mli \
+ target_RHEV.mli \
types.mli \
xml.mli
@@ -37,18 +39,21 @@ SOURCES_ML = \
types.ml \
utils.ml \
xml.ml \
+ DOM.ml \
lib_linux.ml \
cmdline.ml \
source_libvirt.ml \
convert_linux.ml \
convert_windows.ml \
target_local.ml \
+ target_RHEV.ml \
v2v.ml
SOURCES_C = \
$(top_builddir)/fish/progress.c \
$(top_builddir)/mllib/tty-c.c \
$(top_builddir)/mllib/progress-c.c \
+ $(top_builddir)/mllib/mkdtemp-c.c \
$(top_builddir)/customize/crypt-c.c \
utils-c.c \
xml-c.c
@@ -73,6 +78,7 @@ BOBJECTS = \
$(top_builddir)/mllib/tTY.cmo \
$(top_builddir)/mllib/progress.cmo \
$(top_builddir)/mllib/config.cmo \
+ $(top_builddir)/mllib/mkdtemp.cmo \
$(top_builddir)/customize/urandom.cmo \
$(top_builddir)/customize/random_seed.cmo \
$(top_builddir)/customize/hostname.cmo \
diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml
index 52a4959..0d46fc6 100644
--- a/v2v/cmdline.ml
+++ b/v2v/cmdline.ml
@@ -42,6 +42,7 @@ let parse_cmdline () =
let quiet = ref false in
let verbose = ref false in
let trace = ref false in
+ let vmtype = ref "" in
let input_mode = ref `Libvirt in
let set_input_mode = function
@@ -98,6 +99,7 @@ let parse_cmdline () =
"--verbose", Arg.Set verbose, ditto;
"-V", Arg.Unit display_version, " " ^ s_"Display version and exit";
"--version", Arg.Unit display_version, ditto;
+ "--vmtype", Arg.Set_string vmtype, "server|desktop " ^ s_"Set vmtype (for RHEV)";
"-x", Arg.Set trace, " " ^ s_"Enable tracing of libguestfs calls";
] in
long_options := argspec;
@@ -139,6 +141,13 @@ read the man page virt-v2v(1).
let root_choice = !root_choice in
let verbose = !verbose in
let trace = !trace in
+ let vmtype =
+ match !vmtype with
+ | "server" -> Some `Server
+ | "desktop" -> Some `Desktop
+ | "" -> None
+ | _ ->
+ error (f_"unknown --vmtype option, must be \"server\" or \"desktop\"") in
(* No arguments and machine-readable mode? Print out some facts
* about what this binary supports.
@@ -177,6 +186,8 @@ read the man page virt-v2v(1).
| `Libvirt ->
if output_storage <> "" then
error (f_"-o libvirt: do not use the -os option");
+ if vmtype <> None then
+ error (f_"--vmtype option can only be used with '-o rhev'");
OutputLibvirt output_conn
| `Local ->
if output_storage = "" then
@@ -184,11 +195,13 @@ read the man page virt-v2v(1).
if not (is_directory output_storage) then
error (f_"-os %s: output directory does not exist or is not a directory")
output_storage;
+ if vmtype <> None then
+ error (f_"--vmtype option can only be used with '-o rhev'");
OutputLocal output_storage
| `RHEV ->
if output_storage = "" then
error (f_"-o local: output storage was not specified, use '-os'");
- OutputRHEV output_storage in
+ OutputRHEV (output_storage, vmtype) in
input, output,
debug_gc, output_alloc, output_format, output_name,
diff --git a/v2v/target_RHEV.ml b/v2v/target_RHEV.ml
new file mode 100644
index 0000000..d3f360d
--- /dev/null
+++ b/v2v/target_RHEV.ml
@@ -0,0 +1,568 @@
+(* 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 Common_gettext.Gettext
+open Common_utils
+
+open Unix
+open Printf
+
+open Types
+open Utils
+open DOM
+
+let title = sprintf "Exported by virt-v2v %s" Config.package_version
+
+(* Describes a mounted Export Storage Domain. *)
+type export_storage_domain = {
+ mp : string; (* Local mountpoint. *)
+ uuid : string; (* /mp/uuid *)
+}
+
+(* Private data that we keep between calls. Keep it in a global
+ * variables since there's only one conversion going on.
+ *)
+(* Export Storage Domain mountpoint. *)
+let esd = ref { mp = ""; uuid = "" }
+(* Target image directory, UUID. *)
+let image_uuid = ref ""
+let image_dir = ref ""
+(* Flag to indicate if the target image (image_dir) should be
+ * deleted. This is set to false once we know the conversion was
+ * successful.
+ *)
+let delete_target_directory = ref true
+(* We set the creation time to be the same for all dates in
+ * all metadata files.
+ *)
+let time = time ()
+let iso_time =
+ let tm = gmtime time in
+ sprintf "%04d/%02d/%02d %02d:%02d:%02d"
+ (tm.tm_year + 1900) (tm.tm_mon + 1) tm.tm_mday
+ tm.tm_hour tm.tm_min tm.tm_sec
+
+(* This is called early on in the conversion and lets us choose the
+ * name of the target files that eventually get written by the main
+ * code.
+ *
+ * 'os' is the output storage (-os nfs:/export). 'source' contains a
+ * few useful fields such as the guest name. 'overlays' describes the
+ * destination files. We modify and return this list.
+ *
+ * Note it's good to fail here (early) if there are any problems, since
+ * the next time we are called (in {!create_metadata}) we have already
+ * done the conversion and copy, and the user won't thank us for
+ * displaying errors there.
+ *)
+let rec initialize ~verbose os source output_alloc overlays =
+ esd := mount_and_check_export_storage_domain ~verbose os;
+ if verbose then
+ eprintf "RHEV: ESD mountpoint: %s\nRHEV: ESD UUID: %s\n%!" !esd.mp !esd.uuid;
+
+ (* Create a unique UUID for the final image. *)
+ image_uuid := uuidgen ~prog ();
+ image_dir := !esd.mp // !esd.uuid // "images" // !image_uuid;
+
+ (* We need to create the target image directory so there's a place
+ * for the main program to copy the images to. However if image
+ * conversion fails for any reason then we delete this directory.
+ *)
+ mkdir !image_dir 0o755;
+ at_exit (fun () ->
+ if !delete_target_directory then (
+ let cmd = sprintf "rm -rf %s" (quote !image_dir) in
+ ignore (Sys.command cmd)
+ )
+ );
+ if verbose then
+ eprintf "RHEV: export directory: %s\n%!" !image_dir;
+
+ (* This loop has two purposes: (1) Generate the randomly named
+ * target files (just the names). (2) Generate the .meta file
+ * associated with each volume. At the end we have a directory
+ * structure like this:
+ * /<MP>/<ESD_UUID>/images/<IMAGE_UUID>/
+ * <VOL_UUID_1> # first disk - will be created by main code
+ * <VOL_UUID_1>.meta # first disk
+ * <VOL_UUID_2> # second disk - will be created by main code
+ * <VOL_UUID_2>.meta # second disk
+ * <VOL_UUID_3> # etc
+ * <VOL_UUID_3>.meta #
+ *)
+ let overlays =
+ let output_alloc_for_rhev =
+ match output_alloc with
+ | `Sparse -> "SPARSE"
+ | `Preallocated -> "PREALLOCATED" in
+
+ List.map (
+ fun ov ->
+ let vol_uuid = uuidgen ~prog () in
+ let target_file = !image_dir // vol_uuid in
+
+ if verbose then
+ eprintf "RHEV: will export %s to %s\n%!" ov.ov_sd target_file;
+
+ (* Create the per-volume metadata (.meta files, in an oVirt-
+ * specific format).
+ *)
+ let vol_meta = target_file ^ ".meta" in
+
+ let size_in_sectors =
+ if ov.ov_virtual_size &^ 511L <> 0L then
+ error (f_"the virtual size of the input disk %s is not an exact multiple of 512 bytes. The virtual size is: %Ld.\n\nThis probably means something unexpected is going on, so please file a bug about this issue.") ov.ov_source_file ov.ov_virtual_size;
+ ov.ov_virtual_size /^ 512L in
+
+ let format_for_rhev =
+ match ov.ov_target_format with
+ | "raw" -> "RAW"
+ | "qcow2" -> "COW"
+ | _ ->
+ error (f_"RHEV does not support the output format '%s', only raw or qcow2") ov.ov_target_format in
+
+ let chan = open_out vol_meta in
+ let fpf fs = fprintf chan fs in
+ fpf "DOMAIN=%s\n" !esd.uuid; (* "Domain" as in Export Storage Domain *)
+ fpf "VOLTYPE=LEAF\n";
+ fpf "CTIME=%.0f\n" time;
+ fpf "MTIME=%.0f\n" time;
+ fpf "IMAGE=%s\n" !image_uuid;
+ fpf "DISKTYPE=1\n";
+ fpf "PUUID=00000000-0000-0000-0000-000000000000\n";
+ fpf "LEGALITY=LEGAL\n";
+ fpf "POOL_UUID=\n";
+ fpf "SIZE=%Ld\n" size_in_sectors;
+ fpf "FORMAT=%s\n" format_for_rhev;
+ fpf "TYPE=%s\n" output_alloc_for_rhev;
+ fpf "DESCRIPTION=%s\n" title;
+ fpf "EOF\n";
+ close_out chan;
+
+ { ov with
+ ov_target_file_tmp = target_file; ov_target_file = target_file;
+ ov_vol_uuid = vol_uuid }
+ ) overlays in
+
+ (* Return the list of overlays. *)
+ overlays
+
+and mount_and_check_export_storage_domain ~verbose os =
+ (* The user can either specify -os nfs:/export, or a local directory
+ * which is assumed to be the already-mounted NFS export. In either
+ * case we need to check that we have sufficient permissions to write
+ * to this mountpoint.
+ *)
+ match string_split ":/" os with
+ | mp, "" -> (* Already mounted directory. *)
+ check_export_storage_domain os mp
+ | server, export ->
+ let export = "/" ^ export in
+
+ (* Try mounting it. *)
+ let mp = Mkdtemp.temp_dir "v2v." "" in
+ let cmd =
+ sprintf "mount %s:%s %s" (quote server) (quote export) (quote mp) in
+ if verbose then printf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then
+ error (f_"mount command failed, see earlier errors.\n\nThis probably means you didn't specify the right Export Storage Domain path [-os %s], or else you need to rerun virt-v2v as root.") os;
+
+ (* Make sure it is unmounted at exit. *)
+ at_exit (fun () ->
+ let cmd = sprintf "umount %s" (quote mp) in
+ if verbose then printf "%s\n%!" cmd;
+ ignore (Sys.command cmd);
+ try rmdir mp with _ -> ()
+ );
+
+ check_export_storage_domain os mp
+
+and check_export_storage_domain os mp =
+ (* Typical ESD mountpoint looks like this:
+ * $ ls /tmp/mnt
+ * 39b6af0e-1d64-40c2-97e4-4f094f1919c7 __DIRECT_IO_TEST__ lost+found
+ * $ ls /tmp/mnt/39b6af0e-1d64-40c2-97e4-4f094f1919c7
+ * dom_md images master
+ * We expect exactly one of those magic UUIDs.
+ *)
+ let entries =
+ try Sys.readdir mp
+ with Sys_error msg ->
+ error (f_"could not read the Export Storage Domain specified by the '-os %s' parameter on the command line. Is it really an OVirt or RHEV-M Export Storage Domain? The original error is: %s") os msg in
+ let entries = Array.to_list entries in
+ let uuids = List.filter (
+ fun entry ->
+ String.length entry = 36 &&
+ entry.[8] = '-' && entry.[13] = '-' && entry.[18] = '-' &&
+ entry.[23] = '-'
+ ) entries in
+ let uuid =
+ match uuids with
+ | [uuid] -> uuid
+ | [] ->
+ error (f_"there are no UUIDs in the Export Storage Domain (%s). Is it really an OVirt or RHEV-M Export Storage Domain?") os
+ | _::_ ->
+ error (f_"there are multiple UUIDs in the Export Storage Domain (%s). This is unexpected, and may be a bug in virt-v2v or OVirt.") os in
+
+ (* Check that the domain has been attached to a Data Center by checking that
+ * the master/vms directory exists.
+ *)
+ if not (is_directory (mp // uuid // "master" // "vms")) then
+ error (f_"the Export Storage Domain (%s) has not been attached to any Data Center.\n\nYou have to do this through the RHEV-M / OVirt user interface first.") os;
+
+ (* Check that the ESD is writable. *)
+ let testfile = mp // uuid // "v2v-write-test" in
+ let write_test_failed err =
+ error (f_"the Export Storage Domain (%s) is not writable.\n\nThis probably means you need to run virt-v2v as 'root'.\n\nOriginal error was: %s") os err;
+ in
+ (try
+ let chan = open_out testfile in
+ close_out chan;
+ unlink testfile
+ with
+ | Sys_error err -> write_test_failed err
+ | Unix_error (code, _, _) -> write_test_failed (error_message code)
+ );
+
+ (* Looks good, so return the ESD object. *)
+ { mp = mp; uuid = uuid }
+
+(* This is called after conversion to write the OVF metadata. *)
+let rec create_metadata os vmtype source output_alloc
+ overlays inspect guestcaps =
+ let vm_uuid = uuidgen ~prog () in
+
+ let memsize_mb = source.s_memory /^ 1024L /^ 1024L in
+
+ let vmtype =
+ match vmtype with
+ | Some vmtype -> vmtype
+ | None -> get_vmtype inspect in
+ let vmtype = match vmtype with `Desktop -> "DESKTOP" | `Server -> "SERVER" in
+ let ostype = get_ostype inspect in
+
+ let ovf : doc =
+ doc "ovf:Envelope" [
+ "xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData";
+ "xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData";
+ "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance";
+ "xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1/";
+ "ovf:version", "0.9"
+ ] [
+ e "References" [] [];
+ e "Section" ["xsi:type", "ovf:NetworkSection_Type"] [
+ e "Info" [] [PCData "List of networks"]
+ ];
+ e "Section" ["xsi:type", "ovf:DiskSection_Type"] [
+ e "Info" [] [PCData "List of Virtual Disks"]
+ ];
+ e "Content" ["ovf:id", "out"; "xsi:type", "ovf:VirtualSystem_Type"] [
+ e "Name" [] [PCData source.s_name];
+ e "TemplateId" [] [PCData "00000000-0000-0000-0000-000000000000"];
+ e "TemplateName" [] [PCData "Blank"];
+ e "Description" [] [PCData title];
+ e "Domain" [] [];
+ e "CreationDate" [] [PCData iso_time];
+ e "IsInitilized" [] [PCData "True"];
+ e "IsAutoSuspend" [] [PCData "False"];
+ e "TimeZone" [] [];
+ e "IsStateless" [] [PCData "False"];
+ e "Origin" [] [PCData "0"];
+ e "VmType" [] [PCData vmtype];
+ e "DefaultDisplayType" [] [PCData "1"];
+
+ e "Section" ["ovf:id", vm_uuid; "ovf:required", "false";
+ "xsi:type", "ovf:OperatingSystemSection_Type"] [
+ e "Info" [] [PCData "Guest Operating System"];
+ e "Description" [] [PCData ostype];
+ ];
+
+ e "Section" ["xsi:type", "ovf:VirtualHardwareSection_Type"] [
+ e "Info" [] [PCData (sprintf "%d CPU, %Ld Memory" source.s_vcpu memsize_mb)];
+ e "Item" [] [
+ e "rasd:Caption" [] [PCData (sprintf "%d virtual cpu" source.s_vcpu)];
+ e "rasd:Description" [] [PCData "Number of virtual CPU"];
+ e "rasd:InstanceId" [] [PCData "1"];
+ e "rasd:ResourceType" [] [PCData "3"];
+ e "rasd:num_of_sockets" [] [PCData (string_of_int source.s_vcpu)];
+ e "rasd:cpu_per_socket"[] [PCData "1"];
+ ];
+ e "Item" [] [
+ e "rasd:Caption" [] [PCData (sprintf "%Ld MB of memory" memsize_mb)];
+ e "rasd:Description" [] [PCData "Memory Size"];
+ e "rasd:InstanceId" [] [PCData "2"];
+ e "rasd:ResourceType" [] [PCData "4"];
+ e "rasd:AllocationUnits" [] [PCData "MegaBytes"];
+ e "rasd:VirtualQuantity" [] [PCData (Int64.to_string memsize_mb)];
+ ];
+ e "Item" [] [
+ e "rasd:Caption" [] [PCData "USB Controller"];
+ e "rasd:InstanceId" [] [PCData "4"];
+ e "rasd:ResourceType" [] [PCData "23"];
+ e "rasd:UsbPolicy" [] [PCData "Disabled"];
+ ];
+ e "Item" [] [
+ e "rasd:Caption" [] [PCData "Graphical Controller"];
+ e "rasd:InstanceId" [] [PCData "5"];
+ e "rasd:ResourceType" [] [PCData "20"];
+ e "rasd:VirtualQuantity" [] [PCData "1"];
+ e "rasd:Device" [] [PCData "qxl"];
+ ]
+ ]
+ ]
+ ] in
+
+ (* Add disks to the OVF XML. *)
+ add_disks output_alloc overlays guestcaps ovf;
+
+ (* XXX Missing here from old virt-v2v:
+ cdroms and floppies
+ network interfaces
+ display
+ See: lib/Sys/VirtConvert/Connection/RHEVTarget.pm
+ *)
+
+
+
+
+ (* Write it to the metadata file. *)
+ let dir = !esd.mp // !esd.uuid // "master" // "vms" // vm_uuid in
+ mkdir dir 0o755;
+ let file = dir // vm_uuid ^ ".ovf" in
+ let chan = open_out file in
+ doc_to_chan chan ovf;
+ close_out chan;
+
+ (* Finished, so don't delete the target directory on exit. *)
+ delete_target_directory := false
+
+(* Guess vmtype based on the guest inspection data. *)
+and get_vmtype = function
+ | { i_type = "linux"; i_distro = "rhel"; i_major_version = major;
+ i_product_name = product }
+ when major >= 5 && string_find product "Server" >= 0 ->
+ `Server
+
+ | { i_type = "linux"; i_distro = "rhel"; i_major_version = major }
+ when major >= 5 ->
+ `Desktop
+
+ | { i_type = "linux"; i_distro = "rhel"; i_major_version = major;
+ i_product_name = product }
+ when major >= 3 && string_find product "ES" >= 0 ->
+ `Server
+
+ | { i_type = "linux"; i_distro = "rhel"; i_major_version = major;
+ i_product_name = product }
+ when major >= 3 && string_find product "AS" >= 0 ->
+ `Server
+
+ | { i_type = "linux"; i_distro = "rhel"; i_major_version = major }
+ when major >= 3 ->
+ `Desktop
+
+ | { i_type = "linux"; i_distro = "fedora" } -> `Desktop
+
+ | { i_type = "windows"; i_major_version = 5; i_minor_version = 1 } ->
+ `Desktop (* Windows XP *)
+
+ | { i_type = "windows"; i_major_version = 5; i_minor_version = 2;
+ i_product_name = product } when string_find product "XP" >= 0 ->
+ `Desktop (* Windows XP *)
+
+ | { i_type = "windows"; i_major_version = 5; i_minor_version = 2 } ->
+ `Server (* Windows 2003 *)
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 0;
+ i_product_name = product } when string_find product "Server" >= 0 ->
+ `Server (* Windows 2008 *)
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 0 } ->
+ `Desktop (* Vista *)
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 1;
+ i_product_name = product } when string_find product "Server" >= 0 ->
+ `Server (* Windows 2008R2 *)
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 1 } ->
+ `Server (* Windows 7 *)
+
+ | _ -> `Server
+
+and get_ostype = function
+ | { i_type = "linux"; i_distro = "rhel"; i_major_version = v;
+ i_arch = "i386" } ->
+ sprintf "RHEL%d" v
+
+ | { i_type = "linux"; i_distro = "rhel"; i_major_version = v;
+ i_arch = "x86_64" } ->
+ sprintf "RHEL%dx64" v
+
+ | { i_type = "linux" } -> "OtherLinux"
+
+ | { i_type = "windows"; i_major_version = 5; i_minor_version = 1 } ->
+ "WindowsXP" (* no architecture differentiation of XP on RHEV *)
+
+ | { i_type = "windows"; i_major_version = 5; i_minor_version = 2;
+ i_product_name = product } when string_find product "XP" >= 0 ->
+ "WindowsXP" (* no architecture differentiation of XP on RHEV *)
+
+ | { i_type = "windows"; i_major_version = 5; i_minor_version = 2;
+ i_arch = "i386" } ->
+ "Windows2003"
+
+ | { i_type = "windows"; i_major_version = 5; i_minor_version = 2;
+ i_arch = "x86_64" } ->
+ "Windows2003x64"
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 0;
+ i_arch = "i386" } ->
+ "Windows2008"
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 0;
+ i_arch = "x86_64" } ->
+ "Windows2008x64"
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 1;
+ i_arch = "i386" } ->
+ "Windows7"
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 1;
+ i_arch = "x86_64"; i_product_variant = "Client" } ->
+ "Windows7x64"
+
+ | { i_type = "windows"; i_major_version = 6; i_minor_version = 1;
+ i_arch = "x86_64" } ->
+ "Windows2008R2x64"
+
+ | { i_type = typ; i_distro = distro;
+ i_major_version = major; i_minor_version = minor;
+ i_product_name = product } ->
+ warning ~prog (f_"unknown guest operating system: %s %s %d.%d (%s)")
+ typ distro major minor product;
+ "Unassigned"
+
+(* This modifies the OVF DOM, adding a section for each disk. *)
+and add_disks output_alloc overlays guestcaps ovf =
+ let references =
+ let nodes = path_to_nodes ovf ["ovf:Envelope"; "References"] in
+ match nodes with
+ | [] | _::_::_ -> assert false
+ | [node] -> node in
+ let disk_section =
+ let sections = path_to_nodes ovf ["ovf:Envelope"; "Section"] in
+ try find_node_by_attr sections ("xsi:type", "ovf:DiskSection_Type")
+ with Not_found -> assert false in
+ let virtualhardware_section =
+ let sections = path_to_nodes ovf ["ovf:Envelope"; "Content"; "Section"] in
+ try find_node_by_attr sections ("xsi:type", "ovf:VirtualHardwareSection_Type")
+ with Not_found -> assert false in
+
+ let append_child child = function
+ | PCData _ -> assert false
+ | Element e -> e.e_children <- e.e_children @ [child]
+ in
+
+ (* Iterate over the disks, adding them to the OVF document. *)
+ iteri (
+ fun i ov ->
+ let is_boot_drive = i == 0 in
+
+ let target_file = ov.ov_target_file
+ and vol_uuid = ov.ov_vol_uuid in
+ assert (vol_uuid <> "");
+
+ let fileref = !image_uuid // vol_uuid in
+
+ let size_gb =
+ Int64.to_float ov.ov_virtual_size /. 1024. /. 1024. /. 1024. in
+ let usage_gb =
+ let usage_mb = du_m target_file in
+ Int64.to_float usage_mb /. 1024. in
+
+ let format_for_rhev =
+ match ov.ov_target_format with
+ | "raw" -> "RAW"
+ | "qcow2" -> "COW"
+ | _ ->
+ error (f_"RHEV does not support the output format '%s', only raw or qcow2") ov.ov_target_format in
+
+ let output_alloc_for_rhev =
+ match output_alloc with
+ | `Sparse -> "SPARSE"
+ | `Preallocated -> "PREALLOCATED" in
+
+ (* Add disk to <References/> node. *)
+ let disk =
+ e "File" [
+ "ovf:href", fileref;
+ "ovf:id", vol_uuid;
+ "ovf:size", Int64.to_string ov.ov_virtual_size;
+ "ovf:description", title;
+ ] [] in
+ append_child disk references;
+
+ (* Add disk to DiskSection. *)
+ let disk =
+ e "Disk" [
+ "ovf:diskId", vol_uuid;
+ "ovf:size", sprintf "%.1f" size_gb;
+ "ovf:actual_size", sprintf "%.1f" usage_gb;
+ "ovf:fileRef", fileref;
+ "ovf:parentRef", "";
+ "ovf:vm_snapshot_id", uuidgen ~prog ();
+ "ovf:volume-format", format_for_rhev;
+ "ovf:volume-type", output_alloc_for_rhev;
+ "ovf:format", "http://en.wikipedia.org/wiki/Byte"; (* wtf? *)
+ "ovf:disk-interface",
+ if guestcaps.gcaps_block_bus = "virtio" then "VirtIO" else "IDE";
+ "ovf:disk-type", "System"; (* RHBZ#744538 *)
+ "ovf:boot", if is_boot_drive then "True" else "False";
+ ] [] in
+ append_child disk disk_section;
+
+ (* Add disk to VirtualHardware. *)
+ let item =
+ e "Item" [] [
+ e "rasd:InstanceId" [] [PCData vol_uuid];
+ e "rasd:ResourceType" [] [PCData "17"];
+ e "rasd:HostResource" [] [PCData fileref];
+ e "rasd:Parent" [] [PCData "00000000-0000-0000-0000-000000000000"];
+ e "rasd:Template" [] [PCData "00000000-0000-0000-0000-000000000000"];
+ e "rasd:ApplicationList" [] [];
+ e "rasd:StorageId" [] [PCData !esd.uuid];
+ e "rasd:StoragePoolId" [] [PCData "00000000-0000-0000-0000-000000000000"];
+ e "rasd:CreationDate" [] [PCData iso_time];
+ e "rasd:LastModified" [] [PCData iso_time];
+ e "rasd:last_modified_date" [] [PCData iso_time];
+ ] in
+ append_child item virtualhardware_section;
+ ) overlays
+
+and du_m filename =
+ (* There's no OCaml binding for st_blocks, so run coreutils 'du -m'
+ * to get the used size in megabytes.
+ *)
+ let cmd = sprintf "du -m %s | awk '{print $1}'" (quote filename) in
+ let lines = external_command ~prog cmd in
+ (* We really don't want the metadata generation to fail because
+ * of some silly usage information, so ignore errors here.
+ *)
+ match lines with
+ | line::_ -> (try Int64.of_string line with _ -> 0L)
+ | [] -> 0L
diff --git a/v2v/target_RHEV.mli b/v2v/target_RHEV.mli
new file mode 100644
index 0000000..6cc5c76
--- /dev/null
+++ b/v2v/target_RHEV.mli
@@ -0,0 +1,24 @@
+(* 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 : verbose:bool -> string -> Types.source ->
+ [`Sparse | `Preallocated] -> Types.overlay list -> Types.overlay list
+
+val create_metadata : string -> [`Server | `Desktop] option -> Types.source ->
+ [`Sparse | `Preallocated] -> Types.overlay list -> Types.inspect ->
+ Types.guestcaps -> unit
diff --git a/v2v/types.ml b/v2v/types.ml
index 849ad0f..05aafb1 100644
--- a/v2v/types.ml
+++ b/v2v/types.ml
@@ -27,7 +27,7 @@ type input =
type output =
| OutputLibvirt of string option
| OutputLocal of string
-| OutputRHEV of string
+| OutputRHEV of string * [`Server|`Desktop] option
type source = {
s_dom_type : string;
@@ -83,6 +83,7 @@ type overlay = {
ov_preallocation : string option;
ov_source_file : string;
ov_source_format : string option;
+ ov_vol_uuid : string;
}
let string_of_overlay ov =
@@ -96,6 +97,7 @@ ov_virtual_size = %Ld
ov_preallocation = %s
ov_source_file = %s
ov_source_format = %s
+ov_vol_uuid = %s
"
ov.ov_overlay
ov.ov_target_file ov.ov_target_file_tmp
@@ -105,6 +107,7 @@ ov_source_format = %s
(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)
+ ov.ov_vol_uuid
type inspect = {
i_root : string;
diff --git a/v2v/types.mli b/v2v/types.mli
index c8ef5c3..0415a27 100644
--- a/v2v/types.mli
+++ b/v2v/types.mli
@@ -26,7 +26,7 @@ type input =
type output =
| OutputLibvirt of string option (* -o libvirt: -oc *)
| OutputLocal of string (* -o local: directory *)
-| OutputRHEV of string (* -o rhev: output storage *)
+| OutputRHEV of string * [`Server|`Desktop] option (* -o rhev: output storage *)
(** The output arguments as specified on the command line. *)
type source = {
@@ -62,8 +62,11 @@ type overlay = {
(* 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_file : string; (** qemu URI for source file. *)
ov_source_format : string option; (** Source file format, if known. *)
+
+ (* Only used by RHEV. XXX Should be parameterized type. *)
+ ov_vol_uuid : string; (** RHEV volume UUID *)
}
(** Disk overlays and destination disks. *)
diff --git a/v2v/utils.ml b/v2v/utils.ml
index b58c18d..f065a66 100644
--- a/v2v/utils.ml
+++ b/v2v/utils.ml
@@ -40,6 +40,12 @@ let xml_quote_attr str =
let str = Common_utils.replace_str str ">" ">" in
str
+let xml_quote_pcdata 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
+ str
+
external drive_name : int -> string = "v2v_utils_drive_name"
let compare_app2_versions app1 app2 =
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
index 4dca2fe..6f6ee93 100644
--- a/v2v/v2v.ml
+++ b/v2v/v2v.ml
@@ -93,7 +93,7 @@ let rec main () =
* work.
*)
let overlays =
- initialize_target g
+ initialize_target ~verbose g
source output output_alloc output_format output_name overlays in
(* Inspection - this also mounts up the filesystems. *)
@@ -197,7 +197,9 @@ let rec main () =
| OutputLibvirt oc -> assert false
| OutputLocal dir ->
Target_local.create_metadata dir renamed_source overlays guestcaps
- | OutputRHEV os -> assert false in
+ | OutputRHEV (os, vmtype) ->
+ Target_RHEV.create_metadata os vmtype renamed_source output_alloc
+ overlays inspect guestcaps in
(* If we wrote to a temporary file, rename to the real file. *)
List.iter (
@@ -213,7 +215,7 @@ let rec main () =
if debug_gc then
Gc.compact ()
-and initialize_target g
+and initialize_target ~verbose g
source output output_alloc output_format output_name overlays =
let overlays =
mapi (
@@ -244,7 +246,8 @@ and initialize_target g
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; }
+ ov_source_file = qemu_uri; ov_source_format = backing_format;
+ ov_vol_uuid = "" }
) overlays in
let overlays =
let renamed_source =
@@ -254,7 +257,8 @@ and initialize_target g
match output with
| OutputLibvirt oc -> assert false
| OutputLocal dir -> Target_local.initialize dir renamed_source overlays
- | OutputRHEV os -> assert false in
+ | OutputRHEV (os, _) ->
+ Target_RHEV.initialize ~verbose os renamed_source output_alloc overlays in
overlays
and inspect_source g root_choice =
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
index 48fa870..084e4b6 100644
--- a/v2v/virt-v2v.pod
+++ b/v2v/virt-v2v.pod
@@ -132,14 +132,22 @@ 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:
+For I<-o rhev>, this can be an NFS path of the Export Storage Domain
+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.
+when it runs. So you probably have to run virt-v2v as C<root>.
+
+B<Or:> You can mount the Export Storage Domain yourself, and point
+I<-os> to the mountpoint. Note that virt-v2v will still need to write
+to this remote directory, so virt-v2v will still need to run as
+C<root>.
+
+You will get an error if virt-v2v is unable to mount/write to the
+Export Storage Domain.
=item B<-q>
@@ -202,6 +210,13 @@ Enable verbose messages for debugging.
Display version number and exit.
+=item B<--vmtype> server|desktop
+
+For the RHEV target only, specify the type of guest. You can set this
+to C<server> or C<desktop>. If the option is not given, then a
+suitable default is chosen based on the detected guest operating
+system.
+
=item B<-x>
Enable tracing of libguestfs API calls.
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-libvirt/libguestfs.git
More information about the Pkg-libvirt-commits
mailing list