2 * Copyright (c) 2015-2018 Red Hat, Inc.
6 * Author: Jan Friesse (jfriesse@redhat.com)
8 * This software licensed under BSD license, the text of which follows:
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Red Hat, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
37 #include "qdevice-model.h"
38 #include "qdevice-model-net.h"
39 #include "qdevice-log.h"
40 #include "qdevice-net-cast-vote-timer.h"
41 #include "qdevice-net-instance.h"
42 #include "qdevice-net-ipc-cmd.h"
43 #include "qdevice-net-algorithm.h"
44 #include "qdevice-net-heuristics.h"
45 #include "qdevice-net-poll.h"
46 #include "qdevice-net-send.h"
47 #include "qdevice-net-votequorum.h"
48 #include "qnet-config.h"
52 qdevice_model_net_init(struct qdevice_instance
*instance
)
55 struct qdevice_net_instance
*net_instance
;
57 qdevice_log(LOG_DEBUG
, "Initializing qdevice_net_instance");
58 if (qdevice_net_instance_init_from_cmap(instance
) != 0) {
62 net_instance
= instance
->model_data
;
64 qdevice_log(LOG_DEBUG
, "Registering algorithms");
65 if (qdevice_net_algorithm_register_all() != 0) {
69 qdevice_log(LOG_DEBUG
, "Initializing NSS");
70 if (nss_sock_init_nss((net_instance
->tls_supported
!= TLV_TLS_UNSUPPORTED
?
71 instance
->advanced_settings
->net_nss_db_dir
: NULL
)) != 0) {
72 qdevice_log_nss(LOG_ERR
, "Can't init nss");
76 if (qdevice_net_cast_vote_timer_update(net_instance
, TLV_VOTE_ASK_LATER
) != 0) {
77 qdevice_log(LOG_ERR
, "Can't update cast vote timer");
81 if (qdevice_net_algorithm_init(net_instance
) != 0) {
82 qdevice_log(LOG_ERR
, "Algorithm init failed");
86 if (qdevice_net_heuristics_init(net_instance
) != 0) {
87 qdevice_log(LOG_ERR
, "Can't initialize net heuristics");
95 qdevice_model_net_destroy(struct qdevice_instance
*instance
)
97 struct qdevice_net_instance
*net_instance
;
99 net_instance
= instance
->model_data
;
101 qdevice_log(LOG_DEBUG
, "Destroying algorithm");
102 qdevice_net_algorithm_destroy(net_instance
);
104 qdevice_log(LOG_DEBUG
, "Destroying qdevice_net_instance");
105 qdevice_net_instance_destroy(net_instance
);
107 qdevice_log(LOG_DEBUG
, "Shutting down NSS");
108 SSL_ClearSessionCache();
110 if (NSS_Shutdown() != SECSuccess
) {
111 qdevice_log_nss(LOG_WARNING
, "Can't shutdown NSS");
114 if (PR_Cleanup() != PR_SUCCESS
) {
115 qdevice_log_nss(LOG_WARNING
, "Can't shutdown NSPR");
124 qdevice_model_net_timer_connect_timeout(void *data1
, void *data2
)
126 struct qdevice_net_instance
*instance
;
128 instance
= (struct qdevice_net_instance
*)data1
;
130 qdevice_log(LOG_ERR
, "Connect timeout");
132 instance
->schedule_disconnect
= 1;
134 instance
->connect_timer
= NULL
;
135 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_CONNECT_TO_THE_SERVER
;
141 qdevice_model_net_get_af(const struct qdevice_net_instance
*instance
)
146 if (instance
->force_ip_version
== 4) {
150 if (instance
->force_ip_version
== 6) {
158 qdevice_model_net_run(struct qdevice_instance
*instance
)
160 struct qdevice_net_instance
*net_instance
;
164 int delay_before_reconnect
;
167 net_instance
= instance
->model_data
;
169 qdevice_log(LOG_DEBUG
, "Executing qdevice-net");
174 while (try_connect
) {
175 net_instance
->state
= QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT
;
176 net_instance
->socket
= NULL
;
178 net_instance
->connect_timer
= timer_list_add(&net_instance
->main_timer_list
,
179 net_instance
->connect_timeout
, qdevice_model_net_timer_connect_timeout
,
180 (void *)net_instance
, NULL
);
182 if (net_instance
->connect_timer
== NULL
) {
183 qdevice_log(LOG_CRIT
, "Can't schedule connect timer");
189 qdevice_log(LOG_DEBUG
, "Trying connect to qnetd server %s:%u (timeout = %ums)",
190 net_instance
->host_addr
, net_instance
->host_port
, net_instance
->connect_timeout
);
192 res
= nss_sock_non_blocking_client_init(net_instance
->host_addr
,
193 net_instance
->host_port
, qdevice_model_net_get_af(net_instance
),
194 &net_instance
->non_blocking_client
);
196 qdevice_log_nss(LOG_ERR
, "Can't initialize non blocking client connection");
199 res
= nss_sock_non_blocking_client_try_next(&net_instance
->non_blocking_client
);
201 qdevice_log_nss(LOG_ERR
, "Can't connect to qnetd host");
202 nss_sock_non_blocking_client_destroy(&net_instance
->non_blocking_client
);
205 while (qdevice_net_poll(net_instance
) == 0) {
208 if (net_instance
->connect_timer
!= NULL
) {
209 timer_list_delete(&net_instance
->main_timer_list
, net_instance
->connect_timer
);
210 net_instance
->connect_timer
= NULL
;
213 if (net_instance
->echo_request_timer
!= NULL
) {
214 timer_list_delete(&net_instance
->main_timer_list
, net_instance
->echo_request_timer
);
215 net_instance
->echo_request_timer
= NULL
;
218 try_connect
= qdevice_net_disconnect_reason_try_reconnect(net_instance
->disconnect_reason
);
221 * Unpause cast vote timer, because if it is paused we cannot remove tracking
223 qdevice_net_cast_vote_timer_set_paused(net_instance
, 0);
225 vote
= TLV_VOTE_NO_CHANGE
;
227 if (qdevice_net_algorithm_disconnected(net_instance
,
228 net_instance
->disconnect_reason
, &try_connect
, &vote
) != 0) {
229 qdevice_log(LOG_ERR
, "Algorithm returned error, force exit");
232 qdevice_log(LOG_DEBUG
, "Algorithm result vote is %s",
233 tlv_vote_to_str(vote
));
236 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
237 qdevice_log(LOG_ERR
, "qdevice_model_net_run fatal error. "
238 " Can't update cast vote timer vote");
241 if (qdevice_net_disconnect_reason_force_disconnect(net_instance
->disconnect_reason
)) {
246 * Return 0 only when local socket was closed -> regular exit
248 if (net_instance
->disconnect_reason
== QDEVICE_NET_DISCONNECT_REASON_LOCAL_SOCKET_CLOSED
) {
252 if (net_instance
->socket
!= NULL
) {
253 if (PR_Close(net_instance
->socket
) != PR_SUCCESS
) {
254 qdevice_log_nss(LOG_WARNING
, "Unable to close connection");
256 net_instance
->socket
= NULL
;
259 if (!net_instance
->non_blocking_client
.destroyed
) {
260 nss_sock_non_blocking_client_destroy(&net_instance
->non_blocking_client
);
263 if (net_instance
->non_blocking_client
.socket
!= NULL
) {
264 if (PR_Close(net_instance
->non_blocking_client
.socket
) != PR_SUCCESS
) {
265 qdevice_log_nss(LOG_WARNING
, "Unable to close non-blocking client connection");
267 net_instance
->non_blocking_client
.socket
= NULL
;
271 net_instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT
) {
273 * Give qnetd server a little time before reconnect
275 delay_before_reconnect
= random() %
276 (int)(net_instance
->cast_vote_timer_interval
* 0.9);
278 qdevice_log(LOG_DEBUG
, "Sleeping for %u ms before reconnect",
279 delay_before_reconnect
);
280 (void)poll(NULL
, 0, delay_before_reconnect
);
284 qdevice_net_instance_clean(net_instance
);
291 * Called when cmap reload (or nodelist) was requested.
294 * config_version is valid only if config_version_set != 0
296 * Should return 0 if processing should continue or -1 to call exit
299 qdevice_model_net_config_node_list_changed(struct qdevice_instance
*instance
,
300 const struct node_list
*nlist
, int config_version_set
, uint64_t config_version
)
302 struct qdevice_net_instance
*net_instance
;
306 net_instance
= instance
->model_data
;
308 if (net_instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
) {
310 * Nodelist changed, but connection to qnetd not initiated yet.
314 if (net_instance
->cast_vote_timer_vote
== TLV_VOTE_ACK
) {
315 vote
= TLV_VOTE_NACK
;
317 vote
= TLV_VOTE_NO_CHANGE
;
321 vote
= TLV_VOTE_NO_CHANGE
;
324 if (qdevice_net_algorithm_config_node_list_changed(net_instance
, nlist
, config_version_set
,
325 config_version
, &send_node_list
, &vote
) != 0) {
326 qdevice_log(LOG_ERR
, "Algorithm returned error, Disconnecting");
328 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_ALGO_CONFIG_NODE_LIST_CHANGED_ERR
;
329 net_instance
->schedule_disconnect
= 1;
333 qdevice_log(LOG_DEBUG
, "Algorithm decided to %s node list and result vote is %s",
334 (send_node_list
? "send" : "not send"), tlv_vote_to_str(vote
));
337 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
338 qdevice_log(LOG_CRIT
, "qdevice_model_net_config_node_list_changed fatal error. "
339 " Can't update cast vote timer vote");
340 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER
;
341 net_instance
->schedule_disconnect
= 1;
346 if (send_node_list
) {
347 if (qdevice_net_send_config_node_list(net_instance
, nlist
, config_version_set
,
348 config_version
, 0) != 0) {
349 net_instance
->schedule_disconnect
= 1;
350 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
360 * Called when cmap reload (or nodelist) was requested, but it was not possible to
363 * Should return 0 if processing should continue or -1 to call exit
366 qdevice_model_net_get_config_node_list_failed(struct qdevice_instance
*instance
)
368 struct qdevice_net_instance
*net_instance
;
370 net_instance
= instance
->model_data
;
372 net_instance
->schedule_disconnect
= 1;
373 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
379 qdevice_model_net_votequorum_quorum_notify(struct qdevice_instance
*instance
,
380 uint32_t quorate
, uint32_t node_list_entries
, votequorum_node_t node_list
[])
382 struct qdevice_net_instance
*net_instance
;
386 net_instance
= instance
->model_data
;
388 if (net_instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
) {
390 * Nodelist changed, but connection to qnetd not initiated yet.
394 if (net_instance
->cast_vote_timer_vote
== TLV_VOTE_ACK
) {
395 vote
= TLV_VOTE_NACK
;
397 vote
= TLV_VOTE_NO_CHANGE
;
401 vote
= TLV_VOTE_NO_CHANGE
;
404 if (qdevice_net_algorithm_votequorum_quorum_notify(net_instance
, quorate
,
405 node_list_entries
, node_list
, &send_node_list
, &vote
) != 0) {
406 qdevice_log(LOG_ERR
, "Algorithm returned error. Disconnecting.");
408 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_QUORUM_NOTIFY_ERR
;
409 net_instance
->schedule_disconnect
= 1;
413 qdevice_log(LOG_DEBUG
, "Algorithm decided to %s list and result vote is %s",
414 (send_node_list
? "send" : "not send"), tlv_vote_to_str(vote
));
417 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
418 qdevice_log(LOG_CRIT
, "qdevice_model_net_votequorum_quorum_notify fatal error. "
419 " Can't update cast vote timer vote");
420 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER
;
421 net_instance
->schedule_disconnect
= 1;
426 if (send_node_list
) {
427 if (qdevice_net_send_quorum_node_list(net_instance
,
428 (quorate
? TLV_QUORATE_QUORATE
: TLV_QUORATE_INQUORATE
),
429 node_list_entries
, node_list
) != 0) {
431 * Fatal error -> schedule disconnect
433 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
434 net_instance
->schedule_disconnect
= 1;
444 qdevice_model_net_votequorum_node_list_heuristics_notify(struct qdevice_instance
*instance
,
445 votequorum_ring_id_t votequorum_ring_id
, uint32_t node_list_entries
, uint32_t node_list
[],
446 enum qdevice_heuristics_exec_result heuristics_exec_result
)
448 struct qdevice_net_instance
*net_instance
;
449 struct tlv_ring_id tlv_rid
;
451 enum tlv_heuristics heuristics
;
454 net_instance
= instance
->model_data
;
456 qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid
, &votequorum_ring_id
);
457 heuristics
= qdevice_net_heuristics_exec_result_to_tlv(heuristics_exec_result
);
459 if (net_instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
) {
461 * Nodelist changed, but connection to qnetd not initiated yet.
465 if (net_instance
->cast_vote_timer_vote
== TLV_VOTE_ACK
) {
466 vote
= TLV_VOTE_NACK
;
468 vote
= TLV_VOTE_NO_CHANGE
;
472 vote
= TLV_VOTE_WAIT_FOR_REPLY
;
475 if (qdevice_net_algorithm_votequorum_node_list_heuristics_notify(net_instance
, &tlv_rid
,
476 node_list_entries
, node_list
, &send_node_list
, &vote
, &heuristics
) != 0) {
477 qdevice_log(LOG_ERR
, "Algorithm returned error. Disconnecting.");
479 net_instance
->disconnect_reason
=
480 QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_HEURISTICS_NOTIFY_ERR
;
481 net_instance
->schedule_disconnect
= 1;
485 qdevice_log(LOG_DEBUG
, "Algorithm decided to %s list, result vote is %s and heuristics is %s",
486 (send_node_list
? "send" : "not send"), tlv_vote_to_str(vote
),
487 tlv_heuristics_to_str(heuristics
));
490 if (send_node_list
) {
491 if (qdevice_net_send_membership_node_list(net_instance
, &tlv_rid
,
492 node_list_entries
, node_list
, heuristics
) != 0) {
494 * Fatal error -> schedule disconnect
496 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
497 net_instance
->schedule_disconnect
= 1;
504 * Unpause cast vote timer
506 qdevice_net_cast_vote_timer_set_paused(net_instance
, 0);
508 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
509 qdevice_log(LOG_CRIT
, "qdevice_model_net_votequorum_node_list_notify fatal error "
510 "Can't update cast vote timer");
511 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER
;
512 net_instance
->schedule_disconnect
= 1;
517 net_instance
->latest_vq_heuristics_result
= heuristics
;
518 net_instance
->latest_heuristics_result
= heuristics
;
520 if (qdevice_net_heuristics_schedule_timer(net_instance
) != 0) {
528 qdevice_model_net_votequorum_node_list_notify(struct qdevice_instance
*instance
,
529 votequorum_ring_id_t votequorum_ring_id
, uint32_t node_list_entries
, uint32_t node_list
[])
531 struct qdevice_net_instance
*net_instance
;
532 struct tlv_ring_id tlv_rid
;
534 int pause_cast_vote_timer
;
536 net_instance
= instance
->model_data
;
539 * Stop regular heuristics till qdevice_model_net_votequorum_node_list_heuristics_notify
542 if (qdevice_net_heuristics_stop_timer(net_instance
) != 0) {
546 pause_cast_vote_timer
= 1;
547 vote
= TLV_VOTE_NO_CHANGE
;
549 if (net_instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
&&
550 net_instance
->cast_vote_timer_vote
== TLV_VOTE_ACK
) {
552 * Nodelist changed and vote timer still votes ACK. It's needed to start voting
555 if (net_instance
->cast_vote_timer_vote
== TLV_VOTE_ACK
) {
556 vote
= TLV_VOTE_NACK
;
560 qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid
, &votequorum_ring_id
);
562 if (qdevice_net_algorithm_votequorum_node_list_notify(net_instance
, &tlv_rid
,
563 node_list_entries
, node_list
, &pause_cast_vote_timer
, &vote
) != 0) {
564 qdevice_log(LOG_ERR
, "Algorithm returned error. Disconnecting.");
566 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_NOTIFY_ERR
;
567 net_instance
->schedule_disconnect
= 1;
571 qdevice_log(LOG_DEBUG
, "Algorithm decided to %s cast vote timer and result vote is %s ",
572 (pause_cast_vote_timer
? "pause" : "not pause"), tlv_vote_to_str(vote
));
575 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
576 qdevice_log(LOG_CRIT
, "qdevice_model_net_votequorum_node_list_notify fatal error "
577 "Can't update cast vote timer");
578 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER
;
579 net_instance
->schedule_disconnect
= 1;
584 qdevice_net_cast_vote_timer_set_paused(net_instance
, pause_cast_vote_timer
);
590 qdevice_model_net_votequorum_expected_votes_notify(struct qdevice_instance
*instance
,
591 uint32_t expected_votes
)
593 struct qdevice_net_instance
*net_instance
;
596 net_instance
= instance
->model_data
;
598 qdevice_log(LOG_DEBUG
, "qdevice_model_net_votequorum_expected_votes_notify"
599 " (expected votes old=%"PRIu32
" / new=%"PRIu32
")",
600 net_instance
->qdevice_instance_ptr
->vq_expected_votes
, expected_votes
);
602 vote
= TLV_VOTE_NO_CHANGE
;
604 if (qdevice_net_algorithm_votequorum_expected_votes_notify(net_instance
, expected_votes
,
606 qdevice_log(LOG_DEBUG
, "Algorithm returned error. Disconnecting.");
608 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_EXPECTED_VOTES_NOTIFY_ERR
;
609 net_instance
->schedule_disconnect
= 1;
613 qdevice_log(LOG_DEBUG
, "Algorithm result vote is %s", tlv_vote_to_str(vote
));
616 if (qdevice_net_cast_vote_timer_update(net_instance
, vote
) != 0) {
617 qdevice_log(LOG_CRIT
, "qdevice_model_net_votequorum_expected_votes_notify fatal error. "
618 " Can't update cast vote timer vote");
619 net_instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER
;
620 net_instance
->schedule_disconnect
= 1;
629 qdevice_model_net_cmap_changed(struct qdevice_instance
*instance
,
630 const struct qdevice_cmap_change_events
*events
)
632 struct qdevice_net_instance
*net_instance
;
633 enum qdevice_heuristics_mode active_heuristics_mode
;
634 int heuristics_enabled
;
636 net_instance
= instance
->model_data
;
638 if (events
->heuristics
) {
639 active_heuristics_mode
= instance
->heuristics_instance
.mode
;
640 heuristics_enabled
= (active_heuristics_mode
== QDEVICE_HEURISTICS_MODE_ENABLED
||
641 active_heuristics_mode
== QDEVICE_HEURISTICS_MODE_SYNC
);
643 if (net_instance
->state
== QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
&&
644 !net_instance
->server_supports_heuristics
&& heuristics_enabled
) {
645 qdevice_log(LOG_ERR
, "Heuristics are enabled but not supported by the server");
647 net_instance
->disconnect_reason
=
648 QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_OPT
;
650 net_instance
->schedule_disconnect
= 1;
655 if (qdevice_net_heuristics_schedule_timer(net_instance
) != 0) {
664 qdevice_model_net_ipc_cmd_status(struct qdevice_instance
*instance
,
665 struct dynar
*outbuf
, int verbose
)
667 struct qdevice_net_instance
*net_instance
;
669 net_instance
= instance
->model_data
;
671 if (!qdevice_net_ipc_cmd_status(net_instance
, outbuf
, verbose
)) {
678 static struct qdevice_model qdevice_model_net
= {
680 .init
= qdevice_model_net_init
,
681 .destroy
= qdevice_model_net_destroy
,
682 .run
= qdevice_model_net_run
,
683 .get_config_node_list_failed
= qdevice_model_net_get_config_node_list_failed
,
684 .config_node_list_changed
= qdevice_model_net_config_node_list_changed
,
685 .votequorum_quorum_notify
= qdevice_model_net_votequorum_quorum_notify
,
686 .votequorum_node_list_notify
= qdevice_model_net_votequorum_node_list_notify
,
687 .votequorum_node_list_heuristics_notify
= qdevice_model_net_votequorum_node_list_heuristics_notify
,
688 .votequorum_expected_votes_notify
= qdevice_model_net_votequorum_expected_votes_notify
,
689 .cmap_changed
= qdevice_model_net_cmap_changed
,
690 .ipc_cmd_status
= qdevice_model_net_ipc_cmd_status
,
694 qdevice_model_net_register(void)
696 return (qdevice_model_register(QDEVICE_MODEL_TYPE_NET
, &qdevice_model_net
));