#!/usr/bin/perl -w
# virt-ifconfig
# 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 Sys::Guestfs;
use Sys::Guestfs::Lib qw(open_guest get_partitions);
use Pod::Usage;
use Getopt::Long;
use Locale::TextDomain 'virt-tools';

=encoding utf8

=head1 NAME

virt-ifconfig - Display IP address of a virtual machine

=head1 SYNOPSIS

 virt-ifconfig [--options] domname

 virt-ifconfig [--options]

=head1 DESCRIPTION

C<virt-ifconfig domname> displays the IP address of a virtual machine
named C<domname>, where C<domname> is the libvirt name.  If no
C<domname> is given, this lists out the IP addresses of all running
virtual machines known to libvirt.

For Linux guests, this only works if the C<virt-tools-guest> package
is installed in the guest.

=head1 OPTIONS

=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
}

# Open the guest handle.

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 - who cares about their IP address?
    my @doms = $conn->list_domains ();

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

    if (@domnames) {
	print_title ();
        foreach (@domnames) {
            do_ifconfig ($_);
        }
    }
} else {
    print_title ();
    do_ifconfig (@ARGV);
}

sub print_title
{
    print_row (__"Guest", __"Iface", __"Family", __"IP address");
}

sub print_row
{
    my $domname = shift;
    my $iface = shift;
    my $family = shift;
    my $ip = shift;

    if (!$csv) {
	printf ("%-20s %-7s %-7s %s\n", $domname, $iface, $family, $ip);
    } else {
	printf ("%s,%s,%s,%s\n", $domname, $iface, $family, $ip);
    }
}

sub do_ifconfig
{
    my @args = @_;
    my $g;

    if ($uri) {
        $g = open_guest (\@args, address => $uri);
    } else {
        $g = open_guest (\@args);
    }

    $g->launch ();

    # Don't care about mountpoints.  Instead, just look for a
    # directory with one of a selection of names on one of the
    # partitions that we found.
    my @partitions = get_partitions ($g);

    my %ips;
    foreach my $partition (@partitions) {
        eval {
            $g->mount_ro ($partition, "/");
	    my $dir;
	    my @dirs = ("/var/lib/virt-tools", "/lib/virt-tools");
	    foreach $dir (@dirs) {
		if ($g->is_dir ($dir)) {
		    my @names = $g->ls ($dir);
		    @names = grep { /^ip-/ } @names;
		    my $name;
		    foreach $name (@names) {
			my $iface = $name;
			$iface =~ s/^ip-//;
			eval {
			    my @lines = $g->read_lines ("$dir/$name");
			    foreach (@lines) {
				if (m{^\s*inet (\S+)/\d+ }) {
				    $ips{$iface}{inet} = []
					unless exists $ips{$iface}{inet};
				    push @{$ips{$iface}{inet}}, $1;
				} elsif (m{^\s*inet6 (\S+)/\d+ }) {
				    $ips{$iface}{inet6} = []
					unless exists $ips{$iface}{inet6};
				    push @{$ips{$iface}{inet6}}, $1;
				}
			    }
			}
		    }
		}
	    }
        };
        $g->umount_all ();
    }

    my @ifaces = sort keys %ips;
    if (@ifaces) {
	my $iface;
	foreach $iface (@ifaces) {
	    my @families = sort keys %{$ips{$iface}};
	    my $family;
	    foreach $family (@families) {
		foreach (@{$ips{$iface}{$family}}) {
		    print_row ($args[0], $iface, $family, $_);
		}
	    }
	}
    } else {
	print STDERR
	    __x("{n}: no IP address found (is it running virt-tools-guest?)\n",
		n => $args[0]);
    }
}

exit 0;

=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-tools(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 AUTHOR

Richard W.M. Jones L<http://et.redhat.com/~rjones/>

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