virt-df/virt-df.txt
virt-df/virt_df.ml
virt-df/virt_df.mli
+virt-df/virt_df_csv.ml
+virt-df/virt_df_csv.mli
virt-df/virt_df_ext2.ml
virt-df/virt_df_ext2.mli
virt-df/virt_df_linux_swap.ml
dnl Check for optional OCaml packages.
AC_CHECK_OCAML_PKG(gettext)
+AC_CHECK_OCAML_PKG(csv)
AC_SUBST(pkg_unix)
AC_SUBST(pkg_extlib)
-AC_SUBST(pkg_gettext)
+AC_SUBST(pkg_libvirt)
AC_SUBST(pkg_xml_light)
AC_SUBST(pkg_bitmatch)
+AC_SUBST(pkg_gettext)
+AC_SUBST(pkg_csv)
dnl Check for optional perldoc (for building manual pages).
AC_CHECK_PROG(HAVE_PERLDOC,perldoc,perldoc)
virt_df_lvm2_parser.cmi: virt_df_lvm2_metadata.cmi
+virt_df_csv.cmo: virt_df.cmi virt_df_csv.cmi
+virt_df_csv.cmx: virt_df.cmx virt_df_csv.cmi
virt_df_ext2.cmo: virt_df_gettext.cmo virt_df.cmi \
/usr/lib64/ocaml/bitmatch/bitmatch.cmi virt_df_ext2.cmi
virt_df_ext2.cmx: virt_df_gettext.cmx virt_df.cmx \
OCAMLCPACKAGES += -package gettext-stub
endif
-OBJS := \
- virt_df_gettext.cmo \
- virt_df.cmo \
- virt_df_ext2.cmo \
- virt_df_linux_swap.cmo \
- virt_df_lvm2_metadata.cmo \
- virt_df_lvm2_parser.cmo \
- virt_df_lvm2_lexer.cmo \
- virt_df_lvm2.cmo \
- virt_df_mbr.cmo \
- virt_df_main.cmo
+#----------------------------------------------------------------------
+# Build up the list of object files.
+
+# Library objects.
+OBJS := virt_df_gettext.cmo virt_df.cmo
+
+# Plugin objects.
+OBJS += virt_df_ext2.cmo \
+ virt_df_linux_swap.cmo \
+ virt_df_lvm2_metadata.cmo \
+ virt_df_lvm2_parser.cmo \
+ virt_df_lvm2_lexer.cmo \
+ virt_df_lvm2.cmo \
+ virt_df_mbr.cmo
+
+ifneq ($(pkg_csv),no)
+OCAMLCPACKAGES += -package csv
+OBJS += virt_df_csv.cmo
+endif
+
+# Main program.
+OBJS += virt_df_main.cmo
XOBJS := $(OBJS:.cmo=.cmx)
+#----------------------------------------------------------------------
+
SYNTAX := -pp "camlp4o -I`ocamlc -where`/bitmatch pa_bitmatch.cmo"
OCAMLCFLAGS := -g -w s $(SYNTAX)
.IX Item "-c uri, --connect uri"
Connect to libvirt \s-1URI\s0. The default is to connect to the default
libvirt \s-1URI\s0, normally Xen.
+.IP "\fB\-\-csv\fR" 4
+.IX Item "--csv"
+Print the results in \s-1CSV\s0 format, suitable for importing into a
+spreadsheet or database.
+.Sp
+This option is only supported if virt-df was built with \s-1CSV\s0 support.
.IP "\fB\-\-debug\fR" 4
.IX Item "--debug"
Emit debugging information on stderr. Please supply this if you
Connect to libvirt URI. The default is to connect to the default
libvirt URI, normally Xen.
-=item B<--debug>
+=item B<--csv>
+
+Print the results in CSV format, suitable for importing into a
+spreadsheet or database.
+
+This option is only supported if virt-df was built with CSV support.
+
+=item B<--debug>
Emit debugging information on stderr. Please supply this if you
report a bug.
Connect to libvirt URI. The default is to connect to the default
libvirt URI, normally Xen.
+ --csv
+ Print the results in CSV format, suitable for importing into a
+ spreadsheet or database.
+
+ This option is only supported if virt-df was built with CSV support.
+
--debug
Emit debugging information on stderr. Please supply this if you
report a bug.
let ( *^ ) = Int64.mul
let ( /^ ) = Int64.div
+(* Command line arguments. *)
let debug = ref false
let uri = ref None
let inodes = ref false
let human = ref false
let all = ref false
let test_files = ref []
+let csv_mode = ref false
+
+(* Support for CSV (overridden by virt_df_csv.ml, if present). *)
+let csv_write = ref None
class virtual device =
object (self)
val human : bool ref (** Display human-readable. *)
val all : bool ref (** Show all or just active domains. *)
val test_files : string list ref (** In test mode (-t) list of files. *)
+val csv_mode : bool ref (** CSV mode. *)
(** State of command line arguments. *)
+val csv_write : (out_channel -> string list -> unit) option ref
+(** If virt_df_csv.ml is compiled in then this hook is overridden with
+ a function to write a single line to a CSV file.
+*)
+
(**
{2 Domain/device model}
--- /dev/null
+(* 'df' command for virtual domains.
+
+ (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.
+
+ Support for writing CSV files. This is only compiled in if
+ we detect the ocaml-csv module is present at compile time.
+*)
+
+open Printf
+
+open Virt_df
+
+csv_write := Some (
+ fun chan row ->
+ Csv.save_out chan [row];
+ flush chan
+)
--- /dev/null
+(* 'df' command for virtual domains.
+
+ (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.
+*)
+
+(* This file is empty to stop this plug-in from exporting any
+ symbols to other modules by accident.
+*)
open Printf
open ExtList
-open Unix
module C = Libvirt.Connect
module D = Libvirt.Domain
exit 0
in
- let test_mode filename =
- test_files := filename :: !test_files
- in
+ let test_mode filename = test_files := filename :: !test_files in
let argspec = Arg.align [
"-a", Arg.Set all,
"uri " ^ s_ "Connect to URI (default: Xen)";
"--connect", Arg.String set_uri,
"uri " ^ s_ "Connect to URI (default: Xen)";
+ "--csv", Arg.Set csv_mode,
+ " " ^ s_ "Write results in CSV format";
"--debug", Arg.Set debug,
" " ^ s_ "Debug mode (default: false)";
"-h", Arg.Set human,
Arg.parse argspec anon_fun usage_msg;
+ (* Set up CSV support. *)
+ let csv_write =
+ if not !csv_mode then
+ fun _ -> assert false (* Should never happen. *)
+ else
+ match !csv_write with
+ | None ->
+ prerr_endline (s_ "CSV is not supported in this build of virt-df");
+ exit 1
+ | Some csv_write ->
+ csv_write stdout
+ in
+
let doms : domain list =
if !test_files = [] then (
let xmls =
Libvirt.Virterror err ->
prerr_endline (Libvirt.Virterror.to_string err);
(* If non-root and no explicit connection URI, print a warning. *)
- if geteuid () <> 0 && name = None then (
+ if Unix.geteuid () <> 0 && name = None then (
print_endline (s_ "NB: If you want to monitor a local Xen hypervisor, you usually need to be root");
);
exit 1 in
d_dev = dev; d_content = `Unknown
}
with
- Unix_error (err, func, param) ->
- eprintf "%s:%s: %s" func param (error_message err);
+ Unix.Unix_error (err, func, param) ->
+ eprintf "%s:%s: %s" func param
+ (Unix.error_message err);
None
)
| _ -> None (* ignore anything else *)
{ dom with dom_lv_filesystems = filesystems }
) doms in
- (* Now print the results.
- *
- * Print the title.
- *)
+ (*----------------------------------------------------------------------*)
+ (* Now print the results. *)
+
+ (* Print the title. *)
let () =
let total, used, avail =
match !inodes, !human with
| false, false -> s_ "1K-blocks", s_ "Used", s_ "Available"
| false, true -> s_ "Size", s_ "Used", s_ "Available"
| true, _ -> s_ "Inodes", s_ "IUse", s_ "IFree" in
- printf "%-32s %10s %10s %10s %s\n%!"
- (s_ "Filesystem") total used avail (s_ "Type") in
+ if not !csv_mode then
+ printf "%-32s %10s %10s %10s %s\n%!"
+ (s_ "Filesystem") total used avail (s_ "Type")
+ else
+ csv_write [ "Filesystem"; total; used; avail; "Type" ] in
let printable_size bytes =
if bytes < 1024L *^ 1024L then
) doms
in
+ (* Printable name is like "domain:hda" or "domain:hda1". *)
+ let printable_name dom ?disk ?partno dev =
+ let dom_name = dom.dom_name in
+ (* Get the disk name (eg. "hda") from the domain XML, if
+ * we have it, otherwise use the device name (eg. for LVM).
+ *)
+ let disk_name =
+ match disk with
+ | None -> dev#name
+ | Some disk -> disk.d_target
+ in
+ match partno with
+ | None ->
+ dom_name ^ ":" ^ disk_name
+ | Some partno ->
+ dom_name ^ ":" ^ disk_name ^ string_of_int partno
+ in
+
(* Print stats for each recognized filesystem. *)
let print_stats dom ?disk ?partno dev fs =
- (* Printable name is like "domain:hda" or "domain:hda1". *)
- let name =
- let dom_name = dom.dom_name in
- (* Get the disk name (eg. "hda") from the domain XML, if
- * we have it, otherwise use the device name (eg. for LVM).
- *)
- let disk_name =
- match disk with
- | None -> dev#name
- | Some disk -> disk.d_target
- in
- match partno with
- | None ->
- dom_name ^ ":" ^ disk_name
- | Some partno ->
- dom_name ^ ":" ^ disk_name ^ string_of_int partno in
+ let name = printable_name dom ?disk ?partno dev in
printf "%-32s " name;
if fs.fs_is_swap then (
)
)
in
- iter_over_filesystems doms print_stats
+
+ (* Alternate version of print_stats which writes to a CSV file.
+ * We ignore the human-readable option because we assume that
+ * the data will be post-processed by something.
+ *)
+ let print_stats_csv dom ?disk ?partno dev fs =
+ let name = printable_name dom ?disk ?partno dev in
+
+ let row =
+ if fs.fs_is_swap then
+ (* Swap partition. *)
+ [ Int64.to_string (fs.fs_block_size *^ fs.fs_blocks_total /^ 1024L);
+ ""; "" ]
+ else (
+ (* Ordinary filesystem. *)
+ if not !inodes then ( (* Block display. *)
+ (* 'df' doesn't count the restricted blocks. *)
+ let blocks_total = fs.fs_blocks_total -^ fs.fs_blocks_reserved in
+ let blocks_avail = fs.fs_blocks_avail -^ fs.fs_blocks_reserved in
+ let blocks_avail = if blocks_avail < 0L then 0L else blocks_avail in
+
+ [ Int64.to_string (blocks_total *^ fs.fs_block_size /^ 1024L);
+ Int64.to_string (fs.fs_blocks_used *^ fs.fs_block_size /^ 1024L);
+ Int64.to_string (blocks_avail *^ fs.fs_block_size /^ 1024L) ]
+ ) else ( (* Inodes display. *)
+ [ Int64.to_string fs.fs_inodes_total;
+ Int64.to_string fs.fs_inodes_used;
+ Int64.to_string fs.fs_inodes_avail ]
+ )
+ ) in
+
+ let row = name :: row @ [fs.fs_name] in
+ csv_write row
+ in
+
+ iter_over_filesystems doms
+ (if not !csv_mode then print_stats else print_stats_csv)