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