Browse Source

Improve qdevice

- Add support for cmap node list configuration change
- Add client side algorithms
- Check if currently received ring id in membership message
  equals to last sent ring id
- Send config node list only if config node list really changes and not
  after every reload
- Add tlv_ring_id_eq (replacing qnetd_algo_rings_eq) so it's usable in
  client
- Move debug logs from algo-test into qnetd-log-debug.c and call them in
  proper places (= logs are now algorithm independent)
- Fix memory leak in msg

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
Jan Friesse 10 years ago
parent
commit
6c1add2925
80 changed files with 3051 additions and 387 deletions
  1. 7 4
      qdevices/Makefile.am
  2. 393 106
      qdevices/corosync-qdevice-net.c
  3. 1 1
      qdevices/corosync-qnetd-certutil.sh
  4. 6 6
      qdevices/corosync-qnetd.c
  5. 1 1
      qdevices/dynar.c
  6. 1 1
      qdevices/dynar.h
  7. 13 1
      qdevices/msg.c
  8. 2 1
      qdevices/msg.h
  9. 1 1
      qdevices/msgio.c
  10. 1 1
      qdevices/msgio.h
  11. 58 2
      qdevices/node-list.c
  12. 8 2
      qdevices/node-list.h
  13. 1 7
      qdevices/nss-sock.c
  14. 1 3
      qdevices/nss-sock.h
  15. 165 0
      qdevices/qdevice-net-algo-2nodelms.c
  16. 91 0
      qdevices/qdevice-net-algo-2nodelms.h
  17. 165 0
      qdevices/qdevice-net-algo-ffsplit.c
  18. 91 0
      qdevices/qdevice-net-algo-ffsplit.h
  19. 165 0
      qdevices/qdevice-net-algo-lms.c
  20. 91 0
      qdevices/qdevice-net-algo-lms.h
  21. 285 0
      qdevices/qdevice-net-algo-test.c
  22. 91 0
      qdevices/qdevice-net-algo-test.h
  23. 290 0
      qdevices/qdevice-net-algorithm.c
  24. 118 0
      qdevices/qdevice-net-algorithm.h
  25. 4 3
      qdevices/qdevice-net-cast-vote-timer.c
  26. 1 1
      qdevices/qdevice-net-cast-vote-timer.h
  27. 142 1
      qdevices/qdevice-net-cmap.c
  28. 10 1
      qdevices/qdevice-net-cmap.h
  29. 108 0
      qdevices/qdevice-net-disconnect-reason.h
  30. 38 2
      qdevices/qdevice-net-instance.c
  31. 15 2
      qdevices/qdevice-net-instance.h
  32. 1 1
      qdevices/qdevice-net-log.h
  33. 33 0
      qdevices/qdevice-net-msg-received.c
  34. 47 0
      qdevices/qdevice-net-msg-received.h
  35. 50 26
      qdevices/qdevice-net-send.c
  36. 2 2
      qdevices/qdevice-net-send.h
  37. 79 20
      qdevices/qdevice-net-votequorum.c
  38. 3 1
      qdevices/qdevice-net-votequorum.h
  39. 10 1
      qdevices/qnet-config.h
  40. 1 1
      qdevices/qnetd-algo-2nodelms.c
  41. 1 1
      qdevices/qnetd-algo-2nodelms.h
  42. 1 1
      qdevices/qnetd-algo-ffsplit.c
  43. 1 1
      qdevices/qnetd-algo-ffsplit.h
  44. 3 3
      qdevices/qnetd-algo-lms.c
  45. 1 1
      qdevices/qnetd-algo-lms.h
  46. 9 88
      qdevices/qnetd-algo-test.c
  47. 1 1
      qdevices/qnetd-algo-test.h
  48. 19 26
      qdevices/qnetd-algo-utils.c
  49. 24 6
      qdevices/qnetd-algo-utils.h
  50. 36 27
      qdevices/qnetd-algorithm.c
  51. 2 4
      qdevices/qnetd-algorithm.h
  52. 1 1
      qdevices/qnetd-client-list.c
  53. 1 1
      qdevices/qnetd-client-list.h
  54. 40 3
      qdevices/qnetd-client-msg-received.c
  55. 1 1
      qdevices/qnetd-client-msg-received.h
  56. 1 1
      qdevices/qnetd-client-net.c
  57. 1 1
      qdevices/qnetd-client-net.h
  58. 4 1
      qdevices/qnetd-client-send.c
  59. 1 1
      qdevices/qnetd-client-send.h
  60. 1 1
      qdevices/qnetd-client.c
  61. 1 1
      qdevices/qnetd-client.h
  62. 1 1
      qdevices/qnetd-cluster-list.c
  63. 1 1
      qdevices/qnetd-cluster-list.h
  64. 5 1
      qdevices/qnetd-instance.c
  65. 1 1
      qdevices/qnetd-instance.h
  66. 162 0
      qdevices/qnetd-log-debug.c
  67. 79 0
      qdevices/qnetd-log-debug.h
  68. 1 1
      qdevices/qnetd-log.c
  69. 1 1
      qdevices/qnetd-log.h
  70. 1 1
      qdevices/qnetd-poll-array.c
  71. 1 1
      qdevices/qnetd-poll-array.h
  72. 5 1
      qdevices/send-buffer-list.c
  73. 1 1
      qdevices/send-buffer-list.h
  74. 1 1
      qdevices/test-qnetd-cluster-list.c
  75. 1 1
      qdevices/timer-list.c
  76. 1 1
      qdevices/timer-list.h
  77. 39 1
      qdevices/tlv.c
  78. 8 1
      qdevices/tlv.h
  79. 1 1
      qdevices/utils.c
  80. 1 1
      qdevices/utils.h

+ 7 - 4
qdevices/Makefile.am

@@ -1,6 +1,7 @@
-# Copyright (c) 2012 Red Hat, Inc.
+# Copyright (c) 2012-2016 Red Hat, Inc.
 #
-# Authors: Fabio M. Di Nitto <fdinitto@redhat.com>
+# Authors: Fabio M. Di Nitto (fdinitto@redhat.com)
+#          Jan Friesse (jfriesse@redhat.com)
 #
 # This software licensed under BSD license, the text of which follows:
 #
@@ -44,14 +45,16 @@ corosync_qnetd_SOURCES	= corosync-qnetd.c dynar.c msg.c msgio.c nss-sock.c  \
 			    node-list.c qnetd-algo-test.c qnetd-algorithm.c qnetd-algo-utils.c \
 			    qnetd-algo-ffsplit.c qnetd-cluster-list.c qnetd-client-send.c \
 			    qnetd-algo-2nodelms.c qnetd-algo-lms.c utils.c qnetd-instance.c \
-			    qnetd-client-net.c qnetd-client-msg-received.c
+			    qnetd-client-net.c qnetd-client-msg-received.c qnetd-log-debug.c
 
 corosync_qdevice_net_SOURCES	= corosync-qdevice-net.c dynar.c msg.c msgio.c nss-sock.c  \
 			    qnetd-client.c qnetd-client-list.c qnetd-log.c \
 			    qnetd-poll-array.c timer-list.c tlv.c send-buffer-list.c \
 			    node-list.c qdevice-net-send.c qdevice-net-cmap.c \
 			    qdevice-net-votequorum.c qdevice-net-instance.c \
-			    qdevice-net-cast-vote-timer.c utils.c
+			    qdevice-net-cast-vote-timer.c utils.c qdevice-net-algorithm.c \
+			    qdevice-net-algo-test.c qdevice-net-algo-ffsplit.c \
+			    qdevice-net-algo-2nodelms.c qdevice-net-algo-lms.c
 
 corosync_qnetd_CFLAGS		= $(nss_CFLAGS)
 corosync_qnetd_LDADD		= $(nss_LIBS)

+ 393 - 106
qdevices/corosync-qdevice-net.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -72,6 +72,7 @@
 #include "qdevice-net-votequorum.h"
 #include "qdevice-net-cast-vote-timer.h"
 #include "utils.h"
