Commit 686d2dcf authored by Markus Frosch's avatar Markus Frosch 📣
Browse files

ido_mysql: Add icinga2-schema-update

parent d6a9e858
#!/usr/bin/perl
# Checking for Icinga IDO schema upgrades
#
# Based on the schema update files, we will evaluate what schema upgrades are
# pending for Icinga's IDO database.
#
# Copyright (C) 2016 Markus Frosch <markus.frosch@icinga.com>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
use warnings;
use strict;
use Getopt::Long;
use DBI;
my $SCHEMA_LOCATIONS = [
'/usr/share/dbconfig-common/data/icinga2-ido-%type%/upgrade/%type%',
'/usr/share/icinga2-ido-%type%/schema/upgrade',
];
my $DEBUG = 0;
my $check = 0;
my $type = 'mysql';
my $host = 'localhost';
my $database = 'icinga2';
my $username = 'icinga2';
my $password = 'icinga2';
my $port;
my $DBH;
sub bail($) # {{{
{
my $message = shift;
print STDERR "$message\n";
exit(0) if $check eq 1;
exit(1);
}
# }}}
sub parse_options() # {{{
{
my $clipass;
unless (GetOptions(
"c|check" => \$check,
"H|host=s" => \$host,
"D|database=s" => \$database,
"u|username=s" => \$username,
"p|password=s" => \$clipass,
"D|debug" => \$DEBUG,
)) {
print STDERR "Could not parse command line arguments!\n";
exit(1);
}
if (defined $clipass) {
print STDERR "Please set password via environment variable IDO_PASSWORD\n";
$password = $clipass;
}
}
# }}}
sub parse_environment_options() # {{{
{
$type = $ENV{'IDO_TYPE'} if defined $ENV{'IDO_TYPE'};
$host = $ENV{'IDO_HOST'} if defined $ENV{'IDO_HOST'};
$username = $ENV{'IDO_USERNAME'} if defined $ENV{'IDO_USERNAME'};
$password = $ENV{'IDO_PASSWORD'} if defined $ENV{'IDO_PASSWORD'};
}
# }}}
sub find_schema_location($) # {{{
{
my $type = shift;
for my $path (@$SCHEMA_LOCATIONS) {
$path =~ s/%type%/$type/g;
if (-d $path) {
print "found schema updates at $path\n" if $DEBUG;
return $path;
}
}
print STDERR "Could not find schema update location!\n";
exit(1);
}
# }}}
sub get_database() # {{{
{
return $DBH if defined $DBH;
if ($type !~ m/^(mysql|pgsql)$/) {
print STDERR "Invalid database type '$type'!\n";
exit(1);
}
my $dsn = "dbi:$type:";
my $map = {
'database' => $database,
'host' => $host,
'port' => $port,
};
for my $key (keys %$map) {
if (defined $map->{$key}) {
$key =~ s/([=;])/$1/g;
$dsn .= $key . '=' . $map->{$key} . ';';
}
}
unless ($DBH = DBI->connect($dsn, $username, $password)) {
print STDERR "Could not connect to database: " . $DBI::errstr . "\n";
exit(1);
}
return $DBH;
}
# }}}
sub read_file($) # {{{
{
my $file = shift;
open(my $fh, '<', $file) or bail "Could not open file '$file' for reading: $!";
my $contents = '';
while (<$fh>) {
$contents .= $_;
}
close($fh);
return $contents;
}
# }}}
# compareVersionStrings() {{{
# @from http://www.perlmonks.org/?node_id=813994
#
# Compare two version strings
#
# Among other assumptions, this code assumes that the version strings have the
# same number of elements in them. Thus, comparing 3 against 2 will work; as
# will 3.1 against 2.7 and 3.1.4 against 2.7.18. Comparing 2.0 against 4 will
# fail, as will 2.0 against 4.5.6.
sub compareVersionStrings
{
my @left = split( /\./, $a );
my @right = split( /\./, $b );
return ( $left[0] <=> $right[0]
|| $left[1] <=> $right[1]
|| $left[2] <=> $right[2] );
}
# }}}
sub index_schema_upgrades() # {{{
{
my $path = find_schema_location($type);
opendir(my $dh, $path) or bail "Could not open dir '$path' for reading: $!";
my $upgrades = {};
while (readdir $dh) {
next if $_ =~ /^\./;
# not possible on Debian
#next if $_ !~ /\.sql$/;
my $name = $_;
$name =~ s/\.sql$//;
my $fullpath = "$path/$_";
my $contents = read_file($fullpath);
if ($contents =~ m/(?:INSERT|UPDATE)[^;]*icinga_dbversion[^;]*'(\d+\.\d+\.\d+)'[^;]*;/) {
my $version = $1;
print "Found schema version $version inside $fullpath\n" if $DEBUG;
$upgrades->{$version} = {
'name' => $name,
'file' => $fullpath,
'content' => $contents,
};
} else {
bail "Could not find schema upgrade version inside $fullpath";
}
}
return $upgrades;
}
# }}}
# find_schema_upgrades($version) {{{
#
# Check what upgrades are pending
#
sub find_schema_upgrades($)
{
my $version = shift;
my $upgrades = index_schema_upgrades();
my $found_current = 0;
my @versions = sort compareVersionStrings keys %$upgrades;
my $pending_upgrades = {};
for my $ver (@versions) {
local $a = $ver;
local $b = $version;
my $compare = &compareVersionStrings;
if ($compare < 0) {
print "Found older upgrade $ver -> " . $upgrades->{$ver}->{file} . "\n" if $DEBUG;
} elsif ($compare == 0) {
print "Found current upgrade $ver -> " . $upgrades->{$ver}->{file} . "\n" if $DEBUG;
$found_current = 1;
} else {
print "Found pending upgrade $ver -> " . $upgrades->{$ver}->{file} . "\n" if $DEBUG;
$pending_upgrades->{$ver} = $upgrades->{$ver};
}
}
if ($found_current eq 0) {
bail "Haven't found my own version '$version' in the upgrade list: " . join(', ', @versions);
}
return $pending_upgrades;
}
# }}}
sub get_current_schema() # {{{
# Retrieving the current schema version from the database
{
my $dbh = get_database;
my $data;
unless ($data = $dbh->selectrow_hashref('SELECT version, modify_time FROM icinga_dbversion')) {
print STDERR "Could not find schema version!\n";
exit(1);
}
print "Found version " . $data->{version} . " from " . $data->{modify_time} ."\n" if $DEBUG;
return $data->{version};
}
# }}}
# apply_schema_upgrade($hash) {{{
#
# Actually applying a schema upgrade based on a single upgrade indexed by
# index_schema_upgrades()
#
sub apply_schema_upgrade($)
{
my $data = shift;
print "Applying schema update: " . $data->{name} ."\n";
print "SQL statements from: " . $data->{file} ."\n";
my $sql = $data->{content};
# removing comments
$sql =~ s/-- .*//g;
# splitting queries
my @queries = split /[\n\s\t]*\;[\n\s\t]+/s, $sql;
my $dbh = get_database;
$dbh->begin_work;
for my $query (@queries) {
if ($DEBUG) {
print $query . "\n";
print '-' x 80 . "\n";
}
unless($dbh->do($query)) {
$dbh->rollback;
bail "Schema upgrade query failed: " . $DBI::errstr
. "\n\n$query";
}
}
$dbh->commit;
}
# }}}
sub main()
{
parse_environment_options;
parse_options;
my $version = get_current_schema;
my $upgrades = find_schema_upgrades($version);
my @up = keys %$upgrades;
if (scalar(@up) == 0) {
print "No pending upgrades.\n";
exit(1) if $check == 1;
exit(0);
} else {
printf "%d pending upgrades: %s\n", scalar(@up), join(', ', @up);
exit(0) if $check == 1;
for my $version (@up) {
apply_schema_upgrade($upgrades->{$version})
}
print "All done.\n";
exit(0);
}
}
main;
# vi: ts=4 sw=2 expandtab foldmethod=marker :
......@@ -27,5 +27,27 @@ class icinga2_testing::helpers::icinga2::ido_mysql(
],
}
Package['icinga2-ido-mysql'] -> Exec['import icinga2 mysql schema'] ~> Service['icinga2']
Package['icinga2-ido-mysql'] -> Exec['import icinga2 mysql schema'] ~> Class['::icinga2::service']
file { 'icinga2-schema-upgrade':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
path => '/usr/local/bin/icinga2-schema-upgrade',
content => file('icinga2_testing/icinga2-schema-upgrade'),
} ->
exec { 'icinga2-schema-upgrade':
path => $::path,
environment => [
"IDO_DATABASE=$ido_dbname",
"IDO_USERNAME=$ido_username",
"IDO_PASSWORD=$ido_password",
],
logoutput => true,
command => '/usr/local/bin/icinga2-schema-upgrade',
onlyif => '/usr/local/bin/icinga2-schema-upgrade --check',
require => Exec['import icinga2 mysql schema'],
} ~> Class['::icinga2::service']
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment