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/>.
*/
2016-05-23 13:23:18 +03:00
# include <talloc.h>
2015-11-23 08:18:16 +03:00
# include "replace.h"
# include "system/network.h"
# 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
# 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 ,
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 - > noiptakeover =
talloc_zero_array ( ipalloc_state ,
bool ,
ipalloc_state - > num ) ;
if ( ipalloc_state - > noiptakeover = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Out of memory \n " ) ) ;
goto fail ;
}
ipalloc_state - > noiphost =
talloc_zero_array ( ipalloc_state ,
bool ,
ipalloc_state - > num ) ;
if ( ipalloc_state - > noiphost = = NULL ) {
DEBUG ( DEBUG_ERR , ( __location__ " Out of memory \n " ) ) ;
goto fail ;
}
ipalloc_state - > algorithm = algorithm ;
ipalloc_state - > no_ip_failback = no_ip_failback ;
ipalloc_state - > force_rebalance_nodes = force_rebalance_nodes ;
return ipalloc_state ;
fail :
talloc_free ( ipalloc_state ) ;
return NULL ;
}
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 ;
}
if ( this_ip - > pnn = = - 1 ) {
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
{
int i , j ;
struct public_ip_list * ip_list ;
struct ctdb_public_ip_list * public_ips ;
struct trbt_tree * ip_tree ;
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 {
tmp_ip - > pnn = - 1 ;
}
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 ;
trbt_traversearray32 ( ip_tree , IP_KEYLEN , getips_count_callback , & ip_list ) ;
talloc_free ( ip_tree ) ;
return ip_list ;
}
2016-06-22 06:18:04 +03:00
static bool all_nodes_are_disabled ( struct ctdb_node_map * nodemap )
{
int i ;
for ( i = 0 ; i < nodemap - > num ; i + + ) {
if ( ! ( nodemap - > node [ i ] . flags &
( NODE_FLAGS_INACTIVE | NODE_FLAGS_DISABLED ) ) ) {
/* Found one completely healthy node */
return false ;
}
}
return true ;
}
/* Set internal flags for IP allocation:
* Clear ip flags
* Set NOIPTAKOVER ip flags from per - node NoIPTakeover tunable
* Set NOIPHOST ip flag for each INACTIVE node
* if all nodes are disabled :
* Set NOIPHOST ip flags from per - node NoIPHostOnAllDisabled tunable
* else
* Set NOIPHOST ip flags for disabled nodes
*/
void ipalloc_set_node_flags ( struct ipalloc_state * ipalloc_state ,
struct ctdb_node_map * nodemap ,
uint32_t * tval_noiptakeover ,
uint32_t * tval_noiphostonalldisabled )
{
int i ;
for ( i = 0 ; i < nodemap - > num ; i + + ) {
/* Can not take IPs on node with NoIPTakeover set */
if ( tval_noiptakeover [ i ] ! = 0 ) {
ipalloc_state - > noiptakeover [ i ] = true ;
}
/* Can not host IPs on INACTIVE node */
if ( nodemap - > node [ i ] . flags & NODE_FLAGS_INACTIVE ) {
ipalloc_state - > noiphost [ i ] = true ;
}
}
if ( all_nodes_are_disabled ( nodemap ) ) {
/* If all nodes are disabled, can not host IPs on node
* with NoIPHostOnAllDisabled set
*/
for ( i = 0 ; i < nodemap - > num ; i + + ) {
if ( tval_noiphostonalldisabled [ i ] ! = 0 ) {
ipalloc_state - > noiphost [ i ] = true ;
}
}
} else {
/* If some nodes are not disabled, then can not host
* IPs on DISABLED node
*/
for ( i = 0 ; i < nodemap - > num ; i + + ) {
if ( nodemap - > node [ i ] . flags & NODE_FLAGS_DISABLED ) {
ipalloc_state - > noiphost [ i ] = 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 )
{
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 ) {
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 ;
}
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
or - 1 if there is no node that can cover this ip
*/
2016-06-22 07:22:02 +03:00
return ( ret ? ipalloc_state - > all_ips : NULL ) ;
2015-11-23 08:18:16 +03:00
}