3 # Copyright (C) 2009 Red Hat Inc.
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.
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.
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.
23 use Sys::Guestfs::Lib qw(feature_available);
27 use File::Basename qw(basename);
30 use Locale::TextDomain 'libguestfs';
36 virt-df - Display free space on virtual filesystems
42 virt-df [--options] domname
44 virt-df [--options] disk.img [disk.img ...]
48 C<virt-df> is a command line tool to display free space on virtual
49 machine filesystems. Unlike other tools, it doesn't just display the
50 amount of space allocated to a virtual machine, but can look inside
51 the virtual machine to see how much space is really being used.
53 It is like the L<df(1)> command, but for virtual machines, except that
54 it also works for Windows virtual machines.
56 If used without any arguments, C<virt-df> checks with libvirt to get a
57 list of all active and inactive guests, and performs a C<df>-type
58 operation on each one in turn, printing out the results.
60 If used with any argument(s), C<virt-df> performs a C<df>-type
61 operation on either the single named libvirt domain, or on the disk
62 image(s) listed on the command line (which must all belong to a single
63 VM). In this mode (with arguments), C<virt-df> will I<only work for a
64 single guest>. If you want to run on multiple guests, then you have
65 to invoke C<virt-df> multiple times.
67 Use the C<--csv> option to get a format which can be easily parsed by
68 other programs. Other options are mostly similar to standard C<df>
69 options. See below for the complete list.
89 Display version number and exit.
95 =item B<--connect URI> | B<-c URI>
97 If using libvirt, connect to the given I<URI>. If omitted, then we
98 connect to the default libvirt hypervisor.
100 If you specify guest block devices directly, then libvirt is not used
109 Write out the results in CSV format (comma-separated values). This format
110 can be imported easily into databases and spreadsheets, but
111 read L</NOTE ABOUT CSV FORMAT> below.
117 =item B<--human-readable> | B<-h>
119 Print sizes in human-readable format.
121 You are not allowed to use I<-h> and I<--csv> at the same time.
127 =item B<--inodes> | B<-i>
129 Print inodes instead of blocks.
135 GetOptions ("help|?" => \$help,
136 "version" => \$version,
137 "connect|c=s" => \$uri,
139 "human-readable|human|h" => \$human,
140 "inodes|i" => \$inodes,
142 pod2usage (1) if $help;
144 my $g = Sys::Guestfs->new ();
145 my %h = $g->version ();
146 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
151 die __"virt-df: cannot use -h and --csv options together\n" if $human && $csv;
153 # Get the list of domains and block devices.
155 # We can't use Sys::Guestfs::Lib::open_guest here because we want to
156 # create the libguestfs handle/appliance as few times as possible.
158 # If virt-df is called with no parameters, then run the libvirt
159 # equivalent of "virsh list --all", get the XML for each domain, and
160 # get the disk devices.
162 # If virt-df is called with parameters, assume it must either be a
163 # single disk image filename, a list of disk image filenames, or a
164 # single libvirt guest name. Construct disk devices accordingly.
168 if (@ARGV == 0) { # No params, use libvirt.
172 $conn = Sys::Virt->new (readonly => 1, address => $uri);
174 $conn = Sys::Virt->new (readonly => 1);
177 my @doms = $conn->list_defined_domains ();
178 push @doms, $conn->list_domains ();
180 # https://bugzilla.redhat.com/show_bug.cgi?id=538041
181 @doms = grep { $_->get_id () != 0 } @doms;
185 foreach my $dom (@doms) {
186 my @disks = get_disks_from_libvirt ($dom);
187 push @domains, { dom => $dom,
188 name => $dom->get_name (),
191 } elsif (@ARGV == 1) { # One param, could be disk image or domname.
193 push @domains, { name => basename ($ARGV[0]),
194 disks => [ $ARGV[0] ] }
199 $conn = Sys::Virt->new (readonly => 1, address => $uri);
201 $conn = Sys::Virt->new (readonly => 1);
204 my $dom = $conn->get_domain_by_name ($ARGV[0])
205 or die __x("{name} is not the name of a libvirt domain\n",
207 my @disks = get_disks_from_libvirt ($dom);
208 push @domains, { dom => $dom,
209 name => $dom->get_name (),
212 } else { # >= 2 params, all disk images.
213 push @domains, { name => basename ($ARGV[0]),
217 sub get_disks_from_libvirt
220 my $xml = $dom->get_xml_description ();
222 my $p = XML::XPath->new (xml => $xml);
223 my @disks = $p->findnodes ('//devices/disk/source/@dev');
224 push (@disks, $p->findnodes ('//devices/disk/source/@file'));
226 # Code in Sys::Guestfs::Lib dies here if there are no disks at all.
228 return map { $_->getData } @disks;
231 # Sort the domains by name for display.
232 @domains = sort { $a->{name} cmp $b->{name} } @domains;
234 # Since we got this far, we're somewhat sure we're going to
235 # get to print the result, so display the title.
238 # To minimize the number of times we have to launch the appliance,
239 # shuffle as many domains together as we can, but not exceeding 26
240 # disks per request. (26 = # of letters in the English alphabet, and
241 # we are only confident that /dev/sd[a-z] will work because of various
247 my $c = @{$domains[0]->{disks}};
248 last if $n + $c > 26;
249 push @request, shift @domains;
257 my $g = Sys::Guestfs->new ();
262 foreach $disk (@{$d->{disks}}) {
263 $g->add_drive_ro ($disk);
268 my $has_lvm2 = feature_available ($g, "lvm2");
270 my @devices = $g->list_devices ();
271 my @partitions = $g->list_partitions ();
275 my $name = $d->{name};
276 my $nr_disks = @{$d->{disks}};
278 # Filter LVM to only the devices applying to the original domain.
279 my @devs = @devices[$n .. $n+$nr_disks-1];
280 $g->lvm_set_filter (\@devs) if $has_lvm2;
282 # Find which whole devices (RHBZ#590167), partitions and LVs
283 # contain mountable filesystems. Stat those which are
284 # mountable, and ignore the others.
286 try_df ($name, $g, $_, canonical_dev ($_, $n));
288 foreach (filter_partitions (\@devs, @partitions)) {
289 try_df ($name, $g, $_, canonical_dev ($_, $n));
292 foreach ($g->lvs ()) {
293 try_df ($name, $g, $_);
301 sub filter_partitions
308 foreach my $d (@devs) {
319 # Calculate the canonical name for a device.
320 # eg: /dev/vdb1 when offset = 1
321 # => canonical name is /dev/sda1
328 return $dev unless $dev =~ m{^/dev/.d([a-z])(\d*)$};
332 $disk = chr (ord ($disk) - $offset);
334 return "/dev/sd$disk$partnum"
343 my $display = shift || $dev;
347 $g->mount_ro ($dev, "/");
348 %stat = $g->statvfs ("/");
351 print_stat ($domname, $display, \%stat);
362 my @cols = ($domname, $dev);
365 my $bsize = $stat->{bsize}; # block size
366 my $blocks = $stat->{blocks}; # total number of blocks
367 my $bfree = $stat->{bfree}; # blocks free (total)
368 my $bavail = $stat->{bavail}; # blocks free (for non-root users)
370 my $factor = $bsize / 1024;
372 push @cols, $blocks*$factor; # total 1K blocks
373 push @cols, ($blocks-$bfree)*$factor; # total 1K blocks used
374 push @cols, $bavail*$factor; # total 1K blocks available
376 push @cols, 100.0 - 100.0 * $bfree / $blocks;
379 $cols[2] = human_size ($cols[2]);
380 $cols[3] = human_size ($cols[3]);
381 $cols[4] = human_size ($cols[4]);
384 my $files = $stat->{files}; # total number of inodes
385 my $ffree = $stat->{ffree}; # inodes free (total)
386 my $favail = $stat->{favail}; # inodes free (for non-root users)
389 push @cols, $files-$ffree;
392 push @cols, 100.0 - 100.0 * $ffree / $files;
400 my @cols = (__"Virtual Machine", __"Filesystem");
403 push @cols, __"1K-blocks";
405 push @cols, __"Size";
407 push @cols, __"Used";
408 push @cols, __"Available";
409 push @cols, __"Use%";
411 push @cols, __"Inodes";
412 push @cols, __"IUsed";
413 push @cols, __"IFree";
414 push @cols, __"IUse%";
418 # ignore $cols[0] in this mode
419 printf "%-36s%10s %10s %10s %5s\n",
420 $cols[1], $cols[2], $cols[3], $cols[4], $cols[5];
422 print (join (",", @cols), "\n");
429 my $label = sprintf "%s:%s", $_[0], $_[1];
431 printf ("%-36s", $label);
432 print "\n"," "x36 if length ($label) > 36;
434 # Use 'ceil' on the percentage in order to emulate
435 # what df itself does.
436 my $percent = sprintf "%3d%%", ceil($_[5]);
438 printf ("%10s %10s %10s %5s\n", $_[2], $_[3], $_[4], $percent);
440 printf ("\"%s\",\"%s\",%d,%d,%d,%.1f%%\n", @_);
444 # Convert a number of 1K blocks to a human-readable number.
451 } elsif ($_ < 1024 * 1024) {
452 sprintf "%.1fM", ($_ / 1024);
454 sprintf "%.1fG", ($_ / 1024 / 1024);
458 =head1 NOTE ABOUT CSV FORMAT
460 Comma-separated values (CSV) is a deceptive format. It I<seems> like
461 it should be easy to parse, but it is definitely not easy to parse.
463 Myth: Just split fields at commas. Reality: This does I<not> work
464 reliably. This example has two columns:
468 Myth: Read the file one line at a time. Reality: This does I<not>
469 work reliably. This example has one row:
474 For shell scripts, use C<csvtool> (L<http://merjis.com/developers/csv>
475 also packaged in major Linux distributions).
477 For other languages, use a CSV processing library (eg. C<Text::CSV>
478 for Perl or Python's built-in csv library).
480 Most spreadsheets and databases can import CSV directly.
487 L<Sys::Guestfs::Lib(3)>,
489 L<http://libguestfs.org/>.
493 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
497 Copyright (C) 2009-2010 Red Hat Inc.
499 This program is free software; you can redistribute it and/or modify
500 it under the terms of the GNU General Public License as published by
501 the Free Software Foundation; either version 2 of the License, or
502 (at your option) any later version.
504 This program is distributed in the hope that it will be useful,
505 but WITHOUT ANY WARRANTY; without even the implied warranty of
506 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
507 GNU General Public License for more details.
509 You should have received a copy of the GNU General Public License
510 along with this program; if not, write to the Free Software
511 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.