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 Locale::TextDomain 'libguestfs';
33 eval "use YAML::Any;";
39 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
43 virt-inspector [--connect URI] domname
45 virt-inspector guest.img [guest.img ...]
49 B<virt-inspector> examines a virtual machine and tries to determine
50 the version of the OS, the kernel version, what drivers are installed,
51 whether the virtual machine is fully virtualized (FV) or
52 para-virtualized (PV), what applications are installed and more.
54 Virt-inspector can produce output in several formats, including a
55 readable text report, and XML for feeding into other programs.
57 In the normal usage, use C<virt-inspector domname> where C<domname> is
58 the libvirt domain (see: C<virsh list --all>).
60 You can also run virt-inspector directly on disk images from a single
61 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
62 domain has several block devices, in which case you should list them
63 one after another, with the first corresponding to the guest's
64 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
66 Virt-inspector can only inspect and report upon I<one domain at a
67 time>. To inspect several virtual machines, you have to run
68 virt-inspector several times (for example, from a shell script
71 Because virt-inspector needs direct access to guest images, it won't
72 normally work over remote libvirt connections.
92 Display version number and exit.
98 =item B<--connect URI> | B<-c URI>
100 If using libvirt, connect to the given I<URI>. If omitted,
101 then we connect to the default libvirt hypervisor.
103 Libvirt is only used if you specify a C<domname> on the
104 command line. If you specify guest block devices directly,
105 then libvirt is not used at all.
113 The following options select the output format. Use only one of them.
114 The default is a readable text report.
118 =item B<--text> (default)
124 Produce no output at all.
128 If you select I<--xml> then you get XML output which can be fed
133 If you select I<--yaml> then you get YAML output which can be fed
138 If you select I<--perl> then you get Perl structures output which
139 can be used directly in another Perl program.
145 If you select I<--fish> then we print a L<guestfish(1)> command
146 line which will automatically mount up the filesystems on the
147 correct mount points. Try this for example:
149 guestfish $(virt-inspector --fish guest.img)
151 I<--ro-fish> is the same, but the I<--ro> option is passed to
152 guestfish so that the filesystems are mounted read-only.
156 In "query mode" we answer common questions about the guest, such
157 as whether it is fullvirt or needs a Xen hypervisor to run.
159 See section I<QUERY MODE> below.
163 my $windows_registry;
165 =item B<--windows-registry>
167 This flag is ignored for compatibility with earlier releases of the
170 In this version, if L<Win::Hivex(3)> is available, then we attempt to
171 parse information out of the Registry for any Windows guest.
177 GetOptions ("help|?" => \$help,
178 "version" => \$version,
179 "connect|c=s" => \$uri,
180 "text" => sub { $output = "text" },
181 "none" => sub { $output = "none" },
182 "xml" => sub { $output = "xml" },
183 "yaml" => sub { $output = "yaml" },
184 "perl" => sub { $output = "perl" },
185 "fish" => sub { $output = "fish" },
186 "guestfish" => sub { $output = "fish" },
187 "ro-fish" => sub { $output = "ro-fish" },
188 "ro-guestfish" => sub { $output = "ro-fish" },
189 "query" => sub { $output = "query" },
190 "windows-registry" => \$windows_registry,
192 pod2usage (1) if $help;
194 my $g = Sys::Guestfs->new ();
195 my %h = $g->version ();
196 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
199 pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
202 $rw = 1 if $output eq "fish";
207 ($g, $conn, $dom, @images) =
208 open_guest (\@ARGV, rw => $rw, address => $uri);
211 ($g, $conn, $dom, @images) =
212 open_guest (\@ARGV, rw => $rw);
221 Linux (distro + version)
225 +--- Filesystems ---------- Installed apps --- Kernel & drivers
226 ----------- -------------- ----------------
227 mount point => device List of apps Extra information
228 mount point => device and versions about kernel(s)
231 (plus lots of extra information
232 about each filesystem)
234 The output of virt-inspector is a complex two-level data structure.
236 At the top level is a list of the operating systems installed on the
237 guest. (For the vast majority of guests, only a single OS is
238 installed.) The data returned for the OS includes the name (Linux,
239 Windows), the distribution and version.
241 The diagram above shows what we return for each OS.
243 With the I<--xml> option the output is mapped into an XML document.
244 There is a RELAX-NG schema for this XML in the file
245 I<virt-inspector.rng> which normally ships with virt-inspector, or can
246 be found in the source.
248 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
249 L<guestfish(1)> command line parameters, so that you can go in
250 afterwards and inspect the guest with everything mounted in the
251 right place. For example:
253 guestfish $(virt-inspector --ro-fish guest.img)
254 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
258 # List of possible filesystems.
259 my @partitions = get_partitions ($g);
261 # Now query each one to build up a picture of what's in it.
263 inspect_all_partitions ($g, \@partitions);
265 #print "fses -----------\n";
266 #print Dumper(\%fses);
268 my $oses = inspect_operating_systems ($g, \%fses);
270 #print "oses -----------\n";
271 #print Dumper($oses);
273 # Mount up the disks so we can check for applications
274 # and kernels. Skip this if the output is "*fish" because
275 # we don't need to know.
277 if ($output !~ /.*fish$/) {
279 foreach $root_dev (sort keys %$oses) {
280 my $os = $oses->{$root_dev};
281 mount_operating_system ($g, $os);
282 inspect_in_detail ($g, $os);
287 #----------------------------------------------------------------------
290 if ($output eq "fish" || $output eq "ro-fish") {
291 my @osdevs = keys %$oses;
292 # This only works if there is a single OS.
293 die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
295 my $root_dev = $osdevs[0];
297 if ($output eq "ro-fish") {
301 print "-a $_ " foreach @images;
303 my $mounts = $oses->{$root_dev}->{mounts};
304 # Have to mount / first. Luckily '/' is early in the ASCII
305 # character set, so this should be OK.
306 foreach (sort keys %$mounts) {
307 print "-m $mounts->{$_}:$_ " if $_ ne "swap" && $_ ne "none";
313 elsif ($output eq "perl") {
314 print Dumper(%$oses);
318 elsif ($output eq "yaml") {
319 die __"virt-inspector: no YAML support\n"
320 unless exists $INC{"YAML/Any.pm"};
325 # Plain text output (the default).
326 elsif ($output eq "text") {
331 elsif ($output eq "xml") {
336 elsif ($output eq "query") {
342 output_text_os ($oses->{$_}) foreach sort keys %$oses;
349 print $os->{os}, " " if exists $os->{os};
350 print $os->{distro}, " " if exists $os->{distro};
351 print $os->{arch}, " " if exists $os->{arch};
352 print $os->{major_version} if exists $os->{major_version};
353 print ".", $os->{minor_version} if exists $os->{minor_version};
354 print " (", $os->{product_name}, ")" if exists $os->{product_name};
356 print "on ", $os->{root_device}, ":\n";
358 print __" Mountpoints:\n";
359 my $mounts = $os->{mounts};
360 foreach (sort keys %$mounts) {
361 printf " %-30s %s\n", $mounts->{$_}, $_
364 print __" Filesystems:\n";
365 my $filesystems = $os->{filesystems};
366 foreach (sort keys %$filesystems) {
368 print " label: $filesystems->{$_}{label}\n"
369 if exists $filesystems->{$_}{label};
370 print " UUID: $filesystems->{$_}{uuid}\n"
371 if exists $filesystems->{$_}{uuid};
372 print " type: $filesystems->{$_}{fstype}\n"
373 if exists $filesystems->{$_}{fstype};
374 print " content: $filesystems->{$_}{content}\n"
375 if exists $filesystems->{$_}{content};
378 if (exists $os->{modprobe_aliases}) {
379 my %aliases = %{$os->{modprobe_aliases}};
380 my @keys = sort keys %aliases;
382 print __" Modprobe aliases:\n";
384 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
389 if (exists $os->{initrd_modules}) {
390 my %modvers = %{$os->{initrd_modules}};
391 my @keys = sort keys %modvers;
393 print __" Initrd modules:\n";
395 my @modules = @{$modvers{$_}};
397 print " $_\n" foreach @modules;
402 print __" Applications:\n";
403 my @apps = @{$os->{apps}};
405 print " $_->{name} $_->{version}\n"
408 if ($os->{kernels}) {
409 print __" Kernels:\n";
410 my @kernels = @{$os->{kernels}};
412 print " $_->{version} ($_->{arch})\n";
413 my @modules = @{$_->{modules}};
420 if (exists $os->{root}->{registry}) {
421 print __" Windows Registry entries:\n";
422 # These are just lumps of text - dump them out.
423 foreach (@{$os->{root}->{registry}}) {
431 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
433 $xml->startTag("operatingsystems");
434 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
435 $xml->endTag("operatingsystems");
444 $xml->startTag("operatingsystem");
446 foreach ( [ "name" => "os" ],
447 [ "distro" => "distro" ],
448 [ "product_name" => "product_name" ],
449 [ "arch" => "arch" ],
450 [ "major_version" => "major_version" ],
451 [ "minor_version" => "minor_version" ],
452 [ "package_format" => "package_format" ],
453 [ "package_management" => "package_management" ],
454 [ "root" => "root_device" ] ) {
455 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
458 $xml->startTag("mountpoints");
459 my $mounts = $os->{mounts};
460 foreach (sort keys %$mounts) {
461 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
463 $xml->endTag("mountpoints");
465 $xml->startTag("filesystems");
466 my $filesystems = $os->{filesystems};
467 foreach (sort keys %$filesystems) {
468 $xml->startTag("filesystem", "dev" => $_);
470 foreach my $field ( [ "label" => "label" ],
471 [ "uuid" => "uuid" ],
472 [ "type" => "fstype" ],
473 [ "content" => "content" ],
474 [ "spec" => "spec" ] ) {
475 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
476 if exists $filesystems->{$_}{$field->[1]};
479 $xml->endTag("filesystem");
481 $xml->endTag("filesystems");
483 if (exists $os->{modprobe_aliases}) {
484 my %aliases = %{$os->{modprobe_aliases}};
485 my @keys = sort keys %aliases;
487 $xml->startTag("modprobealiases");
489 $xml->startTag("alias", "device" => $_);
491 foreach my $field ( [ "modulename" => "modulename" ],
492 [ "augeas" => "augeas" ],
493 [ "file" => "file" ] ) {
494 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
497 $xml->endTag("alias");
499 $xml->endTag("modprobealiases");
503 if (exists $os->{initrd_modules}) {
504 my %modvers = %{$os->{initrd_modules}};
505 my @keys = sort keys %modvers;
507 $xml->startTag("initrds");
509 my @modules = @{$modvers{$_}};
510 $xml->startTag("initrd", "version" => $_);
511 $xml->dataElement("module", $_) foreach @modules;
512 $xml->endTag("initrd");
514 $xml->endTag("initrds");
518 $xml->startTag("applications");
519 my @apps = @{$os->{apps}};
521 $xml->startTag("application");
522 $xml->dataElement("name", $_->{name});
523 $xml->dataElement("epoch", $_->{epoch}) if defined $_->{epoch};
524 $xml->dataElement("version", $_->{version});
525 $xml->dataElement("release", $_->{release});
526 $xml->dataElement("arch", $_->{arch});
527 $xml->endTag("application");
529 $xml->endTag("applications");
531 if(defined($os->{boot}) && defined($os->{boot}->{configs})) {
532 my $default = $os->{boot}->{default};
533 my $configs = $os->{boot}->{configs};
535 $xml->startTag("boot");
536 for(my $i = 0; $i < scalar(@$configs); $i++) {
537 my $config = $configs->[$i];
540 push(@attrs, ("default" => 1)) if($default == $i);
541 $xml->startTag("config", @attrs);
542 $xml->dataElement("title", $config->{title});
543 $xml->dataElement("kernel", $config->{kernel}->{version})
544 if(defined($config->{kernel}));
545 $xml->dataElement("cmdline", $config->{cmdline})
546 if(defined($config->{cmdline}));
547 $xml->endTag("config");
549 $xml->endTag("boot");
552 if ($os->{kernels}) {
553 $xml->startTag("kernels");
554 my @kernels = @{$os->{kernels}};
556 $xml->startTag("kernel",
557 "version" => $_->{version},
558 "arch" => $_->{arch});
559 $xml->startTag("modules");
560 my @modules = @{$_->{modules}};
562 $xml->dataElement("module", $_);
564 $xml->endTag("modules");
565 $xml->dataElement("path", $_->{path}) if(defined($_->{path}));
566 $xml->dataElement("package", $_->{package}) if(defined($_->{package}));
567 $xml->endTag("kernel");
569 $xml->endTag("kernels");
572 if (exists $os->{root}->{registry}) {
573 $xml->startTag("windowsregistryentries");
574 # These are just lumps of text - dump them out.
575 foreach (@{$os->{root}->{registry}}) {
576 $xml->dataElement("windowsregistryentry", $_);
578 $xml->endTag("windowsregistryentries");
581 $xml->endTag("operatingsystem");
586 When you use C<virt-inspector --query>, the output is a series of
594 (each answer is usually C<yes> or C<no>, or the line is completely
595 missing if we could not determine the answer at all).
597 If the guest is multiboot, you can get apparently conflicting answers
598 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
599 fullvirt and has a Xen PV kernel). This is normal, and just means
600 that the guest can do both things, although it might require operator
601 intervention such as selecting a boot option when the guest is
604 This section describes the full range of answers possible.
612 output_query_windows ();
613 output_query_linux ();
614 output_query_rhel ();
615 output_query_fedora ();
616 output_query_debian ();
617 output_query_fullvirt ();
618 output_query_xen_domU_kernel ();
619 output_query_xen_pv_drivers ();
620 output_query_virtio_drivers ();
621 output_query_kernel_arch ();
622 output_query_userspace_arch ();
625 =item windows=(yes|no)
627 Answer C<yes> if Microsoft Windows is installed in the guest.
631 sub output_query_windows
634 foreach my $os (keys %$oses) {
635 $windows="yes" if $oses->{$os}->{os} eq "windows";
637 print "windows=$windows\n";
642 Answer C<yes> if a Linux kernel is installed in the guest.
646 sub output_query_linux
649 foreach my $os (keys %$oses) {
650 $linux="yes" if $oses->{$os}->{os} eq "linux";
652 print "linux=$linux\n";
657 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
661 sub output_query_rhel
664 foreach my $os (keys %$oses) {
665 $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
666 $oses->{$os}->{distro} eq "rhel");
668 print "rhel=$rhel\n";
671 =item fedora=(yes|no)
673 Answer C<yes> if the guest contains the Fedora Linux distribution.
677 sub output_query_fedora
680 foreach my $os (keys %$oses) {
681 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
683 print "fedora=$fedora\n";
686 =item debian=(yes|no)
688 Answer C<yes> if the guest contains the Debian Linux distribution.
692 sub output_query_debian
695 foreach my $os (keys %$oses) {
696 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
698 print "debian=$debian\n";
701 =item fullvirt=(yes|no)
703 Answer C<yes> if there is at least one operating system kernel
704 installed in the guest which runs fully virtualized. Such a guest
705 would require a hypervisor which supports full system virtualization.
709 sub output_query_fullvirt
711 # The assumption is full-virt, unless all installed kernels
712 # are identified as paravirt.
713 # XXX Fails on Windows guests.
714 foreach my $os (keys %$oses) {
715 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
716 my $is_pv = $kernel->{version} =~ m/xen/;
718 print "fullvirt=yes\n";
723 print "fullvirt=no\n";
726 =item xen_domU_kernel=(yes|no)
728 Answer C<yes> if there is at least one Linux kernel installed in
729 the guest which is compiled as a Xen DomU (a Xen paravirtualized
734 sub output_query_xen_domU_kernel
736 foreach my $os (keys %$oses) {
737 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
738 my $is_xen = $kernel->{version} =~ m/xen/;
740 print "xen_domU_kernel=yes\n";
745 print "xen_domU_kernel=no\n";
748 =item xen_pv_drivers=(yes|no)
750 Answer C<yes> if the guest has Xen paravirtualized drivers installed
751 (usually the kernel itself will be fully virtualized, but the PV
752 drivers have been installed by the administrator for performance
757 sub output_query_xen_pv_drivers
759 foreach my $os (keys %$oses) {
760 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
761 foreach my $module (@{$kernel->{modules}}) {
762 if ($module =~ m/xen-/) {
763 print "xen_pv_drivers=yes\n";
769 print "xen_pv_drivers=no\n";
772 =item virtio_drivers=(yes|no)
774 Answer C<yes> if the guest has virtio paravirtualized drivers
775 installed. Virtio drivers are commonly used to improve the
780 sub output_query_virtio_drivers
782 foreach my $os (keys %$oses) {
783 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
784 foreach my $module (@{$kernel->{modules}}) {
785 if ($module =~ m/virtio_/) {
786 print "virtio_drivers=yes\n";
792 print "virtio_drivers=no\n";
795 =item userspace_arch=(x86_64|...)
797 Print the architecture of userspace.
799 NB. For multi-boot VMs this can print several lines.
803 sub output_query_userspace_arch
807 foreach my $os (keys %$oses) {
808 $arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
811 foreach (sort keys %arches) {
812 print "userspace_arch=$_\n";
816 =item kernel_arch=(x86_64|...)
818 Print the architecture of the kernel.
820 NB. For multi-boot VMs this can print several lines.
824 sub output_query_kernel_arch
828 foreach my $os (keys %$oses) {
829 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
830 $arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
834 foreach (sort keys %arches) {
835 print "kernel_arch=$_\n";
846 L<Sys::Guestfs::Lib(3)>,
848 L<http://libguestfs.org/>.
852 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
854 Matthew Booth L<mbooth@redhat.com>
858 Copyright (C) 2009 Red Hat Inc.
860 This program is free software; you can redistribute it and/or modify
861 it under the terms of the GNU General Public License as published by
862 the Free Software Foundation; either version 2 of the License, or
863 (at your option) any later version.
865 This program is distributed in the hope that it will be useful,
866 but WITHOUT ANY WARRANTY; without even the implied warranty of
867 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
868 GNU General Public License for more details.
870 You should have received a copy of the GNU General Public License
871 along with this program; if not, write to the Free Software
872 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.