4
0
Эх сурвалжийг харах

qnetd: Add skel of ffsplit algo and cluster list

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
Jan Friesse 10 жил өмнө
parent
commit
1365455250

+ 1 - 0
qdevices/.gitignore

@@ -1,3 +1,4 @@
 corosync-qdevice-net
 corosync-qnetd-certutil
 corosync-qnetd
+*.test

+ 11 - 1
qdevices/Makefile.am

@@ -41,7 +41,8 @@ sbin_SCRIPTS             = corosync-qnetd-certutil
 corosync_qnetd_SOURCES	= corosync-qnetd.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 qnetd-algo-test.c qnetd-algorithm.c
+			    node-list.c qnetd-algo-test.c qnetd-algorithm.c \
+			    qnetd-algo-ffsplit.c qnetd-cluster-list.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 \
@@ -60,4 +61,13 @@ corosync-qnetd-certutil: corosync-qnetd-certutil.sh
 	    -e 's#@''BASHPATH@#${BASHPATH}#g' \
 	    $< > $@
 
+TESTS				= qnetd-cluster-list.test
+check_PROGRAMS			= qnetd-cluster-list.test
+
+qnetd_cluster_list_test_SOURCES	= qnetd-cluster-list.c test-qnetd-cluster-list.c \
+				    qnetd-client-list.c qnetd-client.c dynar.c node-list.c \
+				    send-buffer-list.c
+qnetd_cluster_list_test_CFLAGS  = $(nss_CFLAGS)
+qnetd_cluster_list_test_LDADD	= $(nss_LIBS)
+
 endif

+ 15 - 1
qdevices/corosync-qdevice-net.c

@@ -84,6 +84,8 @@
 
 #define QDEVICE_NET_MAX_SEND_BUFFERS		10
 
+#define QDEVICE_NET_DEFAULT_ALGORITHM		TLV_DECISION_ALGORITHM_TYPE_TEST
+
 #define qdevice_net_log			qnetd_log
 #define qdevice_net_log_nss		qnetd_log_nss
 #define qdevice_net_log_init		qnetd_log_init
@@ -1399,7 +1401,19 @@ qdevice_net_instance_init_from_cmap(struct qdevice_net_instance *instance,
 	/*
 	 * Choose decision algorithm
 	 */
-	decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_TEST;
+	if (cmap_get_string(cmap_handle, "quorum.device.net.algorithm", &str) != CS_OK) {
+		decision_algorithm = QDEVICE_NET_DEFAULT_ALGORITHM;
+	} else {
+		if (strcmp(str, "test") == 0) {
+			decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_TEST;
+		} else if (strcmp(str, "ffsplit") == 0) {
+			decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_FFSPLIT;
+		} else {
+			errx(1, "Unknown decision algorithm %s", str);
+		}
+
+		free(str);
+	}
 
 	/*
 	 * Really initialize instance

+ 23 - 2
qdevices/corosync-qnetd.c

@@ -61,6 +61,7 @@
 #include "dynar.h"
 #include "timer-list.h"
 #include "qnetd-algorithm.h"
+#include "qnetd-cluster-list.h"
 
 #define QNETD_LISTEN_BACKLOG		10
 #define QNETD_MAX_CLIENT_SEND_BUFFERS	10
@@ -86,6 +87,7 @@ struct qnetd_instance {
 	size_t max_client_send_buffers;
 	size_t max_client_send_size;
 	struct qnetd_client_list clients;
+	struct qnetd_cluster_list clusters;
 	struct qnetd_poll_array poll_array;
 	enum tlv_tls_supported tls_supported;
 	int tls_client_cert_required;
@@ -101,11 +103,12 @@ PRFileDesc *global_server_socket;
 /*
  * Decision algorithms supported in this server
  */
-#define QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE		1
+#define QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE		2
 
 enum tlv_decision_algorithm_type
     qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE] = {
 	TLV_DECISION_ALGORITHM_TYPE_TEST,
+	TLV_DECISION_ALGORITHM_TYPE_FFSPLIT,
 };
 
 static void