+#include "qdevice-net-algorithm.h"
 
 static SECStatus
 qdevice_net_nss_bad_cert_hook(void *arg, PRFileDesc *fd) {
@@ -129,7 +130,7 @@ qdevice_net_log_msg_decode_error(int ret)
  *  1 - Use TLS
  */
 static int
-qdevice_net_check_tls_compatibility(enum tlv_tls_supported server_tls,
+qdevice_net_msg_received_check_tls_compatibility(enum tlv_tls_supported server_tls,
     enum tlv_tls_supported client_tls)
 {
 	int res;
@@ -171,6 +172,8 @@ qdevice_net_msg_received_unexpected_msg(struct qdevice_net_instance *instance,
 	qdevice_net_log(LOG_ERR, "Received unexpected %s message. Disconnecting from server",
 	    msg_str);
 
+	instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
 	return (-1);
 }
 
@@ -197,28 +200,6 @@ qdevice_net_msg_check_seq_number(struct qdevice_net_instance *instance,
 	return (0);
 }
 
-static int
-qdevice_net_msg_check_echo_reply_seq_number(struct qdevice_net_instance *instance,
-    const struct msg_decoded *msg)
-{
-
-	if (!msg->seq_number_set) {
-		qdevice_net_log(LOG_ERR, "Received echo reply message doesn't contain seq_number.");
-
-		return (-1);
-	}
-
-	if (msg->seq_number != instance->echo_request_expected_msg_seq_num) {
-		qdevice_net_log(LOG_ERR, "Server doesn't replied in expected time. "
-		    "Closing connection");
-
-		return (-1);
-	}
-
-	return (0);
-}
-
-
 static int
 qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance,
     const struct msg_decoded *msg)
@@ -230,10 +211,14 @@ qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Received unexpected preinit reply message. "
 		    "Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
 		return (-1);
 	}
 
 	if (qdevice_net_msg_check_seq_number(instance, msg) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
@@ -244,14 +229,18 @@ qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Required tls_supported or tls_client_cert_required "
 		    "option is unset");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
-	res = qdevice_net_check_tls_compatibility(msg->tls_supported, instance->tls_supported);
+	res = qdevice_net_msg_received_check_tls_compatibility(msg->tls_supported, instance->tls_supported);
 	if (res == -1) {
 		qdevice_net_log(LOG_ERR, "Incompatible tls configuration (server %u client %u)",
 		    msg->tls_supported, instance->tls_supported);
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_TLS;
+
 		return (-1);
 	} else if (res == 1) {
 		/*
@@ -262,6 +251,9 @@ qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance,
 			qdevice_net_log(LOG_ERR, "Can't allocate send list buffer for "
 			    "starttls msg");
 
+			instance->disconnect_reason =
+			    QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
 			return (-1);
 		}
 
@@ -270,6 +262,9 @@ qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance,
 		    instance->last_msg_seq_num) == 0) {
 			qdevice_net_log(LOG_ERR, "Can't allocate send buffer for starttls msg");
 
+			instance->disconnect_reason =
+			    QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
 			return (-1);
 		}
 
@@ -278,6 +273,9 @@ qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance,
 		instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_STARTTLS_BEING_SENT;
 	} else if (res == 0) {
 		if (qdevice_net_send_init(instance) != 0) {
+			instance->disconnect_reason =
+			    QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
 			return (-1);
 		}
 	}
@@ -297,10 +295,14 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Received unexpected init reply message. "
 		    "Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
 		return (-1);
 	}
 
 	if (qdevice_net_msg_check_seq_number(instance, msg) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
@@ -308,6 +310,8 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Received init reply message without error code."
 		    "Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
@@ -315,6 +319,7 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Received init reply message with error code %"PRIu16". "
 		    "Disconnecting from server", msg->reply_error_code);
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR;
 		return (-1);
 	}
 
@@ -322,6 +327,8 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Required maximum_request_size or maximum_reply_size "
 		    "option is unset");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
@@ -329,12 +336,16 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Required supported messages or supported options "
 		    "option is unset");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
 	if (msg->supported_decision_algorithms == NULL) {
 		qdevice_net_log(LOG_ERR, "Required supported decision algorithms option is unset");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
@@ -343,6 +354,7 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 		    "Server accepts maximum %zu bytes message but this client minimum "
 		    "is %zu bytes.", msg->server_maximum_request_size, instance->min_send_size);
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE;
 		return (-1);
 	}
 
@@ -351,6 +363,7 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 		    "Server may send message up to %zu bytes message but this client maximum "
 		    "is %zu bytes.", msg->server_maximum_reply_size, instance->max_receive_size);
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE;
 		return (-1);
 	}
 
@@ -376,6 +389,9 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 	if (!res) {
 		qdevice_net_log(LOG_ERR, "Server doesn't support required decision algorithm");
 
+		instance->disconnect_reason =
+		    QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_ALGORITHM;
+
 		return (-1);
 	}
 
@@ -386,6 +402,8 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 	if (send_buffer == NULL) {
 		qdevice_net_log(LOG_ERR, "Can't allocate send list buffer for set option msg");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
 		return (-1);
 	}
 
@@ -395,6 +413,8 @@ qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
 	    1, instance->heartbeat_interval, 1, &instance->tie_breaker) == 0) {
 		qdevice_net_log(LOG_ERR, "Can't allocate send buffer for set option msg");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
 		return (-1);
 	}
 
@@ -421,9 +441,13 @@ qdevice_net_msg_received_server_error(struct qdevice_net_instance *instance,
 	if (!msg->reply_error_code_set) {
 		qdevice_net_log(LOG_ERR, "Received server error without error code set. "
 		    "Disconnecting from server");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
 	} else {
 		qdevice_net_log(LOG_ERR, "Received server error %"PRIu16". "
 		    "Disconnecting from server", msg->reply_error_code);
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR;
 	}
 
 	return (-1);
@@ -444,7 +468,28 @@ qdevice_net_timer_send_heartbeat(void *data1, void *data2)
 
 	instance = (struct qdevice_net_instance *)data1;
 
+	if (instance->echo_reply_received_msg_seq_num !=
+	    instance->echo_request_expected_msg_seq_num) {
+		qdevice_net_log(LOG_ERR, "Server didn't send echo reply message on time");
+
+		if (qdevice_net_algorithm_echo_reply_not_received(instance) != 0) {
+			qdevice_net_log(LOG_DEBUG, "Algorithm decided to disconnect");
+
+			instance->schedule_disconnect = 1;
+			instance->disconnect_reason =
+			    QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_NOT_RECEIVED_ERR;
+
+			return (0);
+		} else {
+			qdevice_net_log(LOG_DEBUG, "Algorithm decided to continue send heartbeat");
+
+			return (-1);
+		}
+	}
+
 	if (qdevice_net_send_echo_request(instance) == -1) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
 		instance->schedule_disconnect = 1;
 		return (0);
 	}
@@ -480,16 +525,24 @@ qdevice_net_msg_received_set_option_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Received unexpected set option reply message. "
 		    "Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
 		return (-1);
 	}
 
 	if (qdevice_net_msg_check_seq_number(instance, msg) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
 	if (!msg->decision_algorithm_set || !msg->heartbeat_interval_set) {
 		qdevice_net_log(LOG_ERR, "Received set option reply message without "
 		    "required options. Disconnecting from server");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+		return (-1);
 	}
 
 	if (msg->decision_algorithm != instance->decision_algorithm ||
@@ -497,6 +550,8 @@ qdevice_net_msg_received_set_option_reply(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Server doesn't accept sent decision algorithm or "
 		    "heartbeat interval.");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
 		return (-1);
 	}
 
@@ -504,6 +559,9 @@ qdevice_net_msg_received_set_option_reply(struct qdevice_net_instance *instance,
 	 * Server accepted heartbeat interval -> schedule regular sending of echo request
 	 */
 	if (instance->heartbeat_interval > 0) {
+		instance->echo_request_expected_msg_seq_num = 0;
+		instance->echo_reply_received_msg_seq_num = 0;
+
 		instance->echo_request_timer = timer_list_add(&instance->main_timer_list,
 		    instance->heartbeat_interval, qdevice_net_timer_send_heartbeat,
 		    (void *)instance, NULL);
@@ -511,24 +569,40 @@ qdevice_net_msg_received_set_option_reply(struct qdevice_net_instance *instance,
 		if (instance->echo_request_timer == NULL) {
 			qdevice_net_log(LOG_ERR, "Can't schedule regular sending of heartbeat.");
 
+			instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_HB_TIMER;
+
 			return (-1);
 		}
 	}
 
+	if (qdevice_net_algorithm_connected(instance) != 0) {
+		qdevice_net_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_CONNECTED_ERR;
+		return (-1);
+	}
+
 	/*
 	 * Now we can finally really send node list and initialize qdevice
 	 */
-	if (qdevice_net_send_config_node_list(instance, 1) != 0) {
+	if (qdevice_net_send_config_node_list(instance, 1, 1) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
 		return (-1);
 	}
 
 	if (qdevice_net_register_votequorum_callbacks(instance) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_VOTEQUORUM_CALLBACK;
+		return (-1);
+	}
+
+	if (qdevice_net_cmap_add_track(instance) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_CMAP_CALLBACK;
 		return (-1);
 	}
 
 	if (qdevice_net_cast_vote_timer_update(instance, TLV_VOTE_WAIT_FOR_REPLY) != 0) {
-		errx(1, "qdevice_net_msg_received_set_option_reply fatal error. Can't update "
-		    "cast vote timer vote");
+		qdevice_net_log(LOG_CRIT, "qdevice_net_msg_received_set_option_reply fatal error. "
+		    " Can't update cast vote timer vote");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
 	}
 
 	instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS;
@@ -549,7 +623,22 @@ qdevice_net_msg_received_echo_reply(struct qdevice_net_instance *instance,
     const struct msg_decoded *msg)
 {
 
-	if (qdevice_net_msg_check_echo_reply_seq_number(instance, msg) != 0) {
+	if (!msg->seq_number_set) {
+		qdevice_net_log(LOG_ERR, "Received echo reply message doesn't contain seq_number.");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+		return (-1);
+	}
+
+	if (msg->seq_number != instance->echo_request_expected_msg_seq_num) {
+		qdevice_net_log(LOG_WARNING, "Received echo reply message seq_number is not expected one.");
+	}
+
+	if (qdevice_net_algorithm_echo_reply_received(instance, msg->seq_number,
+	    msg->seq_number == instance->echo_request_expected_msg_seq_num) != 0) {
+		qdevice_net_log(LOG_DEBUG, "Algorithm returned error. Disconnecting");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_RECEIVED_ERR;
 		return (-1);
 	}
 
@@ -570,30 +659,99 @@ static int
 qdevice_net_msg_received_node_list_reply(struct qdevice_net_instance *instance,
     const struct msg_decoded *msg)
 {
+	const char *str;
+	enum tlv_vote result_vote;
+	int res;
 
 	if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
 		qdevice_net_log(LOG_ERR, "Received unexpected node list reply message. "
 		    "Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
 		return (-1);
 	}
 
-	if (!msg->vote_set || !msg->seq_number_set) {
+	if (!msg->vote_set || !msg->seq_number_set || !msg->node_list_type_set) {
 		qdevice_net_log(LOG_ERR, "Received node list reply message without "
 		    "required options. Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+		return (-1);
+	}
+
+	if (msg->node_list_type == TLV_NODE_LIST_TYPE_MEMBERSHIP && !msg->ring_id_set) {
+		qdevice_net_log(LOG_ERR, "Received node list reply message with type membership "
+		    "without ring id set. Disconnecting from server");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
 		return (-1);
 	}
 
+	switch (msg->node_list_type) {
+	case TLV_NODE_LIST_TYPE_INITIAL_CONFIG: str = "initial config"; break;
+	case TLV_NODE_LIST_TYPE_CHANGED_CONFIG: str = "changed config"; break;
+	case TLV_NODE_LIST_TYPE_MEMBERSHIP: str ="membership"; break;
+	case TLV_NODE_LIST_TYPE_QUORUM: str ="quorum"; break;
+	default:
+		qdevice_net_log(LOG_CRIT, "qdevice_net_msg_received_node_list_reply fatal error. "
+		    "Unhandled node_list_type (debug output)");
+		exit(1);
+		break;
+	}
+	qdevice_net_log(LOG_DEBUG, "Received %s node list reply", str);
+	qdevice_net_log(LOG_DEBUG, "  seq = %"PRIu32, msg->seq_number);
+	qdevice_net_log(LOG_DEBUG, "  vote = %s", tlv_vote_to_str(msg->vote));
+	if (msg->ring_id_set) {
+		qdevice_net_log(LOG_DEBUG, "  ring id = (%"PRIx32".%"PRIx64")",
+		    msg->ring_id.node_id, msg->ring_id.seq);
+	}
+
 	/*
-	 * TODO API
+	 * Call algorithm
 	 */
-	qdevice_net_log(LOG_INFO, "Received node list reply seq=%"PRIu32", vote=%u",
-	    msg->seq_number, msg->vote);
+	result_vote = msg->vote;
+
+	switch (msg->node_list_type) {
+	case TLV_NODE_LIST_TYPE_INITIAL_CONFIG:
+	case TLV_NODE_LIST_TYPE_CHANGED_CONFIG:
+		res = qdevice_net_algorithm_config_node_list_reply_received(instance,
+		    msg->seq_number, (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG),
+		    &result_vote);
+		break;
+	case TLV_NODE_LIST_TYPE_MEMBERSHIP:
+		res = qdevice_net_algorithm_membership_node_list_reply_received(instance,
+		    msg->seq_number, &msg->ring_id, &result_vote);
+		break;
+	case TLV_NODE_LIST_TYPE_QUORUM:
+		res = qdevice_net_algorithm_quorum_node_list_reply_received(instance,
+		    msg->seq_number, &result_vote);
+		break;
+	default:
+		qdevice_net_log(LOG_CRIT, "qdevice_net_msg_received_node_list_reply fatal error. "
+		    "Unhandled node_list_type (algorithm call)");
+		exit(1);
+		break;
+	}
 
-	if (msg->vote != TLV_VOTE_NO_CHANGE) {
-		if (qdevice_net_cast_vote_timer_update(instance, msg->vote) != 0) {
-			return (-1);
+	if (res != 0) {
+		qdevice_net_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_NODE_LIST_REPLY_ERR;
+		return (-1);
+	} else {
+		qdevice_net_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(msg->vote));
+	}
+
+	if (result_vote != TLV_VOTE_NO_CHANGE) {
+		if (msg->node_list_type == TLV_NODE_LIST_TYPE_MEMBERSHIP &&
+		    !tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) {
+			qdevice_net_log(LOG_INFO, "Received membership node list reply with "
+			    "old ring id. Not updating timer");
+		} else {
+			if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+				instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+				return (-1);
+			}
 		}
 	}
 
@@ -612,26 +770,42 @@ static int
 qdevice_net_msg_received_ask_for_vote_reply(struct qdevice_net_instance *instance,
     const struct msg_decoded *msg)
 {
+	enum tlv_vote result_vote;
 
 	if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
 		qdevice_net_log(LOG_ERR, "Received unexpected ask for vote reply message. "
 		    "Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
 		return (-1);
 	}
 
 	if (!msg->vote_set || !msg->seq_number_set) {
 		qdevice_net_log(LOG_ERR, "Received node list reply message without "
 		    "required options. Disconnecting from server");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+		return (-1);
 	}
 
-	/*
-	 * TODO API
-	 */
-	qdevice_net_log(LOG_INFO, "Received ask for vote reply seq=%"PRIu32", vote=%u",
-	    msg->seq_number, msg->vote);
+	qdevice_net_log(LOG_DEBUG, "Received ask for vote reply");
+	qdevice_net_log(LOG_DEBUG, "  seq = %"PRIu32, msg->seq_number);
+	qdevice_net_log(LOG_DEBUG, "  vote = %s", tlv_vote_to_str(msg->vote));
+
+	result_vote = msg->vote;
 
-	if (qdevice_net_cast_vote_timer_update(instance, msg->vote) != 0) {
+	if (qdevice_net_algorithm_ask_for_vote_reply_received(instance, msg->seq_number,
+	    &result_vote) != 0) {
+		qdevice_net_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_ASK_FOR_VOTE_REPLY_ERR;
+		return (-1);
+	} else {
+		qdevice_net_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(msg->vote));
+	}
+
+	if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
 		return (-1);
 	}
 
@@ -643,26 +817,40 @@ qdevice_net_msg_received_vote_info(struct qdevice_net_instance *instance,
     const struct msg_decoded *msg)
 {
 	struct send_buffer_list_entry *send_buffer;
+	enum tlv_vote result_vote;
 
 	if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
 		qdevice_net_log(LOG_ERR, "Received unexpected vote info message. "
 		    "Disconnecting from server");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
 		return (-1);
 	}
 
 	if (!msg->vote_set || !msg->seq_number_set) {
 		qdevice_net_log(LOG_ERR, "Received node list reply message without "
 		    "required options. Disconnecting from server");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+		return (-1);
 	}
 
-	/*
-	 * TODO API
-	 */
-	qdevice_net_log(LOG_INFO, "Received vote info seq=%"PRIu32", vote=%u",
-	    msg->seq_number, msg->vote);
+	qdevice_net_log(LOG_DEBUG, "Received vote info");
+	qdevice_net_log(LOG_DEBUG, "  seq = %"PRIu32, msg->seq_number);
+	qdevice_net_log(LOG_DEBUG, "  vote = %s", tlv_vote_to_str(msg->vote));
+
+	result_vote = msg->vote;
+	if (qdevice_net_algorithm_vote_info_received(instance, msg->seq_number,
+	    &result_vote) != 0) {
+		qdevice_net_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTE_INFO_ERR;
+		return (-1);
+	} else {
+		qdevice_net_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+	}
 
-	if (qdevice_net_cast_vote_timer_update(instance, msg->vote) != 0) {
+	if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
 		return (-1);
 	}
 
@@ -674,6 +862,7 @@ qdevice_net_msg_received_vote_info(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Can't allocate send list buffer for "
 		    "vote info reply msg");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
 		return (-1);
 	}
 
@@ -681,6 +870,7 @@ qdevice_net_msg_received_vote_info(struct qdevice_net_instance *instance,
 		qdevice_net_log(LOG_ERR, "Can't allocate send buffer for "
 		    "vote info reply list msg");
 
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
 		return (-1);
 	}
 
@@ -711,6 +901,7 @@ qdevice_net_msg_received(struct qdevice_net_instance *instance)
 		 */
 		qdevice_net_log_msg_decode_error(res);
 		qdevice_net_log(LOG_ERR, "Disconnecting from server");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_MSG_DECODE_ERROR;
 
 		return (-1);
 	}
@@ -766,6 +957,8 @@ qdevice_net_msg_received(struct qdevice_net_instance *instance)
 	default:
 		qdevice_net_log(LOG_ERR, "Received unsupported message %u. "
 		    "Disconnecting from server", msg.type);
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
 		ret_val = -1;
 		break;
 	}
@@ -804,32 +997,38 @@ qdevice_net_socket_read(struct qdevice_net_instance *instance)
 		break;
 	case -1:
 		qdevice_net_log(LOG_DEBUG, "Server closed connection");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION;
 		ret_val = -1;
 		break;
 	case -2:
 		qdevice_net_log_nss(LOG_ERR, "Unhandled error when reading from server. "
 		    "Disconnecting from server");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
 		ret_val = -1;
 		break;
 	case -3:
 		qdevice_net_log(LOG_ERR, "Can't store message header from server. "
 		    "Disconnecting from server");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
 		ret_val = -1;
 		break;
 	case -4:
 		qdevice_net_log(LOG_ERR, "Can't store message from server. "
 		    "Disconnecting from server");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
 		ret_val = -1;
 		break;
 	case -5:
 		qdevice_net_log(LOG_WARNING, "Server sent unsupported msg type %u. "
 		    "Disconnecting from server", msg_get_type(&instance->receive_buffer));
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNSUPPORTED_MSG;
 		ret_val = -1;
 		break;
 	case -6:
 		qdevice_net_log(LOG_WARNING,
 		    "Server wants to send too long message %u bytes. Disconnecting from server",
 		    msg_get_len(&instance->receive_buffer));
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
 		ret_val = -1;
 		break;
 	case 1:
@@ -841,7 +1040,8 @@ qdevice_net_socket_read(struct qdevice_net_instance *instance)
 				ret_val = -1;
 			}
 		} else {
-			errx(1, "net_socket_read in skipping msg state");
+			qdevice_net_log(LOG_CRIT, "net_socket_read in skipping msg state");
+			exit(1);
 		}
 
 		instance->skipping_msg = 0;
@@ -849,7 +1049,8 @@ qdevice_net_socket_read(struct qdevice_net_instance *instance)
 		dynar_clean(&instance->receive_buffer);
 		break;
 	default:
-		errx(1, "qdevice_net_socket_read unhandled error %d", res);
+		qdevice_net_log(LOG_CRIT, "qdevice_net_socket_read unhandled error %d", res);
+		exit(1);
 		break;
 	}
 
@@ -870,7 +1071,7 @@ qdevice_net_socket_write_finished(struct qdevice_net_instance *instance)
 		    qdevice_net_nss_get_client_auth_data,
 		    (void *)QDEVICE_NET_NSS_CLIENT_CERT_NICKNAME, 0, NULL)) == NULL) {
 			qdevice_net_log_nss(LOG_ERR, "Can't start TLS");
-
+			instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_START_TLS;
 			return (-1);
 		}
 
@@ -878,6 +1079,9 @@ qdevice_net_socket_write_finished(struct qdevice_net_instance *instance)
 		 * And send init msg
 		 */
 		if (qdevice_net_send_init(instance) != 0) {
+			instance->disconnect_reason =
+			    QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
 			return (-1);
 		}
 
@@ -897,6 +1101,7 @@ qdevice_net_socket_write(struct qdevice_net_instance *instance)
 	send_buffer = send_buffer_list_get_active(&instance->send_buffer_list);
 	if (send_buffer == NULL) {
 		qdevice_net_log(LOG_CRIT, "send_buffer_list_get_active returned NULL");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE;
 
 		return (-1);
 	}
@@ -918,12 +1123,13 @@ qdevice_net_socket_write(struct qdevice_net_instance *instance)
 
 	if (res == -1) {
 		qdevice_net_log_nss(LOG_CRIT, "PR_Send returned 0");
-
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION;
 		return (-1);
 	}
 
 	if (res == -2) {
 		qdevice_net_log_nss(LOG_ERR, "Unhandled error when sending message to server");
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE;
 
 		return (-1);
 	}
@@ -931,9 +1137,10 @@ qdevice_net_socket_write(struct qdevice_net_instance *instance)
 	return (0);
 }
 
-#define QDEVICE_NET_POLL_NO_FDS		2
+#define QDEVICE_NET_POLL_NO_FDS		3
 #define QDEVICE_NET_POLL_SOCKET		0
 #define QDEVICE_NET_POLL_VOTEQUORUM	1
+#define QDEVICE_NET_POLL_CMAP		2
 
 static int
 qdevice_net_poll(struct qdevice_net_instance *instance)
@@ -950,6 +1157,9 @@ qdevice_net_poll(struct qdevice_net_instance *instance)
 	pfds[QDEVICE_NET_POLL_VOTEQUORUM].fd = instance->votequorum_poll_fd;
 	pfds[QDEVICE_NET_POLL_VOTEQUORUM].in_flags = PR_POLL_READ;
 
+	pfds[QDEVICE_NET_POLL_CMAP].fd = instance->cmap_poll_fd;
+	pfds[QDEVICE_NET_POLL_CMAP].in_flags = PR_POLL_READ;
+
 	instance->schedule_disconnect = 0;
 
 	if ((poll_res = PR_Poll(pfds, QDEVICE_NET_POLL_NO_FDS,
@@ -966,11 +1176,24 @@ qdevice_net_poll(struct qdevice_net_instance *instance)
 				case QDEVICE_NET_POLL_VOTEQUORUM:
 					if (votequorum_dispatch(instance->votequorum_handle,
 					    CS_DISPATCH_ALL) != CS_OK) {
-						errx(1, "Can't dispatch votequorum messages");
+						qdevice_net_log(LOG_ERR, "Can't dispatch votequorum messages");
+						instance->schedule_disconnect = 1;
+						instance->disconnect_reason =
+						    QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED;
+					}
+					break;
+				case QDEVICE_NET_POLL_CMAP:
+					if (cmap_dispatch(instance->cmap_handle,
+					    CS_DISPATCH_ALL) != CS_OK) {
+						qdevice_net_log(LOG_ERR, "Can't dispatch cmap messages");
+						instance->schedule_disconnect = 1;
+						instance->disconnect_reason =
+						    QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED;
 					}
 					break;
 				default:
-					errx(1, "Unhandled read poll descriptor %u", i);
+					qdevice_net_log(LOG_CRIT, "Unhandled read on poll descriptor %u", i);
+					exit(1);
 					break;
 				}
 			}
@@ -984,7 +1207,8 @@ qdevice_net_poll(struct qdevice_net_instance *instance)
 
 					break;
 				default:
-					errx(1, "Unhandled write poll descriptor %u", i);
+					qdevice_net_log(LOG_CRIT, "Unhandled write on poll descriptor %u", i);
+					exit(1);
 					break;
 				}
 			}
@@ -994,14 +1218,17 @@ qdevice_net_poll(struct qdevice_net_instance *instance)
 			    (PR_POLL_ERR|PR_POLL_NVAL|PR_POLL_HUP|PR_POLL_EXCEPT)) {
 				switch (i) {
 				case QDEVICE_NET_POLL_SOCKET:
-					qdevice_net_log(LOG_CRIT, "POLL_ERR (%u) on main socket",
+					qdevice_net_log(LOG_ERR, "POLL_ERR (%u) on main socket",
 					    pfds[i].out_flags);
 
-					return (-1);
+					instance->schedule_disconnect = 1;
+					instance->disconnect_reason =
+					    QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION;
 
 					break;
 				default:
-					errx(1, "Unhandled poll err on descriptor %u", i);
+					qdevice_net_log(LOG_CRIT, "Unhandled error on poll descriptor %u", i);
+					exit(1);
 					break;
 				}
 			}
@@ -1041,6 +1268,7 @@ qdevice_net_instance_init_from_cmap(struct qdevice_net_instance *instance,
 	int host_port;
 	char *ep;
 	char *cluster_name;
+	uint32_t connect_timeout;
 
 	/*
 	 * Check if provider is net
@@ -1172,6 +1400,23 @@ qdevice_net_instance_init_from_cmap(struct qdevice_net_instance *instance,
 		free(str);
 	}
 
+	/*
+	 * Get connect timeout
+	 */
+	if (cmap_get_string(cmap_handle, "quorum.device.net.connect_timeout", &str) != CS_OK) {
+		connect_timeout = QDEVICE_NET_CONNECT_TIMEOUT;
+	} else {
+		li = strtol(str, &ep, 10);
+		if (li < QDEVICE_NET_MIN_CONNECT_TIMEOUT || li > QDEVICE_NET_MAX_CONNECT_TIMEOUT || *ep != '\0') {
+			errx(1, "connect_timeout must be valid number in range <%lu,%lu>",
+			    QDEVICE_NET_MIN_CONNECT_TIMEOUT, QDEVICE_NET_MAX_CONNECT_TIMEOUT);
+		}
+
+		connect_timeout = li;
+
+		free(str);
+	}
+
 	/*
 	 * Really initialize instance
 	 */
@@ -1181,11 +1426,12 @@ qdevice_net_instance_init_from_cmap(struct qdevice_net_instance *instance,
 	    QDEVICE_NET_MAX_MSG_RECEIVE_SIZE,
 	    tls_supported, node_id, decision_algorithm,
 	    heartbeat_interval, sync_heartbeat_interval, cast_vote_timer_interval,
-	    host_addr, host_port, cluster_name, &tie_breaker) == -1) {
+	    host_addr, host_port, cluster_name, &tie_breaker, connect_timeout) == -1) {
 		errx(1, "Can't initialize qdevice-net");
 	}
 
 	instance->cmap_handle = cmap_handle;
