albatross/stats/vmm_stats.ml

189 lines
6 KiB
OCaml
Raw Normal View History

(* (c) 2017, 2018 Hannes Mehnert, all rights reserved *)
2017-05-26 14:30:34 +00:00
open Astring
open Rresult.R.Infix
2017-05-26 14:30:34 +00:00
open Vmm_core
external sysctl_rusage : int -> rusage = "vmmanage_sysctl_rusage"
external sysctl_ifcount : unit -> int = "vmmanage_sysctl_ifcount"
external sysctl_ifdata : int -> ifdata = "vmmanage_sysctl_ifdata"
type vmctx
external vmmapi_open : string -> vmctx = "vmmanage_vmmapi_open"
external vmmapi_close : vmctx -> unit = "vmmanage_vmmapi_close"
external vmmapi_statnames : vmctx -> string list = "vmmanage_vmmapi_statnames"
external vmmapi_stats : vmctx -> int64 list = "vmmanage_vmmapi_stats"
2017-05-26 14:30:34 +00:00
let my_version = `WV0
let descr = ref []
2017-05-26 14:30:34 +00:00
type t = {
pid_nic : (vmctx option * (int * string) list) IM.t ;
2017-05-26 14:30:34 +00:00
pid_rusage : rusage IM.t ;
pid_vmmapi : (string * int64) list IM.t ;
2017-05-26 14:30:34 +00:00
nic_ifdata : ifdata String.Map.t ;
}
let pp_strings pp taps = Fmt.(list ~sep:(unit ",@ ") string) pp taps
2017-05-26 14:30:34 +00:00
let empty () =
{ pid_nic = IM.empty ; pid_rusage = IM.empty ; pid_vmmapi = IM.empty ; nic_ifdata = String.Map.empty }
2017-05-26 14:30:34 +00:00
let rec wrap f arg =
2017-05-26 14:30:34 +00:00
try Some (f arg) with
| Unix.Unix_error (Unix.EINTR, _, _) -> wrap f arg
| e ->
Logs.err (fun m -> m "exception %s" (Printexc.to_string e)) ;
None
2017-05-26 14:30:34 +00:00
let gather pid vmctx nics =
wrap sysctl_rusage pid,
(match vmctx with
| None -> None
| Some vmctx -> wrap vmmapi_stats vmctx),
List.fold_left (fun ifd (nic, nname) ->
match wrap sysctl_ifdata nic with
| None ->
Logs.warn (fun m -> m "failed to get ifdata for %s" nname) ;
ifd
| Some data ->
Logs.debug (fun m -> m "adding ifdata for %s" nname) ;
String.Map.add data.name data ifd)
2017-05-26 14:30:34 +00:00
String.Map.empty nics
let tick t =
2018-03-22 13:00:04 +00:00
Logs.debug (fun m -> m "tick with %d vms" (IM.cardinal t.pid_nic)) ;
let pid_rusage, pid_vmmapi, nic_ifdata =
IM.fold (fun pid (vmctx, nics) (rus, vmms, ifds) ->
let ru, vmm, ifd = gather pid vmctx nics in
2017-05-26 14:30:34 +00:00
(match ru with
| None ->
Logs.warn (fun m -> m "failed to get rusage for %d" pid) ;
rus
| Some ru ->
Logs.debug (fun m -> m "adding resource usage for %d" pid) ;
IM.add pid ru rus),
2018-03-22 13:00:04 +00:00
(match vmm with
| None ->
Logs.warn (fun m -> m "failed to get vmmapi_stats for %d" pid) ;
vmms
| Some vmm ->
Logs.debug (fun m -> m "adding vmmapi_stats for %d" pid) ;
IM.add pid (List.combine !descr vmm) vmms),
2017-05-26 14:30:34 +00:00
String.Map.union (fun _k a _b -> Some a) ifd ifds)
t.pid_nic (IM.empty, IM.empty, String.Map.empty)
2017-05-26 14:30:34 +00:00
in
{ t with pid_rusage ; pid_vmmapi ; nic_ifdata }
2017-05-26 14:30:34 +00:00
let fill_descr ctx =
match !descr with
| [] ->
begin match wrap vmmapi_statnames ctx with
| None ->
Logs.err (fun m -> m "vmmapi_statnames failed, shouldn't happen") ;
()
| Some d ->
Logs.info (fun m -> m "descr are %a" pp_strings d) ;
descr := d
end
| ds -> Logs.info (fun m -> m "%d descr are already present" (List.length ds))
2017-05-26 14:30:34 +00:00
let add_pid t pid nics =
let name = "ukvm" ^ string_of_int pid in
match wrap sysctl_ifcount () with
| None ->
Logs.err (fun m -> m "sysctl ifcount failed for %d %a" pid pp_strings nics) ;
Error (`Msg "sysctl ifcount failed")
| Some max_nic ->
let rec go cnt acc id =
if id > 0 && cnt > 0 then
match wrap sysctl_ifdata id with
| Some ifd when List.mem ifd.name nics ->
go (pred cnt) ((id, ifd.name) :: acc) (pred id)
| _ -> go cnt acc (pred id)
else
List.rev acc
in
Ok (go (List.length nics) [] max_nic) >>= fun nic_ids ->
(match wrap vmmapi_open name with
| None ->
Logs.warn (fun m -> m "(ignored) vmmapi_open failed for %d" pid) ;
Ok None
| Some vmctx ->
fill_descr vmctx ;
Ok (Some vmctx)) >>= fun vmctx ->
Logs.info (fun m -> m "adding %d %a with vmctx %b" pid pp_strings nics
(match vmctx with None -> false | Some _ -> true)) ;
let pid_nic = IM.add pid (vmctx, nic_ids) t.pid_nic in
Ok { t with pid_nic }
2017-05-26 14:30:34 +00:00
2017-05-26 14:30:34 +00:00
let stats t pid =
Logs.debug (fun m -> m "querying statistics for %d" pid) ;
2017-05-26 14:30:34 +00:00
try
let _, nics = IM.find pid t.pid_nic
and ru = IM.find pid t.pid_rusage
and vmm =
try IM.find pid t.pid_vmmapi with
| Not_found ->
Logs.err (fun m -> m "failed to find vmm stats for %d" pid);
[]
in
2017-05-26 14:30:34 +00:00
match
List.fold_left (fun acc nic ->
match String.Map.find nic t.nic_ifdata, acc with
| None, _ -> None
| _, None -> None
| Some ifd, Some acc -> Some (ifd :: acc))
(Some []) (snd (List.split nics))
with
| None -> Error (`Msg "failed to find interface statistics")
| Some ifd -> Ok (ru, vmm, ifd)
2017-05-26 14:30:34 +00:00
with
| _ -> Error (`Msg "failed to find resource usage")
let remove_pid t pid =
Logs.info (fun m -> m "removing pid %d" pid) ;
(try
match IM.find pid t.pid_nic with
| Some vmctx, _ -> ignore (wrap vmmapi_close vmctx)
| None, _ -> ()
with
_ -> ()) ;
2017-05-26 14:30:34 +00:00
let pid_nic = IM.remove pid t.pid_nic in
{ t with pid_nic }
let remove_all t = IM.iter (fun pid _ -> ignore (remove_pid t pid)) t.pid_nic
2017-05-26 14:30:34 +00:00
let handle t hdr buf =
let open Vmm_wire in
let open Vmm_wire.Stats in
let cs = Cstruct.of_string buf in
let r =
if not (version_eq my_version hdr.version) then
Error (`Msg "cannot handle version")
else
match int_to_op hdr.tag with
| Some Add ->
decode_pid_taps cs >>= fun (pid, taps) ->
add_pid t pid taps >>= fun t ->
Ok (t, success ~msg:"added" hdr.id my_version)
| Some Remove ->
decode_pid cs >>= fun pid ->
let t = remove_pid t pid in
Ok (t, success ~msg:"removed" hdr.id my_version)
| Some Statistics ->
decode_pid cs >>= fun pid ->
stats t pid >>= fun s ->
Ok (t, stat_reply hdr.id my_version (encode_stats s))
| _ -> Error (`Msg "unknown command")
in
match r with
| Ok (t, out) -> t, out
| Error (`Msg msg) ->
Logs.err (fun m -> m "error while processing %s" msg) ;
t, fail ~msg hdr.id my_version