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