Added GNU GPL/LGPL copyright notices everywhere.
[virt-top.git] / virt-top / virt_top.ml
index 631d04f..dc71a71 100644 (file)
@@ -1,6 +1,21 @@
 (* 'top'-like tool for libvirt domains.
- * $Id: virt_top.ml,v 1.5 2007/08/30 13:52:40 rjones Exp $
- *)
+   (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
+   http://libvirt.org/
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*)
 
 open Printf
 open ExtList
@@ -102,6 +117,9 @@ let display_mode = ref TaskDisplay
 let uri = ref None
 let debug_file = ref ""
 let csv_enabled = ref false
+let csv_cpu = ref true
+let csv_block = ref true
+let csv_net = ref true
 let init_file = ref DefaultInitFile
 let script_mode = ref false
 
@@ -131,6 +149,9 @@ let start_up () =
     "-c", Arg.String set_uri, "uri Connect to URI (default: Xen)";
     "--connect", Arg.String set_uri, "uri Connect to URI (default: Xen)";
     "--csv", Arg.String set_csv, "file Log statistics to CSV file";
+    "--no-csv-cpu", Arg.Clear csv_cpu, " Disable CPU stats in CSV";
+    "--no-csv-block", Arg.Clear csv_block, " Disable block device stats in CSV";
+    "--no-csv-net", Arg.Clear csv_net, " Disable net stats in CSV";
     "-d", Arg.Float set_delay, "delay Delay time interval (seconds)";
     "--debug", Arg.Set_string debug_file, "file Send debug messages to file";
     "--hist-cpu", Arg.Set_int historical_cpu_delay, "secs Historical CPU delay";
@@ -163,6 +184,9 @@ OPTIONS" in
       | _, "connect", uri -> set_uri uri
       | _, "debug", filename -> debug_file := filename
       | _, "csv", filename -> set_csv filename
+      | _, "csv-cpu", b -> csv_cpu := bool_of_string b
+      | _, "csv-block", b -> csv_block := bool_of_string b
+      | _, "csv-net", b -> csv_net := bool_of_string b
       | _, "batch", b -> batch_mode := bool_of_string b
       | _, "secure", b -> secure_mode := bool_of_string b
       | _, "script", b -> script_mode := bool_of_string b
@@ -251,7 +275,8 @@ OPTIONS" in
    *)
   (conn,
    !batch_mode, !script_mode, !csv_enabled, (* immutable modes *)
-   node_info, hostname, libvirt_version)
+   node_info, hostname, libvirt_version (* info that doesn't change *)
+  )
 
 (* Show a percentage in 4 chars. *)
 let show_percent percent =
@@ -409,14 +434,6 @@ let domains_lineno = 5
 let clear_msg () = move message_lineno 0; clrtoeol ()
 let print_msg str = clear_msg (); mvaddstr message_lineno 0 str; ()
 
-(* Write CSV header row. *)
-let write_csv_header () =
-  (!csv_write) [ "Hostname"; "Time"; "Arch"; "Physical CPUs";
-                "Count"; "Running"; "Blocked"; "Paused"; "Shutdown";
-                "Shutoff"; "Crashed"; "Active"; "Inactive";
-                "%CPU"; "Total memory KB"; "Total guest memory KB";
-                "Total CPU time ns" ]
-
 (* Intermediate "domain + stats" structure that we use to collect
  * everything we know about a domain within the collect function.
  *)
@@ -488,7 +505,6 @@ let collect, clear_pcpu_display_data =
     let tm = Unix.localtime time in
     let printable_time =
       sprintf "%02d:%02d:%02d" tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec in
