2015-11-23 08:18:16 +03:00
/*
ctdb ip takeover code
Copyright ( C ) Ronnie Sahlberg 2007
Copyright ( C ) Andrew Tridgell 2007
Copyright ( C ) Martin Schwenke 2011
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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# include "replace.h"
# include "system/network.h"
2018-06-29 08:55:49 +03:00
# include <talloc.h>
2015-11-23 08:18:16 +03:00
# include "lib/util/debug.h"
# include "common/logging.h"
2016-05-23 13:23:18 +03:00
# include "common/rb_tree.h"
2015-11-23 08:18:16 +03:00
2017-09-04 09:00:48 +03:00
# include "protocol/protocol_util.h"
2016-11-18 08:08:13 +03:00
2015-11-23 08:18:16 +03:00
# include "server/ipalloc_private.h"
2016-05-23 07:28:33 +03:00
/* Initialise main ipalloc state and sub-structures */
struct ipalloc_state *
ipalloc_state_init ( TALLOC_CTX * mem_ctx ,
uint32_t num_nodes ,
enum ipalloc_algorithm algorithm ,
2016-12-10 11:39:11 +03:00
bool no_ip_takeover ,
2016-05-23 07:28:33 +03:00
bool no_ip_failback ,
uint32_t * force_rebalance_nodes )
{
struct ipalloc_state * ipalloc_state =
talloc_zero ( mem_ctx , struct ipalloc_state ) ;
if ( ipalloc_state = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Out of memory \n " ) ) ;
return NULL ;
}
ipalloc_state - > num = num_nodes ;
ipalloc_state - > algorithm = algorithm ;
2016-12-10 11:39:11 +03:00
ipalloc_state - > no_ip_takeover = no_ip_takeover ;
2016-05-23 07:28:33 +03:00
ipalloc_state - > no_ip_failback = no_ip_failback ;
ipalloc_state - > force_rebalance_nodes = force_rebalance_nodes ;
return ipalloc_state ;
}
2016-05-23 13:23:18 +03:00
static void * add_ip_callback ( void * parm , void * data )
{
struct public_ip_list * this_ip = parm ;
struct public_ip_list * prev_ip = data ;
if ( prev_ip = = NULL ) {
return parm ;
}
2019-05-30 08:40:42 +03:00
if ( this_ip - > pnn = = CTDB_UNKNOWN_PNN ) {
2016-05-23 13:23:18 +03:00
this_ip - > pnn = prev_ip - > pnn ;
}
return parm ;
}
static int getips_count_callback ( void * param , void * data )
{
struct public_ip_list * * ip_list = ( struct public_ip_list * * ) param ;
struct public_ip_list * new_ip = ( struct public_ip_list * ) data ;
new_ip - > next = * ip_list ;
* ip_list = new_ip ;
return 0 ;
}
/* Nodes only know about those public addresses that they are
* configured to serve and no individual node has a full list of all
* public addresses configured across the cluster . Therefore , a
* merged list of all public addresses needs to be built so that IP
* allocation can be done . */
static struct public_ip_list *
2016-09-12 08:57:23 +03:00
create_merged_ip_list ( struct ipalloc_state * ipalloc_state )
2016-05-23 13:23:18 +03:00
{
2019-05-30 08:37:38 +03:00
unsigned int i , j ;
2016-05-23 13:23:18 +03:00
struct public_ip_list * ip_list ;
struct ctdb_public_ip_list * public_ips ;
struct trbt_tree * ip_tree ;
2022-05-11 13:06:22 +03:00
int ret ;
2016-05-23 13:23:18 +03:00
ip_tree = trbt_create ( ipalloc_state , 0 ) ;
2016-09-12 08:57:23 +03:00
if ( ipalloc_state - > known_public_ips = = NULL ) {
2016-05-23 13:23:18 +03:00
DEBUG ( DEBUG_ERR , ( " Known public IPs not set \n " ) ) ;
return NULL ;
}
for ( i = 0 ; i < ipalloc_state - > num ; i + + ) {
2016-09-12 08:57:23 +03:00
public_ips = & ipalloc_state - > known_public_ips [ i ] ;
2016-05-23 13:23:18 +03:00
for ( j = 0 ; j < public_ips - > num ; j + + ) {
struct public_ip_list * tmp_ip ;
/* This is returned as part of ip_list */
tmp_ip = talloc_zero ( ipalloc_state , struct public_ip_list ) ;
if ( tmp_ip = = NULL ) {
DEBUG ( DEBUG_ERR ,
( __location__ " out of memory \n " ) ) ;
talloc_free ( ip_tree ) ;
return NULL ;
}
/* Do not use information about IP addresses hosted
* on other nodes , it may not be accurate */
if ( public_ips - > ip [ j ] . pnn = = i ) {
tmp_ip - > pnn = public_ips - > ip [ j ] . pnn ;
} else {
2019-05-30 08:40:42 +03:00
tmp_ip - > pnn = CTDB_UNKNOWN_PNN ;
2016-05-23 13:23:18 +03:00
}
tmp_ip - > addr = public_ips - > ip [ j ] . addr ;
tmp_ip - > next = NULL ;
trbt_insertarray32_callback ( ip_tree ,
IP_KEYLEN , ip_key ( & public_ips - > ip [ j ] . addr ) ,
add_ip_callback ,
tmp_ip ) ;
}
}
ip_list = NULL ;
2022-05-11 13:06:22 +03:00
ret = trbt_traversearray32 ( ip_tree , IP_KEYLEN , getips_count_callback , & ip_list ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Error traversing the IP tree. \n " ) ;
}
2016-05-23 13:23:18 +03:00
talloc_free ( ip_tree ) ;
return ip_list ;
}
2016-11-18 08:08:13 +03:00
static bool populate_bitmap ( struct ipalloc_state * ipalloc_state )
{
struct public_ip_list * ip = NULL ;
2019-05-30 08:37:38 +03:00
unsigned int i , j ;
2016-11-18 08:08:13 +03:00
for ( ip = ipalloc_state - > all_ips ; ip ! = NULL ; ip = ip - > next ) {
2017-02-07 07:23:36 +03:00
ip - > known_on = bitmap_talloc ( ip , ipalloc_state - > num ) ;
if ( ip - > known_on = = NULL ) {
return false ;
}
2016-12-02 06:21:59 +03:00
ip - > available_on = bitmap_talloc ( ip , ipalloc_state - > num ) ;
2016-11-18 08:08:13 +03:00
if ( ip - > available_on = = NULL ) {
return false ;
}
for ( i = 0 ; i < ipalloc_state - > num ; i + + ) {
2017-02-07 07:23:36 +03:00
struct ctdb_public_ip_list * known =
& ipalloc_state - > known_public_ips [ i ] ;
2016-11-18 08:08:13 +03:00
struct ctdb_public_ip_list * avail =
& ipalloc_state - > available_public_ips [ i ] ;
/* Check to see if "ip" is available on node "i" */
for ( j = 0 ; j < avail - > num ; j + + ) {
if ( ctdb_sock_addr_same_ip (
& ip - > addr , & avail - > ip [ j ] . addr ) ) {
2016-12-02 06:21:59 +03:00
bitmap_set ( ip - > available_on , i ) ;
2016-11-18 08:08:13 +03:00
break ;
}
}
2017-02-07 07:23:36 +03:00
/* Optimisation: available => known */
if ( bitmap_query ( ip - > available_on , i ) ) {
bitmap_set ( ip - > known_on , i ) ;
continue ;
}
/* Check to see if "ip" is known on node "i" */
for ( j = 0 ; j < known - > num ; j + + ) {
if ( ctdb_sock_addr_same_ip (
& ip - > addr , & known - > ip [ j ] . addr ) ) {
bitmap_set ( ip - > known_on , i ) ;
break ;
}
}
2016-11-18 08:08:13 +03:00
}
}
return true ;
}
2016-09-12 09:04:18 +03:00
void ipalloc_set_public_ips ( struct ipalloc_state * ipalloc_state ,
2016-05-23 13:23:18 +03:00
struct ctdb_public_ip_list * known_ips ,
struct ctdb_public_ip_list * available_ips )
{
ipalloc_state - > available_public_ips = available_ips ;
2016-09-12 08:41:06 +03:00
ipalloc_state - > known_public_ips = known_ips ;
2016-05-23 13:23:18 +03:00
}
2016-06-29 09:36:28 +03:00
/* This can only return false if there are no available IPs *and*
* there are no IP addresses currently allocated . If the latter is
* true then the cluster can clearly host IPs . . . just not necessarily
* right now . . . */
2016-06-21 14:16:27 +03:00
bool ipalloc_can_host_ips ( struct ipalloc_state * ipalloc_state )
{
2019-05-30 08:37:38 +03:00
unsigned int i ;
2016-09-12 08:51:58 +03:00
bool have_ips = false ;
2016-06-29 09:36:28 +03:00
2016-09-12 08:49:03 +03:00
for ( i = 0 ; i < ipalloc_state - > num ; i + + ) {
struct ctdb_public_ip_list * ips =
ipalloc_state - > known_public_ips ;
if ( ips [ i ] . num ! = 0 ) {
2019-05-30 08:37:38 +03:00
unsigned int j ;
2016-09-12 08:51:58 +03:00
have_ips = true ;
2016-09-12 08:49:03 +03:00
/* Succeed if an address is hosted on node i */
for ( j = 0 ; j < ips [ i ] . num ; j + + ) {
if ( ips [ i ] . ip [ j ] . pnn = = i ) {
return true ;
}
}
2016-06-29 09:36:28 +03:00
}
}
2016-06-21 14:16:27 +03:00
2016-09-12 08:51:58 +03:00
if ( ! have_ips ) {
return false ;
}
/* At this point there are known addresses but none are
* hosted . Need to check if cluster can now host some
* addresses .
*/
2016-06-21 14:16:27 +03:00
for ( i = 0 ; i < ipalloc_state - > num ; i + + ) {
if ( ipalloc_state - > available_public_ips [ i ] . num ! = 0 ) {
return true ;
}
}
return false ;
}
2015-11-23 08:18:16 +03:00
/* The calculation part of the IP allocation algorithm. */
2016-06-22 07:22:02 +03:00
struct public_ip_list * ipalloc ( struct ipalloc_state * ipalloc_state )
2015-11-23 08:18:16 +03:00
{
2016-01-14 13:20:21 +03:00
bool ret = false ;
2015-11-23 08:18:16 +03:00
2016-09-12 08:59:09 +03:00
ipalloc_state - > all_ips = create_merged_ip_list ( ipalloc_state ) ;
if ( ipalloc_state - > all_ips = = NULL ) {
return NULL ;
}
2016-11-18 08:08:13 +03:00
if ( ! populate_bitmap ( ipalloc_state ) ) {
return NULL ;
}
2015-11-23 08:18:16 +03:00
switch ( ipalloc_state - > algorithm ) {
case IPALLOC_LCP2 :
ret = ipalloc_lcp2 ( ipalloc_state ) ;
break ;
case IPALLOC_DETERMINISTIC :
ret = ipalloc_deterministic ( ipalloc_state ) ;
break ;
case IPALLOC_NONDETERMINISTIC :
ret = ipalloc_nondeterministic ( ipalloc_state ) ;
break ;
}
/* at this point ->pnn is the node which will own each IP
2019-05-30 08:40:42 +03:00
or CTDB_UNKNOWN_PNN if there is no node that can cover this ip
2015-11-23 08:18:16 +03:00
*/
2016-06-22 07:22:02 +03:00
return ( ret ? ipalloc_state - > all_ips : NULL ) ;
2015-11-23 08:18:16 +03:00
}