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"
28 #include "lib/version.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 sr_session_ctx_t
*session
;
41 static sr_conn_ctx_t
*connection
;
42 static struct nb_transaction
*transaction
;
44 static int frr_sr_read_cb(struct thread
*thread
);
45 static int frr_sr_finish(void);
47 /* Convert FRR YANG data value to sysrepo YANG data value. */
48 static int yang_data_frr2sr(struct yang_data
*frr_data
, sr_val_t
*sr_data
)
50 struct nb_node
*nb_node
;
51 const struct lysc_node
*snode
;
52 struct lysc_node_container
*scontainer
;
53 struct lysc_node_leaf
*sleaf
;
54 struct lysc_node_leaflist
*sleaflist
;
57 sr_val_set_xpath(sr_data
, frr_data
->xpath
);
59 nb_node
= nb_node_find(frr_data
->xpath
);
61 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
62 "%s: unknown data path: %s", __func__
,
67 snode
= nb_node
->snode
;
68 switch (snode
->nodetype
) {
70 scontainer
= (struct lysc_node_container
*)snode
;
71 if (!CHECK_FLAG(scontainer
->flags
, LYS_PRESENCE
))
73 sr_data
->type
= SR_CONTAINER_PRESENCE_T
;
76 sr_data
->type
= SR_LIST_T
;
79 sleaf
= (struct lysc_node_leaf
*)snode
;
80 type
= sleaf
->type
.base
;
83 sleaflist
= (struct lysc_node_leaflist
*)snode
;
84 type
= sleaflist
->type
.base
;
92 sr_val_set_str_data(sr_data
, SR_BINARY_T
, frr_data
->value
);
95 sr_val_set_str_data(sr_data
, SR_BITS_T
, frr_data
->value
);
98 sr_data
->type
= SR_BOOL_T
;
99 sr_data
->data
.bool_val
= yang_str2bool(frr_data
->value
);
102 sr_data
->type
= SR_DECIMAL64_T
;
103 sr_data
->data
.decimal64_val
=
104 yang_str2dec64(frr_data
->xpath
, frr_data
->value
);
107 sr_data
->type
= SR_LEAF_EMPTY_T
;
110 sr_val_set_str_data(sr_data
, SR_ENUM_T
, frr_data
->value
);
113 sr_val_set_str_data(sr_data
, SR_IDENTITYREF_T
, frr_data
->value
);
116 sr_val_set_str_data(sr_data
, SR_INSTANCEID_T
, frr_data
->value
);
119 sr_data
->type
= SR_INT8_T
;
120 sr_data
->data
.int8_val
= yang_str2int8(frr_data
->value
);
123 sr_data
->type
= SR_INT16_T
;
124 sr_data
->data
.int16_val
= yang_str2int16(frr_data
->value
);
127 sr_data
->type
= SR_INT32_T
;
128 sr_data
->data
.int32_val
= yang_str2int32(frr_data
->value
);
131 sr_data
->type
= SR_INT64_T
;
132 sr_data
->data
.int64_val
= yang_str2int64(frr_data
->value
);
135 sr_val_set_str_data(sr_data
, SR_STRING_T
, frr_data
->value
);
138 sr_data
->type
= SR_UINT8_T
;
139 sr_data
->data
.uint8_val
= yang_str2uint8(frr_data
->value
);
142 sr_data
->type
= SR_UINT16_T
;
143 sr_data
->data
.uint16_val
= yang_str2uint16(frr_data
->value
);
146 sr_data
->type
= SR_UINT32_T
;
147 sr_data
->data
.uint32_val
= yang_str2uint32(frr_data
->value
);
150 sr_data
->type
= SR_UINT64_T
;
151 sr_data
->data
.uint64_val
= yang_str2uint64(frr_data
->value
);
160 static int frr_sr_process_change(struct nb_config
*candidate
,
161 sr_change_oper_t sr_op
, sr_val_t
*sr_old_val
,
162 sr_val_t
*sr_new_val
)
164 struct nb_node
*nb_node
;
165 enum nb_operation nb_op
;
168 char value_str
[YANG_VALUE_MAXLEN
];
169 struct yang_data
*data
;
172 sr_data
= sr_new_val
? sr_new_val
: sr_old_val
;
175 xpath
= sr_data
->xpath
;
177 DEBUGD(&nb_dbg_client_sysrepo
, "sysrepo: processing change [xpath %s]",
180 /* Non-presence container - nothing to do. */
181 if (sr_data
->type
== SR_CONTAINER_T
)
184 nb_node
= nb_node_find(xpath
);
186 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
187 "%s: unknown data path: %s", __func__
, xpath
);
191 /* Map operation values. */
195 if (nb_operation_is_valid(NB_OP_CREATE
, nb_node
->snode
))
196 nb_op
= NB_OP_CREATE
;
197 else if (nb_operation_is_valid(NB_OP_MODIFY
, nb_node
->snode
)) {
198 nb_op
= NB_OP_MODIFY
;
200 /* Ignore list keys modifications. */
205 * When a list is deleted or one of its keys is changed, we are
206 * notified about the removal of all of its leafs, even the ones
207 * that are non-optional. We need to ignore these notifications.
209 if (!nb_operation_is_valid(NB_OP_DESTROY
, nb_node
->snode
))
212 nb_op
= NB_OP_DESTROY
;
218 flog_err(EC_LIB_DEVELOPMENT
,
219 "%s: unexpected operation %u [xpath %s]", __func__
,
224 sr_val_to_buff(sr_data
, value_str
, sizeof(value_str
));
225 data
= yang_data_new(xpath
, value_str
);
227 ret
= nb_candidate_edit(candidate
, nb_node
, nb_op
, xpath
, NULL
, data
);
228 yang_data_free(data
);
229 if (ret
!= NB_OK
&& ret
!= NB_ERR_NOT_FOUND
) {
231 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
232 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
233 __func__
, nb_operation_name(nb_op
), xpath
);
240 static int frr_sr_config_change_cb_prepare(sr_session_ctx_t
*session
,
241 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 struct nb_context context
= {};
248 struct nb_config
*candidate
;
249 char errmsg
[BUFSIZ
] = {0};
251 ret
= sr_get_changes_iter(session
, "//*", &it
);
252 if (ret
!= SR_ERR_OK
) {
253 flog_err(EC_LIB_LIBSYSREPO
,
254 "%s: sr_get_changes_iter() failed for \"%s\"",
255 __func__
, module_name
);
259 candidate
= nb_config_dup(running_config
);
261 while ((ret
= sr_get_change_next(session
, it
, &sr_op
, &sr_old_val
,
264 ret
= frr_sr_process_change(candidate
, sr_op
, sr_old_val
,
266 sr_free_val(sr_old_val
);
267 sr_free_val(sr_new_val
);
272 sr_free_change_iter(it
);
273 if (ret
!= NB_OK
&& ret
!= SR_ERR_NOT_FOUND
) {
274 nb_config_free(candidate
);
275 return SR_ERR_INTERNAL
;
279 context
.client
= NB_CLIENT_SYSREPO
;
281 * Validate the configuration changes and allocate all resources
282 * required to apply them.
284 ret
= nb_candidate_commit_prepare(&context
, candidate
, NULL
,
285 &transaction
, errmsg
, sizeof(errmsg
));
286 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
)
289 "%s: failed to prepare configuration transaction: %s (%s)",
290 __func__
, nb_err_name(ret
), errmsg
);
293 nb_config_free(candidate
);
295 /* Map northbound return code to sysrepo return code. */
299 case NB_ERR_NO_CHANGES
:
302 return SR_ERR_LOCKED
;
303 case NB_ERR_RESOURCE
:
306 return SR_ERR_VALIDATION_FAILED
;
310 static int frr_sr_config_change_cb_apply(sr_session_ctx_t
*session
,
311 const char *module_name
)
313 /* Apply the transaction. */
315 struct nb_config
*candidate
= transaction
->config
;
316 char errmsg
[BUFSIZ
] = {0};
318 nb_candidate_commit_apply(transaction
, true, NULL
, errmsg
,
320 nb_config_free(candidate
);
326 static int frr_sr_config_change_cb_abort(sr_session_ctx_t
*session
,
327 const char *module_name
)
329 /* Abort the transaction. */
331 struct nb_config
*candidate
= transaction
->config
;
332 char errmsg
[BUFSIZ
] = {0};
334 nb_candidate_commit_abort(transaction
, errmsg
, sizeof(errmsg
));
335 nb_config_free(candidate
);
341 /* Callback for changes in the running configuration. */
342 static int frr_sr_config_change_cb(sr_session_ctx_t
*session
,
343 const char *module_name
, const char *xpath
,
344 sr_event_t sr_ev
, uint32_t request_id
,
350 return frr_sr_config_change_cb_prepare(session
, module_name
);
352 return frr_sr_config_change_cb_apply(session
, module_name
);
354 return frr_sr_config_change_cb_abort(session
, module_name
);
356 flog_err(EC_LIB_LIBSYSREPO
, "%s: unexpected sysrepo event: %u",
358 return SR_ERR_INTERNAL
;
362 static int frr_sr_state_data_iter_cb(const struct lysc_node
*snode
,
363 struct yang_translator
*translator
,
364 struct yang_data
*data
, void *arg
)
366 struct lyd_node
*dnode
= arg
;
369 dnode
= lyd_new_path(dnode
, ly_native_ctx
, data
->xpath
, data
->value
, 0,
370 LYD_PATH_OPT_UPDATE
);
371 if (!dnode
&& ly_errno
) {
372 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path() failed",
374 yang_data_free(data
);
378 yang_data_free(data
);
382 /* Callback for state retrieval. */
383 static int frr_sr_state_cb(sr_session_ctx_t
*session
, const char *module_name
,
384 const char *xpath
, const char *request_xpath
,
385 uint32_t request_id
, struct lyd_node
**parent
,
388 struct lyd_node
*dnode
;
391 if (nb_oper_data_iterate(request_xpath
, NULL
, 0,
392 frr_sr_state_data_iter_cb
, dnode
)
394 flog_warn(EC_LIB_NB_OPERATIONAL_DATA
,
395 "%s: failed to obtain operational data [xpath %s]",
397 return SR_ERR_INTERNAL
;
405 static int frr_sr_config_rpc_cb(sr_session_ctx_t
*session
, const char *xpath
,
406 const sr_val_t
*sr_input
,
407 const size_t input_cnt
, sr_event_t sr_ev
,
408 uint32_t request_id
, sr_val_t
**sr_output
,
409 size_t *sr_output_cnt
, void *private_ctx
)
411 struct nb_node
*nb_node
;
414 struct yang_data
*data
;
415 size_t cb_output_cnt
;
417 char errmsg
[BUFSIZ
] = {0};
419 nb_node
= nb_node_find(xpath
);
421 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
422 "%s: unknown data path: %s", __func__
, xpath
);
423 return SR_ERR_INTERNAL
;
426 input
= yang_data_list_new();
427 output
= yang_data_list_new();
430 for (size_t i
= 0; i
< input_cnt
; i
++) {
431 char value_str
[YANG_VALUE_MAXLEN
];
433 sr_val_to_buff(&sr_input
[i
], value_str
, sizeof(value_str
));
435 data
= yang_data_new(xpath
, value_str
);
436 listnode_add(input
, data
);
439 /* Execute callback registered for this XPath. */
440 if (nb_callback_rpc(nb_node
, xpath
, input
, output
, errmsg
,
443 flog_warn(EC_LIB_NB_CB_RPC
, "%s: rpc callback failed: %s",
445 ret
= SR_ERR_OPERATION_FAILED
;
449 /* Process output. */
450 if (listcount(output
) > 0) {
451 sr_val_t
*values
= NULL
;
452 struct listnode
*node
;
455 cb_output_cnt
= listcount(output
);
456 ret
= sr_new_values(cb_output_cnt
, &values
);
457 if (ret
!= SR_ERR_OK
) {
458 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_new_values(): %s",
459 __func__
, sr_strerror(ret
));
463 for (ALL_LIST_ELEMENTS_RO(output
, node
, data
)) {
464 if (yang_data_frr2sr(data
, &values
[i
++]) != 0) {
466 EC_LIB_SYSREPO_DATA_CONVERT
,
467 "%s: failed to convert data to Sysrepo format",
469 ret
= SR_ERR_INTERNAL
;
470 sr_free_values(values
, cb_output_cnt
);
476 *sr_output_cnt
= cb_output_cnt
;
480 /* Release memory. */
482 list_delete(&output
);
487 static int frr_sr_notification_send(const char *xpath
, struct list
*arguments
)
489 sr_val_t
*values
= NULL
;
490 size_t values_cnt
= 0;
493 if (arguments
&& listcount(arguments
) > 0) {
494 struct yang_data
*data
;
495 struct listnode
*node
;
498 values_cnt
= listcount(arguments
);
499 ret
= sr_new_values(values_cnt
, &values
);
500 if (ret
!= SR_ERR_OK
) {
501 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_new_values(): %s",
502 __func__
, sr_strerror(ret
));
506 for (ALL_LIST_ELEMENTS_RO(arguments
, node
, data
)) {
507 if (yang_data_frr2sr(data
, &values
[i
++]) != 0) {
509 EC_LIB_SYSREPO_DATA_CONVERT
,
510 "%s: failed to convert data to sysrepo format",
512 sr_free_values(values
, values_cnt
);
518 ret
= sr_event_notif_send(session
, xpath
, values
, values_cnt
);
519 if (ret
!= SR_ERR_OK
) {
520 flog_err(EC_LIB_LIBSYSREPO
,
521 "%s: sr_event_notif_send() failed for xpath %s",
529 static int frr_sr_read_cb(struct thread
*thread
)
531 sr_subscription_ctx_t
*sr_subscription
= THREAD_ARG(thread
);
532 int fd
= THREAD_FD(thread
);
535 ret
= sr_process_events(sr_subscription
, session
, NULL
);
536 if (ret
!= SR_ERR_OK
) {
537 flog_err(EC_LIB_LIBSYSREPO
, "%s: sr_fd_event_process(): %s",
538 __func__
, sr_strerror(ret
));
543 thread_add_read(master
, frr_sr_read_cb
, sr_subscription
, fd
, &thread
);
548 static void frr_sr_subscribe_config(struct yang_module
*module
)
552 DEBUGD(&nb_dbg_client_sysrepo
,
553 "sysrepo: subscribing for configuration changes made in the '%s' module",
556 ret
= sr_module_change_subscribe(
557 session
, module
->name
, NULL
, frr_sr_config_change_cb
, NULL
, 0,
558 SR_SUBSCR_DEFAULT
| SR_SUBSCR_ENABLED
| SR_SUBSCR_NO_THREAD
,
559 &module
->sr_subscription
);
560 if (ret
!= SR_ERR_OK
)
561 flog_err(EC_LIB_LIBSYSREPO
, "sr_module_change_subscribe(): %s",
565 static int frr_sr_subscribe_state(const struct lysc_node
*snode
, void *arg
)
567 struct yang_module
*module
= arg
;
568 struct nb_node
*nb_node
;
571 if (!CHECK_FLAG(snode
->flags
, LYS_CONFIG_R
))
572 return YANG_ITER_CONTINUE
;
573 /* We only need to subscribe to the root of the state subtrees. */
574 if (snode
->parent
&& CHECK_FLAG(snode
->parent
->flags
, LYS_CONFIG_R
))
575 return YANG_ITER_CONTINUE
;
577 nb_node
= snode
->priv
;
579 return YANG_ITER_CONTINUE
;
581 DEBUGD(&nb_dbg_client_sysrepo
, "sysrepo: providing data to '%s'",
584 ret
= sr_oper_get_items_subscribe(
585 session
, snode
->module
->name
, nb_node
->xpath
, frr_sr_state_cb
,
586 NULL
, SR_SUBSCR_CTX_REUSE
, &module
->sr_subscription
);
587 if (ret
!= SR_ERR_OK
)
588 flog_err(EC_LIB_LIBSYSREPO
, "sr_oper_get_items_subscribe(): %s",
591 return YANG_ITER_CONTINUE
;
594 static int frr_sr_subscribe_rpc(const struct lysc_node
*snode
, void *arg
)
596 struct yang_module
*module
= arg
;
597 struct nb_node
*nb_node
;
600 if (snode
->nodetype
!= LYS_RPC
)
601 return YANG_ITER_CONTINUE
;
603 nb_node
= snode
->priv
;
605 return YANG_ITER_CONTINUE
;
607 DEBUGD(&nb_dbg_client_sysrepo
, "sysrepo: providing RPC to '%s'",
610 ret
= sr_rpc_subscribe(session
, nb_node
->xpath
, frr_sr_config_rpc_cb
,
611 NULL
, 0, SR_SUBSCR_CTX_REUSE
,
612 &module
->sr_subscription
);
613 if (ret
!= SR_ERR_OK
)
614 flog_err(EC_LIB_LIBSYSREPO
, "sr_rpc_subscribe(): %s",
617 return YANG_ITER_CONTINUE
;
623 "[no] debug northbound client sysrepo",
626 "Northbound debugging\n"
627 "Northbound client\n"
630 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
631 bool no
= strmatch(argv
[0]->text
, "no");
633 DEBUG_MODE_SET(&nb_dbg_client_sysrepo
, mode
, !no
);
638 static int frr_sr_debug_config_write(struct vty
*vty
)
640 if (DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo
, DEBUG_MODE_CONF
))
641 vty_out(vty
, "debug northbound client sysrepo\n");
646 static int frr_sr_debug_set_all(uint32_t flags
, bool set
)
648 DEBUG_FLAGS_SET(&nb_dbg_client_sysrepo
, flags
, set
);
650 /* If all modes have been turned off, don't preserve options. */
651 if (!DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo
, DEBUG_MODE_ALL
))
652 DEBUG_CLEAR(&nb_dbg_client_sysrepo
);
657 static void frr_sr_cli_init(void)
659 hook_register(nb_client_debug_config_write
, frr_sr_debug_config_write
);
660 hook_register(nb_client_debug_set_all
, frr_sr_debug_set_all
);
662 install_element(ENABLE_NODE
, &debug_nb_sr_cmd
);
663 install_element(CONFIG_NODE
, &debug_nb_sr_cmd
);
666 /* FRR's Sysrepo initialization. */
667 static int frr_sr_init(void)
669 struct yang_module
*module
;
672 /* Connect to Sysrepo. */
673 ret
= sr_connect(SR_CONN_DEFAULT
, &connection
);
674 if (ret
!= SR_ERR_OK
) {
675 flog_err(EC_LIB_SYSREPO_INIT
, "%s: sr_connect(): %s", __func__
,
681 ret
= sr_session_start(connection
, SR_DS_RUNNING
, &session
);
682 if (ret
!= SR_ERR_OK
) {
683 flog_err(EC_LIB_SYSREPO_INIT
, "%s: sr_session_start(): %s",
684 __func__
, sr_strerror(ret
));
688 /* Perform subscriptions. */
689 RB_FOREACH (module
, yang_modules
, &yang_modules
) {
692 frr_sr_subscribe_config(module
);
693 yang_snodes_iterate(module
->info
, frr_sr_subscribe_state
, 0,
695 yang_snodes_iterate(module
->info
, frr_sr_subscribe_rpc
, 0,
698 /* Watch subscriptions. */
699 ret
= sr_get_event_pipe(module
->sr_subscription
, &event_pipe
);
700 if (ret
!= SR_ERR_OK
) {
701 flog_err(EC_LIB_SYSREPO_INIT
,
702 "%s: sr_get_event_pipe(): %s", __func__
,
706 thread_add_read(master
, frr_sr_read_cb
, module
->sr_subscription
,
707 event_pipe
, &module
->sr_thread
);
710 hook_register(nb_notification_send
, frr_sr_notification_send
);
720 static int frr_sr_finish(void)
722 struct yang_module
*module
;
724 RB_FOREACH (module
, yang_modules
, &yang_modules
) {
725 if (!module
->sr_subscription
)
727 sr_unsubscribe(module
->sr_subscription
);
728 THREAD_OFF(module
->sr_thread
);
732 sr_session_stop(session
);
734 sr_disconnect(connection
);
739 static int frr_sr_module_config_loaded(struct thread_master
*tm
)
743 if (frr_sr_init() < 0) {
744 flog_err(EC_LIB_SYSREPO_INIT
,
745 "failed to initialize the Sysrepo module");
749 hook_register(frr_fini
, frr_sr_finish
);
754 static int frr_sr_module_late_init(struct thread_master
*tm
)
761 static int frr_sr_module_init(void)
763 hook_register(frr_late_init
, frr_sr_module_late_init
);
764 hook_register(frr_config_post
, frr_sr_module_config_loaded
);
769 FRR_MODULE_SETUP(.name
= "frr_sysrepo", .version
= FRR_VERSION
,
770 .description
= "FRR sysrepo integration module",
771 .init
= frr_sr_module_init
,