From 507899d4e40ee91027c44d08b88c28a5d714d87e Mon Sep 17 00:00:00 2001 From: rettenbe Date: Fri, 8 May 2009 13:05:22 +0000 Subject: [PATCH] Avoiding a race condition during server-server registration process. git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@13635 594d385d-05f5-0310-b6e9-bd551577e9d8 --- gosa-si/gosa-si-server | 118 +++++++++++++++------ gosa-si/server/events/server_server_com.pm | 27 +++-- 2 files changed, 105 insertions(+), 40 deletions(-) diff --git a/gosa-si/gosa-si-server b/gosa-si/gosa-si-server index 079e86761..67d21a770 100755 --- a/gosa-si/gosa-si-server +++ b/gosa-si/gosa-si-server @@ -154,7 +154,7 @@ my @job_queue_col_names = ("id INTEGER PRIMARY KEY", our $known_server_db; our $known_server_tn = "known_server"; my $known_server_file_name; -my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)"); +my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)", "update_time VARCHAR(14)"); # holds all registrated clients our $known_clients_db; @@ -1080,15 +1080,26 @@ sub msg_to_decrypt { } # an error occurred if(( !$msg ) || ( !$msg_hash ) || ( !$module )){ - # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client - # could not understand a msg from its server the client cause a re-registering process - daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}. - "' to cause a re-registering of the client if necessary", 3); - my $remote_ip = $heap->{'remote_ip'}; - my $remote_port = $heap->{'remote_port'}; - my $ping_msg = "
gosa_ping
$server_address$msg_source
"; - my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id); - + # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client + # or a server. In case of a client, send a ping. If the client could not understand a msg from its + # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db + # and trigger a re-registering process for servers + if (defined $msg_source && $msg_source =~ /^\d+:$server_port$/) + { + daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3); + my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'"; + daemon_log("$session_id DEBUG: $update_statement", 7); + my $upadte_res = $known_server_db->exec_statement($update_statement); + $kernel->yield("register_at_foreign_servers"); + } + else + { + daemon_log("$session_id WARNING: Cannot understand incoming msg from client '".$heap->{remote_ip}."'. Send ping-msg to cause a re-registering of the client if necessary", 3); + my $remote_ip = $heap->{'remote_ip'}; + my $remote_port = $heap->{'remote_port'}; + my $ping_msg = "
gosa_ping
$server_address$msg_source
"; + my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id); + } $error++; } @@ -3140,54 +3151,93 @@ sub cleanup_and_extract { sub register_at_foreign_servers { my ($kernel) = $_[KERNEL]; - # hole alle bekannten server aus known_server_db - my $server_sql = "SELECT * FROM $known_server_tn"; - my $server_res = $known_server_db->exec_statement($server_sql); + # Set current delay of register_at_foreign_servers to 0 - # no entries in known_server_db - if (not ref(@$server_res[0]) eq "ARRAY") { - # TODO - } + # Update status and update-time of all si-server with expired update_time and + # block them for race conditional registration processes of other si-servers. + my $act_time = &get_time(); + my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time "; + &daemon_log("0 DEBUG: $block_statement", 5); + my $block_res = $known_server_db->exec_statement($block_statement); + + # Fetch all si-server from db where update_time is younger than act_time + my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'"; + &daemon_log("0 DEBUG: $fetch_statement", 5); + my $fetch_res = $known_server_db->exec_statement($fetch_statement); - # detect already connected clients + # Detect already connected clients. Will be added to registration msg later. my $client_sql = "SELECT * FROM $known_clients_tn"; my $client_res = $known_clients_db->exec_statement($client_sql); - # send my server details to all other gosa-si-server within the network - foreach my $hit (@$server_res) { + # Send registration messag to all fetched si-server + foreach my $hit (@$fetch_res) { my $hostname = @$hit[0]; my $hostkey = &create_passwd; - # add already connected clients to registration message + # Add already connected clients to registration message my $myhash = &create_xml_hash('new_server', $server_address, $hostname); &add_content2xml_hash($myhash, 'key', $hostkey); map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res); - # add locally loaded gosa-si modules to registration message + # Add locally loaded gosa-si modules to registration message my $loaded_modules = {}; while (my ($package, $pck_info) = each %$known_modules) { - next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH'))); - foreach my $act_module (keys(%{@$pck_info[2]})) { - $loaded_modules->{$act_module} = ""; - } - } - + next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH'))); + foreach my $act_module (keys(%{@$pck_info[2]})) { + $loaded_modules->{$act_module} = ""; + } + } map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules)); - # add macaddress to registration message + # Add macaddress to registration message my ($host_ip, $host_port) = split(/:/, $hostname); my $local_ip = &get_local_ip_for_remote_ip($host_ip); my $network_interface= &get_interface_for_ip($local_ip); my $host_mac = &get_mac_for_interface($network_interface); &add_content2xml_hash($myhash, 'macaddress', $host_mac); - # build registration message and send it + # Build registration message and send it my $foreign_server_msg = &create_xml_string($myhash); my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); } - - $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); - return; + + + # After n sec perform a check of all server registration processes + $kernel->delay_set("control_server_registration", 2); + + return; +} + + +sub control_server_registration { + my ($kernel) = $_[KERNEL]; + + # Check if all registration processes succeed or not + my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'"; + &daemon_log("0 DEBUG $select_statement", 5); + my $select_res = $known_server_db->exec_statement($select_statement); + + # If at least one registration process failed, maybe in case of a race condition + # with a foreign registration process + if (@$select_res > 0) + { + # Release block statement 'new_server' to make the server accessible + # for foreign registration processes + my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'"; + &daemon_log("0 DEBUG: $update_statement", 5); + my $update_res = $known_server_db->exec_statement($update_statement); + + # Set a random delay to avoid the registration race condition + my $new_foreign_servers_register_delay = int(rand(4))+1; + $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay); + } + # If all registration processes succeed + else + { + $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); + } + + return; } @@ -3441,6 +3491,7 @@ foreach my $foreign_server (@foreign_server_list) { hostkey=>"none", loaded_modules => "none", timestamp=>$cur_timestamp, + update_time=>'19700101000000', } ); } @@ -3488,6 +3539,7 @@ POE::Session->create( inline_states => { _start => \&session_start, register_at_foreign_servers => \®ister_at_foreign_servers, + control_server_registration => \&control_server_registration, sig_handler => \&sig_handler, next_task => \&next_task, task_result => \&handle_task_result, diff --git a/gosa-si/server/events/server_server_com.pm b/gosa-si/server/events/server_server_com.pm index 06e87cf2e..66eaf77fa 100644 --- a/gosa-si/server/events/server_server_com.pm +++ b/gosa-si/server/events/server_server_com.pm @@ -152,12 +152,23 @@ sub new_server { my @clients = exists $msg_hash->{'client'} ? @{$msg_hash->{'client'}} : qw(); my @loaded_modules = exists $msg_hash->{'loaded_modules'} ? @{$msg_hash->{'loaded_modules'}} : qw(); - # sanity check + # Ignor message if I'm already within a registration process for server $source + my $check_statement = "SELECT * FROM $main::known_server_tn WHERE status='new_server' AND hostname='$source'"; + &main::daemon_log("$session_id DEBUG $check_statement", 7); + my $check_res = $main::known_server_db->select_dbentry($check_statement); + my $blocking_process = keys(%$check_res); + if ($blocking_process) + { + return; + } + + # Sanity check if (ref $key eq 'HASH') { &main::daemon_log("$session_id ERROR: 'new_server'-message from host '$source' contains no key!", 1); return; } - # add foreign server to known_server_db + # Add foreign server to known_server_db + my $new_update_time = &calc_timestamp(&get_time(), 'plus', $main::foreign_servers_register_delay); my $func_dic = {table=>$main::known_server_tn, primkey=>['hostname'], hostname => $source, @@ -166,6 +177,7 @@ sub new_server { hostkey => $key, loaded_modules => join(',', @loaded_modules), timestamp=>&get_time(), + update_time=>$new_update_time, }; my $res = $main::known_server_db->add_dbentry($func_dic); if (not $res == 0) { @@ -244,8 +256,9 @@ sub confirm_new_server { my @clients = exists $msg_hash->{'client'} ? @{$msg_hash->{'client'}} : qw(); my @loaded_modules = exists $msg_hash->{'loaded_modules'} ? @{$msg_hash->{'loaded_modules'}} : qw(); + my $new_update_time = &calc_timestamp(&get_time(), 'plus', $main::foreign_servers_register_delay); my $sql = "UPDATE $main::known_server_tn". - " SET status='$header', hostkey='$key', loaded_modules='".join(",",@loaded_modules)."', macaddress='$mac'". + " SET status='$header', hostkey='$key', loaded_modules='".join(",",@loaded_modules)."', macaddress='$mac', update_time='$new_update_time'". " WHERE hostname='$source'"; my $res = $main::known_server_db->update_dbentry($sql); @@ -259,10 +272,10 @@ sub confirm_new_server { push(@sql_list, $del_sql); my $sql = "INSERT INTO $main::foreign_clients_tn VALUES (" - ."'".$client_details[0]."'," # hostname - ."'".$client_details[1]."'," # macaddress - ."'".$source."'," # regserver - ."'".&get_time()."')"; # timestamp + ."'".$client_details[0]."'," # hostname + ."'".$client_details[1]."'," # macaddress + ."'".$source."'," # regserver + ."'".&get_time()."')"; # timestamp push(@sql_list, $sql); } if (@sql_list) { -- 2.30.2