Remove dependency on ocaml-extlib
[virt-top.git] / src / csv_output.ml
1 (* 'top'-like tool for libvirt domains.
2    (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
3    http://libvirt.org/
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *)
19
20 (* CSV output functions. *)
21
22 open Printf
23
24 open Utils
25 open Collect
26
27 module C = Libvirt.Connect
28
29 (* Hook for CSV support (see [opt_csv.ml]). *)
30 let csv_write : (string list -> unit) ref =
31   ref (
32     fun _ -> ()
33   )
34
35 (* Write CSV header row. *)
36 let write_csv_header (csv_cpu, csv_mem, csv_block, csv_net) block_in_bytes =
37   (!csv_write) (
38     [ "Hostname"; "Time"; "Arch"; "Physical CPUs";
39       "Count"; "Running"; "Blocked"; "Paused"; "Shutdown";
40       "Shutoff"; "Crashed"; "Active"; "Inactive";
41       "%CPU";
42       "Total hardware memory (KB)";
43       "Total memory (KB)"; "Total guest memory (KB)";
44       "Total CPU time (ns)" ] @
45       (* These fields are repeated for each domain: *)
46     [ "Domain ID"; "Domain name"; ] @
47     (if csv_cpu then [ "CPU (ns)"; "%CPU"; ] else []) @
48     (if csv_mem then [ "Mem (bytes)"; "%Mem";] else []) @
49     (if csv_block && not block_in_bytes
50        then [ "Block RDRQ"; "Block WRRQ"; ] else []) @
51     (if csv_block && block_in_bytes
52        then [ "Block RDBY"; "Block WRBY"; ] else []) @
53     (if csv_net then [ "Net RXBY"; "Net TXBY" ] else [])
54   )
55
56 (* Write summary data to CSV file. *)
57 let append_csv (_, _, _, _, _, node_info, hostname, _) (* setup *)
58                (csv_cpu, csv_mem, csv_block, csv_net)
59                block_in_bytes
60                { rd_doms = doms;
61                  rd_printable_time = printable_time;
62                  rd_nr_pcpus = nr_pcpus; rd_total_cpu = total_cpu;
63                  rd_totals = totals } (* state *) =
64   (* The totals / summary fields. *)
65   let (count, running, blocked, paused, shutdown, shutoff,
66        crashed, active, inactive,
67        total_cpu_time, total_memory, total_domU_memory) = totals in
68
69   let percent_cpu = 100. *. total_cpu_time /. total_cpu in
70
71   let summary_fields = [
72     hostname; printable_time; node_info.C.model; string_of_int nr_pcpus;
73     string_of_int count; string_of_int running; string_of_int blocked;
74     string_of_int paused; string_of_int shutdown; string_of_int shutoff;
75     string_of_int crashed; string_of_int active; string_of_int inactive;
76     sprintf "%2.1f" percent_cpu;
77     Int64.to_string node_info.C.memory;
78     Int64.to_string total_memory; Int64.to_string total_domU_memory;
79     Int64.to_string (Int64.of_float total_cpu_time)
80   ] in
81
82   (* The domains.
83    *
84    * Sort them by ID so that the list of relatively stable.  Ignore
85    * inactive domains.
86    *)
87   let doms = List.filter_map (
88     function
89     | _, Inactive -> None               (* Ignore inactive domains. *)
90     | name, Active rd -> Some (name, rd)
91   ) doms in
92   let cmp (_, { rd_domid = rd_domid1 }) (_, { rd_domid = rd_domid2 }) =
93     compare rd_domid1 rd_domid2
94   in
95   let doms = List.sort cmp doms in
96
97   let string_of_int64_option = map_default Int64.to_string "" in
98
99   let domain_fields = List.map (
100     fun (domname, rd) ->
101       [ string_of_int rd.rd_domid; domname ] @
102         (if csv_cpu then [
103            string_of_float rd.rd_cpu_time; string_of_float rd.rd_percent_cpu
104          ] else []) @
105         (if csv_mem then [
106             Int64.to_string rd.rd_mem_bytes; Int64.to_string rd.rd_mem_percent
107          ] else []) @
108         (if csv_block then
109            if block_in_bytes then [
110              string_of_int64_option rd.rd_block_rd_bytes;
111              string_of_int64_option rd.rd_block_wr_bytes;
112            ] else [
113              string_of_int64_option rd.rd_block_rd_reqs;
114              string_of_int64_option rd.rd_block_wr_reqs;
115            ]
116          else []) @
117         (if csv_net then [
118            string_of_int64_option rd.rd_net_rx_bytes;
119            string_of_int64_option rd.rd_net_tx_bytes;
120          ] else [])
121   ) doms in
122   let domain_fields = List.flatten domain_fields in
123
124   (!csv_write) (summary_fields @ domain_fields)