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";
213 ($g, $conn, $dom, @images) =
214 open_guest (\@ARGV, rw => $rw, address => $uri);
217 ($g, $conn, $dom, @images) =
218 open_guest (\@ARGV, rw => $rw);
228 Linux (distro + version)
232 +--- Filesystems ---------- Installed apps --- Kernel & drivers
233 ----------- -------------- ----------------
234 mount point => device List of apps Extra information
235 mount point => device and versions about kernel(s)
238 (plus lots of extra information
239 about each filesystem)
241 The output of virt-inspector is a complex two-level data structure.
243 At the top level is a list of the operating systems installed on the
244 guest. (For the vast majority of guests, only a single OS is
245 installed.) The data returned for the OS includes the name (Linux,
246 Windows), the distribution and version.
248 The diagram above shows what we return for each OS.
250 With the I<--xml> option the output is mapped into an XML document.
251 Unfortunately there is no clear schema for this document
252 (contributions welcome) but you can get an idea of the format by
253 looking at other documents and as a last resort the source for this
256 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
257 L<guestfish(1)> command line parameters, so that you can go in
258 afterwards and inspect the guest with everything mounted in the
259 right place. For example:
261 guestfish $(virt-inspector --ro-fish guest.img)
262 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
266 # List of possible filesystems.
267 my @partitions = get_partitions ($g);
269 # Now query each one to build up a picture of what's in it.
271 inspect_all_partitions ($g, \@partitions,
272 use_windows_registry => $windows_registry);
274 #print "fses -----------\n";
275 #print Dumper(\%fses);
277 my $oses = inspect_operating_systems ($g, \%fses);
279 #print "oses -----------\n";
280 #print Dumper($oses);
282 # Mount up the disks so we can check for applications
283 # and kernels. Skip this if the output is "*fish" because
284 # we don't need to know.
286 if ($output !~ /.*fish$/) {
288 foreach $root_dev (sort keys %$oses) {
289 my $os = $oses->{$root_dev};
290 mount_operating_system ($g, $os);
291 inspect_in_detail ($g, $os);
296 #----------------------------------------------------------------------
299 if ($output eq "fish" || $output eq "ro-fish") {
300 my @osdevs = keys %$oses;
301 # This only works if there is a single OS.
302 die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
304 my $root_dev = $osdevs[0];
306 if ($output eq "ro-fish") {
310 print "-a $_ " foreach @images;
312 my $mounts = $oses->{$root_dev}->{mounts};
313 # Have to mount / first. Luckily '/' is early in the ASCII
314 # character set, so this should be OK.
315 foreach (sort keys %$mounts) {
316 print "-m $mounts->{$_}:$_ " if $_ ne "swap" && $_ ne "none";
322 elsif ($output eq "perl") {
323 print Dumper(%$oses);
327 elsif ($output eq "yaml") {
328 die __"virt-inspector: no YAML support\n"
329 unless exists $INC{"YAML/Any.pm"};
334 # Plain text output (the default).
335 elsif ($output eq "text") {
340 elsif ($output eq "xml") {
345 elsif ($output eq "query") {
351 output_text_os ($oses->{$_}) foreach sort keys %$oses;
358 print $os->{os}, " " if exists $os->{os};
359 print $os->{distro}, " " if exists $os->{distro};
360 print $os->{arch}, " " if exists $os->{arch};
361 print $os->{major_version} if exists $os->{major_version};
362 print ".", $os->{minor_version} if exists $os->{minor_version};
364 print "on ", $os->{root_device}, ":\n";
366 print __" Mountpoints:\n";
367 my $mounts = $os->{mounts};
368 foreach (sort keys %$mounts) {
369 printf " %-30s %s\n", $mounts->{$_}, $_
372 print __" Filesystems:\n";
373 my $filesystems = $os->{filesystems};
374 foreach (sort keys %$filesystems) {
376 print " label: $filesystems->{$_}{label}\n"
377 if exists $filesystems->{$_}{label};
378 print " UUID: $filesystems->{$_}{uuid}\n"
379 if exists $filesystems->{$_}{uuid};
380 print " type: $filesystems->{$_}{fstype}\n"
381 if exists $filesystems->{$_}{fstype};
382 print " content: $filesystems->{$_}{content}\n"
383 if exists $filesystems->{$_}{content};
386 if (exists $os->{modprobe_aliases}) {
387 my %aliases = %{$os->{modprobe_aliases}};
388 my @keys = sort keys %aliases;
390 print __" Modprobe aliases:\n";
392 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
397 if (exists $os->{initrd_modules}) {
398 my %modvers = %{$os->{initrd_modules}};
399 my @keys = sort keys %modvers;
401 print __" Initrd modules:\n";
403 my @modules = @{$modvers{$_}};
405 print " $_\n" foreach @modules;
410 print __" Applications:\n";
411 my @apps = @{$os->{apps}};
413 print " $_->{name} $_->{version}\n"
416 print __" Kernels:\n";
417 my @kernels = @{$os->{kernels}};
419 print " $_->{version} ($_->{arch})\n";
420 my @modules = @{$_->{modules}};
426 if (exists $os->{root}->{registry}) {
427 print __" Windows Registry entries:\n";
428 # These are just lumps of text - dump them out.
429 foreach (@{$os->{root}->{registry}}) {
437 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
439 $xml->startTag("operatingsystems");
440 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
441 $xml->endTag("operatingsystems");
450 $xml->startTag("operatingsystem");
452 foreach ( [ "name" => "os" ],
453 [ "distro" => "distro" ],
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("version", $_->{version});
529 $xml->endTag("application");
531 $xml->endTag("applications");
533 $xml->startTag("kernels");
534 my @kernels = @{$os->{kernels}};
536 $xml->startTag("kernel",
537 "version" => $_->{version},
538 "arch" => $_->{arch});
539 $xml->startTag("modules");
540 my @modules = @{$_->{modules}};
542 $xml->dataElement("module", $_);
544 $xml->endTag("modules");
545 $xml->endTag("kernel");
547 $xml->endTag("kernels");
549 if (exists $os->{root}->{registry}) {
550 $xml->startTag("windowsregistryentries");
551 # These are just lumps of text - dump them out.
552 foreach (@{$os->{root}->{registry}}) {
553 $xml->dataElement("windowsregistryentry", $_);
555 $xml->endTag("windowsregistryentries");
558 $xml->endTag("operatingsystem");
563 When you use C<virt-inspector --query>, the output is a series of
571 (each answer is usually C<yes> or C<no>, or the line is completely
572 missing if we could not determine the answer at all).
574 If the guest is multiboot, you can get apparently conflicting answers
575 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
576 fullvirt and has a Xen PV kernel). This is normal, and just means
577 that the guest can do both things, although it might require operator
578 intervention such as selecting a boot option when the guest is
581 This section describes the full range of answers possible.
589 output_query_windows ();
590 output_query_linux ();
591 output_query_rhel ();
592 output_query_fedora ();
593 output_query_debian ();
594 output_query_fullvirt ();
595 output_query_xen_domU_kernel ();
596 output_query_xen_pv_drivers ();
597 output_query_virtio_drivers ();
598 output_query_kernel_arch ();
599 output_query_userspace_arch ();
602 =item windows=(yes|no)
604 Answer C<yes> if Microsoft Windows is installed in the guest.
608 sub output_query_windows
611 foreach my $os (keys %$oses) {
612 $windows="yes" if $oses->{$os}->{os} eq "windows";
614 print "windows=$windows\n";
619 Answer C<yes> if a Linux kernel is installed in the guest.
623 sub output_query_linux
626 foreach my $os (keys %$oses) {
627 $linux="yes" if $oses->{$os}->{os} eq "linux";
629 print "linux=$linux\n";
634 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
638 sub output_query_rhel
641 foreach my $os (keys %$oses) {
642 $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
643 $oses->{$os}->{distro} eq "rhel");
645 print "rhel=$rhel\n";
648 =item fedora=(yes|no)
650 Answer C<yes> if the guest contains the Fedora Linux distribution.
654 sub output_query_fedora
657 foreach my $os (keys %$oses) {
658 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
660 print "fedora=$fedora\n";
663 =item debian=(yes|no)
665 Answer C<yes> if the guest contains the Debian Linux distribution.
669 sub output_query_debian
672 foreach my $os (keys %$oses) {
673 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
675 print "debian=$debian\n";
678 =item fullvirt=(yes|no)
680 Answer C<yes> if there is at least one operating system kernel
681 installed in the guest which runs fully virtualized. Such a guest
682 would require a hypervisor which supports full system virtualization.
686 sub output_query_fullvirt
688 # The assumption is full-virt, unless all installed kernels
689 # are identified as paravirt.
690 # XXX Fails on Windows guests.
691 foreach my $os (keys %$oses) {
692 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
693 my $is_pv = $kernel->{version} =~ m/xen/;
695 print "fullvirt=yes\n";
700 print "fullvirt=no\n";
703 =item xen_domU_kernel=(yes|no)
705 Answer C<yes> if there is at least one Linux kernel installed in
706 the guest which is compiled as a Xen DomU (a Xen paravirtualized
711 sub output_query_xen_domU_kernel
713 foreach my $os (keys %$oses) {
714 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
715 my $is_xen = $kernel->{version} =~ m/xen/;
717 print "xen_domU_kernel=yes\n";
722 print "xen_domU_kernel=no\n";
725 =item xen_pv_drivers=(yes|no)
727 Answer C<yes> if the guest has Xen paravirtualized drivers installed
728 (usually the kernel itself will be fully virtualized, but the PV
729 drivers have been installed by the administrator for performance
734 sub output_query_xen_pv_drivers
736 foreach my $os (keys %$oses) {
737 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
738 foreach my $module (@{$kernel->{modules}}) {
739 if ($module =~ m/xen-/) {
740 print "xen_pv_drivers=yes\n";
746 print "xen_pv_drivers=no\n";
749 =item virtio_drivers=(yes|no)
751 Answer C<yes> if the guest has virtio paravirtualized drivers
752 installed. Virtio drivers are commonly used to improve the
757 sub output_query_virtio_drivers
759 foreach my $os (keys %$oses) {
760 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
761 foreach my $module (@{$kernel->{modules}}) {
762 if ($module =~ m/virtio_/) {
763 print "virtio_drivers=yes\n";
769 print "virtio_drivers=no\n";
772 =item userspace_arch=(x86_64|...)
774 Print the architecture of userspace.
776 NB. For multi-boot VMs this can print several lines.
780 sub output_query_userspace_arch
784 foreach my $os (keys %$oses) {
785 $arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
788 foreach (sort keys %arches) {
789 print "userspace_arch=$_\n";
793 =item kernel_arch=(x86_64|...)
795 Print the architecture of the kernel.
797 NB. For multi-boot VMs this can print several lines.
801 sub output_query_kernel_arch
805 foreach my $os (keys %$oses) {
806 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
807 $arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
811 foreach (sort keys %arches) {
812 print "kernel_arch=$_\n";
823 L<Sys::Guestfs::Lib(3)>,
825 L<http://libguestfs.org/>.
827 For Windows registry parsing we require the C<reged> program
828 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
832 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
834 Matthew Booth L<mbooth@redhat.com>
838 Copyright (C) 2009 Red Hat Inc.
840 This program is free software; you can redistribute it and/or modify
841 it under the terms of the GNU General Public License as published by
842 the Free Software Foundation; either version 2 of the License, or
843 (at your option) any later version.
845 This program is distributed in the hope that it will be useful,
846 but WITHOUT ANY WARRANTY; without even the implied warranty of
847 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
848 GNU General Public License for more details.
850 You should have received a copy of the GNU General Public License
851 along with this program; if not, write to the Free Software
852 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.