#!/usr/bin/perl -w
# virt-tools
# Copyright (C) 2009 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

use strict;

use Net::SNMP;
use Pod::Usage;
use Getopt::Long;
use Locale::TextDomain 'virt-tools';

=encoding utf8

=head1 NAME

virt-uname, virt-ps, virt-ping - virtual machine information and statistics

=head1 SYNOPSIS

 virt-uname [--options] [domname]

 virt-ps [--options] [domname]

 virt-ping [--options] [domname]

 virt-ifconfig [--options] [domname]

=head1 COMMON OPTIONS

All the tools take either a single C<domname> parameter, which is the
name of the virtual machine as known to libvirt (C<virsh list>), or no
parameter in which case they operate on all currently running guests.

I<Note:> You must install the C<virt-tools-guest> package in each
Linux guest, otherwise these programs will not work.

There are some common options which can be supplied to any tool:

=over 4

=cut

my $help;

=item B<--help>

Display brief help.

=cut

my $version;

=item B<--version>

Display version number and exit.

=cut

my $uri;

=item B<--connect URI> | B<-c URI>

If using libvirt, connect to the given I<URI>.  If omitted, then we
connect to the default libvirt hypervisor.

=cut

my $csv;

=item B<--csv>

Write out the results in CSV format (comma-separated values).  This
format can be imported easily into databases and spreadsheets, but
read L</NOTE ABOUT CSV FORMAT> below.

=back

=cut

GetOptions ("help|?" => \$help,
            "version" => \$version,
            "connect|c=s" => \$uri,
            "csv" => \$csv,
    ) or pod2usage (2);
pod2usage (1) if $help;
if ($version) {
    print "@PACKAGE_STRING@\n";
    exit
}

my %subcommands = (
    "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 =~ /$_/) {
        $do_it = $subcommands{$_}->[0];
        $title_it = $subcommands{$_}->[1];
        last;
    }
}
die "$0: cannot determine which sub-command to run\n" unless $do_it;

# Do we have named guests?
if (@ARGV == 0) {
    my $conn;

    if ($uri) {
        $conn = Sys::Virt->new (readonly => 1, address => $uri);
    } else {
        $conn = Sys::Virt->new (readonly => 1);
    }

    # Ignore inactive domains.
    my @doms = $conn->list_domains ();

    my @domnames = map { $_->get_name () } @doms;

    if (@domnames) {
	title_it ();
        foreach (@domnames) {
            my ($key, $transport);
            eval {
                $key = get_key ($_);
                $transport = get_transport ($_);
            };
            if (!$@) { do_it ($_, $key, $transport) }
        }
    }
} else {
    title_it ();
    foreach (@ARGV) {
        my ($key, $transport);
        eval {
            $key = get_key ($_);
            $transport = get_transport ($_);
        };
        if (!$@) { do_it ($_, $key, $transport) }
    }
}

exit 0;

=head1 virt-uname

C<virt-uname> displays the system information (kernel version etc) of
the guest.

=cut

sub title_uname
{
    print_row (__"Guest");
}

sub do_uname
{
    my $domname = shift;
    my $key = shift;
    my $transport = shift;



}

=head1 virt-ps

C<virt-ps> displays the process list of the guest.

=cut

sub title_ps
{
    print_row (__"Guest");
}

sub do_ps
{
    my $domname = shift;
    my $key = shift;
    my $transport = shift;



}

=head1 virt-ping

C<virt-ping> pings the guest by making an empty virt-tools request,
and checking that it replies.  This can be used as a simple test that
virt-tools is available and working inside the guest.

=cut

sub title_ping
{
    print_row (__"Guest");
}

sub do_ping
{
    my $domname = shift;
    my $key = shift;
    my $transport = shift;



}

# virt-ifconfig is implemented separately.

=head1 virt-ifconfig

C<virt-ifconfig> displays the IP address of the guest.

=cut

=head1 OVERVIEW

