]>
Commit | Line | Data |
---|---|---|
9a1955a7 | 1 | /* |
406b689d | 2 | * Copyright (c) 2015-2020 Red Hat, Inc. |
9a1955a7 JF |
3 | * |
4 | * All rights reserved. | |
5 | * | |
6 | * Author: Jan Friesse (jfriesse@redhat.com) | |
7 | * | |
8 | * This software licensed under BSD license, the text of which follows: | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions are met: | |
12 | * | |
13 | * - Redistributions of source code must retain the above copyright notice, | |
14 | * this list of conditions and the following disclaimer. | |
15 | * - Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * - Neither the name of the Red Hat, Inc. nor the names of its | |
19 | * contributors may be used to endorse or promote products derived from this | |
20 | * software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
32 | * THE POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <sys/types.h> | |
36 | #include <sys/socket.h> | |
37 | ||
38 | #include <err.h> | |
39 | #include <poll.h> | |
40 | #include <stdio.h> | |
41 | #include <stdint.h> | |
42 | #include <netdb.h> | |
43 | ||
313d42d1 | 44 | #include "log.h" |
9a1955a7 JF |
45 | #include "qdevice-config.h" |
46 | #include "qdevice-cmap.h" | |
47 | #include "qdevice-log.h" | |
313d42d1 | 48 | #include "log-common.h" |
9a1955a7 JF |
49 | #include "qdevice-model.h" |
50 | #include "utils.h" | |
51 | ||
52 | static uint32_t | |
e4bdad3c | 53 | qdevice_cmap_autogenerate_node_id(const char *addr, int clear_node_high_bit) |
9a1955a7 JF |
54 | { |
55 | struct addrinfo *ainfo; | |
56 | struct addrinfo ahints; | |
57 | int ret, i; | |
58 | ||
59 | memset(&ahints, 0, sizeof(ahints)); | |
60 | ahints.ai_socktype = SOCK_DGRAM; | |
61 | ahints.ai_protocol = IPPROTO_UDP; | |
62 | /* | |
63 | * Hardcoded AF_INET because autogenerated nodeid is valid only for ipv4 | |
64 | */ | |
65 | ahints.ai_family = AF_INET; | |
66 | ||
67 | ret = getaddrinfo(addr, NULL, &ahints, &ainfo); | |
68 | if (ret != 0) | |
69 | return (0); | |
70 | ||
71 | if (ainfo->ai_family != AF_INET) { | |
72 | ||
73 | freeaddrinfo(ainfo); | |
74 | ||
75 | return (0); | |
76 | } | |
77 | ||
37fda20a | 78 | memcpy(&i, &(((struct sockaddr_in *)((void *)ainfo->ai_addr))->sin_addr), sizeof(struct in_addr)); |
9a1955a7 JF |
79 | freeaddrinfo(ainfo); |
80 | ||
81 | ret = htonl(i); | |
82 | ||
e4bdad3c | 83 | if (clear_node_high_bit) { |
9a1955a7 JF |
84 | ret &= 0x7FFFFFFF; |
85 | } | |
86 | ||
87 | return (ret); | |
88 | } | |
89 | ||
90 | int | |
91 | qdevice_cmap_get_nodelist(cmap_handle_t cmap_handle, struct node_list *list) | |
92 | { | |
93 | cs_error_t cs_err; | |
94 | cmap_iter_handle_t iter_handle; | |
95 | char key_name[CMAP_KEYNAME_MAXLEN + 1]; | |
96 | char tmp_key[CMAP_KEYNAME_MAXLEN + 1]; | |
97 | int res; | |
98 | int ret_value; | |
99 | unsigned int node_pos; | |
100 | uint32_t node_id; | |
101 | uint32_t data_center_id; | |
102 | char *tmp_str; | |
103 | char *addr0_str; | |
e4bdad3c | 104 | int clear_node_high_bit; |
9a1955a7 JF |
105 | |
106 | ret_value = 0; | |
107 | ||
108 | node_list_init(list); | |
109 | ||
154ded5d JF |
110 | /* |
111 | * Fill clear high node bit | |
112 | */ | |
113 | clear_node_high_bit = 0; | |
114 | ||
115 | if (cmap_get_string(cmap_handle, "totem.clear_node_high_bit", &tmp_str) == CS_OK) { | |
116 | if (strcmp (tmp_str, "yes") == 0) { | |
117 | clear_node_high_bit = 1; | |
118 | } | |
119 | ||
120 | free(tmp_str); | |
121 | } | |
122 | ||
123 | /* | |
124 | * Iterate nodelist | |
125 | */ | |
9a1955a7 JF |
126 | cs_err = cmap_iter_init(cmap_handle, "nodelist.node.", &iter_handle); |
127 | if (cs_err != CS_OK) { | |
128 | return (-1); | |
129 | } | |
130 | ||
131 | while ((cs_err = cmap_iter_next(cmap_handle, iter_handle, key_name, NULL, NULL)) == CS_OK) { | |
132 | res = sscanf(key_name, "nodelist.node.%u.%s", &node_pos, tmp_key); | |
133 | if (res != 2) { | |
134 | continue; | |
135 | } | |
136 | ||
137 | if (strcmp(tmp_key, "ring0_addr") != 0) { | |
138 | continue; | |
139 | } | |
140 | ||
141 | snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos); | |
142 | cs_err = cmap_get_uint32(cmap_handle, tmp_key, &node_id); | |
143 | ||
144 | if (cs_err == CS_ERR_NOT_EXIST) { | |
145 | /* | |
146 | * Nodeid doesn't exists -> autogenerate node id | |
147 | */ | |
9a1955a7 JF |
148 | if (cmap_get_string(cmap_handle, key_name, &addr0_str) != CS_OK) { |
149 | return (-1); | |
150 | } | |
151 | ||
152 | node_id = qdevice_cmap_autogenerate_node_id(addr0_str, | |
e4bdad3c | 153 | clear_node_high_bit); |
9a1955a7 JF |
154 | |
155 | free(addr0_str); | |
156 | } else if (cs_err != CS_OK) { | |
157 | ret_value = -1; | |
158 | ||
159 | goto iter_finalize; | |
160 | } | |
161 | ||
162 | snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.datacenterid", node_pos); | |
163 | if (cmap_get_uint32(cmap_handle, tmp_key, &data_center_id) != CS_OK) { | |
164 | data_center_id = 0; | |
165 | } | |
166 | ||
167 | if (node_list_add(list, node_id, data_center_id, TLV_NODE_STATE_NOT_SET) == NULL) { | |
168 | ret_value = -1; | |
169 | ||
170 | goto iter_finalize; | |
171 | } | |
172 | } | |
173 | ||
174 | iter_finalize: | |
175 | cmap_iter_finalize(cmap_handle, iter_handle); | |
176 | ||
177 | if (ret_value != 0) { | |
178 | node_list_free(list); | |
179 | } | |
180 | ||
181 | return (ret_value); | |
182 | } | |
183 | ||
184 | int | |
185 | qdevice_cmap_get_config_version(cmap_handle_t cmap_handle, uint64_t *config_version) | |
186 | { | |
187 | int res; | |
188 | ||
189 | if (cmap_get_uint64(cmap_handle, "totem.config_version", config_version) == CS_OK) { | |
190 | res = 0; | |
191 | } else { | |
192 | *config_version = 0; | |
193 | res = -1; | |
194 | } | |
195 | ||
196 | return (res); | |
197 | } | |
198 | ||
199 | int | |
200 | qdevice_cmap_store_config_node_list(struct qdevice_instance *instance) | |
201 | { | |
202 | int res; | |
203 | ||
204 | node_list_free(&instance->config_node_list); | |
205 | ||
206 | if (qdevice_cmap_get_nodelist(instance->cmap_handle, &instance->config_node_list) != 0) { | |
c8d19612 | 207 | log(LOG_ERR, "Can't get configuration node list."); |
9a1955a7 JF |
208 | |
209 | return (-1); | |
210 | } | |
211 | ||
212 | res = qdevice_cmap_get_config_version(instance->cmap_handle, &instance->config_node_list_version); | |
213 | instance->config_node_list_version_set = (res == 0); | |
214 | ||
215 | return (0); | |
216 | } | |
217 | ||
218 | void | |
219 | qdevice_cmap_init(struct qdevice_instance *instance) | |
220 | { | |
221 | cs_error_t res; | |
222 | int no_retries; | |
223 | ||
224 | no_retries = 0; | |
225 | ||
226 | while ((res = cmap_initialize(&instance->cmap_handle)) == CS_ERR_TRY_AGAIN && | |
227 | no_retries++ < instance->advanced_settings->max_cs_try_again) { | |
228 | (void)poll(NULL, 0, 1000); | |
229 | } | |
230 | ||
231 | if (res != CS_OK) { | |
406b689d | 232 | errx(EXIT_FAILURE, "Failed to initialize the cmap API. Error %s", cs_strerror(res)); |
9a1955a7 JF |
233 | } |
234 | ||
235 | if ((res = cmap_context_set(instance->cmap_handle, (void *)instance)) != CS_OK) { | |
406b689d | 236 | errx(EXIT_FAILURE, "Can't set cmap context. Error %s", cs_strerror(res)); |
9a1955a7 JF |
237 | } |
238 | ||
239 | cmap_fd_get(instance->cmap_handle, &instance->cmap_poll_fd); | |
240 | } | |
241 | ||
242 | static void | |
243 | qdevice_cmap_node_list_event(struct qdevice_instance *instance) | |
244 | { | |
245 | struct node_list nlist; | |
246 | int config_version_set; | |
247 | uint64_t config_version; | |
248 | ||
c8d19612 | 249 | log(LOG_DEBUG, "Node list configuration possibly changed"); |
9a1955a7 | 250 | if (qdevice_cmap_get_nodelist(instance->cmap_handle, &nlist) != 0) { |
c8d19612 | 251 | log(LOG_ERR, "Can't get configuration node list."); |
9a1955a7 JF |
252 | |
253 | if (qdevice_model_get_config_node_list_failed(instance) != 0) { | |
c8d19612 | 254 | log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit"); |
406b689d | 255 | exit(EXIT_FAILURE); |
9a1955a7 JF |
256 | } |
257 | ||
258 | return ; | |
259 | } | |
260 | ||
261 | config_version_set = (qdevice_cmap_get_config_version(instance->cmap_handle, | |
262 | &config_version) == 0); | |
263 | ||
264 | if (node_list_eq(&instance->config_node_list, &nlist)) { | |
265 | return ; | |
266 | } | |
267 | ||
c8d19612 | 268 | log(LOG_DEBUG, "Node list changed"); |
9a1955a7 | 269 | if (config_version_set) { |
c8d19612 | 270 | log(LOG_DEBUG, " config_version = "UTILS_PRI_CONFIG_VERSION, config_version); |
9a1955a7 | 271 | } |
313d42d1 | 272 | log_common_debug_dump_node_list(&nlist); |
9a1955a7 JF |
273 | |
274 | if (qdevice_model_config_node_list_changed(instance, &nlist, | |
275 | config_version_set, config_version) != 0) { | |
c8d19612 | 276 | log(LOG_DEBUG, "qdevice_model_config_node_list_changed returned error -> exit"); |
406b689d | 277 | exit(EXIT_FAILURE); |
9a1955a7 JF |
278 | } |
279 | ||
280 | node_list_free(&instance->config_node_list); | |
281 | if (node_list_clone(&instance->config_node_list, &nlist) != 0) { | |
c8d19612 | 282 | log(LOG_ERR, "Can't allocate instance->config_node_list clone"); |
9a1955a7 JF |
283 | |
284 | node_list_free(&nlist); | |
285 | ||
286 | if (qdevice_model_get_config_node_list_failed(instance) != 0) { | |
c8d19612 | 287 | log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit"); |
406b689d | 288 | exit(EXIT_FAILURE); |
9a1955a7 JF |
289 | } |
290 | ||
291 | return ; | |
292 | } | |
293 | ||
294 | instance->config_node_list_version_set = config_version_set; | |
295 | ||
296 | if (config_version_set) { | |
297 | instance->config_node_list_version = config_version; | |
298 | } | |
299 | } | |
300 | ||
301 | static void | |
302 | qdevice_cmap_logging_event(struct qdevice_instance *instance) | |
303 | { | |
304 | ||
c8d19612 | 305 | log(LOG_DEBUG, "Logging configuration possibly changed"); |
9a1955a7 JF |
306 | qdevice_log_configure(instance); |
307 | } | |
308 | ||
309 | static void | |
310 | qdevice_cmap_heuristics_event(struct qdevice_instance *instance) | |
311 | { | |
312 | ||
c8d19612 | 313 | log(LOG_DEBUG, "Heuristics configuration possibly changed"); |
9a1955a7 | 314 | if (qdevice_instance_configure_from_cmap_heuristics(instance) != 0) { |
c8d19612 | 315 | log(LOG_DEBUG, "qdevice_instance_configure_from_cmap_heuristics returned error -> exit"); |
406b689d | 316 | exit(EXIT_FAILURE); |
9a1955a7 JF |
317 | } |
318 | } | |
319 | ||
320 | static void | |
321 | qdevice_cmap_reload_cb(cmap_handle_t cmap_handle, cmap_track_handle_t cmap_track_handle, | |
322 | int32_t event, const char *key_name, | |
323 | struct cmap_notify_value new_value, struct cmap_notify_value old_value, | |
324 | void *user_data) | |
325 | { | |
326 | cs_error_t cs_res; | |
327 | uint8_t reload; | |
328 | struct qdevice_instance *instance; | |
329 | const char *node_list_prefix_str; | |
330 | const char *logging_prefix_str; | |
331 | const char *heuristics_prefix_str; | |
332 | struct qdevice_cmap_change_events events; | |
333 | ||
334 | memset(&events, 0, sizeof(events)); | |
335 | node_list_prefix_str = "nodelist."; | |
336 | logging_prefix_str = "logging."; | |
337 | heuristics_prefix_str = "quorum.device.heuristics."; | |
338 | ||
339 | if (cmap_context_get(cmap_handle, (const void **)&instance) != CS_OK) { | |
c8d19612 | 340 | log(LOG_ERR, "Fatal error. Can't get cmap context"); |
406b689d | 341 | exit(EXIT_FAILURE); |
9a1955a7 JF |
342 | } |
343 | ||
344 | /* | |
345 | * Wait for full reload | |
346 | */ | |
347 | if (strcmp(key_name, "config.totemconfig_reload_in_progress") == 0 && | |
348 | new_value.type == CMAP_VALUETYPE_UINT8 && new_value.len == sizeof(reload)) { | |
349 | reload = 1; | |
350 | if (memcmp(new_value.data, &reload, sizeof(reload)) == 0) { | |
351 | /* | |
352 | * Ignore nodelist changes | |
353 | */ | |
354 | instance->cmap_reload_in_progress = 1; | |
355 | return ; | |
356 | } else { | |
357 | instance->cmap_reload_in_progress = 0; | |
358 | events.node_list = 1; | |
359 | events.logging = 1; | |
360 | events.heuristics = 1; | |
361 | } | |
362 | } | |
363 | ||
364 | if (instance->cmap_reload_in_progress) { | |
365 | return ; | |
366 | } | |
367 | ||
368 | if (((cs_res = cmap_get_uint8(cmap_handle, "config.totemconfig_reload_in_progress", | |
369 | &reload)) == CS_OK) && reload == 1) { | |
370 | return ; | |
371 | } | |
372 | ||
373 | if (strncmp(key_name, node_list_prefix_str, strlen(node_list_prefix_str)) == 0) { | |
374 | events.node_list = 1; | |
375 | } | |
376 | ||
377 | if (strncmp(key_name, logging_prefix_str, strlen(logging_prefix_str)) == 0) { | |
378 | events.logging = 1; | |
379 | } | |
380 | ||
381 | if (strncmp(key_name, heuristics_prefix_str, strlen(heuristics_prefix_str)) == 0) { | |
382 | events.heuristics = 1; | |
383 | } | |
384 | ||
385 | if (events.logging) { | |
386 | qdevice_cmap_logging_event(instance); | |
387 | } | |
388 | ||
389 | if (events.node_list) { | |
390 | qdevice_cmap_node_list_event(instance); | |
391 | } | |
392 | ||
393 | if (events.heuristics) { | |
394 | qdevice_cmap_heuristics_event(instance); | |
395 | } | |
396 | ||
397 | /* | |
398 | * Inform model about change | |
399 | */ | |
400 | if (qdevice_model_cmap_changed(instance, &events) != 0) { | |
c8d19612 | 401 | log(LOG_DEBUG, "qdevice_model_cmap_changed returned error -> exit"); |
406b689d | 402 | exit(EXIT_FAILURE); |
9a1955a7 JF |
403 | } |
404 | } | |
405 | ||
406 | int | |
407 | qdevice_cmap_add_track(struct qdevice_instance *instance) | |
408 | { | |
409 | cs_error_t res; | |
410 | ||
411 | res = cmap_track_add(instance->cmap_handle, "config.totemconfig_reload_in_progress", | |
412 | CMAP_TRACK_ADD | CMAP_TRACK_MODIFY, qdevice_cmap_reload_cb, | |
413 | NULL, &instance->cmap_reload_track_handle); | |
414 | ||
415 | if (res != CS_OK) { | |
c8d19612 | 416 | log(LOG_ERR, "Can't initialize cmap totemconfig_reload_in_progress tracking"); |
9a1955a7 JF |
417 | return (-1); |
418 | } | |
419 | ||
420 | res = cmap_track_add(instance->cmap_handle, "nodelist.", | |
421 | CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, | |
422 | qdevice_cmap_reload_cb, | |
423 | NULL, &instance->cmap_nodelist_track_handle); | |
424 | ||
425 | if (res != CS_OK) { | |
c8d19612 | 426 | log(LOG_ERR, "Can't initialize cmap nodelist tracking"); |
9a1955a7 JF |
427 | return (-1); |
428 | } | |
429 | ||
430 | res = cmap_track_add(instance->cmap_handle, "logging.", | |
431 | CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, | |
432 | qdevice_cmap_reload_cb, | |
433 | NULL, &instance->cmap_logging_track_handle); | |
434 | ||
435 | if (res != CS_OK) { | |
c8d19612 | 436 | log(LOG_ERR, "Can't initialize logging tracking"); |
9a1955a7 JF |
437 | return (-1); |
438 | } | |
439 | ||
440 | res = cmap_track_add(instance->cmap_handle, "quorum.device.heuristics.", | |
441 | CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, | |
442 | qdevice_cmap_reload_cb, | |
443 | NULL, &instance->cmap_heuristics_track_handle); | |
444 | ||
445 | if (res != CS_OK) { | |
c8d19612 | 446 | log(LOG_ERR, "Can't initialize logging tracking"); |
9a1955a7 JF |
447 | return (-1); |
448 | } | |
449 | ||
450 | return (0); | |
451 | } | |
452 | ||
453 | int | |
454 | qdevice_cmap_del_track(struct qdevice_instance *instance) | |
455 | { | |
456 | cs_error_t res; | |
457 | ||
458 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_reload_track_handle); | |
459 | if (res != CS_OK) { | |
c8d19612 | 460 | log(LOG_WARNING, "Can't delete cmap totemconfig_reload_in_progress tracking"); |
9a1955a7 JF |
461 | } |
462 | ||
463 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_nodelist_track_handle); | |
464 | if (res != CS_OK) { | |
c8d19612 | 465 | log(LOG_WARNING, "Can't delete cmap nodelist tracking"); |
9a1955a7 JF |
466 | } |
467 | ||
468 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_logging_track_handle); | |
469 | if (res != CS_OK) { | |
c8d19612 | 470 | log(LOG_WARNING, "Can't delete cmap logging tracking"); |
9a1955a7 JF |
471 | } |
472 | ||
473 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_heuristics_track_handle); | |
474 | if (res != CS_OK) { | |
c8d19612 | 475 | log(LOG_WARNING, "Can't delete cmap heuristics tracking"); |
9a1955a7 JF |
476 | } |
477 | ||
478 | return (0); | |
479 | } | |
480 | ||
481 | void | |
482 | qdevice_cmap_destroy(struct qdevice_instance *instance) | |
483 | { | |
484 | cs_error_t res; | |
485 | ||
486 | res = cmap_finalize(instance->cmap_handle); | |
487 | ||
488 | if (res != CS_OK) { | |
c8d19612 | 489 | log(LOG_WARNING, "Can't finalize cmap. Error %s", cs_strerror(res)); |
9a1955a7 JF |
490 | } |
491 | } | |
492 | ||
493 | int | |
494 | qdevice_cmap_dispatch(struct qdevice_instance *instance) | |
495 | { | |
496 | cs_error_t res; | |
497 | ||
498 | /* | |
499 | * dispatch can block if corosync is during sync phase | |
500 | */ | |
501 | if (instance->sync_in_progress) { | |
502 | return (0); | |
503 | } | |
504 | ||
505 | res = cmap_dispatch(instance->cmap_handle, CS_DISPATCH_ALL); | |
506 | ||
507 | if (res != CS_OK && res != CS_ERR_TRY_AGAIN) { | |
c8d19612 | 508 | log(LOG_ERR, "Can't dispatch cmap messages"); |
9a1955a7 JF |
509 | |
510 | return (-1); | |
511 | } | |
512 | ||
513 | return (0); | |
514 | } |