inspector: Rewrite virt-inspector (RHBZ#642930).

Rewrite virt-inspector:

 - remove old and unsupportable features
 - use the C inspection API
 - don't run programs from the guest

The RNG has been updated to reflect the new XML-only output.

The new example files show the new XML output.
This commit is contained in:
Richard W.M. Jones
2010-10-28 11:31:23 +01:00
parent ca7e941329
commit f30210cba8
9 changed files with 3587 additions and 24726 deletions

View File

@@ -17,6 +17,10 @@
include $(top_srcdir)/subdir-rules.mk
EXAMPLE_XML = \
example1.xml example2.xml example3.xml \
example4.xml example5.xml example6.xml
EXTRA_DIST = \
run-inspector-locally \
virt-inspector
@@ -24,7 +28,7 @@ EXTRA_DIST = \
docdir = @docdir@
dist_doc_DATA = \
virt-inspector.rng \
example1.xml example2.xml example3.xml example4.xml
$(EXAMPLE_XML)
if HAVE_INSPECTOR
@@ -52,7 +56,7 @@ $(top_builddir)/html/virt-inspector.1.html: virt-inspector
if HAVE_XMLLINT
TESTS = example1.xml example2.xml example3.xml example4.xml
TESTS = $(EXAMPLE_XML)
TESTS_ENVIRONMENT = $(XMLLINT) --noout --relaxng virt-inspector.rng
endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,21 @@
<operatingsystems>
<operatingsystem>
<root>/dev/vda2</root>
<name>windows</name>
<product_name>Microsoft Windows Server 2003</product_name>
<arch>i386</arch>
<major_version>5</major_version>
<minor_version>2</minor_version>
<root>/dev/sda1</root>
<distro>windows</distro>
<product_name>Windows 7 Enterprise</product_name>
<major_version>6</major_version>
<minor_version>1</minor_version>
<windows_systemroot>/Windows</windows_systemroot>
<mountpoints>
<mountpoint dev="/dev/sda1">/</mountpoint>
<mountpoint dev="/dev/vda2">/</mountpoint>
</mountpoints>
<filesystems>
<filesystem dev="/dev/sda1">
<filesystem dev="/dev/vda2">
<type>ntfs</type>
<content>windows-root</content>
<uuid>F2E8996AE8992E3B</uuid>
</filesystem>
</filesystems>
<applications></applications>
</operatingsystem>
</operatingsystems>

50
inspector/example5.xml Normal file
View File

@@ -0,0 +1,50 @@
<operatingsystems>
<operatingsystem>
<root>/dev/debian5x64.home.annexia.org/root</root>
<name>linux</name>
<arch>x86_64</arch>
<distro>debian</distro>
<product_name>5.0.6</product_name>
<major_version>5</major_version>
<minor_version>0</minor_version>
<mountpoints>
<mountpoint dev="/dev/debian5x64.home.annexia.org/root">/</mountpoint>
<mountpoint dev="/dev/debian5x64.home.annexia.org/usr">/usr</mountpoint>
<mountpoint dev="/dev/debian5x64.home.annexia.org/var">/var</mountpoint>
<mountpoint dev="/dev/debian5x64.home.annexia.org/tmp">/tmp</mountpoint>
<mountpoint dev="/dev/debian5x64.home.annexia.org/home">/home</mountpoint>
<mountpoint dev="/dev/vda1">/boot</mountpoint>
</mountpoints>
<filesystems>
<filesystem dev="/dev/debian5x64.home.annexia.org/root">
<type>ext3</type>
<uuid>e15bda18-deca-40d1-beb4-ff31e4068741</uuid>
</filesystem>
<filesystem dev="/dev/vda1">
<type>ext2</type>
<uuid>b6590940-dc13-4fc5-b306-7c7bdf075f17</uuid>
</filesystem>
<filesystem dev="/dev/debian5x64.home.annexia.org/home">
<type>ext3</type>
<uuid>96b22229-3575-408e-8270-aeba18b3ebf1</uuid>
</filesystem>
<filesystem dev="/dev/debian5x64.home.annexia.org/tmp">
<type>ext3</type>
<uuid>241ea398-034e-4514-9854-eec667ebe9d9</uuid>
</filesystem>
<filesystem dev="/dev/debian5x64.home.annexia.org/usr">
<type>ext3</type>
<uuid>4645668c-60d1-44ea-9ad6-d283bd71e380</uuid>
</filesystem>
<filesystem dev="/dev/debian5x64.home.annexia.org/var">
<type>ext3</type>
<uuid>0eb2cef5-e765-440f-8be7-f40c46b93874</uuid>
</filesystem>
<filesystem dev="/dev/debian5x64.home.annexia.org/swap_1">
<type>swap</type>
</filesystem>
</filesystems>
<package_format>dpkg</package_format>
<package_management>apt</package_management>
</operatingsystem>
</operatingsystems>

26
inspector/example6.xml Normal file
View File

@@ -0,0 +1,26 @@
<operatingsystems>
<operatingsystem>
<root>/dev/vda1</root>
<name>linux</name>
<arch>x86_64</arch>
<distro>debian</distro>
<product_name>squeeze/sid</product_name>
<major_version>0</major_version>
<minor_version>0</minor_version>
<mountpoints>
<mountpoint dev="/dev/vda1">/</mountpoint>
</mountpoints>
<filesystems>
<filesystem dev="/dev/vda1">
<type>ext4</type>
<uuid>09384d4f-df19-479b-85fc-6451e56d41aa</uuid>
</filesystem>
<filesystem dev="/dev/vda5">
<type>swap</type>
<uuid>c0d1b68f-c0f9-4497-a366-938b37532613</uuid>
</filesystem>
</filesystems>
<package_format>dpkg</package_format>
<package_management>apt</package_management>
</operatingsystem>
</operatingsystems>

View File

@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
# virt-inspector
# Copyright (C) 2009 Red Hat Inc.
# Copyright (C) 2010 Red Hat Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,24 +20,19 @@ use warnings;
use strict;
use Sys::Guestfs;
use Sys::Guestfs::Lib qw(open_guest get_partitions resolve_windows_path
inspect_all_partitions inspect_partition
inspect_operating_systems mount_operating_system inspect_in_detail);
use Sys::Guestfs::Lib qw(open_guest);
use Pod::Usage;
use Getopt::Long;
use Data::Dumper;
use File::Temp qw/tempfile/;
use File::Basename;
use XML::Writer;
use String::ShellQuote qw(shell_quote);
use Locale::TextDomain 'libguestfs';
# Optional:
eval "use YAML::Any;";
=encoding utf8
=head1 NAME
virt-inspector - Display OS version, kernel, drivers, mount points, applications, etc. in a virtual machine
virt-inspector - Display operating system version and other information about a virtual machine
=head1 SYNOPSIS
@@ -47,13 +42,11 @@ virt-inspector - Display OS version, kernel, drivers, mount points, applications
=head1 DESCRIPTION
B<virt-inspector> examines a virtual machine and tries to determine
the version of the OS, the kernel version, what drivers are installed,
whether the virtual machine is fully virtualized (FV) or
para-virtualized (PV), what applications are installed and more.
B<virt-inspector> examines a virtual machine or disk image and tries
to determine the version of the operating system and other information
about the virtual machine.
Virt-inspector can produce output in several formats, including a
readable text report, and XML for feeding into other programs.
Virt-inspector produces XML output for feeding into other programs.
In the normal usage, use C<virt-inspector domname> where C<domname> is
the libvirt domain (see: C<virsh list --all>).
@@ -122,72 +115,6 @@ parameter is ignored.
If working with untrusted raw-format guest disk images, you should
ensure the format is always specified.
=cut
my $output = "text";
=back
The following options select the output format. Use only one of them.
The default is a readable text report.
=over 4
=item B<--text> (default)
Plain text report.
=item B<--none>
Produce no output at all.
=item B<--xml>
If you select I<--xml> then you get XML output which can be fed
to other programs.
=item B<--yaml>
If you select I<--yaml> then you get YAML output which can be fed
to other programs.
=item B<--perl>
If you select I<--perl> then you get Perl structures output which
can be used directly in another Perl program.
=item B<--fish>
=item B<--ro-fish>
If you select I<--fish> then we print a L<guestfish(1)> command
line which will automatically mount up the filesystems on the
correct mount points. Try this for example:
guestfish $(virt-inspector --fish guest.img)
I<--ro-fish> is the same, but the I<--ro> option is passed to
guestfish so that the filesystems are mounted read-only.
=item B<--query>
In "query mode" we answer common questions about the guest, such
as whether it is fullvirt or needs a Xen hypervisor to run.
See section I<QUERY MODE> below.
=cut
my $windows_registry;
=item B<--windows-registry>
This flag is ignored for compatibility with earlier releases of the
software.
In this version, if L<Win::Hivex(3)> is available, then we attempt to
parse information out of the Registry for any Windows guest.
=back
=cut
@@ -196,17 +123,6 @@ GetOptions ("help|?" => \$help,
"version" => \$version,
"connect|c=s" => \$uri,
"format=s" => \$format,
"text" => sub { $output = "text" },
"none" => sub { $output = "none" },
"xml" => sub { $output = "xml" },
"yaml" => sub { $output = "yaml" },
"perl" => sub { $output = "perl" },
"fish" => sub { $output = "fish" },
"guestfish" => sub { $output = "fish" },
"ro-fish" => sub { $output = "ro-fish" },
"ro-guestfish" => sub { $output = "ro-fish" },
"query" => sub { $output = "query" },
"windows-registry" => \$windows_registry,
) or pod2usage (2);
pod2usage (1) if $help;
if ($version) {
@@ -217,663 +133,220 @@ if ($version) {
}
pod2usage (__"virt-inspector: no image or VM names given") if @ARGV == 0;
my $rw = 0;
# XXX This is a bug: Originally we intended to open the guest with
# rw=>1 in order to tell Sys::Guestfs::Lib that we should disallow
# active domains. However this also has the effect of opening the
# disk image in write mode, and in any case we don't use this option
# in guestfish any more since we moved all the inspection code into
# the core library. We should drop the fish output modes completely.
$rw = 1 if $output eq "fish";
my $g;
my @images;
if ($uri) {
my ($conn, $dom);
($g, $conn, $dom, @images) =
open_guest (\@ARGV, rw => $rw, address => $uri, format => $format);
} else {
my ($conn, $dom);
($g, $conn, $dom, @images) =
open_guest (\@ARGV, rw => $rw, format => $format);
}
my @args = (\@ARGV);
push @args, address => $uri if defined $uri;
push @args, format => $format if defined $format;
my $g = open_guest (@args);
$g->launch ();
=head1 OUTPUT FORMAT
Operating system(s)
-------------------
Linux (distro + version)
Windows (version)
|
|
+--- Filesystems ---------- Installed apps --- Kernel & drivers
----------- -------------- ----------------
mount point => device List of apps Extra information
mount point => device and versions about kernel(s)
... and drivers
swap => swap device
(plus lots of extra information
about each filesystem)
The output of virt-inspector is a complex two-level data structure.
At the top level is a list of the operating systems installed on the
guest. (For the vast majority of guests, only a single OS is
installed.) The data returned for the OS includes the name (Linux,
Windows), the distribution and version.
The diagram above shows what we return for each OS.
With the I<--xml> option the output is mapped into an XML document.
There is a RELAX-NG schema for this XML in the file
I<virt-inspector.rng> which normally ships with virt-inspector, or can
be found in the source.
With the I<--fish> or I<--ro-fish> option the mount points are mapped to
L<guestfish(1)> command line parameters, so that you can go in
afterwards and inspect the guest with everything mounted in the
right place. For example:
guestfish $(virt-inspector --ro-fish guest.img)
==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot
=cut
# List of possible filesystems.
my @partitions = get_partitions ($g);
# Now query each one to build up a picture of what's in it.
my %fses =
inspect_all_partitions ($g, \@partitions);
#print "fses -----------\n";
#print Dumper(\%fses);
my $oses = inspect_operating_systems ($g, \%fses);
#print "oses -----------\n";
#print Dumper($oses);
# Mount up the disks so we can check for applications
# and kernels. Skip this if the output is "*fish" because
# we don't need to know.
if ($output !~ /.*fish$/) {
my $root_dev;
foreach $root_dev (sort keys %$oses) {
my $os = $oses->{$root_dev};
mount_operating_system ($g, $os);
inspect_in_detail ($g, $os);
$g->umount_all ();
}
my @roots = $g->inspect_os ();
if (@roots == 0) {
die __x("{prog}: No operating system could be detected inside this disk image.\n\nThis may be because the file is not a disk image, or is not a virtual machine\nimage, or because the OS type is not understood by libguestfs.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n",
prog => basename ($0));
}
#----------------------------------------------------------------------
# Output.
# Start the XML output.
my $xml = new XML::Writer (DATA_MODE => 1, DATA_INDENT => 2);
if ($output eq "fish" || $output eq "ro-fish") {
my @osdevs = keys %$oses;
# This only works if there is a single OS.
die __"--fish output is only possible with a single OS\n" if @osdevs != 1;
$xml->startTag ("operatingsystems");
my $root_dev = $osdevs[0];
if ($output eq "ro-fish") {
print "--ro ";
my $root;
foreach $root (@roots) {
my %fses = $g->inspect_get_mountpoints ($root);
my @fses = sort { length $a <=> length $b } keys %fses;
foreach (@fses) {
$g->mount_ro ($fses{$_}, $_);
}
foreach (@images) {
unless (defined $_->[1]) {
printf "-a %s ", shell_quote ($_->[0]);
} else {
printf "--format %s -a %s ",
shell_quote ($_->[1]), shell_quote ($_->[0]);
}
}
$xml->startTag ("operatingsystem");
my $mounts = $oses->{$root_dev}->{mounts};
# Have to mount / first. Luckily '/' is early in the ASCII
# character set, so this should be OK.
foreach (sort keys %$mounts) {
if ($_ ne "swap" && $_ ne "none") {
printf "-m %s ", shell_quote ("$mounts->{$_}:$_");
}
}
print "\n"
}
# Basic OS fields.
$xml->dataElement (root => $root);
# Perl output.
elsif ($output eq "perl") {
print Dumper(%$oses);
}
my ($s, $distro, $major_version);
$s = $g->inspect_get_type ($root);
$xml->dataElement (name => $s) if $s ne "unknown";
$s = $g->inspect_get_arch ($root);
$xml->dataElement (arch => $s) if $s ne "unknown";
$distro = $g->inspect_get_distro ($root);
$xml->dataElement (distro => $distro) if $distro ne "unknown";
$s = $g->inspect_get_product_name ($root);
$xml->dataElement (product_name => $s) if $s ne "unknown";
$major_version = $g->inspect_get_major_version ($root);
$xml->dataElement (major_version => $major_version);
$s = $g->inspect_get_minor_version ($root);
$xml->dataElement (minor_version => $s);
# YAML output
elsif ($output eq "yaml") {
die __"virt-inspector: no YAML support, try installing perl-YAML or libyaml-perl\n"
unless exists $INC{"YAML/Any.pm"};
eval {
$s = $g->inspect_get_windows_systemroot ($root);
$xml->dataElement (windows_systemroot => $s);
};
print Dump(%$oses);
}
# Mountpoints.
output_mountpoints ($root, \@fses, \%fses);
# Plain text output (the default).
elsif ($output eq "text") {
output_text ();
}
# Filesystems.
output_filesystems ($root);
# XML output.
elsif ($output eq "xml") {
output_xml ();
}
# Query mode.
elsif ($output eq "query") {
output_query ();
}
sub output_text
{
output_text_os ($oses->{$_}) foreach sort keys %$oses;
}
sub output_text_os
{
my $os = shift;
print $os->{os}, " " if exists $os->{os};
print $os->{distro}, " " if exists $os->{distro};
print $os->{arch}, " " if exists $os->{arch};
print $os->{major_version} if exists $os->{major_version};
print ".", $os->{minor_version} if exists $os->{minor_version};
print " (", $os->{product_name}, ")" if exists $os->{product_name};
print " ";
print "on ", $os->{root_device}, ":\n";
print __" Mountpoints:\n";
my $mounts = $os->{mounts};
foreach (sort keys %$mounts) {
printf " %-30s %s\n", $mounts->{$_}, $_
}
print __" Filesystems:\n";
my $filesystems = $os->{filesystems};
foreach (sort keys %$filesystems) {
print " $_:\n";
print " label: $filesystems->{$_}{label}\n"
if exists $filesystems->{$_}{label};
print " UUID: $filesystems->{$_}{uuid}\n"
if exists $filesystems->{$_}{uuid};
print " type: $filesystems->{$_}{fstype}\n"
if exists $filesystems->{$_}{fstype};
print " content: $filesystems->{$_}{content}\n"
if exists $filesystems->{$_}{content};
}
if (exists $os->{modprobe_aliases}) {
my %aliases = %{$os->{modprobe_aliases}};
my @keys = sort keys %aliases;
if (@keys) {
print __" Modprobe aliases:\n";
foreach (@keys) {
printf " %-30s %s\n", $_, $aliases{$_}->{modulename}
}
}
}
if (exists $os->{initrd_modules}) {
my %modvers = %{$os->{initrd_modules}};
my @keys = sort keys %modvers;
if (@keys) {
print __" Initrd modules:\n";
foreach (@keys) {
my @modules = @{$modvers{$_}};
print " $_:\n";
print " $_\n" foreach @modules;
}
}
}
print __" Applications:\n";
my @apps = @{$os->{apps}};
foreach (@apps) {
print " $_->{name} $_->{version}\n"
}
if ($os->{kernels}) {
print __" Kernels:\n";
my @kernels = @{$os->{kernels}};
foreach (@kernels) {
print " $_->{version} ($_->{arch})\n";
my @modules = @{$_->{modules}};
foreach (@modules) {
print " $_\n";
}
}
}
if (exists $os->{root}->{registry}) {
print __" Windows Registry entries:\n";
# These are just lumps of text - dump them out.
foreach (@{$os->{root}->{registry}}) {
print "$_\n";
}
}
}
sub output_xml
{
my $xml = new XML::Writer(DATA_MODE => 1, DATA_INDENT => 2);
$xml->startTag("operatingsystems");
output_xml_os ($oses->{$_}, $xml) foreach sort keys %$oses;
$xml->endTag("operatingsystems");
$xml->end();
}
sub output_xml_os
{
my ($os, $xml) = @_;
$xml->startTag("operatingsystem");
foreach ( [ "name" => "os" ],
[ "distro" => "distro" ],
[ "product_name" => "product_name" ],
[ "arch" => "arch" ],
[ "major_version" => "major_version" ],
[ "minor_version" => "minor_version" ],
[ "package_format" => "package_format" ],
[ "package_management" => "package_management" ],
[ "root" => "root_device" ] ) {
$xml->dataElement($_->[0], $os->{$_->[1]}) if exists $os->{$_->[1]};
}
$xml->startTag("mountpoints");
my $mounts = $os->{mounts};
foreach (sort keys %$mounts) {
$xml->dataElement("mountpoint", $_, "dev" => $mounts->{$_});
}
$xml->endTag("mountpoints");
$xml->startTag("filesystems");
my $filesystems = $os->{filesystems};
foreach (sort keys %$filesystems) {
$xml->startTag("filesystem", "dev" => $_);
foreach my $field ( [ "label" => "label" ],
[ "uuid" => "uuid" ],
[ "type" => "fstype" ],
[ "content" => "content" ],
[ "spec" => "spec" ] ) {
$xml->dataElement($field->[0], $filesystems->{$_}{$field->[1]})
if exists $filesystems->{$_}{$field->[1]};
}
$xml->endTag("filesystem");
}
$xml->endTag("filesystems");
if (exists $os->{modprobe_aliases}) {
my %aliases = %{$os->{modprobe_aliases}};
my @keys = sort keys %aliases;
if (@keys) {
$xml->startTag("modprobealiases");
foreach (@keys) {
$xml->startTag("alias", "device" => $_);
foreach my $field ( [ "modulename" => "modulename" ],
[ "augeas" => "augeas" ],
[ "file" => "file" ] ) {
$xml->dataElement($field->[0], $aliases{$_}->{$field->[1]});
}
$xml->endTag("alias");
}
$xml->endTag("modprobealiases");
}
}
if (exists $os->{initrd_modules}) {
my %modvers = %{$os->{initrd_modules}};
my @keys = sort keys %modvers;
if (@keys) {
$xml->startTag("initrds");
foreach (@keys) {
my @modules = @{$modvers{$_}};
$xml->startTag("initrd", "version" => $_);
$xml->dataElement("module", $_) foreach @modules;
$xml->endTag("initrd");
}
$xml->endTag("initrds");
}
}
$xml->startTag("applications");
my @apps = @{$os->{apps}};
foreach (@apps) {
$xml->startTag("application");
$xml->dataElement("name", $_->{name});
$xml->dataElement("epoch", $_->{epoch}) if defined $_->{epoch};
$xml->dataElement("version", $_->{version});
$xml->dataElement("release", $_->{release});
$xml->dataElement("arch", $_->{arch});
$xml->endTag("application");
}
$xml->endTag("applications");
if(defined($os->{boot}) && defined($os->{boot}->{configs})) {
my $default = $os->{boot}->{default};
my $configs = $os->{boot}->{configs};
$xml->startTag("boot");
for(my $i = 0; $i < scalar(@$configs); $i++) {
my $config = $configs->[$i];
my @attrs = ();
push(@attrs, ("default" => 1)) if($default == $i);
$xml->startTag("config", @attrs);
$xml->dataElement("title", $config->{title});
$xml->dataElement("kernel", $config->{kernel}->{version})
if(defined($config->{kernel}));
$xml->dataElement("cmdline", $config->{cmdline})
if(defined($config->{cmdline}));
$xml->endTag("config");
}
$xml->endTag("boot");
}
if ($os->{kernels}) {
$xml->startTag("kernels");
my @kernels = @{$os->{kernels}};
foreach (@kernels) {
$xml->startTag("kernel",
"version" => $_->{version},
"arch" => $_->{arch});
$xml->startTag("modules");
my @modules = @{$_->{modules}};
foreach (@modules) {
$xml->dataElement("module", $_);
}
$xml->endTag("modules");
$xml->dataElement("path", $_->{path}) if(defined($_->{path}));
$xml->dataElement("package", $_->{package}) if(defined($_->{package}));
$xml->endTag("kernel");
}
$xml->endTag("kernels");
}
if (exists $os->{root}->{registry}) {
$xml->startTag("windowsregistryentries");
# These are just lumps of text - dump them out.
foreach (@{$os->{root}->{registry}}) {
$xml->dataElement("windowsregistryentry", $_);
}
$xml->endTag("windowsregistryentries");
}
# Package format / management and applications.
output_applications ($root, $distro, $major_version);
$xml->endTag("operatingsystem");
$g->umount_all ();
}
=head1 QUERY MODE
# End the XML output.
$xml->endTag ("operatingsystems");
$xml->end ();
When you use C<virt-inspector --query>, the output is a series of
lines of the form:
windows=no
linux=yes
fullvirt=yes
xen_pv_drivers=no
(each answer is usually C<yes> or C<no>, or the line is completely
missing if we could not determine the answer at all).
If the guest is multiboot, you can get apparently conflicting answers
(eg. C<windows=yes> and C<linux=yes>, or a guest which is both
fullvirt and has a Xen PV kernel). This is normal, and just means
that the guest can do both things, although it might require operator
intervention such as selecting a boot option when the guest is
booting.
This section describes the full range of answers possible.
=over 4
=cut
sub output_query
sub output_mountpoints
{
output_query_windows ();
output_query_linux ();
output_query_rhel ();
output_query_fedora ();
output_query_debian ();
output_query_fullvirt ();
output_query_xen_domU_kernel ();
output_query_xen_pv_drivers ();
output_query_virtio_drivers ();
output_query_kernel_arch ();
output_query_userspace_arch ();
}
local $_;
my $root = shift;
my $fskeys = shift;
my $fshash = shift;
=item windows=(yes|no)
Answer C<yes> if Microsoft Windows is installed in the guest.
=cut
sub output_query_windows
{
my $windows = "no";
foreach my $os (keys %$oses) {
$windows="yes" if $oses->{$os}->{os} eq "windows";
$xml->startTag ("mountpoints");
foreach (@$fskeys) {
$xml->dataElement ("mountpoint", $_, dev => $fshash->{$_});
}
print "windows=$windows\n";
$xml->endTag ("mountpoints");
}
=item linux=(yes|no)
Answer C<yes> if a Linux kernel is installed in the guest.
=cut
sub output_query_linux
sub output_filesystems
{
my $linux = "no";
foreach my $os (keys %$oses) {
$linux="yes" if $oses->{$os}->{os} eq "linux";
local $_;
my $root = shift;
$xml->startTag ("filesystems");
my @fses = $g->inspect_get_filesystems ($root);
foreach (@fses) {
$xml->startTag ("filesystem", dev => $_);
eval {
my $type = $g->vfs_type ($_);
$xml->dataElement (type => $type)
if defined $type && $type ne "";
};
eval {
my $label = $g->vfs_label ($_);
$xml->dataElement (label => $label)
if defined $label && $label ne "";
};
eval {
my $uuid = $g->vfs_uuid ($_);
$xml->dataElement (uuid => $uuid)
if defined $uuid && $uuid ne "";
};
$xml->endTag ("filesystem");
}
print "linux=$linux\n";
$xml->endTag ("filesystems");
}
=item rhel=(yes|no)
Answer C<yes> if the guest contains Red Hat Enterprise Linux.
=cut
sub output_query_rhel
sub output_applications
{
my $rhel = "no";
foreach my $os (keys %$oses) {
$rhel="yes" if ($oses->{$os}->{os} eq "linux" &&
$oses->{$os}->{distro} eq "rhel");
}
print "rhel=$rhel\n";
}
local $_;
my $root = shift;
my $distro = shift;
my $major_version = shift;
=item fedora=(yes|no)
Answer C<yes> if the guest contains the Fedora Linux distribution.
=cut
sub output_query_fedora
{
my $fedora = "no";
foreach my $os (keys %$oses) {
$fedora="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "fedora";
}
print "fedora=$fedora\n";
}
=item debian=(yes|no)
Answer C<yes> if the guest contains the Debian Linux distribution.
=cut
sub output_query_debian
{
my $debian = "no";
foreach my $os (keys %$oses) {
$debian="yes" if $oses->{$os}->{os} eq "linux" && $oses->{$os}->{distro} eq "debian";
}
print "debian=$debian\n";
}
=item fullvirt=(yes|no)
Answer C<yes> if there is at least one operating system kernel
installed in the guest which runs fully virtualized. Such a guest
would require a hypervisor which supports full system virtualization.
=cut
sub output_query_fullvirt
{
# The assumption is full-virt, unless all installed kernels
# are identified as paravirt.
# XXX Fails on Windows guests.
foreach my $os (keys %$oses) {
foreach my $kernel (@{$oses->{$os}->{kernels}}) {
my $is_pv = $kernel->{version} =~ m/xen/;
unless ($is_pv) {
print "fullvirt=yes\n";
return;
# Based on the distro, take a guess at the package format
# and package management.
my ($package_format, $package_management);
if (defined $distro) {
if ($distro eq "debian") {
$package_format = "dpkg";
$package_management = "apt";
}
elsif ($distro eq "fedora") {
$package_format = "rpm";
$package_management = "yum";
}
elsif ($distro =~ /redhat/ || $distro =~ /rhel/) {
if ($major_version >= 5) {
$package_format = "rpm";
$package_management = "yum";
} else {
$package_format = "rpm";
$package_management = "up2date";
}
}
# else unknown.
}
$xml->dataElement (package_format => $package_format)
if defined $package_format;
$xml->dataElement (package_management => $package_management)
if defined $package_management;
# Do we know how to get a list of applications?
if (defined $package_format) {
if ($package_format eq "rpm") {
output_applications_rpm ($root);
}
# else no we don't.
}
print "fullvirt=no\n";
}
=item xen_domU_kernel=(yes|no)
Answer C<yes> if there is at least one Linux kernel installed in
the guest which is compiled as a Xen DomU (a Xen paravirtualized
guest).
=cut
sub output_query_xen_domU_kernel
sub output_applications_rpm
{
foreach my $os (keys %$oses) {
foreach my $kernel (@{$oses->{$os}->{kernels}}) {
my $is_xen = $kernel->{version} =~ m/xen/;
if ($is_xen) {
print "xen_domU_kernel=yes\n";
return;
local $_;
my $root = shift;
# Previous virt-inspector ran the 'rpm' program from the guest.
# This is insecure, and unnecessary because we can get the same
# information directly from the RPM database.
my @applications;
eval {
my ($fh, $filename) = tempfile (UNLINK => 1);
my $fddev = "/dev/fd/" . fileno ($fh);
$g->download ("/var/lib/rpm/Name", $fddev);
close $fh or die "close: $!";
# Read the database with the Berkeley DB dump tool.
my $cmd = "db_dump -p '$filename'";
open PIPE, "$cmd |" or die "close: $!";
while (<PIPE>) {
chomp;
last if /^HEADER=END$/;
}
while (<PIPE>) {
chomp;
last if /^DATA=END$/;
# First character on each data line is a space.
if (length $_ > 0 && substr ($_, 0, 1) eq ' ') {
$_ = substr ($_, 1);
}
# Name should never contain non-printable chars.
die "name contains non-printable chars" if /\\/;
push @applications, $_;
$_ = <PIPE>; # discard value
}
}
print "xen_domU_kernel=no\n";
}
=item xen_pv_drivers=(yes|no)
Answer C<yes> if the guest has Xen paravirtualized drivers installed
(usually the kernel itself will be fully virtualized, but the PV
drivers have been installed by the administrator for performance
reasons).
=cut
sub output_query_xen_pv_drivers
{
foreach my $os (keys %$oses) {
foreach my $kernel (@{$oses->{$os}->{kernels}}) {
foreach my $module (@{$kernel->{modules}}) {
if ($module =~ m/xen-/) {
print "xen_pv_drivers=yes\n";
return;
}
}
close PIPE or die "close: $!";
};
if (!$@ && @applications > 0) {
@applications = sort @applications;
$xml->startTag ("applications");
foreach (@applications) {
$xml->startTag ("application");
$xml->dataElement (name => $_);
$xml->endTag ("application");
}
}
print "xen_pv_drivers=no\n";
}
=item virtio_drivers=(yes|no)
Answer C<yes> if the guest has virtio paravirtualized drivers
installed. Virtio drivers are commonly used to improve the
performance of KVM.
=cut
sub output_query_virtio_drivers
{
foreach my $os (keys %$oses) {
foreach my $kernel (@{$oses->{$os}->{kernels}}) {
foreach my $module (@{$kernel->{modules}}) {
if ($module =~ m/virtio_/) {
print "virtio_drivers=yes\n";
return;
}
}
}
}
print "virtio_drivers=no\n";
}
=item userspace_arch=(x86_64|...)
Print the architecture of userspace.
NB. For multi-boot VMs this can print several lines.
=cut
sub output_query_userspace_arch
{
my %arches;
foreach my $os (keys %$oses) {
$arches{$oses->{$os}->{arch}} = 1 if exists $oses->{$os}->{arch};
}
foreach (sort keys %arches) {
print "userspace_arch=$_\n";
$xml->endTag ("applications");
}
}
=item kernel_arch=(x86_64|...)
Print the architecture of the kernel.
NB. For multi-boot VMs this can print several lines.
=cut
sub output_query_kernel_arch
{
my %arches;
foreach my $os (keys %$oses) {
foreach my $kernel (@{$oses->{$os}->{kernels}}) {
$arches{$kernel->{arch}} = 1 if exists $kernel->{arch};
}
}
foreach (sort keys %arches) {
print "kernel_arch=$_\n";
}
}
=back
=head1 SHELL QUOTING
Libvirt guest names can contain arbitrary characters, some of which
@@ -892,13 +365,21 @@ L<http://libguestfs.org/>.
=head1 AUTHORS
=over 4
=item *
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
=item *
Matthew Booth L<mbooth@redhat.com>
=back
=head1 COPYRIGHT
Copyright (C) 2009 Red Hat Inc.
Copyright (C) 2010 Red Hat Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -23,7 +23,7 @@
<element name="operatingsystem">
<interleave>
<!-- required fields for an operating system -->
<optional><element name="root"><text/></element></optional>
<element name="name">
<choice>
<value>linux</value>
@@ -31,23 +31,18 @@
</choice>
</element>
<element name="arch"><text/></element>
<element name="root"><text/></element>
<!-- optional fields for an operating system -->
<optional><element name="distro"><text/></element></optional>
<optional><element name="product_name"><text/></element></optional>
<optional><element name="major_version"><text/></element></optional>
<optional><element name="minor_version"><text/></element></optional>
<element name="major_version"><text/></element>
<element name="minor_version"><text/></element>
<optional><element name="windows_systemroot"><text/></element></optional>
<optional><element name="package_format"><text/></element></optional>
<optional><element name="package_management"><text/></element></optional>
<ref name="mountpoints"/>
<ref name="filesystems"/>
<optional><ref name="applications"/></optional>
<optional><ref name="modprobealiases"/></optional>
<optional><ref name="initrds"/></optional>
<optional><ref name="kernels"/></optional>
<optional><ref name="boot"/></optional>
</interleave>
</element>
@@ -74,11 +69,9 @@
<element name="filesystem">
<attribute name="dev"><text/></attribute>
<interleave>
<element name="type"><text/></element>
<optional><element name="content"><text/></element></optional>
<optional><element name="type"><text/></element></optional>
<optional><element name="label"><text/></element></optional>
<optional><element name="uuid"><text/></element></optional>
<optional><element name="spec"><text/></element></optional>
</interleave>
</element>
</oneOrMore>
@@ -91,79 +84,6 @@
<zeroOrMore>
<element name="application">
<element name="name"><text/></element>
<optional><element name="epoch"><text/></element></optional>
<element name="version"><text/></element>
<element name="release"><text/></element>
<element name="arch"><text/></element>
</element>
</zeroOrMore>
</element>
</define>
<!-- contents of /etc/modprobe* -->
<define name="modprobealiases">
<element name="modprobealiases">
<zeroOrMore>
<element name="alias">
<attribute name="device"><text/></attribute>
<interleave>
<element name="modulename"><text/></element>
<optional><element name="augeas"><text/></element></optional>
<element name="file"><text/></element>
</interleave>
</element>
</zeroOrMore>
</element>
</define>
<!-- initrd images found -->
<define name="initrds">
<element name="initrds">
<zeroOrMore>
<element name="initrd">
<attribute name="version"><text/></attribute>
<zeroOrMore>
<element name="module"><text/></element>
</zeroOrMore>
</element>
</zeroOrMore>
</element>
</define>
<!-- boot configurations -->
<define name="boot">
<element name="boot">
<zeroOrMore>
<element name="config">
<optional>
<attribute name="default"><value>1</value></attribute>
</optional>
<interleave>
<element name="title"><text/></element>
<element name="kernel"><text/></element>
<element name="cmdline"><text/></element>
</interleave>
</element>
</zeroOrMore>
</element>
</define>
<!-- kernels -->
<define name="kernels">
<element name="kernels">
<zeroOrMore>
<element name="kernel">
<attribute name="version"><text/></attribute>
<attribute name="arch"><text/></attribute>
<interleave>
<element name="modules">
<zeroOrMore>
<element name="module"><text/></element>
</zeroOrMore>
</element>
<optional><element name="path"><text/></element></optional>
<optional><element name="package"><text/></element></optional>
</interleave>
</element>
</zeroOrMore>
</element>