Virt-tools are a set of tools that you can install in your virtual
machines (host and guests) to get enhanced information about the
guests.

Unlike VMWare Tools, virt-tools is hypervisor agnostic.  Also
virt-tools is just about collecting statistics and does not include
any performance or functionality enhancements for guests (see virtio
if you want that).

There are two parts to any virt-tools installation: some client
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.

Finally the L</ARCHITECTURE> section describes the architecture of
virt-tools and provides information about diagnosing problems.

=head1 GUEST CONFIGURATION











=head1 ARCHITECTURE

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.

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).

Once the key and the transport to the guest are worked out, the query
is a straightforward SNMP call:

 +-----------------+      +-----------------+
 | host            |      | guest           |
 |  virt-ps --- request ---> snmpd          |
 |         <---- reply -----                |
 +-----------------+      +-----------------+

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.

=head2 DETERMINE KEY

All the host tools use an external helper program called
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>

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
 abcdef1234567890

=head3 KEY CACHE

C<virt-tools-get-key> caches the keys of guests that it has seen
before so it doesn't have to read them each time.  The cache is in
C</var/lib/virt-tools/keys/> (in the host).

You can just delete the files in this directory at any time, I<or> you
can drop a file in here which contains the key of a guest.

To do this, create a file C</var/lib/virt-tools/keys/E<lt>UUIDE<gt>>
where E<lt>UUIDE<gt> is the guest's UUID as displayed by this command:

 virsh domuuid <name>

The contents of the file should be the key.

You can test this works by running C<virt-tools-get-key> by hand.

This cache never expires, unless you remove the files by hand.

=cut

sub get_key
{
}

=head2 DETERMINE TRANSPORT

All the host tools use a second helper program called
C<virt-tools-get-transport> to get the transport and address to use
for a guest.  (See L<virt-tools-get-transport(8)> for the precise
usage of this program).

This program tries a series of methods to determine how to access a
guest, be it through a direct network connection or over some
hypervisor-specific vmchannel.

 # virt-tools-get-transport -v domname|uuid
 udp:192.168.122.33

You can diagnose problems with the transport by trying to run this
command by hand.

=head3 TRANSPORT CACHE

C<virt-tools-get-transport> caches the transports of guests that it
has seen before so it doesn't have to determine them each time.  The
cache is in C</var/lib/virt-tools/transports/> (in the host).

As for the L</KEY CACHE>, this directory is just some files that are
named after the UUID of the guest, containing the transport.

Unlike the key cache, C<virt-tools-get-transport> will check that a
transport is still valid, and will expire (ie. delete) the
corresponding entry in the transport cache if it is not valid.

=cut

sub get_transport
{
}

=head1 NOTE ABOUT CSV FORMAT

Comma-separated values (CSV) is a deceptive format.  It I<seems> like
it should be easy to parse, but it is definitely not easy to parse.

Myth: Just split fields at commas.  Reality: This does I<not> work
reliably.  This example has two columns:

 "foo,bar",baz

Myth: Read the file one line at a time.  Reality: This does I<not>
work reliably.  This example has one row:

 "foo
 bar",baz

For shell scripts, use C<csvtool> (L<http://merjis.com/developers/csv>
also packaged in major Linux distributions).

For other languages, use a CSV processing library (eg. C<Text::CSV>
for Perl or Python's built-in csv library).

Most spreadsheets and databases can import CSV directly.

=head1 SEE ALSO

L<virt-ifconfig(8)>,
L<guestfs(3)>,
L<guestfish(1)>,
L<Sys::Guestfs(3)>,
L<Sys::Guestfs::Lib(3)>,
L<Sys::Virt(3)>,
L<http://libguestfs.org/>.

=head1 AUTHORS

=over 4

=item *

Richard W.M. Jones (C<rjones at redhat dot com>)

=item *

Matthew Booth (C<mbooth at redhat dot com>)

=back

=head1 COPYRIGHT

Copyright (C) 2009 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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
