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 Force reading a particular guest even if it appears to be active. In
110 earlier versions of virt-inspector, this could be dangerous (for
111 example, corrupting the guest's disk image). However in more recent
112 versions, it should not cause corruption, but might cause
113 virt-inspector to crash or produce incorrect results.
121 The following options select the output format. Use only one of them.
122 The default is a readable text report.
126 =item B<--text> (default)
132 Produce no output at all.
136 If you select I<--xml> then you get XML output which can be fed
141 If you select I<--yaml> then you get YAML output which can be fed
146 If you select I<--perl> then you get Perl structures output which
147 can be used directly in another Perl program.
153 If you select I<--fish> then we print a L<guestfish(1)> command
154 line which will automatically mount up the filesystems on the
155 correct mount points. Try this for example:
157 guestfish $(virt-inspector --fish guest.img)
159 I<--ro-fish> is the same, but the I<--ro> option is passed to
160 guestfish so that the filesystems are mounted read-only.
164 In "query mode" we answer common questions about the guest, such
165 as whether it is fullvirt or needs a Xen hypervisor to run.
167 See section I<QUERY MODE> below.
171 my $windows_registry;
173 =item B<--windows-registry>
175 If this item is passed, I<and> the guest is Windows, I<and> the
176 external program C<reged> is available (see SEE ALSO section), then we
177 attempt to parse the Windows registry. This allows much more
178 information to be gathered for Windows guests.
180 This is quite an expensive and slow operation, so we don't do it by
187 GetOptions ("help|?" => \$help,
188 "connect|c=s" => \$uri,
190 "text" => sub { $output = "text" },
191 "none" => sub { $output = "none" },
192 "xml" => sub { $output = "xml" },
193 "yaml" => sub { $output = "yaml" },
194 "perl" => sub { $output = "perl" },
195 "fish" => sub { $output = "fish" },
196 "guestfish" => sub { $output = "fish" },
197 "ro-fish" => sub { $output = "ro-fish" },
198 "ro-guestfish" => sub { $output = "ro-fish" },
199 "query" => sub { $output = "query" },
200 "windows-registry" => \$windows_registry,
202 pod2usage (1) if $help;
203 pod2usage ("$0: no image or VM names given") if @ARGV == 0;
206 $rw = 1 if $output eq "fish";
209 $g = open_guest (\@ARGV, rw => $rw, address => $uri);
211 $g = 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 Unfortunately there is no clear schema for this document
245 (contributions welcome) but you can get an idea of the format by
246 looking at other documents and as a last resort the source for this
249 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
250 L<guestfish(1)> command line parameters, so that you can go in
251 afterwards and inspect the guest with everything mounted in the
252 right place. For example:
254 guestfish $(virt-inspector --ro-fish guest.img)
255 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
259 # List of possible filesystems.
260 my @partitions = get_partitions ($g);
262 # Now query each one to build up a picture of what's in it.
264 inspect_all_partitions ($g, \@partitions,
265 use_windows_registry => $windows_registry);
267 #print "fses -----------\n";
268 #print Dumper(\%fses);
270 my $oses = inspect_operating_systems ($g, \%fses);
272 #print "oses -----------\n";
273 #print Dumper($oses);
275 # Mount up the disks so we can check for applications
276 # and kernels. Skip this if the output is "*fish" because
277 # we don't need to know.
279 if ($output !~ /.*fish$/) {
281 foreach $root_dev (sort keys %$oses) {
282 my $os = $oses->{$root_dev};
283 mount_operating_system ($g, $os);
284 inspect_in_detail ($g, $os);
289 #----------------------------------------------------------------------
292 if ($output eq "fish" || $output eq "ro-fish") {
293 my @osdevs = keys %$oses;
294 # This only works if there is a single OS.
295 die "--fish output is only possible with a single OS\n" if @osdevs != 1;
297 my $root_dev = $osdevs[0];
299 if ($output eq "ro-fish") {
303 print "-a $_ " foreach @ARGV;
305 my $mounts = $oses->{$root_dev}->{mounts};
306 # Have to mount / first. Luckily '/' is early in the ASCII
307 # character set, so this should be OK.
308 foreach (sort keys %$mounts) {
309 print "-m $mounts->{$_}:$_ " if $_ ne "swap";
315 elsif ($output eq "perl") {
316 print Dumper(%$oses);
320 elsif ($output eq "yaml") {
321 die "virt-inspector: no YAML support\n"
322 unless exists $INC{"YAML/Any.pm"};
327 # Plain text output (the default).
328 elsif ($output eq "text") {
333 elsif ($output eq "xml") {
338 elsif ($output eq "query") {
344 output_text_os ($oses->{$_}) foreach sort keys %$oses;
351 print $os->{os}, " " if exists $os->{os};
352 print $os->{distro}, " " if exists $os->{distro};
353 print $os->{version}, " " if exists $os->{version};
354 print "on ", $os->{root_device}, ":\n";
356 print " Mountpoints:\n";
357 my $mounts = $os->{mounts};
358 foreach (sort keys %$mounts) {
359 printf " %-30s %s\n", $mounts->{$_}, $_
362 print " Filesystems:\n";
363 my $filesystems = $os->{filesystems};
364 foreach (sort keys %$filesystems) {
366 print " label: $filesystems->{$_}{label}\n"
367 if exists $filesystems->{$_}{label};
368 print " UUID: $filesystems->{$_}{uuid}\n"
369 if exists $filesystems->{$_}{uuid};
370 print " type: $filesystems->{$_}{fstype}\n"
371 if exists $filesystems->{$_}{fstype};
372 print " content: $filesystems->{$_}{content}\n"
373 if exists $filesystems->{$_}{content};
376 if (exists $os->{modprobe_aliases}) {
377 my %aliases = %{$os->{modprobe_aliases}};
378 my @keys = sort keys %aliases;
380 print " Modprobe aliases:\n";
382 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
387 if (exists $os->{initrd_modules}) {
388 my %modvers = %{$os->{initrd_modules}};
389 my @keys = sort keys %modvers;
391 print " Initrd modules:\n";
393 my @modules = @{$modvers{$_}};
395 print " $_\n" foreach @modules;
400 print " Applications:\n";
401 my @apps = @{$os->{apps}};
403 print " $_->{name} $_->{version}\n"
407 my @kernels = @{$os->{kernels}};
409 print " $_->{version}\n";
410 my @modules = @{$_->{modules}};
416 if (exists $os->{root}->{registry}) {
417 print " Windows Registry entries:\n";
418 # These are just lumps of text - dump them out.
419 foreach (@{$os->{root}->{registry}}) {
427 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
429 $xml->startTag("operatingsystems");
430 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
431 $xml->endTag("operatingsystems");
440 $xml->startTag("operatingsystem");
442 foreach ( [ "name" => "os" ],
443 [ "distro" => "distro" ],
444 [ "version" => "version" ],
445 [ "root" => "root_device" ] ) {
446 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
449 $xml->startTag("mountpoints");
450 my $mounts = $os->{mounts};
451 foreach (sort keys %$mounts) {
452 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
454 $xml->endTag("mountpoints");
456 $xml->startTag("filesystems");
457 my $filesystems = $os->{filesystems};
458 foreach (sort keys %$filesystems) {
459 $xml->startTag("filesystem", "dev" => $_);
461 foreach my $field ( [ "label" => "label" ],
462 [ "uuid" => "uuid" ],
463 [ "type" => "fstype" ],
464 [ "content" => "content" ],
465 [ "spec" => "spec" ] ) {
466 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
467 if exists $filesystems->{$_}{$field->[1]};
470 $xml->endTag("filesystem");
472 $xml->endTag("filesystems");
474 if (exists $os->{modprobe_aliases}) {
475 my %aliases = %{$os->{modprobe_aliases}};
476 my @keys = sort keys %aliases;
478 $xml->startTag("modprobealiases");
480 $xml->startTag("alias", "device" => $_);
482 foreach my $field ( [ "modulename" => "modulename" ],
483 [ "augeas" => "augeas" ],
484 [ "file" => "file" ] ) {
485 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
488 $xml->endTag("alias");
490 $xml->endTag("modprobealiases");
494 if (exists $os->{initrd_modules}) {
495 my %modvers = %{$os->{initrd_modules}};
496 my @keys = sort keys %modvers;
498 $xml->startTag("initrds");
500 my @modules = @{$modvers{$_}};
501 $xml->startTag("initrd", "version" => $_);
502 $xml->dataElement("module", $_) foreach @modules;
503 $xml->endTag("initrd");
505 $xml->endTag("initrds");
509 $xml->startTag("applications");
510 my @apps = @{$os->{apps}};
512 $xml->startTag("application");
513 $xml->dataElement("name", $_->{name});
514 $xml->dataElement("version", $_->{version});
515 $xml->endTag("application");
517 $xml->endTag("applications");
519 $xml->startTag("kernels");
520 my @kernels = @{$os->{kernels}};
522 $xml->startTag("kernel", "version" => $_->{version});
523 $xml->startTag("modules");
524 my @modules = @{$_->{modules}};
526 $xml->dataElement("module", $_);
528 $xml->endTag("modules");
529 $xml->endTag("kernel");
531 $xml->endTag("kernels");
533 if (exists $os->{root}->{registry}) {
534 $xml->startTag("windowsregistryentries");
535 # These are just lumps of text - dump them out.
536 foreach (@{$os->{root}->{registry}}) {
537 $xml->dataElement("windowsregistryentry", $_);
539 $xml->endTag("windowsregistryentries");
542 $xml->endTag("operatingsystem");
547 When you use C<virt-inspector --query>, the output is a series of
555 (each answer is usually C<yes> or C<no>, or the line is completely
556 missing if we could not determine the answer at all).
558 If the guest is multiboot, you can get apparently conflicting answers
559 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
560 fullvirt and has a Xen PV kernel). This is normal, and just means
561 that the guest can do both things, although it might require operator
562 intervention such as selecting a boot option when the guest is
565 This section describes the full range of answers possible.
573 output_query_windows ();
574 output_query_linux ();
575 output_query_rhel ();
576 output_query_fedora ();
577 output_query_debian ();
578 output_query_fullvirt ();
579 output_query_xen_domU_kernel ();
580 output_query_xen_pv_drivers ();
581 output_query_virtio_drivers ();
584 =item windows=(yes|no)
586 Answer C<yes> if Microsoft Windows is installed in the guest.
590 sub output_query_windows
593 foreach my $os (keys %$oses) {
594 $windows="yes" if $oses->{$os}->{os} eq "windows";
596 print "windows=$windows\n";
601 Answer C<yes> if a Linux kernel is installed in the guest.
605 sub output_query_linux
608 foreach my $os (keys %$oses) {
609 $linux="yes" if $oses->{$os}->{os} eq "linux";
611 print "linux=$linux\n";
616 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
620 sub output_query_rhel
623 foreach my $os (keys %$oses) {
624 $rhel="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "redhat";
626 print "rhel=$rhel\n";
629 =item fedora=(yes|no)
631 Answer C<yes> if the guest contains the Fedora Linux distribution.
635 sub output_query_fedora
638 foreach my $os (keys %$oses) {
639 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
641 print "fedora=$fedora\n";
644 =item debian=(yes|no)
646 Answer C<yes> if the guest contains the Debian Linux distribution.
650 sub output_query_debian
653 foreach my $os (keys %$oses) {
654 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
656 print "debian=$debian\n";
659 =item fullvirt=(yes|no)
661 Answer C<yes> if there is at least one operating system kernel
662 installed in the guest which runs fully virtualized. Such a guest
663 would require a hypervisor which supports full system virtualization.
667 sub output_query_fullvirt
669 # The assumption is full-virt, unless all installed kernels
670 # are identified as paravirt.
671 # XXX Fails on Windows guests.
672 foreach my $os (keys %$oses) {
673 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
674 my $is_pv = $kernel->{version} =~ m/xen/;
676 print "fullvirt=yes\n";
681 print "fullvirt=no\n";
684 =item xen_domU_kernel=(yes|no)
686 Answer C<yes> if there is at least one Linux kernel installed in
687 the guest which is compiled as a Xen DomU (a Xen paravirtualized
692 sub output_query_xen_domU_kernel
694 foreach my $os (keys %$oses) {
695 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
696 my $is_xen = $kernel->{version} =~ m/xen/;
698 print "xen_domU_kernel=yes\n";
703 print "xen_domU_kernel=no\n";
706 =item xen_pv_drivers=(yes|no)
708 Answer C<yes> if the guest has Xen paravirtualized drivers installed
709 (usually the kernel itself will be fully virtualized, but the PV
710 drivers have been installed by the administrator for performance
715 sub output_query_xen_pv_drivers
717 foreach my $os (keys %$oses) {
718 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
719 foreach my $module (@{$kernel->{modules}}) {
720 if ($module =~ m/xen-/) {
721 print "xen_pv_drivers=yes\n";
727 print "xen_pv_drivers=no\n";
730 =item virtio_drivers=(yes|no)
732 Answer C<yes> if the guest has virtio paravirtualized drivers
733 installed. Virtio drivers are commonly used to improve the
738 sub output_query_virtio_drivers
740 foreach my $os (keys %$oses) {
741 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
742 foreach my $module (@{$kernel->{modules}}) {
743 if ($module =~ m/virtio_/) {
744 print "virtio_drivers=yes\n";
750 print "virtio_drivers=no\n";
760 L<Sys::Guestfs::Lib(3)>,
762 L<http://libguestfs.org/>.
764 For Windows registry parsing we require the C<reged> program
765 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
769 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
773 Copyright (C) 2009 Red Hat Inc.
775 This program is free software; you can redistribute it and/or modify
776 it under the terms of the GNU General Public License as published by
777 the Free Software Foundation; either version 2 of the License, or
778 (at your option) any later version.
780 This program is distributed in the hope that it will be useful,
781 but WITHOUT ANY WARRANTY; without even the implied warranty of
782 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
783 GNU General Public License for more details.
785 You should have received a copy of the GNU General Public License
786 along with this program; if not, write to the Free Software
787 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.