diff --git a/v2v/test-harness/v2v_test_harness.ml b/v2v/test-harness/v2v_test_harness.ml index efbda7b28..f10ff983f 100644 --- a/v2v/test-harness/v2v_test_harness.ml +++ b/v2v/test-harness/v2v_test_harness.ml @@ -26,6 +26,7 @@ open Printf open Common_utils type test_plan = { + guest_clock : float option; post_conversion_test : (Guestfs.guestfs -> string -> Xml.doc -> unit) option; boot_plan : boot_plan; @@ -42,7 +43,15 @@ and boot_plan = | Boot_to_idle | Boot_to_screenshot of string +let new_year's_day year = + let tm = { tm_sec = 0; tm_min = 0; tm_hour = 0; + tm_mday = 1; tm_mon = 0; tm_year = year - 1900; + tm_wday = 0; tm_yday = 0; tm_isdst = false } in + let t, _ = mktime tm in + Some t + let default_plan = { + guest_clock = None; post_conversion_test = None; boot_plan = Boot_to_idle; boot_wait_to_write = 120; @@ -119,6 +128,32 @@ let run ~test ?input_disk ?input_xml ?(test_plan = default_plan) () = Xml.node_set_content node "2097152" ) nodes; + (* Adjust the if requested. *) + (match test_plan.guest_clock with + | None -> () + | Some t -> + 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 clock_node = + match nodes with + | [] -> + (* No element, so insert one. *) + let root (* the element *) = + match Xml.doc_get_root_element boot_xml_doc with + | None -> assert false + | Some root -> root in + Xml.new_text_child root "clock" "" + | [clock_node] -> clock_node + | _ -> + failwith "multiple elements found" in + Xml.set_prop clock_node "offset" "variable"; + Xml.set_prop clock_node "basis" "localtime"; + Xml.set_prop clock_node "adjustment" (string_of_int adjustment) + ); + (* 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 diff --git a/v2v/test-harness/v2v_test_harness.mli b/v2v/test-harness/v2v_test_harness.mli index f8b3f33f4..739b8192e 100644 --- a/v2v/test-harness/v2v_test_harness.mli +++ b/v2v/test-harness/v2v_test_harness.mli @@ -23,6 +23,10 @@ *) type test_plan = { + guest_clock : float option; + (** If not [None], set the guest clock to the specific time. + See below for a list of convenient times. *) + post_conversion_test : (Guestfs.guestfs -> string -> Xml.doc -> unit) option; (** Arbitrary test that can be run after conversion. *) @@ -58,6 +62,10 @@ and boot_plan = | Boot_to_idle (** Boot until VM is idle. *) | Boot_to_screenshot of string (** Boot until screenshot (subimage) is displayed. *) +val new_year's_day : int -> float option +(** Set [test_plan.guest_clock = new_year's_day YYYY] to boot the guest with + its clock set to midnight on YYYY-01-01. *) + val default_plan : test_plan val run : test:string -> ?input_disk:string -> ?input_xml:string -> ?test_plan:test_plan -> unit -> unit diff --git a/v2v/xml-c.c b/v2v/xml-c.c index d2d895cba..d4f11c551 100644 --- a/v2v/xml-c.c +++ b/v2v/xml-c.c @@ -320,6 +320,25 @@ v2v_xml_node_ptr_set_content (value nodev, value contentv) CAMLreturn (Val_unit); } +value +v2v_xml_node_ptr_new_text_child (value nodev, value namev, value contentv) +{ + CAMLparam3 (nodev, namev, contentv); + xmlNodePtr node = (xmlNodePtr) nodev; + xmlNodePtr new_node; + + new_node = xmlNewTextChild (node, NULL, + BAD_CAST String_val (namev), + BAD_CAST String_val (contentv)); + if (new_node == NULL) + caml_invalid_argument ("node_ptr_new_text_child: failed to create new node"); + + /* See comment in v2v_xml_xpathobj_ptr_get_node_ptr about returning + * named xmlNodePtr here. + */ + CAMLreturn ((value) new_node); +} + value v2v_xml_node_ptr_set_prop (value nodev, value namev, value valv) { @@ -345,6 +364,24 @@ v2v_xml_node_ptr_unlink_node (value nodev) CAMLreturn (Val_unit); } +value +v2v_xml_doc_get_root_element (value docv) +{ + CAMLparam1 (docv); + CAMLlocal1 (v); + xmlDocPtr doc = Doc_val (docv); + xmlNodePtr root; + + root = xmlDocGetRootElement (doc); + if (root == NULL) + CAMLreturn (Val_int (0)); /* None */ + else { + v = caml_alloc (1, 0); + Store_field (v, 0, (value) root); + CAMLreturn (v); /* Some node_ptr */ + } +} + value v2v_xml_parse_uri (value strv) { diff --git a/v2v/xml.ml b/v2v/xml.ml index 037bce93d..dbb2b4163 100644 --- a/v2v/xml.ml +++ b/v2v/xml.ml @@ -88,12 +88,22 @@ let node_as_string (doc_ptr, node_ptr) = node_ptr_as_string doc_ptr node_ptr external node_ptr_set_content : node_ptr -> string -> unit = "v2v_xml_node_ptr_set_content" let node_set_content (doc_ptr, node_ptr) = node_ptr_set_content node_ptr +external node_ptr_new_text_child : node_ptr -> string -> string -> node_ptr = "v2v_xml_node_ptr_new_text_child" +let new_text_child (doc_ptr, node_ptr) name content = + doc_ptr, node_ptr_new_text_child node_ptr name content + external node_ptr_set_prop : node_ptr -> string -> string -> unit = "v2v_xml_node_ptr_set_prop" let set_prop (doc_ptr, node_ptr) = node_ptr_set_prop node_ptr external node_ptr_unlink_node : node_ptr -> unit = "v2v_xml_node_ptr_unlink_node" let unlink_node (doc_ptr, node_ptr) = node_ptr_unlink_node node_ptr +external _doc_get_root_element : doc_ptr -> node_ptr option = "v2v_xml_doc_get_root_element" +let doc_get_root_element doc_ptr = + match _doc_get_root_element doc_ptr with + | None -> None + | Some node_ptr -> Some (doc_ptr, node_ptr) + type uri = { uri_scheme : string option; uri_opaque : string option; diff --git a/v2v/xml.mli b/v2v/xml.mli index a3a9c01da..b039d0935 100644 --- a/v2v/xml.mli +++ b/v2v/xml.mli @@ -67,6 +67,9 @@ val node_as_string : node -> string val node_set_content : node -> string -> unit (** xmlNodeSetContent *) +val new_text_child : node -> string -> string -> node +(** xmlNewTextChild *) + val set_prop : node -> string -> string -> unit (** xmlSetProp *) @@ -74,6 +77,9 @@ val unlink_node : node -> unit (** xmlUnlinkNode {b NB:} This frees the [node], do not use it afterwards. *) +val doc_get_root_element : doc -> node option +(** xmlDocGetRootElement *) + type uri = { uri_scheme : string option; uri_opaque : string option;