torrus virtual ports, RT#10574
[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;
141
142   #should just use a proper XML parser huh
143
144   my @ddx = split(/\n/, $self->_torrus_loadddx);
145
146   die "Torrus Service ID $serviceid in use\n"
147     if grep /^\s*$serviceid:/, @ddx;
148
149   my $newline = "     $serviceid:$interface:Both:main,";
150
151   my $new = '';
152
153   my $added = 0;
154
155   while ( my $line = shift(@ddx) ) {
156     $new .= "$line\n";
157     next unless $line =~ /^\s*<param\s+name="snmp-host"\s+value="$router_ip"\/?>/i;
158
159     while ( my $hostline = shift(@ddx) ) {
160       $new .= "$hostline\n" unless $hostline =~ /^\s+<\/host>\s*/i;
161       if ( $hostline =~ /^\s*<param name="RFC2863_IF_MIB::external-serviceid"\/?>/i ) {
162
163         while ( my $paramline = shift(@ddx) ) {
164           if ( $paramline =~ /^\s*<\/param>/ ) {
165             $new .= "$newline\n$paramline\n";
166             last; #paramline
167           } else {
168             $new .= "$paramline\n";
169           }
170         }
171
172         $added++;
173
174       } elsif ( $hostline =~ /^\s+<\/host>\s*/i ) {
175         unless ( $added ) {
176           $new .= 
177             qq(   <param name="RFC2863_IF_MIB::external-serviceid">\n).
178             qq(     $newline\n").
179             qq(   </param>\n);
180         }
181         $new .= "$hostline\n";
182         last; #hostline
183       }
184  
185     }
186
187   }
188
189   $self->_torrus_newddx($new);
190
191 }
192
193 sub _torrus_lock {
194   $lock = new IO::File ">>$lockfile" or die $!;
195   flock($lock, LOCK_EX);
196 }
197
198 sub _torrus_unlock {
199   flock($lock, LOCK_UN);
200   close $lock;
201 }
202
203 sub _torrus_loadddx {
204   my($self) = @_;
205   $self->_torrus_lock;
206   return slurp($ddxfile);
207 }
208
209 sub _torrus_newddx {
210   my($self, $ddx) = @_;
211
212   my $new = new IO::File ">$ddxfile.new"
213     or die "can't write to $ddxfile.new: $!";
214   print $new $ddx;
215   close $new;
216
217   my $tmpname = $ddxfile . Date::Format::time2str('%Y%m%d%H%M%S',time);
218   rename("$ddxfile", $tmpname) or die $!;
219   rename("$ddxfile.new", $ddxfile) or die $!;
220
221   $self->_torrus_reload;
222 }
223
224 sub _torrus_reload {
225   my($self) = @_;
226
227   #i should use IPC::Run and have better error checking (commands are silent
228   # for success, or output errors)
229
230   system('torrus', 'devdiscover', "--in=$ddxfile");
231
232   system('torrus', 'compile', '--tree=main'); # , '--verbose'
233
234   $self->_torrus_unlock;
235
236 }
237
238 sub torrus_serviceids {
239   my $self = shift;
240
241   #is this going to get too slow or will the index make it okay?
242   my $sth = dbh->prepare("SELECT DISTINCT(serviceid) FROM srvexport")
243     or die dbh->errstr;
244   $sth->execute or die $sth->errstr;
245   my %serviceid = ();
246   while ( my $row = $sth->fetchrow_arrayref ) {
247     my $serviceid = $row->[0];
248     $serviceid =~ s/_(IN|OUT)$//;
249     $serviceid{$serviceid}=1;
250   }
251   my @serviceids = sort keys %serviceid;
252
253   @serviceids;
254
255 }
256
257 1;