]>
Commit | Line | Data |
---|---|---|
9a1955a7 | 1 | /* |
c8d19612 | 2 | * Copyright (c) 2015-2019 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 | ||
44 | #include "qdevice-config.h" | |
45 | #include "qdevice-cmap.h" | |
46 | #include "qdevice-log.h" | |
47 | #include "qdevice-log-debug.h" | |
48 | #include "qdevice-model.h" | |
49 | #include "utils.h" | |
50 | ||
51 | static uint32_t | |
52 | qdevice_cmap_autogenerate_node_id(const char *addr, int clear_node_high_byte) | |
53 | { | |
54 | struct addrinfo *ainfo; | |
55 | struct addrinfo ahints; | |
56 | int ret, i; | |
57 | ||
58 | memset(&ahints, 0, sizeof(ahints)); | |
59 | ahints.ai_socktype = SOCK_DGRAM; | |
60 | ahints.ai_protocol = IPPROTO_UDP; | |
61 | /* | |
62 | * Hardcoded AF_INET because autogenerated nodeid is valid only for ipv4 | |
63 | */ | |
64 | ahints.ai_family = AF_INET; | |
65 | ||
66 | ret = getaddrinfo(addr, NULL, &ahints, &ainfo); | |
67 | if (ret != 0) | |
68 | return (0); | |
69 | ||
70 | if (ainfo->ai_family != AF_INET) { | |
71 | ||
72 | freeaddrinfo(ainfo); | |
73 | ||
74 | return (0); | |
75 | } | |
76 | ||
37fda20a | 77 | memcpy(&i, &(((struct sockaddr_in *)((void *)ainfo->ai_addr))->sin_addr), sizeof(struct in_addr)); |
9a1955a7 JF |
78 | freeaddrinfo(ainfo); |
79 | ||
80 | ret = htonl(i); | |
81 | ||
82 | if (clear_node_high_byte) { | |
83 | ret &= 0x7FFFFFFF; | |
84 | } | |
85 | ||
86 | return (ret); | |
87 | } | |
88 | ||
89 | int | |
90 | qdevice_cmap_get_nodelist(cmap_handle_t cmap_handle, struct node_list *list) | |
91 | { | |
92 | cs_error_t cs_err; | |
93 | cmap_iter_handle_t iter_handle; | |
94 | char key_name[CMAP_KEYNAME_MAXLEN + 1]; | |
95 | char tmp_key[CMAP_KEYNAME_MAXLEN + 1]; | |
96 | int res; | |
97 | int ret_value; | |
98 | unsigned int node_pos; | |
99 | uint32_t node_id; | |
100 | uint32_t data_center_id; | |
101 | char *tmp_str; | |
102 | char *addr0_str; | |
103 | int clear_node_high_byte; | |
104 | ||
105 | ret_value = 0; | |
106 | ||
107 | node_list_init(list); | |
108 | ||
109 | cs_err = cmap_iter_init(cmap_handle, "nodelist.node.", &iter_handle); | |
110 | if (cs_err != CS_OK) { | |
111 | return (-1); | |
112 | } | |
113 | ||
114 | while ((cs_err = cmap_iter_next(cmap_handle, iter_handle, key_name, NULL, NULL)) == CS_OK) { | |
115 | res = sscanf(key_name, "nodelist.node.%u.%s", &node_pos, tmp_key); | |
116 | if (res != 2) { | |
117 | continue; | |
118 | } | |
119 | ||
120 | if (strcmp(tmp_key, "ring0_addr") != 0) { | |
121 | continue; | |
122 | } | |
123 | ||
124 | snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos); | |
125 | cs_err = cmap_get_uint32(cmap_handle, tmp_key, &node_id); | |
126 | ||
127 | if (cs_err == CS_ERR_NOT_EXIST) { | |
128 | /* | |
129 | * Nodeid doesn't exists -> autogenerate node id | |
130 | */ | |
131 | clear_node_high_byte = 0; | |
132 | ||
133 | if (cmap_get_string(cmap_handle, "totem.clear_node_high_bit", | |
134 | &tmp_str) == CS_OK) { | |
135 | if (strcmp (tmp_str, "yes") == 0) { | |
136 | clear_node_high_byte = 1; | |
137 | } | |
138 | ||
139 | free(tmp_str); | |
140 | } | |
141 | ||
142 | if (cmap_get_string(cmap_handle, key_name, &addr0_str) != CS_OK) { | |
143 | return (-1); | |
144 | } | |
145 | ||
146 | node_id = qdevice_cmap_autogenerate_node_id(addr0_str, | |
147 | clear_node_high_byte); | |
148 | ||
149 | free(addr0_str); | |
150 | } else if (cs_err != CS_OK) { | |
151 | ret_value = -1; | |
152 | ||
153 | goto iter_finalize; | |
154 | } | |
155 | ||
156 | snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.datacenterid", node_pos); | |
157 | if (cmap_get_uint32(cmap_handle, tmp_key, &data_center_id) != CS_OK) { | |
158 | data_center_id = 0; | |
159 | } | |
160 | ||
161 | if (node_list_add(list, node_id, data_center_id, TLV_NODE_STATE_NOT_SET) == NULL) { | |
162 | ret_value = -1; | |
163 | ||
164 | goto iter_finalize; | |
165 | } | |
166 | } | |
167 | ||
168 | iter_finalize: | |
169 | cmap_iter_finalize(cmap_handle, iter_handle); | |
170 | ||
171 | if (ret_value != 0) { | |
172 | node_list_free(list); | |
173 | } | |
174 | ||
175 | return (ret_value); | |
176 | } | |
177 | ||
178 | int | |
179 | qdevice_cmap_get_config_version(cmap_handle_t cmap_handle, uint64_t *config_version) | |
180 | { | |
181 | int res; | |
182 | ||
183 | if (cmap_get_uint64(cmap_handle, "totem.config_version", config_version) == CS_OK) { | |
184 | res = 0; | |
185 | } else { | |
186 | *config_version = 0; | |
187 | res = -1; | |
188 | } | |
189 | ||
190 | return (res); | |
191 | } | |
192 | ||
193 | int | |
194 | qdevice_cmap_store_config_node_list(struct qdevice_instance *instance) | |
195 | { | |
196 | int res; | |
197 | ||
198 | node_list_free(&instance->config_node_list); | |
199 | ||
200 | if (qdevice_cmap_get_nodelist(instance->cmap_handle, &instance->config_node_list) != 0) { | |
c8d19612 | 201 | log(LOG_ERR, "Can't get configuration node list."); |
9a1955a7 JF |
202 | |
203 | return (-1); | |
204 | } | |
205 | ||
206 | res = qdevice_cmap_get_config_version(instance->cmap_handle, &instance->config_node_list_version); | |
207 | instance->config_node_list_version_set = (res == 0); | |
208 | ||
209 | return (0); | |
210 | } | |
211 | ||
212 | void | |
213 | qdevice_cmap_init(struct qdevice_instance *instance) | |
214 | { | |
215 | cs_error_t res; | |
216 | int no_retries; | |
217 | ||
218 | no_retries = 0; | |
219 | ||
220 | while ((res = cmap_initialize(&instance->cmap_handle)) == CS_ERR_TRY_AGAIN && | |
221 | no_retries++ < instance->advanced_settings->max_cs_try_again) { | |
222 | (void)poll(NULL, 0, 1000); | |
223 | } | |
224 | ||
225 | if (res != CS_OK) { | |
226 | errx(1, "Failed to initialize the cmap API. Error %s", cs_strerror(res)); | |
227 | } | |
228 | ||
229 | if ((res = cmap_context_set(instance->cmap_handle, (void *)instance)) != CS_OK) { | |
230 | errx(1, "Can't set cmap context. Error %s", cs_strerror(res)); | |
231 | } | |
232 | ||
233 | cmap_fd_get(instance->cmap_handle, &instance->cmap_poll_fd); | |
234 | } | |
235 | ||
236 | static void | |
237 | qdevice_cmap_node_list_event(struct qdevice_instance *instance) | |
238 | { | |
239 | struct node_list nlist; | |
240 | int config_version_set; | |
241 | uint64_t config_version; | |
242 | ||
c8d19612 | 243 | log(LOG_DEBUG, "Node list configuration possibly changed"); |
9a1955a7 | 244 | if (qdevice_cmap_get_nodelist(instance->cmap_handle, &nlist) != 0) { |
c8d19612 | 245 | log(LOG_ERR, "Can't get configuration node list."); |
9a1955a7 JF |
246 | |
247 | if (qdevice_model_get_config_node_list_failed(instance) != 0) { | |
c8d19612 | 248 | log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit"); |
9a1955a7 JF |
249 | exit(2); |
250 | } | |
251 | ||
252 | return ; | |
253 | } | |
254 | ||
255 | config_version_set = (qdevice_cmap_get_config_version(instance->cmap_handle, | |
256 | &config_version) == 0); | |
257 | ||
258 | if (node_list_eq(&instance->config_node_list, &nlist)) { | |
259 | return ; | |
260 | } | |
261 | ||
c8d19612 | 262 | log(LOG_DEBUG, "Node list changed"); |
9a1955a7 | 263 | if (config_version_set) { |
c8d19612 | 264 | log(LOG_DEBUG, " config_version = "UTILS_PRI_CONFIG_VERSION, config_version); |
9a1955a7 JF |
265 | } |
266 | qdevice_log_debug_dump_node_list(&nlist); | |
267 | ||
268 | if (qdevice_model_config_node_list_changed(instance, &nlist, | |
269 | config_version_set, config_version) != 0) { | |
c8d19612 | 270 | log(LOG_DEBUG, "qdevice_model_config_node_list_changed returned error -> exit"); |
9a1955a7 JF |
271 | exit(2); |
272 | } | |
273 | ||
274 | node_list_free(&instance->config_node_list); | |
275 | if (node_list_clone(&instance->config_node_list, &nlist) != 0) { | |
c8d19612 | 276 | log(LOG_ERR, "Can't allocate instance->config_node_list clone"); |
9a1955a7 JF |
277 | |
278 | node_list_free(&nlist); | |
279 | ||
280 | if (qdevice_model_get_config_node_list_failed(instance) != 0) { | |
c8d19612 | 281 | log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit"); |
9a1955a7 JF |
282 | exit(2); |
283 | } | |
284 | ||
285 | return ; | |
286 | } | |
287 | ||
288 | instance->config_node_list_version_set = config_version_set; | |
289 | ||
290 | if (config_version_set) { | |
291 | instance->config_node_list_version = config_version; | |
292 | } | |
293 | } | |
294 | ||
295 | static void | |
296 | qdevice_cmap_logging_event(struct qdevice_instance *instance) | |
297 | { | |
298 | ||
c8d19612 | 299 | log(LOG_DEBUG, "Logging configuration possibly changed"); |
9a1955a7 JF |
300 | qdevice_log_configure(instance); |
301 | } | |
302 | ||
303 | static void | |
304 | qdevice_cmap_heuristics_event(struct qdevice_instance *instance) | |
305 | { | |
306 | ||
c8d19612 | 307 | log(LOG_DEBUG, "Heuristics configuration possibly changed"); |
9a1955a7 | 308 | if (qdevice_instance_configure_from_cmap_heuristics(instance) != 0) { |
c8d19612 | 309 | log(LOG_DEBUG, "qdevice_instance_configure_from_cmap_heuristics returned error -> exit"); |
9a1955a7 JF |
310 | exit(2); |
311 | } | |
312 | } | |
313 | ||
314 | static void | |
315 | qdevice_cmap_reload_cb(cmap_handle_t cmap_handle, cmap_track_handle_t cmap_track_handle, | |
316 | int32_t event, const char *key_name, | |
317 | struct cmap_notify_value new_value, struct cmap_notify_value old_value, | |
318 | void *user_data) | |
319 | { | |
320 | cs_error_t cs_res; | |
321 | uint8_t reload; | |
322 | struct qdevice_instance *instance; | |
323 | const char *node_list_prefix_str; | |
324 | const char *logging_prefix_str; | |
325 | const char *heuristics_prefix_str; | |
326 | struct qdevice_cmap_change_events events; | |
327 | ||
328 | memset(&events, 0, sizeof(events)); | |
329 | node_list_prefix_str = "nodelist."; | |
330 | logging_prefix_str = "logging."; | |
331 | heuristics_prefix_str = "quorum.device.heuristics."; | |
332 | ||
333 | if (cmap_context_get(cmap_handle, (const void **)&instance) != CS_OK) { | |
c8d19612 | 334 | log(LOG_ERR, "Fatal error. Can't get cmap context"); |
9a1955a7 JF |
335 | exit(1); |
336 | } | |
337 | ||
338 | /* | |
339 | * Wait for full reload | |
340 | */ | |
341 | if (strcmp(key_name, "config.totemconfig_reload_in_progress") == 0 && | |
342 | new_value.type == CMAP_VALUETYPE_UINT8 && new_value.len == sizeof(reload)) { | |
343 | reload = 1; | |
344 | if (memcmp(new_value.data, &reload, sizeof(reload)) == 0) { | |
345 | /* | |
346 | * Ignore nodelist changes | |
347 | */ | |
348 | instance->cmap_reload_in_progress = 1; | |
349 | return ; | |
350 | } else { | |
351 | instance->cmap_reload_in_progress = 0; | |
352 | events.node_list = 1; | |
353 | events.logging = 1; | |
354 | events.heuristics = 1; | |
355 | } | |
356 | } | |
357 | ||
358 | if (instance->cmap_reload_in_progress) { | |
359 | return ; | |
360 | } | |
361 | ||
362 | if (((cs_res = cmap_get_uint8(cmap_handle, "config.totemconfig_reload_in_progress", | |
363 | &reload)) == CS_OK) && reload == 1) { | |
364 | return ; | |
365 | } | |
366 | ||
367 | if (strncmp(key_name, node_list_prefix_str, strlen(node_list_prefix_str)) == 0) { | |
368 | events.node_list = 1; | |
369 | } | |
370 | ||
371 | if (strncmp(key_name, logging_prefix_str, strlen(logging_prefix_str)) == 0) { | |
372 | events.logging = 1; | |
373 | } | |
374 | ||
375 | if (strncmp(key_name, heuristics_prefix_str, strlen(heuristics_prefix_str)) == 0) { | |
376 | events.heuristics = 1; | |
377 | } | |
378 | ||
379 | if (events.logging) { | |
380 | qdevice_cmap_logging_event(instance); | |
381 | } | |
382 | ||
383 | if (events.node_list) { | |
384 | qdevice_cmap_node_list_event(instance); | |
385 | } | |
386 | ||
387 | if (events.heuristics) { | |
388 | qdevice_cmap_heuristics_event(instance); | |
389 | } | |
390 | ||
391 | /* | |
392 | * Inform model about change | |
393 | */ | |
394 | if (qdevice_model_cmap_changed(instance, &events) != 0) { | |
c8d19612 | 395 | log(LOG_DEBUG, "qdevice_model_cmap_changed returned error -> exit"); |
9a1955a7 JF |
396 | exit(2); |
397 | } | |
398 | } | |
399 | ||
400 | int | |
401 | qdevice_cmap_add_track(struct qdevice_instance *instance) | |
402 | { | |
403 | cs_error_t res; | |
404 | ||
405 | res = cmap_track_add(instance->cmap_handle, "config.totemconfig_reload_in_progress", | |
406 | CMAP_TRACK_ADD | CMAP_TRACK_MODIFY, qdevice_cmap_reload_cb, | |
407 | NULL, &instance->cmap_reload_track_handle); | |
408 | ||
409 | if (res != CS_OK) { | |
c8d19612 | 410 | log(LOG_ERR, "Can't initialize cmap totemconfig_reload_in_progress tracking"); |
9a1955a7 JF |
411 | return (-1); |
412 | } | |
413 | ||
414 | res = cmap_track_add(instance->cmap_handle, "nodelist.", | |
415 | CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, | |
416 | qdevice_cmap_reload_cb, | |
417 | NULL, &instance->cmap_nodelist_track_handle); | |
418 | ||
419 | if (res != CS_OK) { | |
c8d19612 | 420 | log(LOG_ERR, "Can't initialize cmap nodelist tracking"); |
9a1955a7 JF |
421 | return (-1); |
422 | } | |
423 | ||
424 | res = cmap_track_add(instance->cmap_handle, "logging.", | |
425 | CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, | |
426 | qdevice_cmap_reload_cb, | |
427 | NULL, &instance->cmap_logging_track_handle); | |
428 | ||
429 | if (res != CS_OK) { | |
c8d19612 | 430 | log(LOG_ERR, "Can't initialize logging tracking"); |
9a1955a7 JF |
431 | return (-1); |
432 | } | |
433 | ||
434 | res = cmap_track_add(instance->cmap_handle, "quorum.device.heuristics.", | |
435 | CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, | |
436 | qdevice_cmap_reload_cb, | |
437 | NULL, &instance->cmap_heuristics_track_handle); | |
438 | ||
439 | if (res != CS_OK) { | |
c8d19612 | 440 | log(LOG_ERR, "Can't initialize logging tracking"); |
9a1955a7 JF |
441 | return (-1); |
442 | } | |
443 | ||
444 | return (0); | |
445 | } | |
446 | ||
447 | int | |
448 | qdevice_cmap_del_track(struct qdevice_instance *instance) | |
449 | { | |
450 | cs_error_t res; | |
451 | ||
452 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_reload_track_handle); | |
453 | if (res != CS_OK) { | |
c8d19612 | 454 | log(LOG_WARNING, "Can't delete cmap totemconfig_reload_in_progress tracking"); |
9a1955a7 JF |
455 | } |
456 | ||
457 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_nodelist_track_handle); | |
458 | if (res != CS_OK) { | |
c8d19612 | 459 | log(LOG_WARNING, "Can't delete cmap nodelist tracking"); |
9a1955a7 JF |
460 | } |
461 | ||
462 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_logging_track_handle); | |
463 | if (res != CS_OK) { | |
c8d19612 | 464 | log(LOG_WARNING, "Can't delete cmap logging tracking"); |
9a1955a7 JF |
465 | } |
466 | ||
467 | res = cmap_track_delete(instance->cmap_handle, instance->cmap_heuristics_track_handle); | |
468 | if (res != CS_OK) { | |
c8d19612 | 469 | log(LOG_WARNING, "Can't delete cmap heuristics tracking"); |
9a1955a7 JF |
470 | } |
471 | ||
472 | return (0); | |
473 | } | |
474 | ||
475 | void | |
476 | qdevice_cmap_destroy(struct qdevice_instance *instance) | |
477 | { | |
478 | cs_error_t res; | |
479 | ||
480 | res = cmap_finalize(instance->cmap_handle); | |
481 | ||
482 | if (res != CS_OK) { | |
c8d19612 | 483 | log(LOG_WARNING, "Can't finalize cmap. Error %s", cs_strerror(res)); |
9a1955a7 JF |
484 | } |
485 | } | |
486 | ||
487 | int | |
488 | qdevice_cmap_dispatch(struct qdevice_instance *instance) | |
489 | { | |
490 | cs_error_t res; | |
491 | ||
492 | /* | |
493 | * dispatch can block if corosync is during sync phase | |
494 | */ | |
495 | if (instance->sync_in_progress) { | |
496 | return (0); | |
497 | } | |
498 | ||
499 | res = cmap_dispatch(instance->cmap_handle, CS_DISPATCH_ALL); | |
500 | ||
501 | if (res != CS_OK && res != CS_ERR_TRY_AGAIN) { | |
c8d19612 | 502 | log(LOG_ERR, "Can't dispatch cmap messages"); |
9a1955a7 JF |
503 | |
504 | return (-1); | |
505 | } | |
506 | ||
507 | return (0); | |
508 | } |