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