4
0

qdevice-cmap.c 14 KB

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