|
|
0d20ef |
From 97ad621955e5be3ef3f8bbfc373f31b0027e7d6c Mon Sep 17 00:00:00 2001
|
|
|
0d20ef |
From: "Richard W.M. Jones" <rjones@redhat.com>
|
|
|
0d20ef |
Date: Fri, 31 Oct 2014 14:16:36 +0000
|
|
|
0d20ef |
Subject: [PATCH] v2v: -o libvirt: Get the <features/> right in the output XML
|
|
|
0d20ef |
(RHBZ#1159258).
|
|
|
0d20ef |
|
|
|
0d20ef |
Implement what old virt-v2v did (from
|
|
|
0d20ef |
lib/Sys/VirtConvert/Connection/LibVirtTarget.pm)
|
|
|
0d20ef |
|
|
|
0d20ef |
Thanks: Tingting Zheng, Matthew Booth
|
|
|
0d20ef |
(cherry picked from commit 68dc488a4476caf742d5342307258dd72d0e2256)
|
|
|
0d20ef |
---
|
|
|
0d20ef |
v2v/output_libvirt.ml | 113 ++++++++++++++++++++++++++++++++++++++++++++++---
|
|
|
0d20ef |
v2v/output_libvirt.mli | 2 +-
|
|
|
0d20ef |
v2v/output_local.ml | 13 +++++-
|
|
|
0d20ef |
v2v/test-v2v-i-ova.xml | 5 ++-
|
|
|
0d20ef |
4 files changed, 123 insertions(+), 10 deletions(-)
|
|
|
0d20ef |
|
|
|
0d20ef |
diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml
|
|
|
0d20ef |
index 59a390f..386d777 100644
|
|
|
0d20ef |
--- a/v2v/output_libvirt.ml
|
|
|
0d20ef |
+++ b/v2v/output_libvirt.ml
|
|
|
0d20ef |
@@ -25,6 +25,42 @@ open Types
|
|
|
0d20ef |
open Utils
|
|
|
0d20ef |
open DOM
|
|
|
0d20ef |
|
|
|
0d20ef |
+module StringSet = Set.Make (String)
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+let string_set_of_list =
|
|
|
0d20ef |
+ List.fold_left (fun set x -> StringSet.add x set) StringSet.empty
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+let target_features_of_capabilities_doc doc arch =
|
|
|
0d20ef |
+ let xpathctx = Xml.xpath_new_context doc in
|
|
|
0d20ef |
+ let expr =
|
|
|
0d20ef |
+ (* NB: Pay attention to the square brackets. This returns the
|
|
|
0d20ef |
+ * <guest> nodes!
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ sprintf "/capabilities/guest[arch[@name='%s']/domain/@type='kvm']" arch in
|
|
|
0d20ef |
+ let obj = Xml.xpath_eval_expression xpathctx expr in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ if Xml.xpathobj_nr_nodes obj < 1 then (
|
|
|
0d20ef |
+ (* Old virt-v2v used to die here, but that seems unfair since the
|
|
|
0d20ef |
+ * user has gone through conversion before we reach here.
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ warning ~prog (f_"the target hypervisor does not support a %s KVM guest") arch;
|
|
|
0d20ef |
+ []
|
|
|
0d20ef |
+ ) else (
|
|
|
0d20ef |
+ let node (* first matching <guest> *) = Xml.xpathobj_node doc obj 0 in
|
|
|
0d20ef |
+ Xml.xpathctx_set_current_context xpathctx node;
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ (* Get guest/features/* nodes. *)
|
|
|
0d20ef |
+ let obj = Xml.xpath_eval_expression xpathctx "features/*" in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ let features = ref [] in
|
|
|
0d20ef |
+ for i = 0 to Xml.xpathobj_nr_nodes obj - 1 do
|
|
|
0d20ef |
+ let feature_node = Xml.xpathobj_node doc obj i in
|
|
|
0d20ef |
+ let feature_name = Xml.node_name feature_node in
|
|
|
0d20ef |
+ features := feature_name :: !features
|
|
|
0d20ef |
+ done;
|
|
|
0d20ef |
+ !features
|
|
|
0d20ef |
+ )
|
|
|
0d20ef |
+
|
|
|
0d20ef |
let append_child child = function
|
|
|
0d20ef |
| PCData _ | Comment _ -> assert false
|
|
|
0d20ef |
| Element e -> e.e_children <- e.e_children @ [child]
|
|
|
0d20ef |
@@ -33,15 +69,43 @@ let append_attr attr = function
|
|
|
0d20ef |
| PCData _ | Comment _ -> assert false
|
|
|
0d20ef |
| Element e -> e.e_attrs <- e.e_attrs @ [attr]
|
|
|
0d20ef |
|
|
|
0d20ef |
-let create_libvirt_xml ?pool source targets guestcaps =
|
|
|
0d20ef |
+let create_libvirt_xml ?pool source targets guestcaps target_features =
|
|
|
0d20ef |
let memory_k = source.s_memory /^ 1024L in
|
|
|
0d20ef |
|
|
|
0d20ef |
+ (* We have the machine features of the guest when it was on the
|
|
|
0d20ef |
+ * source hypervisor (source.s_features). We have the acpi flag
|
|
|
0d20ef |
+ * which tells us whether acpi is required by this guest
|
|
|
0d20ef |
+ * (guestcaps.gcaps_acpi). And we have the set of hypervisor
|
|
|
0d20ef |
+ * features supported by the target (target_features). Combine all
|
|
|
0d20ef |
+ * this into a final list of features.
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ let features = string_set_of_list source.s_features in
|
|
|
0d20ef |
+ let target_features = string_set_of_list target_features in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ (* If the guest supports ACPI, add it to the output XML. Conversely
|
|
|
0d20ef |
+ * if the guest does not support ACPI, then we must drop it.
|
|
|
0d20ef |
+ * (RHBZ#1159258)
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
let features =
|
|
|
0d20ef |
- List.filter (
|
|
|
0d20ef |
- fun feature ->
|
|
|
0d20ef |
- (* drop acpi if the guest doesn't support it *)
|
|
|
0d20ef |
- feature <> "acpi" || guestcaps.gcaps_acpi
|
|
|
0d20ef |
- ) source.s_features in
|
|
|
0d20ef |
+ if guestcaps.gcaps_acpi then
|
|
|
0d20ef |
+ StringSet.add "acpi" features
|
|
|
0d20ef |
+ else
|
|
|
0d20ef |
+ StringSet.remove "acpi" features in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ (* Make sure we don't add any features which are not supported by
|
|
|
0d20ef |
+ * the target hypervisor.
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ let features = StringSet.inter(*section*) features target_features in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ (* But if the target supports apic or pae then we should add them
|
|
|
0d20ef |
+ * anyway (old virt-v2v did this).
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ let force_features = string_set_of_list ["apic"; "pae"] in
|
|
|
0d20ef |
+ let force_features =
|
|
|
0d20ef |
+ StringSet.inter(*section*) force_features target_features in
|
|
|
0d20ef |
+ let features = StringSet.union features force_features in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ let features = List.sort compare (StringSet.elements features) in
|
|
|
0d20ef |
|
|
|
0d20ef |
let disks =
|
|
|
0d20ef |
let block_prefix =
|
|
|
0d20ef |
@@ -202,12 +266,36 @@ let create_libvirt_xml ?pool source targets guestcaps =
|
|
|
0d20ef |
class output_libvirt verbose oc output_pool = object
|
|
|
0d20ef |
inherit output verbose
|
|
|
0d20ef |
|
|
|
0d20ef |
+ val mutable capabilities_doc = None
|
|
|
0d20ef |
+
|
|
|
0d20ef |
method as_options =
|
|
|
0d20ef |
match oc with
|
|
|
0d20ef |
| None -> sprintf "-o libvirt -os %s" output_pool
|
|
|
0d20ef |
| Some uri -> sprintf "-o libvirt -oc %s -os %s" uri output_pool
|
|
|
0d20ef |
|
|
|
0d20ef |
method prepare_targets source targets =
|
|
|
0d20ef |
+ (* Get the capabilities from libvirt. *)
|
|
|
0d20ef |
+ let cmd =
|
|
|
0d20ef |
+ match oc with
|
|
|
0d20ef |
+ | None -> "virsh capabilities"
|
|
|
0d20ef |
+ | Some uri -> sprintf "virsh -c %s capabilities" (quote uri) in
|
|
|
0d20ef |
+ if verbose then printf "%s\n%!" cmd;
|
|
|
0d20ef |
+ let xml = external_command ~prog cmd in
|
|
|
0d20ef |
+ let xml = String.concat "\n" xml in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ if verbose then printf "libvirt capabilities XML:\n%s\n%!" xml;
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ (* This just checks that the capabilities XML is well-formed,
|
|
|
0d20ef |
+ * early so that we catch parsing errors before conversion.
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ let doc = Xml.parse_memory xml in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ (* Stash the capabilities XML, since we cannot get the bits we
|
|
|
0d20ef |
+ * need from it until we know the guest architecture, which happens
|
|
|
0d20ef |
+ * after conversion.
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ capabilities_doc <- Some doc;
|
|
|
0d20ef |
+
|
|
|
0d20ef |
(* Connect to output libvirt instance and check that the pool exists
|
|
|
0d20ef |
* and dump out its XML.
|
|
|
0d20ef |
*)
|
|
|
0d20ef |
@@ -250,11 +338,22 @@ class output_libvirt verbose oc output_pool = object
|
|
|
0d20ef |
| Some uri ->
|
|
|
0d20ef |
sprintf "virsh -c %s pool-refresh %s"
|
|
|
0d20ef |
(quote uri) (quote output_pool) in
|
|
|
0d20ef |
+ if verbose then printf "%s\n%!" cmd;
|
|
|
0d20ef |
if Sys.command cmd <> 0 then
|
|
|
0d20ef |
warning ~prog (f_"could not refresh libvirt pool %s") output_pool;
|
|
|
0d20ef |
|
|
|
0d20ef |
+ (* Parse the capabilities XML in order to get the supported features. *)
|
|
|
0d20ef |
+ let doc =
|
|
|
0d20ef |
+ match capabilities_doc with
|
|
|
0d20ef |
+ | None -> assert false
|
|
|
0d20ef |
+ | Some doc -> doc in
|
|
|
0d20ef |
+ let target_features =
|
|
|
0d20ef |
+ target_features_of_capabilities_doc doc guestcaps.gcaps_arch in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
(* Create the metadata. *)
|
|
|
0d20ef |
- let doc = create_libvirt_xml ~pool:output_pool source targets guestcaps in
|
|
|
0d20ef |
+ let doc =
|
|
|
0d20ef |
+ create_libvirt_xml ~pool:output_pool source targets
|
|
|
0d20ef |
+ guestcaps target_features in
|
|
|
0d20ef |
|
|
|
0d20ef |
let tmpfile, chan = Filename.open_temp_file "v2vlibvirt" ".xml" in
|
|
|
0d20ef |
DOM.doc_to_chan chan doc;
|
|
|
0d20ef |
diff --git a/v2v/output_libvirt.mli b/v2v/output_libvirt.mli
|
|
|
0d20ef |
index 25d4690..da41956 100644
|
|
|
0d20ef |
--- a/v2v/output_libvirt.mli
|
|
|
0d20ef |
+++ b/v2v/output_libvirt.mli
|
|
|
0d20ef |
@@ -23,5 +23,5 @@ val output_libvirt : bool -> string option -> string -> Types.output
|
|
|
0d20ef |
{!Types.output} object specialized for writing output to
|
|
|
0d20ef |
libvirt. *)
|
|
|
0d20ef |
|
|
|
0d20ef |
-val create_libvirt_xml : ?pool:string -> Types.source -> Types.target list -> Types.guestcaps -> DOM.doc
|
|
|
0d20ef |
+val create_libvirt_xml : ?pool:string -> Types.source -> Types.target list -> Types.guestcaps -> string list -> DOM.doc
|
|
|
0d20ef |
(** This is called from {!Output_local} to generate the libvirt XML. *)
|
|
|
0d20ef |
diff --git a/v2v/output_local.ml b/v2v/output_local.ml
|
|
|
0d20ef |
index db36f0e..ffcfad0 100644
|
|
|
0d20ef |
--- a/v2v/output_local.ml
|
|
|
0d20ef |
+++ b/v2v/output_local.ml
|
|
|
0d20ef |
@@ -37,7 +37,18 @@ class output_local verbose dir = object
|
|
|
0d20ef |
) targets
|
|
|
0d20ef |
|
|
|
0d20ef |
method create_metadata source targets guestcaps _ =
|
|
|
0d20ef |
- let doc = Output_libvirt.create_libvirt_xml source targets guestcaps in
|
|
|
0d20ef |
+ (* We don't know what target features the hypervisor supports, but
|
|
|
0d20ef |
+ * assume a common set that libvirt supports.
|
|
|
0d20ef |
+ *)
|
|
|
0d20ef |
+ let target_features =
|
|
|
0d20ef |
+ match guestcaps.gcaps_arch with
|
|
|
0d20ef |
+ | "i686" -> [ "acpi"; "apic"; "pae" ]
|
|
|
0d20ef |
+ | "x86_64" -> [ "acpi"; "apic" ]
|
|
|
0d20ef |
+ | _ -> [] in
|
|
|
0d20ef |
+
|
|
|
0d20ef |
+ let doc =
|
|
|
0d20ef |
+ Output_libvirt.create_libvirt_xml source targets
|
|
|
0d20ef |
+ guestcaps target_features in
|
|
|
0d20ef |
|
|
|
0d20ef |
let name = source.s_name in
|
|
|
0d20ef |
let file = dir // name ^ ".xml" in
|
|
|
0d20ef |
diff --git a/v2v/test-v2v-i-ova.xml b/v2v/test-v2v-i-ova.xml
|
|
|
0d20ef |
index ff83285..2d611f9 100644
|
|
|
0d20ef |
--- a/v2v/test-v2v-i-ova.xml
|
|
|
0d20ef |
+++ b/v2v/test-v2v-i-ova.xml
|
|
|
0d20ef |
@@ -7,7 +7,10 @@
|
|
|
0d20ef |
<os>
|
|
|
0d20ef |
<type arch='x86_64'>hvm</type>
|
|
|
0d20ef |
</os>
|
|
|
0d20ef |
- <features/>
|
|
|
0d20ef |
+ <features>
|
|
|
0d20ef |
+ <acpi/>
|
|
|
0d20ef |
+ <apic/>
|
|
|
0d20ef |
+ </features>
|
|
|
0d20ef |
<on_poweroff>destroy</on_poweroff>
|
|
|
0d20ef |
<on_reboot>restart</on_reboot>
|
|
|
0d20ef |
<on_crash>restart</on_crash>
|
|
|
0d20ef |
--
|
|
|
0d20ef |
1.8.3.1
|
|
|
0d20ef |
|