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);
32 eval "use YAML::Any;";
38 virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
42 virt-inspector [--connect URI] domname
44 virt-inspector guest.img [guest.img ...]
48 B<virt-inspector> examines a virtual machine and tries to determine
49 the version of the OS, the kernel version, what drivers are installed,
50 whether the virtual machine is fully virtualized (FV) or
51 para-virtualized (PV), what applications are installed and more.
53 Virt-inspector can produce output in several formats, including a
54 readable text report, and XML for feeding into other programs.
56 Virt-inspector should only be run on I<inactive> virtual machines.
57 The program tries to determine that the machine is inactive and will
58 refuse to run if it thinks you are trying to inspect a running domain.
60 In the normal usage, use C<virt-inspector domname> where C<domname> is
61 the libvirt domain (see: C<virsh list --all>).
63 You can also run virt-inspector directly on disk images from a single
64 virtual machine. Use C<virt-inspector guest.img>. In rare cases a
65 domain has several block devices, in which case you should list them
66 one after another, with the first corresponding to the guest's
67 C</dev/sda>, the second to the guest's C</dev/sdb> and so on.
69 Virt-inspector can only inspect and report upon I<one domain at a
70 time>. To inspect several virtual machines, you have to run
71 virt-inspector several times (for example, from a shell script
74 Because virt-inspector needs direct access to guest images, it won't
75 normally work over remote libvirt connections.
93 =item B<--connect URI> | B<-c URI>
95 If using libvirt, connect to the given I<URI>. If omitted,
96 then we connect to the default libvirt hypervisor.
98 Libvirt is only used if you specify a C<domname> on the
99 command line. If you specify guest block devices directly,
100 then libvirt is not used at all.
108 The following options select the output format. Use only one of them.
109 The default is a readable text report.
113 =item B<--text> (default)
119 Produce no output at all.
123 If you select I<--xml> then you get XML output which can be fed
128 If you select I<--yaml> then you get YAML output which can be fed
133 If you select I<--perl> then you get Perl structures output which
134 can be used directly in another Perl program.
140 If you select I<--fish> then we print a L<guestfish(1)> command
141 line which will automatically mount up the filesystems on the
142 correct mount points. Try this for example:
144 guestfish $(virt-inspector --fish guest.img)
146 I<--ro-fish> is the same, but the I<--ro> option is passed to
147 guestfish so that the filesystems are mounted read-only.
151 In "query mode" we answer common questions about the guest, such
152 as whether it is fullvirt or needs a Xen hypervisor to run.
154 See section I<QUERY MODE> below.
158 my $windows_registry;
160 =item B<--windows-registry>
162 If this item is passed, I<and> the guest is Windows, I<and> the
163 external program C<reged> is available (see SEE ALSO section), then we
164 attempt to parse the Windows registry. This allows much more
165 information to be gathered for Windows guests.
167 This is quite an expensive and slow operation, so we don't do it by
174 GetOptions ("help|?" => \$help,
175 "connect|c=s" => \$uri,
176 "text" => sub { $output = "text" },
177 "none" => sub { $output = "none" },
178 "xml" => sub { $output = "xml" },
179 "yaml" => sub { $output = "yaml" },
180 "perl" => sub { $output = "perl" },
181 "fish" => sub { $output = "fish" },
182 "guestfish" => sub { $output = "fish" },
183 "ro-fish" => sub { $output = "ro-fish" },
184 "ro-guestfish" => sub { $output = "ro-fish" },
185 "query" => sub { $output = "query" },
186 "windows-registry" => \$windows_registry,
188 pod2usage (1) if $help;
189 pod2usage ("$0: no image or VM names given") if @ARGV == 0;
192 $rw = 1 if $output eq "fish";
195 $g = open_guest (\@ARGV, rw => $rw, address => $uri);
197 $g = open_guest (\@ARGV, rw => $rw);
207 Linux (distro + version)
211 +--- Filesystems ---------- Installed apps --- Kernel & drivers
212 ----------- -------------- ----------------
213 mount point => device List of apps Extra information
214 mount point => device and versions about kernel(s)
217 (plus lots of extra information
218 about each filesystem)
220 The output of virt-inspector is a complex two-level data structure.
222 At the top level is a list of the operating systems installed on the
223 guest. (For the vast majority of guests, only a single OS is
224 installed.) The data returned for the OS includes the name (Linux,
225 Windows), the distribution and version.
227 The diagram above shows what we return for each OS.
229 With the I<--xml> option the output is mapped into an XML document.
230 Unfortunately there is no clear schema for this document
231 (contributions welcome) but you can get an idea of the format by
232 looking at other documents and as a last resort the source for this
235 With the I<--fish> or I<--ro-fish> option the mount points are mapped to
236 L<guestfish(1)> command line parameters, so that you can go in
237 afterwards and inspect the guest with everything mounted in the
238 right place. For example:
240 guestfish $(virt-inspector --ro-fish guest.img)
241 ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
245 # List of possible filesystems.
246 my @partitions = get_partitions ($g);
248 # Now query each one to build up a picture of what's in it.
250 inspect_all_partitions ($g, \@partitions,
251 use_windows_registry => $windows_registry);
253 #print "fses -----------\n";
254 #print Dumper(\%fses);
256 my $oses = inspect_operating_systems ($g, \%fses);
258 #print "oses -----------\n";
259 #print Dumper($oses);
261 # Mount up the disks so we can check for applications
262 # and kernels. Skip this if the output is "*fish" because
263 # we don't need to know.
265 if ($output !~ /.*fish$/) {
267 foreach $root_dev (sort keys %$oses) {
268 my $os = $oses->{$root_dev};
269 mount_operating_system ($g, $os);
270 inspect_in_detail ($g, $os);
275 #----------------------------------------------------------------------
278 if ($output eq "fish" || $output eq "ro-fish") {
279 my @osdevs = keys %$oses;
280 # This only works if there is a single OS.
281 die "--fish output is only possible with a single OS\n" if @osdevs != 1;
283 my $root_dev = $osdevs[0];
285 if ($output eq "ro-fish") {
289 print "-a $_ " foreach @ARGV;
291 my $mounts = $oses->{$root_dev}->{mounts};
292 # Have to mount / first. Luckily '/' is early in the ASCII
293 # character set, so this should be OK.
294 foreach (sort keys %$mounts) {
295 print "-m $mounts->{$_}:$_ " if $_ ne "swap";
301 elsif ($output eq "perl") {
302 print Dumper(%$oses);
306 elsif ($output eq "yaml") {
307 die "virt-inspector: no YAML support\n"
308 unless exists $INC{"YAML/Any.pm"};
313 # Plain text output (the default).
314 elsif ($output eq "text") {
319 elsif ($output eq "xml") {
324 elsif ($output eq "query") {
330 output_text_os ($oses->{$_}) foreach sort keys %$oses;
337 print $os->{os}, " " if exists $os->{os};
338 print $os->{distro}, " " if exists $os->{distro};
339 print $os->{version}, " " if exists $os->{version};
340 print "on ", $os->{root_device}, ":\n";
342 print " Mountpoints:\n";
343 my $mounts = $os->{mounts};
344 foreach (sort keys %$mounts) {
345 printf " %-30s %s\n", $mounts->{$_}, $_
348 print " Filesystems:\n";
349 my $filesystems = $os->{filesystems};
350 foreach (sort keys %$filesystems) {
352 print " label: $filesystems->{$_}{label}\n"
353 if exists $filesystems->{$_}{label};
354 print " UUID: $filesystems->{$_}{uuid}\n"
355 if exists $filesystems->{$_}{uuid};
356 print " type: $filesystems->{$_}{fstype}\n"
357 if exists $filesystems->{$_}{fstype};
358 print " content: $filesystems->{$_}{content}\n"
359 if exists $filesystems->{$_}{content};
362 if (exists $os->{modprobe_aliases}) {
363 my %aliases = %{$os->{modprobe_aliases}};
364 my @keys = sort keys %aliases;
366 print " Modprobe aliases:\n";
368 printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
373 if (exists $os->{initrd_modules}) {
374 my %modvers = %{$os->{initrd_modules}};
375 my @keys = sort keys %modvers;
377 print " Initrd modules:\n";
379 my @modules = @{$modvers{$_}};
381 print " $_\n" foreach @modules;
386 print " Applications:\n";
387 my @apps = @{$os->{apps}};
389 print " $_->{name} $_->{version}\n"
393 my @kernels = @{$os->{kernels}};
395 print " $_->{version}\n";
396 my @modules = @{$_->{modules}};
402 if (exists $os->{root}->{registry}) {
403 print " Windows Registry entries:\n";
404 # These are just lumps of text - dump them out.
405 foreach (@{$os->{root}->{registry}}) {
413 my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
415 $xml->startTag("operatingsystems");
416 output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
417 $xml->endTag("operatingsystems");
426 $xml->startTag("operatingsystem");
428 foreach ( [ "name" => "os" ],
429 [ "distro" => "distro" ],
430 [ "version" => "version" ],
431 [ "root" => "root_device" ] ) {
432 $xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
435 $xml->startTag("mountpoints");
436 my $mounts = $os->{mounts};
437 foreach (sort keys %$mounts) {
438 $xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
440 $xml->endTag("mountpoints");
442 $xml->startTag("filesystems");
443 my $filesystems = $os->{filesystems};
444 foreach (sort keys %$filesystems) {
445 $xml->startTag("filesystem", "dev" => $_);
447 foreach my $field ( [ "label" => "label" ],
448 [ "uuid" => "uuid" ],
449 [ "type" => "fstype" ],
450 [ "content" => "content" ],
451 [ "spec" => "spec" ] ) {
452 $xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
453 if exists $filesystems->{$_}{$field->[1]};
456 $xml->endTag("filesystem");
458 $xml->endTag("filesystems");
460 if (exists $os->{modprobe_aliases}) {
461 my %aliases = %{$os->{modprobe_aliases}};
462 my @keys = sort keys %aliases;
464 $xml->startTag("modprobealiases");
466 $xml->startTag("alias", "device" => $_);
468 foreach my $field ( [ "modulename" => "modulename" ],
469 [ "augeas" => "augeas" ],
470 [ "file" => "file" ] ) {
471 $xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
474 $xml->endTag("alias");
476 $xml->endTag("modprobealiases");
480 if (exists $os->{initrd_modules}) {
481 my %modvers = %{$os->{initrd_modules}};
482 my @keys = sort keys %modvers;
484 $xml->startTag("initrds");
486 my @modules = @{$modvers{$_}};
487 $xml->startTag("initrd", "version" => $_);
488 $xml->dataElement("module", $_) foreach @modules;
489 $xml->endTag("initrd");
491 $xml->endTag("initrds");
495 $xml->startTag("applications");
496 my @apps = @{$os->{apps}};
498 $xml->startTag("application");
499 $xml->dataElement("name", $_->{name});
500 $xml->dataElement("version", $_->{version});
501 $xml->endTag("application");
503 $xml->endTag("applications");
505 $xml->startTag("kernels");
506 my @kernels = @{$os->{kernels}};
508 $xml->startTag("kernel", "version" => $_->{version});
509 $xml->startTag("modules");
510 my @modules = @{$_->{modules}};
512 $xml->dataElement("module", $_);
514 $xml->endTag("modules");
515 $xml->endTag("kernel");
517 $xml->endTag("kernels");
519 if (exists $os->{root}->{registry}) {
520 $xml->startTag("windowsregistryentries");
521 # These are just lumps of text - dump them out.
522 foreach (@{$os->{root}->{registry}}) {
523 $xml->dataElement("windowsregistryentry", $_);
525 $xml->endTag("windowsregistryentries");
528 $xml->endTag("operatingsystem");
533 When you use C<virt-inspector --query>, the output is a series of
541 (each answer is usually C<yes> or C<no>, or the line is completely
542 missing if we could not determine the answer at all).
544 If the guest is multiboot, you can get apparently conflicting answers
545 (eg. C<windows=yes> and C<linux=yes>, or a guest which is both
546 fullvirt and has a Xen PV kernel). This is normal, and just means
547 that the guest can do both things, although it might require operator
548 intervention such as selecting a boot option when the guest is
551 This section describes the full range of answers possible.
559 output_query_windows ();
560 output_query_linux ();
561 output_query_rhel ();
562 output_query_fedora ();
563 output_query_debian ();
564 output_query_fullvirt ();
565 output_query_xen_domU_kernel ();
566 output_query_xen_pv_drivers ();
567 output_query_virtio_drivers ();
570 =item windows=(yes|no)
572 Answer C<yes> if Microsoft Windows is installed in the guest.
576 sub output_query_windows
579 foreach my $os (keys %$oses) {
580 $windows="yes" if $oses->{$os}->{os} eq "windows";
582 print "windows=$windows\n";
587 Answer C<yes> if a Linux kernel is installed in the guest.
591 sub output_query_linux
594 foreach my $os (keys %$oses) {
595 $linux="yes" if $oses->{$os}->{os} eq "linux";
597 print "linux=$linux\n";
602 Answer C<yes> if the guest contains Red Hat Enterprise Linux.
606 sub output_query_rhel
609 foreach my $os (keys %$oses) {
610 $rhel="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "redhat";
612 print "rhel=$rhel\n";
615 =item fedora=(yes|no)
617 Answer C<yes> if the guest contains the Fedora Linux distribution.
621 sub output_query_fedora
624 foreach my $os (keys %$oses) {
625 $fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
627 print "fedora=$fedora\n";
630 =item debian=(yes|no)
632 Answer C<yes> if the guest contains the Debian Linux distribution.
636 sub output_query_debian
639 foreach my $os (keys %$oses) {
640 $debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
642 print "debian=$debian\n";
645 =item fullvirt=(yes|no)
647 Answer C<yes> if there is at least one operating system kernel
648 installed in the guest which runs fully virtualized. Such a guest
649 would require a hypervisor which supports full system virtualization.
653 sub output_query_fullvirt
655 # The assumption is full-virt, unless all installed kernels
656 # are identified as paravirt.
657 # XXX Fails on Windows guests.
658 foreach my $os (keys %$oses) {
659 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
660 my $is_pv = $kernel->{version} =~ m/xen/;
662 print "fullvirt=yes\n";
667 print "fullvirt=no\n";
670 =item xen_domU_kernel=(yes|no)
672 Answer C<yes> if there is at least one Linux kernel installed in
673 the guest which is compiled as a Xen DomU (a Xen paravirtualized
678 sub output_query_xen_domU_kernel
680 foreach my $os (keys %$oses) {
681 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
682 my $is_xen = $kernel->{version} =~ m/xen/;
684 print "xen_domU_kernel=yes\n";
689 print "xen_domU_kernel=no\n";
692 =item xen_pv_drivers=(yes|no)
694 Answer C<yes> if the guest has Xen paravirtualized drivers installed
695 (usually the kernel itself will be fully virtualized, but the PV
696 drivers have been installed by the administrator for performance
701 sub output_query_xen_pv_drivers
703 foreach my $os (keys %$oses) {
704 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
705 foreach my $module (@{$kernel->{modules}}) {
706 if ($module =~ m/xen-/) {
707 print "xen_pv_drivers=yes\n";
713 print "xen_pv_drivers=no\n";
716 =item virtio_drivers=(yes|no)
718 Answer C<yes> if the guest has virtio paravirtualized drivers
719 installed. Virtio drivers are commonly used to improve the
724 sub output_query_virtio_drivers
726 foreach my $os (keys %$oses) {
727 foreach my $kernel (@{$oses->{$os}->{kernels}}) {
728 foreach my $module (@{$kernel->{modules}}) {
729 if ($module =~ m/virtio_/) {
730 print "virtio_drivers=yes\n";
736 print "virtio_drivers=no\n";
746 L<Sys::Guestfs::Lib(3)>,
748 L<http://libguestfs.org/>.
750 For Windows registry parsing we require the C<reged> program
751 from L<http://home.eunet.no/~pnordahl/ntpasswd/>.
755 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
759 Copyright (C) 2009 Red Hat Inc.
761 This program is free software; you can redistribute it and/or modify
762 it under the terms of the GNU General Public License as published by
763 the Free Software Foundation; either version 2 of the License, or
764 (at your option) any later version.
766 This program is distributed in the hope that it will be useful,
767 but WITHOUT ANY WARRANTY; without even the implied warranty of
768 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
769 GNU General Public License for more details.
771 You should have received a copy of the GNU General Public License
772 along with this program; if not, write to the Free Software
773 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.