@@ -177,6 +180,7 @@ qnetd_client_msg_received_preinit(struct qnetd_instance *instance, struct qnetd_
     const struct msg_decoded *msg)
 {
 	struct send_buffer_list_entry *send_buffer;
+	struct qnetd_cluster *cluster;
 
 	if (msg->cluster_name == NULL) {
 		qnetd_log(LOG_ERR, "Received preinit message without cluster name. "
@@ -201,11 +205,25 @@ qnetd_client_msg_received_preinit(struct qnetd_instance *instance, struct qnetd_
 
 		return (0);
 	}
+	memset(client->cluster_name, 0, msg->cluster_name_len + 1);
+	memcpy(client->cluster_name, msg->cluster_name, msg->cluster_name_len);
 
-	memcpy(client->cluster_name, msg->cluster_name, msg->cluster_name_len + 1);
 	client->cluster_name_len = msg->cluster_name_len;
 	client->preinit_received = 1;
 
+	cluster = qnetd_cluster_list_add_client(&instance->clusters, client);
+	if (cluster == NULL) {
+		qnetd_log(LOG_ERR, "Can't add client to cluster list. Sending error reply.");
+
+		if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+		    TLV_REPLY_ERROR_CODE_INTERNAL_ERROR) != 0) {
+			return (-1);
+		}
+
+		return (0);
+	}
+	client->cluster = cluster;
+
 	send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
 	if (send_buffer == NULL) {
 		qnetd_log(LOG_ERR, "Can't alloc preinit reply msg from list. "
@@ -1085,6 +1103,7 @@ qnetd_client_disconnect(struct qnetd_instance *instance, struct qnetd_client *cl
 
 	qnetd_algorithm_client_disconnect(client, server_going_down);
 	PR_Close(client->socket);
+	qnetd_cluster_list_del_client(&instance->clusters, client->cluster, client);
 	qnetd_client_list_del(&instance->clients, client);
 }
 
@@ -1218,6 +1237,7 @@ qnetd_instance_init(struct qnetd_instance *instance, size_t max_client_receive_s
 
 	qnetd_poll_array_init(&instance->poll_array);
 	qnetd_client_list_init(&instance->clients);
+	qnetd_cluster_list_init(&instance->clusters);
 
 	instance->max_client_receive_size = max_client_receive_size;
 	instance->max_client_send_buffers = max_client_send_buffers;
@@ -1245,6 +1265,7 @@ qnetd_instance_destroy(struct qnetd_instance *instance)
 	}
 
 	qnetd_poll_array_destroy(&instance->poll_array);
+	qnetd_cluster_list_free(&instance->clusters);
 	qnetd_client_list_free(&instance->clients);
 
 	return (0);

+ 90 - 0
qdevices/qnetd-algo-ffsplit.c

@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015 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 "qnetd-algo-ffsplit.h"
+#include "qnetd-log.h"
+
+enum tlv_reply_error_code
+qnetd_algo_ffsplit_client_init(struct qnetd_client *client)
+{
+
+	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_ffsplit_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)
+{
+
+	*result_vote = TLV_VOTE_ASK_LATER;
+
+	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * Called after client sent membership node list.
+ * All client fields are already set. Nodes is actual node list.
+ * msg_seq_num is 32-bit number set by client. If client sent config file version,
+ * config_version_set is set to 1 and config_version contains valid config file version.
+ * ring_id and quorate are copied from client votequorum callback.
+ *
+ * Function has to return result_vote. This can be one of ack/nack, ask_later (client
+ * should ask later for a vote) or wait_for_reply (client should wait for reply).
+ *
+ * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_*
+ * on failure (error is send back to client)
+ */
+
+enum tlv_reply_error_code
+qnetd_algo_ffsplit_membership_node_list_received(struct qnetd_client *client,
+    uint32_t msg_seq_num, int config_version_set, uint64_t config_version,
+    const struct tlv_ring_id *ring_id, enum tlv_quorate quorate,
+    const struct node_list *nodes, enum tlv_vote *result_vote)
+{
+
+	*result_vote = TLV_VOTE_ASK_LATER;
+
+	return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+void
+qnetd_algo_ffsplit_client_disconnect(struct qnetd_client *client, int server_going_down)
+{
+
+}

+ 63 - 0
qdevices/qnetd-algo-ffsplit.h

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015 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_ALGO_FFSPLIT_H_
+#define _QNETD_ALGO_FFSPLIT_H_
+
+#include "qnetd-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_reply_error_code	qnetd_algo_ffsplit_client_init(struct qnetd_client *client);
+
+extern enum tlv_reply_error_code	qnetd_algo_ffsplit_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);
+
+extern enum tlv_reply_error_code	qnetd_algo_ffsplit_membership_node_list_received(
+    struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set,
+    uint64_t config_version, const struct tlv_ring_id *ring_id, enum tlv_quorate quorate,
+    const struct node_list *nodes, enum tlv_vote *result_vote);
+
+extern void				qnetd_algo_ffsplit_client_disconnect(
+    struct qnetd_client *client, int server_going_down);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ALGO_FFSPLIT_H_ */

+ 15 - 0
qdevices/qnetd-algorithm.c

@@ -38,6 +38,7 @@
 
 #include "qnetd-algorithm.h"
 #include "qnetd-algo-test.h"
+#include "qnetd-algo-ffsplit.h"
 
 enum tlv_reply_error_code
 qnetd_algorithm_client_init(struct qnetd_client *client)
@@ -47,6 +48,9 @@ qnetd_algorithm_client_init(struct qnetd_client *client)
 	case TLV_DECISION_ALGORITHM_TYPE_TEST:
 		return (qnetd_algo_test_client_init(client));
 		break;
+	case TLV_DECISION_ALGORITHM_TYPE_FFSPLIT:
+		return (qnetd_algo_ffsplit_client_init(client));
+		break;
 	default:
 		errx(1, "qnetd_algorithm_client_init unhandled decision algorithm");
 		break;
@@ -66,6 +70,10 @@ qnetd_algorithm_config_node_list_received(struct qnetd_client *client,
 		return (qnetd_algo_test_config_node_list_received(client, msg_seq_num,
 		    config_version_set, config_version, nodes, initial, result_vote));
 		break;
+	case TLV_DECISION_ALGORITHM_TYPE_FFSPLIT:
+		return (qnetd_algo_ffsplit_config_node_list_received(client, msg_seq_num,
+		    config_version_set, config_version, nodes, initial, result_vote));
+		break;
 	default:
 		errx(1, "qnetd_algorithm_config_node_list_received unhandled "
 		    "decision algorithm");
@@ -88,6 +96,10 @@ qnetd_algorithm_membership_node_list_received(struct qnetd_client *client,
 		return (qnetd_algo_test_membership_node_list_received(client, msg_seq_num,
 		    config_version_set, config_version, ring_id, quorate, nodes, result_vote));
 		break;
+	case TLV_DECISION_ALGORITHM_TYPE_FFSPLIT:
+		return (qnetd_algo_ffsplit_membership_node_list_received(client, msg_seq_num,
+		    config_version_set, config_version, ring_id, quorate, nodes, result_vote));
+		break;
 	default:
 		errx(1, "qnetd_algorithm_membership_node_list_received unhandled "
 		    "decision algorithm");
@@ -105,6 +117,9 @@ qnetd_algorithm_client_disconnect(struct qnetd_client *client, int server_going_
 	case TLV_DECISION_ALGORITHM_TYPE_TEST:
 		qnetd_algo_test_client_disconnect(client, server_going_down);
 		break;
+	case TLV_DECISION_ALGORITHM_TYPE_FFSPLIT:
+		qnetd_algo_ffsplit_client_disconnect(client, server_going_down);
+		break;
 	default:
 		errx(1, "qnetd_algorithm_client_disconnect unhandled decision algorithm");
 		break;

+ 2 - 0
qdevices/qnetd-client.h

@@ -71,7 +71,9 @@ struct qnetd_client {
 	void *algorithm_data;
 	struct node_list configuration_node_list;
 	struct node_list last_membership_node_list;
+	struct qnetd_cluster *cluster;
 	TAILQ_ENTRY(qnetd_client) entries;
+	TAILQ_ENTRY(qnetd_client) cluster_entries;
 };
 
 extern void		qnetd_client_init(struct qnetd_client *client, PRFileDesc *sock,

+ 131 - 0
qdevices/qnetd-cluster-list.c

@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qnetd-cluster-list.h"
+
+void
+qnetd_cluster_list_init(struct qnetd_cluster_list *list)
+{
+
+	TAILQ_INIT(list);
+}
+
+struct qnetd_cluster *
+qnetd_cluster_list_find_by_name(struct qnetd_cluster_list *list,
+    const char *cluster_name, size_t cluster_name_len)
+{
+	struct qnetd_cluster *cluster;
+
+	TAILQ_FOREACH(cluster, list, entries) {
+		if (cluster->cluster_name_len == cluster_name_len &&
+		    memcmp(cluster->cluster_name, cluster_name, cluster_name_len) == 0) {
+			return (cluster);
+		}
+	}
+
+	return (NULL);
+}
+
+struct qnetd_cluster *
+qnetd_cluster_list_add_client(struct qnetd_cluster_list *list, struct qnetd_client *client)
+{
+	struct qnetd_cluster *cluster;
+
+	cluster = qnetd_cluster_list_find_by_name(list, client->cluster_name,
+	    client->cluster_name_len);
+	if (cluster == NULL) {
+		cluster = (struct qnetd_cluster *)malloc(sizeof(*cluster));
+		if (cluster == NULL) {
+			return (NULL);
+		}
+
+		memset(cluster, 0, sizeof(*cluster));
+
+		cluster->cluster_name = malloc(client->cluster_name_len + 1);
+		if (cluster->cluster_name == NULL) {
+			free(cluster);
+
+			return (NULL);
+		}
+		memset(cluster->cluster_name, 0, client->cluster_name_len + 1);
+		memcpy(cluster->cluster_name, client->cluster_name, client->cluster_name_len);
+
+		cluster->cluster_name_len = client->cluster_name_len;
+		TAILQ_INIT(&cluster->client_list);
+
+		TAILQ_INSERT_TAIL(list, cluster, entries);
+	}
+
+	TAILQ_INSERT_TAIL(&cluster->client_list, client, cluster_entries);
+
+	return (cluster);
+}
+
+void
+qnetd_cluster_list_del_client(struct qnetd_cluster_list *list, struct qnetd_cluster *cluster,
+    struct qnetd_client *client)
+{
+
+	TAILQ_REMOVE(&cluster->client_list, client, cluster_entries);
+
+	if (TAILQ_EMPTY(&cluster->client_list)) {
+		TAILQ_REMOVE(list, cluster, entries);
+
+		free(cluster->cluster_name);
+		free(cluster);
+	}
+}
+
+void
+qnetd_cluster_list_free(struct qnetd_cluster_list *list)
+{
+	struct qnetd_cluster *cluster;
+	struct qnetd_cluster *cluster_next;
+
+	cluster = TAILQ_FIRST(list);
+	while (cluster != NULL) {
+		cluster_next = TAILQ_NEXT(cluster, entries);
+
+		free(cluster->cluster_name);
+		free(cluster);
+
+		cluster = cluster_next;
+        }
+
+	TAILQ_INIT(list);
+}

+ 76 - 0
qdevices/qnetd-cluster-list.h

@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 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_CLUSTER_LIST_H_
+#define _QNETD_CLUSTER_LIST_H_
+
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+#include "tlv.h"
+#include "qnetd-client-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_cluster {
+	char *cluster_name;
+	size_t cluster_name_len;
+	struct qnetd_client_list client_list;
+	TAILQ_ENTRY(qnetd_cluster) entries;
+};
+
+TAILQ_HEAD(qnetd_cluster_list, qnetd_cluster);
+
+extern void				 qnetd_cluster_list_init(struct qnetd_cluster_list *list);
+
+extern struct qnetd_cluster		*qnetd_cluster_list_find_by_name(
+    struct qnetd_cluster_list *list, const char *cluster_name, size_t cluster_name_len);
+
+extern struct qnetd_cluster		*qnetd_cluster_list_add_client(
+    struct qnetd_cluster_list *list, struct qnetd_client *client);
+
+extern void				 qnetd_cluster_list_del_client(
+    struct qnetd_cluster_list *list, struct qnetd_cluster *cluster, struct qnetd_client *client);
+
+extern void				 qnetd_cluster_list_free(struct qnetd_cluster_list *list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLUSTER_LIST_H_ */

+ 203 - 0
qdevices/test-qnetd-cluster-list.c

@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015 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 <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "qnetd-cluster-list.h"
+#include "qnetd-client.h"
+#include "qnetd-client-list.h"
+
+static struct qnetd_client_list clients;
+static struct qnetd_cluster_list clusters;
+
+static void
+add_client(const char *cluster_name, size_t cluster_name_len,
+    struct qnetd_client **client, struct qnetd_cluster **cluster)
+{
+	PRNetAddr addr;
+	struct qnetd_client *tmp_client;
+	struct qnetd_cluster *tmp_cluster;
+
+	memset(&addr, 0, sizeof(addr));
+
+	tmp_client = qnetd_client_list_add(&clients, NULL, &addr, 1000, 2, 1000);
+	assert(tmp_client != NULL);
+	tmp_client->cluster_name = malloc(cluster_name_len + 1);
+	assert(tmp_client->cluster_name != NULL);
+	memcpy(tmp_client->cluster_name, cluster_name, cluster_name_len);
+	tmp_client->cluster_name_len = cluster_name_len;
+
+	tmp_cluster = qnetd_cluster_list_add_client(&clusters, tmp_client);
+	assert(cluster != NULL);
+	tmp_client->cluster = tmp_cluster;
+
+	*client = tmp_client;
+	*cluster = tmp_cluster;
+}
+
+static int
+no_clients_in_cluster(struct qnetd_cluster *cluster)
+{
+	int i;
+	struct qnetd_client *client;
+
+	i = 0;
+
+	TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+		i++;
+	}
+
+	return (i);
+}
+
+static int
+no_clusters(void)
+{
+	int i;
+	struct qnetd_cluster *cluster;
+
+	i = 0;
+
+	TAILQ_FOREACH(cluster, &clusters, entries) {
+		i++;
+	}
+
+	return (i);
+}
+
+static int
+is_client_in_cluster(struct qnetd_cluster *cluster, const struct qnetd_client *client)
+{
+	struct qnetd_client *tmp_client;
+
+	TAILQ_FOREACH(tmp_client, &cluster->client_list, cluster_entries) {
+		if (tmp_client == client) {
+			return (1);
+		}
+	}
+
+	return (0);
+}
+
+static void
+del_client(struct qnetd_client *client)
+{
+
+	qnetd_cluster_list_del_client(&clusters, client->cluster, client);
+	qnetd_client_list_del(&clients, client);
+}
+
+int
+main(void)
+{
+	struct qnetd_client *client[4];
+	struct qnetd_cluster *cluster[4];
+	const char *cl_name;
+
+	qnetd_client_list_init(&clients);
+	qnetd_cluster_list_init(&clusters);
+
+	assert(no_clusters() == 0);
+
+	cl_name = "test_cluster";
+	add_client(cl_name, strlen(cl_name), &client[0], &cluster[0]);
+	assert(no_clusters() == 1);
+	add_client(cl_name, strlen(cl_name), &client[1], &cluster[1]);
+	assert(no_clusters() == 1);
+
+	cl_name = "cluster2";
+	add_client(cl_name, strlen(cl_name), &client[2], &cluster[2]);
+	assert(no_clusters() == 2);
+	add_client(cl_name, strlen(cl_name), &client[3], &cluster[3]);
+	assert(no_clusters() == 2);
+
+	assert(cluster[0] == cluster[1]);
+	assert(cluster[2] == cluster[3]);
+	assert(cluster[0] != cluster[2]);
+
+	assert(no_clients_in_cluster(cluster[0]) == 2);
+	assert(no_clients_in_cluster(cluster[2]) == 2);
+
+	assert(is_client_in_cluster(cluster[0], client[0]));
+	assert(is_client_in_cluster(client[0]->cluster, client[0]));
+	assert(is_client_in_cluster(cluster[0], client[1]));
+	assert(!is_client_in_cluster(cluster[0], client[2]));
+	assert(!is_client_in_cluster(cluster[0], client[3]));
+
+	assert(!is_client_in_cluster(cluster[2], client[0]));
+	assert(!is_client_in_cluster(cluster[2], client[1]));
+	assert(is_client_in_cluster(cluster[2], client[2]));
+	assert(is_client_in_cluster(cluster[2], client[3]));
+	assert(is_client_in_cluster(client[2]->cluster, client[2]));
+
+	del_client(client[0]);
+	assert(no_clusters() == 2);
+	assert(no_clients_in_cluster(cluster[0]) == 1);
+	assert(no_clients_in_cluster(cluster[2]) == 2);
+
+	assert(!is_client_in_cluster(cluster[0], client[0]));
+	assert(is_client_in_cluster(cluster[0], client[1]));
+	assert(!is_client_in_cluster(cluster[0], client[2]));
+	assert(!is_client_in_cluster(cluster[0], client[3]));
+
+	add_client(cl_name, strlen(cl_name), &client[0], &cluster[0]);
+	assert(no_clients_in_cluster(cluster[1]) == 1);
+	assert(no_clients_in_cluster(cluster[2]) == 3);
+
+	assert(!is_client_in_cluster(cluster[1], client[0]));
+	assert(is_client_in_cluster(cluster[1], client[1]));
+	assert(!is_client_in_cluster(cluster[1], client[2]));
+	assert(!is_client_in_cluster(cluster[1], client[3]));
+
+	assert(is_client_in_cluster(cluster[2], client[0]));
+	assert(!is_client_in_cluster(cluster[2], client[1]));
+	assert(is_client_in_cluster(cluster[2], client[2]));
+	assert(is_client_in_cluster(cluster[2], client[3]));
+
+	del_client(client[1]);
+	assert(no_clusters() == 1);
+
+	del_client(client[2]);
+	assert(no_clusters() == 1);
+
+	del_client(client[3]);
+	assert(no_clusters() == 1);
+
+	del_client(client[0]);
+	assert(no_clusters() == 0);
+
+	return (0);
+}

+ 1 - 0
qdevices/tlv.h

@@ -93,6 +93,7 @@ enum tlv_reply_error_code {
 
 enum tlv_decision_algorithm_type {
 	TLV_DECISION_ALGORITHM_TYPE_TEST = 0,
+	TLV_DECISION_ALGORITHM_TYPE_FFSPLIT = 1,
 };
 
 struct tlv_ring_id {