3 # Copyright (C) 2009-2010 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 =item B<--one-per-guest>
137 Run one libguestfs appliance per guest. Normally C<virt-df> will
138 add the disks from several guests to a single libguestfs appliance.
140 You might use this option in the following circumstances:
146 If you think an untrusted guest might actively try to exploit the
147 libguestfs appliance kernel, then this prevents one guest from
148 interfering with the stats printed for another guest.
152 If the kernel has a bug which stops it from accessing a
153 filesystem in one guest (see for example RHBZ#635373) then
154 this allows libguestfs to continue and report stats for further
163 GetOptions ("help|?" => \$help,
164 "version" => \$version,
165 "connect|c=s" => \$uri,
167 "human-readable|human|h" => \$human,
168 "inodes|i" => \$inodes,
169 "one-per-guest" => \$one_per_guest,
171 pod2usage (1) if $help;
173 my $g = Sys::Guestfs->new ();
174 my %h = $g->version ();
175 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
180 die __"virt-df: cannot use -h and --csv options together\n" if $human && $csv;
184 # Limit the number of devices we will ever add to the appliance. The
185 # overall limit in current libguestfs is 25: 26 = number of letters in
186 # the English alphabet since we are only confident that /dev/sd[a-z]
187 # will work because of various limits, minus 1 because that may be
188 # used by the ext2 initial filesystem.
191 # Get the list of domains and block devices.
193 # We can't use Sys::Guestfs::Lib::open_guest here because we want to
194 # create the libguestfs handle/appliance as few times as possible.
196 # If virt-df is called with no parameters, then run the libvirt
197 # equivalent of "virsh list --all", get the XML for each domain, and
198 # get the disk devices.
200 # If virt-df is called with parameters, assume it must either be a
201 # single disk image filename, a list of disk image filenames, or a
202 # single libvirt guest name. Construct disk devices accordingly.
206 if (@ARGV == 0) { # No params, use libvirt.
210 $conn = Sys::Virt->new (readonly => 1, address => $uri);
212 $conn = Sys::Virt->new (readonly => 1);
215 my @doms = $conn->list_defined_domains ();
216 push @doms, $conn->list_domains ();
218 # https://bugzilla.redhat.com/show_bug.cgi?id=538041
219 @doms = grep { $_->get_id () != 0 } @doms;
223 foreach my $dom (@doms) {
224 my @disks = get_disks_from_libvirt ($dom);
225 push @domains, { dom => $dom,
226 name => $dom->get_name (),
229 } elsif (@ARGV == 1) { # One param, could be disk image or domname.
231 push @domains, { name => basename ($ARGV[0]),
232 disks => [ $ARGV[0] ] }
237 $conn = Sys::Virt->new (readonly => 1, address => $uri);
239 $conn = Sys::Virt->new (readonly => 1);
242 my $dom = $conn->get_domain_by_name ($ARGV[0])
243 or die __x("{name} is not the name of a libvirt domain\n",
245 my @disks = get_disks_from_libvirt ($dom);
246 push @domains, { dom => $dom,
247 name => $dom->get_name (),
250 } else { # >= 2 params, all disk images.
251 push @domains, { name => basename ($ARGV[0]),
255 sub get_disks_from_libvirt
258 my $xml = $dom->get_xml_description ();
260 my $p = XML::XPath->new (xml => $xml);
261 my @disks = $p->findnodes ('//devices/disk/source/@dev');
262 push (@disks, $p->findnodes ('//devices/disk/source/@file'));
264 # Code in Sys::Guestfs::Lib dies here if there are no disks at all.
266 return map { $_->getData } @disks;
269 # Sort the domains by name for display.
270 @domains = sort { $a->{name} cmp $b->{name} } @domains;
272 # Since we got this far, we're somewhat sure we're going to
273 # get to print the result, so display the title.
276 # To minimize the number of times we have to launch the appliance,
277 # shuffle as many domains together as we can, but not exceeding
278 # MAX_DISKS per request. If --one-per-guest was requested then only
279 # request disks from a single guest each time.
280 if ($one_per_guest) {
282 my @request = ( $_ );
287 my $n = 0; # number of disks added so far
290 my $c = @{$domains[0]->{disks}};
291 if ($c > $max_disks) {
292 warn __x("virt-df: ignoring {name}, it has too many disks ({c} > {max})",
293 name => $domains[0]->{name},
294 c => $c, max => $max_disks);
297 last if $n + $c > $max_disks;
299 push @request, shift (@domains);
309 my $g = Sys::Guestfs->new ();
314 foreach $disk (@{$d->{disks}}) {
315 $g->add_drive_ro ($disk);
320 my $has_lvm2 = feature_available ($g, "lvm2");
322 my @devices = $g->list_devices ();
323 my @partitions = $g->list_partitions ();
327 my $name = $d->{name};
328 my $nr_disks = @{$d->{disks}};
330 # Filter LVM to only the devices applying to the original domain.
331 my @devs = @devices[$n .. $n+$nr_disks-1];
332 $g->lvm_set_filter (\@devs) if $has_lvm2;
334 # Find which whole devices (RHBZ#590167), partitions and LVs
335 # contain mountable filesystems. Stat those which are
336 # mountable, and ignore the others.
338 try_df ($name, $g, $_, canonical_dev ($_, $n));
340 foreach (filter_partitions (\@devs, @partitions)) {
341 try_df ($name, $g, $_, canonical_dev ($_, $n));
344 foreach ($g->lvs ()) {
345 try_df ($name, $g, $_);
355 sub filter_partitions
362 foreach my $d (@devs) {
373 # Calculate the canonical name for a device.
374 # eg: /dev/vdb1 when offset = 1
375 # => canonical name is /dev/sda1
382 return $dev unless $dev =~ m{^/dev/.d([a-z])(\d*)$};
386 $disk = chr (ord ($disk) - $offset);
388 return "/dev/sd$disk$partnum"
397 my $display = shift || $dev;
401 $g->mount_ro ($dev, "/");
402 %stat = $g->statvfs ("/");
405 print_stat ($domname, $display, \%stat);
416 my @cols = ($domname, $dev);
419 my $bsize = $stat->{bsize}; # block size
420 my $blocks = $stat->{blocks}; # total number of blocks
421 my $bfree = $stat->{bfree}; # blocks free (total)
422 my $bavail = $stat->{bavail}; # blocks free (for non-root users)
424 my $factor = $bsize / 1024;
426 push @cols, $blocks*$factor; # total 1K blocks
427 push @cols, ($blocks-$bfree)*$factor; # total 1K blocks used
428 push @cols, $bavail*$factor; # total 1K blocks available
430 push @cols, 100.0 - 100.0 * $bfree / $blocks;
433 $cols[2] = human_size ($cols[2]);
434 $cols[3] = human_size ($cols[3]);
435 $cols[4] = human_size ($cols[4]);
438 my $files = $stat->{files}; # total number of inodes
439 my $ffree = $stat->{ffree}; # inodes free (total)
440 my $favail = $stat->{favail}; # inodes free (for non-root users)
443 push @cols, $files-$ffree;
446 push @cols, 100.0 - 100.0 * $ffree / $files;
454 my @cols = (__"Virtual Machine", __"Filesystem");
457 push @cols, __"1K-blocks";
459 push @cols, __"Size";
461 push @cols, __"Used";
462 push @cols, __"Available";
463 push @cols, __"Use%";
465 push @cols, __"Inodes";
466 push @cols, __"IUsed";
467 push @cols, __"IFree";
468 push @cols, __"IUse%";
472 # ignore $cols[0] in this mode
473 printf "%-36s%10s %10s %10s %5s\n",
474 $cols[1], $cols[2], $cols[3], $cols[4], $cols[5];
476 print (join (",", @cols), "\n");
483 my $label = sprintf "%s:%s", $_[0], $_[1];
485 printf ("%-36s", $label);
486 print "\n"," "x36 if length ($label) > 36;
488 # Use 'ceil' on the percentage in order to emulate
489 # what df itself does.
490 my $percent = sprintf "%3d%%", ceil($_[5]);
492 printf ("%10s %10s %10s %5s\n", $_[2], $_[3], $_[4], $percent);
494 printf ("\"%s\",\"%s\",%d,%d,%d,%.1f%%\n", @_);
498 # Convert a number of 1K blocks to a human-readable number.
505 } elsif ($_ < 1024 * 1024) {
506 sprintf "%.1fM", ($_ / 1024);
508 sprintf "%.1fG", ($_ / 1024 / 1024);
512 =head1 NOTE ABOUT CSV FORMAT
514 Comma-separated values (CSV) is a deceptive format. It I<seems> like
515 it should be easy to parse, but it is definitely not easy to parse.
517 Myth: Just split fields at commas. Reality: This does I<not> work
518 reliably. This example has two columns:
522 Myth: Read the file one line at a time. Reality: This does I<not>
523 work reliably. This example has one row:
528 For shell scripts, use C<csvtool> (L<http://merjis.com/developers/csv>
529 also packaged in major Linux distributions).
531 For other languages, use a CSV processing library (eg. C<Text::CSV>
532 for Perl or Python's built-in csv library).
534 Most spreadsheets and databases can import CSV directly.
541 L<Sys::Guestfs::Lib(3)>,
543 L<http://libguestfs.org/>.
547 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
551 Copyright (C) 2009-2010 Red Hat Inc.
553 This program is free software; you can redistribute it and/or modify
554 it under the terms of the GNU General Public License as published by
555 the Free Software Foundation; either version 2 of the License, or
556 (at your option) any later version.
558 This program is distributed in the hope that it will be useful,
559 but WITHOUT ANY WARRANTY; without even the implied warranty of
560 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
561 GNU General Public License for more details.
563 You should have received a copy of the GNU General Public License
564 along with this program; if not, write to the Free Software
565 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.