+	qdevice_net_cmap_init_fd(instance);
 }
 
 int
@@ -1194,6 +1440,7 @@ main(void)
 	struct qdevice_net_instance instance;
 	cmap_handle_t cmap_handle;
 	struct send_buffer_list_entry *send_buffer;
+	int try_connect;
 
 	/*
 	 * Init
@@ -1204,74 +1451,114 @@ main(void)
 	qdevice_net_log_init(QDEVICE_NET_LOG_TARGET_STDERR);
         qdevice_net_log_set_debug(1);
 
+	qdevice_net_log(LOG_DEBUG, "Registering algorithms");
+	qdevice_net_algorithm_register_all();
+
 	if (nss_sock_init_nss((instance.tls_supported != TLV_TLS_UNSUPPORTED ?
 	    (char *)QDEVICE_NET_NSS_DB_DIR : NULL)) != 0) {
-		nss_sock_err(1);
+		qdevice_net_log_nss(LOG_ERR, "Can't init nss");
+		exit(1);
 	}
 
-	/*
-	 * Try to connect to qnetd host
-	 */
-	instance.socket = nss_sock_create_client_socket(instance.host_addr, instance.host_port,
-	    PR_AF_UNSPEC, 100);
-	if (instance.socket == NULL) {
-		nss_sock_err(1);
-	}
 
-	if (nss_sock_set_nonblocking(instance.socket) != 0) {
-		nss_sock_err(1);
+	if (qdevice_net_algorithm_init(&instance) != 0) {
+		qdevice_net_log(LOG_ERR, "Algorithm init failed");
+		exit(1);
 	}
 
-	qdevice_net_votequorum_init(&instance);
+	try_connect = 1;
+	while (try_connect) {
+		qdevice_net_votequorum_init(&instance);
 
-	/*
-	 * Create and schedule send of preinit message to qnetd
-	 */
-	send_buffer = send_buffer_list_get_new(&instance.send_buffer_list);
-	if (send_buffer == NULL) {
-		errx(1, "Can't allocate send buffer list");
-	}
+		/*
+		 * Try to connect to qnetd host
+		 */
+		instance.socket = nss_sock_create_client_socket(instance.host_addr, instance.host_port,
+		    PR_AF_UNSPEC, PR_MillisecondsToInterval(instance.connect_timeout));
+		if (instance.socket == NULL) {
+			qdevice_net_log_nss(LOG_CRIT, "Can't connect to server");
+			poll(NULL, 0, QDEVICE_NET_PAUSE_BEFORE_RECONNECT);
 
-	instance.last_msg_seq_num = 1;
-	if (msg_create_preinit(&send_buffer->buffer, instance.cluster_name, 1,
-	    instance.last_msg_seq_num) == 0) {
-		errx(1, "Can't allocate buffer");
-	}
+			continue ;
+		}
 
-	send_buffer_list_put(&instance.send_buffer_list, send_buffer);
+		if (nss_sock_set_nonblocking(instance.socket) != 0) {
+			if (PR_Close(instance.socket) != PR_SUCCESS) {
+				qdevice_net_log_nss(LOG_WARNING, "Unable to close connection");
+			}
 
-	instance.state = QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY;
+			qdevice_net_log_nss(LOG_CRIT, "Can't set socket nonblocking");
+			poll(NULL, 0, QDEVICE_NET_PAUSE_BEFORE_RECONNECT);
 
-	/*
-	 * Main loop
-	 */
-	while (qdevice_net_poll(&instance) == 0) {
-	}
+			continue ;
+		}
 
-	/*
-	 * Cleanup
-	 */
-	if (PR_Close(instance.socket) != PR_SUCCESS) {
-		qdevice_net_log_nss(LOG_WARNING, "Unable to close connection");
-	}
+		/*
+		 * Create and schedule send of preinit message to qnetd
+		 */
+		send_buffer = send_buffer_list_get_new(&instance.send_buffer_list);
+		if (send_buffer == NULL) {
+			errx(1, "Can't allocate send buffer list");
+		}
 
-	/*
-	 * Close cmap and votequorum connections
-	 */
-	if (votequorum_qdevice_unregister(instance.votequorum_handle,
-	    QDEVICE_NET_VOTEQUORUM_DEVICE_NAME) != CS_OK) {
-		qdevice_net_log_nss(LOG_WARNING, "Unable to unregister votequorum device");
+		if (msg_create_preinit(&send_buffer->buffer, instance.cluster_name, 1,
+		    instance.last_msg_seq_num) == 0) {
+			errx(1, "Can't allocate buffer");
+		}
+
+		send_buffer_list_put(&instance.send_buffer_list, send_buffer);
+
+		instance.state = QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY;
+
+		/*
+		 * Main loop
+		 */
+		while (qdevice_net_poll(&instance) == 0) {
+		}
+
+		try_connect = qdevice_net_disconnect_reason_try_reconnect(instance.disconnect_reason);
+
+		if (qdevice_net_algorithm_disconnected(&instance, instance.disconnect_reason,
+		    &try_connect) != 0) {
+			qdevice_net_log(LOG_ERR, "Algorithm returned error, force exit");
+			exit(2);
+		}
+
+		if (instance.disconnect_reason == QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED) {
+			try_connect = 0;
+		}
+
+		/*
+		 * Cleanup
+		 */
+		if (PR_Close(instance.socket) != PR_SUCCESS) {
+			qdevice_net_log_nss(LOG_WARNING, "Unable to close connection");
+		}
+
+		/*
+		 * Close cmap and votequorum connections
+		 */
+		qdevice_net_votequorum_destroy(&instance);
+		qdevice_net_cmap_del_track(&instance);
+
+		if (try_connect) {
+			/*
+			 * Reinit instance
+			 */
+			qdevice_net_instance_clean(&instance);
+		}
 	}
 
-	votequorum_finalize(instance.votequorum_handle);
-	cmap_finalize(instance.cmap_handle);
+	qdevice_net_algorithm_destroy(&instance);
+
+	qdevice_net_cmap_destroy(&instance);
 
 	qdevice_net_instance_destroy(&instance);
 
 	SSL_ClearSessionCache();
 
 	if (NSS_Shutdown() != SECSuccess) {
-		nss_sock_err(1);
+		qdevice_net_log_nss(LOG_WARNING, "Can't shutdown NSS");
 	}
 
 	PR_Cleanup();

+ 1 - 1
qdevices/corosync-qnetd-certutil.sh

@@ -1,7 +1,7 @@
 #!@BASHPATH@
 
 #
-# Copyright (c) 2015 Red Hat, Inc.
+# Copyright (c) 2015-2016 Red Hat, Inc.
 #
 # All rights reserved.
 #

+ 6 - 6
qdevices/corosync-qnetd.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -337,7 +337,7 @@ main(int argc, char *argv[])
 		exit(1);
 	}
 
-	qnetd_log_printf(LOG_DEBUG, "Initializing nss");
+	qnetd_log(LOG_DEBUG, "Initializing nss");
 	if (nss_sock_init_nss((tls_supported != TLV_TLS_UNSUPPORTED ?
 	    (char *)QNETD_NSS_DB_DIR : NULL)) != 0) {
 		qnetd_err_nss();
@@ -360,7 +360,7 @@ main(int argc, char *argv[])
 		qnetd_err_nss();
 	}
 
-	qnetd_log_printf(LOG_DEBUG, "Creating listening socket");
+	qnetd_log(LOG_DEBUG, "Creating listening socket");
 	instance.server.socket = nss_sock_create_listen_socket(instance.host_addr,
 	    instance.host_port, address_family);
 	if (instance.server.socket == NULL) {
@@ -378,10 +378,10 @@ main(int argc, char *argv[])
 	global_server_socket = instance.server.socket;
 	signal_handlers_register();
 
-	qnetd_log_printf(LOG_DEBUG, "Registering algorithms");
-	algorithms_register();
+	qnetd_log(LOG_DEBUG, "Registering algorithms");
+	qnetd_algorithm_register_all();
 
-	qnetd_log_printf(LOG_DEBUG, "QNetd ready to provide service");
+	qnetd_log(LOG_DEBUG, "QNetd ready to provide service");
 	/*
 	 * MAIN LOOP
 	 */

+ 1 - 1
qdevices/dynar.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/dynar.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 13 - 1
qdevices/msg.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -579,6 +579,7 @@ small_buf_err:
 
 size_t
 msg_create_node_list_reply(struct dynar *msg, uint32_t msg_seq_number,
+    enum tlv_node_list_type node_list_type, int add_ring_id, const struct tlv_ring_id *ring_id,
     enum tlv_vote vote)
 {
 
@@ -591,6 +592,16 @@ msg_create_node_list_reply(struct dynar *msg, uint32_t msg_seq_number,
 		goto small_buf_err;
 	}
 
+	if (tlv_add_node_list_type(msg, node_list_type) == -1) {
+		goto small_buf_err;
+	}
+
+	if (add_ring_id) {
+		if (tlv_add_ring_id(msg, ring_id) == -1) {
+			goto small_buf_err;
+		}
+	}
+
 	if (tlv_add_vote(msg, vote) == -1) {
 		goto small_buf_err;
 	}
@@ -728,6 +739,7 @@ msg_decoded_destroy(struct msg_decoded *decoded_msg)
 	free(decoded_msg->cluster_name);
 	free(decoded_msg->supported_messages);
 	free(decoded_msg->supported_options);
+	free(decoded_msg->supported_decision_algorithms);
 	node_list_free(&decoded_msg->nodes);
 
 	msg_decoded_init(decoded_msg);

+ 2 - 1
qdevices/msg.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -163,6 +163,7 @@ extern size_t		msg_create_node_list(struct dynar *msg,
     const struct node_list *nodes);
 
 extern size_t		msg_create_node_list_reply(struct dynar *msg, uint32_t msg_seq_number,
+    enum tlv_node_list_type node_list_type, int add_ring_id, const struct tlv_ring_id *ring_id,
     enum tlv_vote vote);
 
 extern size_t		msg_create_ask_for_vote(struct dynar *msg, uint32_t msg_seq_number);

+ 1 - 1
qdevices/msgio.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/msgio.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 58 - 2
qdevices/node-list.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -133,8 +133,64 @@ node_list_del(struct node_list *list, struct node_list_entry *node)
 }
 
 int
-node_list_is_empty(struct node_list *list)
+node_list_is_empty(const struct node_list *list)
 {
 
 	return (TAILQ_EMPTY(list));
 }
+
+struct node_list_entry *
+node_list_find_node_id(const struct node_list *list, uint32_t node_id)
+{
+	struct node_list_entry *node_entry;
+
+	TAILQ_FOREACH(node_entry, list, entries) {
+		if (node_entry->node_id == node_id) {
+			return (node_entry);
+		}
+	}
+
+	return (NULL);
+}
+
+int
+node_list_eq(const struct node_list *list1, const struct node_list *list2)
+{
+	struct node_list_entry *node1_entry;
+	struct node_list_entry *node2_entry;
+	struct node_list tmp_list;
+	int res;
+
+	res = 1;
+
+	if (node_list_clone(&tmp_list, list2) != 0) {
+		return (-1);
+	}
+
+	TAILQ_FOREACH(node1_entry, list1, entries) {
+		node2_entry = node_list_find_node_id(&tmp_list, node1_entry->node_id);
+		if (node2_entry == NULL) {
+			res = 0;
+			goto return_res;
+		}
+
+		if (node1_entry->node_id != node2_entry->node_id ||
+		    node1_entry->data_center_id != node2_entry->data_center_id ||
+		    node1_entry->node_state != node2_entry->node_state) {
+			res = 0;
+			goto return_res;
+		}
+
+		node_list_del(&tmp_list, node2_entry);
+	}
+
+	if (!node_list_is_empty(&tmp_list)) {
+		res = 0;
+		goto return_res;
+	}
+
+return_res:
+	node_list_free(&tmp_list);
+
+	return (res);
+}

+ 8 - 2
qdevices/node-list.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -71,11 +71,17 @@ extern void				 node_list_free(struct node_list *list);
 extern void				 node_list_del(struct node_list *list,
     struct node_list_entry *node);
 
-extern int				 node_list_is_empty(struct node_list *list);
+extern int				 node_list_is_empty(const struct node_list *list);
 
 extern void				 node_list_entry_to_tlv_node_info(
     const struct node_list_entry *node, struct tlv_node_info *node_info);
 
+extern struct node_list_entry *		 node_list_find_node_id(const struct node_list *list,
+    uint32_t node_id);
+
+extern int				 node_list_eq(const struct node_list *list1,
+    const struct node_list *list2);
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 7
qdevices/nss-sock.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -38,12 +38,6 @@
 
 #include "nss-sock.h"
 
-void
-nss_sock_err(int eval) {
-	errx(eval, "nss error %d: %s", PR_GetError(), PR_ErrorToString(PR_GetError(),
-	    PR_LANGUAGE_I_DEFAULT));
-}
-
 int
 nss_sock_init_nss(char *config_dir)
 {

+ 1 - 3
qdevices/nss-sock.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -42,8 +42,6 @@
 extern "C" {
 #endif
 
-extern void		nss_sock_err(int eval);
-
 extern int		nss_sock_init_nss(char *config_dir);
 
 extern PRFileDesc	*nss_sock_create_listen_socket(const char *hostname, uint16_t port,

+ 165 - 0
qdevices/qdevice-net-algo-2nodelms.c

@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-2nodelms.h"
+#include "qdevice-net-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+
+int
+qdevice_net_algo_2nodelms_init(struct qdevice_net_instance *instance)
+{
+
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_connected(struct qdevice_net_instance *instance)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+    const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+    enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+    uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_config_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int initial, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_echo_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int is_expected_seq_number)
+{
+
+	return (is_expected_seq_number ? 0 : -1);
+}
+
+int
+qdevice_net_algo_2nodelms_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+	return (-1);
+}
+
+int
+qdevice_net_algo_2nodelms_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect)
+{
+
+	return (0);
+}
+
+void
+qdevice_net_algo_2nodelms_destroy(struct qdevice_net_instance *instance)
+{
+
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_2nodelms = {
+	.init					= qdevice_net_algo_2nodelms_init,
+	.connected				= qdevice_net_algo_2nodelms_connected,
+	.votequorum_node_list_notify		= qdevice_net_algo_2nodelms_votequorum_node_list_notify,
+	.votequorum_quorum_notify		= qdevice_net_algo_2nodelms_votequorum_quorum_notify,
+	.config_node_list_reply_received	= qdevice_net_algo_2nodelms_config_node_list_reply_received,
+	.membership_node_list_reply_received	= qdevice_net_algo_2nodelms_membership_node_list_reply_received,
+	.quorum_node_list_reply_received	= qdevice_net_algo_2nodelms_quorum_node_list_reply_received,
+	.ask_for_vote_reply_received		= qdevice_net_algo_2nodelms_ask_for_vote_reply_received,
+	.vote_info_received			= qdevice_net_algo_2nodelms_vote_info_received,
+	.echo_reply_received			= qdevice_net_algo_2nodelms_echo_reply_received,
+	.echo_reply_not_received		= qdevice_net_algo_2nodelms_echo_reply_not_received,
+	.disconnected				= qdevice_net_algo_2nodelms_disconnected,
+	.destroy				= qdevice_net_algo_2nodelms_destroy,
+};
+
+int
+qdevice_net_algo_2nodelms_register(void)
+{
+	return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_2NODELMS, &qdevice_net_algo_2nodelms));
+}

+ 91 - 0
qdevices/qdevice-net-algo-2nodelms.h

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_2NODELMS_H_
+#define _QDEVICE_NET_ALGO_2NODELMS_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	qdevice_net_algo_2nodelms_init(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_2nodelms_connected(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_2nodelms_votequorum_node_list_notify(
+    struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+    uint32_t node_list_entries, uint32_t node_list[], enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_2nodelms_votequorum_quorum_notify(
+    struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+    votequorum_node_t node_list[], int *send_node_list);
+
+extern int	qdevice_net_algo_2nodelms_config_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int initial, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_2nodelms_membership_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_2nodelms_quorum_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_2nodelms_ask_for_vote_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_2nodelms_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_2nodelms_echo_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int	qdevice_net_algo_2nodelms_echo_reply_not_received(
+    struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_2nodelms_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect);
+
+extern void	qdevice_net_algo_2nodelms_destroy(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_2nodelms_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_2NODELMS_H_ */

+ 165 - 0
qdevices/qdevice-net-algo-ffsplit.c

@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-ffsplit.h"
+#include "qdevice-net-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+
+int
+qdevice_net_algo_ffsplit_init(struct qdevice_net_instance *instance)
+{
+
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_connected(struct qdevice_net_instance *instance)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+    const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+    enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+    uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_config_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int initial, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_echo_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int is_expected_seq_number)
+{
+
+	return (is_expected_seq_number ? 0 : -1);
+}
+
+int
+qdevice_net_algo_ffsplit_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+	return (-1);
+}
+
+int
+qdevice_net_algo_ffsplit_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect)
+{
+
+	return (0);
+}
+
+void
+qdevice_net_algo_ffsplit_destroy(struct qdevice_net_instance *instance)
+{
+
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_ffsplit = {
+	.init					= qdevice_net_algo_ffsplit_init,
+	.connected				= qdevice_net_algo_ffsplit_connected,
+	.votequorum_node_list_notify		= qdevice_net_algo_ffsplit_votequorum_node_list_notify,
+	.votequorum_quorum_notify		= qdevice_net_algo_ffsplit_votequorum_quorum_notify,
+	.config_node_list_reply_received	= qdevice_net_algo_ffsplit_config_node_list_reply_received,
+	.membership_node_list_reply_received	= qdevice_net_algo_ffsplit_membership_node_list_reply_received,
+	.quorum_node_list_reply_received	= qdevice_net_algo_ffsplit_quorum_node_list_reply_received,
+	.ask_for_vote_reply_received		= qdevice_net_algo_ffsplit_ask_for_vote_reply_received,
+	.vote_info_received			= qdevice_net_algo_ffsplit_vote_info_received,
+	.echo_reply_received			= qdevice_net_algo_ffsplit_echo_reply_received,
+	.echo_reply_not_received		= qdevice_net_algo_ffsplit_echo_reply_not_received,
+	.disconnected				= qdevice_net_algo_ffsplit_disconnected,
+	.destroy				= qdevice_net_algo_ffsplit_destroy,
+};
+
+int
+qdevice_net_algo_ffsplit_register(void)
+{
+	return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_FFSPLIT, &qdevice_net_algo_ffsplit));
+}

