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);
32 eval "use YAML::Any;";
38 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
42 virt-inspector [--connect URI] domname
44 virt-inspector guest.img [guest.img ...]
48 B<virt-inspector> examines a virtual machine and tries to determine
49 the version of the OS, the kernel version, what drivers are installed,
50 whether the virtual machine is fully virtualized (FV) or
51 para-virtualized (PV), what applications are installed and more.
53 Virt-inspector can produce output in several formats, including a
54 readable text report, and XML for feeding into other programs.
56 Virt-inspector should only be run on I<inactive> virtual machines.
57 The program tries to determine that the machine is inactive and will
58 refuse to run if it thinks you are trying to inspect a running domain.
60 In the normal usage, use C<virt-inspector domname> where C<domname> is
61 the libvirt domain (see: C<virsh list --all>).
63 You can also run virt-inspector directly on disk images from a single
64 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
65 domain has several block devices, in which case you should list them
66 one after another, with the first corresponding to the guest's
67 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
69 Virt-inspector can only inspect and report upon I<one domain at a
70 time>. To inspect several virtual machines, you have to run
71 virt-inspector several times (for example, from a shell script
74 Because virt-inspector needs direct access to guest images, it won't
75 normally work over remote libvirt connections.
95 Display version number and exit.
101 =item B<--connect URI> | B<-c URI>
103 If using libvirt, connect to the given I<URI>. If omitted,
104 then we connect to the default libvirt hypervisor.
106 Libvirt is only used if you specify a C<domname> on the
107 command line. If you specify guest block devices directly,
108 then libvirt is not used at all.
116 The following options select the output format. Use only one of them.
117 The default is a readable text report.
121 =item B<--text> (default)
127 Produce no output at all.
131 If you select I<--xml> then you get XML output which can be fed
136 If you select I<--yaml> then you get YAML output which can be fed
141 If you select I<--perl> then you get Perl structures output which
142 can be used directly in another Perl program.
148 If you select I<--fish> then we print a L<guestfish(1)> command
149 line which will automatically mount up the filesystems on the
150 correct mount points. Try this for example:
152 guestfish $(virt-inspector --fish guest.img)
154 I<--ro-fish> is the same, but the I<--ro> option is passed to
155 guestfish so that the filesystems are mounted read-only.
159 In "query mode" we answer common questions about the guest, such
160 as whether it is fullvirt or needs a Xen hypervisor to run.
162 See section I<QUERY MODE> below.
166 my $windows_registry;
168 =item B<--windows-registry>
170 If this item is passed, I<and> the guest is Windows, I<and> the
171 external program C<reged> is available (see SEE ALSO section), then we
172 attempt to parse the Windows registry. This allows much more
173 information to be gathered for Windows guests.
175 This is quite an expensive and slow operation, so we don't do it by
182 GetOptions ("help|?" => \$help,
183 "version" => \$version,
184 "connect|c=s" => \$uri,
185 "text" => sub { $output = "text" },
186 "none" => sub { $output = "none" },
187 "xml" => sub { $output = "xml" },
188 "yaml" => sub { $output = "yaml" },
189 "perl" => sub { $output = "perl" },
190 "fish" => sub { $output = "fish" },
191 "guestfish" => sub { $output = "fish" },
192 "ro-fish" => sub { $output = "ro-fish" },
193 "ro-guestfish" => sub { $output = "ro-fish" },
194 "query" => sub { $output = "query" },
195 "windows-registry" => \$windows_registry,
197 pod2usage (1) if $help;
199 my $g = Sys::Guestfs->new ();
200 my %h = $g->version ();
201 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
204 pod2usage ("$0: no image or VM names given") if @ARGV == 0;
207 $rw = 1 if $output eq "fish";
210 $g = open_guest (\@ARGV, rw => $rw, address => $uri);
212 $g = open_guest (\@ARGV, rw => $rw);
222 Linux (distro + version)
226 +--- Filesystems ---------- Installed apps --- Kernel & drivers
227 ----------- -------------- ----------------
228 mount point => device List of apps Extra information
229 mount point => device and versions about kernel(s)
232 (plus lots of extra information
233 about each filesystem)
235 The output of virt-inspector is a complex two-level data structure.
237 At the top level is a list of the operating systems installed on the
238 guest. (For the vast majority of guests, only a single OS is
239 installed.) The data returned for the OS includes the name (Linux,
240 Windows), the distribution and version.
242 The diagram above shows what we return for each OS.
244 With the I<--xml> option the output is mapped into an XML document.
245 Unfortunately there is no clear schema for this document
246 (contributions welcome) but you can get an idea of the format by
247 looking at other documents and as a last resort the source for this
250 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
251 L<guestfish(1)> command line parameters, so that you can go in
252 afterwards and inspect the guest with everything mounted in the
253 right place. For example:
255 guestfish $(virt-inspector --ro-fish guest.img)
256 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
260 # List of possible filesystems.
261 my @partitions = get_partitions ($g);
263 # Now query each one to build up a picture of what's in it.
265 inspect_all_partitions ($g, \@partitions,
266 use_windows_registry => $windows_registry);
268 #print "fses -----------\n";
269 #print Dumper(\%fses);
271 my $oses = inspect_operating_systems ($g, \%fses);
273 #print "oses -----------\n";
274 #print Dumper($oses);
276 # Mount up the disks so we can check for applications
277 # and kernels. Skip this if the output is "*fish" because
278 # we don't need to know.
280 if ($output !~ /.*fish$/) {
282 foreach $root_dev (sort keys %$oses) {
283 my $os = $oses->{$root_dev};
284 mount_operating_system ($g, $os);
285 inspect_in_detail ($g, $os);
290 #----------------------------------------------------------------------
293 if ($output eq "fish" || $output eq "ro-fish") {
294 my @osdevs = keys %$oses;
295 # This only works if there is a single OS.
296 die "--fish output is only possible with a single OS\n" if @osdevs != 1;
298 my $root_dev = $osdevs[0];
300 if ($output eq "ro-fish") {
304 print "-a $_ " foreach @ARGV;
306 my $mounts = $oses->{$root_dev}->{mounts};
307 # Have to mount / first. Luckily '/' is early in the ASCII
308 # character set, so this should be OK.
309 foreach (sort keys %$mounts) {
310 print "-m $mounts->{$_}:$_ " if $_ ne "swap" && $_ ne "none";
316 elsif ($output eq "perl") {
317 print Dumper(%$oses);
321 elsif ($output eq "yaml") {
322 die "virt-inspector: no YAML support\n"
323 unless exists $INC{"YAML/Any.pm"};
328 # Plain text output (the default).
329 elsif ($output eq "text") {
334 elsif ($output eq "xml") {
339 elsif ($output eq "query") {
345 output_text_os ($oses->{$_}) foreach sort keys %$oses;
352 print $os->{os}, " " if exists $os->{os};
353 print $os->{distro}, " " if exists $os->{distro};
354 print $os->{version}, " " if exists $os->{version};
355 print "on ", $os->{root_device}, ":\n";
357 print " Mountpoints:\n";
358 my $mounts = $os->{mounts};
359 foreach (sort keys %$mounts) {
360 printf " %-30s %s\n", $mounts->{$_}, $_
363 print " Filesystems:\n";
364 my $filesystems = $os->{filesystems};
365 foreach (sort keys %$filesystems) {
367 print " label: $filesystems->{$_}{label}\n"
368 if exists $filesystems->{$_}{label};
369 print " UUID: $filesystems->{$_}{uuid}\n"
370 if exists $filesystems->{$_}{uuid};
371 print " type: $filesystems->{$_}{fstype}\n"
372 if exists $filesystems->{$_}{fstype};
373 print " content: $filesystems->{$_}{content}\n"
374 if exists $filesystems->{$_}{content};
377 if (exists $os->{modprobe_aliases}) {
378 my %aliases = %{$os->{modprobe_aliases}};
379 my @keys = sort keys %aliases;
381 print " Modprobe aliases:\n";
383 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
388 if (exists $os->{initrd_modules}) {
389 my %modvers = %{$os->{initrd_modules}};
390 my @keys = sort keys %modvers;
392 print " Initrd modules:\n";
394 my @modules = @{$modvers{$_}};
396 print " $_\n" foreach @modules;
401 print " Applications:\n";
402 my @apps = @{$os->{apps}};
404 print " $_->{name} $_->{version}\n"
408 my @kernels = @{$os->{kernels}};
410 print " $_->{version}\n";
411 my @modules = @{$_->{modules}};
417 if (exists $os->{root}->{registry}) {
418 print " Windows Registry entries:\n";
419 # These are just lumps of text - dump them out.
420 foreach (@{$os->{root}->{registry}}) {
428 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
430 $xml->startTag("operatingsystems");
431 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
432 $xml->endTag("operatingsystems");
441 $xml->startTag("operatingsystem");
443 foreach ( [ "name" => "os" ],
444 [ "distro" => "distro" ],
445 [ "version" => "version" ],
446 [ "root" => "root_device" ] ) {
447 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
450 $xml->startTag("mountpoints");
451 my $mounts = $os->{mounts};
452 foreach (sort keys %$mounts) {
453 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
455 $xml->endTag("mountpoints");
457 $xml->startTag("filesystems");
458 my $filesystems = $os->{filesystems};
459 foreach (sort keys %$filesystems) {
460 $xml->startTag("filesystem", "dev" => $_);
462 foreach my $field ( [ "label" => "label" ],
463 [ "uuid" => "uuid" ],
464 [ "type" => "fstype" ],
465 [ "content" => "content" ],
466 [ "spec" => "spec" ] ) {
467 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
468 if exists $filesystems->{$_}{$field->[1]};
471 $xml->endTag("filesystem");
473 $xml->endTag("filesystems");
475 if (exists $os->{modprobe_aliases}) {
476 my %aliases = %{$os->{modprobe_aliases}};
477 my @keys = sort keys %aliases;
479 $xml->startTag("modprobealiases");
481 $xml->startTag("alias", "device" => $_);
483 foreach my $field ( [ "modulename" => "modulename" ],
484 [ "augeas" => "augeas" ],
485 [ "file" => "file" ] ) {
486 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
489 $xml->endTag("alias");
491 $xml->endTag("modprobealiases");
495 if (exists $os->{initrd_modules}) {
496 my %modvers = %{$os->{initrd_modules}};
497 my @keys = sort keys %modvers;
499 $xml->startTag("initrds");
501 my @modules = @{$modvers{$_}};
502 $xml->startTag("initrd", "version" => $_);
503 $xml->dataElement("module", $_) foreach @modules;
504 $xml->endTag("initrd");
506 $xml->endTag("initrds");
510 $xml->startTag("applications");
511 my @apps = @{$os->{apps}};
513 $xml->startTag("application");
514 $xml->dataElement("name", $_->{name});
515 $xml->dataElement("version", $_->{version});
516 $xml->endTag("application");
518 $xml->endTag("applications");
520 $xml->startTag("kernels");
521 my @kernels = @{$os->{kernels}};
523 $xml->startTag("kernel", "version" => $_->{version});
524 $xml->startTag("modules");
525 my @modules = @{$_->{modules}};
527 $xml->dataElement("module", $_);
529 $xml->endTag("modules");
530 $xml->endTag("kernel");
532 $xml->endTag("kernels");
534 if (exists $os->{root}->{registry}) {
535 $xml->startTag("windowsregistryentries");
536 # These are just lumps of text - dump them out.
537 foreach (@{$os->{root}->{registry}}) {
538 $xml->dataElement("windowsregistryentry", $_);
540 $xml->endTag("windowsregistryentries");
543 $xml->endTag("operatingsystem");
548 When you use C<virt-inspector --query>, the output is a series of
556 (each answer is usually C<yes> or C<no>, or the line is completely
557 missing if we could not determine the answer at all).
559 If the guest is multiboot, you can get apparently conflicting answers
560 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
561 fullvirt and has a Xen PV kernel). This is normal, and just means
562 that the guest can do both things, although it might require operator
563 intervention such as selecting a boot option when the guest is
566 This section describes the full range of answers possible.
574 output_query_windows ();
575 output_query_linux ();
576 output_query_rhel ();
577 output_query_fedora ();
578 output_query_debian ();
579 output_query_fullvirt ();
580 output_query_xen_domU_kernel ();
581 output_query_xen_pv_drivers ();
582 output_query_virtio_drivers ();
585 =item windows=(yes|no)
587 Answer C<yes> if Microsoft Windows is installed in the guest.
591 sub output_query_windows
594 foreach my $os (keys %$oses) {
595 $windows="yes" if $oses->{$os}->{os} eq "windows";
597 print "windows=$windows\n";
602 Answer C<yes> if a Linux kernel is installed in the guest.
606 sub output_query_linux
609 foreach my $os (keys %$oses) {
610 $linux="yes" if $oses->{$os}->{os} eq "linux";
612 print "linux=$linux\n";
617 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
621 sub output_query_rhel
624 foreach my $os (keys %$oses) {
625 $rhel="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "redhat";
627 print "rhel=$rhel\n";
630 =item fedora=(yes|no)
632 Answer C<yes> if the guest contains the Fedora Linux distribution.
636 sub output_query_fedora
639 foreach my $os (keys %$oses) {
640 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
642 print "fedora=$fedora\n";
645 =item debian=(yes|no)
647 Answer C<yes> if the guest contains the Debian Linux distribution.
651 sub output_query_debian
654 foreach my $os (keys %$oses) {
655 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
657 print "debian=$debian\n";
660 =item fullvirt=(yes|no)
662 Answer C<yes> if there is at least one operating system kernel
663 installed in the guest which runs fully virtualized. Such a guest
664 would require a hypervisor which supports full system virtualization.
668 sub output_query_fullvirt
670 # The assumption is full-virt, unless all installed kernels
671 # are identified as paravirt.
672 # XXX Fails on Windows guests.
673 foreach my $os (keys %$oses) {
674 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
675 my $is_pv = $kernel->{version} =~ m/xen/;
677 print "fullvirt=yes\n";
682 print "fullvirt=no\n";
685 =item xen_domU_kernel=(yes|no)
687 Answer C<yes> if there is at least one Linux kernel installed in
688 the guest which is compiled as a Xen DomU (a Xen paravirtualized
693 sub output_query_xen_domU_kernel
695 foreach my $os (keys %$oses) {
696 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
697 my $is_xen = $kernel->{version} =~ m/xen/;
699 print "xen_domU_kernel=yes\n";
704 print "xen_domU_kernel=no\n";
707 =item xen_pv_drivers=(yes|no)
709 Answer C<yes> if the guest has Xen paravirtualized drivers installed
710 (usually the kernel itself will be fully virtualized, but the PV
711 drivers have been installed by the administrator for performance
716 sub output_query_xen_pv_drivers
718 foreach my $os (keys %$oses) {
719 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
720 foreach my $module (@{$kernel->{modules}}) {
721 if ($module =~ m/xen-/) {
722 print "xen_pv_drivers=yes\n";
728 print "xen_pv_drivers=no\n";
731 =item virtio_drivers=(yes|no)
733 Answer C<yes> if the guest has virtio paravirtualized drivers
734 installed. Virtio drivers are commonly used to improve the
739 sub output_query_virtio_drivers
741 foreach my $os (keys %$oses) {
742 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
743 foreach my $module (@{$kernel->{modules}}) {
744 if ($module =~ m/virtio_/) {
745 print "virtio_drivers=yes\n";
751 print "virtio_drivers=no\n";
761 L<Sys::Guestfs::Lib(3)>,
763 L<http://libguestfs.org/>.
765 For Windows registry parsing we require the C<reged> program
766 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
770 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
772 Matthew Booth L<mbooth@redhat.com>
776 Copyright (C) 2009 Red Hat Inc.
778 This program is free software; you can redistribute it and/or modify
779 it under the terms of the GNU General Public License as published by
780 the Free Software Foundation; either version 2 of the License, or
781 (at your option) any later version.
783 This program is distributed in the hope that it will be useful,
784 but WITHOUT ANY WARRANTY; without even the implied warranty of
785 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
786 GNU General Public License for more details.
788 You should have received a copy of the GNU General Public License
789 along with this program; if not, write to the Free Software
790 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.