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);
227 Linux (distro + version)
231 +--- Filesystems ---------- Installed apps --- Kernel & drivers
232 ----------- -------------- ----------------
233 mount point => device List of apps Extra information
234 mount point => device and versions about kernel(s)
237 (plus lots of extra information
238 about each filesystem)
240 The output of virt-inspector is a complex two-level data structure.
242 At the top level is a list of the operating systems installed on the
243 guest. (For the vast majority of guests, only a single OS is
244 installed.) The data returned for the OS includes the name (Linux,
245 Windows), the distribution and version.
247 The diagram above shows what we return for each OS.
249 With the I<--xml> option the output is mapped into an XML document.
250 Unfortunately there is no clear schema for this document
251 (contributions welcome) but you can get an idea of the format by
252 looking at other documents and as a last resort the source for this
255 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
256 L<guestfish(1)> command line parameters, so that you can go in
257 afterwards and inspect the guest with everything mounted in the
258 right place. For example:
260 guestfish $(virt-inspector --ro-fish guest.img)
261 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
265 # List of possible filesystems.
266 my @partitions = get_partitions ($g);
268 # Now query each one to build up a picture of what's in it.
270 inspect_all_partitions ($g, \@partitions,
271 use_windows_registry => $windows_registry);
273 #print "fses -----------\n";
274 #print Dumper(\%fses);
276 my $oses = inspect_operating_systems ($g, \%fses);
278 #print "oses -----------\n";
279 #print Dumper($oses);
281 # Mount up the disks so we can check for applications
282 # and kernels. Skip this if the output is "*fish" because
283 # we don't need to know.
285 if ($output !~ /.*fish$/) {
287 foreach $root_dev (sort keys %$oses) {
288 my $os = $oses->{$root_dev};
289 mount_operating_system ($g, $os);
290 inspect_in_detail ($g, $os);
295 #----------------------------------------------------------------------
298 if ($output eq "fish" || $output eq "ro-fish") {
299 my @osdevs = keys %$oses;
300 # This only works if there is a single OS.
301 die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
303 my $root_dev = $osdevs[0];
305 if ($output eq "ro-fish") {
309 print "-a $_ " foreach @images;
311 my $mounts = $oses->{$root_dev}->{mounts};
312 # Have to mount / first. Luckily '/' is early in the ASCII
313 # character set, so this should be OK.
314 foreach (sort keys %$mounts) {
315 print "-m $mounts->{$_}:$_ " if $_ ne "swap" && $_ ne "none";
321 elsif ($output eq "perl") {
322 print Dumper(%$oses);
326 elsif ($output eq "yaml") {
327 die __"virt-inspector: no YAML support\n"
328 unless exists $INC{"YAML/Any.pm"};
333 # Plain text output (the default).
334 elsif ($output eq "text") {
339 elsif ($output eq "xml") {
344 elsif ($output eq "query") {
350 output_text_os ($oses->{$_}) foreach sort keys %$oses;
357 print $os->{os}, " " if exists $os->{os};
358 print $os->{distro}, " " if exists $os->{distro};
359 print $os->{arch}, " " if exists $os->{arch};
360 print $os->{major_version} if exists $os->{major_version};
361 print ".", $os->{minor_version} if exists $os->{minor_version};
363 print "on ", $os->{root_device}, ":\n";
365 print __" Mountpoints:\n";
366 my $mounts = $os->{mounts};
367 foreach (sort keys %$mounts) {
368 printf " %-30s %s\n", $mounts->{$_}, $_
371 print __" Filesystems:\n";
372 my $filesystems = $os->{filesystems};
373 foreach (sort keys %$filesystems) {
375 print " label: $filesystems->{$_}{label}\n"
376 if exists $filesystems->{$_}{label};
377 print " UUID: $filesystems->{$_}{uuid}\n"
378 if exists $filesystems->{$_}{uuid};
379 print " type: $filesystems->{$_}{fstype}\n"
380 if exists $filesystems->{$_}{fstype};
381 print " content: $filesystems->{$_}{content}\n"
382 if exists $filesystems->{$_}{content};
385 if (exists $os->{modprobe_aliases}) {
386 my %aliases = %{$os->{modprobe_aliases}};
387 my @keys = sort keys %aliases;
389 print __" Modprobe aliases:\n";
391 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
396 if (exists $os->{initrd_modules}) {
397 my %modvers = %{$os->{initrd_modules}};
398 my @keys = sort keys %modvers;
400 print __" Initrd modules:\n";
402 my @modules = @{$modvers{$_}};
404 print " $_\n" foreach @modules;
409 print __" Applications:\n";
410 my @apps = @{$os->{apps}};
412 print " $_->{name} $_->{version}\n"
415 print __" Kernels:\n";
416 my @kernels = @{$os->{kernels}};
418 print " $_->{version} ($_->{arch})\n";
419 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 [ "arch" => "arch" ],
454 [ "major_version" => "major_version" ],
455 [ "minor_version" => "minor_version" ],
456 [ "package_format" => "package_format" ],
457 [ "package_management" => "package_management" ],
458 [ "root" => "root_device" ] ) {
459 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
462 $xml->startTag("mountpoints");
463 my $mounts = $os->{mounts};
464 foreach (sort keys %$mounts) {
465 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
467 $xml->endTag("mountpoints");
469 $xml->startTag("filesystems");
470 my $filesystems = $os->{filesystems};
471 foreach (sort keys %$filesystems) {
472 $xml->startTag("filesystem", "dev" => $_);
474 foreach my $field ( [ "label" => "label" ],
475 [ "uuid" => "uuid" ],
476 [ "type" => "fstype" ],
477 [ "content" => "content" ],
478 [ "spec" => "spec" ] ) {
479 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
480 if exists $filesystems->{$_}{$field->[1]};
483 $xml->endTag("filesystem");
485 $xml->endTag("filesystems");
487 if (exists $os->{modprobe_aliases}) {
488 my %aliases = %{$os->{modprobe_aliases}};
489 my @keys = sort keys %aliases;
491 $xml->startTag("modprobealiases");
493 $xml->startTag("alias", "device" => $_);
495 foreach my $field ( [ "modulename" => "modulename" ],
496 [ "augeas" => "augeas" ],
497 [ "file" => "file" ] ) {
498 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
501 $xml->endTag("alias");
503 $xml->endTag("modprobealiases");
507 if (exists $os->{initrd_modules}) {
508 my %modvers = %{$os->{initrd_modules}};
509 my @keys = sort keys %modvers;
511 $xml->startTag("initrds");
513 my @modules = @{$modvers{$_}};
514 $xml->startTag("initrd", "version" => $_);
515 $xml->dataElement("module", $_) foreach @modules;
516 $xml->endTag("initrd");
518 $xml->endTag("initrds");
522 $xml->startTag("applications");
523 my @apps = @{$os->{apps}};
525 $xml->startTag("application");
526 $xml->dataElement("name", $_->{name});
527 $xml->dataElement("version", $_->{version});
528 $xml->endTag("application");
530 $xml->endTag("applications");
532 if(defined($os->{boot}) && defined($os->{boot}->{configs})) {
533 my $default = $os->{boot}->{default};
534 my $configs = $os->{boot}->{configs};
536 $xml->startTag("boot");
537 for(my $i = 0; $i < scalar(@$configs); $i++) {
538 my $config = $configs->[$i];
541 push(@attrs, ("default" => 1)) if($default == $i);
542 $xml->startTag("config", @attrs);
543 $xml->dataElement("title", $config->{title});
544 $xml->dataElement("kernel", $config->{kernel}->{version})
545 if(defined($config->{kernel}));
546 $xml->dataElement("cmdline", $config->{cmdline})
547 if(defined($config->{cmdline}));
548 $xml->endTag("config");
550 $xml->endTag("boot");
553 $xml->startTag("kernels");
554 my @kernels = @{$os->{kernels}};
556 $xml->startTag("kernel",
557 "version" => $_->{version},
558 "arch" => $_->{arch});
559 $xml->startTag("modules");
560 my @modules = @{$_->{modules}};
562 $xml->dataElement("module", $_);
564 $xml->endTag("modules");
565 $xml->dataElement("path", $_->{path}) if(defined($_->{path}));
566 $xml->dataElement("package", $_->{package}) if(defined($_->{package}));
567 $xml->endTag("kernel");
569 $xml->endTag("kernels");
571 if (exists $os->{root}->{registry}) {
572 $xml->startTag("windowsregistryentries");
573 # These are just lumps of text - dump them out.
574 foreach (@{$os->{root}->{registry}}) {
575 $xml->dataElement("windowsregistryentry", $_);
577 $xml->endTag("windowsregistryentries");
580 $xml->endTag("operatingsystem");
585 When you use C<virt-inspector --query>, the output is a series of
593 (each answer is usually C<yes> or C<no>, or the line is completely
594 missing if we could not determine the answer at all).
596 If the guest is multiboot, you can get apparently conflicting answers
597 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
598 fullvirt and has a Xen PV kernel). This is normal, and just means
599 that the guest can do both things, although it might require operator
600 intervention such as selecting a boot option when the guest is
603 This section describes the full range of answers possible.
611 output_query_windows ();
612 output_query_linux ();
613 output_query_rhel ();
614 output_query_fedora ();
615 output_query_debian ();
616 output_query_fullvirt ();
617 output_query_xen_domU_kernel ();
618 output_query_xen_pv_drivers ();
619 output_query_virtio_drivers ();
620 output_query_kernel_arch ();
621 output_query_userspace_arch ();
624 =item windows=(yes|no)
626 Answer C<yes> if Microsoft Windows is installed in the guest.
630 sub output_query_windows
633 foreach my $os (keys %$oses) {
634 $windows="yes" if $oses->{$os}->{os} eq "windows";
636 print "windows=$windows\n";
641 Answer C<yes> if a Linux kernel is installed in the guest.
645 sub output_query_linux
648 foreach my $os (keys %$oses) {
649 $linux="yes" if $oses->{$os}->{os} eq "linux";
651 print "linux=$linux\n";
656 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
660 sub output_query_rhel
663 foreach my $os (keys %$oses) {
664 $rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
665 $oses->{$os}->{distro} eq "rhel");
667 print "rhel=$rhel\n";
670 =item fedora=(yes|no)
672 Answer C<yes> if the guest contains the Fedora Linux distribution.
676 sub output_query_fedora
679 foreach my $os (keys %$oses) {
680 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
682 print "fedora=$fedora\n";
685 =item debian=(yes|no)
687 Answer C<yes> if the guest contains the Debian Linux distribution.
691 sub output_query_debian
694 foreach my $os (keys %$oses) {
695 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
697 print "debian=$debian\n";
700 =item fullvirt=(yes|no)
702 Answer C<yes> if there is at least one operating system kernel
703 installed in the guest which runs fully virtualized. Such a guest
704 would require a hypervisor which supports full system virtualization.
708 sub output_query_fullvirt
710 # The assumption is full-virt, unless all installed kernels
711 # are identified as paravirt.
712 # XXX Fails on Windows guests.
713 foreach my $os (keys %$oses) {
714 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
715 my $is_pv = $kernel->{version} =~ m/xen/;
717 print "fullvirt=yes\n";
722 print "fullvirt=no\n";
725 =item xen_domU_kernel=(yes|no)
727 Answer C<yes> if there is at least one Linux kernel installed in
728 the guest which is compiled as a Xen DomU (a Xen paravirtualized
733 sub output_query_xen_domU_kernel
735 foreach my $os (keys %$oses) {
736 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
737 my $is_xen = $kernel->{version} =~ m/xen/;
739 print "xen_domU_kernel=yes\n";
744 print "xen_domU_kernel=no\n";
747 =item xen_pv_drivers=(yes|no)
749 Answer C<yes> if the guest has Xen paravirtualized drivers installed
750 (usually the kernel itself will be fully virtualized, but the PV
751 drivers have been installed by the administrator for performance
756 sub output_query_xen_pv_drivers
758 foreach my $os (keys %$oses) {
759 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
760 foreach my $module (@{$kernel->{modules}}) {
761 if ($module =~ m/xen-/) {
762 print "xen_pv_drivers=yes\n";
768 print "xen_pv_drivers=no\n";
771 =item virtio_drivers=(yes|no)
773 Answer C<yes> if the guest has virtio paravirtualized drivers
774 installed. Virtio drivers are commonly used to improve the
779 sub output_query_virtio_drivers
781 foreach my $os (keys %$oses) {
782 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
783 foreach my $module (@{$kernel->{modules}}) {
784 if ($module =~ m/virtio_/) {
785 print "virtio_drivers=yes\n";
791 print "virtio_drivers=no\n";
794 =item userspace_arch=(x86_64|...)
796 Print the architecture of userspace.
798 NB. For multi-boot VMs this can print several lines.
802 sub output_query_userspace_arch
806 foreach my $os (keys %$oses) {
807 $arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
810 foreach (sort keys %arches) {
811 print "userspace_arch=$_\n";
815 =item kernel_arch=(x86_64|...)
817 Print the architecture of the kernel.
819 NB. For multi-boot VMs this can print several lines.
823 sub output_query_kernel_arch
827 foreach my $os (keys %$oses) {
828 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
829 $arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
833 foreach (sort keys %arches) {
834 print "kernel_arch=$_\n";
845 L<Sys::Guestfs::Lib(3)>,
847 L<http://libguestfs.org/>.
849 For Windows registry parsing we require the C<reged> program
850 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
854 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
856 Matthew Booth L<mbooth@redhat.com>
860 Copyright (C) 2009 Red Hat Inc.
862 This program is free software; you can redistribute it and/or modify
863 it under the terms of the GNU General Public License as published by
864 the Free Software Foundation; either version 2 of the License, or
865 (at your option) any later version.
867 This program is distributed in the hope that it will be useful,
868 but WITHOUT ANY WARRANTY; without even the implied warranty of
869 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
870 GNU General Public License for more details.
872 You should have received a copy of the GNU General Public License
873 along with this program; if not, write to the Free Software
874 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.