]> git.proxmox.com Git - mirror_frr.git/blame - lib/northbound_sysrepo.c
lib: do not subscribe to config changes on a state data tree (confd plugin)
[mirror_frr.git] / lib / northbound_sysrepo.c
CommitLineData
a7ca2199
RW
1/*
2 * Copyright (C) 2018 NetDEF, Inc.
3 * Renato Westphal
4 *
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)
8 * any later version.
9 *
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
13 * more details.
14 *
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
18 */
19
20#include <zebra.h>
21
22#include "log.h"
23#include "lib_errors.h"
24#include "command.h"
25#include "memory.h"
26#include "libfrr.h"
27#include "version.h"
28#include "northbound.h"
29
30#include <sysrepo.h>
31#include <sysrepo/values.h>
32#include <sysrepo/xpath.h>
33
34DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module")
35
36static struct thread_master *master;
37static struct list *sysrepo_threads;
38static sr_session_ctx_t *session;
39static sr_conn_ctx_t *connection;
40
41static int frr_sr_read_cb(struct thread *thread);
42static int frr_sr_write_cb(struct thread *thread);
43static int frr_sr_finish(void);
44
45/* Convert FRR YANG data value to sysrepo YANG data value. */
46static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
47{
80243aef 48 struct nb_node *nb_node;
a7ca2199
RW
49 const struct lys_node *snode;
50 struct lys_node_container *scontainer;
51 struct lys_node_leaf *sleaf;
52 struct lys_node_leaflist *sleaflist;
53 LY_DATA_TYPE type;
54
55 sr_val_set_xpath(sr_data, frr_data->xpath);
56
80243aef
RW
57 nb_node = nb_node_find(frr_data->xpath);
58 if (!nb_node) {
59 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
60 "%s: unknown data path: %s", __func__,
61 frr_data->xpath);
62 return -1;
63 }
64
65 snode = nb_node->snode;
a7ca2199
RW
66 switch (snode->nodetype) {
67 case LYS_CONTAINER:
68 scontainer = (struct lys_node_container *)snode;
69 if (!scontainer->presence)
70 return -1;
71 sr_data->type = SR_CONTAINER_PRESENCE_T;
72 return 0;
73 case LYS_LIST:
74 sr_data->type = SR_LIST_T;
75 return 0;
76 case LYS_LEAF:
77 sleaf = (struct lys_node_leaf *)snode;
78 type = sleaf->type.base;
79 break;
80 case LYS_LEAFLIST:
81 sleaflist = (struct lys_node_leaflist *)snode;
82 type = sleaflist->type.base;
83 break;
84 default:
85 return -1;
86 }
87
88 switch (type) {
89 case LY_TYPE_BINARY:
90 sr_val_set_str_data(sr_data, SR_BINARY_T, frr_data->value);
91 break;
92 case LY_TYPE_BITS:
93 sr_val_set_str_data(sr_data, SR_BITS_T, frr_data->value);
94 break;
95 case LY_TYPE_BOOL:
96 sr_data->type = SR_BOOL_T;
97 sr_data->data.bool_val = yang_str2bool(frr_data->value);
98 break;
99 case LY_TYPE_DEC64:
100 sr_data->type = SR_DECIMAL64_T;
101 sr_data->data.decimal64_val =
102 yang_str2dec64(frr_data->xpath, frr_data->value);
103 break;
104 case LY_TYPE_EMPTY:
105 sr_data->type = SR_LEAF_EMPTY_T;
106 break;
107 case LY_TYPE_ENUM:
108 sr_val_set_str_data(sr_data, SR_ENUM_T, frr_data->value);
109 break;
110 case LY_TYPE_IDENT:
111 sr_val_set_str_data(sr_data, SR_IDENTITYREF_T, frr_data->value);
112 break;
113 case LY_TYPE_INST:
114 sr_val_set_str_data(sr_data, SR_INSTANCEID_T, frr_data->value);
115 break;
116 case LY_TYPE_INT8:
117 sr_data->type = SR_INT8_T;
118 sr_data->data.int8_val = yang_str2int8(frr_data->value);
119 break;
120 case LY_TYPE_INT16:
121 sr_data->type = SR_INT16_T;
122 sr_data->data.int16_val = yang_str2int16(frr_data->value);
123 break;
124 case LY_TYPE_INT32:
125 sr_data->type = SR_INT32_T;
126 sr_data->data.int32_val = yang_str2int32(frr_data->value);
127 break;
128 case LY_TYPE_INT64:
129 sr_data->type = SR_INT64_T;
130 sr_data->data.int64_val = yang_str2int64(frr_data->value);
131 break;
132 case LY_TYPE_STRING:
133 sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
134 break;
135 case LY_TYPE_UINT8:
136 sr_data->type = SR_UINT8_T;
137 sr_data->data.uint8_val = yang_str2uint8(frr_data->value);
138 break;
139 case LY_TYPE_UINT16:
140 sr_data->type = SR_UINT16_T;
141 sr_data->data.uint16_val = yang_str2uint16(frr_data->value);
142 break;
143 case LY_TYPE_UINT32:
144 sr_data->type = SR_UINT32_T;
145 sr_data->data.uint32_val = yang_str2uint32(frr_data->value);
146 break;
147 case LY_TYPE_UINT64:
148 sr_data->type = SR_UINT64_T;
149 sr_data->data.uint64_val = yang_str2uint64(frr_data->value);
150 break;
151 default:
152 return -1;
153 }
154
155 return 0;
156}
157
158static int frr_sr_process_change(struct nb_config *candidate,
159 sr_change_oper_t sr_op, sr_val_t *sr_old_val,
160 sr_val_t *sr_new_val)
161{
162 struct nb_node *nb_node;
163 enum nb_operation nb_op;
164 sr_val_t *sr_data;
165 const char *xpath;
166 char value_str[YANG_VALUE_MAXLEN];
167 struct yang_data *data;
168 int ret;
169
170 sr_data = sr_new_val ? sr_new_val : sr_old_val;
171 assert(sr_data);
172
173 xpath = sr_data->xpath;
174
175 /* Non-presence container - nothing to do. */
176 if (sr_data->type == SR_CONTAINER_T)
177 return NB_OK;
178
179 nb_node = nb_node_find(xpath);
180 if (!nb_node) {
181 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
182 "%s: unknown data path: %s", __func__, xpath);
183 return NB_ERR;
184 }
185
186 /* Map operation values. */
187 switch (sr_op) {
188 case SR_OP_CREATED:
189 case SR_OP_MODIFIED:
190 if (nb_operation_is_valid(NB_OP_CREATE, nb_node->snode))
191 nb_op = NB_OP_CREATE;
192 else if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode)) {
193 nb_op = NB_OP_MODIFY;
194 } else
195 /* Ignore list keys modifications. */
196 return NB_OK;
197 break;
198 case SR_OP_DELETED:
199 /*
200 * When a list is deleted or one of its keys is changed, we are
201 * notified about the removal of all of its leafs, even the ones
202 * that are non-optional. We need to ignore these notifications.
203 */
204 if (!nb_operation_is_valid(NB_OP_DELETE, nb_node->snode))
205 return NB_OK;
206
207 nb_op = NB_OP_DELETE;
208 break;
209 case SR_OP_MOVED:
210 nb_op = NB_OP_MOVE;
211 break;
212 default:
213 flog_err(EC_LIB_DEVELOPMENT,
214 "%s: unexpected operation %u [xpath %s]", __func__,
215 sr_op, xpath);
216 return NB_ERR;
217 }
218
219 sr_val_to_buff(sr_data, value_str, sizeof(value_str));
220 data = yang_data_new(xpath, value_str);
221
222 ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data);
223 yang_data_free(data);
224 if (ret != NB_OK) {
225 flog_warn(
226 EC_LIB_NB_CANDIDATE_EDIT_ERROR,
227 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
228 __func__, nb_operation_name(nb_op), xpath);
229 return NB_ERR;
230 }
231
232 return NB_OK;
233}
234
235/* Callback for changes in the running configuration. */
236static int frr_sr_config_change_cb(sr_session_ctx_t *session,
237 const char *module_name,
238 sr_notif_event_t sr_ev, void *private_ctx)
239{
240 sr_change_iter_t *it;
241 int ret;
242 sr_change_oper_t sr_op;
243 sr_val_t *sr_old_val, *sr_new_val;
244 char xpath[XPATH_MAXLEN];
245 struct nb_config *candidate;
246
247 /*
248 * Ignore SR_EV_ABORT and SR_EV_APPLY. We'll leverage the northbound
249 * layer itself to abort or apply the configuration changes when a
250 * transaction is created.
251 */
252 if (sr_ev != SR_EV_ENABLED && sr_ev != SR_EV_VERIFY)
253 return SR_ERR_OK;
254
255 snprintf(xpath, sizeof(xpath), "/%s:*", module_name);
256 ret = sr_get_changes_iter(session, xpath, &it);
257 if (ret != SR_ERR_OK) {
258 flog_err(EC_LIB_LIBSYSREPO,
259 "%s: sr_get_changes_iter() failed for xpath %s",
260 __func__, xpath);
261 return ret;
262 }
263
264 candidate = nb_config_dup(running_config);
265
266 while ((ret = sr_get_change_next(session, it, &sr_op, &sr_old_val,
267 &sr_new_val))
268 == SR_ERR_OK) {
269 ret = frr_sr_process_change(candidate, sr_op, sr_old_val,
270 sr_new_val);
271 sr_free_val(sr_old_val);
272 sr_free_val(sr_new_val);
273 if (ret != NB_OK)
274 break;
275 }
276
277 sr_free_change_iter(it);
278 if (ret != NB_OK && ret != SR_ERR_NOT_FOUND) {
279 nb_config_free(candidate);
280 return SR_ERR_INTERNAL;
281 }
282
283 /* Commit changes. */
284 ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, true, NULL,
285 NULL);
286 nb_config_free(candidate);
287
288 /* Map northbound return code to sysrepo return code. */
289 switch (ret) {
290 case NB_OK:
291 case NB_ERR_NO_CHANGES:
292 return SR_ERR_OK;
293 case NB_ERR_LOCKED:
294 return SR_ERR_LOCKED;
295 case NB_ERR_RESOURCE:
296 return SR_ERR_NOMEM;
297 default:
298 return SR_ERR_VALIDATION_FAILED;
299 }
300}
301
302static void frr_sr_state_get_elem(struct list *elements,
303 struct nb_node *nb_node,
304 const void *list_entry, const char *xpath)
305{
306 struct yang_data *data;
307
308 data = nb_node->cbs.get_elem(xpath, list_entry);
309 if (data)
310 listnode_add(elements, data);
311}
312
313static void frr_sr_state_cb_container(struct list *elements, const char *xpath,
314 const struct lys_node *snode)
315{
316 struct lys_node *child;
317
318 LY_TREE_FOR (snode->child, child) {
319 struct nb_node *nb_node = child->priv;
320 char xpath_child[XPATH_MAXLEN];
321
322 if (!nb_operation_is_valid(NB_OP_GET_ELEM, child))
323 continue;
324
325 snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath,
326 child->name);
327
328 frr_sr_state_get_elem(elements, nb_node, NULL, xpath_child);
329 }
330}
331
332static void frr_sr_state_cb_list_entry(struct list *elements,
333 const char *xpath_list,
334 const void *list_entry,
335 struct lys_node *child)
336{
337 struct nb_node *nb_node = child->priv;
338 struct lys_node_leaf *sleaf;
339 char xpath_child[XPATH_MAXLEN];
340
341 /* Sysrepo doesn't want to know about list keys. */
342 switch (child->nodetype) {
343 case LYS_LEAF:
344 sleaf = (struct lys_node_leaf *)child;
345 if (lys_is_key(sleaf, NULL))
346 return;
347 break;
348 case LYS_LEAFLIST:
349 break;
350 default:
351 return;
352 }
353
354 if (!nb_operation_is_valid(NB_OP_GET_ELEM, child))
355 return;
356
357 snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath_list,
358 child->name);
359
360 frr_sr_state_get_elem(elements, nb_node, list_entry, xpath_child);
361}
362
363static void frr_sr_state_cb_list(struct list *elements, const char *xpath,
364 const struct lys_node *snode)
365{
366 struct nb_node *nb_node = snode->priv;
367 struct lys_node_list *slist = (struct lys_node_list *)snode;
368 const void *next;
369
370 for (next = nb_node->cbs.get_next(xpath, NULL); next;
371 next = nb_node->cbs.get_next(xpath, next)) {
372 struct yang_list_keys keys;
373 const void *list_entry;
374 char xpath_list[XPATH_MAXLEN];
375 struct lys_node *child;
376
377 /* Get the list keys. */
378 if (nb_node->cbs.get_keys(next, &keys) != NB_OK) {
379 flog_warn(EC_LIB_NB_CB_STATE,
380 "%s: failed to get list keys", __func__);
381 continue;
382 }
383
384 /* Get list item. */
385 list_entry = nb_node->cbs.lookup_entry(&keys);
386 if (!list_entry) {
387 flog_warn(EC_LIB_NB_CB_STATE,
388 "%s: failed to lookup list entry", __func__);
389 continue;
390 }
391
392 /* Append list keys to the XPath. */
393 strlcpy(xpath_list, xpath, sizeof(xpath_list));
394 for (unsigned int i = 0; i < keys.num; i++) {
395 snprintf(xpath_list + strlen(xpath_list),
396 sizeof(xpath_list) - strlen(xpath_list),
397 "[%s='%s']", slist->keys[i]->name,
80243aef 398 keys.key[i]);
a7ca2199
RW
399 }
400
401 /* Loop through list entries. */
402 LY_TREE_FOR (snode->child, child) {
403 frr_sr_state_cb_list_entry(elements, xpath_list,
404 list_entry, child);
405 }
406 }
407}
408
409/* Callback for state retrieval. */
410static int frr_sr_state_cb(const char *xpath, sr_val_t **values,
411 size_t *values_cnt, uint64_t request_id,
412 void *private_ctx)
413{
414 struct list *elements;
415 struct yang_data *data;
416 const struct lys_node *snode;
417 struct listnode *node;
418 sr_val_t *v;
419 int ret, count, i = 0;
420
421 /* Find schema node. */
422 snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
423
424 elements = yang_data_list_new();
425
426 switch (snode->nodetype) {
427 case LYS_CONTAINER:
428 frr_sr_state_cb_container(elements, xpath, snode);
429 break;
430 case LYS_LIST:
431 frr_sr_state_cb_list(elements, xpath, snode);
432 break;
433 default:
434 break;
435 }
436 if (list_isempty(elements))
437 goto exit;
438
439 count = listcount(elements);
440 ret = sr_new_values(count, &v);
441 if (ret != SR_ERR_OK) {
442 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s", __func__,
443 sr_strerror(ret));
444 goto exit;
445 }
446
447 for (ALL_LIST_ELEMENTS_RO(elements, node, data)) {
448 if (yang_data_frr2sr(data, &v[i++]) != 0) {
449 flog_err(EC_LIB_SYSREPO_DATA_CONVERT,
450 "%s: failed to convert data to sysrepo format",
451 __func__);
452 }
453 }
454
455 *values = v;
456 *values_cnt = count;
457
458 list_delete(&elements);
459
460 return SR_ERR_OK;
461
462exit:
463 list_delete(&elements);
464 *values = NULL;
465 values_cnt = 0;
466
467 return SR_ERR_OK;
468}
469
470static int frr_sr_config_rpc_cb(const char *xpath, const sr_val_t *sr_input,
471 const size_t input_cnt, sr_val_t **sr_output,
472 size_t *sr_output_cnt, void *private_ctx)
473{
474 struct nb_node *nb_node;
475 struct list *input;
476 struct list *output;
477 struct yang_data *data;
478 size_t cb_output_cnt;
479 int ret = SR_ERR_OK;
480
481 nb_node = nb_node_find(xpath);
482 if (!nb_node) {
483 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
484 "%s: unknown data path: %s", __func__, xpath);
485 return SR_ERR_INTERNAL;
486 }
487
488 input = yang_data_list_new();
489 output = yang_data_list_new();
490
491 /* Process input. */
492 for (size_t i = 0; i < input_cnt; i++) {
493 char value_str[YANG_VALUE_MAXLEN];
494
495 sr_val_to_buff(&sr_input[i], value_str, sizeof(value_str));
496
497 data = yang_data_new(xpath, value_str);
498 listnode_add(input, data);
499 }
500
501 /* Execute callback registered for this XPath. */
502 if (nb_node->cbs.rpc(xpath, input, output) != NB_OK) {
503 flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
504 __func__, xpath);
505 ret = SR_ERR_OPERATION_FAILED;
506 goto exit;
507 }
508
509 /* Process output. */
510 if (listcount(output) > 0) {
511 sr_val_t *values = NULL;
512 struct listnode *node;
513 int i = 0;
514
515 cb_output_cnt = listcount(output);
516 ret = sr_new_values(cb_output_cnt, &values);
517 if (ret != SR_ERR_OK) {
518 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s",
519 __func__, sr_strerror(ret));
520 goto exit;
521 }
522
523 for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
524 if (yang_data_frr2sr(data, &values[i++]) != 0) {
525 flog_err(
526 EC_LIB_SYSREPO_DATA_CONVERT,
527 "%s: failed to convert data to Sysrepo format",
528 __func__);
529 ret = SR_ERR_INTERNAL;
530 sr_free_values(values, cb_output_cnt);
531 goto exit;
532 }
533 }
534
535 *sr_output = values;
536 *sr_output_cnt = cb_output_cnt;
537 }
538
539exit:
540 /* Release memory. */
541 list_delete(&input);
542 list_delete(&output);
543
544 return ret;
545}
546
547static int frr_sr_notification_send(const char *xpath, struct list *arguments)
548{
549 sr_val_t *values = NULL;
550 size_t values_cnt = 0;
551 int ret;
552
553 if (arguments && listcount(arguments) > 0) {
554 struct yang_data *data;
555 struct listnode *node;
556 int i = 0;
557
558 values_cnt = listcount(arguments);
559 ret = sr_new_values(values_cnt, &values);
560 if (ret != SR_ERR_OK) {
561 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s",
562 __func__, sr_strerror(ret));
563 return NB_ERR;
564 }
565
566 for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) {
567 if (yang_data_frr2sr(data, &values[i++]) != 0) {
568 flog_err(
569 EC_LIB_SYSREPO_DATA_CONVERT,
570 "%s: failed to convert data to sysrepo format",
571 __func__);
572 sr_free_values(values, values_cnt);
573 return NB_ERR;
574 }
575 }
576 }
577
578 ret = sr_event_notif_send(session, xpath, values, values_cnt,
579 SR_EV_NOTIF_DEFAULT);
580 if (ret != SR_ERR_OK) {
581 flog_err(EC_LIB_LIBSYSREPO,
582 "%s: sr_event_notif_send() failed for xpath %s",
583 __func__, xpath);
584 return NB_ERR;
585 }
586
587 return NB_OK;
588}
589
590/* Code to integrate the sysrepo client into FRR main event loop. */
591struct sysrepo_thread {
592 struct thread *thread;
593 sr_fd_event_t event;
594 int fd;
595};
596
597static struct sysrepo_thread *frr_sr_fd_lookup(sr_fd_event_t event, int fd)
598{
599 struct sysrepo_thread *sr_thread;
600 struct listnode *node;
601
602 for (ALL_LIST_ELEMENTS_RO(sysrepo_threads, node, sr_thread)) {
603 if (sr_thread->event == event && sr_thread->fd == fd)
604 return sr_thread;
605 }
606
607 return NULL;
608}
609
610static void frr_sr_fd_add(int event, int fd)
611{
612 struct sysrepo_thread *sr_thread;
613
614 if (frr_sr_fd_lookup(event, fd) != NULL)
615 return;
616
617 sr_thread = XCALLOC(MTYPE_SYSREPO, sizeof(*sr_thread));
618 sr_thread->event = event;
619 sr_thread->fd = fd;
620 listnode_add(sysrepo_threads, sr_thread);
621
622 switch (event) {
623 case SR_FD_INPUT_READY:
624 thread_add_read(master, frr_sr_read_cb, NULL, fd,
625 &sr_thread->thread);
626 break;
627 case SR_FD_OUTPUT_READY:
628 thread_add_write(master, frr_sr_write_cb, NULL, fd,
629 &sr_thread->thread);
630 break;
631 default:
632 return;
633 }
634}
635
636static void frr_sr_fd_free(struct sysrepo_thread *sr_thread)
637{
638 THREAD_OFF(sr_thread->thread);
639 XFREE(MTYPE_SYSREPO, sr_thread);
640}
641
642static void frr_sr_fd_del(int event, int fd)
643{
644 struct sysrepo_thread *sr_thread;
645
646 sr_thread = frr_sr_fd_lookup(event, fd);
647 if (!sr_thread)
648 return;
649
650 listnode_delete(sysrepo_threads, sr_thread);
651 frr_sr_fd_free(sr_thread);
652}
653
654static void frr_sr_fd_update(sr_fd_change_t *fd_change_set,
655 size_t fd_change_set_cnt)
656{
657 for (size_t i = 0; i < fd_change_set_cnt; i++) {
658 int fd = fd_change_set[i].fd;
659 int event = fd_change_set[i].events;
660
661 if (event != SR_FD_INPUT_READY && event != SR_FD_OUTPUT_READY)
662 continue;
663
664 switch (fd_change_set[i].action) {
665 case SR_FD_START_WATCHING:
666 frr_sr_fd_add(event, fd);
667 break;
668 case SR_FD_STOP_WATCHING:
669 frr_sr_fd_del(event, fd);
670 break;
671 default:
672 break;
673 }
674 }
675}
676
677static int frr_sr_read_cb(struct thread *thread)
678{
679 int fd = THREAD_FD(thread);
680 sr_fd_change_t *fd_change_set = NULL;
681 size_t fd_change_set_cnt = 0;
682 int ret;
683
684 ret = sr_fd_event_process(fd, SR_FD_INPUT_READY, &fd_change_set,
685 &fd_change_set_cnt);
686 if (ret != SR_ERR_OK) {
687 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s",
688 __func__, sr_strerror(ret));
689 return -1;
690 }
691
692 thread = NULL;
693 thread_add_read(master, frr_sr_read_cb, NULL, fd, &thread);
694
695 frr_sr_fd_update(fd_change_set, fd_change_set_cnt);
696 free(fd_change_set);
697
698 return 0;
699}
700
701static int frr_sr_write_cb(struct thread *thread)
702{
703 int fd = THREAD_FD(thread);
704 sr_fd_change_t *fd_change_set = NULL;
705 size_t fd_change_set_cnt = 0;
706 int ret;
707
708 ret = sr_fd_event_process(fd, SR_FD_OUTPUT_READY, &fd_change_set,
709 &fd_change_set_cnt);
710 if (ret != SR_ERR_OK) {
711 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s",
712 __func__, sr_strerror(ret));
713 return -1;
714 }
715
716 frr_sr_fd_update(fd_change_set, fd_change_set_cnt);
717 free(fd_change_set);
718
719 return 0;
720}
721
722static void frr_sr_subscribe_config(struct yang_module *module)
723{
724 int ret;
725
726 ret = sr_module_change_subscribe(
727 session, module->name, frr_sr_config_change_cb, NULL, 0,
728 SR_SUBSCR_DEFAULT | SR_SUBSCR_EV_ENABLED,
729 &module->sr_subscription);
730 if (ret != SR_ERR_OK)
731 flog_err(EC_LIB_LIBSYSREPO, "sr_module_change_subscribe(): %s",
732 sr_strerror(ret));
733}
734
e0ccfad2 735static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg)
a7ca2199 736{
e0ccfad2 737 struct yang_module *module = arg;
a7ca2199
RW
738 struct nb_node *nb_node;
739 int ret;
740
db452508 741 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
e0ccfad2 742 return YANG_ITER_CONTINUE;
a7ca2199 743 /* We only need to subscribe to the root of the state subtrees. */
db452508 744 if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
e0ccfad2 745 return YANG_ITER_CONTINUE;
a7ca2199
RW
746
747 nb_node = snode->priv;
748 if (debug_northbound)
749 zlog_debug("%s: providing data to '%s'", __func__,
750 nb_node->xpath);
751
752 ret = sr_dp_get_items_subscribe(
753 session, nb_node->xpath, frr_sr_state_cb, NULL,
754 SR_SUBSCR_CTX_REUSE, &module->sr_subscription);
755 if (ret != SR_ERR_OK)
756 flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s",
757 sr_strerror(ret));
e0ccfad2
RW
758
759 return YANG_ITER_CONTINUE;
a7ca2199
RW
760}
761
e0ccfad2 762static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg)
a7ca2199 763{
e0ccfad2 764 struct yang_module *module = arg;
a7ca2199
RW
765 struct nb_node *nb_node;
766 int ret;
767
768 if (snode->nodetype != LYS_RPC)
e0ccfad2 769 return YANG_ITER_CONTINUE;
a7ca2199
RW
770
771 nb_node = snode->priv;
772 if (debug_northbound)
773 zlog_debug("%s: providing RPC to '%s'", __func__,
774 nb_node->xpath);
775
776 ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb,
777 NULL, SR_SUBSCR_CTX_REUSE,
778 &module->sr_subscription);
779 if (ret != SR_ERR_OK)
780 flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
781 sr_strerror(ret));
e0ccfad2
RW
782
783 return YANG_ITER_CONTINUE;
a7ca2199
RW
784}
785
e0ccfad2 786static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg)
a7ca2199 787{
e0ccfad2 788 struct yang_module *module = arg;
a7ca2199
RW
789 struct nb_node *nb_node;
790 int ret;
791
792 if (snode->nodetype != LYS_ACTION)
e0ccfad2 793 return YANG_ITER_CONTINUE;
a7ca2199
RW
794
795 nb_node = snode->priv;
796 if (debug_northbound)
797 zlog_debug("%s: providing action to '%s'", __func__,
798 nb_node->xpath);
799
800 ret = sr_action_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb,
801 NULL, SR_SUBSCR_CTX_REUSE,
802 &module->sr_subscription);
803 if (ret != SR_ERR_OK)
804 flog_err(EC_LIB_LIBSYSREPO, "sr_action_subscribe(): %s",
805 sr_strerror(ret));
e0ccfad2
RW
806
807 return YANG_ITER_CONTINUE;
a7ca2199
RW
808}
809
810/* FRR's Sysrepo initialization. */
811static int frr_sr_init(const char *program_name)
812{
813 struct yang_module *module;
814 int sysrepo_fd, ret;
815
816 sysrepo_threads = list_new();
817
818 ret = sr_fd_watcher_init(&sysrepo_fd, NULL);
819 if (ret != SR_ERR_OK) {
820 flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_fd_watcher_init(): %s",
821 __func__, sr_strerror(ret));
822 goto cleanup;
823 }
824
825 /* Connect to Sysrepo. */
826 ret = sr_connect(program_name, SR_CONN_DEFAULT, &connection);
827 if (ret != SR_ERR_OK) {
828 flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_connect(): %s", __func__,
829 sr_strerror(ret));
830 goto cleanup;
831 }
832
833 /* Start session. */
834 ret = sr_session_start(connection, SR_DS_RUNNING, SR_SESS_DEFAULT,
835 &session);
836 if (ret != SR_ERR_OK) {
837 flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_session_start(): %s",
838 __func__, sr_strerror(ret));
839 goto cleanup;
840 }
841
842 /* Perform subscriptions. */
843 RB_FOREACH (module, yang_modules, &yang_modules) {
844 frr_sr_subscribe_config(module);
e0ccfad2
RW
845 yang_snodes_iterate_module(module->info, frr_sr_subscribe_state,
846 0, module);
847 yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc,
848 0, module);
849 yang_snodes_iterate_module(module->info,
850 frr_sr_subscribe_action, 0, module);
a7ca2199
RW
851 }
852
853 hook_register(nb_notification_send, frr_sr_notification_send);
854
855 frr_sr_fd_add(SR_FD_INPUT_READY, sysrepo_fd);
856
857 return 0;
858
859cleanup:
860 frr_sr_finish();
861
862 return -1;
863}
864
865static int frr_sr_finish(void)
866{
867 struct yang_module *module;
868
869 RB_FOREACH (module, yang_modules, &yang_modules) {
870 if (!module->sr_subscription)
871 continue;
872 sr_unsubscribe(session, module->sr_subscription);
873 }
874
875 if (session)
876 sr_session_stop(session);
877 if (connection)
878 sr_disconnect(connection);
879
880 sysrepo_threads->del = (void (*)(void *))frr_sr_fd_free;
881 list_delete(&sysrepo_threads);
882 sr_fd_watcher_cleanup();
883
884 return 0;
885}
886
887static int frr_sr_module_late_init(struct thread_master *tm)
888{
889 master = tm;
890
891 if (frr_sr_init(frr_get_progname()) < 0) {
892 flog_err(EC_LIB_SYSREPO_INIT,
893 "failed to initialize the Sysrepo module");
894 return -1;
895 }
896
897 hook_register(frr_fini, frr_sr_finish);
898
899 return 0;
900}
901
902static int frr_sr_module_init(void)
903{
904 hook_register(frr_late_init, frr_sr_module_late_init);
905
906 return 0;
907}
908
909FRR_MODULE_SETUP(.name = "frr_sysrepo", .version = FRR_VERSION,
910 .description = "FRR sysrepo integration module",
911 .init = frr_sr_module_init, )