+ 91 - 0
qdevices/qdevice-net-algo-ffsplit.h

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_FFSPLIT_H_
+#define _QDEVICE_NET_ALGO_FFSPLIT_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	qdevice_net_algo_ffsplit_init(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_ffsplit_connected(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_ffsplit_votequorum_node_list_notify(
+    struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+    uint32_t node_list_entries, uint32_t node_list[], enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_ffsplit_votequorum_quorum_notify(
+    struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+    votequorum_node_t node_list[], int *send_node_list);
+
+extern int	qdevice_net_algo_ffsplit_config_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int initial, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_ffsplit_membership_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_ffsplit_quorum_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_ffsplit_ask_for_vote_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_ffsplit_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_ffsplit_echo_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int	qdevice_net_algo_ffsplit_echo_reply_not_received(
+    struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_ffsplit_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect);
+
+extern void	qdevice_net_algo_ffsplit_destroy(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_ffsplit_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_FFSPLIT_H_ */

+ 165 - 0
qdevices/qdevice-net-algo-lms.c

@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-lms.h"
+#include "qdevice-net-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+
+int
+qdevice_net_algo_lms_init(struct qdevice_net_instance *instance)
+{
+
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_connected(struct qdevice_net_instance *instance)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+    const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+    enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+    uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_config_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int initial, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	return (0);
+}
+
+int
+qdevice_net_algo_lms_echo_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int is_expected_seq_number)
+{
+
+	return (is_expected_seq_number ? 0 : -1);
+}
+
+int
+qdevice_net_algo_lms_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+	return (-1);
+}
+
+int
+qdevice_net_algo_lms_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect)
+{
+
+	return (0);
+}
+
+void
+qdevice_net_algo_lms_destroy(struct qdevice_net_instance *instance)
+{
+
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_lms = {
+	.init					= qdevice_net_algo_lms_init,
+	.connected				= qdevice_net_algo_lms_connected,
+	.votequorum_node_list_notify		= qdevice_net_algo_lms_votequorum_node_list_notify,
+	.votequorum_quorum_notify		= qdevice_net_algo_lms_votequorum_quorum_notify,
+	.config_node_list_reply_received	= qdevice_net_algo_lms_config_node_list_reply_received,
+	.membership_node_list_reply_received	= qdevice_net_algo_lms_membership_node_list_reply_received,
+	.quorum_node_list_reply_received	= qdevice_net_algo_lms_quorum_node_list_reply_received,
+	.ask_for_vote_reply_received		= qdevice_net_algo_lms_ask_for_vote_reply_received,
+	.vote_info_received			= qdevice_net_algo_lms_vote_info_received,
+	.echo_reply_received			= qdevice_net_algo_lms_echo_reply_received,
+	.echo_reply_not_received		= qdevice_net_algo_lms_echo_reply_not_received,
+	.disconnected				= qdevice_net_algo_lms_disconnected,
+	.destroy				= qdevice_net_algo_lms_destroy,
+};
+
+int
+qdevice_net_algo_lms_register(void)
+{
+	return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_LMS, &qdevice_net_algo_lms));
+}

+ 91 - 0
qdevices/qdevice-net-algo-lms.h

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_LMS_H_
+#define _QDEVICE_NET_ALGO_LMS_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	qdevice_net_algo_lms_init(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_lms_connected(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_lms_votequorum_node_list_notify(
+    struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+    uint32_t node_list_entries, uint32_t node_list[], enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_lms_votequorum_quorum_notify(
+    struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+    votequorum_node_t node_list[], int *send_node_list);
+
+extern int	qdevice_net_algo_lms_config_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int initial, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_lms_membership_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_lms_quorum_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_lms_ask_for_vote_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_lms_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_lms_echo_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int	qdevice_net_algo_lms_echo_reply_not_received(
+    struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_lms_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect);
+
+extern void	qdevice_net_algo_lms_destroy(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_lms_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_LMS_H_ */

+ 285 - 0
qdevices/qdevice-net-algo-test.c

@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-test.h"
+#include "qdevice-net-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+
+/*
+ * Called after qdevice_net_instance is initialized. Connection to server is not yet
+ * established. Used mainly for allocating instance->algorithm_data.
+ *
+ * Callback should return 0 on success or -1 on failure.
+ */
+int
+qdevice_net_algo_test_init(struct qdevice_net_instance *instance)
+{
+
+	instance->algorithm_data = NULL;
+	qnetd_log(LOG_INFO, "algo-test: Initialized");
+
+	return (0);
+}
+
+/*
+ * Called after qdevice connected to qnetd.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_connected(struct qdevice_net_instance *instance)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Connected");
+
+	return (0);
+}
+
+/*
+ * Called after votequorum node list notify is dispatched.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ *
+ * Main responsibility for this function is to set vote variable (variable is set by caller to
+ * TLV_VOTE_WAIT_FOR_REPLY).
+ *
+ * TLV_VOTE_ACK = do not send list and vote ACK
+ * TLV_VOTE_NACK = do not send list and vote NACK
+ * TLV_VOTE_NO_CHANGE = do not send list and no change in vote
+ * TLV_VOTE_WAIT_FOR_REPLY = send list and wait with voting until reply is received
+ * TLV_VOTE_ASK_LATER = send list but do not change vote
+ */
+int
+qdevice_net_algo_test_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+    const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+    enum tlv_vote *vote)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Votequorum list notify");
+
+	*vote = TLV_VOTE_WAIT_FOR_REPLY;
+
+	return (0);
+}
+
+/*
+ * Called after votequorum quorum notify is dispatched.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ *
+ * Main responsibility is to set send_node_list (set by caller to 1). If send_node_list is set to
+ * non zero, node list is send to qnetd
+ */
+int
+qdevice_net_algo_test_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+    uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Votequorum quorum notify");
+
+	*send_node_list = 1;
+
+	return (0);
+}
+
+/*
+ * Called when config node list reply is received. Vote is set to value returned by server (and can
+ * be overwriten by algorithm).
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_config_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int initial, enum tlv_vote *vote)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Config node list reply");
+
+	return (0);
+}
+
+/*
+ * Called when membership node list reply (reply for votequorum votequorum_nodelist_notify_fn)
+ * is received. Vote is set to value returned by server (and can be overwriten by algorithm).
+ *
+ * Also if server returned TLV_VOTE_ASK_LATER, it's good idea to create timer (call timer_list_add
+ * with instance->main_timer_list parameter) and ask for reply (qdevice_net_send_ask_for_vote).
+ * Another option may be to wait for vote_info message (if server algorithm is configured so).
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Membership node list reply");
+
+	return (0);
+}
+
+/*
+ * Called when quorum node list reply (reply for votequorum votequorum_quorum_notify_fn)
+ * is received. Vote is set to value returned by server (and can be overwriten by algorithm).
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Quorum node list reply");
+
+	return (0);
+}
+
+/*
+ * Called when reply for ask for vote message was received.
+ * Vote is set to value returned by server (and can be overwriten by algorithm).
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Ask for vote reply received");
+
+	return (0);
+}
+
+/*
+ * Called when vote info message from server was received.
+ * Vote is set to value sent by server (and can be overwriten by algorithm).
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Vote info received");
+
+	return (0);
+}
+
+/*
+ * Called when echo reply message was received.
+ * is_expected_seq_number is set to 1 if received seq_number was equal to last sent echo request.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_echo_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int is_expected_seq_number)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Echo reply received");
+
+	return (is_expected_seq_number ? 0 : -1);
+}
+
+/*
+ * Called when client is about to send echo request but echo reply to previous echo request
+ * was not yet received.
+ *
+ * Callback should return 0 if processing should continue (echo request is not send but timer is
+ * scheduled again) otherwise -1 (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Echo reply not received");
+
+	return (-1);
+}
+
+/*
+ * Called when client disconnect from server.
+ *
+ * disconnect_reason contains one of QDEVICE_NET_DISCONNECT_REASON_
+ * try_reconnect can be set to non zero value if reconnect to server should be tried
+ *
+ * Callback should return 0 on success, -1 on failure (-> force exit)
+ */
+int
+qdevice_net_algo_test_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Disconnected");
+
+	return (0);
+}
+
+/*
+ * Called when qdevice-net is going down.
+ */
+void
+qdevice_net_algo_test_destroy(struct qdevice_net_instance *instance)
+{
+
+	qnetd_log(LOG_INFO, "algo-test: Destroy");
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_test = {
+	.init					= qdevice_net_algo_test_init,
+	.connected				= qdevice_net_algo_test_connected,
+	.votequorum_node_list_notify		= qdevice_net_algo_test_votequorum_node_list_notify,
+	.votequorum_quorum_notify		= qdevice_net_algo_test_votequorum_quorum_notify,
+	.config_node_list_reply_received	= qdevice_net_algo_test_config_node_list_reply_received,
+	.membership_node_list_reply_received	= qdevice_net_algo_test_membership_node_list_reply_received,
+	.quorum_node_list_reply_received	= qdevice_net_algo_test_quorum_node_list_reply_received,
+	.ask_for_vote_reply_received		= qdevice_net_algo_test_ask_for_vote_reply_received,
+	.vote_info_received			= qdevice_net_algo_test_vote_info_received,
+	.echo_reply_received			= qdevice_net_algo_test_echo_reply_received,
+	.echo_reply_not_received		= qdevice_net_algo_test_echo_reply_not_received,
+	.disconnected				= qdevice_net_algo_test_disconnected,
+	.destroy				= qdevice_net_algo_test_destroy,
+};
+
+int
+qdevice_net_algo_test_register(void)
+{
+	return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_TEST, &qdevice_net_algo_test));
+}

+ 91 - 0
qdevices/qdevice-net-algo-test.h

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_TEST_H_
+#define _QDEVICE_NET_ALGO_TEST_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	qdevice_net_algo_test_init(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_test_connected(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_test_votequorum_node_list_notify(
+    struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+    uint32_t node_list_entries, uint32_t node_list[], enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_test_votequorum_quorum_notify(
+    struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+    votequorum_node_t node_list[], int *send_node_list);
+
+extern int	qdevice_net_algo_test_config_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int initial, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_test_membership_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_test_quorum_node_list_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_test_ask_for_vote_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number,
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_test_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote);
+
+extern int	qdevice_net_algo_test_echo_reply_received(
+    struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int	qdevice_net_algo_test_echo_reply_not_received(
+    struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_test_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect);
+
+extern void	qdevice_net_algo_test_destroy(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algo_test_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_TEST_H_ */

+ 290 - 0
qdevices/qdevice-net-algorithm.c

@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include "qnet-config.h"
+#include "qdevice-net-algorithm.h"
+#include "qdevice-net-log.h"
+
+#include "qdevice-net-algo-test.h"
+#include "qdevice-net-algo-ffsplit.h"
+#include "qdevice-net-algo-2nodelms.h"
+#include "qdevice-net-algo-lms.h"
+
+static struct qdevice_net_algorithm *qdevice_net_algorithm_array[QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE];
+
+int
+qdevice_net_algorithm_init(struct qdevice_net_instance *instance)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_init unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->init(instance));
+}
+
+int
+qdevice_net_algorithm_connected(struct qdevice_net_instance *instance)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_connected unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->connected(instance));
+}
+
+int
+qdevice_net_algorithm_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+    const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+    enum tlv_vote *vote)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_node_list_notify "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->votequorum_node_list_notify(
+	    instance, ring_id, node_list_entries, node_list, vote));
+}
+
+int
+qdevice_net_algorithm_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+    uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_quorum_notify "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->votequorum_quorum_notify(
+	    instance, quorate, node_list_entries, node_list, send_node_list));
+}
+
+int
+qdevice_net_algorithm_config_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int initial, enum tlv_vote *vote)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_config_node_list_reply_received "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    config_node_list_reply_received(instance, seq_number, initial, vote));
+}
+
+int
+qdevice_net_algorithm_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_membership_node_list_reply_received "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    membership_node_list_reply_received(instance, seq_number, ring_id, vote));
+}
+
+int
+qdevice_net_algorithm_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_quorum_node_list_reply_received "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    quorum_node_list_reply_received(instance, seq_number, vote));
+}
+
+int
+qdevice_net_algorithm_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_ask_for_vote_reply_received "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    ask_for_vote_reply_received(instance, seq_number, vote));
+}
+
+int
+qdevice_net_algorithm_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_vote_info_received "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    vote_info_received(instance, seq_number, vote));
+}
+
+int
+qdevice_net_algorithm_echo_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int is_expected_seq_number)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_echo_reply_received "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    echo_reply_received(instance, seq_number, is_expected_seq_number));
+}
+
+int
+qdevice_net_algorithm_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_echo_reply_not_received "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    echo_reply_not_received(instance));
+}
+
+int
+qdevice_net_algorithm_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_disconnected "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    disconnected(instance, disconnect_reason, try_reconnect));
+}
+
+void
+qdevice_net_algorithm_destroy(struct qdevice_net_instance *instance)
+{
+
+	if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+		qdevice_net_log(LOG_CRIT, "qdevice_net_algorithm_destroy "
+		    "unhandled decision algorithm");
+		exit(1);
+	}
+
+	return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+	    destroy(instance));
+}
+
+int
+qdevice_net_algorithm_register(enum tlv_decision_algorithm_type algorithm_number,
+    struct qdevice_net_algorithm *algorithm)
+{
+
+	if (algorithm_number >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) {
+		return (-1);
+	}
+
+	if (qdevice_net_algorithm_array[algorithm_number] != NULL) {
+		return (-1);
+	}
+
+	qdevice_net_algorithm_array[algorithm_number] = algorithm;
+
+	return (0);
+}
+
+void
+qdevice_net_algorithm_register_all(void)
+{
+
+	if (qdevice_net_algo_test_register() != 0) {
+		qdevice_net_log(LOG_CRIT, "Failed to register decision algorithm 'test' ");
+		exit(1);
+	}
+
+	if (qdevice_net_algo_ffsplit_register() != 0) {
+		qdevice_net_log(LOG_CRIT, "Failed to register decision algorithm 'ffsplit' ");
+		exit(1);
+	}
+
+	if (qdevice_net_algo_2nodelms_register() != 0) {
+		qdevice_net_log(LOG_CRIT, "Failed to register decision algorithm '2nodelms' ");
+		exit(1);
+	}
+
+	if (qdevice_net_algo_lms_register() != 0) {
+		qdevice_net_log(LOG_CRIT, "Failed to register decision algorithm 'lms' ");
+		exit(1);
+	}
+}

+ 118 - 0
qdevices/qdevice-net-algorithm.h

@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QDEVICE_NET_ALGORITHM_H_
+#define _QDEVICE_NET_ALGORITHM_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "qdevice-net-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	qdevice_net_algorithm_init(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algorithm_connected(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algorithm_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+    const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+    enum tlv_vote *vote);
+
+extern int	qdevice_net_algorithm_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+    uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list);
+
+extern int	qdevice_net_algorithm_config_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int initial, enum tlv_vote *vote);
+
+extern int	qdevice_net_algorithm_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote);
+
+extern int	qdevice_net_algorithm_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote);
+
+extern int	qdevice_net_algorithm_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote);
+
+extern int	qdevice_net_algorithm_vote_info_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, enum tlv_vote *vote);
+
+extern int	qdevice_net_algorithm_echo_reply_received(struct qdevice_net_instance *instance,
+    uint32_t seq_number, int is_expected_seq_number);
+
+extern int	qdevice_net_algorithm_echo_reply_not_received(struct qdevice_net_instance *instance);
+
+extern int	qdevice_net_algorithm_disconnected(struct qdevice_net_instance *instance,
+    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect);
+
+extern void	qdevice_net_algorithm_destroy(struct qdevice_net_instance *instance);
+
+struct qdevice_net_algorithm {
+	int (*init)(struct qdevice_net_instance *instance);
+	int (*connected)(struct qdevice_net_instance *instance);
+	int (*votequorum_node_list_notify)(struct qdevice_net_instance *instance,
+	    const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+	    enum tlv_vote *vote);
+	int (*votequorum_quorum_notify)(struct qdevice_net_instance *instance, uint32_t quorate,
+	    uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list);
+	int (*config_node_list_reply_received)(struct qdevice_net_instance *instance,
+	    uint32_t seq_number, int initial, enum tlv_vote *vote);
+	int (*membership_node_list_reply_received)(struct qdevice_net_instance *instance,
+	    uint32_t seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote *vote);
+	int (*quorum_node_list_reply_received)(struct qdevice_net_instance *instance,
+	    uint32_t seq_number, enum tlv_vote *vote);
+	int (*ask_for_vote_reply_received)(struct qdevice_net_instance *instance,
+	    uint32_t seq_number, enum tlv_vote *vote);
+	int (*vote_info_received)(struct qdevice_net_instance *instance,
+	    uint32_t seq_number, enum tlv_vote *vote);
+	int (*echo_reply_received)(struct qdevice_net_instance *instance,
+	    uint32_t seq_number, int is_expected_seq_number);
+	int (*echo_reply_not_received)(struct qdevice_net_instance *instance);
+	int (*disconnected)(struct qdevice_net_instance *instance,
+	    enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect);
+	void (*destroy)(struct qdevice_net_instance *instance);
+};
+
+extern int		qdevice_net_algorithm_register(
+	enum tlv_decision_algorithm_type algorithm_number, struct qdevice_net_algorithm *algorithm);
+
+extern void qdevice_net_algorithm_register_all(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGORITHM_H_ */

+ 4 - 3
qdevices/qdevice-net-cast-vote-timer.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -75,6 +75,7 @@ qdevice_net_cast_vote_timer_callback(void *data1, void *data2)
 			qdevice_net_log(LOG_CRIT, "Can't call votequorum_qdevice_poll. Error %u",
 			    res);
 
+			instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
 			instance->schedule_disconnect = 1;
 			return (0);
 		}
