The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings FATAL => 'all';

use CGI qw/:standard :cgi-lib/;
use URI::Escape 'uri_escape';
use Time::Local;
use Config::Any;

CGI::autoEscape(0);

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

my $yatg_config_file = $ENV{YATG_CONFIG_FILE} || die "no yatg conf location";
my $yatg_conf = Config::Any->load_files(
    {files => [$yatg_config_file], use_ext => 1})->[0]->{$yatg_config_file}
    || die "failed to load yatg config $yatg_config_file";

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

my $title  = "YATG : ";
my $output = '';

# TODO: in the future will allow these to be overridden
my $ytitle = uri_escape('Mbits/s');
my $lf     = 'lf=ifHCInOctets&lf=ifHCOutOctets';
my $lftxt  = 'lftxt=In&lftxt=Out';
my $end    = time;

print header;
my %p = Vars;

# FIXME need to validate params

if ( $p{ip} && $p{port} ) {
    my ($ip, $dns, $port, $name) = @p{qw(ip dns port name)};
    my @ports = split "\0", $port;

    my $graph_args
        = $yatg_conf->{yatg}->{graph_generator} ."?ytitle=$ytitle&$lf&$lftxt"
          .'&ip='   .uri_escape($ip)
          .'&port=' .(join '&port=', (map {uri_escape($_)} @ports))
          .'&end='  .uri_escape($end);

    $title  .= "$ip ($dns)";
    $output .= "Device: $ip ($dns)".br;

    my $speed =
        $ports[0] =~ m/TenGiga/ ? '10 Gbps'  :
        $ports[0] =~ m/Giga/    ? '1 Gbps'   :
        $ports[0] =~ m/Fast/    ? '100 Mbps' : 'n/a';

    $output .= "Interface: @ports".br;
    $output .= "Description: $name".br;
    $output .= "Speed: $speed".br;
    $output .= "Page Generated: ".localtime(time).br;
    $output .= hr;

    my $ip_port =
        scalar @ports == 1 ? " ($ip $ports[0])" : ''; 

    $output .= b("Day View").br;
    $output .= img({src => "$graph_args&period=day&title="
        .uri_escape("Traffic for the Last 24 Hours for \"$name\"$ip_port")}).br;
    $output .= b("$ip: @ports ($name)").br;
    $output .= hr;

    $output .= b("Week View").br;
    $output .= img({src => "$graph_args&period=week&title="
        .uri_escape("Traffic for the Last Week for \"$name\"$ip_port")}).br;
    $output .= b("$ip: @ports ($name)").br;
    $output .= hr;

    $output .= b("Month View").br;
    $output .= img({src => "$graph_args&period=month&title="
        .uri_escape("Traffic for the Last Month for \"$name\"$ip_port")}).br;
    $output .= b("$ip: @ports ($name)").br;
    $output .= hr;
}
else { # something fishy
    $output .= "Something went wrong. Please "
        .a({href => $yatg_conf->{yatg}->{homepage} || '/'},"start over").".".br;
}

print start_html(
    -title => $title,
    -background => $yatg_conf->{yatg}->{background_image},
);
print hr;
print $output;
print end_html;

# ABSTRACT: CGI to display graphs of YATG polled port traffic data
# PODNAME: yatgview.cgi

=head1 IMPORTANT NOTE

Do not place this script on a public or Internet-accessible web server!

It is a proof-of-concept, and contains no parameter checking whatsoever, so
your users can pass any old junk parameters in, and they will be assumed
valid. This could cause your web-server to be hacked.

The author and copyright holder take no responsibility whatsoever for any
damages incurred as a result of using this software.

=head1 DESCRIPTION

The two CGI scripts included in this distribution offer an example of how you
might visualize data polled by YATG. Specifically, they are designed for
showing traffic counter levels on switch ports, as polled by YATG.

