ctdb-build: Split protocol-util as a separate subsystem
[metze/samba/wip.git] / ctdb / server / ipalloc_common.c
1 /*
2    ctdb ip takeover code
3
4    Copyright (C) Ronnie Sahlberg  2007
5    Copyright (C) Andrew Tridgell  2007
6    Copyright (C) Martin Schwenke  2011
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "replace.h"
23 #include "system/network.h"
24
25 #include "ctdb_private.h"
26
27 #include "lib/util/time.h"
28
29 #include "lib/util/debug.h"
30 #include "common/logging.h"
31
32 #include "common/common.h"
33 #include "common/rb_tree.h"
34
35 #include "protocol/protocol_util.h"
36
37 #include "server/ipalloc_private.h"
38
39 #define TAKEOVER_TIMEOUT() timeval_current_ofs(ctdb->tunable.takeover_timeout,0)
40
41 /* Given a physical node, return the number of
42    public addresses that is currently assigned to this node.
43 */
44 int node_ip_coverage(int32_t pnn, struct public_ip_list *ips)
45 {
46         int num=0;
47
48         for (;ips;ips=ips->next) {
49                 if (ips->pnn == pnn) {
50                         num++;
51                 }
52         }
53         return num;
54 }
55
56
57 /* Can the given node host the given IP: is the public IP known to the
58  * node and is NOIPHOST unset?
59 */
60 static bool can_node_host_ip(struct ipalloc_state *ipalloc_state,
61                              int32_t pnn,
62                              struct public_ip_list *ip)
63 {
64         if (bitmap_query(ipalloc_state->noiphost, pnn)) {
65                 return false;
66         }
67
68         return bitmap_query(ip->available_on, pnn);
69 }
70
71 bool can_node_takeover_ip(struct ipalloc_state *ipalloc_state,
72                           int32_t pnn,
73                           struct public_ip_list *ip)
74 {
75         if (ipalloc_state->no_ip_takeover) {
76                 return false;
77         }
78
79         return can_node_host_ip(ipalloc_state, pnn, ip);
80 }
81
82 /* search the node lists list for a node to takeover this ip.
83    pick the node that currently are serving the least number of ips
84    so that the ips get spread out evenly.
85 */
86 int find_takeover_node(struct ipalloc_state *ipalloc_state,
87                        struct public_ip_list *ip)
88 {
89         int pnn, min=0, num;
90         int i, numnodes;
91
92         numnodes = ipalloc_state->num;
93         pnn    = -1;
94         for (i=0; i<numnodes; i++) {
95                 /* verify that this node can serve this ip */
96                 if (!can_node_takeover_ip(ipalloc_state, i, ip)) {
97                         /* no it couldnt   so skip to the next node */
98                         continue;
99                 }
100
101                 num = node_ip_coverage(i, ipalloc_state->all_ips);
102                 /* was this the first node we checked ? */
103                 if (pnn == -1) {
104                         pnn = i;
105                         min  = num;
106                 } else {
107                         if (num < min) {
108                                 pnn = i;
109                                 min  = num;
110                         }
111                 }
112         }
113         if (pnn == -1) {
114                 DEBUG(DEBUG_WARNING,(__location__ " Could not find node to take over public address '%s'\n",
115                                      ctdb_sock_addr_to_string(ipalloc_state,
116                                                               &ip->addr)));
117
118                 return -1;
119         }
120
121         ip->pnn = pnn;
122         return 0;
123 }
124
125 uint32_t *ip_key(ctdb_sock_addr *ip)
126 {
127         static uint32_t key[IP_KEYLEN];
128
129         bzero(key, sizeof(key));
130
131         switch (ip->sa.sa_family) {
132         case AF_INET:
133                 key[3]  = htonl(ip->ip.sin_addr.s_addr);
134                 break;
135         case AF_INET6: {
136                 uint32_t *s6_a32 = (uint32_t *)&(ip->ip6.sin6_addr.s6_addr);
137                 key[0]  = htonl(s6_a32[0]);
138                 key[1]  = htonl(s6_a32[1]);
139                 key[2]  = htonl(s6_a32[2]);
140                 key[3]  = htonl(s6_a32[3]);
141                 break;
142         }
143         default:
144                 DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", ip->sa.sa_family));
145                 return key;
146         }
147
148         return key;
149 }
150
151 /* Allocate any unassigned IPs just by looping through the IPs and
152  * finding the best node for each.
153  */
154 void basic_allocate_unassigned(struct ipalloc_state *ipalloc_state)
155 {
156         struct public_ip_list *t;
157
158         /* loop over all ip's and find a physical node to cover for
159            each unassigned ip.
160         */
161         for (t = ipalloc_state->all_ips; t != NULL; t = t->next) {
162                 if (t->pnn == -1) {
163                         if (find_takeover_node(ipalloc_state, t)) {
164                                 DEBUG(DEBUG_WARNING,
165                                       ("Failed to find node to cover ip %s\n",
166                                        ctdb_sock_addr_to_string(ipalloc_state,
167                                                                 &t->addr)));
168                         }
169                 }
170         }
171 }
172
173 void unassign_unsuitable_ips(struct ipalloc_state *ipalloc_state)
174 {
175         struct public_ip_list *t;
176
177         /* verify that the assigned nodes can serve that public ip
178            and set it to -1 if not
179         */
180         for (t = ipalloc_state->all_ips; t != NULL; t = t->next) {
181                 if (t->pnn == -1) {
182                         continue;
183                 }
184                 if (!can_node_host_ip(ipalloc_state, t->pnn, t) != 0) {
185                         /* this node can not serve this ip. */
186                         DEBUG(DEBUG_DEBUG,("Unassign IP: %s from %d\n",
187                                            ctdb_sock_addr_to_string(ipalloc_state,
188                                                                     &t->addr),
189                                            t->pnn));
190                         t->pnn = -1;
191                 }
192         }
193 }