mrc0mmand / rpms / libguestfs

Forked from rpms/libguestfs 3 years ago
Clone

Blame SOURCES/0028-v2v-o-libvirt-Get-the-features-right-in-the-output-X.patch

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