The first CGI script creates a basic HTML web page, with some boilerplate text
determined by GET parameters. The second CGI retrieves data from your YATG
storage back-end and outputs a PNG image. It is called three times by the
first CGI script for day, week and month graphs.

=head1 LIMITATIONS

You need to poll the C<ifHCInOctets> and C<ifHCOutOctets> SNMP leaf names
with YATG, and mark them with the C<ifindex> magic word in your configuration.

Further, this code expects to use the Disk or RPC YATG retreival modules. The
main configuration file for your YATG system needs to be available on the web
server running this CGI. In the C<examples/> folder of this distribution, that
would be the C<yatg.yml> file. Of course, this configuration file must have
the right settings for your local YATG installation.

Graphs are generated by this CGI using the excellent ChartDirector graphing
component from ASE. Please support the product by buying a license - it's very
good value for money.

=head1 USAGE

The first thing is to have the two CGI scripts installed in a location where
your web server will execute them as Perl CGI.

The script names can be anything you like, but if you change them from the
defaults of C<yatview.cgi> and C<yatggraph.cgi> then be sure to update your
main configuration file.

Then, make sure your web server is setting the following two environment
variables when calling the CGI:

 YATG_CONFIG_FILE  # main YATG configuration, e.g. yatg.yml
 YATG_GRAPH_CONF   # graph hints file, copied from examples/yatg_graph.pl

For example, in your Apache2 config:

 SetEnv YATG_CONFIG_FILE /etc/yatg.yml
 SetEnv YATG_GRAPH_CONF  /etc/yatg_graph.pl

You will see the CGI die, and an error in your Apache error log if these
variables are not being set correctly.

The author has patched the NetDisco network management tool web interface to
generate links to this CGI with the correct GET parameters for each device
port, and you will have to do something similar locally. These are the
parameters accepted, and of course they should be URI-escaped:

=over 4

=item C<ip>

This must be the IP address of the device of which you want to retrieve
stored YATG results.

=item C<dns>

This should be a human-readable name for the device mentioned in C<ip>. It is
used verbatim in the boilerplate HTML text and graph title.

=item C<port>

This must be the port on the device, as used by YATG to store the results,
e.g. C<GigabitEthernet5/1> (but URI-escaped, of course).

As a special case, if the C<port> CGI parameter is given more than once, then
the data values for the two ports are summed together and a graph drawn of
that. This might be handy if you load share over multiple links but want a
single utilization graph.

=item C<name>

This should be a human-readable description for the device interface mentioned
in C<port>. For example the value obtained from the C<ifAlias> SNMP OID,
something like C<Trunk Link to Campus>.

=back

=head1 CONFIGURATION

There is one required configuration setting in the main YATG configuration,
and some optional parameters.

=over 4

=item C<graph_generator>

This setting is required. It must be the complete URI linking to the
C<yatggraph.cgi> CGI script as available through your web server. For example:

 yatg:
     graph_generator:
         'https://my.web-server.example.com/cgi-bin/yatggraph.cgi'

=item C<background_image>

Set this parameter if you want a background image on the main HTML page, for
example:

 yatg:
     background_image: '/images/grey-back.gif'

=item C<homepage>

If there is an error in the HTML page gneration (although be warned there
isn't much error checking!) then you will see a link back to your site
homepage if set in this parameter, e.g.:

 yatg:
     homepage: '/netdisco'

=item C<perlchartdir_key>

Graphs are generated by this CGI using the excellent ChartDirector graphing
component from ASE. If you have registered the product, then set this
parameter to your license key. Otherwise, things will work fine but you'll see
the small ASE banner on each graph.

=back

=head1 SEE ALSO

=over 4

=item L<http://www.advsofteng.com/cdperl.html>

Graphs are generated by this CGI using the excellent ChartDirector graphing
component from ASE. Please support the product by buying a license - it's very
good value for money.

=back

=head1 ACKNOWLEDGEMENTS

This CGI is based upon the RTG CGIs by Anthony Tonns.

=cut