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 In the normal usage, use C<virt-inspector domname> where C<domname> is
58 the libvirt domain (see: C<virsh list --all>).
60 You can also run virt-inspector directly on disk images from a single
61 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
62 domain has several block devices, in which case you should list them
63 one after another, with the first corresponding to the guest's
64 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
66 Virt-inspector can only inspect and report upon I<one domain at a
67 time>. To inspect several virtual machines, you have to run
68 virt-inspector several times (for example, from a shell script
71 Because virt-inspector needs direct access to guest images, it won't
72 normally work over remote libvirt connections.
92 Display version number and exit.
98 =item B<--connect URI> | B<-c URI>
100 If using libvirt, connect to the given I<URI>. If omitted,
101 then we connect to the default libvirt hypervisor.
103 Libvirt is only used if you specify a C<domname> on the
104 command line. If you specify guest block devices directly,
105 then libvirt is not used at all.
113 The following options select the output format. Use only one of them.
114 The default is a readable text report.
118 =item B<--text> (default)
124 Produce no output at all.
128 If you select I<--xml> then you get XML output which can be fed
133 If you select I<--yaml> then you get YAML output which can be fed
138 If you select I<--perl> then you get Perl structures output which
139 can be used directly in another Perl program.
145 If you select I<--fish> then we print a L<guestfish(1)> command
146 line which will automatically mount up the filesystems on the
147 correct mount points. Try this for example:
149 guestfish $(virt-inspector --fish guest.img)
151 I<--ro-fish> is the same, but the I<--ro> option is passed to
152 guestfish so that the filesystems are mounted read-only.
156 In "query mode" we answer common questions about the guest, such
157 as whether it is fullvirt or needs a Xen hypervisor to run.
159 See section I<QUERY MODE> below.
163 my $windows_registry;
165 =item B<--windows-registry>
167 If this item is passed, I<and> the guest is Windows, I<and> the
168 external program C<reged> is available (see SEE ALSO section), then we
169 attempt to parse the Windows registry. This allows much more
170 information to be gathered for Windows guests.
172 This is quite an expensive and slow operation, so we don't do it by
179 GetOptions ("help|?" => \$help,
180 "version" => \$version,
181 "connect|c=s" => \$uri,
182 "text" => sub { $output = "text" },
183 "none" => sub { $output = "none" },
184 "xml" => sub { $output = "xml" },
185 "yaml" => sub { $output = "yaml" },
186 "perl" => sub { $output = "perl" },
187 "fish" => sub { $output = "fish" },
188 "guestfish" => sub { $output = "fish" },
189 "ro-fish" => sub { $output = "ro-fish" },
190 "ro-guestfish" => sub { $output = "ro-fish" },
191 "query" => sub { $output = "query" },
192 "windows-registry" => \$windows_registry,
194 pod2usage (1) if $help;
196 my $g = Sys::Guestfs->new ();
197 my %h = $g->version ();
198 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
201 pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
204 $rw = 1 if $output eq "fish";
209 ($g, $conn, $dom, @images) =
210 open_guest (\@ARGV, rw => $rw, address => $uri);
213 ($g, $conn, $dom, @images) =
214 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 There is a RELAX-NG schema for this XML in the file
247 I<virt-inspector.rng> which normally ships with virt-inspector, or can
248 be found in the source.
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 @images;
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->{arch}, " " if exists $os->{arch};
355 print $os->{major_version} if exists $os->{major_version};
356 print ".", $os->{minor_version} if exists $os->{minor_version};
357 print " (", $os->{product_name}, ")" if exists $os->{product_name};
359 print "on ", $os->{root_device}, ":\n";
361 print __" Mountpoints:\n";
362 my $mounts = $os->{mounts};
363 foreach (sort keys %$mounts) {
364 printf " %-30s %s\n", $mounts->{$_}, $_
367 print __" Filesystems:\n";
368 my $filesystems = $os->{filesystems};
369 foreach (sort keys %$filesystems) {
371 print " label: $filesystems->{$_}{label}\n"
372 if exists $filesystems->{$_}{label};
373 print " UUID: $filesystems->{$_}{uuid}\n"
374 if exists $filesystems->{$_}{uuid};
375 print " type: $filesystems->{$_}{fstype}\n"
376 if exists $filesystems->{$_}{fstype};
377 print " content: $filesystems->{$_}{content}\n"
378 if exists $filesystems->{$_}{content};
381 if (exists $os->{modprobe_aliases}) {
382 my %aliases = %{$os->{modprobe_aliases}};
383 my @keys = sort keys %aliases;
385 print __" Modprobe aliases:\n";
387 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
392 if (exists $os->{initrd_modules}) {
393 my %modvers = %{$os->{initrd_modules}};
394 my @keys = sort keys %modvers;
396 print __" Initrd modules:\n";
398 my @modules = @{$modvers{$_}};
400 print " $_\n" foreach @modules;
405 print __" Applications:\n";
406 my @apps = @{$os->{apps}};
408 print " $_->{name} $_->{version}\n"
411 if ($os->{kernels}) {
412 print __" Kernels:\n";
413 my @kernels = @{$os->{kernels}};
415 print " $_->{version} ($_->{arch})\n";
416 my @modules = @{$_->{modules}};
423 if (exists $os->{root}->{registry}) {
424 print __" Windows Registry entries:\n";
425 # These are just lumps of text - dump them out.
426 foreach (@{$os->{root}->{registry}}) {
434 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
436 $xml->startTag("operatingsystems");
437 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
438 $xml->endTag("operatingsystems");
447 $xml->startTag("operatingsystem");
449 foreach ( [ "name" => "os" ],
450 [ "distro" => "distro" ],
451 [ "product_name" => "product_name" ],
452 [ "arch" => "arch" ],
453 [ "major_version" => "major_version" ],
454 [ "minor_version" => "minor_version" ],
455 [ "package_format" => "package_format" ],
456 [ "package_management" => "package_management" ],
457 [ "root" => "root_device" ] ) {
458 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
461 $xml->startTag("mountpoints");
462 my $mounts = $os->{mounts};
463 foreach (sort keys %$mounts) {
464 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
466 $xml->endTag("mountpoints");
468 $xml->startTag("filesystems");
469 my $filesystems = $os->{filesystems};
470 foreach (sort keys %$filesystems) {
471 $xml->startTag("filesystem", "dev" => $_);
473 foreach my $field ( [ "label" => "label" ],
474 [ "uuid" => "uuid" ],
475 [ "type" => "fstype" ],
476 [ "content" => "content" ],
477 [ "spec" => "spec" ] ) {
478 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
479 if exists $filesystems->{$_}{$field->[1]};
482 $xml->endTag("filesystem");
484 $xml->endTag("filesystems");
486 if (exists $os->{modprobe_aliases}) {
487 my %aliases = %{$os->{modprobe_aliases}};
488 my @keys = sort keys %aliases;
490 $xml->startTag("modprobealiases");
492 $xml->startTag("alias", "device" => $_);
494 foreach my $field ( [ "modulename" => "modulename" ],
495 [ "augeas" => "augeas" ],
496 [ "file" => "file" ] ) {
497 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
500 $xml->endTag("alias");
502 $xml->endTag("modprobealiases");
506 if (exists $os->{initrd_modules}) {
507 my %modvers = %{$os->{initrd_modules}};
508 my @keys = sort keys %modvers;
510 $xml->startTag("initrds");
512 my @modules = @{$modvers{$_}};
513 $xml->startTag("initrd", "version" => $_);
514 $xml->dataElement("module", $_) foreach @modules;
515 $xml->endTag("initrd");
517 $xml->endTag("initrds");
521 $xml->startTag("applications");
522 my @apps = @{$os->{apps}};
524 $xml->startTag("application");
525 $xml->dataElement("name", $_->{name});
526 $xml->dataElement("epoch", $_->{epoch}) if defined $_->{epoch};
527 $xml->dataElement("version", $_->{version});
528 $xml->dataElement("release", $_->{release});
529 $xml->dataElement("arch", $_->{arch});
530 $xml->endTag("application");
532 $xml->endTag("applications");
534 if(defined($os->{boot}) && defined($os->{boot}->{configs})) {
535 my $default = $os->{boot}->{default};
536 my $configs = $os->{boot}->{configs};
538 $xml->startTag("boot");
539 for(my $i = 0; $i < scalar(@$configs); $i++) {
540 my $config = $configs->[$i];
543 push(@attrs, ("default" => 1)) if($default == $i);
544 $xml->startTag("config", @attrs);
545 $xml->dataElement("title", $config->{title});
546 $xml->dataElement("kernel", $config->{kernel}->{version})
547 if(defined($config->{kernel}));
548 $xml->dataElement("cmdline", $config->{cmdline})
549 if(defined($config->{cmdline}));
550 $xml->endTag("config");
552 $xml->endTag("boot");
555 if ($os->{kernels}) {
556 $xml->startTag("kernels");
557 my @kernels = @{$os->{kernels}};
559 $xml->startTag("kernel",
560 "version" => $_->{version},
561 "arch" => $_->{arch});
562 $xml->startTag("modules");
563 my @modules = @{$_->{modules}};
565 $xml->dataElement("module", $_);
567 $xml->endTag("modules");
568 $xml->dataElement("path", $_->{path}) if(defined($_->{path}));
569 $xml->dataElement("package", $_->{package}) if(defined($_->{package}));
570 $xml->endTag("kernel");
572 $xml->endTag("kernels");
575 if (exists $os->{root}->{registry}) {
576 $xml->startTag("windowsregistryentries");
577 # These are just lumps of text - dump them out.
578 foreach (@{$os->{root}->{registry}}) {
579 $xml->dataElement("windowsregistryentry", $_);
581 $xml->endTag("windowsregistryentries");
584 $xml->endTag("operatingsystem");
589 When you use C<virt-inspector --query>, the output is a series of
597 (each answer is usually C<yes> or C<no>, or the line is completely
598 missing if we could not determine the answer at all).
600 If the guest is multiboot, you can get apparently conflicting answers
601 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
602 fullvirt and has a Xen PV kernel). This is normal, and just means
603 that the guest can do both things, although it might require operator
604 intervention such as selecting a boot option when the guest is
607 This section describes the full range of answers possible.
615 output_query_windows ();
616 output_query_linux ();
617 output_query_rhel ();
618 output_query_fedora ();
619 output_query_debian ();
620 output_query_fullvirt ();
621 output_query_xen_domU_kernel ();
622 output_query_xen_pv_drivers ();
623 output_query_virtio_drivers ();
624 output_query_kernel_arch ();
625 output_query_userspace_arch ();
628 =item windows=(yes|no)
630 Answer C<yes> if Microsoft Windows is installed in the guest.
634 sub output_query_windows
637 foreach my $os (keys %$oses) {
638 $windows="yes" if $oses->{$os}->{os} eq "windows";
640 print "windows=$windows\n";
645 Answer C<yes> if a Linux kernel is installed in the guest.
649 sub output_query_linux
652 foreach my $os (keys %$oses) {
653 $linux="yes" if $oses->{$os}->{os} eq "linux";
655 print "linux=$linux\n";
660 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
664 sub output_query_rhel
667 foreach my $os (keys %$oses) {
668 $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
669 $oses->{$os}->{distro} eq "rhel");
671 print "rhel=$rhel\n";
674 =item fedora=(yes|no)
676 Answer C<yes> if the guest contains the Fedora Linux distribution.
680 sub output_query_fedora
683 foreach my $os (keys %$oses) {
684 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
686 print "fedora=$fedora\n";
689 =item debian=(yes|no)
691 Answer C<yes> if the guest contains the Debian Linux distribution.
695 sub output_query_debian
698 foreach my $os (keys %$oses) {
699 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
701 print "debian=$debian\n";
704 =item fullvirt=(yes|no)
706 Answer C<yes> if there is at least one operating system kernel
707 installed in the guest which runs fully virtualized. Such a guest
708 would require a hypervisor which supports full system virtualization.
712 sub output_query_fullvirt
714 # The assumption is full-virt, unless all installed kernels
715 # are identified as paravirt.
716 # XXX Fails on Windows guests.
717 foreach my $os (keys %$oses) {
718 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
719 my $is_pv = $kernel->{version} =~ m/xen/;
721 print "fullvirt=yes\n";
726 print "fullvirt=no\n";
729 =item xen_domU_kernel=(yes|no)
731 Answer C<yes> if there is at least one Linux kernel installed in
732 the guest which is compiled as a Xen DomU (a Xen paravirtualized
737 sub output_query_xen_domU_kernel
739 foreach my $os (keys %$oses) {
740 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
741 my $is_xen = $kernel->{version} =~ m/xen/;
743 print "xen_domU_kernel=yes\n";
748 print "xen_domU_kernel=no\n";
751 =item xen_pv_drivers=(yes|no)
753 Answer C<yes> if the guest has Xen paravirtualized drivers installed
754 (usually the kernel itself will be fully virtualized, but the PV
755 drivers have been installed by the administrator for performance
760 sub output_query_xen_pv_drivers
762 foreach my $os (keys %$oses) {
763 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
764 foreach my $module (@{$kernel->{modules}}) {
765 if ($module =~ m/xen-/) {
766 print "xen_pv_drivers=yes\n";
772 print "xen_pv_drivers=no\n";
775 =item virtio_drivers=(yes|no)
777 Answer C<yes> if the guest has virtio paravirtualized drivers
778 installed. Virtio drivers are commonly used to improve the
783 sub output_query_virtio_drivers
785 foreach my $os (keys %$oses) {
786 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
787 foreach my $module (@{$kernel->{modules}}) {
788 if ($module =~ m/virtio_/) {
789 print "virtio_drivers=yes\n";
795 print "virtio_drivers=no\n";
798 =item userspace_arch=(x86_64|...)
800 Print the architecture of userspace.
802 NB. For multi-boot VMs this can print several lines.
806 sub output_query_userspace_arch
810 foreach my $os (keys %$oses) {
811 $arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
814 foreach (sort keys %arches) {
815 print "userspace_arch=$_\n";
819 =item kernel_arch=(x86_64|...)
821 Print the architecture of the kernel.
823 NB. For multi-boot VMs this can print several lines.
827 sub output_query_kernel_arch
831 foreach my $os (keys %$oses) {
832 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
833 $arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
837 foreach (sort keys %arches) {
838 print "kernel_arch=$_\n";
849 L<Sys::Guestfs::Lib(3)>,
851 L<http://libguestfs.org/>.
853 For Windows registry parsing we require the C<reged> program
854 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
858 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
860 Matthew Booth L<mbooth@redhat.com>
864 Copyright (C) 2009 Red Hat Inc.
866 This program is free software; you can redistribute it and/or modify
867 it under the terms of the GNU General Public License as published by
868 the Free Software Foundation; either version 2 of the License, or
869 (at your option) any later version.
871 This program is distributed in the hope that it will be useful,
872 but WITHOUT ANY WARRANTY; without even the implied warranty of
873 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
874 GNU General Public License for more details.
876 You should have received a copy of the GNU General Public License
877 along with this program; if not, write to the Free Software
878 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.