corosync-quorumtool.c 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  1. /*
  2. * Copyright (c) 2009-2014 Red Hat, Inc.
  3. *
  4. * All rights reserved.
  5. *
  6. * Authors: Christine Caulfield <ccaulfie@redhat.com>
  7. * Fabio M. Di Nitto (fdinitto@redhat.com)
  8. *
  9. * This software licensed under BSD license, the text of which follows:
  10. *
  11. * Redistribution and use in source and binary forms, with or without
  12. * modification, are permitted provided that the following conditions are met:
  13. *
  14. * - Redistributions of source code must retain the above copyright notice,
  15. * this list of conditions and the following disclaimer.
  16. * - Redistributions in binary form must reproduce the above copyright notice,
  17. * this list of conditions and the following disclaimer in the documentation
  18. * and/or other materials provided with the distribution.
  19. * - Neither the name of the Red Hat Inc. nor the names of its
  20. * contributors may be used to endorse or promote products derived from this
  21. * software without specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  33. * THE POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. #include <config.h>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <sys/types.h>
  40. #include <sys/socket.h>
  41. #include <netdb.h>
  42. #include <limits.h>
  43. #include <corosync/totem/totem.h>
  44. #include <corosync/cfg.h>
  45. #include <corosync/cmap.h>
  46. #include <corosync/quorum.h>
  47. #include <corosync/votequorum.h>
  48. typedef enum {
  49. NODEID_FORMAT_DECIMAL,
  50. NODEID_FORMAT_HEX
  51. } nodeid_format_t;
  52. typedef enum {
  53. ADDRESS_FORMAT_NAME,
  54. ADDRESS_FORMAT_IP
  55. } name_format_t;
  56. typedef enum {
  57. CMD_UNKNOWN,
  58. CMD_SHOWNODES,
  59. CMD_SHOWSTATUS,
  60. CMD_SETVOTES,
  61. CMD_SETEXPECTED,
  62. CMD_MONITOR,
  63. CMD_UNREGISTER_QDEVICE
  64. } command_t;
  65. typedef enum {
  66. SORT_ADDR,
  67. SORT_NODEID,
  68. SORT_NODENAME
  69. } sorttype_t;
  70. /*
  71. * global vars
  72. */
  73. /*
  74. * cmap bits
  75. */
  76. static cmap_handle_t cmap_handle;
  77. /*
  78. * quorum bits
  79. */
  80. static void quorum_notification_fn(
  81. quorum_handle_t handle,
  82. uint32_t quorate,
  83. uint64_t ring_id,
  84. uint32_t view_list_entries,
  85. uint32_t *view_list);
  86. static quorum_handle_t q_handle;
  87. static uint32_t q_type;
  88. static quorum_callbacks_t q_callbacks = {
  89. .quorum_notify_fn = quorum_notification_fn
  90. };
  91. /*
  92. * quorum call back vars
  93. */
  94. /* Containing struct to keep votequorum & normal quorum bits together */
  95. typedef struct {
  96. struct votequorum_info *vq_info; /* Might be NULL if votequorum not present */
  97. char *name; /* Might be IP address or NULL */
  98. int node_id; /* Always present */
  99. } view_list_entry_t;
  100. static view_list_entry_t *g_view_list;
  101. static uint32_t g_quorate;
  102. static uint64_t g_ring_id;
  103. static uint32_t g_ring_id_rep_node;
  104. static uint32_t g_view_list_entries;
  105. static uint32_t g_called;
  106. static uint32_t g_vq_called;
  107. static uint32_t g_show_all_addrs = 0;
  108. /*
  109. * votequorum bits
  110. */
  111. static void votequorum_notification_fn(
  112. votequorum_handle_t handle,
  113. uint64_t context,
  114. votequorum_ring_id_t ring_id,
  115. uint32_t node_list_entries,
  116. uint32_t node_list[]);
  117. static votequorum_handle_t v_handle;
  118. static votequorum_callbacks_t v_callbacks = {
  119. .votequorum_quorum_notify_fn = NULL,
  120. .votequorum_expectedvotes_notify_fn = NULL,
  121. .votequorum_nodelist_notify_fn = votequorum_notification_fn,
  122. };
  123. static uint32_t our_nodeid = 0;
  124. /*
  125. * cfg bits
  126. */
  127. static corosync_cfg_handle_t c_handle;
  128. static corosync_cfg_callbacks_t c_callbacks = {
  129. .corosync_cfg_shutdown_callback = NULL
  130. };
  131. /*
  132. * global
  133. */
  134. static int machine_parsable = 0;
  135. static void show_usage(const char *name)
  136. {
  137. printf("usage: \n");
  138. printf("%s <options>\n", name);
  139. printf("\n");
  140. printf(" options:\n");
  141. printf("\n");
  142. printf(" -s show quorum status\n");
  143. printf(" -m constantly monitor quorum status\n");
  144. printf(" -l list nodes\n");
  145. printf(" -a show all names or addresses for each node\n");
  146. printf(" -p when used with -s or -l, generates machine parsable output\n");
  147. printf(" -v <votes> change the number of votes for a node (*)\n");
  148. printf(" -n <nodeid> optional nodeid of node for -v\n");
  149. printf(" -e <expected> change expected votes for the cluster (*)\n");
  150. printf(" -H show nodeids in hexadecimal rather than decimal\n");
  151. printf(" -i show node IP addresses instead of the resolved name\n");
  152. printf(" -o <a|n|i> order by [a] IP address (default), [n] name, [i] nodeid\n");
  153. printf(" -f forcefully unregister a quorum device *DANGEROUS* (*)\n");
  154. printf(" -h show this help text\n");
  155. printf(" -V show version and exit\n");
  156. printf("\n");
  157. printf(" (*) Starred items only work if votequorum is the quorum provider for corosync\n");
  158. printf("\n");
  159. }
  160. static int get_quorum_type(char *quorum_type, size_t quorum_type_len)
  161. {
  162. int err;
  163. char *str = NULL;
  164. if ((!quorum_type) || (quorum_type_len <= 0)) {
  165. return -1;
  166. }
  167. if (q_type == QUORUM_FREE) {
  168. return -1;
  169. }
  170. if ((err = cmap_get_string(cmap_handle, "quorum.provider", &str)) != CS_OK) {
  171. goto out;
  172. }
  173. if (!str) {
  174. return -1;
  175. }
  176. strncpy(quorum_type, str, quorum_type_len - 1);
  177. free(str);
  178. return 0;
  179. out:
  180. return err;
  181. }
  182. /*
  183. * Returns 1 if 'votequorum' is active. The called then knows that
  184. * votequorum calls should work and can provide extra information
  185. */
  186. static int using_votequorum(void)
  187. {
  188. char quorumtype[256];
  189. int using_voteq;
  190. memset(quorumtype, 0, sizeof(quorumtype));
  191. if (get_quorum_type(quorumtype, sizeof(quorumtype))) {
  192. return -1;
  193. }
  194. if (strcmp(quorumtype, "corosync_votequorum") == 0) {
  195. using_voteq = 1;
  196. } else {
  197. using_voteq = 0;
  198. }
  199. return using_voteq;
  200. }
  201. static int set_votes(uint32_t nodeid, int votes)
  202. {
  203. int err;
  204. if ((err=votequorum_setvotes(v_handle, nodeid, votes)) != CS_OK) {
  205. fprintf(stderr, "Unable to set votes %d for nodeid: %u: %s\n",
  206. votes, nodeid, cs_strerror(err));
  207. }
  208. return err==CS_OK?0:err;
  209. }
  210. static int set_expected(int expected_votes)
  211. {
  212. int err;
  213. if ((err=votequorum_setexpected(v_handle, expected_votes)) != CS_OK) {
  214. fprintf(stderr, "Unable to set expected votes: %s\n", cs_strerror(err));
  215. }
  216. return err==CS_OK?0:err;
  217. }
  218. /*
  219. * node name by nodelist
  220. */
  221. static const char *node_name_by_nodelist(uint32_t nodeid)
  222. {
  223. cmap_iter_handle_t iter;
  224. char key_name[CMAP_KEYNAME_MAXLEN + 1];
  225. char tmp_key[CMAP_KEYNAME_MAXLEN + 1];
  226. static char ret_buf[_POSIX_HOST_NAME_MAX];
  227. char *str = NULL;
  228. uint32_t node_pos, cur_nodeid;
  229. int res = 0;
  230. if (cmap_iter_init(cmap_handle, "nodelist.node.", &iter) != CS_OK) {
  231. return "";
  232. }
  233. memset(ret_buf, 0, sizeof(ret_buf));
  234. while ((cmap_iter_next(cmap_handle, iter, key_name, NULL, NULL)) == CS_OK) {
  235. res = sscanf(key_name, "nodelist.node.%u.%s", &node_pos, tmp_key);
  236. if (res != 2) {
  237. continue;
  238. }
  239. if (strcmp(tmp_key, "ring0_addr") != 0) {
  240. continue;
  241. }
  242. snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
  243. if (cmap_get_uint32(cmap_handle, tmp_key, &cur_nodeid) != CS_OK) {
  244. continue;
  245. }
  246. if (cur_nodeid != nodeid) {
  247. continue;
  248. }
  249. snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos);
  250. if (cmap_get_string(cmap_handle, tmp_key, &str) != CS_OK) {
  251. continue;
  252. }
  253. if (!str) {
  254. continue;
  255. }
  256. strncpy(ret_buf, str, sizeof(ret_buf) - 1);
  257. free(str);
  258. break;
  259. }
  260. cmap_iter_finalize(cmap_handle, iter);
  261. return ret_buf;
  262. }
  263. /*
  264. * This resolves the first address assigned to a node
  265. * and returns the name or IP address. Use cfgtool if you need more information.
  266. */
  267. static const char *node_name(uint32_t nodeid, name_format_t name_format)
  268. {
  269. int err;
  270. int numaddrs;
  271. corosync_cfg_node_address_t addrs[INTERFACE_MAX];
  272. static char buf[(INET6_ADDRSTRLEN + 1) * INTERFACE_MAX];
  273. const char *nodelist_name = NULL;
  274. socklen_t addrlen;
  275. struct sockaddr_storage *ss;
  276. int start_addr = 0;
  277. int i;
  278. int bufptr = 0;
  279. buf[0] = '\0';
  280. /* If a name is required, always look for the nodelist node0_addr name first */
  281. if (name_format == ADDRESS_FORMAT_NAME) {
  282. nodelist_name = node_name_by_nodelist(nodeid);
  283. }
  284. if ((nodelist_name) &&
  285. (strlen(nodelist_name) > 0)) {
  286. start_addr = 1;
  287. strcpy(buf, nodelist_name);
  288. bufptr = strlen(buf);
  289. }
  290. err = corosync_cfg_get_node_addrs(c_handle, nodeid, INTERFACE_MAX, &numaddrs, addrs);
  291. if (err != CS_OK) {
  292. fprintf(stderr, "Unable to get node address for nodeid %u: %s\n", nodeid, cs_strerror(err));
  293. return "";
  294. }
  295. /* Don't show all addressess */
  296. if (!g_show_all_addrs) {
  297. numaddrs = 1;
  298. }
  299. for (i=start_addr; i<numaddrs; i++) {
  300. if (i) {
  301. buf[bufptr++] = ',';
  302. buf[bufptr++] = ' ';
  303. }
  304. ss = (struct sockaddr_storage *)addrs[i].address;
  305. if (ss->ss_family == AF_INET6) {
  306. addrlen = sizeof(struct sockaddr_in6);
  307. } else {
  308. addrlen = sizeof(struct sockaddr_in);
  309. }
  310. if (!getnameinfo(
  311. (struct sockaddr *)addrs[i].address, addrlen,
  312. buf+bufptr, sizeof(buf)-bufptr,
  313. NULL, 0,
  314. (name_format == ADDRESS_FORMAT_IP)?NI_NUMERICHOST:0)) {
  315. bufptr += strlen(buf+bufptr);
  316. }
  317. }
  318. return buf;
  319. }
  320. static void votequorum_notification_fn(
  321. votequorum_handle_t handle,
  322. uint64_t context,
  323. votequorum_ring_id_t ring_id,
  324. uint32_t node_list_entries,
  325. uint32_t node_list[])
  326. {
  327. g_ring_id_rep_node = ring_id.nodeid;
  328. g_vq_called = 1;
  329. }
  330. static void quorum_notification_fn(
  331. quorum_handle_t handle,
  332. uint32_t quorate,
  333. uint64_t ring_id,
  334. uint32_t view_list_entries,
  335. uint32_t *view_list)
  336. {
  337. int i;
  338. g_called = 1;
  339. g_quorate = quorate;
  340. g_ring_id = ring_id;
  341. g_view_list_entries = view_list_entries;
  342. if (g_view_list) {
  343. free(g_view_list);
  344. }
  345. g_view_list = malloc(sizeof(view_list_entry_t) * view_list_entries);
  346. if (g_view_list) {
  347. for (i=0; i< view_list_entries; i++) {
  348. g_view_list[i].node_id = view_list[i];
  349. g_view_list[i].name = NULL;
  350. g_view_list[i].vq_info = NULL;
  351. }
  352. }
  353. }
  354. static void print_string_padded(const char *buf)
  355. {
  356. int len, delta;
  357. len = strlen(buf);
  358. delta = 10 - len;
  359. while (delta > 0) {
  360. printf(" ");
  361. delta--;
  362. }
  363. printf("%s ", buf);
  364. }
  365. static void print_uint32_padded(uint32_t value)
  366. {
  367. char buf[12];
  368. snprintf(buf, sizeof(buf) - 1, "%u", value);
  369. print_string_padded(buf);
  370. }
  371. /* for qsort */
  372. static int compare_nodeids(const void *one, const void *two)
  373. {
  374. const view_list_entry_t *info1 = one;
  375. const view_list_entry_t *info2 = two;
  376. if (info1->node_id == info2->node_id) {
  377. return 0;
  378. }
  379. if (info1->node_id > info2->node_id) {
  380. return 1;
  381. }
  382. return -1;
  383. }
  384. static int compare_nodenames(const void *one, const void *two)
  385. {
  386. const view_list_entry_t *info1 = one;
  387. const view_list_entry_t *info2 = two;
  388. return strcmp(info1->name, info2->name);
  389. }
  390. static void display_nodes_data(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type)
  391. {
  392. int i, display_qdevice = 0;
  393. unsigned int our_flags = 0;
  394. struct votequorum_info info[g_view_list_entries];
  395. /*
  396. * cache node info because we need to parse them twice
  397. */
  398. if (v_handle) {
  399. for (i=0; i < g_view_list_entries; i++) {
  400. if (votequorum_getinfo(v_handle, g_view_list[i].node_id, &info[i]) != CS_OK) {
  401. printf("Unable to get node %u info\n", g_view_list[i].node_id);
  402. }
  403. g_view_list[i].vq_info = &info[i];
  404. if (info[i].flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED) {
  405. display_qdevice = 1;
  406. }
  407. }
  408. }
  409. /*
  410. * Get node names
  411. */
  412. for (i=0; i < g_view_list_entries; i++) {
  413. g_view_list[i].name = strdup(node_name(g_view_list[i].node_id, name_format));
  414. }
  415. printf("\nMembership information\n");
  416. printf("----------------------\n");
  417. print_string_padded("Nodeid");
  418. if (v_handle) {
  419. print_string_padded("Votes");
  420. if ((display_qdevice) || (machine_parsable)) {
  421. print_string_padded("Qdevice");
  422. }
  423. }
  424. printf("Name\n");
  425. /* corosync sends them already sorted by address */
  426. if (sort_type == SORT_NODEID) {
  427. qsort(g_view_list, g_view_list_entries, sizeof(view_list_entry_t), compare_nodeids);
  428. }
  429. if (sort_type == SORT_NODENAME) {
  430. qsort(g_view_list, g_view_list_entries, sizeof(view_list_entry_t), compare_nodenames);
  431. }
  432. for (i=0; i < g_view_list_entries; i++) {
  433. if (nodeid_format == NODEID_FORMAT_DECIMAL) {
  434. print_uint32_padded(g_view_list[i].node_id);
  435. } else {
  436. printf("0x%08x ", g_view_list[i].node_id);
  437. }
  438. if (v_handle) {
  439. int votes = -1;
  440. votes = info[i].node_votes;
  441. print_uint32_padded(votes);
  442. if ((display_qdevice) || (machine_parsable)) {
  443. if (info[i].flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED) {
  444. char buf[10];
  445. snprintf(buf, sizeof(buf),
  446. "%s,%s,%s",
  447. info[i].flags & VOTEQUORUM_INFO_QDEVICE_ALIVE?"A":"NA",
  448. info[i].flags & VOTEQUORUM_INFO_QDEVICE_CAST_VOTE?"V":"NV",
  449. info[i].flags & VOTEQUORUM_INFO_QDEVICE_MASTER_WINS?"MW":"NMW");
  450. print_string_padded(buf);
  451. } else {
  452. print_string_padded("NR");
  453. }
  454. }
  455. }
  456. printf("%s", g_view_list[i].name);
  457. if (g_view_list[i].node_id == our_nodeid) {
  458. printf(" (local)");
  459. if (v_handle) {
  460. our_flags = info[i].flags;
  461. }
  462. }
  463. printf("\n");
  464. }
  465. if (g_view_list_entries) {
  466. for (i=0; i < g_view_list_entries; i++) {
  467. free(g_view_list[i].name);
  468. }
  469. free(g_view_list);
  470. g_view_list = NULL;
  471. }
  472. if (display_qdevice) {
  473. if (nodeid_format == NODEID_FORMAT_DECIMAL) {
  474. print_uint32_padded(VOTEQUORUM_QDEVICE_NODEID);
  475. } else {
  476. printf("0x%08x ", VOTEQUORUM_QDEVICE_NODEID);
  477. }
  478. /* If the quorum device is inactive on this node then show votes as 0
  479. so that the display is not confusing */
  480. if (our_flags & VOTEQUORUM_INFO_QDEVICE_CAST_VOTE) {
  481. print_uint32_padded(info[0].qdevice_votes);
  482. }
  483. else {
  484. print_uint32_padded(0);
  485. }
  486. printf(" %s", info[0].qdevice_name);
  487. if (our_flags & VOTEQUORUM_INFO_QDEVICE_CAST_VOTE) {
  488. printf("\n");
  489. }
  490. else {
  491. printf(" (votes %d)\n", info[0].qdevice_votes);
  492. }
  493. }
  494. }
  495. static int display_quorum_data(int is_quorate,
  496. nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type,
  497. int loop)
  498. {
  499. struct votequorum_info info;
  500. int err;
  501. char quorumtype[256];
  502. time_t t;
  503. memset(quorumtype, 0, sizeof(quorumtype));
  504. printf("Quorum information\n");
  505. printf("------------------\n");
  506. time(&t);
  507. printf("Date: %s", ctime((const time_t *)&t));
  508. if (get_quorum_type(quorumtype, sizeof(quorumtype))) {
  509. strncpy(quorumtype, "Not configured", sizeof(quorumtype) - 1);
  510. }
  511. printf("Quorum provider: %s\n", quorumtype);
  512. printf("Nodes: %d\n", g_view_list_entries);
  513. if (nodeid_format == NODEID_FORMAT_DECIMAL) {
  514. printf("Node ID: %u\n", our_nodeid);
  515. } else {
  516. printf("Node ID: 0x%08x\n", our_nodeid);
  517. }
  518. if (v_handle) {
  519. printf("Ring ID: %d/%" PRIu64 "\n", g_ring_id_rep_node, g_ring_id);
  520. }
  521. else {
  522. printf("Ring ID: %" PRIu64 "\n", g_ring_id);
  523. }
  524. printf("Quorate: %s\n", is_quorate?"Yes":"No");
  525. if (!v_handle) {
  526. return CS_OK;
  527. }
  528. err=votequorum_getinfo(v_handle, our_nodeid, &info);
  529. if ((err == CS_OK) || (err == CS_ERR_NOT_EXIST)) {
  530. printf("\nVotequorum information\n");
  531. printf("----------------------\n");
  532. printf("Expected votes: %d\n", info.node_expected_votes);
  533. printf("Highest expected: %d\n", info.highest_expected);
  534. printf("Total votes: %d\n", info.total_votes);
  535. printf("Quorum: %d %s\n", info.quorum, info.flags & VOTEQUORUM_INFO_QUORATE?" ":"Activity blocked");
  536. printf("Flags: ");
  537. if (info.flags & VOTEQUORUM_INFO_TWONODE) printf("2Node ");
  538. if (info.flags & VOTEQUORUM_INFO_QUORATE) printf("Quorate ");
  539. if (info.flags & VOTEQUORUM_INFO_WAIT_FOR_ALL) printf("WaitForAll ");
  540. if (info.flags & VOTEQUORUM_INFO_LAST_MAN_STANDING) printf("LastManStanding ");
  541. if (info.flags & VOTEQUORUM_INFO_AUTO_TIE_BREAKER) printf("AutoTieBreaker ");
  542. if (info.flags & VOTEQUORUM_INFO_ALLOW_DOWNSCALE) printf("AllowDownscale ");
  543. if (info.flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED) printf("Qdevice ");
  544. printf("\n");
  545. } else {
  546. fprintf(stderr, "Unable to get node info: %s\n", cs_strerror(err));
  547. }
  548. display_nodes_data(nodeid_format, name_format, sort_type);
  549. return err;
  550. }
  551. /*
  552. * return 1 if quorate
  553. * 0 if not quorate
  554. * -1 on error
  555. */
  556. static int show_status(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type)
  557. {
  558. int is_quorate;
  559. int err;
  560. err=quorum_getquorate(q_handle, &is_quorate);
  561. if (err != CS_OK) {
  562. fprintf(stderr, "Unable to get cluster quorate status: %s\n", cs_strerror(err));
  563. goto quorum_err;
  564. }
  565. err=quorum_trackstart(q_handle, CS_TRACK_CURRENT);
  566. if (err != CS_OK) {
  567. fprintf(stderr, "Unable to start quorum status tracking: %s\n", cs_strerror(err));
  568. goto quorum_err;
  569. }
  570. g_called = 0;
  571. while (g_called == 0 && err == CS_OK) {
  572. err = quorum_dispatch(q_handle, CS_DISPATCH_ONE);
  573. if (err != CS_OK) {
  574. fprintf(stderr, "Unable to dispatch quorum status: %s\n", cs_strerror(err));
  575. }
  576. }
  577. if (quorum_trackstop(q_handle) != CS_OK) {
  578. fprintf(stderr, "Unable to stop quorum status tracking: %s\n", cs_strerror(err));
  579. }
  580. if (using_votequorum()) {
  581. if ( (err=votequorum_trackstart(v_handle, 0LL, CS_TRACK_CURRENT)) != CS_OK) {
  582. fprintf(stderr, "Unable to start votequorum status tracking: %s\n", cs_strerror(err));
  583. goto quorum_err;
  584. }
  585. g_vq_called = 0;
  586. while (g_vq_called == 0 && err == CS_OK) {
  587. err = votequorum_dispatch(v_handle, CS_DISPATCH_ONE);
  588. if (err != CS_OK) {
  589. fprintf(stderr, "Unable to dispatch votequorum status: %s\n", cs_strerror(err));
  590. }
  591. }
  592. }
  593. quorum_err:
  594. if (err != CS_OK) {
  595. return -1;
  596. }
  597. err = display_quorum_data(is_quorate, nodeid_format, name_format, sort_type, 0);
  598. if (err != CS_OK) {
  599. return -1;
  600. }
  601. return is_quorate;
  602. }
  603. static int monitor_status(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type) {
  604. int err;
  605. int loop = 0;
  606. if (q_type == QUORUM_FREE) {
  607. printf("\nQuorum is not configured - cannot monitor\n");
  608. return show_status(nodeid_format, name_format, sort_type);
  609. }
  610. err=quorum_trackstart(q_handle, CS_TRACK_CHANGES);
  611. if (err != CS_OK) {
  612. fprintf(stderr, "Unable to start quorum status tracking: %s\n", cs_strerror(err));
  613. goto quorum_err;
  614. }
  615. if (using_votequorum()) {
  616. if ( (err=votequorum_trackstart(v_handle, 0LL, CS_TRACK_CHANGES)) != CS_OK) {
  617. fprintf(stderr, "Unable to start votequorum status tracking: %s\n", cs_strerror(err));
  618. goto quorum_err;
  619. }
  620. }
  621. while (1) {
  622. err = quorum_dispatch(q_handle, CS_DISPATCH_ONE);
  623. if (err != CS_OK) {
  624. fprintf(stderr, "Unable to dispatch quorum status: %s\n", cs_strerror(err));
  625. goto quorum_err;
  626. }
  627. if (using_votequorum()) {
  628. g_vq_called = 0;
  629. while (!g_vq_called) {
  630. err = votequorum_dispatch(v_handle, CS_DISPATCH_ONE);
  631. if (err != CS_OK) {
  632. fprintf(stderr, "Unable to dispatch votequorum status: %s\n", cs_strerror(err));
  633. goto quorum_err;
  634. }
  635. }
  636. }
  637. err = display_quorum_data(g_quorate, nodeid_format, name_format, sort_type, loop);
  638. printf("\n");
  639. loop = 1;
  640. if (err != CS_OK) {
  641. fprintf(stderr, "Unable to display quorum data: %s\n", cs_strerror(err));
  642. goto quorum_err;
  643. }
  644. }
  645. quorum_err:
  646. return -1;
  647. }
  648. static int show_nodes(nodeid_format_t nodeid_format, name_format_t name_format, sorttype_t sort_type)
  649. {
  650. int err;
  651. int result = EXIT_FAILURE;
  652. err = quorum_trackstart(q_handle, CS_TRACK_CURRENT);
  653. if (err != CS_OK) {
  654. fprintf(stderr, "Unable to start quorum status tracking: %s\n", cs_strerror(err));
  655. goto err_exit;
  656. }
  657. g_called = 0;
  658. while (g_called == 0) {
  659. err = quorum_dispatch(q_handle, CS_DISPATCH_ONE);
  660. if (err != CS_OK) {
  661. fprintf(stderr, "Unable to dispatch quorum status: %s\n", cs_strerror(err));
  662. goto err_exit;
  663. }
  664. }
  665. display_nodes_data(nodeid_format, name_format, sort_type);
  666. result = EXIT_SUCCESS;
  667. err_exit:
  668. return result;
  669. }
  670. static int unregister_qdevice(void)
  671. {
  672. int err;
  673. struct votequorum_info info;
  674. err = votequorum_getinfo(v_handle, our_nodeid, &info);
  675. if (err != CS_OK) {
  676. fprintf(stderr, "Unable to get quorum device info: %s\n", cs_strerror(err));
  677. return -1;
  678. }
  679. if (!(info.flags & VOTEQUORUM_INFO_QDEVICE_REGISTERED)) {
  680. return 0;
  681. }
  682. err = votequorum_qdevice_unregister(v_handle, info.qdevice_name);
  683. if (err != CS_OK) {
  684. fprintf(stderr, "Unable to unregister quorum device: %s\n", cs_strerror(err));
  685. return -1;
  686. }
  687. return 0;
  688. }
  689. /*
  690. * return -1 on error
  691. * 0 if OK
  692. */
  693. static int init_all(void) {
  694. cmap_handle = 0;
  695. q_handle = 0;
  696. v_handle = 0;
  697. c_handle = 0;
  698. if (cmap_initialize(&cmap_handle) != CS_OK) {
  699. fprintf(stderr, "Cannot initialize CMAP service\n");
  700. cmap_handle = 0;
  701. goto out;
  702. }
  703. if (quorum_initialize(&q_handle, &q_callbacks, &q_type) != CS_OK) {
  704. fprintf(stderr, "Cannot initialize QUORUM service\n");
  705. q_handle = 0;
  706. goto out;
  707. }
  708. if (corosync_cfg_initialize(&c_handle, &c_callbacks) != CS_OK) {
  709. fprintf(stderr, "Cannot initialise CFG service\n");
  710. c_handle = 0;
  711. goto out;
  712. }
  713. if (using_votequorum() <= 0) {
  714. return 0;
  715. }
  716. if (votequorum_initialize(&v_handle, &v_callbacks) != CS_OK) {
  717. fprintf(stderr, "Cannot initialise VOTEQUORUM service\n");
  718. v_handle = 0;
  719. goto out;
  720. }
  721. if (cmap_get_uint32(cmap_handle, "runtime.votequorum.this_node_id", &our_nodeid) != CS_OK) {
  722. fprintf(stderr, "Unable to retrieve this_node_id\n");
  723. goto out;
  724. }
  725. return 0;
  726. out:
  727. return -1;
  728. }
  729. static void close_all(void) {
  730. if (cmap_handle) {
  731. cmap_finalize(cmap_handle);
  732. }
  733. if (q_handle) {
  734. quorum_finalize(q_handle);
  735. }
  736. if (c_handle) {
  737. corosync_cfg_finalize(c_handle);
  738. }
  739. if (v_handle) {
  740. votequorum_finalize(v_handle);
  741. }
  742. }
  743. int main (int argc, char *argv[]) {
  744. const char *options = "VHaslpmfe:v:hin:o:";
  745. char *endptr;
  746. int opt;
  747. int votes = 0;
  748. int ret = 0;
  749. uint32_t nodeid = 0;
  750. uint32_t nodeid_set = 0;
  751. nodeid_format_t nodeid_format = NODEID_FORMAT_DECIMAL;
  752. name_format_t address_format = ADDRESS_FORMAT_NAME;
  753. command_t command_opt = CMD_SHOWSTATUS;
  754. sorttype_t sort_opt = SORT_ADDR;
  755. char sortchar;
  756. long int l;
  757. if (init_all()) {
  758. close_all();
  759. exit(1);
  760. }
  761. while ( (opt = getopt(argc, argv, options)) != -1 ) {
  762. switch (opt) {
  763. case 'f':
  764. if (using_votequorum() > 0) {
  765. command_opt = CMD_UNREGISTER_QDEVICE;
  766. } else {
  767. fprintf(stderr, "You cannot unregister quorum device, corosync is not using votequorum\n");
  768. exit(2);
  769. }
  770. break;
  771. case 's':
  772. command_opt = CMD_SHOWSTATUS;
  773. break;
  774. case 'a':
  775. g_show_all_addrs = 1;
  776. break;
  777. case 'm':
  778. command_opt = CMD_MONITOR;
  779. break;
  780. case 'i':
  781. address_format = ADDRESS_FORMAT_IP;
  782. break;
  783. case 'H':
  784. nodeid_format = NODEID_FORMAT_HEX;
  785. break;
  786. case 'l':
  787. command_opt = CMD_SHOWNODES;
  788. break;
  789. case 'p':
  790. machine_parsable = 1;
  791. break;
  792. case 'e':
  793. if (using_votequorum() > 0) {
  794. votes = strtol(optarg, &endptr, 0);
  795. if ((votes == 0 && endptr == optarg) || votes <= 0) {
  796. fprintf(stderr, "New expected votes value was not valid, try a positive number\n");
  797. } else {
  798. command_opt = CMD_SETEXPECTED;
  799. }
  800. } else {
  801. fprintf(stderr, "You cannot change expected votes, corosync is not using votequorum\n");
  802. exit(2);
  803. }
  804. break;
  805. case 'n':
  806. l = strtol(optarg, &endptr, 0);
  807. if ((l == 0 && endptr == optarg) || l < 0) {
  808. fprintf(stderr, "The nodeid was not valid, try a positive number\n");
  809. exit(2);
  810. }
  811. nodeid = l;
  812. nodeid_set = 1;
  813. break;
  814. case 'v':
  815. if (using_votequorum() > 0) {
  816. votes = strtol(optarg, &endptr, 0);
  817. if ((votes == 0 && endptr == optarg) || votes < 0) {
  818. fprintf(stderr, "New votes value was not valid, try a positive number or zero\n");
  819. exit(2);
  820. } else {
  821. command_opt = CMD_SETVOTES;
  822. }
  823. }
  824. else {
  825. fprintf(stderr, "You cannot change node votes, corosync is not using votequorum\n");
  826. exit(2);
  827. }
  828. break;
  829. case 'o':
  830. sortchar = optarg[0];
  831. switch (sortchar) {
  832. case 'a': sort_opt = SORT_ADDR;
  833. break;
  834. case 'i': sort_opt = SORT_NODEID;
  835. break;
  836. case 'n': sort_opt = SORT_NODENAME;
  837. break;
  838. default:
  839. fprintf(stderr, "Invalid ordering option. valid orders are a(address), i(node ID) or n(name)\n");
  840. exit(2);
  841. break;
  842. }
  843. break;
  844. case 'V':
  845. printf("corosync-quorumtool version: %s\n", VERSION);
  846. exit(0);
  847. case ':':
  848. case 'h':
  849. case '?':
  850. default:
  851. command_opt = CMD_UNKNOWN;
  852. break;
  853. }
  854. }
  855. switch (command_opt) {
  856. case CMD_UNKNOWN:
  857. show_usage(argv[0]);
  858. ret = -1;
  859. break;
  860. case CMD_SHOWNODES:
  861. ret = show_nodes(nodeid_format, address_format, sort_opt);
  862. break;
  863. case CMD_SHOWSTATUS:
  864. ret = show_status(nodeid_format, address_format, sort_opt);
  865. break;
  866. case CMD_SETVOTES:
  867. if (!nodeid_set) {
  868. nodeid = our_nodeid;
  869. }
  870. ret = set_votes(nodeid, votes);
  871. break;
  872. case CMD_SETEXPECTED:
  873. ret = set_expected(votes);
  874. break;
  875. case CMD_MONITOR:
  876. ret = monitor_status(nodeid_format, address_format, sort_opt);
  877. break;
  878. case CMD_UNREGISTER_QDEVICE:
  879. ret = unregister_qdevice();
  880. break;
  881. }
  882. close_all();
  883. return (ret);
  884. }