[Libguestfs] [PATCH v11 7/8] mllib: add XPath helper xpath_get_nodes

Cédric Bosdonnat cbosdonnat at suse.com
Fri Oct 27 14:08:21 UTC 2017


This function will allow more OCaml-ish processing of XPath queries
with multiple results.
---
 common/mltools/xpath_helpers.ml      |  9 +++++++
 common/mltools/xpath_helpers.mli     |  4 +++
 v2v/output_libvirt.ml                | 11 ++------
 v2v/test-harness/v2v_test_harness.ml | 51 +++++++++++-------------------------
 4 files changed, 30 insertions(+), 45 deletions(-)

diff --git a/common/mltools/xpath_helpers.ml b/common/mltools/xpath_helpers.ml
index 3afee8b21..d2bfd3fb9 100644
--- a/common/mltools/xpath_helpers.ml
+++ b/common/mltools/xpath_helpers.ml
@@ -40,3 +40,12 @@ let xpath_eval parsefn xpathctx expr =
 let xpath_string = xpath_eval identity
 let xpath_int = xpath_eval int_of_string
 let xpath_int64 = xpath_eval Int64.of_string
+
+let xpath_get_nodes xpathctx expr =
+  let obj = Xml.xpath_eval_expression xpathctx expr in
+  let nodes = ref [] in
+  for i = 0 to Xml.xpathobj_nr_nodes obj - 1 do
+    let node = Xml.xpathobj_node obj i in
+    push_front node nodes
+  done;
+  List.rev !nodes
diff --git a/common/mltools/xpath_helpers.mli b/common/mltools/xpath_helpers.mli
index 3a8190b05..3a2607aeb 100644
--- a/common/mltools/xpath_helpers.mli
+++ b/common/mltools/xpath_helpers.mli
@@ -25,3 +25,7 @@ val xpath_int : Xml.xpathctx -> string -> int option
 val xpath_int64 : Xml.xpathctx -> string -> int64 option
 (** Parse an xpath expression and return a string/int.  Returns
     [Some v], or [None] if the expression doesn't match. *)
+
+val xpath_get_nodes : Xml.xpathctx -> string -> Xml.node list
+(** Parse an XPath expression and return a list with the matching
+    XML nodes. *)
diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml
index 02b4d54ff..729f8b67a 100644
--- a/v2v/output_libvirt.ml
+++ b/v2v/output_libvirt.ml
@@ -55,15 +55,8 @@ let target_features_of_capabilities_doc doc arch =
     Xml.xpathctx_set_current_context xpathctx node;
 
     (* Get guest/features/* nodes. *)
-    let obj = Xml.xpath_eval_expression xpathctx "features/*" in
-
-    let features = ref [] in
-    for i = 0 to Xml.xpathobj_nr_nodes obj - 1 do
-      let feature_node = Xml.xpathobj_node obj i in
-      let feature_name = Xml.node_name feature_node in
-      push_front feature_name features
-    done;
-    !features
+    let features = xpath_get_nodes xpathctx "features/*" in
+    List.map Xml.node_name features
   )
 
 class output_libvirt oc output_pool = object
diff --git a/v2v/test-harness/v2v_test_harness.ml b/v2v/test-harness/v2v_test_harness.ml
index ae0033dde..79e97a4b2 100644
--- a/v2v/test-harness/v2v_test_harness.ml
+++ b/v2v/test-harness/v2v_test_harness.ml
@@ -25,6 +25,7 @@ open Printf
 
 open Std_utils
 open Tools_utils
+open Xpath_helpers
 
 type test_plan = {
   guest_clock : float option;
@@ -90,29 +91,18 @@ let run ~test ?input_disk ?input_xml ?(test_plan = default_plan) () =
     g, root
   in
 
-  let nodes_of_xpathobj doc xpathobj =
-    let nodes = ref [] in
-    for i = 0 to Xml.xpathobj_nr_nodes xpathobj - 1 do
-      push_front (Xml.xpathobj_node xpathobj i) nodes
-    done;
-    List.rev !nodes
-  in
-
   let test_boot boot_disk boot_xml_doc =
     (* Modify boot XML (in memory). *)
     let xpathctx = Xml.xpath_new_context boot_xml_doc in
 
     (* Change <name> to something unique. *)
     let domname = "tmpv2v-" ^ test in
-    let xpath = Xml.xpath_eval_expression xpathctx "/domain/name" in
-    let nodes = nodes_of_xpathobj boot_xml_doc xpath in
+    let nodes = xpath_get_nodes xpathctx "/domain/name" in
     List.iter (fun node -> Xml.node_set_content node domname) nodes;
 
     (* Limit the RAM used by the guest to 2GB. *)
-    let xpath = Xml.xpath_eval_expression xpathctx "/domain/memory" in
-    let nodes = nodes_of_xpathobj boot_xml_doc xpath in
-    let xpath = Xml.xpath_eval_expression xpathctx "/domain/currentMemory" in
-    let nodes = nodes @ nodes_of_xpathobj boot_xml_doc xpath in
+    let nodes = xpath_get_nodes xpathctx "/domain/memory" in
+    let nodes = nodes @ xpath_get_nodes xpathctx "/domain/currentMemory" in
     List.iter (
       fun node ->
         let i = int_of_string (Xml.node_as_string node) in
@@ -127,8 +117,7 @@ let run ~test ?input_disk ?input_xml ?(test_plan = default_plan) () =
         let adjustment = t -. time () in
         assert (adjustment <= 0.);
         let adjustment = int_of_float adjustment in
-        let xpath = Xml.xpath_eval_expression xpathctx "/domain/clock" in
-        let nodes = nodes_of_xpathobj boot_xml_doc xpath in
+        let nodes = xpath_get_nodes xpathctx "/domain/clock" in
         let clock_node =
           match nodes with
           | [] ->
@@ -147,8 +136,7 @@ let run ~test ?input_disk ?input_xml ?(test_plan = default_plan) () =
     );
 
     (* Remove all devices except for a whitelist. *)
-    let xpath = Xml.xpath_eval_expression xpathctx "/domain/devices/*" in
-    let nodes = nodes_of_xpathobj boot_xml_doc xpath in
+    let nodes = xpath_get_nodes xpathctx "/domain/devices/*" in
     List.iter (
       fun node ->
         match Xml.node_name node with
@@ -157,33 +145,26 @@ let run ~test ?input_disk ?input_xml ?(test_plan = default_plan) () =
     ) nodes;
 
     (* Remove CDROMs. *)
-    let xpath =
-      Xml.xpath_eval_expression xpathctx
-        "/domain/devices/disk[@device=\"cdrom\"]" in
-    let nodes = nodes_of_xpathobj boot_xml_doc xpath in
+    let nodes = xpath_get_nodes xpathctx
+      "/domain/devices/disk[@device=\"cdrom\"]" in
     List.iter Xml.unlink_node nodes;
 
     (* Change <on_*> settings to destroy ... *)
-    let xpath = Xml.xpath_eval_expression xpathctx "/domain/on_poweroff" in
-    let nodes = nodes_of_xpathobj boot_xml_doc xpath in
-    let xpath = Xml.xpath_eval_expression xpathctx "/domain/on_crash" in
-    let nodes = nodes @ nodes_of_xpathobj boot_xml_doc xpath in
+    let nodes = xpath_get_nodes xpathctx "/domain/on_poweroff" in
+    let nodes = nodes @ xpath_get_nodes xpathctx "/domain/on_crash" in
     List.iter (fun node -> Xml.node_set_content node "destroy") nodes;
     (* ... except for <on_reboot> which is permitted (for SELinux
      * relabelling)
      *)
-    let xpath = Xml.xpath_eval_expression xpathctx "/domain/on_reboot" in
-    let nodes = nodes_of_xpathobj boot_xml_doc xpath in
+    let nodes = xpath_get_nodes xpathctx "/domain/on_reboot" in
     List.iter (fun node -> Xml.node_set_content node "restart") nodes;
 
     (* Get the name of the disk device (eg. "sda"), which is used
      * for getting disk stats.
      *)
-    let xpath =
-      Xml.xpath_eval_expression xpathctx
-        "/domain/devices/disk[@device=\"disk\"]/target/@dev" in
     let dev =
-      match nodes_of_xpathobj boot_xml_doc xpath with
+      match xpath_get_nodes xpathctx
+        "/domain/devices/disk[@device=\"disk\"]/target/@dev" with
       | [node] -> Xml.node_as_string node
       | _ -> assert false in
 
@@ -523,10 +504,8 @@ let run ~test ?input_disk ?input_xml ?(test_plan = default_plan) () =
     (* We need to remember to change the XML to point to the boot overlay. *)
     let () =
       let xpathctx = Xml.xpath_new_context boot_xml_doc in
-      let xpath =
-        Xml.xpath_eval_expression xpathctx
-          "/domain/devices/disk[@device=\"disk\"]/source" in
-      match nodes_of_xpathobj boot_xml_doc xpath with
+      match xpath_get_nodes xpathctx
+        "/domain/devices/disk[@device=\"disk\"]/source" with
       | [node] ->
         (* Libvirt requires that the path is absolute. *)
         let abs_boot_disk = Sys.getcwd () // boot_disk in
-- 
2.13.2




More information about the Libguestfs mailing list