]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/support-scripts/eg_db_config.in
LP#1468422 Admin seed data sets new-style passwd
[Evergreen.git] / Open-ILS / src / support-scripts / eg_db_config.in
1 #!/usr/bin/perl
2 # eg_db_config.pl -- configure Evergreen database settings and create schema
3 # vim:noet:ts=4:sw=4:
4 #
5 # Copyright (C) 2008 Equinox Software, Inc.
6 # Copyright (C) 2008-2009 Laurentian University
7 # Author: Kevin Beswick <kevinbeswick00@gmail.com>
8 # Author: Dan Scott <dscott@laurentian.ca>
9 #
10 # This program is free software; you can redistribute it and/or
11 # modify it under the terms of the GNU General Public License
12 # as published by the Free Software Foundation; either version 2
13 # of the License, or (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
19
20 use strict; use warnings;
21 use XML::LibXML;
22 use File::Copy;
23 use Getopt::Long;
24 use File::Spec;
25 use File::Basename;
26 use DBI;
27 use Cwd qw/abs_path getcwd/;
28
29 my ($dbhost, $dbport, $dbname, $dbuser, $dbpw, $help, $admin_user, $admin_pw, $load_all, $load_concerto);
30 my $config_file = '';
31 my $build_db_sh = '';
32 my $offline_file = '';
33 my $prefix = '';
34 my $sysconfdir = '';
35 my $pg_contribdir = '';
36 my $create_db_sql_contribs = '';
37 my $create_db_sql_extensions = '';
38 my @services;
39
40 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
41
42 my $cwd = getcwd();
43
44 # Get the directory for this script
45 my $script_dir = abs_path(dirname($0));
46
47 # Set the location and base file for sample data
48 my $_sample_dir = abs_path(File::Spec->catdir($script_dir, '../../tests/datasets/sql/'));
49 my $_sample_all = 'load_all.sql';
50 my $_sample_concerto = 'load_concerto.sql';
51
52 =over
53
54 =item update_config() - Puts command line specified settings into xml file
55 =cut
56 sub update_config {
57         my ($services, $settings) = @_;
58
59         my $parser = XML::LibXML->new();
60         my $opensrf_config = $parser->parse_file($config_file);
61
62         if (@$services) {
63                 foreach my $service (@$services) {
64                         foreach my $key (keys %$settings) {
65                                 next unless $settings->{$key};
66                                 my @node;
67
68                                 if ($service eq 'state_store') {
69                                         (@node) = $opensrf_config->findnodes("//state_store/$key/text()");
70                                 } else {
71                                         (@node) = $opensrf_config->findnodes("//$service//database/$key/text()");
72                                 }
73
74                                 foreach (@node) {
75                                         $_->setData($settings->{$key});
76                                 }
77                         }
78
79                 }
80         }
81
82         my $timestamp = sprintf("%d.%d.%d.%d.%d.%d",
83                 $year + 1900, $mon +1, $mday, $hour, $min, $sec);
84         if (copy($config_file, "$config_file.$timestamp")) {
85                 print "Backed up original configuration file to '$config_file.$timestamp'\n";
86         } else {
87                 print STDERR "Unable to write to '$config_file.$timestamp'; bailed out.\n";
88         }
89
90         $opensrf_config->toFile($config_file) or
91                 die "ERROR: Failed to update the configuration file '$config_file'\n";
92 }
93
94 =item create_offline_config() - Write out the offline config
95 =cut
96 sub create_offline_config {
97         my ($setup, $settings) = @_;
98
99         open(FH, '>', $setup) or die "Could not write offline database setup to $setup\n";
100
101         print "Writing offline database configuration to $setup\n";
102
103         printf FH "\$main::config{base_dir} = '%s/var/data/offline/';\n", $prefix;
104         printf FH "\$main::config{bootstrap} = '%s/opensrf_core.xml';\n", $sysconfdir;
105
106         printf FH "\$main::config{dsn} = 'dbi:Pg:host=%s;dbname=%s;port=%d';\n",
107                 $settings->{host}, $settings->{db}, $settings->{port};
108
109         printf FH "\$main::config{usr} = '%s';\n", $settings->{user};
110         printf FH "\$main::config{pw} = '%s';\n", $settings->{pw};
111
112         close(FH);
113 }
114
115 =item get_settings() - Extracts database settings from opensrf.xml
116 =cut
117 sub get_settings {
118         my $settings = shift;
119
120         my $host = "/opensrf/default/apps/open-ils.storage/app_settings/databases/database/host/text()";
121         my $port = "/opensrf/default/apps/open-ils.storage/app_settings/databases/database/port/text()";
122         my $dbname = "/opensrf/default/apps/open-ils.storage/app_settings/databases/database/db/text()";
123         my $user = "/opensrf/default/apps/open-ils.storage/app_settings/databases/database/user/text()";
124         my $pw = "/opensrf/default/apps/open-ils.storage/app_settings/databases/database/pw/text()";
125
126         my $parser = XML::LibXML->new();
127         my $opensrf_config = $parser->parse_file($config_file);
128
129         # If the user passed in settings at the command line,
130         # we don't want to override them
131         $settings->{host} = $settings->{host} || $opensrf_config->findnodes($host);
132         $settings->{port} = $settings->{port} || $opensrf_config->findnodes($port);
133         $settings->{db} = $settings->{db} || $opensrf_config->findnodes($dbname);
134         $settings->{user} = $settings->{user} || $opensrf_config->findnodes($user);
135         $settings->{pw} = $settings->{pw} || $opensrf_config->findnodes($pw);
136 }
137
138 =item create_database() - Creates the database using create_database_contribs.sql
139 =cut
140 sub create_database {
141         my $settings = shift;
142
143         $ENV{'PGUSER'} = $settings->{user};
144         $ENV{'PGPASSWORD'} = $settings->{pw};
145         $ENV{'PGPORT'} = $settings->{port};
146         $ENV{'PGHOST'} = $settings->{host};
147         my @temp = `psql -d postgres -qtc 'show server_version;' | xargs | cut -c1,3`;
148         chomp $temp[0];
149         my $pgversion = $temp[0];
150         my $cmd;
151         # If it looks like it is 9.1 or greater, use create_database_extensions.sql
152         # Otherwise use create_database_contribs.sql
153         if($pgversion >= '91') {
154                 $cmd = 'psql -vdb_name=' . $settings->{db} . ' -d postgres -f ' . $create_db_sql_extensions;
155         } else {
156                 $cmd = 'psql -vdb_name=' . $settings->{db} . ' -vcontrib_dir=' . $pg_contribdir .
157                         ' -d postgres -f ' . $create_db_sql_contribs;
158         }
159         my @output = `$cmd 2>&1`;
160         if(grep(/(ERROR|No such file or directory)/,@output)) {
161                 push(@output, "\n------------------------------------------------------------------------------\n",
162                         "There was a problem creating the database.\n",
163                         "See above for more information.\n");
164                 if(grep/unsupported language/, @output) {
165                         push(@output, "\nYou may need to install the postgresql plperl package on the database server.\n");
166                 }
167                 if(grep/No such file or directory/, @output) {
168                         if($pgversion >= '91') {
169                                 push(@output, "\nYou may need to install the postgresql contrib package on the database server.\n"); 
170                         } else {
171                                 push(@output, "\nYou may need to install the postgresql contrib package on this server.\n");
172                         }
173                 }
174                 push(@output, "------------------------------------------------------------------------------\n");
175                 die(@output);
176         }
177 }
178
179 =item create_schema() - Creates the database schema by calling build-db.sh
180 =cut
181 sub create_schema {
182         my $settings = shift;
183
184         chdir(dirname($build_db_sh));
185         my $cmd = File::Spec->catfile('.', basename($build_db_sh)) . " " .
186                 $settings->{host} ." ".  $settings->{port} ." ". 
187                 $settings->{db} ." ".  $settings->{user} ." ". 
188                 $settings->{pw};
189         system($cmd);
190         chdir($script_dir);
191 }
192
193 =item load_sample_data() - Loads sample bib records, copies, users, and transactions
194 =cut
195 sub load_sample_data {
196         my $settings = shift;
197
198         my $load_script = $_sample_all;
199         chdir($_sample_dir);
200         if ($load_concerto) {
201                 $load_script = $_sample_concerto;
202         }
203         $ENV{'PGUSER'} = $settings->{user};
204         $ENV{'PGPASSWORD'} = $settings->{pw};
205         $ENV{'PGPORT'} = $settings->{port};
206         $ENV{'PGHOST'} = $settings->{host};
207         $ENV{'PGDATABASE'} = $settings->{db};
208         my @output = `psql -f $load_script 2>&1`;
209         print @output;
210         chdir($cwd);
211 }
212
213 =item set_admin_account() - Sets the administrative user's user name and password
214 =cut
215 sub set_admin_account {
216         my $admin_user = shift;
217         my $admin_pw = shift;
218         my $settings = shift;
219
220         my $dbh = DBI->connect('dbi:Pg:dbname=' . $settings->{db} . 
221                 ';host=' . $settings->{host} . ';port=' . $settings->{port} . ';',
222                  $settings->{user} . "", $settings->{pw} . "", {AutoCommit => 1}
223         );
224         if ($dbh->err) {
225                 print STDERR "Could not connect to database to set admin account. ";
226                 print STDERR "Error was " . $dbh->errstr . "\n";
227                 return;
228         }
229         my $stmt = $dbh->prepare("UPDATE actor.usr SET usrname = ? WHERE id = 1");
230         $stmt->execute(($admin_user));
231         if ($dbh->err) {
232                 print STDERR "Failed to set admin username. ";
233                 print STDERR "Error was " . $dbh->errstr . "\n";
234                 return;
235         }
236
237         # Legacy actor.usr.passwd-style passwords must go through
238         # in intermediate round of hashing before final crypt'ing.
239         # The hashing step requires access to the password salt.
240         # Create a new salt, perform MD5 hashing, set the new password.
241         $stmt = $dbh->prepare("SELECT actor.create_salt('main') AS new_salt");
242         $stmt->execute;
243         my $new_salt = $stmt->selectrow_hashref->{new_salt};
244
245     $stmt = $dbh->prepare(
246                 "SELECT actor.set_passwd(1, 'main', MD5(? || MD5(?)), ?)");
247         $stmt->execute(($new_salt, $admin_pw, $new_salt));
248         if ($dbh->err) {
249                 print STDERR "Failed to set admin password. ";
250                 print STDERR "Error was " . $dbh->errstr . "\n";
251                 return;
252         }
253 }
254
255 my $offline;
256 my $cdatabase;
257 my $cschema;
258 my $uconfig;
259 my $pgconfig;
260 my %settings;
261
262 GetOptions("create-schema" => \$cschema, 
263                 "create-database" => \$cdatabase,
264                 "load-all-sample" => \$load_all,
265                 "load-concerto-sample" => \$load_concerto,
266                 "create-offline" => \$offline,
267                 "update-config" => \$uconfig,
268                 "config-file=s" => \$config_file,
269                 "build-db-file=s" => \$build_db_sh,
270                 "pg-contrib-dir=s" => \$pg_contribdir,
271                 "create-db-sql-contribs=s" => \$create_db_sql_contribs,
272                 "create-db-sql-extensions=s" => \$create_db_sql_extensions,
273                 "pg-config=s" => \$pgconfig,
274                 "admin-user=s" => \$admin_user,
275                 "admin-password=s" => \$admin_pw,
276                 "service=s" => \@services,
277                 "user=s" => \$settings{'user'},
278                 "password=s" => \$settings{'pw'},
279                 "database=s" => \$settings{'db'},
280                 "hostname=s" => \$settings{'host'},
281                 "port=i" => \$settings{'port'}, 
282                 "help" => \$help
283 );
284
285 if (grep(/^all$/, @services)) {
286         @services = qw/reporter open-ils.cstore open-ils.pcrud open-ils.storage open-ils.reporter-store state_store/;
287 }
288
289 my $eg_config = File::Spec->catfile($script_dir, '../extras/eg_config');
290
291 if (!$config_file) { 
292         my @temp = `$eg_config --sysconfdir`;
293         chomp $temp[0];
294         $sysconfdir = $temp[0];
295         $config_file = File::Spec->catfile($sysconfdir, "opensrf.xml");
296 }
297
298 if (!$prefix) {
299         my @temp = `$eg_config --prefix`;
300         chomp $temp[0];
301         $prefix = $temp[0];
302 }
303
304 if (!$build_db_sh) {
305         $build_db_sh = File::Spec->catfile($script_dir, '../sql/Pg/build-db.sh');
306 }
307
308 if (!$pg_contribdir) {
309         $pgconfig = 'pg_config' if(!$pgconfig);
310         my @temp = `$pgconfig --sharedir`;
311         chomp $temp[0];
312         $pg_contribdir = File::Spec->catdir($temp[0], 'contrib');
313 }
314
315 if (!$create_db_sql_contribs) {
316         $create_db_sql_contribs = File::Spec->catfile($script_dir, '../sql/Pg/create_database_contribs.sql');
317 }
318
319 if (!$create_db_sql_extensions) {
320         $create_db_sql_extensions = File::Spec->catfile($script_dir, '../sql/Pg/create_database_extensions.sql');
321 }
322
323 if (!$offline_file) {
324         $offline_file = File::Spec->catfile($sysconfdir, 'offline-config.pl');
325 }
326
327 unless (-e $build_db_sh) { die "Error: $build_db_sh does not exist. \n"; }
328 unless (-e $config_file) { die "Error: $config_file does not exist. \n"; }
329
330 if ($uconfig) { update_config(\@services, \%settings); }
331
332 # Get our settings from the config file
333 get_settings(\%settings);
334
335 if ($cdatabase) { create_database(\%settings); }
336 if ($cschema) { create_schema(\%settings); }
337 if ($admin_user && $admin_pw) {
338         set_admin_account($admin_user, $admin_pw, \%settings);
339 }
340 if ($load_all || $load_concerto) {
341         load_sample_data(\%settings);
342 }
343 if ($offline) { create_offline_config($offline_file, \%settings); }
344
345 if ((!$cdatabase && !$cschema && !$load_all && !$load_concerto && !$uconfig && !$offline && !$admin_pw) || $help) {
346         print <<HERE;
347
348 SYNOPSIS
349     eg_db_config.pl [OPTION] ... [COMMAND] ... [CONFIG OPTIONS]
350
351 DESCRIPTION
352     Creates or recreates the Evergreen database schema based on the settings
353     in the opensrf.xml configuration file.
354
355     Manipulates the configuration file 
356
357 OPTIONS
358     --config-file
359         specifies the opensrf.xml file. Defaults to @sysconfdir@/opensrf.xml
360
361     --build-db-file
362         specifies the script that creates the database schema. Defaults to
363         Open-ILS/src/sql/pg/build-db.sh
364
365     --offline-file
366         specifies the offline database settings file required by the offline
367         data uploader. Defaults to @sysconfdir@/offline-config.pl
368
369 COMMANDS
370     --update-config
371         Configures Evergreen database settings in the file specified by
372         --config-file.
373
374     --create-offline
375         Creates the database setting file required by the offline data uploader
376
377     --create-schema
378         Creates the Evergreen database schema according to the settings in
379         the file specified by --build-db-file.  
380
381     --create-database
382         Creates the database itself, provided the user and password options
383         represent a superuser.
384
385     --load-all-sample
386                 Loads all sample data, including bibliographic records, call numbers,
387                 copies, users, and transactions.
388
389     --load-concerto-sample
390                 Loads a subset of sample data that includes just 100 bibliographic
391                 records, and associated call numbers and copies.
392
393 SERVICE OPTIONS
394     --service
395         Specify "all" or one or more of the following services to update:
396             * reporter
397             * open-ils.cstore
398             * open-ils.pcrud
399             * open-ils.storage
400             * open-ils.reporter-store
401             * state_store
402     
403 DATABASE CONFIGURATION OPTIONS
404     --user            username for the database 
405
406     --password        password for the user 
407
408     --database        name of the database 
409
410     --hostname        name or address of the database host 
411
412     --port            port number for database access
413
414     --admin-user      administration user's user name
415
416     --admin-pass      administration user's password
417
418 EXAMPLES
419    This script is normally used during the initial installation and
420    configuration process. This creates the database schema, sets
421    the administration user's user name and password, and modifies your
422    configuration files to include the correct database connection
423    information.
424
425    For a single server install, or an install with one web/application
426    server and one database server, you will typically want to invoke this
427    script with a complete set of commands:
428
429    perl Open-ILS/src/support-scripts/eg_db_config.pl --update-config \
430        --service all --create-schema --create-offline \
431        --user <db-user> --password <db-pass> --hostname localhost --port 5432 \
432        --database evergreen --admin-user <admin-user> --admin-pass <admin-pass> 
433
434    To update the configuration for a single service - for example, if you
435    replicated a database for reporting purposes - just issue the
436    --update-config command with the service identified and the changed
437    database parameters specified:
438
439    perl Open-ILS/src/support-scripts/eg_db_config.pl --update-config \
440        --service reporter --hostname foobar --password newpass
441
442 HERE
443 }