use strict;
use Net::SNMP;
+use Sys::Virt;
use Pod::Usage;
use Getopt::Long;
use Locale::TextDomain 'virt-tools';
format can be imported easily into databases and spreadsheets, but
read L</NOTE ABOUT CSV FORMAT> below.
+=cut
+
+my $verbose;
+
+=item B<--verbose> | B<-v>
+
+Enable verbose messages, useful for debugging.
+
=back
=cut
"version" => \$version,
"connect|c=s" => \$uri,
"csv" => \$csv,
+ "verbose|v" => \$verbose,
) or pod2usage (2);
pod2usage (1) if $help;
if ($version) {
}
my %subcommands = (
- "virt-uname" => [ &do_uname, &title_uname ],
- "virt-ps" => [ &do_ps, &title_ps ],
- "virt-ping" => [ &do_ping, &title_ping ],
+ "virt-uname" => [ \&do_uname, \&title_uname ],
+ "virt-ps" => [ \&do_ps, \&title_ps ],
+ "virt-ping" => [ \&do_ping, \&title_ping ],
);
# Which subcommand?
my ($do_it, $title_it);
foreach (keys %subcommands) {
if ($0 =~ /$_/) {
+ print STDERR "subcommand = $_\n" if $verbose;
$do_it = $subcommands{$_}->[0];
$title_it = $subcommands{$_}->[1];
last;
}
die "$0: cannot determine which sub-command to run\n" unless $do_it;
+# If we are being run from a local directory, add that directory to
+# the path, so we can be run from the source directory without being
+# installed.
+if (substr ($0, 0, 1) ne "/") {
+ $_ = $0;
+ s{/[^/]+$}{};
+ $ENV{PATH} = "$_:$ENV{PATH}"; # XXX Windows?
+ print STDERR "PATH set to $ENV{PATH}\n" if $verbose;
+}
+
+our $errors = 0;
+
# Do we have named guests?
if (@ARGV == 0) {
my $conn;
my @domnames = map { $_->get_name () } @doms;
if (@domnames) {
- title_it ();
+ &$title_it ();
foreach (@domnames) {
- my ($key, $transport);
- eval {
- $key = get_key ($_);
- $transport = get_transport ($_);
- };
- if (!$@) { do_it ($_, $key, $transport) }
+ get_and_do_it ($_);
}
}
} else {
- title_it ();
+ &$title_it ();
foreach (@ARGV) {
- my ($key, $transport);
- eval {
- $key = get_key ($_);
- $transport = get_transport ($_);
- };
- if (!$@) { do_it ($_, $key, $transport) }
+ get_and_do_it ($_);
}
}
-exit 0;
+sub get_and_do_it
+{
+ # Turn any errors into warnings.
+ eval {
+ my ($key, $transport);
+ $key = get_key ($_);
+ $transport = get_transport ($_);
+ &$do_it ($_, $key, $transport);
+ };
+ if ($@) {
+ $errors++;
+ warn "$@";
+ }
+}
+
+print STDERR "errors = $errors\n" if $verbose;
+
+exit ($errors == 0 ? 0 : 1);
=head1 virt-uname
=cut
+sub print_row
+{
+ my @fields = @_;
+
+ local $_;
+ my $comma = 0;
+
+ foreach (@fields) {
+ print "," if $comma;
+ $comma = 1;
+
+ if (!$csv) {
+ printf "%-16s ", $_
+ } else {
+ # XXX Use Text::CSV here.
+ if ($_ =~ /"/) {
+ s/"/""/;
+ printf "\"%s\"", $_;
+ } elsif ($_ =~ /,/ || $_ =~ /\n/) {
+ printf "\"%s\"", $_;
+ } else {
+ print $_;
+ }
+ }
+ }
+ print "\n";
+}
+
=head1 OVERVIEW
Virt-tools are a set of tools that you can install in your virtual
programs like C<virt-uname> and C<virt-ps> that you run on the host,
to query guest information. On the guest, you have to install and run
a virt-tools service. Between the host and guest is a transport which
-should be secured. The L</GUEST CONFIGURATION> section describes how
-to configure guests and secure the transport.
+should be secured.
+
+The L</GUEST ARCHITECTURE> section describes how virt-tools appears
+from the guest side.
+
+The L</HOST ARCHITECTURE> section describes the architecture of
+virt-tools on the host side.
-Finally the L</ARCHITECTURE> section describes the architecture of
-virt-tools and provides information about diagnosing problems.
+=head1 GUEST ARCHITECTURE
-=head1 GUEST CONFIGURATION
+In most cases, you can just install the C<virt-tools-guest> package in
+your Linux guests, or the Windows virt-tools guest package in your
+Windows guests, and everything should just work. In this section we
+describe more about how it works (or is supposed to work) from the
+guest side.
+=head2 COMMUNICATIONS DIRECTORY
+The guest writes various static, mostly unchanging, information into
+its own directory. On Linux the directory is C</var/lib/virt-tools/>
+and under Windows it is C<%systemroot%\virttool\>. In the discussion
+below, this communications directory is referred to as
+C<$GUESTCOMMSDIR>.
+The host is able to read files out of this directory using
+L<libguestfs(3)> (without any cooperation needed by the guest).
+=head2 IP ADDRESSES
+The host can't easily see the guest's IP address. The host provides
+the guest with a network interface connected to a bridge, but the
+guest can use any IP address it likes (although well-behaved guests
+will usually have some static IPs or are allocated one by DHCP).
+So when the guest starts up, or its IP address changes (usually these
+are rare events) the guest writes a file
+C<$GUESTCOMMSDIR/ip-E<lt>ifaceE<gt>> which contains details of the IP
+address of the interface E<lt>ifaceE<gt> (eg. the file might be called
+C<ip-eth0> under Linux).
+C<virt-ifconfig> reads this file directly using L<libguestfs(3)>.
+=head2 KEYS
+When the guest is first installed (or more precisely, when the
+virt-tools-guest package is first installed in the guest), a random
+secret key is generated. This is used to encrypt communications with
+the guest, and it is described in more detail below.
+The key is written to C<$GUESTCOMMSDIR/key>.
-=head1 ARCHITECTURE
+=head2 SNMP DAEMON
-Guests run an SNMP (Simple Network Management Protocol) server. The
-host client tools access this server in order to query information
-about the guest. They query this using standard SNMP calls.
+For process listings, and just about every other piece of data except
+for IP address, guests run a completely standard SNMP (Simple Network
+Management Protocol) server. The host client tools access this server
+in order to query information about the guest. They query this using
+standard SNMP calls.
The protocol used is SNMPv3 (RFC 2571) which addresses security
concerns in earlier versions of the protocol. In order to ensure that
-only the host can access the SNMP server, the guest generates a random
-secret key which the host must find out. Also the host must find a
-suitable transport to connect to the SNMP server (eg. by finding the
-IP address of the guest or using another transport into the guest).
+only the host can access the SNMP server and see the results, all
+communications are encrypted and authenticated using the guest's key.
+
+=head2 TRANSPORT
+
+There is not necessarily a network connection between the host and the
+guest. There are many configurations of virtualization in which the
+host has no network access to the guest: for example, if the host
+firewalls itself off from the guest (or vice versa), or if the guest
+has a physically separate network card from the host.
-Once the key and the transport to the guest are worked out, the query
-is a straightforward SNMP call:
+Therefore the guest to host SNMP transport is not necessarily over an
+IP network. Other transports are possible, including "vmchannel"
+(where "vmchannel" is the generic name for a collection of specialized
+host-guest communication channels implemented in different ways by
+different hypervisors).
+
+=head1 HOST ARCHITECTURE
+
+On the host side, the host uses L<libguestfs(3)> to read the guest's
+IP address and key, and uses some heuristics to determine the
+transport to use.
+
+Once the key and the transport to the guest are worked out, programs
+like C<virt-ps>, C<virt-uname> and so on are just making
+straightforward SNMP calls:
+-----------------+ +-----------------+
| host | | guest |
The difficulty is in determining the key and the transport to use,
which is what this section covers. You can also use this knowledge to
-diagnose problems, and to create non-standard configurations.
+diagnose problems or to create non-standard configurations.
=head2 DETERMINE KEY
C<virt-tools-get-key> to get the key of the guest. (See
L<virt-tools-get-key(8)> for the precise usage of this program).
-The key is generated by the guest once -- when the virt-tools package
-is installed in the guest. The key is written to a file
-C</var/lib/virt-tools/key> (in the guest) which is readable only by
-root.
-
-On Windows guests the key is written to
-C<%systemroot%\virttools.key>
+The key is generated by the guest once -- when the virt-tools-guest
+package is installed in the guest. The key is written to a file
+C<$GUESTCOMMSDIR/key> (in the guest) which is readable only by root.
Using L<libguestfs(3)> the host can read any file in the guest, so it
can read this key out directly. This is what the
C<virt-tools-get-key> program does, and you can run it by hand to
verify its operation:
- # virt-tools-get-key -v domname|uuid
+ # virt-tools-get-key -v domname
abcdef1234567890
=head3 KEY CACHE
sub get_key
{
+ my $domname = shift;
+
+ my $cmd = "virt-tools-get-key";
+ $cmd .= " -v" if $verbose;
+ # XXX quoting
+ $cmd .= " -c '$uri'" if $uri;
+ $cmd .= " '$domname'";
+
+ print STDERR "$cmd\n" if $verbose;
+
+ open PIPE, "$cmd |" or die "$cmd: $!";
+ my $line = <PIPE>;
+ die "no response from virt-tools-get-key\n" unless $line;
+ chomp $line;
+ close PIPE;
+
+ $line
}
=head2 DETERMINE TRANSPORT
guest, be it through a direct network connection or over some
hypervisor-specific vmchannel.
- # virt-tools-get-transport -v domname|uuid
+ # virt-tools-get-transport -v domname
udp:192.168.122.33
You can diagnose problems with the transport by trying to run this
sub get_transport
{
+ my $domname = shift;
+
+ my $cmd = "virt-tools-get-transport";
+ $cmd .= " -v" if $verbose;
+ # XXX quoting
+ $cmd .= " -c '$uri'" if $uri;
+ $cmd .= " '$domname'";
+
+ print STDERR "$cmd\n" if $verbose;
+
+ open PIPE, "$cmd |" or die "$cmd: $!";
+ my $line = <PIPE>;
+ die "no response from virt-tools-get-transport\n" unless $line;
+ chomp $line;
+ close PIPE;
+
+ $line
}
+=head2 SNMP QUERIES
+
+Standard SNMP queries are used between the host and guest.
+
+SNMP already supports many of the features we are trying to query
+(eg. the UCD SNMP MIB provides a way to query the process list of a
+machine in a form which is a de facto standard).
+
+To determine what precise queries are sent, run the tools in verbose
+mode or examine the source.
+
+=head2 RUNNING YOUR OWN SNMP SERVER IN A GUEST
+
+I<(To be written)>
+
=head1 NOTE ABOUT CSV FORMAT
Comma-separated values (CSV) is a deceptive format. It I<seems> like