X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=tools%2Fvirt-df;h=6ec1c1a723cb07bee1ee1f8b9785def9db16e338;hp=15512216e77dccedd5fe722d662bd9f86a86688f;hb=bf3b9e2e236b4dd2216200993ba39545ad5160bb;hpb=a80177cc847102d5a6e43a8ff87769e86f30bd6c diff --git a/tools/virt-df b/tools/virt-df index 1551221..6ec1c1a 100755 --- a/tools/virt-df +++ b/tools/virt-df @@ -112,6 +112,23 @@ read L below. =cut +my $format; + +=item B<--format> raw + +Specify the format of disk images given on the command line. If this +is omitted then the format is autodetected from the content of the +disk image. + +If disk images are requested from libvirt, then this program asks +libvirt for this information. In this case, the value of the format +parameter is ignored. + +If working with untrusted raw-format guest disk images, you should +ensure the format is always specified. + +=cut + my $human; =item B<--human-readable> | B<-h> @@ -156,6 +173,20 @@ guests. =back +=cut + +my $uuid; + +=item B<--uuid> + +Print UUIDs instead of names. This is useful for following +a guest even when the guest is migrated or renamed, or when +two guests happen to have the same name. + +Note that only domains that we fetch from libvirt come with UUIDs. +For disk images, we still print the disk image name even when +this option is specified. + =back =cut @@ -164,9 +195,11 @@ GetOptions ("help|?" => \$help, "version" => \$version, "connect|c=s" => \$uri, "csv" => \$csv, + "format=s" => \$format, "human-readable|human|h" => \$human, "inodes|i" => \$inodes, "one-per-guest" => \$one_per_guest, + "uuid" => \$uuid, ) or pod2usage (2); pod2usage (1) if $help; if ($version) { @@ -224,12 +257,13 @@ if (@ARGV == 0) { # No params, use libvirt. my @disks = get_disks_from_libvirt ($dom); push @domains, { dom => $dom, name => $dom->get_name (), + uuid => $dom->get_uuid_string (), disks => \@disks } } } elsif (@ARGV == 1) { # One param, could be disk image or domname. if (-e $ARGV[0]) { push @domains, { name => basename ($ARGV[0]), - disks => [ $ARGV[0] ] } + disks => [ [ $ARGV[0], $format ] ] } } else { my $conn; @@ -245,11 +279,13 @@ if (@ARGV == 0) { # No params, use libvirt. my @disks = get_disks_from_libvirt ($dom); push @domains, { dom => $dom, name => $dom->get_name (), + uuid => $dom->get_uuid_string (), disks => \@disks } } } else { # >= 2 params, all disk images. + my @disks = map { [ $_, $format ] } @ARGV; push @domains, { name => basename ($ARGV[0]), - disks => \@ARGV } + disks => \@disks } } sub get_disks_from_libvirt @@ -258,12 +294,29 @@ sub get_disks_from_libvirt my $xml = $dom->get_xml_description (); my $p = XML::XPath->new (xml => $xml); - my @disks = $p->findnodes ('//devices/disk/source/@dev'); - push (@disks, $p->findnodes ('//devices/disk/source/@file')); + my $nodes = $p->find ('//devices/disk'); + + my @disks; + my $node; + foreach $node ($nodes->get_nodelist) { + # The filename can be in dev or file attribute, hence: + my $filename = $p->find ('./source/@dev', $node); + unless ($filename) { + $filename = $p->find ('./source/@file', $node); + next unless $filename; + } + $filename = $filename->to_literal; + + # Get the disk format (may not be set). + my $format = $p->find ('./driver/@type', $node); + $format = $format->to_literal if $format; + + push @disks, [ $filename, $format ]; + } # Code in Sys::Guestfs::Lib dies here if there are no disks at all. - return map { $_->getData } @disks; + return @disks; } # Sort the domains by name for display. @@ -312,7 +365,12 @@ sub multi_df foreach $d (@_) { foreach $disk (@{$d->{disks}}) { - $g->add_drive_ro ($disk); + my $filename = $disk->[0]; + my $format = $disk->[1]; + my @args = ($filename); + push @args, readonly => 1; + push @args, format => $format if defined $format; + $g->add_drive_opts (@args); } } @@ -325,6 +383,7 @@ sub multi_df my $n = 0; foreach $d (@_) { my $name = $d->{name}; + my $uuid = $d->{uuid}; my $nr_disks = @{$d->{disks}}; # Filter LVM to only the devices applying to the original domain. @@ -335,14 +394,14 @@ sub multi_df # contain mountable filesystems. Stat those which are # mountable, and ignore the others. foreach (@devs) { - try_df ($name, $g, $_, canonical_dev ($_, $n)); + try_df ($name, $uuid, $g, $_, canonical_dev ($_, $n)); } foreach (filter_partitions (\@devs, @partitions)) { - try_df ($name, $g, $_, canonical_dev ($_, $n)); + try_df ($name, $uuid, $g, $_, canonical_dev ($_, $n)); } if ($has_lvm2) { foreach ($g->lvs ()) { - try_df ($name, $g, $_); + try_df ($name, $uuid, $g, $_); } } @@ -392,6 +451,7 @@ sub try_df { local $_; my $domname = shift; + my $domuuid = shift; my $g = shift; my $dev = shift; my $display = shift || $dev; @@ -402,7 +462,7 @@ sub try_df %stat = $g->statvfs ("/"); }; if (!$@) { - print_stat ($domname, $display, \%stat); + print_stat ($domname, $domuuid, $display, \%stat); } $g->umount_all (); } @@ -410,10 +470,17 @@ sub try_df sub print_stat { my $domname = shift; + my $domuuid = shift; my $dev = shift; my $stat = shift; - my @cols = ($domname, $dev); + my @cols; + if (!$uuid || !defined $domuuid) { + push @cols, $domname; + } else { + push @cols, $domuuid; + } + push @cols, $dev; if (!$inodes) { my $bsize = $stat->{bsize}; # block size @@ -473,6 +540,7 @@ sub print_title printf "%-36s%10s %10s %10s %5s\n", $cols[1], $cols[2], $cols[3], $cols[4], $cols[5]; } else { + # Columns don't need special CSV quoting. print (join (",", @cols), "\n"); } } @@ -491,7 +559,11 @@ sub print_cols printf ("%10s %10s %10s %5s\n", $_[2], $_[3], $_[4], $percent); } else { - printf ("\"%s\",\"%s\",%d,%d,%d,%.1f%%\n", @_); + # Need to quote libvirt domain and filesystem. + my $dom = shift; + my $fs = shift; + print csv_quote($dom), ",", csv_quote($fs), ","; + printf ("%d,%d,%d,%.1f%%\n", @_); } } @@ -509,6 +581,31 @@ sub human_size } } +# Quote field for CSV without using an external module. +sub csv_quote +{ + local $_ = shift; + + my $needs_quoting = /[ ",\n\0]/; + return $_ unless $needs_quoting; + + my $i; + my $out = '"'; + for ($i = 0; $i < length; ++$i) { + my $c = substr $_, $i, 1; + if ($c eq '"') { + $out .= '""'; + } elsif ($c eq '\0') { + $out .= '"0'; + } else { + $out .= $c; + } + } + $out .= '"'; + + return $out; +} + =head1 NOTE ABOUT CSV FORMAT Comma-separated values (CSV) is a deceptive format. It I like