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(open_guest get_partitions resolve_windows_path
24 inspect_all_partitions inspect_partition
25 inspect_operating_systems mount_operating_system inspect_in_detail);
30 use String::ShellQuote qw(shell_quote);
31 use Locale::TextDomain 'libguestfs';
34 eval "use YAML::Any;";
40 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
44 virt-inspector [--connect URI] domname
46 virt-inspector guest.img [guest.img ...]
50 B<virt-inspector> examines a virtual machine and tries to determine
51 the version of the OS, the kernel version, what drivers are installed,
52 whether the virtual machine is fully virtualized (FV) or
53 para-virtualized (PV), what applications are installed and more.
55 Virt-inspector can produce output in several formats, including a
56 readable text report, and XML for feeding into other programs.
58 In the normal usage, use C<virt-inspector domname> where C<domname> is
59 the libvirt domain (see: C<virsh list --all>).
61 You can also run virt-inspector directly on disk images from a single
62 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
63 domain has several block devices, in which case you should list them
64 one after another, with the first corresponding to the guest's
65 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
67 Virt-inspector can only inspect and report upon I<one domain at a
68 time>. To inspect several virtual machines, you have to run
69 virt-inspector several times (for example, from a shell script
72 Because virt-inspector needs direct access to guest images, it won't
73 normally work over remote libvirt connections.
93 Display version number and exit.
99 =item B<--connect URI> | B<-c URI>
101 If using libvirt, connect to the given I<URI>. If omitted,
102 then we connect to the default libvirt hypervisor.
104 Libvirt is only used if you specify a C<domname> on the
105 command line. If you specify guest block devices directly,
106 then libvirt is not used at all.
112 =item B<--format> raw
114 Specify the format of disk images given on the command line. If this
115 is omitted then the format is autodetected from the content of the
118 If disk images are requested from libvirt, then this program asks
119 libvirt for this information. In this case, the value of the format
120 parameter is ignored.
122 If working with untrusted raw-format guest disk images, you should
123 ensure the format is always specified.
131 The following options select the output format. Use only one of them.
132 The default is a readable text report.
136 =item B<--text> (default)
142 Produce no output at all.
146 If you select I<--xml> then you get XML output which can be fed
151 If you select I<--yaml> then you get YAML output which can be fed
156 If you select I<--perl> then you get Perl structures output which
157 can be used directly in another Perl program.
163 If you select I<--fish> then we print a L<guestfish(1)> command
164 line which will automatically mount up the filesystems on the
165 correct mount points. Try this for example:
167 guestfish $(virt-inspector --fish guest.img)
169 I<--ro-fish> is the same, but the I<--ro> option is passed to
170 guestfish so that the filesystems are mounted read-only.
174 In "query mode" we answer common questions about the guest, such
175 as whether it is fullvirt or needs a Xen hypervisor to run.
177 See section I<QUERY MODE> below.
181 my $windows_registry;
183 =item B<--windows-registry>
185 This flag is ignored for compatibility with earlier releases of the
188 In this version, if L<Win::Hivex(3)> is available, then we attempt to
189 parse information out of the Registry for any Windows guest.
195 GetOptions ("help|?" => \$help,
196 "version" => \$version,
197 "connect|c=s" => \$uri,
198 "format=s" => \$format,
199 "text" => sub { $output = "text" },
200 "none" => sub { $output = "none" },
201 "xml" => sub { $output = "xml" },
202 "yaml" => sub { $output = "yaml" },
203 "perl" => sub { $output = "perl" },
204 "fish" => sub { $output = "fish" },
205 "guestfish" => sub { $output = "fish" },
206 "ro-fish" => sub { $output = "ro-fish" },
207 "ro-guestfish" => sub { $output = "ro-fish" },
208 "query" => sub { $output = "query" },
209 "windows-registry" => \$windows_registry,
211 pod2usage (1) if $help;
213 my $g = Sys::Guestfs->new ();
214 my %h = $g->version ();
215 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
218 pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
222 # XXX This is a bug: Originally we intended to open the guest with
223 # rw=>1 in order to tell Sys::Guestfs::Lib that we should disallow
224 # active domains. However this also has the effect of opening the
225 # disk image in write mode, and in any case we don't use this option
226 # in guestfish any more since we moved all the inspection code into
227 # the core library. We should drop the fish output modes completely.
228 $rw = 1 if $output eq "fish";
234 ($g, $conn, $dom, @images) =
235 open_guest (\@ARGV, rw => $rw, address => $uri, format => $format);
238 ($g, $conn, $dom, @images) =
239 open_guest (\@ARGV, rw => $rw, format => $format);
248 Linux (distro + version)
252 +--- Filesystems ---------- Installed apps --- Kernel & drivers
253 ----------- -------------- ----------------
254 mount point => device List of apps Extra information
255 mount point => device and versions about kernel(s)
258 (plus lots of extra information
259 about each filesystem)
261 The output of virt-inspector is a complex two-level data structure.
263 At the top level is a list of the operating systems installed on the
264 guest. (For the vast majority of guests, only a single OS is
265 installed.) The data returned for the OS includes the name (Linux,
266 Windows), the distribution and version.
268 The diagram above shows what we return for each OS.
270 With the I<--xml> option the output is mapped into an XML document.
271 There is a RELAX-NG schema for this XML in the file
272 I<virt-inspector.rng> which normally ships with virt-inspector, or can
273 be found in the source.
275 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
276 L<guestfish(1)> command line parameters, so that you can go in
277 afterwards and inspect the guest with everything mounted in the
278 right place. For example:
280 guestfish $(virt-inspector --ro-fish guest.img)
281 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
285 # List of possible filesystems.
286 my @partitions = get_partitions ($g);
288 # Now query each one to build up a picture of what's in it.
290 inspect_all_partitions ($g, \@partitions);
292 #print "fses -----------\n";
293 #print Dumper(\%fses);
295 my $oses = inspect_operating_systems ($g, \%fses);
297 #print "oses -----------\n";
298 #print Dumper($oses);
300 # Mount up the disks so we can check for applications
301 # and kernels. Skip this if the output is "*fish" because
302 # we don't need to know.
304 if ($output !~ /.*fish$/) {
306 foreach $root_dev (sort keys %$oses) {
307 my $os = $oses->{$root_dev};
308 mount_operating_system ($g, $os);
309 inspect_in_detail ($g, $os);
314 #----------------------------------------------------------------------
317 if ($output eq "fish" || $output eq "ro-fish") {
318 my @osdevs = keys %$oses;
319 # This only works if there is a single OS.
320 die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
322 my $root_dev = $osdevs[0];
324 if ($output eq "ro-fish") {
329 unless (defined $_->[1]) {
330 printf "-a %s ", shell_quote ($_->[0]);
332 printf "--format %s -a %s ",
333 shell_quote ($_->[1]), shell_quote ($_->[0]);
337 my $mounts = $oses->{$root_dev}->{mounts};
338 # Have to mount / first. Luckily '/' is early in the ASCII
339 # character set, so this should be OK.
340 foreach (sort keys %$mounts) {
341 if ($_ ne "swap" && $_ ne "none") {
342 printf "-m %s ", shell_quote ("$mounts->{$_}:$_");
349 elsif ($output eq "perl") {
350 print Dumper(%$oses);
354 elsif ($output eq "yaml") {
355 die __"virt-inspector: no YAML support, try installing perl-YAML or libyaml-perl\n"
356 unless exists $INC{"YAML/Any.pm"};
361 # Plain text output (the default).
362 elsif ($output eq "text") {
367 elsif ($output eq "xml") {
372 elsif ($output eq "query") {
378 output_text_os ($oses->{$_}) foreach sort keys %$oses;
385 print $os->{os}, " " if exists $os->{os};
386 print $os->{distro}, " " if exists $os->{distro};
387 print $os->{arch}, " " if exists $os->{arch};
388 print $os->{major_version} if exists $os->{major_version};
389 print ".", $os->{minor_version} if exists $os->{minor_version};
390 print " (", $os->{product_name}, ")" if exists $os->{product_name};
392 print "on ", $os->{root_device}, ":\n";
394 print __" Mountpoints:\n";
395 my $mounts = $os->{mounts};
396 foreach (sort keys %$mounts) {
397 printf " %-30s %s\n", $mounts->{$_}, $_
400 print __" Filesystems:\n";
401 my $filesystems = $os->{filesystems};
402 foreach (sort keys %$filesystems) {
404 print " label: $filesystems->{$_}{label}\n"
405 if exists $filesystems->{$_}{label};
406 print " UUID: $filesystems->{$_}{uuid}\n"
407 if exists $filesystems->{$_}{uuid};
408 print " type: $filesystems->{$_}{fstype}\n"
409 if exists $filesystems->{$_}{fstype};
410 print " content: $filesystems->{$_}{content}\n"
411 if exists $filesystems->{$_}{content};
414 if (exists $os->{modprobe_aliases}) {
415 my %aliases = %{$os->{modprobe_aliases}};
416 my @keys = sort keys %aliases;
418 print __" Modprobe aliases:\n";
420 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
425 if (exists $os->{initrd_modules}) {
426 my %modvers = %{$os->{initrd_modules}};
427 my @keys = sort keys %modvers;
429 print __" Initrd modules:\n";
431 my @modules = @{$modvers{$_}};
433 print " $_\n" foreach @modules;
438 print __" Applications:\n";
439 my @apps = @{$os->{apps}};
441 print " $_->{name} $_->{version}\n"
444 if ($os->{kernels}) {
445 print __" Kernels:\n";
446 my @kernels = @{$os->{kernels}};
448 print " $_->{version} ($_->{arch})\n";
449 my @modules = @{$_->{modules}};
456 if (exists $os->{root}->{registry}) {
457 print __" Windows Registry entries:\n";
458 # These are just lumps of text - dump them out.
459 foreach (@{$os->{root}->{registry}}) {
467 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
469 $xml->startTag("operatingsystems");
470 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
471 $xml->endTag("operatingsystems");
480 $xml->startTag("operatingsystem");
482 foreach ( [ "name" => "os" ],
483 [ "distro" => "distro" ],
484 [ "product_name" => "product_name" ],
485 [ "arch" => "arch" ],
486 [ "major_version" => "major_version" ],
487 [ "minor_version" => "minor_version" ],
488 [ "package_format" => "package_format" ],
489 [ "package_management" => "package_management" ],
490 [ "root" => "root_device" ] ) {
491 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
494 $xml->startTag("mountpoints");
495 my $mounts = $os->{mounts};
496 foreach (sort keys %$mounts) {
497 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
499 $xml->endTag("mountpoints");
501 $xml->startTag("filesystems");
502 my $filesystems = $os->{filesystems};
503 foreach (sort keys %$filesystems) {
504 $xml->startTag("filesystem", "dev" => $_);
506 foreach my $field ( [ "label" => "label" ],
507 [ "uuid" => "uuid" ],
508 [ "type" => "fstype" ],
509 [ "content" => "content" ],
510 [ "spec" => "spec" ] ) {
511 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
512 if exists $filesystems->{$_}{$field->[1]};
515 $xml->endTag("filesystem");
517 $xml->endTag("filesystems");
519 if (exists $os->{modprobe_aliases}) {
520 my %aliases = %{$os->{modprobe_aliases}};
521 my @keys = sort keys %aliases;
523 $xml->startTag("modprobealiases");
525 $xml->startTag("alias", "device" => $_);
527 foreach my $field ( [ "modulename" => "modulename" ],
528 [ "augeas" => "augeas" ],
529 [ "file" => "file" ] ) {
530 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
533 $xml->endTag("alias");
535 $xml->endTag("modprobealiases");
539 if (exists $os->{initrd_modules}) {
540 my %modvers = %{$os->{initrd_modules}};
541 my @keys = sort keys %modvers;
543 $xml->startTag("initrds");
545 my @modules = @{$modvers{$_}};
546 $xml->startTag("initrd", "version" => $_);
547 $xml->dataElement("module", $_) foreach @modules;
548 $xml->endTag("initrd");
550 $xml->endTag("initrds");
554 $xml->startTag("applications");
555 my @apps = @{$os->{apps}};
557 $xml->startTag("application");
558 $xml->dataElement("name", $_->{name});
559 $xml->dataElement("epoch", $_->{epoch}) if defined $_->{epoch};
560 $xml->dataElement("version", $_->{version});
561 $xml->dataElement("release", $_->{release});
562 $xml->dataElement("arch", $_->{arch});
563 $xml->endTag("application");
565 $xml->endTag("applications");
567 if(defined($os->{boot}) && defined($os->{boot}->{configs})) {
568 my $default = $os->{boot}->{default};
569 my $configs = $os->{boot}->{configs};
571 $xml->startTag("boot");
572 for(my $i = 0; $i < scalar(@$configs); $i++) {
573 my $config = $configs->[$i];
576 push(@attrs, ("default" => 1)) if($default == $i);
577 $xml->startTag("config", @attrs);
578 $xml->dataElement("title", $config->{title});
579 $xml->dataElement("kernel", $config->{kernel}->{version})
580 if(defined($config->{kernel}));
581 $xml->dataElement("cmdline", $config->{cmdline})
582 if(defined($config->{cmdline}));
583 $xml->endTag("config");
585 $xml->endTag("boot");
588 if ($os->{kernels}) {
589 $xml->startTag("kernels");
590 my @kernels = @{$os->{kernels}};
592 $xml->startTag("kernel",
593 "version" => $_->{version},
594 "arch" => $_->{arch});
595 $xml->startTag("modules");
596 my @modules = @{$_->{modules}};
598 $xml->dataElement("module", $_);
600 $xml->endTag("modules");
601 $xml->dataElement("path", $_->{path}) if(defined($_->{path}));
602 $xml->dataElement("package", $_->{package}) if(defined($_->{package}));
603 $xml->endTag("kernel");
605 $xml->endTag("kernels");
608 if (exists $os->{root}->{registry}) {
609 $xml->startTag("windowsregistryentries");
610 # These are just lumps of text - dump them out.
611 foreach (@{$os->{root}->{registry}}) {
612 $xml->dataElement("windowsregistryentry", $_);
614 $xml->endTag("windowsregistryentries");
617 $xml->endTag("operatingsystem");
622 When you use C<virt-inspector --query>, the output is a series of
630 (each answer is usually C<yes> or C<no>, or the line is completely
631 missing if we could not determine the answer at all).
633 If the guest is multiboot, you can get apparently conflicting answers
634 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
635 fullvirt and has a Xen PV kernel). This is normal, and just means
636 that the guest can do both things, although it might require operator
637 intervention such as selecting a boot option when the guest is
640 This section describes the full range of answers possible.
648 output_query_windows ();
649 output_query_linux ();
650 output_query_rhel ();
651 output_query_fedora ();
652 output_query_debian ();
653 output_query_fullvirt ();
654 output_query_xen_domU_kernel ();
655 output_query_xen_pv_drivers ();
656 output_query_virtio_drivers ();
657 output_query_kernel_arch ();
658 output_query_userspace_arch ();
661 =item windows=(yes|no)
663 Answer C<yes> if Microsoft Windows is installed in the guest.
667 sub output_query_windows
670 foreach my $os (keys %$oses) {
671 $windows="yes" if $oses->{$os}->{os} eq "windows";
673 print "windows=$windows\n";
678 Answer C<yes> if a Linux kernel is installed in the guest.
682 sub output_query_linux
685 foreach my $os (keys %$oses) {
686 $linux="yes" if $oses->{$os}->{os} eq "linux";
688 print "linux=$linux\n";
693 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
697 sub output_query_rhel
700 foreach my $os (keys %$oses) {
701 $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
702 $oses->{$os}->{distro} eq "rhel");
704 print "rhel=$rhel\n";
707 =item fedora=(yes|no)
709 Answer C<yes> if the guest contains the Fedora Linux distribution.
713 sub output_query_fedora
716 foreach my $os (keys %$oses) {
717 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
719 print "fedora=$fedora\n";
722 =item debian=(yes|no)
724 Answer C<yes> if the guest contains the Debian Linux distribution.
728 sub output_query_debian
731 foreach my $os (keys %$oses) {
732 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
734 print "debian=$debian\n";
737 =item fullvirt=(yes|no)
739 Answer C<yes> if there is at least one operating system kernel
740 installed in the guest which runs fully virtualized. Such a guest
741 would require a hypervisor which supports full system virtualization.
745 sub output_query_fullvirt
747 # The assumption is full-virt, unless all installed kernels
748 # are identified as paravirt.
749 # XXX Fails on Windows guests.
750 foreach my $os (keys %$oses) {
751 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
752 my $is_pv = $kernel->{version} =~ m/xen/;
754 print "fullvirt=yes\n";
759 print "fullvirt=no\n";
762 =item xen_domU_kernel=(yes|no)
764 Answer C<yes> if there is at least one Linux kernel installed in
765 the guest which is compiled as a Xen DomU (a Xen paravirtualized
770 sub output_query_xen_domU_kernel
772 foreach my $os (keys %$oses) {
773 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
774 my $is_xen = $kernel->{version} =~ m/xen/;
776 print "xen_domU_kernel=yes\n";
781 print "xen_domU_kernel=no\n";
784 =item xen_pv_drivers=(yes|no)
786 Answer C<yes> if the guest has Xen paravirtualized drivers installed
787 (usually the kernel itself will be fully virtualized, but the PV
788 drivers have been installed by the administrator for performance
793 sub output_query_xen_pv_drivers
795 foreach my $os (keys %$oses) {
796 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
797 foreach my $module (@{$kernel->{modules}}) {
798 if ($module =~ m/xen-/) {
799 print "xen_pv_drivers=yes\n";
805 print "xen_pv_drivers=no\n";
808 =item virtio_drivers=(yes|no)
810 Answer C<yes> if the guest has virtio paravirtualized drivers
811 installed. Virtio drivers are commonly used to improve the
816 sub output_query_virtio_drivers
818 foreach my $os (keys %$oses) {
819 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
820 foreach my $module (@{$kernel->{modules}}) {
821 if ($module =~ m/virtio_/) {
822 print "virtio_drivers=yes\n";
828 print "virtio_drivers=no\n";
831 =item userspace_arch=(x86_64|...)
833 Print the architecture of userspace.
835 NB. For multi-boot VMs this can print several lines.
839 sub output_query_userspace_arch
843 foreach my $os (keys %$oses) {
844 $arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
847 foreach (sort keys %arches) {
848 print "userspace_arch=$_\n";
852 =item kernel_arch=(x86_64|...)
854 Print the architecture of the kernel.
856 NB. For multi-boot VMs this can print several lines.
860 sub output_query_kernel_arch
864 foreach my $os (keys %$oses) {
865 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
866 $arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
870 foreach (sort keys %arches) {
871 print "kernel_arch=$_\n";
879 Libvirt guest names can contain arbitrary characters, some of which
880 have meaning to the shell such as C<#> and space. You may need to
881 quote or escape these characters on the command line. See the shell
882 manual page L<sh(1)> for details.
889 L<Sys::Guestfs::Lib(3)>,
891 L<http://libguestfs.org/>.
895 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
897 Matthew Booth L<mbooth@redhat.com>
901 Copyright (C) 2009 Red Hat Inc.
903 This program is free software; you can redistribute it and/or modify
904 it under the terms of the GNU General Public License as published by
905 the Free Software Foundation; either version 2 of the License, or
906 (at your option) any later version.
908 This program is distributed in the hope that it will be useful,
909 but WITHOUT ANY WARRANTY; without even the implied warranty of
910 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
911 GNU General Public License for more details.
913 You should have received a copy of the GNU General Public License
914 along with this program; if not, write to the Free Software
915 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.