2 * Copyright (C) 2018 NetDEF, Inc.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "lib_errors.h"
29 #include "northbound.h"
32 #include <sysrepo/values.h>
33 #include <sysrepo/xpath.h>
35 DEFINE_MTYPE_STATIC(LIB
, SYSREPO
, "Sysrepo module")
37 static struct debug nb_dbg_client_sysrepo
= {0, "Northbound client: Sysrepo"};
39 static struct thread_master
*master
;
40 static struct list
*sysrepo_threads
;
41 static sr_session_ctx_t
*session
;
42 static sr_conn_ctx_t
*connection
;
43 static struct nb_transaction
*transaction
;
45 static int frr_sr_read_cb(struct thread
*thread
);
46 static int frr_sr_write_cb(struct thread
*thread
);
47 static int frr_sr_finish(void);
49 /* Convert FRR YANG data value to sysrepo YANG data value. */
50 static int yang_data_frr2sr(struct yang_data
*frr_data
, sr_val_t
*sr_data
)
52 struct nb_node
*nb_node
;
53 const struct lys_node
*snode
;
54 struct lys_node_container
*scontainer
;
55 struct lys_node_leaf
*sleaf
;
56 struct lys_node_leaflist
*sleaflist
;
59 sr_val_set_xpath(sr_data
, frr_data
->xpath
);
61 nb_node
= nb_node_find(frr_data
->xpath
);
63 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
64 "%s: unknown data path: %s", __func__
,
69 snode
= nb_node
->snode
;
70 switch (snode
->nodetype
) {
72 scontainer
= (struct lys_node_container
*)snode
;
73 if (!scontainer
->presence
)
75 sr_data
->type
= SR_CONTAINER_PRESENCE_T
;
78 sr_data
->type
= SR_LIST_T
;
81 sleaf
= (struct lys_node_leaf
*)snode
;
82 type
= sleaf
->type
.base
;
85 sleaflist
= (struct lys_node_leaflist
*)snode
;
86 type
= sleaflist
->type
.base
;
94 sr_val_set_str_data(sr_data
, SR_BINARY_T
, frr_data
->value
);
97 sr_val_set_str_data(sr_data
, SR_BITS_T
, frr_data
->value
);
100 sr_data
->type
= SR_BOOL_T
;
101 sr_data
->data
.bool_val
= yang_str2bool(frr_data
->value
);
104 sr_data
->type
= SR_DECIMAL64_T
;
105 sr_data
->data
.decimal64_val
=
106 yang_str2dec64(frr_data
->xpath
, frr_data
->value
);
109 sr_data
->type
= SR_LEAF_EMPTY_T
;
112 sr_val_set_str_data(sr_data
, SR_ENUM_T
, frr_data
->value
);
115 sr_val_set_str_data(sr_data
, SR_IDENTITYREF_T
, frr_data
->value
);
118 sr_val_set_str_data(sr_data
, SR_INSTANCEID_T
, frr_data
->value
);
121 sr_data
->type
= SR_INT8_T
;
122 sr_data
->data
.int8_val
= yang_str2int8(frr_data
->value
);
125 sr_data
->type
= SR_INT16_T
;
126 sr_data
->data
.int16_val
= yang_str2int16(frr_data
->value
);
129 sr_data
->type
= SR_INT32_T
;
130 sr_data
->data
.int32_val
= yang_str2int32(frr_data
->value
);
133 sr_data
->type
= SR_INT64_T
;
134 sr_data
->data
.int64_val
= yang_str2int64(frr_data
->value
);
137 sr_val_set_str_data(sr_data
, SR_STRING_T
, frr_data
->value
);
140 sr_data
->type
= SR_UINT8_T
;
141 sr_data
->data
.uint8_val
= yang_str2uint8(frr_data
->value
);
144 sr_data
->type
= SR_UINT16_T
;
145 sr_data
->data
.uint16_val
= yang_str2uint16(frr_data
->value
);
148 sr_data
->type
= SR_UINT32_T
;
149 sr_data
->data
.uint32_val
= yang_str2uint32(frr_data
->value
);
152 sr_data
->type
= SR_UINT64_T
;
153 sr_data
->data
.uint64_val
= yang_str2uint64(frr_data
->value
);
162 static int frr_sr_process_change(struct nb_config
*candidate
,
163 sr_change_oper_t sr_op
, sr_val_t
*sr_old_val
,
164 sr_val_t
*sr_new_val
)
166 struct nb_node
*nb_node
;
167 enum nb_operation nb_op
;
170 char value_str
[YANG_VALUE_MAXLEN
];
171 struct yang_data
*data
;
174 sr_data
= sr_new_val
? sr_new_val
: sr_old_val
;
177 xpath
= sr_data
->xpath
;
179 /* Non-presence container - nothing to do. */
180 if (sr_data
->type
== SR_CONTAINER_T
)
183 nb_node
= nb_node_find(xpath
);
185 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
186 "%s: unknown data path: %s", __func__
, xpath
);
190 /* Map operation values. */
194 if (nb_operation_is_valid(NB_OP_CREATE
, nb_node
->snode
))
195 nb_op
= NB_OP_CREATE
;
196 else if (nb_operation_is_valid(NB_OP_MODIFY
, nb_node
->snode
)) {
197 nb_op
= NB_OP_MODIFY
;
199 /* Ignore list keys modifications. */
204 * When a list is deleted or one of its keys is changed, we are
205 * notified about the removal of all of its leafs, even the ones
206 * that are non-optional. We need to ignore these notifications.
208 if (!nb_operation_is_valid(NB_OP_DESTROY
, nb_node
->snode
))
211 nb_op
= NB_OP_DESTROY
;
217 flog_err(EC_LIB_DEVELOPMENT
,
218 "%s: unexpected operation %u [xpath %s]", __func__
,
223 sr_val_to_buff(sr_data
, value_str
, sizeof(value_str
));
224 data
= yang_data_new(xpath
, value_str
);
226 ret
= nb_candidate_edit(candidate
, nb_node
, nb_op
, xpath
, NULL
, data
);
227 yang_data_free(data
);
230 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
231 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
232 __func__
, nb_operation_name(nb_op
), xpath
);
239 static int frr_sr_config_change_cb_verify(sr_session_ctx_t
*session
,
240 const char *module_name
,
243 sr_change_iter_t
*it
;
245 sr_change_oper_t sr_op
;
246 sr_val_t
*sr_old_val
, *sr_new_val
;
247 char xpath
[XPATH_MAXLEN
];
248 struct nb_context context
= {};
249 struct nb_config
*candidate
;
250 char errmsg
[BUFSIZ
] = {0};
252 snprintf(xpath
, sizeof(xpath
), "/%s:*", module_name
);
253 ret
= sr_get_changes_iter(session
, xpath
, &it
);
254 if (ret
!= SR_ERR_OK
) {
255 flog_err(EC_LIB_LIBSYSREPO
,
256 "%s: sr_get_changes_iter() failed for xpath %s",
261 candidate
= nb_config_dup(running_config
);
263 while ((ret
= sr_get_change_next(session
, it
, &sr_op
, &sr_old_val
,
266 ret
= frr_sr_process_change(candidate
, sr_op
, sr_old_val
,
268 sr_free_val(sr_old_val
);
269 sr_free_val(sr_new_val
);
274 sr_free_change_iter(it
);
275 if (ret
!= NB_OK
&& ret
!= SR_ERR_NOT_FOUND
) {
276 nb_config_free(candidate
);
277 return SR_ERR_INTERNAL
;
281 context
.client
= NB_CLIENT_SYSREPO
;
282 if (startup_config
) {
284 * sysrepod sends the entire startup configuration using a
285 * single event (SR_EV_ENABLED). This means we need to perform
286 * the full two-phase commit protocol in one go here.
288 ret
= nb_candidate_commit(&context
, candidate
, true, NULL
, NULL
,
289 errmsg
, sizeof(errmsg
));
290 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
)
293 "%s: failed to apply startup configuration: %s (%s)",
294 __func__
, nb_err_name(ret
), errmsg
);
297 * Validate the configuration changes and allocate all resources
298 * required to apply them.
300 ret
= nb_candidate_commit_prepare(&context
, candidate
, NULL
,
301 &transaction
, errmsg
,
303 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
)
306 "%s: failed to prepare configuration transaction: %s (%s)",
307 __func__
, nb_err_name(ret
), errmsg
);
310 /* Map northbound return code to sysrepo return code. */
314 case NB_ERR_NO_CHANGES
:
315 nb_config_free(candidate
);
318 return SR_ERR_LOCKED
;
319 case NB_ERR_RESOURCE
:
322 return SR_ERR_VALIDATION_FAILED
;
326 static int frr_sr_config_change_cb_apply(sr_session_ctx_t
*session
,
327 const char *module_name
)
329 /* Apply the transaction. */
331 struct nb_config
*candidate
= transaction
->config
;
333 nb_candidate_commit_apply(transaction
, true, NULL
);
334 nb_config_free(candidate
);
340 static int frr_sr_config_change_cb_abort(sr_session_ctx_t
*session
,
341 const char *module_name
)
343 /* Abort the transaction. */
345 struct nb_config
*candidate
= transaction
->config
;
347 nb_candidate_commit_abort(transaction
);
348 nb_config_free(candidate
);
354 /* Callback for changes in the running configuration. */
355 static int frr_sr_config_change_cb(sr_session_ctx_t
*session
,
356 const char *module_name
,
357 sr_notif_event_t sr_ev
, void *private_ctx
)
361 return frr_sr_config_change_cb_verify(session
, module_name
,
364 return frr_sr_config_change_cb_verify(session
, module_name
,
367 return frr_sr_config_change_cb_apply(session
, module_name
);
369 return frr_sr_config_change_cb_abort(session
, module_name
);
371 flog_err(EC_LIB_LIBSYSREPO
, "%s: unknown sysrepo event: %u",
373 return SR_ERR_INTERNAL
;
377 static int frr_sr_state_data_iter_cb(const struct lys_node
*snode
,
378 struct yang_translator
*translator
,
379 struct yang_data
*data
, void *arg
)
381 struct list
*elements
= arg
;
383 listnode_add(elements
, data
);
388 /* Callback for state retrieval. */
389 static int frr_sr_state_cb(const char *xpath
, sr_val_t
**values
,
390 size_t *values_cnt
, uint64_t request_id
,
391 const char *original_xpath
, void *private_ctx
)
393 struct list
*elements
;
394 struct yang_data
*data
;
395 struct listnode
*node
;
397 int ret
, count
, i
= 0;
399 elements
= yang_data_list_new();
400 if (nb_oper_data_iterate(xpath
, NULL
, NB_OPER_DATA_ITER_NORECURSE
,
401 frr_sr_state_data_iter_cb
, elements
)
403 flog_warn(EC_LIB_NB_OPERATIONAL_DATA
,
404 "%s: failed to obtain operational data [xpath %s]",
409 if (list_isempty(elements
))
412 count
= listcount(elements
);
413 ret
= sr_new_values(count
, &v
);
414 if (ret
!= SR_ERR_OK
) {
415 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_new_values(): %s", __func__
,
420 for (ALL_LIST_ELEMENTS_RO(elements
, node
, data
)) {
421 if (yang_data_frr2sr(data
, &v
[i
++]) != 0) {
422 flog_err(EC_LIB_SYSREPO_DATA_CONVERT
,
423 "%s: failed to convert data to sysrepo format",
431 list_delete(&elements
);
436 list_delete(&elements
);
443 static int frr_sr_config_rpc_cb(const char *xpath
, const sr_val_t
*sr_input
,
444 const size_t input_cnt
, sr_val_t
**sr_output
,
445 size_t *sr_output_cnt
, void *private_ctx
)
447 struct nb_node
*nb_node
;
450 struct yang_data
*data
;
451 size_t cb_output_cnt
;
454 nb_node
= nb_node_find(xpath
);
456 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
457 "%s: unknown data path: %s", __func__
, xpath
);
458 return SR_ERR_INTERNAL
;
461 input
= yang_data_list_new();
462 output
= yang_data_list_new();
465 for (size_t i
= 0; i
< input_cnt
; i
++) {
466 char value_str
[YANG_VALUE_MAXLEN
];
468 sr_val_to_buff(&sr_input
[i
], value_str
, sizeof(value_str
));
470 data
= yang_data_new(xpath
, value_str
);
471 listnode_add(input
, data
);
474 /* Execute callback registered for this XPath. */
475 if (nb_callback_rpc(nb_node
, xpath
, input
, output
) != NB_OK
) {
476 flog_warn(EC_LIB_NB_CB_RPC
, "%s: rpc callback failed: %s",
478 ret
= SR_ERR_OPERATION_FAILED
;
482 /* Process output. */
483 if (listcount(output
) > 0) {
484 sr_val_t
*values
= NULL
;
485 struct listnode
*node
;
488 cb_output_cnt
= listcount(output
);
489 ret
= sr_new_values(cb_output_cnt
, &values
);
490 if (ret
!= SR_ERR_OK
) {
491 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_new_values(): %s",
492 __func__
, sr_strerror(ret
));
496 for (ALL_LIST_ELEMENTS_RO(output
, node
, data
)) {
497 if (yang_data_frr2sr(data
, &values
[i
++]) != 0) {
499 EC_LIB_SYSREPO_DATA_CONVERT
,
500 "%s: failed to convert data to Sysrepo format",
502 ret
= SR_ERR_INTERNAL
;
503 sr_free_values(values
, cb_output_cnt
);
509 *sr_output_cnt
= cb_output_cnt
;
513 /* Release memory. */
515 list_delete(&output
);
520 static int frr_sr_notification_send(const char *xpath
, struct list
*arguments
)
522 sr_val_t
*values
= NULL
;
523 size_t values_cnt
= 0;
526 if (arguments
&& listcount(arguments
) > 0) {
527 struct yang_data
*data
;
528 struct listnode
*node
;
531 values_cnt
= listcount(arguments
);
532 ret
= sr_new_values(values_cnt
, &values
);
533 if (ret
!= SR_ERR_OK
) {
534 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_new_values(): %s",
535 __func__
, sr_strerror(ret
));
539 for (ALL_LIST_ELEMENTS_RO(arguments
, node
, data
)) {
540 if (yang_data_frr2sr(data
, &values
[i
++]) != 0) {
542 EC_LIB_SYSREPO_DATA_CONVERT
,
543 "%s: failed to convert data to sysrepo format",
545 sr_free_values(values
, values_cnt
);
551 ret
= sr_event_notif_send(session
, xpath
, values
, values_cnt
,
552 SR_EV_NOTIF_DEFAULT
);
553 if (ret
!= SR_ERR_OK
) {
554 flog_err(EC_LIB_LIBSYSREPO
,
555 "%s: sr_event_notif_send() failed for xpath %s",
563 /* Code to integrate the sysrepo client into FRR main event loop. */
564 struct sysrepo_thread
{
565 struct thread
*thread
;
570 static struct sysrepo_thread
*frr_sr_fd_lookup(sr_fd_event_t event
, int fd
)
572 struct sysrepo_thread
*sr_thread
;
573 struct listnode
*node
;
575 for (ALL_LIST_ELEMENTS_RO(sysrepo_threads
, node
, sr_thread
)) {
576 if (sr_thread
->event
== event
&& sr_thread
->fd
== fd
)
583 static void frr_sr_fd_add(int event
, int fd
)
585 struct sysrepo_thread
*sr_thread
;
587 if (frr_sr_fd_lookup(event
, fd
) != NULL
)
590 sr_thread
= XCALLOC(MTYPE_SYSREPO
, sizeof(*sr_thread
));
591 sr_thread
->event
= event
;
593 listnode_add(sysrepo_threads
, sr_thread
);
596 case SR_FD_INPUT_READY
:
597 thread_add_read(master
, frr_sr_read_cb
, NULL
, fd
,
600 case SR_FD_OUTPUT_READY
:
601 thread_add_write(master
, frr_sr_write_cb
, NULL
, fd
,
609 static void frr_sr_fd_free(struct sysrepo_thread
*sr_thread
)
611 THREAD_OFF(sr_thread
->thread
);
612 XFREE(MTYPE_SYSREPO
, sr_thread
);
615 static void frr_sr_fd_del(int event
, int fd
)
617 struct sysrepo_thread
*sr_thread
;
619 sr_thread
= frr_sr_fd_lookup(event
, fd
);
623 listnode_delete(sysrepo_threads
, sr_thread
);
624 frr_sr_fd_free(sr_thread
);
627 static void frr_sr_fd_update(sr_fd_change_t
*fd_change_set
,
628 size_t fd_change_set_cnt
)
630 for (size_t i
= 0; i
< fd_change_set_cnt
; i
++) {
631 int fd
= fd_change_set
[i
].fd
;
632 int event
= fd_change_set
[i
].events
;
634 if (event
!= SR_FD_INPUT_READY
&& event
!= SR_FD_OUTPUT_READY
)
637 switch (fd_change_set
[i
].action
) {
638 case SR_FD_START_WATCHING
:
639 frr_sr_fd_add(event
, fd
);
641 case SR_FD_STOP_WATCHING
:
642 frr_sr_fd_del(event
, fd
);
650 static int frr_sr_read_cb(struct thread
*thread
)
652 int fd
= THREAD_FD(thread
);
653 sr_fd_change_t
*fd_change_set
= NULL
;
654 size_t fd_change_set_cnt
= 0;
657 ret
= sr_fd_event_process(fd
, SR_FD_INPUT_READY
, &fd_change_set
,
659 if (ret
!= SR_ERR_OK
) {
660 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_fd_event_process(): %s",
661 __func__
, sr_strerror(ret
));
666 thread_add_read(master
, frr_sr_read_cb
, NULL
, fd
, &thread
);
668 frr_sr_fd_update(fd_change_set
, fd_change_set_cnt
);
674 static int frr_sr_write_cb(struct thread
*thread
)
676 int fd
= THREAD_FD(thread
);
677 sr_fd_change_t
*fd_change_set
= NULL
;
678 size_t fd_change_set_cnt
= 0;
681 ret
= sr_fd_event_process(fd
, SR_FD_OUTPUT_READY
, &fd_change_set
,
683 if (ret
!= SR_ERR_OK
) {
684 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_fd_event_process(): %s",
685 __func__
, sr_strerror(ret
));
689 frr_sr_fd_update(fd_change_set
, fd_change_set_cnt
);
695 static void frr_sr_subscribe_config(struct yang_module
*module
)
699 ret
= sr_module_change_subscribe(
700 session
, module
->name
, frr_sr_config_change_cb
, NULL
, 0,
701 SR_SUBSCR_DEFAULT
| SR_SUBSCR_EV_ENABLED
,
702 &module
->sr_subscription
);
703 if (ret
!= SR_ERR_OK
)
704 flog_err(EC_LIB_LIBSYSREPO
, "sr_module_change_subscribe(): %s",
708 static int frr_sr_subscribe_state(const struct lys_node
*snode
, void *arg
)
710 struct yang_module
*module
= arg
;
711 struct nb_node
*nb_node
;
714 if (!CHECK_FLAG(snode
->flags
, LYS_CONFIG_R
))
715 return YANG_ITER_CONTINUE
;
716 /* We only need to subscribe to the root of the state subtrees. */
717 if (snode
->parent
&& CHECK_FLAG(snode
->parent
->flags
, LYS_CONFIG_R
))
718 return YANG_ITER_CONTINUE
;
720 nb_node
= snode
->priv
;
722 DEBUGD(&nb_dbg_client_sysrepo
, "%s: providing data to '%s'", __func__
,
725 ret
= sr_dp_get_items_subscribe(
726 session
, nb_node
->xpath
, frr_sr_state_cb
, NULL
,
727 SR_SUBSCR_CTX_REUSE
, &module
->sr_subscription
);
728 if (ret
!= SR_ERR_OK
)
729 flog_err(EC_LIB_LIBSYSREPO
, "sr_dp_get_items_subscribe(): %s",
732 return YANG_ITER_CONTINUE
;
735 static int frr_sr_subscribe_rpc(const struct lys_node
*snode
, void *arg
)
737 struct yang_module
*module
= arg
;
738 struct nb_node
*nb_node
;
741 if (snode
->nodetype
!= LYS_RPC
)
742 return YANG_ITER_CONTINUE
;
744 nb_node
= snode
->priv
;
746 DEBUGD(&nb_dbg_client_sysrepo
, "%s: providing RPC to '%s'", __func__
,
749 ret
= sr_rpc_subscribe(session
, nb_node
->xpath
, frr_sr_config_rpc_cb
,
750 NULL
, SR_SUBSCR_CTX_REUSE
,
751 &module
->sr_subscription
);
752 if (ret
!= SR_ERR_OK
)
753 flog_err(EC_LIB_LIBSYSREPO
, "sr_rpc_subscribe(): %s",
756 return YANG_ITER_CONTINUE
;
759 static int frr_sr_subscribe_action(const struct lys_node
*snode
, void *arg
)
761 struct yang_module
*module
= arg
;
762 struct nb_node
*nb_node
;
765 if (snode
->nodetype
!= LYS_ACTION
)
766 return YANG_ITER_CONTINUE
;
768 nb_node
= snode
->priv
;
770 DEBUGD(&nb_dbg_client_sysrepo
, "%s: providing action to '%s'", __func__
,
773 ret
= sr_action_subscribe(session
, nb_node
->xpath
, frr_sr_config_rpc_cb
,
774 NULL
, SR_SUBSCR_CTX_REUSE
,
775 &module
->sr_subscription
);
776 if (ret
!= SR_ERR_OK
)
777 flog_err(EC_LIB_LIBSYSREPO
, "sr_action_subscribe(): %s",
780 return YANG_ITER_CONTINUE
;
786 "[no] debug northbound client sysrepo",
789 "Northbound debugging\n"
790 "Northbound client\n"
793 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
794 bool no
= strmatch(argv
[0]->text
, "no");
796 DEBUG_MODE_SET(&nb_dbg_client_sysrepo
, mode
, !no
);
801 static int frr_sr_debug_config_write(struct vty
*vty
)
803 if (DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo
, DEBUG_MODE_CONF
))
804 vty_out(vty
, "debug northbound client sysrepo\n");
809 static int frr_sr_debug_set_all(uint32_t flags
, bool set
)
811 DEBUG_FLAGS_SET(&nb_dbg_client_sysrepo
, flags
, set
);
813 /* If all modes have been turned off, don't preserve options. */
814 if (!DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo
, DEBUG_MODE_ALL
))
815 DEBUG_CLEAR(&nb_dbg_client_sysrepo
);
820 static void frr_sr_cli_init(void)
822 hook_register(nb_client_debug_config_write
, frr_sr_debug_config_write
);
823 hook_register(nb_client_debug_set_all
, frr_sr_debug_set_all
);
825 install_element(ENABLE_NODE
, &debug_nb_sr_cmd
);
826 install_element(CONFIG_NODE
, &debug_nb_sr_cmd
);
829 /* FRR's Sysrepo initialization. */
830 static int frr_sr_init(const char *program_name
)
832 struct yang_module
*module
;
835 sysrepo_threads
= list_new();
837 ret
= sr_fd_watcher_init(&sysrepo_fd
, NULL
);
838 if (ret
!= SR_ERR_OK
) {
839 flog_err(EC_LIB_SYSREPO_INIT
, "%s: sr_fd_watcher_init(): %s",
840 __func__
, sr_strerror(ret
));
844 /* Connect to Sysrepo. */
845 ret
= sr_connect(program_name
, SR_CONN_DEFAULT
, &connection
);
846 if (ret
!= SR_ERR_OK
) {
847 flog_err(EC_LIB_SYSREPO_INIT
, "%s: sr_connect(): %s", __func__
,
853 ret
= sr_session_start(connection
, SR_DS_RUNNING
, SR_SESS_DEFAULT
,
855 if (ret
!= SR_ERR_OK
) {
856 flog_err(EC_LIB_SYSREPO_INIT
, "%s: sr_session_start(): %s",
857 __func__
, sr_strerror(ret
));
861 /* Perform subscriptions. */
862 RB_FOREACH (module
, yang_modules
, &yang_modules
) {
863 frr_sr_subscribe_config(module
);
864 yang_snodes_iterate_module(module
->info
, frr_sr_subscribe_state
,
866 yang_snodes_iterate_module(module
->info
, frr_sr_subscribe_rpc
,
868 yang_snodes_iterate_module(module
->info
,
869 frr_sr_subscribe_action
, 0, module
);
872 hook_register(nb_notification_send
, frr_sr_notification_send
);
874 frr_sr_fd_add(SR_FD_INPUT_READY
, sysrepo_fd
);
884 static int frr_sr_finish(void)
886 struct yang_module
*module
;
888 RB_FOREACH (module
, yang_modules
, &yang_modules
) {
889 if (!module
->sr_subscription
)
891 sr_unsubscribe(session
, module
->sr_subscription
);
895 sr_session_stop(session
);
897 sr_disconnect(connection
);
899 sysrepo_threads
->del
= (void (*)(void *))frr_sr_fd_free
;
900 list_delete(&sysrepo_threads
);
901 sr_fd_watcher_cleanup();
906 static int frr_sr_module_late_init(struct thread_master
*tm
)
910 if (frr_sr_init(frr_get_progname()) < 0) {
911 flog_err(EC_LIB_SYSREPO_INIT
,
912 "failed to initialize the Sysrepo module");
916 hook_register(frr_fini
, frr_sr_finish
);
922 static int frr_sr_module_init(void)
924 hook_register(frr_late_init
, frr_sr_module_late_init
);
929 FRR_MODULE_SETUP(.name
= "frr_sysrepo", .version
= FRR_VERSION
,
930 .description
= "FRR sysrepo integration module",
931 .init
= frr_sr_module_init
, )