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 String::ShellQuote qw(shell_quote);
31 use Locale::TextDomain 'libguestfs';
34 eval "use YAML::Any;";
40 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
44 virt-inspector [--connect URI] domname
46 virt-inspector guest.img [guest.img ...]
50 B<virt-inspector> examines a virtual machine and tries to determine
51 the version of the OS, the kernel version, what drivers are installed,
52 whether the virtual machine is fully virtualized (FV) or
53 para-virtualized (PV), what applications are installed and more.
55 Virt-inspector can produce output in several formats, including a
56 readable text report, and XML for feeding into other programs.
58 In the normal usage, use C<virt-inspector domname> where C<domname> is
59 the libvirt domain (see: C<virsh list --all>).
61 You can also run virt-inspector directly on disk images from a single
62 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
63 domain has several block devices, in which case you should list them
64 one after another, with the first corresponding to the guest's
65 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
67 Virt-inspector can only inspect and report upon I<one domain at a
68 time>. To inspect several virtual machines, you have to run
69 virt-inspector several times (for example, from a shell script
72 Because virt-inspector needs direct access to guest images, it won't
73 normally work over remote libvirt connections.
93 Display version number and exit.
99 =item B<--connect URI> | B<-c URI>
101 If using libvirt, connect to the given I<URI>. If omitted,
102 then we connect to the default libvirt hypervisor.
104 Libvirt is only used if you specify a C<domname> on the
105 command line. If you specify guest block devices directly,
106 then libvirt is not used at all.
114 The following options select the output format. Use only one of them.
115 The default is a readable text report.
119 =item B<--text> (default)
125 Produce no output at all.
129 If you select I<--xml> then you get XML output which can be fed
134 If you select I<--yaml> then you get YAML output which can be fed
139 If you select I<--perl> then you get Perl structures output which
140 can be used directly in another Perl program.
146 If you select I<--fish> then we print a L<guestfish(1)> command
147 line which will automatically mount up the filesystems on the
148 correct mount points. Try this for example:
150 guestfish $(virt-inspector --fish guest.img)
152 I<--ro-fish> is the same, but the I<--ro> option is passed to
153 guestfish so that the filesystems are mounted read-only.
157 In "query mode" we answer common questions about the guest, such
158 as whether it is fullvirt or needs a Xen hypervisor to run.
160 See section I<QUERY MODE> below.
164 my $windows_registry;
166 =item B<--windows-registry>
168 This flag is ignored for compatibility with earlier releases of the
171 In this version, if L<Win::Hivex(3)> is available, then we attempt to
172 parse information out of the Registry for any Windows guest.
178 GetOptions ("help|?" => \$help,
179 "version" => \$version,
180 "connect|c=s" => \$uri,
181 "text" => sub { $output = "text" },
182 "none" => sub { $output = "none" },
183 "xml" => sub { $output = "xml" },
184 "yaml" => sub { $output = "yaml" },
185 "perl" => sub { $output = "perl" },
186 "fish" => sub { $output = "fish" },
187 "guestfish" => sub { $output = "fish" },
188 "ro-fish" => sub { $output = "ro-fish" },
189 "ro-guestfish" => sub { $output = "ro-fish" },
190 "query" => sub { $output = "query" },
191 "windows-registry" => \$windows_registry,
193 pod2usage (1) if $help;
195 my $g = Sys::Guestfs->new ();
196 my %h = $g->version ();
197 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
200 pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
203 $rw = 1 if $output eq "fish";
208 ($g, $conn, $dom, @images) =
209 open_guest (\@ARGV, rw => $rw, address => $uri);
212 ($g, $conn, $dom, @images) =
213 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 There is a RELAX-NG schema for this XML in the file
246 I<virt-inspector.rng> which normally ships with virt-inspector, or can
247 be found in the source.
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);
266 #print "fses -----------\n";
267 #print Dumper(\%fses);
269 my $oses = inspect_operating_systems ($g, \%fses);
271 #print "oses -----------\n";
272 #print Dumper($oses);
274 # Mount up the disks so we can check for applications
275 # and kernels. Skip this if the output is "*fish" because
276 # we don't need to know.
278 if ($output !~ /.*fish$/) {
280 foreach $root_dev (sort keys %$oses) {
281 my $os = $oses->{$root_dev};
282 mount_operating_system ($g, $os);
283 inspect_in_detail ($g, $os);
288 #----------------------------------------------------------------------
291 if ($output eq "fish" || $output eq "ro-fish") {
292 my @osdevs = keys %$oses;
293 # This only works if there is a single OS.
294 die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
296 my $root_dev = $osdevs[0];
298 if ($output eq "ro-fish") {
303 printf "-a %s ", shell_quote ($_);
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 if ($_ ne "swap" && $_ ne "none") {
311 printf "-m %s ", shell_quote ("$mounts->{$_}:$_");
318 elsif ($output eq "perl") {
319 print Dumper(%$oses);
323 elsif ($output eq "yaml") {
324 die __"virt-inspector: no YAML support, try installing perl-YAML or libyaml-perl\n"
325 unless exists $INC{"YAML/Any.pm"};
330 # Plain text output (the default).
331 elsif ($output eq "text") {
336 elsif ($output eq "xml") {
341 elsif ($output eq "query") {
347 output_text_os ($oses->{$_}) foreach sort keys %$oses;
354 print $os->{os}, " " if exists $os->{os};
355 print $os->{distro}, " " if exists $os->{distro};
356 print $os->{arch}, " " if exists $os->{arch};
357 print $os->{major_version} if exists $os->{major_version};
358 print ".", $os->{minor_version} if exists $os->{minor_version};
359 print " (", $os->{product_name}, ")" if exists $os->{product_name};
361 print "on ", $os->{root_device}, ":\n";
363 print __" Mountpoints:\n";
364 my $mounts = $os->{mounts};
365 foreach (sort keys %$mounts) {
366 printf " %-30s %s\n", $mounts->{$_}, $_
369 print __" Filesystems:\n";
370 my $filesystems = $os->{filesystems};
371 foreach (sort keys %$filesystems) {
373 print " label: $filesystems->{$_}{label}\n"
374 if exists $filesystems->{$_}{label};
375 print " UUID: $filesystems->{$_}{uuid}\n"
376 if exists $filesystems->{$_}{uuid};
377 print " type: $filesystems->{$_}{fstype}\n"
378 if exists $filesystems->{$_}{fstype};
379 print " content: $filesystems->{$_}{content}\n"
380 if exists $filesystems->{$_}{content};
383 if (exists $os->{modprobe_aliases}) {
384 my %aliases = %{$os->{modprobe_aliases}};
385 my @keys = sort keys %aliases;
387 print __" Modprobe aliases:\n";
389 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
394 if (exists $os->{initrd_modules}) {
395 my %modvers = %{$os->{initrd_modules}};
396 my @keys = sort keys %modvers;
398 print __" Initrd modules:\n";
400 my @modules = @{$modvers{$_}};
402 print " $_\n" foreach @modules;
407 print __" Applications:\n";
408 my @apps = @{$os->{apps}};
410 print " $_->{name} $_->{version}\n"
413 if ($os->{kernels}) {
414 print __" Kernels:\n";
415 my @kernels = @{$os->{kernels}};
417 print " $_->{version} ($_->{arch})\n";
418 my @modules = @{$_->{modules}};
425 if (exists $os->{root}->{registry}) {
426 print __" Windows Registry entries:\n";
427 # These are just lumps of text - dump them out.
428 foreach (@{$os->{root}->{registry}}) {
436 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
438 $xml->startTag("operatingsystems");
439 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
440 $xml->endTag("operatingsystems");
449 $xml->startTag("operatingsystem");
451 foreach ( [ "name" => "os" ],
452 [ "distro" => "distro" ],
453 [ "product_name" => "product_name" ],
454 [ "arch" => "arch" ],
455 [ "major_version" => "major_version" ],
456 [ "minor_version" => "minor_version" ],
457 [ "package_format" => "package_format" ],
458 [ "package_management" => "package_management" ],
459 [ "root" => "root_device" ] ) {
460 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
463 $xml->startTag("mountpoints");
464 my $mounts = $os->{mounts};
465 foreach (sort keys %$mounts) {
466 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
468 $xml->endTag("mountpoints");
470 $xml->startTag("filesystems");
471 my $filesystems = $os->{filesystems};
472 foreach (sort keys %$filesystems) {
473 $xml->startTag("filesystem", "dev" => $_);
475 foreach my $field ( [ "label" => "label" ],
476 [ "uuid" => "uuid" ],
477 [ "type" => "fstype" ],
478 [ "content" => "content" ],
479 [ "spec" => "spec" ] ) {
480 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
481 if exists $filesystems->{$_}{$field->[1]};
484 $xml->endTag("filesystem");
486 $xml->endTag("filesystems");
488 if (exists $os->{modprobe_aliases}) {
489 my %aliases = %{$os->{modprobe_aliases}};
490 my @keys = sort keys %aliases;
492 $xml->startTag("modprobealiases");
494 $xml->startTag("alias", "device" => $_);
496 foreach my $field ( [ "modulename" => "modulename" ],
497 [ "augeas" => "augeas" ],
498 [ "file" => "file" ] ) {
499 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
502 $xml->endTag("alias");
504 $xml->endTag("modprobealiases");
508 if (exists $os->{initrd_modules}) {
509 my %modvers = %{$os->{initrd_modules}};
510 my @keys = sort keys %modvers;
512 $xml->startTag("initrds");
514 my @modules = @{$modvers{$_}};
515 $xml->startTag("initrd", "version" => $_);
516 $xml->dataElement("module", $_) foreach @modules;
517 $xml->endTag("initrd");
519 $xml->endTag("initrds");
523 $xml->startTag("applications");
524 my @apps = @{$os->{apps}};
526 $xml->startTag("application");
527 $xml->dataElement("name", $_->{name});
528 $xml->dataElement("epoch", $_->{epoch}) if defined $_->{epoch};
529 $xml->dataElement("version", $_->{version});
530 $xml->dataElement("release", $_->{release});
531 $xml->dataElement("arch", $_->{arch});
532 $xml->endTag("application");
534 $xml->endTag("applications");
536 if(defined($os->{boot}) && defined($os->{boot}->{configs})) {
537 my $default = $os->{boot}->{default};
538 my $configs = $os->{boot}->{configs};
540 $xml->startTag("boot");
541 for(my $i = 0; $i < scalar(@$configs); $i++) {
542 my $config = $configs->[$i];
545 push(@attrs, ("default" => 1)) if($default == $i);
546 $xml->startTag("config", @attrs);
547 $xml->dataElement("title", $config->{title});
548 $xml->dataElement("kernel", $config->{kernel}->{version})
549 if(defined($config->{kernel}));
550 $xml->dataElement("cmdline", $config->{cmdline})
551 if(defined($config->{cmdline}));
552 $xml->endTag("config");
554 $xml->endTag("boot");
557 if ($os->{kernels}) {
558 $xml->startTag("kernels");
559 my @kernels = @{$os->{kernels}};
561 $xml->startTag("kernel",
562 "version" => $_->{version},
563 "arch" => $_->{arch});
564 $xml->startTag("modules");
565 my @modules = @{$_->{modules}};
567 $xml->dataElement("module", $_);
569 $xml->endTag("modules");
570 $xml->dataElement("path", $_->{path}) if(defined($_->{path}));
571 $xml->dataElement("package", $_->{package}) if(defined($_->{package}));
572 $xml->endTag("kernel");
574 $xml->endTag("kernels");
577 if (exists $os->{root}->{registry}) {
578 $xml->startTag("windowsregistryentries");
579 # These are just lumps of text - dump them out.
580 foreach (@{$os->{root}->{registry}}) {
581 $xml->dataElement("windowsregistryentry", $_);
583 $xml->endTag("windowsregistryentries");
586 $xml->endTag("operatingsystem");
591 When you use C<virt-inspector --query>, the output is a series of
599 (each answer is usually C<yes> or C<no>, or the line is completely
600 missing if we could not determine the answer at all).
602 If the guest is multiboot, you can get apparently conflicting answers
603 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
604 fullvirt and has a Xen PV kernel). This is normal, and just means
605 that the guest can do both things, although it might require operator
606 intervention such as selecting a boot option when the guest is
609 This section describes the full range of answers possible.
617 output_query_windows ();
618 output_query_linux ();
619 output_query_rhel ();
620 output_query_fedora ();
621 output_query_debian ();
622 output_query_fullvirt ();
623 output_query_xen_domU_kernel ();
624 output_query_xen_pv_drivers ();
625 output_query_virtio_drivers ();
626 output_query_kernel_arch ();
627 output_query_userspace_arch ();
630 =item windows=(yes|no)
632 Answer C<yes> if Microsoft Windows is installed in the guest.
636 sub output_query_windows
639 foreach my $os (keys %$oses) {
640 $windows="yes" if $oses->{$os}->{os} eq "windows";
642 print "windows=$windows\n";
647 Answer C<yes> if a Linux kernel is installed in the guest.
651 sub output_query_linux
654 foreach my $os (keys %$oses) {
655 $linux="yes" if $oses->{$os}->{os} eq "linux";
657 print "linux=$linux\n";
662 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
666 sub output_query_rhel
669 foreach my $os (keys %$oses) {
670 $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
671 $oses->{$os}->{distro} eq "rhel");
673 print "rhel=$rhel\n";
676 =item fedora=(yes|no)
678 Answer C<yes> if the guest contains the Fedora Linux distribution.
682 sub output_query_fedora
685 foreach my $os (keys %$oses) {
686 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
688 print "fedora=$fedora\n";
691 =item debian=(yes|no)
693 Answer C<yes> if the guest contains the Debian Linux distribution.
697 sub output_query_debian
700 foreach my $os (keys %$oses) {
701 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
703 print "debian=$debian\n";
706 =item fullvirt=(yes|no)
708 Answer C<yes> if there is at least one operating system kernel
709 installed in the guest which runs fully virtualized. Such a guest
710 would require a hypervisor which supports full system virtualization.
714 sub output_query_fullvirt
716 # The assumption is full-virt, unless all installed kernels
717 # are identified as paravirt.
718 # XXX Fails on Windows guests.
719 foreach my $os (keys %$oses) {
720 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
721 my $is_pv = $kernel->{version} =~ m/xen/;
723 print "fullvirt=yes\n";
728 print "fullvirt=no\n";
731 =item xen_domU_kernel=(yes|no)
733 Answer C<yes> if there is at least one Linux kernel installed in
734 the guest which is compiled as a Xen DomU (a Xen paravirtualized
739 sub output_query_xen_domU_kernel
741 foreach my $os (keys %$oses) {
742 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
743 my $is_xen = $kernel->{version} =~ m/xen/;
745 print "xen_domU_kernel=yes\n";
750 print "xen_domU_kernel=no\n";
753 =item xen_pv_drivers=(yes|no)
755 Answer C<yes> if the guest has Xen paravirtualized drivers installed
756 (usually the kernel itself will be fully virtualized, but the PV
757 drivers have been installed by the administrator for performance
762 sub output_query_xen_pv_drivers
764 foreach my $os (keys %$oses) {
765 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
766 foreach my $module (@{$kernel->{modules}}) {
767 if ($module =~ m/xen-/) {
768 print "xen_pv_drivers=yes\n";
774 print "xen_pv_drivers=no\n";
777 =item virtio_drivers=(yes|no)
779 Answer C<yes> if the guest has virtio paravirtualized drivers
780 installed. Virtio drivers are commonly used to improve the
785 sub output_query_virtio_drivers
787 foreach my $os (keys %$oses) {
788 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
789 foreach my $module (@{$kernel->{modules}}) {
790 if ($module =~ m/virtio_/) {
791 print "virtio_drivers=yes\n";
797 print "virtio_drivers=no\n";
800 =item userspace_arch=(x86_64|...)
802 Print the architecture of userspace.
804 NB. For multi-boot VMs this can print several lines.
808 sub output_query_userspace_arch
812 foreach my $os (keys %$oses) {
813 $arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
816 foreach (sort keys %arches) {
817 print "userspace_arch=$_\n";
821 =item kernel_arch=(x86_64|...)
823 Print the architecture of the kernel.
825 NB. For multi-boot VMs this can print several lines.
829 sub output_query_kernel_arch
833 foreach my $os (keys %$oses) {
834 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
835 $arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
839 foreach (sort keys %arches) {
840 print "kernel_arch=$_\n";
851 L<Sys::Guestfs::Lib(3)>,
853 L<http://libguestfs.org/>.
857 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
859 Matthew Booth L<mbooth@redhat.com>
863 Copyright (C) 2009 Red Hat Inc.
865 This program is free software; you can redistribute it and/or modify
866 it under the terms of the GNU General Public License as published by
867 the Free Software Foundation; either version 2 of the License, or
868 (at your option) any later version.
870 This program is distributed in the hope that it will be useful,
871 but WITHOUT ANY WARRANTY; without even the implied warranty of
872 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
873 GNU General Public License for more details.
875 You should have received a copy of the GNU General Public License
876 along with this program; if not, write to the Free Software
877 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.