Support checking for multiple revisions at once and do batch execution

This dramatically speeds up the ngcp-update-db-schema process if
everything has been applied already or if just a few single
scripts have to be executed.

Execution times for old mode vs new (batch) mode with all present
scripts executed already:

| root@spce:~# time { /usr/sbin/ngcp-update-db-schema.orig >/dev/null ; }
|
| real    0m53.281s
| user    0m45.439s
| sys     0m5.100s

 vs.

| root@spce:~# time /usr/sbin/ngcp-update-db-schema
| [ ok ] Starting MySQL database server: mysqld already running.
| Synchronizing passwords with MySQL
|
| real    0m4.096s
| user    0m1.812s
| sys     0m0.472s

The main behaviour of ngcp-check-rev-applied hasn't been changed,
it's backwards compatible (so also cfg-schema which uses
ngcp-check-rev-applied continues to work as it is), now it's just
possible to provide multiple arguments to the "--revision" option
and its output is parsed by ngcp-update-db-schema then.
agranig/pref-label
Michael Prokop 12 years ago
parent 835a264a7b
commit 4b0977edc7

4
debian/control vendored

@ -8,7 +8,9 @@ Homepage: http://sipwise.com/
Package: ngcp-db-schema
Architecture: all
Depends: libconfig-tiny-perl, ${misc:Depends}
Depends: libconfig-tiny-perl,
liblist-moreutils-perl,
${misc:Depends}
Description: database schema for Sipwise's NGCP platform
This package provides the database schema for Sipwise's NGCP
platform.

