ctdb-daemon: Move ctdb_init() to the only place it is used
[vlendec/samba-autobuild/.git] / ctdb / server / ipalloc.c
index 295671a924f53bddc03e242ea7f5eff2fe1cb33f..caa50224156eb7a1b8eea5c6e03102c6a7d6fee8 100644 (file)
 #include "common/logging.h"
 #include "common/rb_tree.h"
 
+#include "protocol/protocol_util.h"
+
 #include "server/ipalloc_private.h"
 
+/* 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_takeover,
+                  bool no_ip_failback,
+                  bool no_ip_host_on_all_disabled,
+                  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->noiphost = bitmap_talloc(ipalloc_state,
+                                               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_takeover = no_ip_takeover;
+       ipalloc_state->no_ip_failback = no_ip_failback;
+       ipalloc_state->no_ip_host_on_all_disabled = no_ip_host_on_all_disabled;
+       ipalloc_state->force_rebalance_nodes = force_rebalance_nodes;
+
+       return ipalloc_state;
+fail:
+       talloc_free(ipalloc_state);
+       return NULL;
+}
+
 static void *add_ip_callback(void *parm, void *data)
 {
        struct public_ip_list *this_ip = parm;
@@ -62,8 +102,7 @@ static int getips_count_callback(void *param, void *data)
  * merged list of all public addresses needs to be built so that IP
  * allocation can be done. */
 static struct public_ip_list *
-create_merged_ip_list(struct ipalloc_state *ipalloc_state,
-                     struct ctdb_public_ip_list *known_ips)
+create_merged_ip_list(struct ipalloc_state *ipalloc_state)
 {
        int i, j;
        struct public_ip_list *ip_list;
@@ -72,14 +111,14 @@ create_merged_ip_list(struct ipalloc_state *ipalloc_state,
 
        ip_tree = trbt_create(ipalloc_state, 0);
 
-       if (known_ips == NULL) {
+       if (ipalloc_state->known_public_ips == NULL) {
                DEBUG(DEBUG_ERR, ("Known public IPs not set\n"));
                return NULL;
        }
 
        for (i=0; i < ipalloc_state->num; i++) {
 
-               public_ips = &known_ips[i];
+               public_ips = &ipalloc_state->known_public_ips[i];
 
                for (j=0; j < public_ips->num; j++) {
                        struct public_ip_list *tmp_ip;
@@ -117,16 +156,113 @@ create_merged_ip_list(struct ipalloc_state *ipalloc_state,
        return ip_list;
 }
 
-bool ipalloc_set_public_ips(struct ipalloc_state *ipalloc_state,
+static bool populate_bitmap(struct ipalloc_state *ipalloc_state)
+{
+       struct public_ip_list *ip = NULL;
+       int i, j;
+
+       for (ip = ipalloc_state->all_ips; ip != NULL; ip = ip->next) {
+
+               ip->known_on = bitmap_talloc(ip, ipalloc_state->num);
+               if (ip->known_on == NULL) {
+                       return false;
+               }
+
+               ip->available_on = bitmap_talloc(ip, ipalloc_state->num);
+               if (ip->available_on == NULL) {
+                       return false;
+               }
+
+               for (i = 0; i < ipalloc_state->num; i++) {
+                       struct ctdb_public_ip_list *known =
+                               &ipalloc_state->known_public_ips[i];
+                       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)) {
+                                       bitmap_set(ip->available_on, i);
+                                       break;
+                               }
+                       }
+
+                       /* 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;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+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 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)
+{
+       int i;
+       bool all_disabled = all_nodes_are_disabled(nodemap);
+
+       for (i=0;i<nodemap->num;i++) {
+               /* Can not host IPs on INACTIVE node */
+               if (nodemap->node[i].flags & NODE_FLAGS_INACTIVE) {
+                       bitmap_set(ipalloc_state->noiphost, i);
+               }
+
+               /* If node is disabled then it can only host IPs if
+                * all nodes are disabled and NoIPHostOnAllDisabled is
+                * unset
+                */
+               if (nodemap->node[i].flags & NODE_FLAGS_DISABLED) {
+                       if (!(all_disabled &&
+                             ipalloc_state->no_ip_host_on_all_disabled == 0)) {
+
+                               bitmap_set(ipalloc_state->noiphost, i);
+                       }
+               }
+       }
+}
+
+void ipalloc_set_public_ips(struct ipalloc_state *ipalloc_state,
                            struct ctdb_public_ip_list *known_ips,
                            struct ctdb_public_ip_list *available_ips)
 {
        ipalloc_state->available_public_ips = available_ips;
-
-       ipalloc_state->all_ips = create_merged_ip_list(ipalloc_state,
-                                                      known_ips);
-
-       return (ipalloc_state->all_ips != NULL);
+       ipalloc_state->known_public_ips = known_ips;
 }
 
 /* This can only return false if there are no available IPs *and*
@@ -136,17 +272,31 @@ bool ipalloc_set_public_ips(struct ipalloc_state *ipalloc_state,
 bool ipalloc_can_host_ips(struct ipalloc_state *ipalloc_state)
 {
        int i;
-       struct public_ip_list *ip_list;
-
+       bool have_ips = false;
 
-       for (ip_list = ipalloc_state->all_ips;
-            ip_list != NULL;
-            ip_list = ip_list->next) {
-               if (ip_list->pnn != -1) {
-                       return true;
+       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;
+                       have_ips = true;
+                       /* 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;
+                               }
+                       }
                }
        }
 
+       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.
+        */
        for (i=0; i < ipalloc_state->num; i++) {
                if (ipalloc_state->available_public_ips[i].num != 0) {
                        return true;
@@ -157,10 +307,19 @@ bool ipalloc_can_host_ips(struct ipalloc_state *ipalloc_state)
 }
 
 /* The calculation part of the IP allocation algorithm. */
-bool ipalloc(struct ipalloc_state *ipalloc_state)
+struct public_ip_list *ipalloc(struct ipalloc_state *ipalloc_state)
 {
        bool ret = false;
 
+       ipalloc_state->all_ips = create_merged_ip_list(ipalloc_state);
+       if (ipalloc_state->all_ips == NULL) {
+               return NULL;
+       }
+
+       if (!populate_bitmap(ipalloc_state)) {
+               return NULL;
+       }
+
        switch (ipalloc_state->algorithm) {
        case IPALLOC_LCP2:
                ret = ipalloc_lcp2(ipalloc_state);
@@ -177,5 +336,5 @@ bool ipalloc(struct ipalloc_state *ipalloc_state)
           or -1 if there is no node that can cover this ip
        */
 
-       return ret;
+       return (ret ? ipalloc_state->all_ips : NULL);
 }