diff --git a/helper/compare_dbs.pl b/helper/compare_dbs.pl index 044da2b..225f8b1 100755 --- a/helper/compare_dbs.pl +++ b/helper/compare_dbs.pl @@ -23,17 +23,23 @@ my @diff_exceptions = qw( views/ldap/ldap_entries/view_definition tables/mysql/.+/create_options ); -my @missing_sp1_exceptions = qw(); -my @missing_sp2_exceptions = qw(); +my @local_missing_exceptions = qw(); +my @remote_missing_exceptions = qw(); + +my $exception_list = { + default_diff_exceptions => \@diff_exceptions, + default_local_missing_exceptions => \@local_missing_exceptions, + default_remote_missing_exceptions => \@remote_missing_exceptions, +}; my $credentials_file = '/etc/mysql/sipwise_extra.cnf'; my $argv = { formatter => '', schemes => '', + host_db1 => '', pass_db1 => '', - pass_db2 => '', user_db1 => '', - user_db2 => '', + port_db1 => '', }; get_options(); @@ -46,7 +52,7 @@ SELECT CREATE_OPTIONS, TABLE_NAME AS key_col FROM information_schema.TABLES - WHERE TABLE_SCHEMA = DATABASE() + WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME __SQL__ @@ -68,8 +74,8 @@ FROM information_schema.COLUMNS c INNER JOIN information_schema.TABLES t ON c.TABLE_NAME = t.TABLE_NAME WHERE t.TABLE_TYPE = 'BASE TABLE' - AND c.TABLE_SCHEMA = DATABASE() - AND t.TABLE_SCHEMA = DATABASE() + AND c.TABLE_SCHEMA = t.TABLE_SCHEMA + AND t.TABLE_SCHEMA = ? ORDER BY c.TABLE_NAME, c.COLUMN_NAME __SQL__ , @@ -87,7 +93,7 @@ SELECT INDEX_TYPE, CONCAT(TABLE_NAME, '/', INDEX_NAME, '/', SEQ_IN_INDEX) AS key_col FROM information_schema.STATISTICS -WHERE TABLE_SCHEMA = DATABASE() +WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME, INDEX_NAME, COLUMN_NAME __SQL__ , @@ -101,14 +107,14 @@ SELECT rc.DELETE_RULE, cu.REFERENCED_COLUMN_NAME, cu.COLUMN_NAME, - CONCAT(rc.CONSTRAINT_NAME, '/', rc.TABLE_NAME, '/', + CONCAT( rc.TABLE_NAME, '/', rc.CONSTRAINT_NAME, '/', cu.COLUMN_NAME, '/', rc.REFERENCED_TABLE_NAME, '/', cu.REFERENCED_COLUMN_NAME) AS key_col FROM information_schema.REFERENTIAL_CONSTRAINTS rc LEFT JOIN information_schema.KEY_COLUMN_USAGE cu ON (rc.CONSTRAINT_NAME=cu.CONSTRAINT_NAME AND rc.CONSTRAINT_SCHEMA=cu.CONSTRAINT_SCHEMA) -WHERE rc.CONSTRAINT_SCHEMA = DATABASE() +WHERE rc.CONSTRAINT_SCHEMA = ? ORDER BY CONSTRAINT_NAME, rc.TABLE_NAME, cu.COLUMN_NAME __SQL__ , @@ -123,7 +129,7 @@ SELECT EVENT_OBJECT_TABLE, CONCAT(TRIGGER_NAME, '/', EVENT_OBJECT_TABLE) AS key_col FROM information_schema.TRIGGERS -WHERE TRIGGER_SCHEMA = DATABASE() +WHERE TRIGGER_SCHEMA = ? ORDER BY EVENT_OBJECT_TABLE, TRIGGER_NAME __SQL__ , @@ -133,7 +139,7 @@ SELECT TABLE_NAME AS key_col, VIEW_DEFINITION FROM information_schema.VIEWS -WHERE TABLE_SCHEMA = DATABASE() +WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME __SQL__ , @@ -144,47 +150,38 @@ SELECT ROUTINE_DEFINITION, ROUTINE_TYPE FROM information_schema.ROUTINES -WHERE ROUTINE_SCHEMA = DATABASE() +WHERE ROUTINE_SCHEMA = ? __SQL__ , }; my @objs_list = qw( tables columns indexes constraints triggers views routines ); -if ($argv->{schemes} eq '') { - warn " --schemes is empty\n\n"; - print_usage(); - exit 1; -} - +my $schema1 = "DBI:mysql:;host=$argv->{host_db1};port=$argv->{port_db1};mysql_read_default_file=$credentials_file"; my $dbh1 = DBI->connect( - "DBI:mysql:;host=sp1;mysql_read_default_file=$credentials_file", + $schema1, $argv->{user_db1}, $argv->{pass_db1}, { RaiseError => 1 } ) or - croak("Can't connect to db1: DBI:mysql:;host=sp1;mysql_read_default_file=$credentials_file, $argv->{user_db1}, $argv->{pass_db1} "); - -my $dbh2 = DBI->connect( - "DBI:mysql:;host=sp2;mysql_read_default_file=$credentials_file", - $argv->{user_db2}, - $argv->{pass_db2}, - { RaiseError => 1 } ) or - croak("Can't connect to db2: DBI:mysql:;host=sp2;mysql_read_default_file=$credentials_file, $argv->{user_db2}, $argv->{pass_db2} "); + croak("Can't connect to local db: $schema1"); +my $masters = $dbh1->selectall_hashref('SHOW ALL SLAVES STATUS', 'Master_Host'); my $res = []; my $exit = 0; -foreach my $schema ( split( / /, $argv->{schemes} ) ) { - $dbh1->do("USE $schema"); - $dbh2->do("USE $schema"); - - my ($sth1, $sth2); - my ($struct1, $struct2); - foreach my $obj ( @objs_list ) { - $struct1 = $dbh1->selectall_hashref( $queries->{$obj}, 'key_col' ); - $struct2 = $dbh2->selectall_hashref( $queries->{$obj}, 'key_col' ); - - print_diff($struct1, $struct2, $obj, $res, $schema); +foreach my $master ( keys( %{$masters} ) ) { + $exception_list->{local_missing_exceptions} = $exception_list->{default_local_missing_exceptions}; + $exception_list->{remote_missing_exceptions} = $exception_list->{remote_missing_exceptions}; + + my $master_port = $masters->{$master}->{Master_Port}; + my $schemes = $masters->{$master}->{Replicate_Wild_Do_Table}; + my $ignore_tables = $masters->{$master}->{Replicate_Ignore_Table}; + foreach my $entry ( split(/,/, $ignore_tables) ) { + foreach my $obj (@objs_list) { + push( @{$exception_list->{local_missing_exceptions}}, "$obj/" . $entry =~ s/\./\//r ); + } } + + compare_schemes($master, $master_port, $schemes); } if ( $argv->{formatter} eq 'tap' ) { @@ -201,28 +198,54 @@ sub get_options { GetOptions( 'formatter=s' => \$argv->{'formatter'}, 'schemes=s' => \$argv->{'schemes'}, + 'host-db1=s' => \$argv->{'host_db1'}, 'user-db1=s' => \$argv->{'user_db1'}, 'pass-db1=s' => \$argv->{'pass_db1'}, - 'user-db2=s' => \$argv->{'user_db2'}, - 'pass-db2=s' => \$argv->{'pass_db2'}, + 'port-db1=s' => \$argv->{'port_db1'}, 'help|h' => sub{ print_usage(); exit(0); }, ); } +sub compare_schemes { + my ($host, $port, $schemes) = @_; + my $schema2 = "DBI:mysql:;host=$host;port=$port;mysql_read_default_file=$credentials_file"; + my $dbh2 = DBI->connect( + $schema2, + '', + '', + { RaiseError => 1 } ) or + croak("Can't connect to remote db: $schema2"); + + my $connection = "$host:$port"; + my @schemes = map { s/\..*//r } split(/,/, $schemes); + + foreach my $schema (@schemes) { + my ($sth1, $sth2); + my ($struct1, $struct2); + foreach my $obj (@objs_list) { + $struct1 = $dbh1->selectall_hashref( $queries->{$obj}, 'key_col', undef, $schema ); + $struct2 = $dbh2->selectall_hashref( $queries->{$obj}, 'key_col', undef, $schema ); + + print_diff($struct1, $struct2, $obj, $res, $schema, $connection); + } + } +} + sub print_usage { my $usage =<<__USAGE__ Usage: compare_db.pl [] -This script compares two databases by structure and prints result. +This script compares structure of schemes on local mysql instance with +all configured replica instances and prints result. Options: --formatter=[tap] The format of output. Supported values: tap - print in a TAP format. --schemes= List of schemes which should be compared. + --host-db1= Host of the 1st schema --user-db1= User of the 1st schema --pass-db1= Password of the 1st schema - --user-db2= User of the 2nd schema - --pass-db2= Password of the 2nd schema. + --port-db1= Port of the 1st schema -h, --help Print this message and exit. __USAGE__ ; @@ -247,18 +270,21 @@ sub is_exception { } sub print_diff { - my ($obj1, $obj2, $object_name, $result, $schema) = @_; + my ($obj1, $obj2, $object_name, $result, $schema, $connection) = @_; + + my $schema1_name = "$argv->{host_db1}:$argv->{port_db1}"; + my $schema2_name = $connection; foreach my $key ( sort( keys( %{$obj1} ) ) ) { unless ( exists($obj2->{$key}) ) { - next if ( is_exception(\@missing_sp2_exceptions, $object_name, $schema, $key) ); - push( @{$result}, "Element: " . lc("$object_name/$schema/$key") . " is missing in Schema2" ); + next if ( is_exception($exception_list->{remote_missing_exceptions}, $object_name, $schema, $key) ); + push( @{$result}, "Element: " . lc("$object_name/$schema/$key") . " is missing in $schema2_name" ); next; } foreach my $c_name ( sort( keys( %{ $obj1->{$key} } ) ) ) { unless ( exists($obj2->{$key}->{$c_name}) ) { - next if ( is_exception(\@missing_sp2_exceptions, $object_name, $schema, $key, $c_name) ); - push( @{$result}, "Element: " . lc("$object_name/$schema/$key/$c_name") . " is missing in Schema2" ); + next if ( is_exception($exception_list->{remote_missing_exceptions}, $object_name, $schema, $key, $c_name) ); + push( @{$result}, "Element: " . lc("$object_name/$schema/$key/$c_name") . " is missing in $schema2_name" ); next; } @@ -269,24 +295,24 @@ sub print_diff { $obj2->{$key}->{$c_name} = 'NULL' if ( ! defined($obj2->{$key}->{$c_name}) ); if ( $obj1->{$key}->{$c_name} ne $obj2->{$key}->{$c_name} ) { - next if ( is_exception(\@diff_exceptions, $object_name, $schema, $key, $c_name) ); + next if ( is_exception($exception_list->{diff_exceptions}, $object_name, $schema, $key, $c_name) ); push( @{$result}, "Element: " . lc("$object_name/$schema/$key/$c_name") . " are not equal:\n ---\n" - . " Schema1: $obj1->{$key}->{$c_name}\n" - . " Schema2: $obj2->{$key}->{$c_name}" ); + . " $schema1_name: $obj1->{$key}->{$c_name}\n" + . " $schema2_name: $obj2->{$key}->{$c_name}" ); } } } foreach my $key ( sort( keys( %{$obj2} ) ) ) { unless ( exists($obj1->{$key}) ) { - next if ( is_exception(\@missing_sp1_exceptions, $object_name, $schema, $key) ); - push( @{$result}, "Element: " . lc("$object_name/$schema/$key") . " is missing in Schema1" ); + next if ( is_exception($exception_list->{local_missing_exceptions}, $object_name, $schema, $key) ); + push( @{$result}, "Element: " . lc("$object_name/$schema/$key") . " is missing in $schema1_name" ); next; } foreach my $c_name ( sort( keys( %{ $obj2->{$key} } ) ) ) { unless ( exists($obj1->{$key}->{$c_name}) ) { - next if ( is_exception(\@missing_sp1_exceptions, $object_name, $schema, $key, $c_name) ); - push( @{$result}, "Element: ". lc("$object_name/$schema/$key/$c_name") ." is missing in Schema1" ); + next if ( is_exception($exception_list->{local_missing_exceptions}, $object_name, $schema, $key, $c_name) ); + push( @{$result}, "Element: ". lc("$object_name/$schema/$key/$c_name") ." is missing in $schema1_name" ); next; } } diff --git a/sbin/ngcp-mysql-compare-dbs b/sbin/ngcp-mysql-compare-dbs index bf82541..52d28ea 100755 --- a/sbin/ngcp-mysql-compare-dbs +++ b/sbin/ngcp-mysql-compare-dbs @@ -18,6 +18,13 @@ Options: __USAGE__ } +get_local_instances() { + local_instances+=("${NGCP_HOSTNAME}:3306") + if [[ "${NGCP_TYPE}" == 'carrier' && "${NGCP_IS_PROXY}" == 'yes' ]]; then + local_instances+=("${NGCP_HOSTNAME}:3308") + fi +} + FORMATTER='' args=$(getopt -n "$(basename "$0")" -o h -l help,formatter: -- "$@") @@ -43,11 +50,12 @@ while true; do esac done -DB_BACKUP_LIST='/etc/ngcp-backup-tools/db-backup.conf' -if [[ ! -r "${DB_BACKUP_LIST}" ]]; then - echo "Cannot read mandatory config file ${DB_BACKUP_LIST}" 2>&1 +NGCP_ROLES='/etc/default/ngcp-roles' +if [[ ! -r "${NGCP_ROLES}" ]]; then + echo "Cannot read mandatory config file ${NGCP_ROLES}" 2>&1 exit 1 fi +. "${NGCP_ROLES}" MYSQL_CREDENTIALS='/etc/mysql/sipwise_extra.cnf' if [[ ! -r "${MYSQL_CREDENTIALS}" ]]; then @@ -55,11 +63,16 @@ if [[ ! -r "${MYSQL_CREDENTIALS}" ]]; then exit 1 fi -. "${DB_BACKUP_LIST}" declare -a opts if [[ -n "${FORMATTER}" ]]; then opts+=(--formatter="${FORMATTER}") fi -/usr/share/ngcp-system-tests/compare_dbs.pl \ - --schemes="${NGCP_DB_BACKUP_FINAL_LIST[*]}" \ - "${opts[@]}" + +declare -a local_instances=() +get_local_instances +for local_mysql in "${local_instances[@]}"; do + /usr/share/ngcp-system-tests/compare_dbs.pl \ + --host-db1="${local_mysql%:*}" \ + --port-db1="${local_mysql#*:}" \ + "${opts[@]}" +done