@@ -101,8 +102,8 @@ qdevice_net_cast_vote_timer_update(struct qdevice_net_instance *instance, enum t
 		timer_needs_running = 0;
 		break;
 	case TLV_VOTE_NO_CHANGE:
-		errx(1, "qdevice_net_cast_vote_timer_update_vote: Incorrect vote parameter %u\n",
-		    vote);
+		return (0);
+
 		break;
 	default:
 		errx(1, "qdevice_net_cast_vote_timer_update_vote: Unhandled vote parameter %u\n",

+ 1 - 1
qdevices/qdevice-net-cast-vote-timer.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 142 - 1
qdevices/qdevice-net-cmap.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -36,12 +36,20 @@
 #include <sys/socket.h>
 
 #include <stdio.h>
+#include <stdint.h>
 #include <netdb.h>
 #include <err.h>
 #include <poll.h>
 
+/*
+ * Needed for creating nspr handle from unix fd
+ */
+#include <private/pprio.h>
+
 #include "qnet-config.h"
 #include "qdevice-net-cmap.h"
+#include "qdevice-net-log.h"
+#include "qdevice-net-send.h"
 
 static uint32_t
 qdevice_net_cmap_autogenerate_node_id(const char *addr, int clear_node_high_byte)
@@ -202,3 +210,136 @@ qdevice_net_cmap_init(cmap_handle_t *handle)
 		errx(1, "Failed to initialize the cmap API. Error %s", cs_strerror(res));
 	}
 }
+
+void
+qdevice_net_cmap_init_fd(struct qdevice_net_instance *instance)
+{
+	int fd;
+	cs_error_t res;
+
+	if ((res = cmap_context_set(instance->cmap_handle, (void *)instance)) != CS_OK) {
+		qdevice_net_log(LOG_ERR, "Can't set cmap context. Error %s", cs_strerror(res));
+		exit(1);
+	}
+
+	cmap_fd_get(instance->cmap_handle, &fd);
+	if ((instance->cmap_poll_fd = PR_CreateSocketPollFd(fd)) == NULL) {
+		qdevice_net_log_nss(LOG_CRIT, "Can't create NSPR cmap poll fd");
+		exit(1);
+	}
+}
+
+static void
+qdevice_net_cmap_nodelist_reload_cb(cmap_handle_t cmap_handle, cmap_track_handle_t cmap_track_handle,
+    int32_t event, const char *key_name,
+    struct cmap_notify_value new_value, struct cmap_notify_value old_value,
+    void *user_data)
+{
+	cs_error_t cs_res;
+	uint8_t reload;
+	struct qdevice_net_instance *instance;
+
+	if (cmap_context_get(cmap_handle, (const void **)&instance) != CS_OK) {
+		qdevice_net_log(LOG_ERR, "Fatal error. Can't get cmap context");
+		exit(1);
+	}
+
+	/*
+	 * Wait for full reload
+	 */
+	if (strcmp(key_name, "config.totemconfig_reload_in_progress") == 0 &&
+	    new_value.type == CMAP_VALUETYPE_UINT8 && new_value.len == sizeof(reload)) {
+		reload = 1;
+		if (memcmp(new_value.data, &reload, sizeof(reload)) == 0) {
+			/*
+			 * Ignore nodelist changes
+			 */
+			instance->cmap_reload_in_progress = 1;
+			return ;
+		} else {
+			instance->cmap_reload_in_progress = 0;
+		}
+	}
+
+	if (instance->cmap_reload_in_progress) {
+		return ;
+	}
+
+	if (((cs_res = cmap_get_uint8(cmap_handle, "config.totemconfig_reload_in_progress",
+	    &reload)) == CS_OK) && reload == 1) {
+		return ;
+	}
+
+	if (qdevice_net_send_config_node_list(instance, 0, 0) != 0) {
+		/*
+		 * Fatal error -> schedule disconnect
+		 */
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+		instance->schedule_disconnect = 1;
+	}
+}
+
+int
+qdevice_net_cmap_add_track(struct qdevice_net_instance *instance)
+{
+	cs_error_t res;
+
+	res = cmap_track_add(instance->cmap_handle, "config.totemconfig_reload_in_progress",
+	    CMAP_TRACK_ADD | CMAP_TRACK_MODIFY, qdevice_net_cmap_nodelist_reload_cb,
+	    NULL, &instance->cmap_reload_track_handle);
+
+	if (res != CS_OK) {
+		qdevice_net_log(LOG_ERR, "Can't initialize cmap totemconfig_reload_in_progress tracking");
+		return (-1);
+	}
+
+	res = cmap_track_add(instance->cmap_handle, "nodelist.",
+	    CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX,
+	    qdevice_net_cmap_nodelist_reload_cb,
+	    NULL, &instance->cmap_nodelist_track_handle);
+
+	if (res != CS_OK) {
+		qdevice_net_log(LOG_ERR, "Can't initialize cmap nodelist tracking");
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+qdevice_net_cmap_del_track(struct qdevice_net_instance *instance)
+{
+	cs_error_t res;
+
+	res = cmap_track_delete(instance->cmap_handle, instance->cmap_reload_track_handle);
+
+	if (res != CS_OK) {
+		qdevice_net_log(LOG_WARNING, "Can't delete cmap totemconfig_reload_in_progress tracking");
+	}
+	instance->cmap_reload_track_handle = 0;
+
+	res = cmap_track_delete(instance->cmap_handle, instance->cmap_nodelist_track_handle);
+	instance->cmap_nodelist_track_handle = 0;
+
+	if (res != CS_OK) {
+		qdevice_net_log(LOG_WARNING, "Can't delete cmap nodelist tracking");
+	}
+
+	return (0);
+}
+
+void
+qdevice_net_cmap_destroy(struct qdevice_net_instance *instance)
+{
+	cs_error_t res;
+
+	res = cmap_finalize(instance->cmap_handle);
+
+        if (res != CS_OK) {
+		qdevice_net_log(LOG_WARNING, "Can't finalize cmap. Error %s", cs_strerror(res));
+	}
+
+	if (PR_DestroySocketPollFd(instance->cmap_poll_fd) != PR_SUCCESS) {
+		qdevice_net_log_nss(LOG_WARNING, "Unable to close votequorum connection fd");
+	}
+}

+ 10 - 1
qdevices/qdevice-net-cmap.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -38,6 +38,7 @@
 #include <cmap.h>
 
 #include "node-list.h"
+#include "qdevice-net-instance.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -51,6 +52,14 @@ extern int		qdevice_net_cmap_get_config_version(cmap_handle_t cmap_handle,
 
 extern void		qdevice_net_cmap_init(cmap_handle_t *handle);
 
+extern void		qdevice_net_cmap_init_fd(struct qdevice_net_instance *instance);
+
+extern int		qdevice_net_cmap_add_track(struct qdevice_net_instance *instance);
+
+extern int		qdevice_net_cmap_del_track(struct qdevice_net_instance *instance);
+
+extern void		qdevice_net_cmap_destroy(struct qdevice_net_instance *instance);
+
 #ifdef __cplusplus
 }
 #endif

+ 108 - 0
qdevices/qdevice-net-disconnect-reason.h

@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QDEVICE_NET_DISCONNECT_REASON_H_
+#define _QDEVICE_NET_DISCONNECT_REASON_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_net_disconnect_reason {
+	/* Undefined reason. If this error appears, it's error in source code */
+	QDEVICE_NET_DISCONNECT_REASON_UNDEFINED,
+
+	/* Received known message, but it was not expected */
+	QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG,
+	/* Received unknown message */
+	QDEVICE_NET_DISCONNECT_REASON_UNSUPPORTED_MSG,
+	/* TLS setting of server and client are incompatible */
+	QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_TLS,
+	/* MSG setting of server and client are incompatible */
+	QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE,
+	/* Message doesn't contain required option */
+	QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING,
+
+	/* Can't allocate send list item or message is too long to fit into send buffer */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER,
+	/* Impossible to create or update heartbeat sending timer */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_HB_TIMER,
+	/* Impossible to create or update votequorum poll timer */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER,
+	/* Impossible to register votequorum callback */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_VOTEQUORUM_CALLBACK,
+	/* Impossible to register cmap callback */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_CMAP_CALLBACK,
+	/* Impossible to start TLS session */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_START_TLS,
+
+	/* Received message with error field set to non TLV_REPLY_ERROR_CODE_NO_ERROR value */
+	QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR,
+	/* Server doesn't support client selected decision algorithm */
+	QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_ALGORITHM,
+
+	/* Can't decode message sent by server */
+	QDEVICE_NET_DISCONNECT_REASON_MSG_DECODE_ERROR,
+	/* Server closed connection */
+	QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION,
+	/* Can't read or store message received from server */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE,
+	/* Can't send message to server */
+	QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE,
+
+	/* Can't dispatch cmap or votequroum. This cannot be overwritten and always means end of qdevice-net */
+	QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED,
+
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_CONNECTED_ERR,
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_QUORUM_NOTIFY_ERR,
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_NOTIFY_ERR,
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_NODE_LIST_REPLY_ERR,
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_ASK_FOR_VOTE_REPLY_ERR,
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTE_INFO_ERR,
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_RECEIVED_ERR,
+	QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_NOT_RECEIVED_ERR,
+};
+
+#define qdevice_net_disconnect_reason_try_reconnect(reason)	(	\
+    reason == QDEVICE_NET_DISCONNECT_REASON_MSG_DECODE_ERROR ||		\
+    reason == QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION ||	\
+    reason == QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE ||	\
+    reason == QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_DISCONNECT_REASON_H_ */

