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);
29 use File::Temp qw/tempdir/;
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 Virt-inspector should only be run on I<inactive> virtual machines.
58 The program tries to determine that the machine is inactive and will
59 refuse to run if it thinks you are trying to inspect a running domain.
61 In the normal usage, use C<virt-inspector domname> where C<domname> is
62 the libvirt domain (see: C<virsh list --all>).
64 You can also run virt-inspector directly on disk images from a single
65 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
66 domain has several block devices, in which case you should list them
67 one after another, with the first corresponding to the guest's
68 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
70 Virt-inspector can only inspect and report upon I<one domain at a
71 time>. To inspect several virtual machines, you have to run
72 virt-inspector several times (for example, from a shell script
75 Because virt-inspector needs direct access to guest images, it won't
76 normally work over remote libvirt connections.
94 =item B<--connect URI> | B<-c URI>
96 If using libvirt, connect to the given I<URI>. If omitted,
97 then we connect to the default libvirt hypervisor.
99 Libvirt is only used if you specify a C<domname> on the
100 command line. If you specify guest block devices directly,
101 then libvirt is not used at all.
109 The following options select the output format. Use only one of them.
110 The default is a readable text report.
114 =item B<--text> (default)
120 Produce no output at all.
124 If you select I<--xml> then you get XML output which can be fed
129 If you select I<--yaml> then you get YAML output which can be fed
134 If you select I<--perl> then you get Perl structures output which
135 can be used directly in another Perl program.
141 If you select I<--fish> then we print a L<guestfish(1)> command
142 line which will automatically mount up the filesystems on the
143 correct mount points. Try this for example:
145 guestfish $(virt-inspector --fish guest.img)
147 I<--ro-fish> is the same, but the I<--ro> option is passed to
148 guestfish so that the filesystems are mounted read-only.
152 In "query mode" we answer common questions about the guest, such
153 as whether it is fullvirt or needs a Xen hypervisor to run.
155 See section I<QUERY MODE> below.
159 my $windows_registry;
161 =item B<--windows-registry>
163 If this item is passed, I<and> the guest is Windows, I<and> the
164 external program C<reged> is available (see SEE ALSO section), then we
165 attempt to parse the Windows registry. This allows much more
166 information to be gathered for Windows guests.
168 This is quite an expensive and slow operation, so we don't do it by
175 GetOptions ("help|?" => \$help,
176 "connect|c=s" => \$uri,
177 "text" => sub { $output = "text" },
178 "none" => sub { $output = "none" },
179 "xml" => sub { $output = "xml" },
180 "yaml" => sub { $output = "yaml" },
181 "perl" => sub { $output = "perl" },
182 "fish" => sub { $output = "fish" },
183 "guestfish" => sub { $output = "fish" },
184 "ro-fish" => sub { $output = "ro-fish" },
185 "ro-guestfish" => sub { $output = "ro-fish" },
186 "query" => sub { $output = "query" },
187 "windows-registry" => \$windows_registry,
189 pod2usage (1) if $help;
190 pod2usage ("$0: no image or VM names given") if @ARGV == 0;
193 $rw = 1 if $output eq "fish";
196 $g = open_guest (\@ARGV, rw => $rw, address => $uri);
198 $g = open_guest (\@ARGV, rw => $rw);
208 Linux (distro + version)
212 +--- Filesystems ---------- Installed apps --- Kernel & drivers
213 ----------- -------------- ----------------
214 mount point => device List of apps Extra information
215 mount point => device and versions about kernel(s)
218 (plus lots of extra information
219 about each filesystem)
221 The output of virt-inspector is a complex two-level data structure.
223 At the top level is a list of the operating systems installed on the
224 guest. (For the vast majority of guests, only a single OS is
225 installed.) The data returned for the OS includes the name (Linux,
226 Windows), the distribution and version.
228 The diagram above shows what we return for each OS.
230 With the I<--xml> option the output is mapped into an XML document.
231 Unfortunately there is no clear schema for this document
232 (contributions welcome) but you can get an idea of the format by
233 looking at other documents and as a last resort the source for this
236 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
237 L<guestfish(1)> command line parameters, so that you can go in
238 afterwards and inspect the guest with everything mounted in the
239 right place. For example:
241 guestfish $(virt-inspector --ro-fish guest.img)
242 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
246 # List of possible filesystems.
247 my @partitions = get_partitions ($g);
249 # Now query each one to build up a picture of what's in it.
251 inspect_all_partitions ($g, \@partitions,
252 use_windows_registry => $windows_registry);
254 #print "fses -----------\n";
255 #print Dumper(\%fses);
257 my $oses = inspect_operating_systems ($g, \%fses);
259 #print "oses -----------\n";
260 #print Dumper($oses);
262 # Mount up the disks so we can check for applications
263 # and kernels. Skip this if the output is "*fish" because
264 # we don't need to know.
266 if ($output !~ /.*fish$/) {
268 foreach $root_dev (sort keys %$oses) {
269 my $os = $oses->{$root_dev};
270 mount_operating_system ($g, $os);
271 inspect_in_detail ($g, $os);
276 #----------------------------------------------------------------------
279 if ($output eq "fish" || $output eq "ro-fish") {
280 my @osdevs = keys %$oses;
281 # This only works if there is a single OS.
282 die "--fish output is only possible with a single OS\n" if @osdevs != 1;
284 my $root_dev = $osdevs[0];
286 if ($output eq "ro-fish") {
290 print "-a $_ " foreach @ARGV;
292 my $mounts = $oses->{$root_dev}->{mounts};
293 # Have to mount / first. Luckily '/' is early in the ASCII
294 # character set, so this should be OK.
295 foreach (sort keys %$mounts) {
296 print "-m $mounts->{$_}:$_ " if $_ ne "swap";
302 elsif ($output eq "perl") {
303 print Dumper(%$oses);
307 elsif ($output eq "yaml") {
308 die "virt-inspector: no YAML support\n"
309 unless exists $INC{"YAML/Any.pm"};
314 # Plain text output (the default).
315 elsif ($output eq "text") {
320 elsif ($output eq "xml") {
325 elsif ($output eq "query") {
331 output_text_os ($oses->{$_}) foreach sort keys %$oses;
338 print $os->{os}, " " if exists $os->{os};
339 print $os->{distro}, " " if exists $os->{distro};
340 print $os->{version}, " " if exists $os->{version};
341 print "on ", $os->{root_device}, ":\n";
343 print " Mountpoints:\n";
344 my $mounts = $os->{mounts};
345 foreach (sort keys %$mounts) {
346 printf " %-30s %s\n", $mounts->{$_}, $_
349 print " Filesystems:\n";
350 my $filesystems = $os->{filesystems};
351 foreach (sort keys %$filesystems) {
353 print " label: $filesystems->{$_}{label}\n"
354 if exists $filesystems->{$_}{label};
355 print " UUID: $filesystems->{$_}{uuid}\n"
356 if exists $filesystems->{$_}{uuid};
357 print " type: $filesystems->{$_}{fstype}\n"
358 if exists $filesystems->{$_}{fstype};
359 print " content: $filesystems->{$_}{content}\n"
360 if exists $filesystems->{$_}{content};
363 if (exists $os->{modprobe_aliases}) {
364 my %aliases = %{$os->{modprobe_aliases}};
365 my @keys = sort keys %aliases;
367 print " Modprobe aliases:\n";
369 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
374 if (exists $os->{initrd_modules}) {
375 my %modvers = %{$os->{initrd_modules}};
376 my @keys = sort keys %modvers;
378 print " Initrd modules:\n";
380 my @modules = @{$modvers{$_}};
382 print " $_\n" foreach @modules;
387 print " Applications:\n";
388 my @apps = @{$os->{apps}};
390 print " $_->{name} $_->{version}\n"
394 my @kernels = @{$os->{kernels}};
396 print " $_->{version}\n";
397 my @modules = @{$_->{modules}};
403 if (exists $os->{root}->{registry}) {
404 print " Windows Registry entries:\n";
405 # These are just lumps of text - dump them out.
406 foreach (@{$os->{root}->{registry}}) {
414 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
416 $xml->startTag("operatingsystems");
417 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
418 $xml->endTag("operatingsystems");
427 $xml->startTag("operatingsystem");
429 foreach ( [ "name" => "os" ],
430 [ "distro" => "distro" ],
431 [ "version" => "version" ],
432 [ "root" => "root_device" ] ) {
433 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
436 $xml->startTag("mountpoints");
437 my $mounts = $os->{mounts};
438 foreach (sort keys %$mounts) {
439 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
441 $xml->endTag("mountpoints");
443 $xml->startTag("filesystems");
444 my $filesystems = $os->{filesystems};
445 foreach (sort keys %$filesystems) {
446 $xml->startTag("filesystem", "dev" => $_);
448 foreach my $field ( [ "label" => "label" ],
449 [ "uuid" => "uuid" ],
450 [ "type" => "fstype" ],
451 [ "content" => "content" ],
452 [ "spec" => "spec" ] ) {
453 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
454 if exists $filesystems->{$_}{$field->[1]};
457 $xml->endTag("filesystem");
459 $xml->endTag("filesystems");
461 if (exists $os->{modprobe_aliases}) {
462 my %aliases = %{$os->{modprobe_aliases}};
463 my @keys = sort keys %aliases;
465 $xml->startTag("modprobealiases");
467 $xml->startTag("alias", "device" => $_);
469 foreach my $field ( [ "modulename" => "modulename" ],
470 [ "augeas" => "augeas" ],
471 [ "file" => "file" ] ) {
472 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
475 $xml->endTag("alias");
477 $xml->endTag("modprobealiases");
481 if (exists $os->{initrd_modules}) {
482 my %modvers = %{$os->{initrd_modules}};
483 my @keys = sort keys %modvers;
485 $xml->startTag("initrds");
487 my @modules = @{$modvers{$_}};
488 $xml->startTag("initrd", "version" => $_);
489 $xml->dataElement("module", $_) foreach @modules;
490 $xml->endTag("initrd");
492 $xml->endTag("initrds");
496 $xml->startTag("applications");
497 my @apps = @{$os->{apps}};
499 $xml->startTag("application");
500 $xml->dataElement("name", $_->{name});
501 $xml->dataElement("version", $_->{version});
502 $xml->endTag("application");
504 $xml->endTag("applications");
506 $xml->startTag("kernels");
507 my @kernels = @{$os->{kernels}};
509 $xml->startTag("kernel", "version" => $_->{version});
510 $xml->startTag("modules");
511 my @modules = @{$_->{modules}};
513 $xml->dataElement("module", $_);
515 $xml->endTag("modules");
516 $xml->endTag("kernel");
518 $xml->endTag("kernels");
520 if (exists $os->{root}->{registry}) {
521 $xml->startTag("windowsregistryentries");
522 # These are just lumps of text - dump them out.
523 foreach (@{$os->{root}->{registry}}) {
524 $xml->dataElement("windowsregistryentry", $_);
526 $xml->endTag("windowsregistryentries");
529 $xml->endTag("operatingsystem");
534 When you use C<virt-inspector --query>, the output is a series of
542 (each answer is usually C<yes> or C<no>, or the line is completely
543 missing if we could not determine the answer at all).
545 If the guest is multiboot, you can get apparently conflicting answers
546 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
547 fullvirt and has a Xen PV kernel). This is normal, and just means
548 that the guest can do both things, although it might require operator
549 intervention such as selecting a boot option when the guest is
552 This section describes the full range of answers possible.
560 output_query_windows ();
561 output_query_linux ();
562 output_query_rhel ();
563 output_query_fedora ();
564 output_query_debian ();
565 output_query_fullvirt ();
566 output_query_xen_domU_kernel ();
567 output_query_xen_pv_drivers ();
568 output_query_virtio_drivers ();
571 =item windows=(yes|no)
573 Answer C<yes> if Microsoft Windows is installed in the guest.
577 sub output_query_windows
580 foreach my $os (keys %$oses) {
581 $windows="yes" if $oses->{$os}->{os} eq "windows";
583 print "windows=$windows\n";
588 Answer C<yes> if a Linux kernel is installed in the guest.
592 sub output_query_linux
595 foreach my $os (keys %$oses) {
596 $linux="yes" if $oses->{$os}->{os} eq "linux";
598 print "linux=$linux\n";
603 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
607 sub output_query_rhel
610 foreach my $os (keys %$oses) {
611 $rhel="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "redhat";
613 print "rhel=$rhel\n";
616 =item fedora=(yes|no)
618 Answer C<yes> if the guest contains the Fedora Linux distribution.
622 sub output_query_fedora
625 foreach my $os (keys %$oses) {
626 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
628 print "fedora=$fedora\n";
631 =item debian=(yes|no)
633 Answer C<yes> if the guest contains the Debian Linux distribution.
637 sub output_query_debian
640 foreach my $os (keys %$oses) {
641 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
643 print "debian=$debian\n";
646 =item fullvirt=(yes|no)
648 Answer C<yes> if there is at least one operating system kernel
649 installed in the guest which runs fully virtualized. Such a guest
650 would require a hypervisor which supports full system virtualization.
654 sub output_query_fullvirt
656 # The assumption is full-virt, unless all installed kernels
657 # are identified as paravirt.
658 # XXX Fails on Windows guests.
659 foreach my $os (keys %$oses) {
660 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
661 my $is_pv = $kernel->{version} =~ m/xen/;
663 print "fullvirt=yes\n";
668 print "fullvirt=no\n";
671 =item xen_domU_kernel=(yes|no)
673 Answer C<yes> if there is at least one Linux kernel installed in
674 the guest which is compiled as a Xen DomU (a Xen paravirtualized
679 sub output_query_xen_domU_kernel
681 foreach my $os (keys %$oses) {
682 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
683 my $is_xen = $kernel->{version} =~ m/xen/;
685 print "xen_domU_kernel=yes\n";
690 print "xen_domU_kernel=no\n";
693 =item xen_pv_drivers=(yes|no)
695 Answer C<yes> if the guest has Xen paravirtualized drivers installed
696 (usually the kernel itself will be fully virtualized, but the PV
697 drivers have been installed by the administrator for performance
702 sub output_query_xen_pv_drivers
704 foreach my $os (keys %$oses) {
705 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
706 foreach my $module (@{$kernel->{modules}}) {
707 if ($module =~ m/xen-/) {
708 print "xen_pv_drivers=yes\n";
714 print "xen_pv_drivers=no\n";
717 =item virtio_drivers=(yes|no)
719 Answer C<yes> if the guest has virtio paravirtualized drivers
720 installed. Virtio drivers are commonly used to improve the
725 sub output_query_virtio_drivers
727 foreach my $os (keys %$oses) {
728 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
729 foreach my $module (@{$kernel->{modules}}) {
730 if ($module =~ m/virtio_/) {
731 print "virtio_drivers=yes\n";
737 print "virtio_drivers=no\n";
747 L<Sys::Guestfs::Lib(3)>,
749 L<http://libguestfs.org/>.
751 For Windows registry parsing we require the C<reged> program
752 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
756 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
760 Copyright (C) 2009 Red Hat Inc.
762 This program is free software; you can redistribute it and/or modify
763 it under the terms of the GNU General Public License as published by
764 the Free Software Foundation; either version 2 of the License, or
765 (at your option) any later version.
767 This program is distributed in the hope that it will be useful,
768 but WITHOUT ANY WARRANTY; without even the implied warranty of
769 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
770 GNU General Public License for more details.
772 You should have received a copy of the GNU General Public License
773 along with this program; if not, write to the Free Software
774 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.