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 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.
96 Display version number and exit.
102 =item B<--connect URI> | B<-c URI>
104 If using libvirt, connect to the given I<URI>. If omitted,
105 then we connect to the default libvirt hypervisor.
107 Libvirt is only used if you specify a C<domname> on the
108 command line. If you specify guest block devices directly,
109 then libvirt is not used at all.
117 The following options select the output format. Use only one of them.
118 The default is a readable text report.
122 =item B<--text> (default)
128 Produce no output at all.
132 If you select I<--xml> then you get XML output which can be fed
137 If you select I<--yaml> then you get YAML output which can be fed
142 If you select I<--perl> then you get Perl structures output which
143 can be used directly in another Perl program.
149 If you select I<--fish> then we print a L<guestfish(1)> command
150 line which will automatically mount up the filesystems on the
151 correct mount points. Try this for example:
153 guestfish $(virt-inspector --fish guest.img)
155 I<--ro-fish> is the same, but the I<--ro> option is passed to
156 guestfish so that the filesystems are mounted read-only.
160 In "query mode" we answer common questions about the guest, such
161 as whether it is fullvirt or needs a Xen hypervisor to run.
163 See section I<QUERY MODE> below.
167 my $windows_registry;
169 =item B<--windows-registry>
171 If this item is passed, I<and> the guest is Windows, I<and> the
172 external program C<reged> is available (see SEE ALSO section), then we
173 attempt to parse the Windows registry. This allows much more
174 information to be gathered for Windows guests.
176 This is quite an expensive and slow operation, so we don't do it by
183 GetOptions ("help|?" => \$help,
184 "version" => \$version,
185 "connect|c=s" => \$uri,
186 "text" => sub { $output = "text" },
187 "none" => sub { $output = "none" },
188 "xml" => sub { $output = "xml" },
189 "yaml" => sub { $output = "yaml" },
190 "perl" => sub { $output = "perl" },
191 "fish" => sub { $output = "fish" },
192 "guestfish" => sub { $output = "fish" },
193 "ro-fish" => sub { $output = "ro-fish" },
194 "ro-guestfish" => sub { $output = "ro-fish" },
195 "query" => sub { $output = "query" },
196 "windows-registry" => \$windows_registry,
198 pod2usage (1) if $help;
200 my $g = Sys::Guestfs->new ();
201 my %h = $g->version ();
202 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
205 pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
208 $rw = 1 if $output eq "fish";
211 $g = open_guest (\@ARGV, rw => $rw, address => $uri);
213 $g = open_guest (\@ARGV, rw => $rw);
223 Linux (distro + version)
227 +--- Filesystems ---------- Installed apps --- Kernel & drivers
228 ----------- -------------- ----------------
229 mount point => device List of apps Extra information
230 mount point => device and versions about kernel(s)
233 (plus lots of extra information
234 about each filesystem)
236 The output of virt-inspector is a complex two-level data structure.
238 At the top level is a list of the operating systems installed on the
239 guest. (For the vast majority of guests, only a single OS is
240 installed.) The data returned for the OS includes the name (Linux,
241 Windows), the distribution and version.
243 The diagram above shows what we return for each OS.
245 With the I<--xml> option the output is mapped into an XML document.
246 Unfortunately there is no clear schema for this document
247 (contributions welcome) but you can get an idea of the format by
248 looking at other documents and as a last resort the source for this
251 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
252 L<guestfish(1)> command line parameters, so that you can go in
253 afterwards and inspect the guest with everything mounted in the
254 right place. For example:
256 guestfish $(virt-inspector --ro-fish guest.img)
257 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
261 # List of possible filesystems.
262 my @partitions = get_partitions ($g);
264 # Now query each one to build up a picture of what's in it.
266 inspect_all_partitions ($g, \@partitions,
267 use_windows_registry => $windows_registry);
269 #print "fses -----------\n";
270 #print Dumper(\%fses);
272 my $oses = inspect_operating_systems ($g, \%fses);
274 #print "oses -----------\n";
275 #print Dumper($oses);
277 # Mount up the disks so we can check for applications
278 # and kernels. Skip this if the output is "*fish" because
279 # we don't need to know.
281 if ($output !~ /.*fish$/) {
283 foreach $root_dev (sort keys %$oses) {
284 my $os = $oses->{$root_dev};
285 mount_operating_system ($g, $os);
286 inspect_in_detail ($g, $os);
291 #----------------------------------------------------------------------
294 if ($output eq "fish" || $output eq "ro-fish") {
295 my @osdevs = keys %$oses;
296 # This only works if there is a single OS.
297 die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
299 my $root_dev = $osdevs[0];
301 if ($output eq "ro-fish") {
305 print "-a $_ " foreach @ARGV;
307 my $mounts = $oses->{$root_dev}->{mounts};
308 # Have to mount / first. Luckily '/' is early in the ASCII
309 # character set, so this should be OK.
310 foreach (sort keys %$mounts) {
311 print "-m $mounts->{$_}:$_ " if $_ ne "swap" && $_ ne "none";
317 elsif ($output eq "perl") {
318 print Dumper(%$oses);
322 elsif ($output eq "yaml") {
323 die __"virt-inspector: no YAML support\n"
324 unless exists $INC{"YAML/Any.pm"};
329 # Plain text output (the default).
330 elsif ($output eq "text") {
335 elsif ($output eq "xml") {
340 elsif ($output eq "query") {
346 output_text_os ($oses->{$_}) foreach sort keys %$oses;
353 print $os->{os}, " " if exists $os->{os};
354 print $os->{distro}, " " if exists $os->{distro};
355 print $os->{version}, " " if exists $os->{version};
356 print "(".$os->{distrofamily}." family)", " " if exists $os->{distrofamily};
357 print "on ", $os->{root_device}, ":\n";
359 print __" Mountpoints:\n";
360 my $mounts = $os->{mounts};
361 foreach (sort keys %$mounts) {
362 printf " %-30s %s\n", $mounts->{$_}, $_
365 print __" Filesystems:\n";
366 my $filesystems = $os->{filesystems};
367 foreach (sort keys %$filesystems) {
369 print " label: $filesystems->{$_}{label}\n"
370 if exists $filesystems->{$_}{label};
371 print " UUID: $filesystems->{$_}{uuid}\n"
372 if exists $filesystems->{$_}{uuid};
373 print " type: $filesystems->{$_}{fstype}\n"
374 if exists $filesystems->{$_}{fstype};
375 print " content: $filesystems->{$_}{content}\n"
376 if exists $filesystems->{$_}{content};
379 if (exists $os->{modprobe_aliases}) {
380 my %aliases = %{$os->{modprobe_aliases}};
381 my @keys = sort keys %aliases;
383 print __" Modprobe aliases:\n";
385 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
390 if (exists $os->{initrd_modules}) {
391 my %modvers = %{$os->{initrd_modules}};
392 my @keys = sort keys %modvers;
394 print __" Initrd modules:\n";
396 my @modules = @{$modvers{$_}};
398 print " $_\n" foreach @modules;
403 print __" Applications:\n";
404 my @apps = @{$os->{apps}};
406 print " $_->{name} $_->{version}\n"
409 print __" Kernels:\n";
410 my @kernels = @{$os->{kernels}};
412 print " $_->{version}\n";
413 my @modules = @{$_->{modules}};
419 if (exists $os->{root}->{registry}) {
420 print __" Windows Registry entries:\n";
421 # These are just lumps of text - dump them out.
422 foreach (@{$os->{root}->{registry}}) {
430 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
432 $xml->startTag("operatingsystems");
433 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
434 $xml->endTag("operatingsystems");
443 $xml->startTag("operatingsystem");
445 foreach ( [ "name" => "os" ],
446 [ "distro" => "distro" ],
447 [ "distrofamily" => "distrofamily" ],
448 [ "version" => "version" ],
449 [ "root" => "root_device" ] ) {
450 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
453 $xml->startTag("mountpoints");
454 my $mounts = $os->{mounts};
455 foreach (sort keys %$mounts) {
456 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
458 $xml->endTag("mountpoints");
460 $xml->startTag("filesystems");
461 my $filesystems = $os->{filesystems};
462 foreach (sort keys %$filesystems) {
463 $xml->startTag("filesystem", "dev" => $_);
465 foreach my $field ( [ "label" => "label" ],
466 [ "uuid" => "uuid" ],
467 [ "type" => "fstype" ],
468 [ "content" => "content" ],
469 [ "spec" => "spec" ] ) {
470 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
471 if exists $filesystems->{$_}{$field->[1]};
474 $xml->endTag("filesystem");
476 $xml->endTag("filesystems");
478 if (exists $os->{modprobe_aliases}) {
479 my %aliases = %{$os->{modprobe_aliases}};
480 my @keys = sort keys %aliases;
482 $xml->startTag("modprobealiases");
484 $xml->startTag("alias", "device" => $_);
486 foreach my $field ( [ "modulename" => "modulename" ],
487 [ "augeas" => "augeas" ],
488 [ "file" => "file" ] ) {
489 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
492 $xml->endTag("alias");
494 $xml->endTag("modprobealiases");
498 if (exists $os->{initrd_modules}) {
499 my %modvers = %{$os->{initrd_modules}};
500 my @keys = sort keys %modvers;
502 $xml->startTag("initrds");
504 my @modules = @{$modvers{$_}};
505 $xml->startTag("initrd", "version" => $_);
506 $xml->dataElement("module", $_) foreach @modules;
507 $xml->endTag("initrd");
509 $xml->endTag("initrds");
513 $xml->startTag("applications");
514 my @apps = @{$os->{apps}};
516 $xml->startTag("application");
517 $xml->dataElement("name", $_->{name});
518 $xml->dataElement("version", $_->{version});
519 $xml->endTag("application");
521 $xml->endTag("applications");
523 $xml->startTag("kernels");
524 my @kernels = @{$os->{kernels}};
526 $xml->startTag("kernel", "version" => $_->{version});
527 $xml->startTag("modules");
528 my @modules = @{$_->{modules}};
530 $xml->dataElement("module", $_);
532 $xml->endTag("modules");
533 $xml->endTag("kernel");
535 $xml->endTag("kernels");
537 if (exists $os->{root}->{registry}) {
538 $xml->startTag("windowsregistryentries");
539 # These are just lumps of text - dump them out.
540 foreach (@{$os->{root}->{registry}}) {
541 $xml->dataElement("windowsregistryentry", $_);
543 $xml->endTag("windowsregistryentries");
546 $xml->endTag("operatingsystem");
551 When you use C<virt-inspector --query>, the output is a series of
559 (each answer is usually C<yes> or C<no>, or the line is completely
560 missing if we could not determine the answer at all).
562 If the guest is multiboot, you can get apparently conflicting answers
563 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
564 fullvirt and has a Xen PV kernel). This is normal, and just means
565 that the guest can do both things, although it might require operator
566 intervention such as selecting a boot option when the guest is
569 This section describes the full range of answers possible.
577 output_query_windows ();
578 output_query_linux ();
579 output_query_rhel ();
580 output_query_fedora ();
581 output_query_debian ();
582 output_query_fullvirt ();
583 output_query_xen_domU_kernel ();
584 output_query_xen_pv_drivers ();
585 output_query_virtio_drivers ();
588 =item windows=(yes|no)
590 Answer C<yes> if Microsoft Windows is installed in the guest.
594 sub output_query_windows
597 foreach my $os (keys %$oses) {
598 $windows="yes" if $oses->{$os}->{os} eq "windows";
600 print "windows=$windows\n";
605 Answer C<yes> if a Linux kernel is installed in the guest.
609 sub output_query_linux
612 foreach my $os (keys %$oses) {
613 $linux="yes" if $oses->{$os}->{os} eq "linux";
615 print "linux=$linux\n";
620 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
624 sub output_query_rhel
627 foreach my $os (keys %$oses) {
628 $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
629 $oses->{$os}->{distro} eq "rhel");
631 print "rhel=$rhel\n";
634 =item fedora=(yes|no)
636 Answer C<yes> if the guest contains the Fedora Linux distribution.
640 sub output_query_fedora
643 foreach my $os (keys %$oses) {
644 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
646 print "fedora=$fedora\n";
649 =item debian=(yes|no)
651 Answer C<yes> if the guest contains the Debian Linux distribution.
655 sub output_query_debian
658 foreach my $os (keys %$oses) {
659 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
661 print "debian=$debian\n";
664 =item fullvirt=(yes|no)
666 Answer C<yes> if there is at least one operating system kernel
667 installed in the guest which runs fully virtualized. Such a guest
668 would require a hypervisor which supports full system virtualization.
672 sub output_query_fullvirt
674 # The assumption is full-virt, unless all installed kernels
675 # are identified as paravirt.
676 # XXX Fails on Windows guests.
677 foreach my $os (keys %$oses) {
678 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
679 my $is_pv = $kernel->{version} =~ m/xen/;
681 print "fullvirt=yes\n";
686 print "fullvirt=no\n";
689 =item xen_domU_kernel=(yes|no)
691 Answer C<yes> if there is at least one Linux kernel installed in
692 the guest which is compiled as a Xen DomU (a Xen paravirtualized
697 sub output_query_xen_domU_kernel
699 foreach my $os (keys %$oses) {
700 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
701 my $is_xen = $kernel->{version} =~ m/xen/;
703 print "xen_domU_kernel=yes\n";
708 print "xen_domU_kernel=no\n";
711 =item xen_pv_drivers=(yes|no)
713 Answer C<yes> if the guest has Xen paravirtualized drivers installed
714 (usually the kernel itself will be fully virtualized, but the PV
715 drivers have been installed by the administrator for performance
720 sub output_query_xen_pv_drivers
722 foreach my $os (keys %$oses) {
723 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
724 foreach my $module (@{$kernel->{modules}}) {
725 if ($module =~ m/xen-/) {
726 print "xen_pv_drivers=yes\n";
732 print "xen_pv_drivers=no\n";
735 =item virtio_drivers=(yes|no)
737 Answer C<yes> if the guest has virtio paravirtualized drivers
738 installed. Virtio drivers are commonly used to improve the
743 sub output_query_virtio_drivers
745 foreach my $os (keys %$oses) {
746 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
747 foreach my $module (@{$kernel->{modules}}) {
748 if ($module =~ m/virtio_/) {
749 print "virtio_drivers=yes\n";
755 print "virtio_drivers=no\n";
765 L<Sys::Guestfs::Lib(3)>,
767 L<http://libguestfs.org/>.
769 For Windows registry parsing we require the C<reged> program
770 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
774 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
776 Matthew Booth L<mbooth@redhat.com>
780 Copyright (C) 2009 Red Hat Inc.
782 This program is free software; you can redistribute it and/or modify
783 it under the terms of the GNU General Public License as published by
784 the Free Software Foundation; either version 2 of the License, or
785 (at your option) any later version.
787 This program is distributed in the hope that it will be useful,
788 but WITHOUT ANY WARRANTY; without even the implied warranty of
789 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
790 GNU General Public License for more details.
792 You should have received a copy of the GNU General Public License
793 along with this program; if not, write to the Free Software
794 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.