-    mvaddstr top_lineno 0 ("virt-top " ^ printable_time ^ " - ");
 
     (* What's the total CPU time elapsed since we were last called? (ns) *)
     let total_cpu_per_pcpu = 1_000_000_000. *. (time -. !last_time) in
@@ -801,6 +817,9 @@ let redraw =
     (* Get the screen/window size. *)
     let lines, cols = get_size () in
 
+    (* Time. *)
+    mvaddstr top_lineno 0 ("virt-top " ^ printable_time ^ " - ");
+
     (* Basic node_info. *)
     addstr (sprintf "%s %d/%dCPU %dMHz %LdMB "
              node_info.C.model node_info.C.cpus nr_pcpus node_info.C.mhz
@@ -1226,21 +1245,38 @@ let redraw =
     refresh ();                   (* Refresh the display. *)
     ()
 
-(* Write summary data to CSV file.  See also write_csv_header (). *)
+(* Write CSV header row. *)
+let write_csv_header () =
+  (!csv_write) (
+    [ "Hostname"; "Time"; "Arch"; "Physical CPUs";
+      "Count"; "Running"; "Blocked"; "Paused"; "Shutdown";
+      "Shutoff"; "Crashed"; "Active"; "Inactive";
+      "%CPU"; "Total memory (KB)"; "Total guest memory (KB)";
+      "Total CPU time (ns)" ] @
+      (* These fields are repeated for each domain: *)
+    [ "Domain ID"; "Domain name"; ] @
+    (if !csv_cpu then [ "CPU (ns)"; "%CPU"; ] else []) @
+    (if !csv_block then [ "Block RDRQ"; "Block WRRQ"; ] else []) @
+    (if !csv_net then [ "Net RXBY"; "Net TXBY" ] else [])
+  )
+
+(* Write summary data to CSV file. *)
 let append_csv
     (_, _, _, _, node_info, hostname, _) (* setup *)
-    (_,
+    (doms,
      _, printable_time,
      nr_pcpus, total_cpu, _,
      totals,
      _) (* state *) =
+
+  (* The totals / summary fields. *)
   let (count, running, blocked, paused, shutdown, shutoff,
        crashed, active, inactive,
        total_cpu_time, total_memory, total_domU_memory) = totals in
 
   let percent_cpu = 100. *. total_cpu_time /. total_cpu in
 
-  (!csv_write) [
+  let summary_fields = [
     hostname; printable_time; node_info.C.model; string_of_int nr_pcpus;
     string_of_int count; string_of_int running; string_of_int blocked;
     string_of_int paused; string_of_int shutdown; string_of_int shutoff;
@@ -1248,7 +1284,43 @@ let append_csv
     sprintf "%2.1f" percent_cpu;
     Int64.to_string total_memory; Int64.to_string total_domU_memory;
     Int64.to_string (Int64.of_float total_cpu_time)
-  ]
+  ] in
+
+  (* The domains.
+   *
+   * Sort them by ID so that the list of relatively stable.  Ignore
+   * inactive domains.
+   *)
+  let doms = List.filter_map (
+    function
+    | _, Inactive -> None              (* Ignore inactive domains. *)
+    | name, Active rd -> Some (name, rd)
+  ) doms in
+  let cmp (_, { rd_domid = rd_domid1 }) (_, { rd_domid = rd_domid2 }) =
+    compare rd_domid1 rd_domid2
+  in
+  let doms = List.sort ~cmp doms in
+
+  let string_of_int64_option = Option.map_default Int64.to_string "" in
+
+  let domain_fields = List.map (
+    fun (domname, rd) ->
+      [ string_of_int rd.rd_domid; domname ] @
+       (if !csv_cpu then [
+          string_of_float rd.rd_cpu_time; string_of_float rd.rd_percent_cpu
+        ] else []) @
+       (if !csv_block then [
+          string_of_int64_option rd.rd_block_rd_reqs;
+          string_of_int64_option rd.rd_block_wr_reqs;
+        ] else []) @
+       (if !csv_net then [
+          string_of_int64_option rd.rd_net_rx_bytes;
+          string_of_int64_option rd.rd_net_tx_bytes;
+        ] else [])
+  ) doms in
+  let domain_fields = List.flatten domain_fields in
+
+  (!csv_write) (summary_fields @ domain_fields)
 
 (* Main loop. *)
 let rec main_loop ((_, batch_mode, script_mode, csv_enabled, _, _, _)