+ 38 - 2
qdevices/qdevice-net-instance.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -42,7 +42,7 @@ qdevice_net_instance_init(struct qdevice_net_instance *instance, size_t initial_
     enum tlv_decision_algorithm_type decision_algorithm, uint32_t heartbeat_interval,
     uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
     const char *host_addr, uint16_t host_port, const char *cluster_name,
-    const struct tlv_tie_breaker *tie_breaker)
+    const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout)
 {
 
 	memset(instance, 0, sizeof(*instance));
@@ -60,6 +60,10 @@ qdevice_net_instance_init(struct qdevice_net_instance *instance, size_t initial_
 	instance->host_addr = host_addr;
 	instance->host_port = host_port;
 	instance->cluster_name = cluster_name;
+	instance->connect_timeout = connect_timeout;
+	instance->last_msg_seq_num = 1;
+	instance->echo_request_expected_msg_seq_num = 1;
+	instance->echo_reply_received_msg_seq_num = 1;
 	memcpy(&instance->tie_breaker, tie_breaker, sizeof(*tie_breaker));
 
 	dynar_init(&instance->receive_buffer, initial_receive_size);
@@ -69,11 +73,38 @@ qdevice_net_instance_init(struct qdevice_net_instance *instance, size_t initial_
 
 	timer_list_init(&instance->main_timer_list);
 
+	node_list_init(&instance->last_sent_config_node_list);
+
 	instance->tls_supported = tls_supported;
 
 	return (0);
 }
 
+void
+qdevice_net_instance_clean(struct qdevice_net_instance *instance)
+{
+
+	dynar_clean(&instance->receive_buffer);
+
+	send_buffer_list_free(&instance->send_buffer_list);
+
+	instance->skipping_msg = 0;
+	instance->msg_already_received_bytes = 0;
+	instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY;
+	instance->echo_request_expected_msg_seq_num = instance->echo_reply_received_msg_seq_num;
+	instance->using_tls = 0;
+
+	node_list_free(&instance->last_sent_config_node_list);
+
+	timer_list_free(&instance->main_timer_list);
+	instance->cast_vote_timer = NULL;
+	instance->echo_request_timer = NULL;
+
+	instance->schedule_disconnect = 0;
+	instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNDEFINED;
+	instance->cmap_reload_in_progress = 0;
+}
+
 int
 qdevice_net_instance_destroy(struct qdevice_net_instance *instance)
 {
@@ -82,7 +113,12 @@ qdevice_net_instance_destroy(struct qdevice_net_instance *instance)
 
 	send_buffer_list_free(&instance->send_buffer_list);
 
+	node_list_free(&instance->last_sent_config_node_list);
+
 	timer_list_free(&instance->main_timer_list);
 
+	free((void *)instance->cluster_name);
+	free((void *)instance->host_addr);
+
 	return (0);
 }

+ 15 - 2
qdevices/qdevice-net-instance.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -46,9 +46,11 @@
 #include <votequorum.h>
 
 #include "dynar.h"
+#include "node-list.h"
 #include "send-buffer-list.h"
 #include "tlv.h"
 #include "timer-list.h"
+#include "qdevice-net-disconnect-reason.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -82,6 +84,7 @@ struct qdevice_net_instance {
 	uint32_t heartbeat_interval;		/* Heartbeat interval during normal operation */
 	uint32_t sync_heartbeat_interval;	/* Heartbeat interval during corosync sync */
 	uint32_t cast_vote_timer_interval;	/* Timer for cast vote */
+	uint32_t connect_timeout;
 	struct timer_list_entry *cast_vote_timer;
 	enum tlv_vote cast_vote_timer_vote;
 	const char *host_addr;
@@ -94,8 +97,16 @@ struct qdevice_net_instance {
 	cmap_handle_t cmap_handle;
 	votequorum_handle_t votequorum_handle;
 	PRFileDesc *votequorum_poll_fd;
+	PRFileDesc *cmap_poll_fd;
 	votequorum_ring_id_t last_received_votequorum_ring_id;
+	struct tlv_ring_id last_sent_ring_id;
 	struct tlv_tie_breaker tie_breaker;
+	void *algorithm_data;
+	cmap_track_handle_t cmap_reload_track_handle;
+	cmap_track_handle_t cmap_nodelist_track_handle;
+	int cmap_reload_in_progress;
+	struct node_list last_sent_config_node_list;
+	enum qdevice_net_disconnect_reason disconnect_reason;
 };
 
 extern int		qdevice_net_instance_init(struct qdevice_net_instance *instance,
@@ -105,7 +116,9 @@ extern int		qdevice_net_instance_init(struct qdevice_net_instance *instance,
     enum tlv_decision_algorithm_type decision_algorithm, uint32_t heartbeat_interval,
     uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
     const char *host_addr, uint16_t host_port, const char *cluster_name,
-    const struct tlv_tie_breaker *tie_breaker);
+    const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout);
+
+extern void		qdevice_net_instance_clean(struct qdevice_net_instance *instance);
 
 extern int		qdevice_net_instance_destroy(struct qdevice_net_instance *instance);
 

+ 1 - 1
qdevices/qdevice-net-log.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 33 - 0
qdevices/qdevice-net-msg-received.c

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */

+ 47 - 0
qdevices/qdevice-net-msg-received.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QDEVICE_NET_MSG_RECEIVED_H_
+#define _QDEVICE_NET_MSG_RECEIVED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_MSG_RECEIVED_H_ */

+ 50 - 26
qdevices/qdevice-net-send.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -42,13 +42,6 @@ qdevice_net_send_echo_request(struct qdevice_net_instance *instance)
 {
 	struct send_buffer_list_entry *send_buffer;
 
-	if (instance->echo_reply_received_msg_seq_num !=
-	    instance->echo_request_expected_msg_seq_num) {
-		qdevice_net_log(LOG_ERR, "Server didn't send echo reply message on time. "
-		    "Disconnecting from server.");
-		return (-1);
-	}
-
 	send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
 	if (send_buffer == NULL) {
 		qdevice_net_log(LOG_CRIT, "Can't allocate send list buffer for reply msg.");
@@ -120,6 +113,9 @@ qdevice_net_send_ask_for_vote(struct qdevice_net_instance *instance)
 
 	instance->last_msg_seq_num++;
 
+	qdevice_net_log(LOG_DEBUG, "Sending ask for vote seq = %"PRIu32,
+	    instance->last_msg_seq_num);
+
 	if (msg_create_ask_for_vote(&send_buffer->buffer, instance->last_msg_seq_num) == 0) {
 		qdevice_net_log(LOG_ERR, "Can't allocate send buffer for ask for vote msg");
 
@@ -131,25 +127,46 @@ qdevice_net_send_ask_for_vote(struct qdevice_net_instance *instance)
 	return (0);
 }
 
+static
+void qdevice_net_log_debug_node_list(const struct node_list *nlist)
+{
+	struct node_list_entry *node_info;
+	size_t zi;
+
+	qdevice_net_log(LOG_DEBUG, "  Node list:");
+
+	zi = 0;
+
+	TAILQ_FOREACH(node_info, nlist, entries) {
+		qdevice_net_log(LOG_DEBUG, "    %zu node_id = %"PRIx32", "
+		    "data_center_id = %"PRIx32", node_state = %s",
+		    zi, node_info->node_id, node_info->data_center_id,
+		    tlv_node_state_to_str(node_info->node_state));
+		zi++;
+        }
+}
+
 int
-qdevice_net_send_config_node_list(struct qdevice_net_instance *instance, int initial)
+qdevice_net_send_config_node_list(struct qdevice_net_instance *instance, int initial, int force_send)
 {
 	struct node_list nlist;
 	struct send_buffer_list_entry *send_buffer;
 	uint64_t config_version;
 	int send_config_version;
-	struct node_list_entry *node_info;
-	size_t zi;
 
-	/*
-	 * Send initial node list
-	 */
 	if (qdevice_net_cmap_get_nodelist(instance->cmap_handle, &nlist) != 0) {
 		qdevice_net_log(LOG_ERR, "Can't get initial configuration node list.");
 
 		return (-1);
 	}
 
+	/*
+	 * Send only if list changed or force was used
+	 */
+	if (!force_send && node_list_eq(&instance->last_sent_config_node_list, &nlist)) {
+		return (0);
+	}
+
 	send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
 	if (send_buffer == NULL) {
 		qdevice_net_log(LOG_ERR, "Can't allocate send list buffer for config "
@@ -165,18 +182,18 @@ qdevice_net_send_config_node_list(struct qdevice_net_instance *instance, int ini
 
 	instance->last_msg_seq_num++;
 
-	qdevice_net_log(LOG_DEBUG, "Sending config node list seq=%"PRIu32".",
+	qdevice_net_log(LOG_DEBUG, "Sending config node list seq = %"PRIu32,
 	    instance->last_msg_seq_num);
+	qdevice_net_log_debug_node_list(&nlist);
 
-	qdevice_net_log(LOG_DEBUG, "  Node list:");
+	node_list_free(&instance->last_sent_config_node_list);
+	if (node_list_clone(&instance->last_sent_config_node_list, &nlist) != 0) {
+		qdevice_net_log(LOG_ERR, "Can't allocate last sent config node list clone");
 
-	zi = 0;
+		node_list_free(&nlist);
 
-	TAILQ_FOREACH(node_info, &nlist, entries) {
-		qdevice_net_log(LOG_DEBUG, "    %zu node_id = %"PRIx32", "
-		    "data_center_id = %"PRIx32, zi, node_info->node_id, node_info->data_center_id);
-		zi++;
-        }
+		return (-1);
+	}
 
 	if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num,
 	    (initial ? TLV_NODE_LIST_TYPE_INITIAL_CONFIG : TLV_NODE_LIST_TYPE_CHANGED_CONFIG),
@@ -228,8 +245,10 @@ qdevice_net_send_membership_node_list(struct qdevice_net_instance *instance,
 
 	instance->last_msg_seq_num++;
 
-	qdevice_net_log(LOG_DEBUG, "Sending membership node list seq=%"PRIu32".",
-	    instance->last_msg_seq_num);
+	qdevice_net_log(LOG_DEBUG, "Sending membership node list seq = %"PRIu32", "
+	    "ringid = (%"PRIx32".%"PRIx64").", instance->last_msg_seq_num,
+	    ring_id->node_id, ring_id->seq);
+	qdevice_net_log_debug_node_list(&nlist);
 
 	if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num,
 	    TLV_NODE_LIST_TYPE_MEMBERSHIP,
@@ -241,6 +260,8 @@ qdevice_net_send_membership_node_list(struct qdevice_net_instance *instance,
 		return (-1);
 	}
 
+	memcpy(&instance->last_sent_ring_id, ring_id, sizeof(instance->last_sent_ring_id));
+
 	node_list_free(&nlist);
 
 	send_buffer_list_put(&instance->send_buffer_list, send_buffer);
@@ -286,8 +307,9 @@ qdevice_net_send_quorum_node_list(struct qdevice_net_instance *instance,
 
 	instance->last_msg_seq_num++;
 
-	qdevice_net_log(LOG_DEBUG, "Sending quorum node list seq=%"PRIu32".",
-	    instance->last_msg_seq_num);
+	qdevice_net_log(LOG_DEBUG, "Sending quorum node list seq = %"PRIu32", quorate = %u",
+	    instance->last_msg_seq_num, quorate);
+	qdevice_net_log_debug_node_list(&nlist);
 
 	if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num,
 	    TLV_NODE_LIST_TYPE_QUORUM,
@@ -299,6 +321,8 @@ qdevice_net_send_quorum_node_list(struct qdevice_net_instance *instance,
 		return (-1);
 	}
 
+	node_list_free(&nlist);
+
 	send_buffer_list_put(&instance->send_buffer_list, send_buffer);
 
 	return (0);

+ 2 - 2
qdevices/qdevice-net-send.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -51,7 +51,7 @@ extern int		qdevice_net_send_init(struct qdevice_net_instance *instance);
 extern int		qdevice_net_send_ask_for_vote(struct qdevice_net_instance *instance);
 
 extern int		qdevice_net_send_config_node_list(struct qdevice_net_instance *instance,
-    int initial);
+    int initial, int force_send);
 
 extern int		qdevice_net_send_membership_node_list(
     struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,

+ 79 - 20
qdevices/qdevice-net-votequorum.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -37,7 +37,7 @@
 
 /*
  * Needed for creating nspr handle from unix fd
-  */
+ */
 #include <private/pprio.h>
 
 #include "qnet-config.h"
@@ -45,6 +45,7 @@
 #include "qdevice-net-send.h"
 #include "qdevice-net-log.h"
 #include "qdevice-net-cast-vote-timer.h"
+#include "qdevice-net-algorithm.h"
 #include "nss-sock.h"
 
 enum tlv_node_state
@@ -81,6 +82,7 @@ qdevice_net_votequorum_quorum_notify_callback(votequorum_handle_t votequorum_han
 {
 	struct qdevice_net_instance *instance;
 	uint32_t u32;
+	int algo_send_list;
 
 	if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) {
 		errx(1, "Fatal error. Can't get votequorum context");
@@ -91,17 +93,32 @@ qdevice_net_votequorum_quorum_notify_callback(votequorum_handle_t votequorum_han
 
 	qdevice_net_log(LOG_DEBUG, "  Node list (size = %"PRIu32"):", node_list_entries);
 	for (u32 = 0; u32 < node_list_entries; u32++) {
-		qdevice_net_log(LOG_DEBUG, "    %"PRIu32" nodeid = %"PRIu32", state = %"PRIu32,
+		qdevice_net_log(LOG_DEBUG, "    %"PRIu32" nodeid = %"PRIx32", state = %"PRIu32,
 		    u32, node_list[u32].nodeid, node_list[u32].state);
 	}
 
-	if (qdevice_net_send_quorum_node_list(instance,
-	    (quorate ? TLV_QUORATE_QUORATE : TLV_QUORATE_INQUORATE),
-	    node_list_entries, node_list) != 0) {
-		/*
-		 * Fatal error -> schedule disconnect
-		 */
+	algo_send_list = 1;
+	if (qdevice_net_algorithm_votequorum_quorum_notify(instance, quorate,
+	    node_list_entries, node_list, &algo_send_list) != 0) {
+		qdevice_net_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_QUORUM_NOTIFY_ERR;
 		instance->schedule_disconnect = 1;
+	} else {
+		qdevice_net_log(LOG_DEBUG, "Algorithm decided to %s list",
+		    (algo_send_list ? "send" : "not send"));
+	}
+
+	if (algo_send_list) {
+		if (qdevice_net_send_quorum_node_list(instance,
+		    (quorate ? TLV_QUORATE_QUORATE : TLV_QUORATE_INQUORATE),
+		    node_list_entries, node_list) != 0) {
+			/*
+			 * Fatal error -> schedule disconnect
+			 */
+			instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+			instance->schedule_disconnect = 1;
+		}
 	}
 }
 
@@ -113,9 +130,11 @@ qdevice_net_votequorum_node_list_notify_callback(votequorum_handle_t votequorum_
 	struct qdevice_net_instance *instance;
 	struct tlv_ring_id tlv_rid;
 	uint32_t u32;
+	enum tlv_vote result_vote;
 
 	if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) {
-		errx(1, "Fatal error. Can't get votequorum context");
+		qdevice_net_log(LOG_ERR, "Fatal error. Can't get votequorum context");
+		exit(1);
 	}
 
 	qdevice_net_log(LOG_DEBUG, "Votequorum nodelist notify callback:");
@@ -124,26 +143,43 @@ qdevice_net_votequorum_node_list_notify_callback(votequorum_handle_t votequorum_
 
 	qdevice_net_log(LOG_DEBUG, "  Node list (size = %"PRIu32"):", node_list_entries);
 	for (u32 = 0; u32 < node_list_entries; u32++) {
-		qdevice_net_log(LOG_DEBUG, "    %"PRIu32" nodeid = %"PRIu32,
+		qdevice_net_log(LOG_DEBUG, "    %"PRIu32" nodeid = %"PRIx32,
 		    u32, node_list[u32]);
 	}
 
 	qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid, &votequorum_ring_id);
 
-	if (qdevice_net_send_membership_node_list(instance,
-	    &tlv_rid, node_list_entries, node_list) != 0) {
-		/*
-		 * Fatal error -> schedule disconnect
-		 */
+	result_vote = TLV_VOTE_WAIT_FOR_REPLY;
+	if (qdevice_net_algorithm_votequorum_node_list_notify(instance, &tlv_rid, node_list_entries,
+	    node_list, &result_vote) != 0) {
+		qdevice_net_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+		instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_NOTIFY_ERR;
 		instance->schedule_disconnect = 1;
+	} else {
+		qdevice_net_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+	}
+
+	if (result_vote == TLV_VOTE_WAIT_FOR_REPLY || result_vote == TLV_VOTE_ASK_LATER) {
+		if (qdevice_net_send_membership_node_list(instance, &tlv_rid, node_list_entries, node_list) != 0) {
+			/*
+			 * Fatal error -> schedule disconnect
+			 */
+			instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+			instance->schedule_disconnect = 1;
+		}
 	}
 
 	memcpy(&instance->last_received_votequorum_ring_id, &votequorum_ring_id,
 	    sizeof(votequorum_ring_id));
 
-	if (qdevice_net_cast_vote_timer_update(instance, TLV_VOTE_WAIT_FOR_REPLY) != 0) {
-		errx(1, "qdevice_net_votequorum_notify_callback fatal error. "
-		    "Can't update cast vote timer vote");
+	if (result_vote != TLV_VOTE_NO_CHANGE && result_vote != TLV_VOTE_ASK_LATER) {
+		if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+			qdevice_net_log(LOG_CRIT, "qdevice_net_votequorum_node_list_notify_callback fatal error "
+			    "Can't update cast vote timer");
+			instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+			instance->schedule_disconnect = 1;
+		}
 	}
 }
 
@@ -189,6 +225,29 @@ qdevice_net_votequorum_init(struct qdevice_net_instance *instance)
 
 	votequorum_fd_get(votequorum_handle, &fd);
 	if ((instance->votequorum_poll_fd = PR_CreateSocketPollFd(fd)) == NULL) {
-		nss_sock_err(1);
+		qdevice_net_log_nss(LOG_CRIT, "Can't create NSPR votequorum poll fd");
+		exit(1);
+	}
+}
+
+void
+qdevice_net_votequorum_destroy(struct qdevice_net_instance *instance)
+{
+	cs_error_t res;
+
+	res = votequorum_qdevice_unregister(instance->votequorum_handle,
+		QDEVICE_NET_VOTEQUORUM_DEVICE_NAME);
+
+        if (res != CS_OK) {
+                qdevice_net_log(LOG_WARNING, "Unable to unregister votequorum device. Error %s", cs_strerror(res));
+	}
+
+	res = votequorum_finalize(instance->votequorum_handle);
+	if (res != CS_OK) {
+		qdevice_net_log(LOG_WARNING, "Unable to finalize votequorum. Error %s", cs_strerror(res));
+	}
+
+	if (PR_DestroySocketPollFd(instance->votequorum_poll_fd) != PR_SUCCESS) {
+		qdevice_net_log_nss(LOG_WARNING, "Unable to close votequorum connection fd");
 	}
 }

+ 3 - 1
qdevices/qdevice-net-votequorum.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -52,6 +52,8 @@ extern void			qdevice_net_votequorum_ring_id_to_tlv(struct tlv_ring_id *tlv_rid,
 
 extern void			qdevice_net_votequorum_init(struct qdevice_net_instance *instance);
 
+extern void			qdevice_net_votequorum_destroy(struct qdevice_net_instance *instance);
+
 #ifdef __cplusplus
 }
 #endif

+ 10 - 1
qdevices/qnet-config.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -87,6 +87,13 @@ extern "C" {
 
 #define QDEVICE_NET_LOCK_FILE			LOCALSTATEDIR"/run/corosync-qdevice-net.pid"
 
+#define QDEVICE_NET_CONNECT_TIMEOUT		(1000*30)
+#define QDEVICE_NET_MIN_CONNECT_TIMEOUT		1L
+#define QDEVICE_NET_MAX_CONNECT_TIMEOUT		(1000*60*10L)
+
+#define QDEVICE_NET_PAUSE_BEFORE_RECONNECT	1000
+
+
 /*
  * Decision algorithms supported by qnetd
  */
@@ -95,6 +102,8 @@ extern "C" {
 extern enum tlv_decision_algorithm_type
     qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE];
 
+#define QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE	QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 1
qdevices/qnetd-algo-2nodelms.c

@@ -148,7 +148,7 @@ qnetd_algo_2nodelms_membership_node_list_received(struct qnetd_client *client,
 	if (info->last_result == 0) {
 		TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
 			struct qnetd_algo_2nodelms_info *other_info = other_client->algorithm_data;
-			if (!qnetd_algo_rings_eq(&client->last_ring_id, &other_client->last_ring_id) &&
+			if (!tlv_ring_id_eq(&client->last_ring_id, &other_client->last_ring_id) &&
 			    other_info->last_result == TLV_VOTE_ACK) {
 
 				/* Don't save NACK, we need to know subsequently if we haven't been voting */

+ 1 - 1
qdevices/qnetd-algo-2nodelms.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-algo-ffsplit.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-algo-ffsplit.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 3 - 3
qdevices/qnetd-algo-lms.c

@@ -101,7 +101,7 @@ static enum tlv_reply_error_code do_lms_algorithm(struct qnetd_client *client,
 	if (info->last_result == 0) {
 		TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
 			struct qnetd_algo_lms_info *other_info = other_client->algorithm_data;
-			if (!qnetd_algo_rings_eq(&client->last_ring_id, &other_client->last_ring_id) &&
+			if (!tlv_ring_id_eq(&client->last_ring_id, &other_client->last_ring_id) &&
 			    other_info->last_result == TLV_VOTE_ACK) {
 				qnetd_algo_free_partitions(&info->partition_list);
 
@@ -136,7 +136,7 @@ static enum tlv_reply_error_code do_lms_algorithm(struct qnetd_client *client,
 
 	if (!joint_leader) {
 		/* Largest partition is unique, allow us to run if we're in that partition. */
-		if (qnetd_algo_rings_eq(&largest_partition->ring_id, &client->last_ring_id)) {
+		if (tlv_ring_id_eq(&largest_partition->ring_id, &client->last_ring_id)) {
 			qnetd_log(LOG_DEBUG, "algo-lms: We are in the largest partition. ACK\n");
 			*result_vote = info->last_result = TLV_VOTE_ACK;
 		}
@@ -201,7 +201,7 @@ static enum tlv_reply_error_code do_lms_algorithm(struct qnetd_client *client,
 			}
 		}
 
-		if (client->node_id == tb_node_id || qnetd_algo_rings_eq(&tb_node_ring_id, &client->last_ring_id)) {
+		if (client->node_id == tb_node_id || tlv_ring_id_eq(&tb_node_ring_id, &client->last_ring_id)) {
 			qnetd_log(LOG_DEBUG, "algo-lms: We are in the same partition (%d/%ld) as tie-breaker node id %d. ACK",
 				  tb_node_ring_id.node_id, tb_node_ring_id.seq, tb_node_id);
 			*result_vote = info->last_result = TLV_VOTE_ACK;

+ 1 - 1
qdevices/qnetd-algo-lms.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 9 - 88
qdevices/qnetd-algo-test.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -40,18 +40,7 @@
 #include "qnetd-log.h"
 #include "qnetd-cluster-list.h"
 #include "qnetd-client-send.h"
-
-static void
-qnetd_algo_test_dump_cluster(struct qnetd_cluster *cluster)
-{
-	struct qnetd_client *client;
-
-	qnetd_log(LOG_INFO, "algo-test:   Cluster dump:");
-	TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
-		qnetd_log(LOG_INFO, "algo-test:     client = %p, node_id = %"PRIx32,
-		    client, client->node_id);
-	}
-}
+#include "qnetd-log-debug.h"
 
 /*
  * Called right after client sent init message. This happens after initial accept of client,
@@ -74,15 +63,7 @@ qnetd_algo_test_client_init(struct qnetd_client *client)
 {
 	int *algo_data;
 
-	qnetd_log(LOG_INFO, "algo-test: New client connected");
-	qnetd_log(LOG_INFO, "algo-test:   cluster name = %s", client->cluster_name);
-	qnetd_log(LOG_INFO, "algo-test:   tls started = %u", client->tls_started);
-	qnetd_log(LOG_INFO, "algo-test:   tls peer certificate verified = %u",
-	    client->tls_peer_certificate_verified);
-	qnetd_log(LOG_INFO, "algo-test:   node_id = %"PRIx32, client->node_id);
-	qnetd_log(LOG_INFO, "algo-test:   pointer = %p", client);
-
-	qnetd_algo_test_dump_cluster(client->cluster);
+	qnetd_log(LOG_INFO, "algo-test: client_init");
 
 	client->algorithm_data = malloc(sizeof(int));
 	if (client->algorithm_data == NULL) {
@@ -95,38 +76,6 @@ qnetd_algo_test_client_init(struct qnetd_client *client)
 	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
 }
 
-static const char *
-qnetd_algo_test_node_state_to_str(enum tlv_node_state node_state)
-{
-	switch (node_state) {
-	case TLV_NODE_STATE_NOT_SET: return ("not set"); break;
-	case TLV_NODE_STATE_MEMBER: return ("member"); break;
-	case TLV_NODE_STATE_DEAD: return ("dead"); break;
-	case TLV_NODE_STATE_LEAVING: return ("leaving"); break;
-	default: return ("unhandled"); break;
-	}
-
-	return ("");
-}
-
-
-static void
-qnetd_algo_dump_node_list(struct qnetd_client *client, const struct node_list *nodes)
-{
-	int *algo_data;
-	struct node_list_entry *node_info;
-
-	algo_data = client->algorithm_data;
-
-	qnetd_log(LOG_INFO, "algo-test:   algo data = %u", *algo_data);
-
-	TAILQ_FOREACH(node_info, nodes, entries) {
-		qnetd_log(LOG_INFO, "algo-test:   node_id = %"PRIx32", "
-		    "data_center_id = %"PRIx32", "
-		    "node_state = %s", node_info->node_id, node_info->data_center_id,
-		    qnetd_algo_test_node_state_to_str(node_info->node_state));
-	}
-}
 
 /*
  * Called after client sent configuration node list
@@ -147,17 +96,7 @@ qnetd_algo_test_config_node_list_received(struct qnetd_client *client,
     const struct node_list *nodes, int initial, enum tlv_vote *result_vote)
 {
 
-	qnetd_log(LOG_INFO, "algo-test: Client %p (cluster %s, node_id %"PRIx32") "
-	    "sent %s node list.", client, client->cluster_name, client->node_id,
-	    (initial ? "initial" : "changed"));
-
-	qnetd_log(LOG_INFO, "algo-test:   msg seq num %"PRIu32, msg_seq_num);
-
-	if (config_version_set) {
-		qnetd_log(LOG_INFO, "algo-test:   config version %"PRIu64, config_version);
-	}
-
-	qnetd_algo_dump_node_list(client, nodes);
+	qnetd_log(LOG_INFO, "algo-test: node_list_received");
 
 	*result_vote = TLV_VOTE_NO_CHANGE;
 
@@ -183,15 +122,7 @@ qnetd_algo_test_membership_node_list_received(struct qnetd_client *client,
     const struct node_list *nodes, enum tlv_vote *result_vote)
 {
 
-	qnetd_log(LOG_INFO, "algo-test: Client %p (cluster %s, node_id %"PRIx32") "
-	    "sent membership node list.", client, client->cluster_name, client->node_id);
-
-	qnetd_log(LOG_INFO, "algo-test:   msg seq num %"PRIu32, msg_seq_num);
-
-	qnetd_log(LOG_INFO, "algo-test:   ring id = (%"PRIx32".%"PRIx64")",
-	    ring_id->node_id, ring_id->seq);
-
-	qnetd_algo_dump_node_list(client, nodes);
+	qnetd_log(LOG_INFO, "algo-test: membership_node_list_received");
 
 	*result_vote = TLV_VOTE_ACK;
 
@@ -214,13 +145,8 @@ qnetd_algo_test_quorum_node_list_received(struct qnetd_client *client,
     uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes,
     enum tlv_vote *result_vote)
 {
-	qnetd_log(LOG_INFO, "algo-test: Client %p (cluster %s, node_id %"PRIx32") "
-	    "sent quorum node list.", client, client->cluster_name, client->node_id);
-
-	qnetd_log(LOG_INFO, "algo-test:   msg seq num %"PRIu32, msg_seq_num);
-	qnetd_log(LOG_INFO, "algo-test:   quorate = %u", quorate);
 
-	qnetd_algo_dump_node_list(client, nodes);
+	qnetd_log(LOG_INFO, "algo-test: quorum_node_list_received");
 
 	*result_vote = TLV_VOTE_NO_CHANGE;
 
@@ -236,10 +162,7 @@ void
 qnetd_algo_test_client_disconnect(struct qnetd_client *client, int server_going_down)
 {
 
-	qnetd_log(LOG_INFO, "algo-test: Client %p (cluster %s, node_id %"PRIx32") "
-	    "disconnect", client, client->cluster_name, client->node_id);
-
-	qnetd_log(LOG_INFO, "algo-test:   server going down %u", server_going_down);
+	qnetd_log(LOG_INFO, "algo-test: client_disconnect");
 
 	free(client->algorithm_data);
 }
@@ -253,8 +176,7 @@ qnetd_algo_test_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_
     enum tlv_vote *result_vote)
 {
 
-	qnetd_log(LOG_INFO, "algo-test: Client %p (cluster %s, node_id %"PRIx32") "
-	    "asked for a vote", client, client->cluster_name, client->node_id);
+	qnetd_log(LOG_INFO, "algo-test: ask_for_vote_received");
 
 	*result_vote = TLV_VOTE_ACK;
 
@@ -265,8 +187,7 @@ enum tlv_reply_error_code
 qnetd_algo_test_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
 {
 
-	qnetd_log(LOG_INFO, "algo-test: Client %p (cluster %s, node_id %"PRIx32") "
-	    "replied back to vote info message", client, client->cluster_name, client->node_id);
+	qnetd_log(LOG_INFO, "algo-test: vote_info_reply_received");
 
 	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
 }

+ 1 - 1
qdevices/qnetd-algo-test.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 19 - 26
qdevices/qnetd-algo-utils.c

@@ -48,7 +48,8 @@
  * If this happens it simply means that qnetd does not yet have the full current view
  * of the cluster and should wait until all of the ring_ids in this membership list match up
  */
-int qnetd_algo_all_ring_ids_match(struct qnetd_client *client, const struct tlv_ring_id *ring_id)
+int
+qnetd_algo_all_ring_ids_match(struct qnetd_client *client, const struct tlv_ring_id *ring_id)
 {
 	struct node_list_entry *node_info;
  	struct qnetd_client *other_client;
@@ -72,40 +73,30 @@ int qnetd_algo_all_ring_ids_match(struct qnetd_client *client, const struct tlv_
 		 * If the other nodes on our side of a partition have a different ring ID then
 		 * we need to wait until they have all caught up before making a decision
 		 */
-		if (in_our_partition && !qnetd_algo_rings_eq(ring_id, &other_client->last_ring_id)) {
+		if (in_our_partition && !tlv_ring_id_eq(ring_id, &other_client->last_ring_id)) {
 			qnetd_log(LOG_DEBUG, "algo-util: nodeid %d in our partition has different ring_id (%d/%ld) to us (%d/%ld)", other_client->node_id, other_client->last_ring_id.node_id, other_client->last_ring_id.seq, ring_id->node_id, ring_id->seq);
-			return -1; /* ring IDs don't match */
+			return (-1); /* ring IDs don't match */
 		}
 	}
-	return 0;
+	return (0);
 }
 
-
-int qnetd_algo_rings_eq(const struct tlv_ring_id *ring_id1, const struct tlv_ring_id *ring_id2)
-{
-	if (ring_id1->node_id == ring_id2->node_id &&
-	    ring_id1->seq == ring_id2->seq) {
-		return 1;
-	}
-	else {
-		return 0;
-	}
-}
-
-struct qnetd_algo_partition *qnetd_algo_find_partition(partitions_list_t *partitions_list, const struct tlv_ring_id *ring_id)
+struct qnetd_algo_partition *
+qnetd_algo_find_partition(partitions_list_t *partitions_list, const struct tlv_ring_id *ring_id)
 {
 	struct qnetd_algo_partition *cur_partition;
 
 	TAILQ_FOREACH(cur_partition, partitions_list, entries) {
-		if (qnetd_algo_rings_eq(&cur_partition->ring_id, ring_id)) {
-			return cur_partition;
+		if (tlv_ring_id_eq(&cur_partition->ring_id, ring_id)) {
+			return (cur_partition);
 		}
 	}
 
-	return NULL;
+	return (NULL);
 }
 
-int qnetd_algo_create_partitions(struct qnetd_client *client, partitions_list_t *partitions_list, const struct tlv_ring_id *ring_id)
+int
+qnetd_algo_create_partitions(struct qnetd_client *client, partitions_list_t *partitions_list, const struct tlv_ring_id *ring_id)
 {
  	struct qnetd_client *other_client;
 	int num_partitions = 0;
@@ -120,7 +111,7 @@ int qnetd_algo_create_partitions(struct qnetd_client *client, partitions_list_t
 		if (!partition) {
 			partition = malloc(sizeof(struct qnetd_algo_partition));
 			if (!partition) {
-				return -1;
+				return (-1);
 			}
 			partition->num_nodes = 0;
 			memcpy(&partition->ring_id, &other_client->last_ring_id, sizeof(*ring_id));
@@ -130,11 +121,13 @@ int qnetd_algo_create_partitions(struct qnetd_client *client, partitions_list_t
 		partition->num_nodes++;
 
 	}
-	return num_partitions;
+
+	return (num_partitions);
 }
 
 
-void qnetd_algo_free_partitions(partitions_list_t *partitions_list)
+void
+qnetd_algo_free_partitions(partitions_list_t *partitions_list)
 {
 	struct qnetd_algo_partition *cur_partition;
 
@@ -144,7 +137,8 @@ void qnetd_algo_free_partitions(partitions_list_t *partitions_list)
 	}
 }
 
-void qnetd_algo_dump_partitions(partitions_list_t *partitions_list)
+void
+qnetd_algo_dump_partitions(partitions_list_t *partitions_list)
 {
 	struct qnetd_algo_partition *partition;
 
@@ -153,4 +147,3 @@ void qnetd_algo_dump_partitions(partitions_list_t *partitions_list)
 			  partition->ring_id.node_id, partition->ring_id.seq, partition, partition->num_nodes);
 	}
 }
-

+ 24 - 6
qdevices/qnetd-algo-utils.h

@@ -32,18 +32,36 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef _QNETD_ALGO_UTILS_H_
+#define _QNETD_ALGO_UTILS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 struct qnetd_algo_partition {
 	struct tlv_ring_id ring_id;
 	int num_nodes;
 	TAILQ_ENTRY(qnetd_algo_partition) entries;
 };
+
 typedef TAILQ_HEAD(, qnetd_algo_partition) partitions_list_t;
 
-int qnetd_algo_all_ring_ids_match(struct qnetd_client *client, const struct tlv_ring_id *ring_id);
-int qnetd_algo_rings_eq(const struct tlv_ring_id *ring_id1, const struct tlv_ring_id *ring_id2);
+extern int				 qnetd_algo_all_ring_ids_match(struct qnetd_client *client,
+    const struct tlv_ring_id *ring_id);
+
+extern struct qnetd_algo_partition	*qnetd_algo_find_partition(partitions_list_t *partitions,
+    const struct tlv_ring_id *ring_id);
+
+extern int				 qnetd_algo_create_partitions(struct qnetd_client *client,
+    partitions_list_t *partitions, const struct tlv_ring_id *ring_id);
+
+extern void				 qnetd_algo_free_partitions(partitions_list_t *partitions);
+
+extern void				 qnetd_algo_dump_partitions(partitions_list_t *partitions);
+
+#ifdef __cplusplus
+}
+#endif
 
-struct qnetd_algo_partition *qnetd_algo_find_partition(partitions_list_t *partitions, const struct tlv_ring_id *ring_id);
-int qnetd_algo_create_partitions(struct qnetd_client *client, partitions_list_t *partitions, const struct tlv_ring_id *ring_id);
-void qnetd_algo_free_partitions(partitions_list_t *partitions);
-void qnetd_algo_dump_partitions(partitions_list_t *partitions);
+#endif /* _QNETD_ALGO_UTILS_H_ */

+ 36 - 27
qdevices/qnetd-algorithm.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -36,25 +36,26 @@
 
 #include <err.h>
 
+#include "qnet-config.h"
 #include "qnetd-algorithm.h"
 #include "qnetd-algo-test.h"
 #include "qnetd-algo-ffsplit.h"
 #include "qnetd-algo-2nodelms.h"
 #include "qnetd-algo-lms.h"
 
-static struct qnetd_algorithm *qnetd_algorithm[MAX_QNETD_ALGORITHMS];
+static struct qnetd_algorithm *qnetd_algorithm_array[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE];
 
 enum tlv_reply_error_code
 qnetd_algorithm_client_init(struct qnetd_client *client)
 {
-	if (client->decision_algorithm >= MAX_QNETD_ALGORITHMS ||
-	    qnetd_algorithm[client->decision_algorithm] == NULL) {
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
 
 		errx(1, "qnetd_algorithm_client_init unhandled decision algorithm");
 		return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
 	}
 
-	return (qnetd_algorithm[client->decision_algorithm]->init(client));
+	return (qnetd_algorithm_array[client->decision_algorithm]->init(client));
 }
 
 enum tlv_reply_error_code
@@ -62,15 +63,15 @@ qnetd_algorithm_config_node_list_received(struct qnetd_client *client,
     uint32_t msg_seq_num, int config_version_set, uint64_t config_version,
     const struct node_list *nodes, int initial, enum tlv_vote *result_vote)
 {
-	if (client->decision_algorithm >= MAX_QNETD_ALGORITHMS ||
-	    qnetd_algorithm[client->decision_algorithm] == NULL) {
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
 
 		errx(1, "qnetd_algorithm_config_node_list_received unhandled "
 		     "decision algorithm");
 		return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
 	}
 
-	return (qnetd_algorithm[client->decision_algorithm]->config_node_list_received(
+	return (qnetd_algorithm_array[client->decision_algorithm]->config_node_list_received(
 		client, msg_seq_num,
 		config_version_set, config_version, nodes, initial, result_vote));
 }
@@ -81,15 +82,15 @@ qnetd_algorithm_membership_node_list_received(struct qnetd_client *client,
     const struct node_list *nodes, enum tlv_vote *result_vote)
 {
 
-	if (client->decision_algorithm >= MAX_QNETD_ALGORITHMS ||
-	    qnetd_algorithm[client->decision_algorithm] == NULL) {
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
 
 		errx(1, "qnetd_algorithm_membership_node_list_received unhandled "
 		    "decision algorithm");
 		return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
 	}
 
-	return (qnetd_algorithm[client->decision_algorithm]->membership_node_list_received(
+	return (qnetd_algorithm_array[client->decision_algorithm]->membership_node_list_received(
 		client, msg_seq_num,
 		ring_id, nodes, result_vote));
 }
@@ -100,56 +101,59 @@ qnetd_algorithm_quorum_node_list_received(struct qnetd_client *client,
     const struct node_list *nodes, enum tlv_vote *result_vote)
 {
 
-	if (client->decision_algorithm >= MAX_QNETD_ALGORITHMS ||
-	    qnetd_algorithm[client->decision_algorithm] == NULL) {
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
 
 		errx(1, "qnetd_algorithm_quorum_node_list_received unhandled "
 		    "decision algorithm");
 		return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
 	}
 
-	return (qnetd_algorithm[client->decision_algorithm]->quorum_node_list_received(
+	return (qnetd_algorithm_array[client->decision_algorithm]->quorum_node_list_received(
 		client, msg_seq_num, quorate, nodes, result_vote));
 }
 
 void
 qnetd_algorithm_client_disconnect(struct qnetd_client *client, int server_going_down)
 {
-	if (client->decision_algorithm >= MAX_QNETD_ALGORITHMS ||
-	    qnetd_algorithm[client->decision_algorithm] == NULL) {
+
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
 
 		errx(1, "qnetd_algorithm_client_disconnect unhandled decision algorithm");
 		return;
 	}
 
-	qnetd_algorithm[client->decision_algorithm]->client_disconnect(client, server_going_down);
+	qnetd_algorithm_array[client->decision_algorithm]->client_disconnect(client, server_going_down);
 }
 
 enum tlv_reply_error_code
 qnetd_algorithm_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num,
     enum tlv_vote *result_vote)
 {
-	if (client->decision_algorithm >= MAX_QNETD_ALGORITHMS ||
-	    qnetd_algorithm[client->decision_algorithm] == NULL) {
+
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
 
 		errx(1, "qnetd_algorithm_ask_for_vote_received unhandled decision algorithm");
 		return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
 	}
 
-	return (qnetd_algorithm[client->decision_algorithm]->ask_for_vote_received(
+	return (qnetd_algorithm_array[client->decision_algorithm]->ask_for_vote_received(
 		client, msg_seq_num, result_vote));
 }
 
 enum tlv_reply_error_code
 qnetd_algorithm_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
 {
-	if (client->decision_algorithm >= MAX_QNETD_ALGORITHMS ||
-	    qnetd_algorithm[client->decision_algorithm] == NULL) {
+
+	if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+	    qnetd_algorithm_array[client->decision_algorithm] == NULL) {
 		errx(1, "qnetd_algorithm_vote_info_reply_received unhandled decision algorithm");
 		return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
 	}
 
-	return (qnetd_algorithm[client->decision_algorithm]->vote_info_reply_received(
+	return (qnetd_algorithm_array[client->decision_algorithm]->vote_info_reply_received(
 		client, msg_seq_num));
 
 }
@@ -159,19 +163,24 @@ enum tlv_reply_error_code
 qnetd_algorithm_register(enum tlv_decision_algorithm_type algorithm_number,
     struct qnetd_algorithm *algorithm)
 {
-	if (algorithm_number >= MAX_QNETD_ALGORITHMS) {
+
+	if (algorithm_number >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) {
 		return (TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM);
 	}
 
-	if (qnetd_algorithm[algorithm_number] != NULL) {
+	if (qnetd_algorithm_array[algorithm_number] != NULL) {
 		return (TLV_REPLY_ERROR_CODE_DECISION_ALGORITHM_ALREADY_REGISTERED);
 	}
-	qnetd_algorithm[algorithm_number] = algorithm;
+
+	qnetd_algorithm_array[algorithm_number] = algorithm;
+
 	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
 }
 
-void algorithms_register(void)
+void
+qnetd_algorithm_register_all(void)
 {
+
 	if (qnetd_algo_test_register() != TLV_REPLY_ERROR_CODE_NO_ERROR) {
 		errx(1, "Failed to register decision algorithm 'test' ");
 	}

+ 2 - 4
qdevices/qnetd-algorithm.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -99,9 +99,7 @@ struct qnetd_algorithm {
 extern enum tlv_reply_error_code	qnetd_algorithm_register(
 	enum tlv_decision_algorithm_type algorithm_number, struct qnetd_algorithm *algorithm);
 
-extern void algorithms_register(void);
-
-#define MAX_QNETD_ALGORITHMS 10
+extern void qnetd_algorithm_register_all(void);
 
 #ifdef __cplusplus
 }

+ 1 - 1
qdevices/qnetd-client-list.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-client-list.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 40 - 3
qdevices/qnetd-client-msg-received.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -37,6 +37,7 @@
 #include "qnetd-algorithm.h"
 #include "qnetd-instance.h"
 #include "qnetd-log.h"
+#include "qnetd-log-debug.h"
 #include "qnetd-client-send.h"
 #include "msg.h"
 #include "nss-sock.h"
@@ -362,6 +363,8 @@ qnetd_client_msg_received_init(struct qnetd_instance *instance, struct qnetd_cli
 	}
 
 	if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) {
+		qnetd_log_debug_new_client_connected(client);
+
 		reply_error_code = qnetd_algorithm_client_init(client);
 	}
 
@@ -370,6 +373,8 @@ qnetd_client_msg_received_init(struct qnetd_instance *instance, struct qnetd_cli
 		 * Correct init received
 		 */
 		client->init_received = 1;
+	} else {
+		qnetd_log(LOG_ERR, "Algorithm returned error code. Sending error reply.");
 	}
 
 	send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
@@ -583,9 +588,15 @@ qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnet
 		return (0);
 	}
 
+	result_vote = TLV_VOTE_NO_CHANGE;
+
 	switch (msg->node_list_type) {
 	case TLV_NODE_LIST_TYPE_INITIAL_CONFIG:
 	case TLV_NODE_LIST_TYPE_CHANGED_CONFIG:
+		qnetd_log_debug_config_node_list_received(client, msg->seq_number,
+		    msg->config_version_set, msg->config_version, &msg->nodes,
+		    (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG));
+
 		reply_error_code = qnetd_algorithm_config_node_list_received(client,
 		    msg->seq_number, msg->config_version_set, msg->config_version,
 		    &msg->nodes,
@@ -605,8 +616,13 @@ qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnet
 			return (0);
 		}
 
+		/*
+		 * TODO REMOVE THIS memcpy.
+		 */
 		memcpy(&client->last_ring_id, &msg->ring_id, sizeof(struct tlv_ring_id));
-		qnetd_log(LOG_DEBUG, "CC: client %p (nodeid %d) ring id = %d/%ld", client, client->node_id, client->last_ring_id.node_id, client->last_ring_id.seq);
+		qnetd_log_debug_membership_node_list_received(client, msg->seq_number, &msg->ring_id,
+		    &msg->nodes);
+
 		reply_error_code = qnetd_algorithm_membership_node_list_received(client,
 		    msg->seq_number, &msg->ring_id, &msg->nodes, &result_vote);
 		break;
@@ -622,6 +638,10 @@ qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnet
 
 			return (0);
 		}
+
+		qnetd_log_debug_quorum_node_list_received(client, msg->seq_number,msg->quorate,
+		    &msg->nodes);
+
 		reply_error_code = qnetd_algorithm_quorum_node_list_received(client,
 		    msg->seq_number,msg->quorate, &msg->nodes, &result_vote);
 		break;
@@ -642,6 +662,15 @@ qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnet
 		}
 
 		return (0);
+	} else {
+		qnetd_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+	}
+
+	if (msg->node_list_type == TLV_NODE_LIST_TYPE_MEMBERSHIP &&
+	    result_vote == TLV_VOTE_NO_CHANGE) {
+		qnetd_log(LOG_ERR, "qnetd_client_msg_received_node_list fatal error. "
+		    "node_list_type is membership and algorithm result vote is no_change");
+		exit(1);
 	}
 
 	/*
@@ -666,6 +695,7 @@ qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnet
 
 			return (-1);
 		}
+		memcpy(&client->last_ring_id, &msg->ring_id, sizeof(struct tlv_ring_id));
 		break;
 	case TLV_NODE_LIST_TYPE_QUORUM:
 		node_list_free(&client->last_quorum_node_list);
@@ -691,7 +721,8 @@ qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnet
 		return (-1);
 	}
 
-	if (msg_create_node_list_reply(&send_buffer->buffer, msg->seq_number, result_vote) == -1) {
+	if (msg_create_node_list_reply(&send_buffer->buffer, msg->seq_number, msg->node_list_type,
+	    msg->ring_id_set, &msg->ring_id, result_vote) == -1) {
 		qnetd_log(LOG_ERR, "Can't alloc node list reply msg. "
 		    "Disconnecting client connection.");
 
@@ -750,6 +781,8 @@ qnetd_client_msg_received_ask_for_vote(struct qnetd_instance *instance, struct q
 		return (0);
 	}
 
+	qnetd_log_debug_ask_for_vote_received(client, msg->seq_number);
+
 	reply_error_code = qnetd_algorithm_ask_for_vote_received(client, msg->seq_number,
 	    &result_vote);
 
@@ -763,6 +796,8 @@ qnetd_client_msg_received_ask_for_vote(struct qnetd_instance *instance, struct q
 		}
 
 		return (0);
+	} else {
+		qnetd_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
 	}
 
 	send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
@@ -839,6 +874,8 @@ qnetd_client_msg_received_vote_info_reply(struct qnetd_instance *instance,
 		return (0);
 	}
 
+	qnetd_log_debug_vote_info_reply_received(client, msg->seq_number);
+
 	reply_error_code = qnetd_algorithm_vote_info_reply_received(client, msg->seq_number);
 
 	if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {

+ 1 - 1
qdevices/qnetd-client-msg-received.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-client-net.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-client-net.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 4 - 1
qdevices/qnetd-client-send.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -38,6 +38,7 @@
 
 #include "qnetd-client-send.h"
 #include "qnetd-log.h"
+#include "qnetd-log-debug.h"
 #include "msg.h"
 
 int
@@ -73,6 +74,8 @@ qnetd_client_send_vote_info(struct qnetd_client *client, uint32_t msg_seq_number
 {
 	struct send_buffer_list_entry *send_buffer;
 
+	qnetd_log_debug_send_vote_info(client, msg_seq_number, vote);
+
 	send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
 	if (send_buffer == NULL) {
 		qnetd_log(LOG_ERR, "Can't alloc vote info msg from list. "

+ 1 - 1
qdevices/qnetd-client-send.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-client.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-client.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-cluster-list.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-cluster-list.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 5 - 1
qdevices/qnetd-instance.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -38,6 +38,7 @@
 #include "qnetd-instance.h"
 #include "qnetd-client.h"
 #include "qnetd-algorithm.h"
+#include "qnetd-log-debug.h"
 
 int
 qnetd_instance_init(struct qnetd_instance *instance, size_t max_client_receive_size,
@@ -90,7 +91,10 @@ qnetd_instance_client_disconnect(struct qnetd_instance *instance, struct qnetd_c
     int server_going_down)
 {
 
+	qnetd_log_debug_client_disconnect(client, server_going_down);
+
 	qnetd_algorithm_client_disconnect(client, server_going_down);
+
 	PR_Close(client->socket);
 	if (client->cluster != NULL) {
 		qnetd_cluster_list_del_client(&instance->clusters, client->cluster, client);

+ 1 - 1
qdevices/qnetd-instance.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 162 - 0
qdevices/qnetd-log-debug.c

@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qnetd-log.h"
+#include "qnetd-log-debug.h"
+
+void
+qnetd_log_debug_dump_cluster(struct qnetd_cluster *cluster)
+{
+	struct qnetd_client *client;
+
+	qnetd_log(LOG_DEBUG, "  cluster dump:");
+	TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+		qnetd_log(LOG_DEBUG, "    client = %p, node_id = %"PRIx32,
+		    client, client->node_id);
+	}
+}
+
+void
+qnetd_log_debug_new_client_connected(struct qnetd_client *client)
+{
+
+	qnetd_log(LOG_DEBUG, "New client connected");
+	qnetd_log(LOG_DEBUG, "  cluster name = %s", client->cluster_name);
+	qnetd_log(LOG_DEBUG, "  tls started = %u", client->tls_started);
+	qnetd_log(LOG_DEBUG, "  tls peer certificate verified = %u",
+	    client->tls_peer_certificate_verified);
+	qnetd_log(LOG_DEBUG, "  node_id = %"PRIx32, client->node_id);
+	qnetd_log(LOG_DEBUG, "  pointer = %p", client);
+
+	qnetd_log_debug_dump_cluster(client->cluster);
+}
+
+void
+qnetd_log_debug_dump_node_list(struct qnetd_client *client, const struct node_list *nodes)
+{
+	struct node_list_entry *node_info;
+
+	qnetd_log(LOG_DEBUG, "  node list:");
+	TAILQ_FOREACH(node_info, nodes, entries) {
+		qnetd_log(LOG_DEBUG, "    node_id = %"PRIx32", "
+		    "data_center_id = %"PRIx32", "
+		    "node_state = %s", node_info->node_id, node_info->data_center_id,
+		    tlv_node_state_to_str(node_info->node_state));
+	}
+}
+
+void
+qnetd_log_debug_config_node_list_received(struct qnetd_client *client,
+    uint32_t msg_seq_num, int config_version_set, uint64_t config_version,
+    const struct node_list *nodes, int initial)
+{
+
+	qnetd_log(LOG_DEBUG, "Client %p (cluster %s, node_id %"PRIx32") "
+	    "sent %s node list.", client, client->cluster_name, client->node_id,
+	    (initial ? "initial" : "changed"));
+
+	qnetd_log(LOG_DEBUG, "  msg seq num %"PRIu32, msg_seq_num);
+
+	if (config_version_set) {
+		qnetd_log(LOG_DEBUG, "  config version %"PRIu64, config_version);
+	}
+
+	qnetd_log_debug_dump_node_list(client, nodes);
+}
+
+void
+qnetd_log_debug_membership_node_list_received(struct qnetd_client *client,
+    uint32_t msg_seq_num, const struct tlv_ring_id *ring_id,
+    const struct node_list *nodes)
+{
+	qnetd_log(LOG_DEBUG, "Client %p (cluster %s, node_id %"PRIx32") "
+	    "sent membership node list.", client, client->cluster_name, client->node_id);
+
+	qnetd_log(LOG_DEBUG, "  msg seq num %"PRIu32, msg_seq_num);
+
+	qnetd_log(LOG_DEBUG, "  ring id = (%"PRIx32".%"PRIx64")", ring_id->node_id, ring_id->seq);
+
+	qnetd_log_debug_dump_node_list(client, nodes);
+}
+
+void
+qnetd_log_debug_quorum_node_list_received(struct qnetd_client *client,
+    uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes)
+{
+
+	qnetd_log(LOG_DEBUG, "Client %p (cluster %s, node_id %"PRIx32") "
+	    "sent quorum node list.", client, client->cluster_name, client->node_id);
+
+	qnetd_log(LOG_DEBUG, "  msg seq num %"PRIu32, msg_seq_num);
+	qnetd_log(LOG_DEBUG, "  quorate = %u", quorate);
+
+	qnetd_log_debug_dump_node_list(client, nodes);
+}
+
+void
+qnetd_log_debug_client_disconnect(struct qnetd_client *client, int server_going_down)
+{
+
+	qnetd_log(LOG_DEBUG, "Client %p (cluster %s, node_id %"PRIx32
+		") disconnect%s", client, client->cluster_name, client->node_id,
+		(server_going_down ? " (server is going down)" : ""));
+}
+
+void
+qnetd_log_debug_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+
+	qnetd_log(LOG_DEBUG, "Client %p (cluster %s, node_id %"PRIx32") "
+	    "asked for a vote", client, client->cluster_name, client->node_id);
+	qnetd_log(LOG_DEBUG, "  msg seq num %"PRIu32, msg_seq_num);
+}
+
+void
+qnetd_log_debug_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+
+	qnetd_log(LOG_DEBUG, "Client %p (cluster %s, node_id %"PRIx32") "
+	    "replied back to vote info message", client, client->cluster_name, client->node_id);
+	qnetd_log(LOG_DEBUG, "  msg seq num %"PRIu32, msg_seq_num);
+}
+
+void
+qnetd_log_debug_send_vote_info(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote vote)
+{
+
+	qnetd_log(LOG_DEBUG, "Sending vote info to client %p (cluster %s, node_id %"PRIx32") ",
+	    client, client->cluster_name, client->node_id);
+	qnetd_log(LOG_DEBUG, "  msg seq num %"PRIu32, msg_seq_num);
+	qnetd_log(LOG_DEBUG, "  vote %s", tlv_vote_to_str(vote));
+}

+ 79 - 0
qdevices/qnetd-log-debug.h

@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _QNETD_LOG_DEBUG_H_
+#define _QNETD_LOG_DEBUG_H_
+
+#include "qnetd-client.h"
+#include "qnetd-cluster-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void		qnetd_log_debug_dump_cluster(struct qnetd_cluster *cluster);
+
+extern void		qnetd_log_debug_new_client_connected(struct qnetd_client *client);
+
+extern void		qnetd_log_debug_dump_node_list(struct qnetd_client *client,
+    const struct node_list *nodes);
+
+extern void		qnetd_log_debug_config_node_list_received(struct qnetd_client *client,
+    uint32_t msg_seq_num, int config_version_set, uint64_t config_version,
+    const struct node_list *nodes, int initial);
+
+extern void		qnetd_log_debug_membership_node_list_received(struct qnetd_client *client,
+    uint32_t msg_seq_num, const struct tlv_ring_id *ring_id,
+    const struct node_list *nodes);
+
+extern void		qnetd_log_debug_quorum_node_list_received(struct qnetd_client *client,
+    uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes);
+
+extern void		qnetd_log_debug_client_disconnect(struct qnetd_client *client,
+    int server_going_down);
+
+extern void		qnetd_log_debug_ask_for_vote_received(struct qnetd_client *client,
+    uint32_t msg_seq_num);
+
+extern void		qnetd_log_debug_vote_info_reply_received(struct qnetd_client *client,
+    uint32_t msg_seq_num);
+
+extern void		qnetd_log_debug_send_vote_info(struct qnetd_client *client,
+    uint32_t msg_seq_num, enum tlv_vote vote);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_LOG_DEBUG_H_ */

+ 1 - 1
qdevices/qnetd-log.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-log.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-poll-array.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/qnetd-poll-array.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 5 - 1
qdevices/send-buffer-list.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -152,6 +152,10 @@ send_buffer_list_free(struct send_buffer_list *sblist)
 
 		entry = entry_next;
 	}
+
+	sblist->allocated_list_entries = 0;
+	TAILQ_INIT(&sblist->list);
+	TAILQ_INIT(&sblist->free_list);
 }
 
 void

+ 1 - 1
qdevices/send-buffer-list.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/test-qnetd-cluster-list.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/timer-list.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/timer-list.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 39 - 1
qdevices/tlv.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -961,3 +961,41 @@ tlv_get_supported_options(enum tlv_opt_type **supported_options, size_t *no_supp
 	*supported_options = tlv_static_supported_options;
 	*no_supported_options = TLV_STATIC_SUPPORTED_OPTIONS_SIZE;
 }
+
+int
+tlv_ring_id_eq(const struct tlv_ring_id *rid1, const struct tlv_ring_id *rid2)
+{
+
+	return (rid1->node_id == rid2->node_id && rid1->seq == rid2->seq);
+}
+
+const char *
+tlv_vote_to_str(enum tlv_vote vote)
+{
+
+	switch (vote) {
+	case TLV_VOTE_ACK: return ("ACK"); break;
+	case TLV_VOTE_NACK: return ("NACK"); break;
+	case TLV_VOTE_ASK_LATER: return ("Ask later"); break;
+	case TLV_VOTE_WAIT_FOR_REPLY: return ("Wait for reply"); break;
+	case TLV_VOTE_NO_CHANGE: return ("No change"); break;
+	default: return ("Unknown vote value"); break;
+	}
+
+	return ("Unknown vote value");
+}
+
+const char *
+tlv_node_state_to_str(enum tlv_node_state state)
+{
+
+	switch (state) {
+	case TLV_NODE_STATE_NOT_SET: return ("not set"); break;
+	case TLV_NODE_STATE_MEMBER: return ("member"); break;
+	case TLV_NODE_STATE_DEAD: return ("dead"); break;
+	case TLV_NODE_STATE_LEAVING: return ("leaving"); break;
+	default: return ("Unhandled node state"); break;
+	}
+
+	return ("Unhandled node state");
+}

+ 8 - 1
qdevices/tlv.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *
@@ -313,6 +313,13 @@ extern int			 tlv_iter_decode_quorate(struct tlv_iterator *tlv_iter,
 extern void			 tlv_get_supported_options(enum tlv_opt_type **supported_options,
     size_t *no_supported_options);
 
+extern int			 tlv_ring_id_eq(const struct tlv_ring_id *rid1,
+    const struct tlv_ring_id *rid2);
+
+extern const char *		 tlv_vote_to_str(enum tlv_vote vote);
+
+extern const char *		 tlv_node_state_to_str(enum tlv_node_state state);
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 1
qdevices/utils.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *

+ 1 - 1
qdevices/utils.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc.
+ * Copyright (c) 2015-2016 Red Hat, Inc.
  *
  * All rights reserved.
  *