periods become underscores in router.ddx interface names too
[freeside.git] / FS / FS / NetworkMonitoringSystem / Torrus_Internal.pm
1 package FS::NetworkMonitoringSystem::Torrus_Internal;
2
3 use strict;
4 #use vars qw( $DEBUG $me );
5 use Fcntl qw(:flock);
6 use IO::File;
7 use File::Slurp qw(slurp);
8 use Date::Format;
9 use XML::Simple;
10 use FS::Record qw(qsearch qsearchs dbh);
11 use FS::svc_port;
12 use FS::torrus_srvderive_component;
13 use Torrus::ConfigTree;
14
15 #$DEBUG = 0;
16 #$me = '[FS::NetworkMonitoringSystem::Torrus_Internal]';
17
18 our $lock;
19 our $lockfile = '/usr/local/etc/torrus/discovery/FSLOCK';
20 our $ddxfile  = '/usr/local/etc/torrus/discovery/routers.ddx';
21
22 sub new {
23     my $class = shift;
24     my $self = {};
25     bless $self, $class;
26     return $self;
27 }
28
29 sub ddx2hash {
30     my $self = shift;
31     my $ddx_xml = slurp($ddxfile);
32     my $xs = new XML::Simple(RootName=> undef, SuppressEmpty => '', 
33                                 ForceArray => 1, );
34     return $xs->XMLin($ddx_xml);
35 }
36
37 sub get_router_serviceids {
38     my $self = shift;
39     my $router = shift;
40     my $find_serviceid = shift;
41     my $found_serviceid = 0;
42     my $ddx_hash = $self->ddx2hash;
43     if($ddx_hash->{host}){
44         my @hosts = @{$ddx_hash->{host}};
45         foreach my $host ( @hosts ) {
46             my $param = $host->{param};
47             if($param && $param->{'snmp-host'} 
48                       && (!$router || $param->{'snmp-host'}->{'value'} eq $router)
49                       && $param->{'RFC2863_IF_MIB::external-serviceid'}) {
50                 my $serviceids = $param->{'RFC2863_IF_MIB::external-serviceid'}->{'content'};
51                 my %hash = ();
52                 if($serviceids) {
53                     my @serviceids = split(',',$serviceids);
54                     foreach my $serviceid ( @serviceids ) {
55                         $serviceid =~ s/^\s+|\s+$//g;
56                         my @s = split(':',$serviceid);
57                         next unless scalar(@s) == 4;
58                         $hash{$s[1]} = $s[0] if $router;
59                         if ($find_serviceid && $find_serviceid eq $s[0]) {
60                             $hash{$param->{'snmp-host'}->{'value'}} = $s[1];
61                             $found_serviceid = 1;
62                         }
63                     }
64                 }
65                 return \%hash if ($router || $found_serviceid);
66             }
67         }
68     }
69     '';
70 }
71
72 sub port_graphs_link {
73     # hardcoded for 'main' tree for now 
74     my $self = shift;
75     my $serviceid = shift;
76     my $hash = $self->get_router_serviceids(undef,$serviceid) or return '';
77     my @keys = keys %$hash; # yeah this is weird...
78     my $host = $keys[0];
79     my $iface = $hash->{$keys[0]};
80     my $config_tree = new Torrus::ConfigTree( -TreeName => 'main' );
81     my $token = $config_tree->token("/Routers/$host/Interface_Counters/$iface/InOut_bps");
82     return $Torrus::Freeside::FSURL."/torrus/main?token=$token";
83 }
84
85 sub find_svc {
86     my $self = shift;
87     my $serviceid = shift;
88     return '' unless $serviceid =~ /^[0-9A-Za-z_\-.\\\/ ]+$/;
89   
90     my @svc_port = qsearch('svc_port', { 'serviceid' => $serviceid });
91     return '' unless scalar(@svc_port);
92
93     # for now it's like this, later on just change to qsearchs
94
95     return $svc_port[0];
96 }
97
98 sub find_torrus_srvderive_component {
99     my $self = shift;
100     my $serviceid = shift;
101     return '' unless $serviceid =~ /^[0-9A-Za-z_\-.\\\/ ]+$/;
102   
103     qsearchs('torrus_srvderive_component', { 'serviceid' => $serviceid });
104 }
105
106 sub report {
107   my $self = shift;
108
109   my @ls = localtime(time);
110   my ($d,$m,$y) = ($ls[3], $ls[4]+1, $ls[5]+1900);
111   if ( $ls[3] == 1 ) {
112     $m--;
113     if ($m == 0) { $m=12; $y-- }
114     #i should have better error checking
115     system('torrus', 'report', '--report=MonthlyUsage', "--date=$y-$m-01");
116     system('torrus', 'report', '--genhtml', '--tree=main');
117   }
118
119 }
120
121 sub add_router {
122   my($self, $ip) = @_;
123
124   my $newhost = 
125     qq(  <host>\n).
126     qq(    <param name="snmp-host" value="$ip"/>\n).
127     qq(  </host>\n);
128
129   my $ddx = $self->_torrus_loadddx;
130
131   $ddx =~ s{(</snmp-discovery>)}{$newhost$1};
132
133   $self->_torrus_newddx($ddx);
134
135 }
136
137 sub add_interface {
138   my($self, $router_ip, $interface, $serviceid ) = @_;
139
140   $interface =~ s(\/)(_)g; #slashes become underscores
141   $interface =~ s(\.)(_)g; #periods too, huh
142
143   #should just use a proper XML parser huh
144
145   my @ddx = split(/\n/, $self->_torrus_loadddx);
146
147   die "Torrus Service ID $serviceid in use\n"
148     if grep /^\s*$serviceid:/, @ddx;
149
150   my $newline = "     $serviceid:$interface:Both:main,";
151
152   my $new = '';
153
154   my $added = 0;
155
156   while ( my $line = shift(@ddx) ) {
157     $new .= "$line\n";
158     next unless $line =~ /^\s*<param\s+name="snmp-host"\s+value="$router_ip"\/?>/i;
159
160     while ( my $hostline = shift(@ddx) ) {
161       $new .= "$hostline\n" unless $hostline =~ /^\s+<\/host>\s*/i;
162       if ( $hostline =~ /^\s*<param name="RFC2863_IF_MIB::external-serviceid"\/?>/i ) {
163
164         while ( my $paramline = shift(@ddx) ) {
165           if ( $paramline =~ /^\s*<\/param>/ ) {
166             $new .= "$newline\n$paramline\n";
167             last; #paramline
168           } else {
169             $new .= "$paramline\n";
170           }
171         }
172
173         $added++;
174
175       } elsif ( $hostline =~ /^\s+<\/host>\s*/i ) {
176         unless ( $added ) {
177           $new .= 
178             qq(   <param name="RFC2863_IF_MIB::external-serviceid">\n).
179             qq(     $newline\n").
180             qq(   </param>\n);
181         }
182         $new .= "$hostline\n";
183         last; #hostline
184       }
185  
186     }
187
188   }
189
190   $self->_torrus_newddx($new);
191
192 }
193
194 sub _torrus_lock {
195   $lock = new IO::File ">>$lockfile" or die $!;
196   flock($lock, LOCK_EX);
197 }
198
199 sub _torrus_unlock {
200   flock($lock, LOCK_UN);
201   close $lock;
202 }
203
204 sub _torrus_loadddx {
205   my($self) = @_;
206   $self->_torrus_lock;
207   return slurp($ddxfile);
208 }
209
210 sub _torrus_newddx {
211   my($self, $ddx) = @_;
212
213   my $new = new IO::File ">$ddxfile.new"
214     or die "can't write to $ddxfile.new: $!";
215   print $new $ddx;
216   close $new;
217
218   my $tmpname = $ddxfile . Date::Format::time2str('%Y%m%d%H%M%S',time);
219   rename("$ddxfile", $tmpname) or die $!;
220   rename("$ddxfile.new", $ddxfile) or die $!;
221
222   $self->_torrus_reload;
223 }
224
225 sub _torrus_reload {
226   my($self) = @_;
227
228   #i should use IPC::Run and have better error checking (commands are silent
229   # for success, or output errors)
230
231   system('torrus', 'devdiscover', "--in=$ddxfile");
232
233   system('torrus', 'compile', '--tree=main'); # , '--verbose'
234
235   $self->_torrus_unlock;
236
237 }
238
239 sub torrus_serviceids {
240   my $self = shift;
241
242   #is this going to get too slow or will the index make it okay?
243   my $sth = dbh->prepare("SELECT DISTINCT(serviceid) FROM srvexport")
244     or die dbh->errstr;
245   $sth->execute or die $sth->errstr;
246   my %serviceid = ();
247   while ( my $row = $sth->fetchrow_arrayref ) {
248     my $serviceid = $row->[0];
249     $serviceid =~ s/_(IN|OUT)$//;
250     $serviceid{$serviceid}=1;
251   }
252   my @serviceids = sort keys %serviceid;
253
254   @serviceids;
255
256 }
257
258 1;