diff options
| author | levinse <levinse> | 2011-01-29 02:50:27 +0000 | 
|---|---|---|
| committer | levinse <levinse> | 2011-01-29 02:50:27 +0000 | 
| commit | 6877b0f1447c211f1a992487eeaf9bda787c4b71 (patch) | |
| tree | ccedb0a2bc7589064abeb86285842c03ac8b5836 | |
| parent | 7a30043712c15f9db1604ea1c3acd7db203d14c2 (diff) | |
torrus, RT10574
| -rw-r--r-- | FS/FS/NetworkMonitoringSystem/Torrus_Internal.pm | 2 | ||||
| -rw-r--r-- | FS/FS/svc_port.pm | 191 | ||||
| -rw-r--r-- | httemplate/misc/nms-add_iface.html | 2 | ||||
| -rw-r--r-- | httemplate/view/port_graph.html | 40 | ||||
| -rw-r--r-- | httemplate/view/svc_port.cgi | 91 | 
5 files changed, 321 insertions, 5 deletions
| diff --git a/FS/FS/NetworkMonitoringSystem/Torrus_Internal.pm b/FS/FS/NetworkMonitoringSystem/Torrus_Internal.pm index 68356e72d..a56a72a40 100644 --- a/FS/FS/NetworkMonitoringSystem/Torrus_Internal.pm +++ b/FS/FS/NetworkMonitoringSystem/Torrus_Internal.pm @@ -27,6 +27,8 @@ sub add_router {    $self->_torrus_newddx($ddx); +} +  sub add_interface {    my($self, $router_ip, $interface, $serviceid ) = @_; diff --git a/FS/FS/svc_port.pm b/FS/FS/svc_port.pm index 931880e55..671ec4e8e 100644 --- a/FS/FS/svc_port.pm +++ b/FS/FS/svc_port.pm @@ -1,9 +1,22 @@  package FS::svc_port;  use strict; +use vars qw($conf $system $DEBUG $me );  use base qw( FS::svc_Common ); -#use FS::Record qw( qsearch qsearchs ); +use FS::Record qw( qsearch qsearchs dbh );  use FS::cust_svc; +use GD::Graph; +use GD::Graph::mixed; +use Date::Format qw(time2str); +use Data::Dumper; + +$DEBUG = 0; +$me = '[FS::svc_port]'; + +FS::UID->install_callback( sub {  +  $conf = new FS::Conf; +  $system = $conf->config('network_monitoring_system'); +} );  =head1 NAME @@ -182,19 +195,189 @@ sub check {    $self->SUPER::check;  } -=item +=item graph_png  Returns a PNG graph for this port. -XXX Options +The following options must be specified: + +=over 4 + +=item start +=item end + +=back  =cut +sub _format_bandwidth { +    my $self = shift; +    my $value = shift; +    my $space = shift; +    $space = ' ' if $space; + +    my $suffix = ''; + +    warn "$me _format_bandwidth $value" if $DEBUG; + +    if ( $value >= 1000 && $value < 1000000 ) { +	$value = ($value/1000); +	$suffix = $space. "k"; +    } +    elsif( $value >= 1000000 && $value < 1000000000 ) { +	$value = ($value/1000/1000); +	$suffix = $space . "M"; +    } +    elsif( $value >= 1000000000 && $value < 1000000000000 ) { +	$value = ($value/1000/1000/1000); +	$suffix = $space . "G"; +    } +    # and hopefully we don't have folks doing Tbps on a single port :) + +    $value = sprintf("%.2f$suffix",$value) if $value >= 0; + +    $value; +} +  sub graph_png { -  my $self = shift; +  my($self, %opt) = @_;    my $serviceid = $self->serviceid; +  if($serviceid && $system eq 'Torrus_Internal') { +      my $start = -1; +      my $end = -1; +	my $now = time; + +	$start = $opt{start} if $opt{start}; +	$end = $opt{end} if $opt{end}; + +	return 'Invalid date range' if ($start < 0 || $start >= $end  +	    || $end <= $start || $end < 0 || $end > $now || $start > $now +	    || $end-$start > 86400*366 ); + +	my $serviceid_sql = "('${serviceid}_IN','${serviceid}_OUT')"; +	my @records; +	my $dbh = dbh; +	if ( $dbh->{Driver}->{Name} eq 'Pg' ) { +	    @records = qsearch({  +		'table' => 'srvexport', +		'select' => "*, date_part('epoch',to_timestamp(srv_date||' '||srv_time,'YYYY-MM-DD HH:MI:SS')) as _date", +		'extra_sql' => "where serviceid in $serviceid_sql and  +		    date_part('epoch',to_timestamp(srv_date||' '||srv_time,'YYYY-MM-DD HH:MI:SS')) >= $start +		    and date_part('epoch',to_timestamp(srv_date||' '||srv_time,'YYYY-MM-DD HH:MI:SS')) <= $end", +		'order_by' => "order by date_part('epoch',to_timestamp(srv_date||' '||srv_time,'YYYY-MM-DD HH:MI:SS')) asc", +	     }); +	 } elsif ( $dbh->{Driver}->{Name} eq 'mysql' ) { +		@records = qsearch({  +		    'table' => 'srvexport', +		    'select' => "*, unix_timestamp(srv_date||' '||srv_time) as _date", +		    'extra_sql' => "where serviceid in $serviceid_sql and  +			unix_timestamp(srv_date||' '||srv_time) >= $start +			and unix_timestamp(srv_date||' '||srv_time) <= $end", +		    'order_by' => "order by unix_timestamp(srv_date||' '||srv_time) asc", +		 }); +	} else { +	      return 'Unsupported DBMS'; +	} + + +	# assume data in DB is correct, +	# assume always _IN and _OUT pair, assume intvl = 300 + +	my @times; +	my @in; +	my @out; +	foreach my $rec ( @records ) { +	    push @times, $rec->_date  +		unless grep { $_ eq $rec->_date } @times; +	    push @in, $rec->value if $rec->serviceid =~ /_IN$/; +	    push @out, $rec->value if $rec->serviceid =~ /_OUT$/; +	} + +	my $timediff = $times[-1] - $times[0]; # they're sorted ascending + +	my $y_min = 999999999999; # ~1Tbps +	my $y_max = 0; +	my $in_sum = 0; +	my $out_sum = 0; +	my $in_min = 999999999999; +	my $in_max = 0; +	my $out_min = 999999999999; +	my $out_max = 0; +	foreach my $in ( @in ) { +	    $y_max = $in if $in > $y_max; +	    $y_min = $in if $in < $y_min; +	    $in_sum += $in; +	    $in_max = $in if $in > $in_max; +	    $in_min = $in if $in < $in_min; +	} +	foreach my $out ( @out ) { +	    $y_max = $out if $out > $y_max; +	    $y_min = $out if $out < $y_min; +	    $out_sum += $out; +	    $out_max = $out if $out > $out_max; +	    $out_min = $out if $out < $out_min; +	} +	my $bwdiff = $y_max - $y_min; +	$in_min = $self->_format_bandwidth($in_min); +	$out_min = $self->_format_bandwidth($out_min); +	$in_max = $self->_format_bandwidth($in_max); +	$out_max = $self->_format_bandwidth($out_max); +	my $in_curr = $self->_format_bandwidth($in[-1]); +	my $out_curr = $self->_format_bandwidth($out[-1]); +	my $numsamples = scalar(@records)/2; +	my $in_avg = $self->_format_bandwidth($in_sum/$numsamples); +	my $out_avg = $self->_format_bandwidth($out_sum/$numsamples); + +      warn "$me timediff=$timediff bwdiff=$bwdiff start=$start end=$end " +	    . "in_min=$in_min out_min=$out_min in_max=$in_max " +	    . "out_max=$out_max in_avg=$in_avg out_avg=$out_avg " +	    . " # records = " . scalar(@records) . "\n\ntimes:\n"  +	    . Dumper(@times) . "\n\nin:\n" . Dumper(@in) . "\n\nout:\n" +	    . Dumper(@out) if $DEBUG; + +      my @data = ( \@times, \@in, \@out ); + +      # hardcoded size, colour, etc. +      my $graph = new GD::Graph::mixed(600,400);  +      $graph->set( +        types => ['area','lines'], +	dclrs => ['green','blue'], +	x_label => "(In Out)  Current: $in_curr $out_curr  Average: $in_avg $out_avg  Maximum: $in_max $out_max  Minimum: $in_min $out_min", +	x_tick_number => 'auto', +	x_number_format => sub { +	    my $value = shift; +	    if ( $timediff < 86401 ) { # one day +		$value = time2str("%a %H:%M",$value)  +	    } elsif ( $timediff < 86401*7 ) { # one week +		$value = time2str("%d",$value)  +	    } elsif ( $timediff < 86401*30 ) { # one month +		$value = time2str("Week %U",$value)  +	    } elsif ( $timediff < 86401*366 ) { # one year +		$value = time2str("%b",$value) +	    } +	    $value; +	}, +	y_number_format => sub { +	    my $value = shift; +	    $self->_format_bandwidth($value,1); +	}, +	y_label => 'bps', +	legend_placement => 'BR', +	title => $self->serviceid, +      ) or return "can't create graph: ".$graph->error; +       +      $graph->set_text_clr('black')  +	or return "can't set text colour: ".$graph->error; +      $graph->set_legend(('In','Out'))  +	or return "can't set legend: ".$graph->error; + +      my $gd = $graph->plot(\@data); +      return "graph error: ".$graph->error unless($gd); +      return $gd->png; +  } +  '';  }  =back diff --git a/httemplate/misc/nms-add_iface.html b/httemplate/misc/nms-add_iface.html index ab4cb178b..ef4b4eb46 100644 --- a/httemplate/misc/nms-add_iface.html +++ b/httemplate/misc/nms-add_iface.html @@ -19,6 +19,6 @@ my $host = $cgi->param('host');  die 'invalid host' unless $host =~ /^[0-9.a-zA-Z\-]+$/;  my $iface = $cgi->param('iface'); -die 'invalid iface' unless $iface =~ /^[0-9A-Za-z_]+$/; +die 'invalid iface' unless $iface =~ /^[0-9A-Za-z_\-.]+$/;  </%init> diff --git a/httemplate/view/port_graph.html b/httemplate/view/port_graph.html new file mode 100644 index 000000000..fc5d0db3f --- /dev/null +++ b/httemplate/view/port_graph.html @@ -0,0 +1,40 @@ +% if ($error) { warn $error; } +% else { +<% $png %> +% } +<%init> + +# NOTE: the weird warn/die stuff here is because this file is accessed as  +# IMG SRC="port_graph.html" - easier than hacking an uglier solution + +unless ( $FS::CurrentUser::CurrentUser->access_right('View customer services') ) { +    warn "access denied"; +    die; +} + +my $svcnum = $cgi->param('svcnum'); +unless ( $svcnum =~ /^\d+$/ ) { +    warn 'invalid svcnum'; +    die; +} + +my $start = $cgi->param('start'); +my $end = $cgi->param('end'); +unless ( $start =~ /^\d+$/ && $end =~ /^\d+$/ ) { +    warn 'invalid start and/or end times'; +    die; +} + +my $svc_port = qsearchs('svc_port', { 'svcnum' => $svcnum }); +unless($svc_port) { +    warn 'invalid svc_port'; +    die; +} + +my $error = ''; +my $png = $svc_port->graph_png('start' => $start, 'end' => $end); +$error = 'error from graph_png: '.$png if length($png) < 200; + +http_header('Content-Type' => 'image/png') unless($error); + +</%init> diff --git a/httemplate/view/svc_port.cgi b/httemplate/view/svc_port.cgi new file mode 100644 index 000000000..84d0e49e3 --- /dev/null +++ b/httemplate/view/svc_port.cgi @@ -0,0 +1,91 @@ +<% include('elements/svc_Common.html', +            'table'     => 'svc_port', +	    'fields'	=> \@fields, +	    'labels'	=> \%labels, +	    'html_foot' => $html_foot, +          ) +%> +<%init> + +use Date::Parse 'str2time'; + +my $conf = new FS::Conf; +my $date_format = $conf->config('date_format') || '%m/%d/%Y'; + +my $fields = FS::svc_port->table_info->{'fields'}; +my %labels = map { $_ =>  ( ref($fields->{$_}) +                             ? $fields->{$_}{'label'} +                             : $fields->{$_} +                         ); +                 } keys %$fields; +my @fields = keys %$fields; + +my $svcnum; +if ( $cgi->param('svcnum') ) { +  $cgi->param('svcnum') =~ /^(\d+)$/ or die "unparsable svcnum"; +  $svcnum = $1; +} else { +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/ or die "no svcnum"; +  $svcnum = $1; +} + +my $start = ''; +my $end = ''; +if ( $cgi->param('start') && $cgi->param('end') ) { +    $start = $cgi->param('start'); +    $end = $cgi->param('end'); +} + +sub preset_range { +    my($start,$end,$label,$date_format) = (shift,shift,shift,shift); +    warn "$start $end $date_format"; +    $start = time2str($date_format,$start); +    $end = time2str($date_format,$end); +    return '<A HREF="javascript:void(0);" onclick="preset_range(\'' +	    .$start.'\',\''.$end.'\')">'.$label.'</A>'; +} + +my $html_foot = sub { +    my $default_end = time; +    my $default_start = $default_end-86400; +    my $graph = ''; + +    if($start && $end) { +	$graph = "<BR><BR><IMG SRC=${p}/view/port_graph.html?svcnum=$svcnum;". +		"start=".str2time($start).";end=".str2time($end).">"; +    } + +    return ' +    <script type="text/javascript"> +	function preset_range(start,end){ +	    document.getElementById(\'start_text\').value = start; +	    document.getElementById(\'end_text\').value = end; +	} +    </script> +    <FORM ACTION=? METHOD="GET"> +    <INPUT TYPE="HIDDEN" NAME="svcnum" VALUE="'.$svcnum.'"> +    <B>Bandwidth Graph</B><BR> +  '.preset_range($default_start,$default_end,'Last Day',$date_format) +    .' | '.preset_range($default_end-86400*7,$default_end,'Last Week',$date_format) +    .' | '.preset_range($default_end-86400*30,$default_end,'Last Month',$date_format) +    .' | '.preset_range($default_end-86400*365,$default_end,'Last Year',$date_format) +    .' <BR> +    <TABLE>' +	. include('/elements/tr-input-date-field.html', {  +		'name' => 'start', +		'label' => 'Start Date', +		'value' => $start, +	    })  +	. include('/elements/tr-input-date-field.html', {  +		'name' => 'end', +		'label' => 'End Date', +		'noinit' => 1, +		'value' => $end, +	    })  +	. '<TR><TD colspan="2"><input type="submit" value="Display"></TR> +    </TABLE> +    </FORM>'.$graph; +}; + +</%init> | 
