]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/support-scripts/settings-tester.pl
3a2c4aca6ef7a29af5fff44cc410f425779dd2fa
[working/Evergreen.git] / Open-ILS / src / support-scripts / settings-tester.pl
1 #!/usr/bin/perl
2 # vim:noet:ts=4:
3 use strict;
4 use warnings;
5
6 BEGIN {
7         eval "use OpenSRF::Utils::Config;";
8         die "Please ensure that /openils/lib/perl5 is in your PERL5LIB environment variable.
9         You must run this script as the 'opensrf' user.\n" if ($@);
10         eval "use Error qw/:try/;";
11         die "Please install Error.pm.\n" if ($@);
12         eval "use UNIVERSAL::require;";
13         die "Please install the UNIVERSAL::require perl module.\n" if ($@);
14         eval "use Getopt::Long;";
15         die "Please install the Getopt::Long perl module.\n" if ($@);
16         eval "use Net::Domain;";
17         die "Please install the Net::Domain perl module.\n" if ($@);
18 }
19
20 my $output = '';
21 my $perloutput = '';
22 my $result;
23
24 my ($gather, $hostname, $core_config, $tmpdir) =
25         (0, Net::Domain::hostfqdn(), '/openils/conf/opensrf_core.xml', '/tmp/');
26
27 GetOptions(
28         'gather' => \$gather,
29         'hostname=s' => \$hostname,
30         'config_file=s' => \$core_config,
31         'tempdir=s' => \$tmpdir,
32 );
33
34 while (my $mod = <DATA>) {
35         chomp $mod;
36         my @list = split / /, $mod;
37
38         my $ok = 0;
39         for my $m (@list) {
40                 if ($m->use) {
41                         $ok++;
42                         my $version = $m->VERSION;
43                         print "$m version $version\n" if ($version);
44                 }
45         }
46
47         unless ($ok) {
48                 if (@list == 1) {
49                         warn "Please install $mod\n";
50                         $perloutput .= "Please install the $mod Perl module.\n";
51                 } else {
52                         warn "Please install one of the following modules: $mod\n";
53                         $perloutput .= "Please install one of the following modules: $mod\n";
54                 }
55         }
56                         
57 }
58
59 use OpenSRF::Transport::SlimJabber::Client;
60 use OpenSRF::Utils::SettingsParser;
61 use OpenSRF::Utils::SettingsClient;
62 use Data::Dumper;
63 use DBI;
64
65 (my $conf_dir = $core_config) =~ s#(.*)/.*#$1#;
66 OpenSRF::Utils::Config->load(config_file => $core_config);
67 my $conf = OpenSRF::Utils::Config->current;
68 my $settings_config = $conf->bootstrap->settings_config;
69 my $logfile    = $conf->bootstrap->logfile;
70 (my $log_dir = $logfile) =~ s#(.*)/.*#$1#;
71
72 my $xmlparser = XML::LibXML->new();
73 my $confxml = $xmlparser->parse_file($core_config);
74 my $confxpc = XML::LibXML::XPathContext->new($confxml);
75
76 foreach my $section (qw/opensrf gateway/) {
77         my $j_username = $confxpc->find("/config/$section/username");
78         my $j_password = $confxpc->find("/config/$section/passwd");
79         my $j_port = $confxpc->find("/config/$section/port");
80         # We should check for a domains element to catch likely upgrade errors
81         my $j_domain = $confxpc->find("/config/$section/domain");
82         check_jabber($j_username, $j_password, $j_domain, $j_port);
83 }
84
85 my @routers = $confxpc->findnodes("/config/routers/router");
86 foreach my $router (@routers) {
87         my $j_username = $router->findvalue("./transport/username");
88         my $j_password = $router->findvalue("./transport/password");
89         my $j_port = $router->findvalue("./transport/port");
90         # We should check for a domains element to catch likely upgrade errors
91         my $j_domain = $router->findvalue("./transport/server");
92         check_jabber($j_username, $j_password, $j_domain, $j_port);
93 }
94
95 my $osrfxml = $xmlparser->parse_file($settings_config);
96 print "\nChecking database connections\n";
97 # Check database connections
98 my @databases = $osrfxml->findnodes('//database');
99
100 # If we have no database connections, this is probably the OpenSRF version
101 # of opensrf.xml
102 if (!@databases) {
103         my $de = "* WARNING: There are no database connections defined in " .
104                 "opensrf.xml. These are defined in services such as " .
105                 "open-ils.cstore and open-ils.reporter. Please ensure that " .
106                 "your opensrf_core.xml and opensrf.xml configuration files " .
107                 "are based on the examples shipped with Evergreen instead of " .
108                 "OpenSRF.\n";
109         $output .= $de;
110         warn $de;
111 }
112
113 foreach my $database (@databases) {
114         my $db_name = $database->findvalue("./db");     
115         if (!$db_name) {
116                 $db_name = $database->findvalue("./name");      
117         }
118         my $db_host = $database->findvalue("./host");   
119         my $db_port = $database->findvalue("./port");   
120         my $db_user = $database->findvalue("./user");   
121         my $db_pw = $database->findvalue("./pw");       
122         if (!$db_pw && $database->parentNode->parentNode->nodeName eq 'reporter') {
123                 $db_pw = $database->findvalue("./password");
124                 if ($db_pw) {
125                         my $de = "* WARNING: Deprecated <password> element used for the " .
126                                 "<reporter> entry. Please use <pw> instead.\n";
127                         $output .= $de;
128                         warn $de;
129                 }
130         }
131
132         my $osrf_xpath;
133         foreach my $node ($database->findnodes("ancestor::node()")) {
134                 next unless $node->nodeType == XML::LibXML::XML_ELEMENT_NODE;
135                 $osrf_xpath .= "/" . $node->nodeName;
136         }
137         $output .= test_db_connect($db_name, $db_host, $db_port, $db_user, $db_pw, $osrf_xpath);
138 }
139
140 print "\nChecking database drivers to ensure <driver> matches <language>\n";
141 # Check database drivers
142 # if language eq 'C', driver eq 'pgsql'
143 # if language eq 'perl', driver eq 'Pg'
144 my @drivers = $osrfxml->findnodes('//driver');
145 foreach my $driver_node (@drivers) {
146         my $language;
147         my $driver_xpath;
148         my @driver_xpath_nodes;
149         foreach my $node ($driver_node->findnodes("ancestor::node()")) {
150                 next unless $node->nodeType == XML::LibXML::XML_ELEMENT_NODE;
151                 $driver_xpath .= "/" . $node->nodeName;
152                 push @driver_xpath_nodes, $node->nodeName;
153         }
154         my $lang_xpath;
155         my $driver = $driver_node->findvalue("child::text()");
156         while (pop(@driver_xpath_nodes) && scalar(@driver_xpath_nodes) > 0 && !$language) {
157                 $lang_xpath = "/" . join('/', @driver_xpath_nodes) . "/language";
158                 my @lang_nodes = $osrfxml->findnodes($lang_xpath);
159                 next unless scalar(@lang_nodes > 0);
160                 $language = $lang_nodes[0]->findvalue("child::text()");
161         }
162         if ($driver eq "pgsql") {
163                 if ($driver_xpath =~ m#/reporter/#) {
164                         $result = "* ERROR: reporter application must use driver 'Pg', but '$driver' is defined\n";
165                         warn $result;
166                 } elsif ($language eq "C") {
167                         $result = "* OK: $driver language is $language in $lang_xpath\n";
168                 } else {
169                         $result = "* ERROR: $driver language is $language in $lang_xpath\n";
170                         warn $result;
171                 }
172         } elsif ($driver eq "Pg") {
173                 if ($driver_xpath =~ m#/reporter/#) {
174                         $result = "* OK: $driver language is undefined for reporter base configuration\n";
175                 } elsif ($language eq "perl") {
176                         $result = "* OK: $driver language is $language in $lang_xpath\n";
177                 } else {
178                         $result = "* ERROR: $driver language is $language in $lang_xpath\n";
179                         warn $result;
180                 }
181         } else {
182                 $result = "* ERROR: Unknown driver $driver in $driver_xpath\n";
183                 warn $result;
184         }
185         print $result;
186         $output .= $result;
187 }
188
189 print "\nChecking libdbi and libdbi-drivers\n";
190 $output .= check_libdbd();
191
192 print "\nChecking hostname\n";
193 my @hosts = $osrfxml->findnodes('/opensrf/hosts/*');
194 foreach my $host (@hosts) {
195         next unless $host->nodeType == XML::LibXML::XML_ELEMENT_NODE;
196         my $osrfhost = $host->nodeName;
197         my $he;
198         if ($osrfhost ne $hostname && $osrfhost ne "localhost") {
199                 $result = " * ERROR: expected hostname '$hostname', found '$osrfhost' in <hosts> section of opensrf.xml\n";
200                 warn $result;
201                 $he = 1;
202         } elsif ($osrfhost eq "localhost") {
203                 $result = " * OK: found hostname 'localhost' in <hosts> section of opensrf.xml\n";
204         } else {
205                 $result = " * OK: found hostname '$hostname' in <hosts> section of opensrf.xml\n";
206         }
207         print $result unless $he;
208         $output .= $result;
209 }
210
211 # Check for oils_web.xml, required for acquisitions and many administration
212 # interfaces as of Evergreen 1.6
213 if (!-t '/openils/conf/oils_web.xml') {
214         my $de = "* WARNING: As of Evergreen 1.6, /openils/conf/oils_web.xml " .
215                 "is a required configuration file. Copying " .
216                 "/openils/conf/oils_web.xml.example should resolve this " .
217                 "problem.\n";
218         $output .= $de;
219         warn $de;
220 }
221
222
223 if ($gather) {
224         get_debug_info( $tmpdir, $log_dir, $conf_dir, $perloutput, $output );
225 }
226
227 sub test_db_connect {
228         my ($db_name, $db_host, $db_port, $db_user, $db_pw, $osrf_xpath) = @_;
229
230         my $dsn = "dbi:Pg:dbname=$db_name;host=$db_host;port=$db_port";
231         my $de = undef;
232         my ($dbh, $encoding, $langs);
233         $dbh = DBI->connect($dsn, $db_user, $db_pw);
234
235         # Short-circuit if we didn't connect successfully
236         unless($dbh) {
237                 $de = "* $osrf_xpath :: Unable to connect to database $dsn, user=$db_user, password=$db_pw\n";
238                 warn "* $osrf_xpath :: Unable to connect to database $dsn, user=$db_user, password=$db_pw\n";
239                 return $de;
240         }
241
242         # Get server encoding
243         my $sth = $dbh->prepare("SHOW server_encoding");
244         $sth->execute;
245         $sth->bind_col(1, \$encoding);
246         $sth->fetch;
247         $sth->finish;
248
249         # Get list of server languages
250         $sth = $dbh->prepare("SELECT lanname FROM pg_catalog.pg_language");
251         $sth->execute;
252         $langs = $sth->fetchall_arrayref([0]);
253         $sth->finish;
254
255         $dbh->disconnect;
256         print "* $osrf_xpath :: Successfully connected to database $dsn\n" unless ($de);
257
258         # Check encoding
259         if ($encoding !~ m/(utf-?8|unicode)/i) {
260                 $de .= "* ERROR: $osrf_xpath :: Database $dsn has encoding $encoding instead of UTF8 or UNICODE.\n";
261                 warn "* ERROR: $osrf_xpath :: Database $dsn has encoding $encoding instead of UTF8 or UNICODE.\n";
262         } else {
263                 print "  * Database has the expected server encoding $encoding.\n";
264         }
265
266         my $result = check_db_langs($langs);
267         if ($result) {
268                 $de .= $result;
269                 warn $result;
270         }
271
272         return ($de) ? $de : "* $osrf_xpath :: Successfully connected to database $dsn with encoding $encoding\n";
273
274 }
275
276 sub check_db_langs {
277         my $langs = shift;
278
279         my $errors;
280
281         # Ensure the following PostgreSQL languages have been enabled
282         my %languages = (
283                 'plperl' => 0,
284                 'plperlu' => 0,
285                 'plpgsql' => 0,
286         );
287
288         foreach my $lang (@$langs) {
289                 my $lower = lc($$lang[0]);
290                 $languages{$lower} = 1;
291         }
292         
293         foreach my $lang (keys %languages) {
294                 if (!$languages{$lang}) {
295                         $errors .= "  * ERROR: Language '$lang' is not enabled in the target database\n";
296                 }
297         }
298
299         return $errors;
300 }
301
302 sub check_jabber {
303         my ($j_username, $j_password, $j_domain, $j_port) = @_;
304         print "\nChecking Jabber connection for user $j_username, domain $j_domain\n";
305
306         # connect to jabber 
307         my $client = OpenSRF::Transport::SlimJabber::Client->new(
308                 port => $j_port, 
309                 username => $j_username, 
310                 password => $j_password,
311                 host => $j_domain,
312                 resource => 'test123'
313         );
314
315
316         my $je = undef;
317         try {
318                 unless($client->initialize()) {
319                         $je = "* Unable to connect to jabber server $j_domain\n";
320                         warn "* Unable to connect to jabber server $j_domain\n";
321                 }
322         } catch Error with {
323                 $je = "* Error connecting to jabber:\n" . shift() . "\n";
324                 warn "* Error connecting to jabber:\n" . shift() . "\n";
325         };
326
327         print "* Jabber successfully connected\n" unless ($je);
328         $output .= ($je) ? $je : "* Jabber successfully connected\n";
329 }
330
331 sub check_libdbd {
332         my $results = '';
333         my @location = `/sbin/ldconfig --print | grep libdbdpgsql`; # simple(ton) attempt to filter out build versions
334     unless(@location) {
335                 # This is pretty distro-specific, but let's worry about other distros and operating systems when we get there
336         my $res = "libdbi PostgreSQL driver not found in shared library path;
337   you may need to edit /etc/ld.so.conf or add an entry to /etc/ld.so.conf.d/
338   and run 'ldconfig' as root\n";
339         print $res;
340         return $res;
341     }
342         if ($location[0] !~ m#/usr/local/lib/dbd/#) {
343                 my $res = "libdbdpgsql.so was not found in /usr/local/libdbi/dbd/
344   We have found that system packages don't link against libdbi.so;
345   therefore, we strongly recommend compiling libdbi and libdbi-drivers from source.\n";
346                 $results .= $res;
347                 print $res;
348         }
349         if ($results eq '') {
350                 $results = "  * OK - found locally installed libdbi.so and libdbdpgsql.so in shared library path\n";
351                 print $results;
352         }
353         return $results;
354 }
355
356 sub get_debug_info {
357   my $temp_dir = shift; # place we can write files
358   my $log = shift; # location of the log directory
359   my $config = shift; # location of the config files
360   my $perl_test = shift; # output from the Perl prereq testing
361   my $config_test = shift; # output from the config file testing
362
363   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
364   my $oils_time = sprintf("%04d-%02d-%02d_%02dh-%02d-%02d", $year+1900, $mon, $mday, $hour, $min, $sec);
365   
366   # evil approach that requires no other Perl dependencies
367   chdir($temp_dir);
368   my $oils_debug_dir = "$temp_dir/oils_$oils_time";
369
370   # Replace with something Perlish
371   mkdir($oils_debug_dir) or die $!;
372
373   # Replace with File::Copy
374   system("cp $log/*log $oils_debug_dir");
375
376   # Passwords will go through in the clear for now
377   system("cp $config/*xml $oils_debug_dir");
378
379   # Get Perl output
380   open(FH, ">", "$oils_debug_dir/perl_test.out") or die $!;
381   print FH $perl_test;
382   close(FH);
383
384   # Get XML output
385   open(FH, ">", "$oils_debug_dir/xml_test.out") or die $!;
386   print FH $config_test;
387   close(FH);
388   
389   # Tar this up - does any system not have tar?
390   system("tar czf oils_$oils_time.tar.gz oils_$oils_time");
391
392   # Clean up after ourselves, somewhat dangerously
393   system("rm -fr $oils_debug_dir");
394
395   print "Wrote your debug information to $temp_dir/oils_$oils_time.tar.gz.\n";
396 }
397
398 __DATA__
399 LWP::UserAgent
400 XML::LibXML
401 XML::LibXML::XPathContext
402 XML::LibXSLT
403 Net::Server::PreFork
404 Cache::Memcached
405 Class::DBI
406 Class::DBI::AbstractSearch
407 Template
408 DBD::Pg
409 Net::Z3950 Net::Z3950::ZOOM
410 MARC::Record
411 MARC::Charset
412 MARC::File::XML
413 Text::Aspell
414 CGI
415 DateTime::TimeZone
416 DateTime
417 DateTime::Format::ISO8601
418 DateTime::Format::Mail
419 Unix::Syslog
420 GD::Graph3d
421 JavaScript::SpiderMonkey
422 Log::Log4perl
423 Email::Send
424 Text::CSV
425 Text::CSV_XS
426 Spreadsheet::WriteExcel::Big
427 Tie::IxHash
428 Parse::RecDescent
429 SRU
430 JSON::XS