diff --git a/ocaml/tests/test_guest_agent.ml b/ocaml/tests/test_guest_agent.ml index 6d9c7d8f40b..6b74aa55544 100644 --- a/ocaml/tests/test_guest_agent.ml +++ b/ocaml/tests/test_guest_agent.ml @@ -468,9 +468,93 @@ module Initial_guest_metrics = Generic.MakeStateless (struct ] end) +module Services = Generic.MakeStateless (struct + module Io = struct + type input_t = (string * string) list + + type output_t = (string * string) list + + let string_of_input_t = Test_printers.(assoc_list string string) + + let string_of_output_t = Test_printers.(assoc_list string string) + end + + (* prototype funtions lookup and list are in Xapi_xenops.ml::update_vm *) + let lookup state key = List.assoc_opt key state + + let list_subkeys state dir = + if dir = "" then + [] + else + let dir = + if dir.[0] = '/' then + String.sub dir 1 (String.length dir - 1) + else + dir + in + let results = + List.filter_map + (fun (path, _) -> + if String.starts_with ~prefix:dir path then + let rest = + String.sub path (String.length dir) + (String.length path - String.length dir) + in + let is_sep = function '/' -> true | _ -> false in + match Astring.String.fields ~empty:false ~is_sep rest with + | x :: _ -> + Some x + | _ -> + None + else + None + ) + state + |> Xapi_stdext_std.Listext.List.setify + in + results + + let transform input = + Xapi_guest_agent.get_guest_services (lookup input) (list_subkeys input) + + let tests = + `QuickAndAutoDocumented + [ + (* no data/service *) + ([("data/key1", "v1"); ("data/key2", "v2")], []) + ; (* less than two depth in data/service *) + ([("data/service/key1", "v1"); ("data/service/key2", "v2")], []) + ; (* beyond two depth in data/service *) + ( [ + ("data/service/service-a/sub/key1", "sab-v1") + ; ("data/service/service-a/sub/key2", "sab-v2") + ] + , [("service-a/sub", "")] + ) + ; (* normal case *) + ( [ + ("data/service", "") + ; ("data/service/service-a", "") + ; ("data/service/service-b", "") + ; ("data/service/service-a/key1", "sa-v1") + ; ("data/service/service-a/key2", "sa-v2") + ; ("data/service/service-b/key1", "sb-v1") + ; ("data/service/service-b/key2", "sb-v2") + ] + , [ + ("service-a/key1", "sa-v1") + ; ("service-a/key2", "sa-v2") + ; ("service-b/key1", "sb-v1") + ; ("service-b/key2", "sb-v2") + ] + ) + ] +end) + let tests = make_suite "guest_agent_" [ ("networks", Networks.tests) ; ("get_initial_guest_metrics", Initial_guest_metrics.tests) + ; ("get_guest_services", Services.tests) ] diff --git a/ocaml/xapi/xapi_guest_agent.ml b/ocaml/xapi/xapi_guest_agent.ml index 00e44d5925c..7160737e8c3 100644 --- a/ocaml/xapi/xapi_guest_agent.ml +++ b/ocaml/xapi/xapi_guest_agent.ml @@ -236,6 +236,29 @@ let dead_domains : IntSet.t ref = ref IntSet.empty let mutex = Mutex.create () +(* Parse data/service which has the following structure: + data/service// = + data/service// = + ... + data/service// = + Read and convert to [(/, )] pair list. + The list is intended to store in VM_guest_metrics.services at last *) +let get_guest_services (lookup : string -> string option) + (list : string -> string list) = + let base_path = "data/service" in + let services = list base_path in + services + |> List.concat_map (fun service -> + let sub_path = base_path // service in + list sub_path + |> List.map (fun key -> + let full_path_key = sub_path // key in + let db_key = service // key in + let value = lookup full_path_key in + (db_key, Option.value ~default:"" value) + ) + ) + (* In the following functions, 'lookup' reads a key from xenstore and 'list' reads a directory from xenstore. Both are relative to the guest's domainpath. *) let get_initial_guest_metrics (lookup : string -> string option) @@ -290,7 +313,7 @@ let get_initial_guest_metrics (lookup : string -> string option) ; networks "xenserver/attr" "net-sriov-vf" list ] ) - and services = [] + and services = get_guest_services lookup list and other = List.append (to_map (other all_control)) ts and memory = to_map memory and last_updated = Unix.gettimeofday () in diff --git a/ocaml/xenopsd/xc/xenops_server_xen.ml b/ocaml/xenopsd/xc/xenops_server_xen.ml index ba3dd7e2b8a..5912f816e3b 100644 --- a/ocaml/xenopsd/xc/xenops_server_xen.ml +++ b/ocaml/xenopsd/xc/xenops_server_xen.ml @@ -2744,9 +2744,10 @@ module VM = struct (fun port -> {Vm.protocol= Vm.Vt100; port; path= ""}) (Device.get_tc_port ~xs di.Xenctrl.domid) in - let local x = - Printf.sprintf "/local/domain/%d/%s" di.Xenctrl.domid x + let root_path = + Printf.sprintf "/local/domain/%d" di.Xenctrl.domid in + let local x = Printf.sprintf "%s/%s" root_path x in let uncooperative = try ignore_string (xs.Xs.read (local "memory/uncooperative")) ; @@ -2849,12 +2850,11 @@ module VM = struct ; ("drivers", None, 0) ; ("data", None, 0) (* in particular avoid data/volumes which contains many entries for each disk *) + ; ("data/service", None, 1) (* data/service//*) ] |> List.fold_left (fun acc (dir, excludes, depth) -> - ls_lR ?excludes ~depth - (Printf.sprintf "/local/domain/%d" di.Xenctrl.domid) - acc dir + ls_lR ?excludes ~depth root_path acc dir ) (quota, []) |> fun (quota, acc) -> @@ -2862,9 +2862,7 @@ module VM = struct in let quota, xsdata_state = Domain.allowed_xsdata_prefixes - |> List.fold_left - (ls_lR (Printf.sprintf "/local/domain/%d" di.Xenctrl.domid)) - (quota, []) + |> List.fold_left (ls_lR root_path) (quota, []) in let path = Device_common.xenops_path_of_domain di.Xenctrl.domid @@ -4825,6 +4823,7 @@ module Actions = struct sprintf "/local/domain/%d/attr" domid ; sprintf "/local/domain/%d/data/updated" domid ; sprintf "/local/domain/%d/data/ts" domid + ; sprintf "/local/domain/%d/data/service" domid ; sprintf "/local/domain/%d/memory/target" domid ; sprintf "/local/domain/%d/memory/uncooperative" domid ; sprintf "/local/domain/%d/console/vnc-port" domid