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