Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / httemplate / view / Status.html
1 <& /elements/header.html, 'System Status' &>
2
3 <& /elements/init_overlib.html &>
4
5 % foreach my $section ( keys %status ) {
6 <FONT CLASS="fsinnerbox-title"><% mt($section) |h %></FONT>
7 <TABLE CLASS="fsinnerbox">
8 %   foreach my $item ( @{ $status{$section} } ) {
9       <TR>
10         <TD ALIGN="right"><% $item->{title} %></TH>
11         <TD><B><% $item->{value} %></B></TD>
12       </TR>
13 %   }
14 </TABLE>
15 <BR><BR>
16 % }
17
18 <& /elements/footer.html &>
19 <%init>
20
21 ###
22 # Basics and Daemons
23 ###
24
25 my $os;
26 -e '/usr/bin/lsb_release' and run( ['lsb_release', '-d'], '>',\$os );
27 if ( ! $@ && $os =~ /^\s*Description:\s*(.+)$/ ) {
28   $os = $1;
29 } elsif ( my $deb_version = slurp('/etc/debian_version') ) {
30   $os = "Debian $deb_version";
31 }
32
33 ( my $perl_ver = $^V ) =~ s/^v//;
34
35 my $db = driver_name;
36 $db = 'PostgreSQL'    if $db =~ /^Pg/;
37 $db = 'MySQL/MariaDB' if $db =~ /^mysql/;
38
39 my $db_ver = FS::Record->scalar_sql('SELECT VERSION()');
40 if ( $db eq 'PostgreSQL' && $db_ver =~ /^\s*PostgreSQL\s+([\w\.]+)\s+on\s+/ ) {
41   $db_ver = $1;
42 }
43
44 my $db_size = 'Unknown';
45 if ( $db eq 'PostgreSQL' ) {
46   $db_size = FS::Record->scalar_sql(qq(
47                SELECT pg_size_pretty(pg_database_size('freeside'))
48              )). ' '.
49              include('/elements/popup_link.html',
50                        'action'      => 'Status-db_size_detail.html',
51                        'label'       => '(details)',
52                        'actionlabel' => 'Database size details',
53              );
54 }
55
56 tie my %status, 'Tie::IxHash',
57   'Basics' => [
58     { 'title' => 'Freeside version',
59       'value' => $FS::VERSION,
60     },
61     { 'title' => 'Operating System',
62       'value' => $os,
63     },
64     { 'title' => 'Perl version',
65       'value' => $perl_ver,
66     },
67     { 'title' => 'Database engine',
68       'value' => $db,
69     },
70     { 'title' => 'Database version',
71       'value' => $db_ver,
72     },
73     { 'title' => 'Database size',
74       'value' => $db_size,
75     },
76   ],
77   'Required Daemons' => [
78     { 'title' => 'Queue daemon',
79       'value' => _is_running('queued') ? 'Running' : 'Not running',
80     },
81   ],
82   'Optional Daemons' => [
83     { 'title' => 'Self-service server(s)',
84       'value' => '(Not checked)', #XXX multiple pid files, per machine etc
85     },
86     { 'title' => 'Self-service XML-RPC server',
87       'value' => _is_running('selfservice-xmlrpcd') ? 'Running' : 'Not running',
88     },
89     { 'title' => 'Back office XML-RPC server',
90       'value' => _is_running('xmlrpcd') ? 'Running' : 'Not running',
91     },
92     { 'title' => 'RADIUS accounting import daemon',
93       'value' => _is_running('sqlradius-radacctd') ? 'Running' : 'Not running',
94     },
95     { 'title' => 'Prepaid daemon',
96       'value' => _is_running('prepaidd') ? 'Running' : 'Not running',
97     },
98     { 'title' => 'CDR Rewrite daemon',
99       'value' => _is_running('cdrrewrited') ? 'Running' : 'Not running',
100     },
101     { 'title' => 'CDR Prepaid daemon',
102       'value' => _is_running('cdrd') ? 'Running' : 'Not running',
103     },
104     { 'title' => 'CDR Real-time rating daemon',
105       'value' => _is_running('cdrrated') ? 'Running' : 'Not running',
106     },
107     #{ 'title' => 'Network monitoring port combiner', #?
108     #  'value' => _is_running('torrus-srvderive') ? 'Running' : 'Not running',
109     #},
110   ],
111 ;
112
113
114 ###
115 # Replication
116 ###
117
118 if ( $db eq 'PostgreSQL' ) {
119
120   my $enabled =    FS::Record->scalar_sql('SHOW wal_level')    eq 'hot_standby'
121                 && FS::Record->scalar_sql('SHOW archive_mode') eq 'on';
122
123   my $slave = 
124     FS::Record->scalar_sql('SHOW archive_command') =~ / postgres\@([\w\.\-]+): /
125       ? $1 : '';
126
127   $status{'Replication'} = [
128     { 'title' => 'Status', #?
129       'value' => $enabled ? 'Enabled' : 'Disabled',
130     },
131   ];
132
133   if ( $enabled ) {
134     push @{ $status{'Replication'} }, 
135       { 'title' => 'Slave',
136         'value' => $slave || '(Missing, or unparseable archive_command)',
137       },
138     ;
139     if ( $slave ) {
140       #how far behind is it?  will be easier once we're off 9.1
141       # http://www.keithf4.com/monitoring_streaming_slave_lag/
142       # except pg_stat_replication still doesn't fill in the columns we need as
143       # non-Pg user :/
144       push @{ $status{'Replication'} }, 
145         { 'title' => 'Slave',
146           'value' => $slave || '(Missing, or unparseable archive_command)',
147         },
148       ;
149     }
150   }
151
152 } else {
153
154   $status{'Replication'} = [
155     { 'title' => 'Enabled',
156       'value' => "(Not yet checked on $db)",
157     },
158   ];
159
160 }
161
162
163 ###
164 # CDR Processing
165 ###
166
167 if ( _is_running('cdrd') ) {
168
169   my $delay = FS::Record->scalar_sql('
170     SELECT AVG(end_date-insert_date)
171       FROM queue_stat
172       GROUP BY statnum
173       ORDER BY statnum DESC
174       LIMIT 100
175   ');
176   if ( $delay ) {
177     my $h = int($delay/3600);
178     my $m = int( ($delay%3600) / 60 );
179     my $s = $delay%60;
180
181     $delay = ( $h     ? $h. 'h' : '' ).
182              ( $h||$m ? $m. 'm' : '').
183                         $s. 's';
184
185   }
186
187   my $pr_delay = FS::Record->scalar_sql('
188     SELECT AVG(end_date-start_date)
189       FROM queue_stat
190       GROUP BY statnum
191       ORDER BY statnum DESC
192       LIMIT 100
193   ');
194   if ( $pr_delay ) {
195     my $h = int($delay/3600);
196     my $m = int( ($delay%3600) / 60 );
197     my $s = $delay%60;
198
199     $pr_delay = ( $h     ? $h. 'h' : '' ).
200                 ( $h||$m ? $m. 'm' : '').
201                            $s. 's';
202
203   }
204
205   my $dayago = time2str('%Y-%m-%d %X', time - 86400);
206   my $cdrs = FS::Record->scalar_sql(qq{
207     SELECT COUNT(*) FROM cdr
208       WHERE ( freesidestatus IS NULL OR freesidestatus = '' )
209         AND calldate > '$dayago'
210   });
211
212   $status{'CDR Processing'} = [
213     { 'title' => 'Current processing delay',
214       'value' => $delay,
215     },
216     { 'title' => 'Average billing time',
217       'value' => $pr_delay,
218     },
219     { 'title' => 'Unprocessed CDRs (last 24 hours)',
220       'value' => $cdrs,
221     },
222   ];
223
224 }
225
226
227 ###
228 # PCI Compliance
229 ###
230
231 my($store, $tokenize) = (0,0);
232 foreach my $agent (
233   qsearch({
234     'table'     => 'agent',
235     'hashref'   => { 'disabled' => '', },
236     'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
237   })
238 ) {
239   my $gateway = $agent->payment_gateway('method'=>'VISA card', 'nofatal'=>1, );
240   next unless $gateway
241            && $gateway->gateway_namespace eq 'Business::OnlinePayment';
242   eval "use Business::OnlinePayment";
243   die $@ if $@; #die?
244   my $bop = new Business::OnlinePayment( $gateway->gateway_module,
245                                          $gateway->gatewaynum
246                                            ? $gateway->options
247                                            : @{ $gateway->get('options') }
248                                        );
249   my %actions = $bop->info('supported_actions');
250   if ( $actions{'CC'} && grep /^Tokenize$/, @{$actions{'CC'}} ) {
251     $tokenize++;
252   } else {
253     $store++;
254   }
255   
256 }
257
258 if ( $tokenize && ! $store ) {
259
260   $status{'PCI Compliance'} = [
261     { 'title' => 'Tokenization',
262       'value' => 'Enabled',
263     },
264     { 'title' => 'SAQ type',
265       'value' => 'A / A-EP',
266     },
267   ];
268
269 } elsif ( $store ) {
270
271   my $conf = new FS::Conf; #wow, didn't need this before?
272
273   $status{'PCI Compliance'} = [
274     { 'title' => 'Tokenization',
275       'value' => $tokenize ? 'Partialy enabled (some agents)' : 'Disabled'
276     },
277     { 'title' => 'Encryption',
278       'value' =>
279         ( $conf->exists('encryption') && $conf->config('encryptionpublickey')
280             ? 'Enabled' : 'Disabled'
281         ),
282     },
283     { 'title' => 'SAQ type',
284       'value' => 'D (enable tokenization for A / A-EP)',
285     },
286   ];
287
288 }
289
290 ###
291 # Subroutines
292 ###
293
294 sub _is_running {
295   my $thing = shift;
296
297   my $pid_path = '/var/run'; #XXX hardcoded path
298
299   my $pidfile =
300     -e "$pid_path/freeside/$thing.pid" ? "$pid_path/freeside/$thing.pid" :
301     -e "$pid_path/freeside-$thing.pid" ? "$pid_path/freeside-$thing.pid" :
302    return 0;
303
304   -e $pidfile and kill 0, slurp($pidfile)
305 }
306
307 </%init>