@ -2,88 +2,130 @@
use strict;
use warnings;
use Carp;
use Config::Tiny;
use DBI;
use English qw( -no_match_vars );
use Getopt::Long;
use List::MoreUtils qw{ any };
use Pod::Usage;
use Config::Tiny;
my $dbhost = 'localhost';
my $dbname = 'ngcp';
my $debug = 0;
my $help = 0;
my $man = 0;
my $node = undef;
my $revision = undef;
my $schema = undef;
my $dbhost = 'localhost';
my $dbname = 'ngcp';
my $debug = 0;
my $exit_code = 0;
my $help = 0;
my $man = 0;
my $node = undef;
my $result_date = undef;
my $result_host = undef;
my $result_id = undef;
my $result_rev = undef;
my @revision;
my @result_list;
my $schema = undef;
GetOptions(
'dbhost=s' => \$dbhost,
'dbname=s' => \$dbname,
'debug|d' => \$debug,
'help|?' => \$help,
'man' => \$man,
'node=s' => \$node,
'revision=s' => \$revision,
'schema=s' => \$schema,
'dbhost=s' => \$dbhost,
'dbname=s' => \$dbname,
'debug|d' => \$debug,
'help|?' => \$help,
'man' => \$man,
'node=s' => \$node,
'revision=s{1,}' => \@revision,
'schema=s' => \$schema,
) or pod2usage(2);
pod2usage(-exitval => 2, -verbose => 0) if $help;
pod2usage(-exitval => 2, -verbose => 2) if $man;
pod2usage(2) if !defined $revision;
pod2usage( -exitval => 2, -verbose => 0 ) if $help;
pod2usage( -exitval => 2, -verbose => 2 ) if $man;
pod2usage(2) if !@revision;
pod2usage(2) if !defined $schema;
# connect as user sipwise to DB
my $Config = Config::Tiny->read('/etc/mysql/sipwise.cnf') or die "Could not read /etc/mysql/sipwise.cnf";
my $Config = Config::Tiny->read('/etc/mysql/sipwise.cnf')
or croak "Could not read /etc/mysql/sipwise.cnf: $ERRNO";
my $dbuser = 'sipwise';
my $dbpwd = $Config->{_}->{SIPWISE_DB_PASSWORD};
my $dbpwd = $Config->{_}->{SIPWISE_DB_PASSWORD};
if (!defined $dbpwd) {
die "Couldn't identify password for user sipwise to connect to database.";
if ( !defined $dbpwd ) {
croak "Couldn't identify password for user sipwise to connect to database.";
}
$dbpwd =~ s/^['"](.*)['"]$/$1/; # get rid of possibly surrounding quotes
$dbpwd =~ s/^['"](.*)['"]$/$1/; # get rid of possibly surrounding quotes
my $dbh = DBI->connect("dbi:mysql:dbname=$dbname;host=$dbhost", $dbuser, $dbpwd)
or die "Couldn't connect to database: " . DBI->errstr;
my $dbh =
DBI->connect( "dbi:mysql:dbname=$dbname;host=$dbhost", $dbuser, $dbpwd )
or croak "Couldn't connect to database: " . DBI->errstr;
$schema = $dbh->quote_identifier($schema);
my $sql_clause = ""; # if no node is specified then use empty SQL statement
if (defined $node) {
$sql_clause = "and node = ?";
my $sql_clause = ""; # if no node is specified then use empty SQL statement
if ( defined $node ) {
$sql_clause = "where node = ?";
}
my $sth = $dbh->prepare("SELECT * FROM $schema WHERE revision = ? $sql_clause")
or die "Couldn't prepare statement: " . $dbh->errstr;
my $sth = $dbh->prepare("SELECT * FROM $schema $sql_clause")
or croak "Couldn't prepare statement: " . $dbh->errstr;
$sth->bind_param(1, $revision);
if (defined $node) {
$sth->bind_param(2, $node);
if ( defined $node ) {
$sth->bind_param( 1, $node );
}
$sth->execute()
or die "Couldn't execute statement: " . $sth->errstr;
if ($sth->rows == 0) {
if (defined $node) {
print STDERR "No match for revision $revision on host $node.\n" if $debug;
exit(1);
} else {
print STDERR "No match for revision $revision.\n" if $debug;
exit(1);
or croak "Couldn't execute statement: " . $sth->errstr;
$sth->bind_columns( \$result_id, \$result_rev, \$result_host, \$result_date );
while ( $sth->fetch ) {
foreach my $rev (@revision) {
# adjust for MySQL handling (we're interested only in the ID in MySQL speak)
# the regex handling is ugly, some love to it would be nice...
my $short_rev = $rev;
$short_rev =~ s/.*\///; # drop anything until and incl. the first slash
$short_rev =~ s/\..*//; # drop anything starting at the first '.'
$short_rev =~ s/_.*//; # drop anything starting at the first '_'
$short_rev =~ s/^0+//; # strip any trailing zeros from the id
if ( $result_rev =~ /^${short_rev}$/msx ) {
if ( defined $node ) {
if ( $result_host =~ /$node/msx ) {
push @result_list, $rev;
}
}
else {
push @result_list, $rev;
}
}
}
}
foreach my $rev (@revision) {
if ( any { /^${rev}$/msx } @result_list ) {
if ( defined $node ) {
print "Revision $rev already executed on host $node\n";
}
} else {
# DBI::dump_results($sth) if $debug;
my @data;
while (@data = $sth->fetchrow_array()) {
my $id = $data[1];
my $hostname = $data[2];
print "revision $id already executed on $hostname\n" if $debug;
else {
print "Revision $rev already executed\n";
}
}
else {
if ( defined $node ) {
print "No match for revision $rev on host $node\n";
$exit_code = 1;
}
else {
print "No match for revision $rev\n";
$exit_code = 1;
}
}
}
$sth->finish;
$dbh->disconnect;
exit($exit_code);
__END__
=head1 NAME
@ -92,14 +134,16 @@ ngcp-check-rev-applied - check which db/config revisions have been executed
=head1 SYNOPSIS
ngcp-check-rev-applied --schema <schema> --revision <id>
ngcp-check-rev-applied --schema <schema> --revision <id> [<2nd_id> [<3rd_id> [...]]
[--node <name>] [--dbname <db>] [--dbhost <host>]
=head1 DESCRIPTION
This program queries the ngcp database to check for db/config revisions that
have been applied. If the specified ID is present the exit code is 0, otherwise
(the specified ID is not present) the exit code is 1.
have been applied. If the specified ID is present (or when multiple IDs are
specified: all of them are present) the exit code is 0, otherwise (the specified
ID is not present, or when multiple IDs are specified:: one of them is not
present) the exit code is 1.
=head1 USAGE EXAMPLES
@ -107,12 +151,19 @@ have been applied. If the specified ID is present the exit code is 0, otherwise
=item Check db_schema table for revision 23:
ngcp-check-rev-applied --schema db_schema --revision 23
% ngcp-check-rev-applied --schema db_schema --revision 23
=item Check cfg_schema table for revision 42 and use output of `hostname`
to limit the specified ID to the current host:
ngcp-check-rev-applied --schema cfg_schema --revision 42 --node $(hostname)
% ngcp-check-rev-applied --schema cfg_schema --revision 42 --node $(hostname)
=item Check db_schema table for revisions 11, 23 and 42:
% ngcp-check-rev-applied --schema db_schema --revision 11 23 42 --debug
No match for revision 11.
No match for revision 23.
Revision 42 already executed.
=back
@ -122,11 +173,12 @@ to limit the specified ID to the current host:
=item B<0>
The requested ID is present.
The requested ID is present. If multiple IDs have been specified all of them are present.
=item B<1>
The requested ID is *NOT* present.
The requested ID is *NOT* present. If multiple IDs have been specified at least
one of the specified IDs it *NOT* present.
=item B<2>
@ -170,7 +222,8 @@ to check for (non-)replicated revisions.
=item B<--revision> <id>
Query database for specified ID.
Query database for specified ID. Multiple IDs can be specified white-space
separated (e.g.: '--revision 11 23 42').
=item B<--schema> <name>

@ -110,35 +110,42 @@ apply_revision() {
apply_generic_revs() {
[ -n "$1" ] || return 1
rev="$1"
revname="$(basename $rev)"
revs="$1"
if ngcp-check-rev-applied --schema db_schema --revision "$revname" ; then
echo "Revision $revname has been applied already, nothing to do."
return 0
fi
for missing_revision in $(ngcp-check-rev-applied --schema db_schema --revision $revs | awk '/^No match for revision/ {print $5}') ; do
revision_file="$(find /usr/share/ngcp-db-schema/ -name "$missing_revision")"
if [ -r "$revision_file" ] ; then
apply_revision "$revision_file"
else
echo "Warning: missing revision $missing_revision identified but could not find according db-schema file."
fi
done
apply_revision "$rev"
}
# execute the rev script iff there's no entry for the *current* host yet
apply_host_specific_revs() {
[ -n "$1" ] || return 1
rev="$1"
revname="$(basename $rev)"
revs="$1"
if ngcp-check-rev-applied --schema db_schema --revision "$revname" --node "$hostname" ; then
echo "Revision $revname has been applied on $hostname already, nothing to do."
return 0
fi
for missing_revision in $(ngcp-check-rev-applied --schema db_schema --revision $revs --node "$hostname" | awk '/^No match for revision/ {print $5}') ; do
revision_file="$(find /usr/share/ngcp-db-schema/ -name "$missing_revision")"
apply_revision "$rev"
if [ -r "$revision_file" ] ; then
apply_revision "$revision_file"
else
echo "Warning: missing revision $missing_revision identified but could not find according file."
fi
done
}
revision_wrapper() {
[ -n "$1" ] || return 1
local host_specific_revs;
local generic_revs;
for rev in $* ; do
if ! [ -r "$rev" ] ; then
@ -154,19 +161,21 @@ revision_wrapper() {
# the scripts that should be executed on *all* hosts, no matter whether
# they are active or inactive, since the script's content doesn't get replicated
*_not_replicated.up)
apply_host_specific_revs "$rev"
;;
host_specific_revs="$host_specific_revs $(basename $rev)"
;;
*)
if running_on_active_node ; then
echo "Replication script ${revname} noted, nothing to do on active node"
else
apply_generic_revs "$rev"
if running_on_active_node ; then
echo "Replication script ${revname} noted, nothing to do on active node"
else
generic_revs="$generic_revs $(basename $rev)"
fi
;;
;;
esac
done
apply_host_specific_revs "$host_specific_revs"
apply_generic_revs "$generic_revs"
}
# make sure we get sorted 10XXX after 9XXX

Loading…
Cancel
Save