]> git.proxmox.com Git - mirror_frr.git/blame - lib/northbound_sysrepo.c
tools: fix frr-reload.py daemon option
[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"
9eb2c0a1 25#include "debug.h"
a7ca2199
RW
26#include "memory.h"
27#include "libfrr.h"
28#include "version.h"
29#include "northbound.h"
30
31#include <sysrepo.h>
32#include <sysrepo/values.h>
33#include <sysrepo/xpath.h>
34
35DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module")
36
9eb2c0a1
RW
37static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"};
38
a7ca2199
RW
39static struct thread_master *master;
40static struct list *sysrepo_threads;
41static sr_session_ctx_t *session;
42static sr_conn_ctx_t *connection;
88a7d121 43static struct nb_transaction *transaction;
a7ca2199
RW
44
45static int frr_sr_read_cb(struct thread *thread);
46static int frr_sr_write_cb(struct thread *thread);
47static int frr_sr_finish(void);
48
49/* Convert FRR YANG data value to sysrepo YANG data value. */
50static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
51{
80243aef 52 struct nb_node *nb_node;
a7ca2199
RW
53 const struct lys_node *snode;
54 struct lys_node_container *scontainer;
55 struct lys_node_leaf *sleaf;
56 struct lys_node_leaflist *sleaflist;
57 LY_DATA_TYPE type;
58
59 sr_val_set_xpath(sr_data, frr_data->xpath);
60
80243aef
RW
61 nb_node = nb_node_find(frr_data->xpath);
62 if (!nb_node) {
63 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
64 "%s: unknown data path: %s", __func__,
65 frr_data->xpath);
66 return -1;
67 }
68
69 snode = nb_node->snode;
a7ca2199
RW
70 switch (snode->nodetype) {
71 case LYS_CONTAINER:
72 scontainer = (struct lys_node_container *)snode;
73 if (!scontainer->presence)
74 return -1;
75 sr_data->type = SR_CONTAINER_PRESENCE_T;
76 return 0;
77 case LYS_LIST:
78 sr_data->type = SR_LIST_T;
79 return 0;
80 case LYS_LEAF:
81 sleaf = (struct lys_node_leaf *)snode;
82 type = sleaf->type.base;
83 break;
84 case LYS_LEAFLIST:
85 sleaflist = (struct lys_node_leaflist *)snode;
86 type = sleaflist->type.base;
87 break;
88 default:
89 return -1;
90 }
91
92 switch (type) {
93 case LY_TYPE_BINARY:
94 sr_val_set_str_data(sr_data, SR_BINARY_T, frr_data->value);
95 break;
96 case LY_TYPE_BITS:
97 sr_val_set_str_data(sr_data, SR_BITS_T, frr_data->value);
98 break;
99 case LY_TYPE_BOOL:
100 sr_data->type = SR_BOOL_T;
101 sr_data->data.bool_val = yang_str2bool(frr_data->value);
102 break;
103 case LY_TYPE_DEC64:
104 sr_data->type = SR_DECIMAL64_T;
105 sr_data->data.decimal64_val =
106 yang_str2dec64(frr_data->xpath, frr_data->value);
107 break;
108 case LY_TYPE_EMPTY:
109 sr_data->type = SR_LEAF_EMPTY_T;
110 break;
111 case LY_TYPE_ENUM:
112 sr_val_set_str_data(sr_data, SR_ENUM_T, frr_data->value);
113 break;
114 case LY_TYPE_IDENT:
115 sr_val_set_str_data(sr_data, SR_IDENTITYREF_T, frr_data->value);
116 break;
117 case LY_TYPE_INST:
118 sr_val_set_str_data(sr_data, SR_INSTANCEID_T, frr_data->value);
119 break;
120 case LY_TYPE_INT8:
121 sr_data->type = SR_INT8_T;
122 sr_data->data.int8_val = yang_str2int8(frr_data->value);
123 break;
124 case LY_TYPE_INT16:
125 sr_data->type = SR_INT16_T;
126 sr_data->data.int16_val = yang_str2int16(frr_data->value);
127 break;
128 case LY_TYPE_INT32:
129 sr_data->type = SR_INT32_T;
130 sr_data->data.int32_val = yang_str2int32(frr_data->value);
131 break;
132 case LY_TYPE_INT64:
133 sr_data->type = SR_INT64_T;
134 sr_data->data.int64_val = yang_str2int64(frr_data->value);
135 break;
136 case LY_TYPE_STRING:
137 sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
138 break;
139 case LY_TYPE_UINT8:
140 sr_data->type = SR_UINT8_T;
141 sr_data->data.uint8_val = yang_str2uint8(frr_data->value);
142 break;
143 case LY_TYPE_UINT16:
144 sr_data->type = SR_UINT16_T;
145 sr_data->data.uint16_val = yang_str2uint16(frr_data->value);
146 break;
147 case LY_TYPE_UINT32:
148 sr_data->type = SR_UINT32_T;
149 sr_data->data.uint32_val = yang_str2uint32(frr_data->value);
150 break;
151 case LY_TYPE_UINT64:
152 sr_data->type = SR_UINT64_T;
153 sr_data->data.uint64_val = yang_str2uint64(frr_data->value);
154 break;
155 default:
156 return -1;
157 }
158
159 return 0;
160}
161
162static 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)
165{
166 struct nb_node *nb_node;
167 enum nb_operation nb_op;
168 sr_val_t *sr_data;
169 const char *xpath;
170 char value_str[YANG_VALUE_MAXLEN];
171 struct yang_data *data;
172 int ret;
173
174 sr_data = sr_new_val ? sr_new_val : sr_old_val;
175 assert(sr_data);
176
177 xpath = sr_data->xpath;
178
179 /* Non-presence container - nothing to do. */
180 if (sr_data->type == SR_CONTAINER_T)
181 return NB_OK;
182
183 nb_node = nb_node_find(xpath);
184 if (!nb_node) {
185 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
186 "%s: unknown data path: %s", __func__, xpath);
187 return NB_ERR;
188 }
189
190 /* Map operation values. */
191 switch (sr_op) {
192 case SR_OP_CREATED:
193 case SR_OP_MODIFIED:
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;
198 } else
199 /* Ignore list keys modifications. */
200 return NB_OK;
201 break;
202 case SR_OP_DELETED:
203 /*
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.
207 */
95ce849b 208 if (!nb_operation_is_valid(NB_OP_DESTROY, nb_node->snode))
a7ca2199
RW
209 return NB_OK;
210
95ce849b 211 nb_op = NB_OP_DESTROY;
a7ca2199
RW
212 break;
213 case SR_OP_MOVED:
214 nb_op = NB_OP_MOVE;
215 break;
216 default:
217 flog_err(EC_LIB_DEVELOPMENT,
218 "%s: unexpected operation %u [xpath %s]", __func__,
219 sr_op, xpath);
220 return NB_ERR;
221 }
222
223 sr_val_to_buff(sr_data, value_str, sizeof(value_str));
224 data = yang_data_new(xpath, value_str);
225
226 ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data);
227 yang_data_free(data);
228 if (ret != NB_OK) {
229 flog_warn(
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);
233 return NB_ERR;
234 }
235
236 return NB_OK;
237}
238
88a7d121
RW
239static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session,
240 const char *module_name,
241 bool startup_config)
a7ca2199
RW
242{
243 sr_change_iter_t *it;
244 int ret;
245 sr_change_oper_t sr_op;
246 sr_val_t *sr_old_val, *sr_new_val;
247 char xpath[XPATH_MAXLEN];
13d6b9c1 248 struct nb_context context = {};
a7ca2199 249 struct nb_config *candidate;
df5eda3d 250 char errmsg[BUFSIZ] = {0};
a7ca2199 251
a7ca2199
RW
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",
257 __func__, xpath);
258 return ret;
259 }
260
8685be73 261 candidate = nb_config_dup(running_config);
a7ca2199
RW
262
263 while ((ret = sr_get_change_next(session, it, &sr_op, &sr_old_val,
264 &sr_new_val))
265 == SR_ERR_OK) {
266 ret = frr_sr_process_change(candidate, sr_op, sr_old_val,
267 sr_new_val);
268 sr_free_val(sr_old_val);
269 sr_free_val(sr_new_val);
270 if (ret != NB_OK)
271 break;
272 }
273
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;
278 }
279
88a7d121 280 transaction = NULL;
13d6b9c1 281 context.client = NB_CLIENT_SYSREPO;
88a7d121
RW
282 if (startup_config) {
283 /*
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.
287 */
df5eda3d
RW
288 ret = nb_candidate_commit(&context, candidate, true, NULL, NULL,
289 errmsg, sizeof(errmsg));
290 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
291 flog_warn(
292 EC_LIB_LIBSYSREPO,
293 "%s: failed to apply startup configuration: %s (%s)",
294 __func__, nb_err_name(ret), errmsg);
88a7d121
RW
295 } else {
296 /*
297 * Validate the configuration changes and allocate all resources
298 * required to apply them.
299 */
13d6b9c1 300 ret = nb_candidate_commit_prepare(&context, candidate, NULL,
df5eda3d
RW
301 &transaction, errmsg,
302 sizeof(errmsg));
303 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
304 flog_warn(
305 EC_LIB_LIBSYSREPO,
306 "%s: failed to prepare configuration transaction: %s (%s)",
307 __func__, nb_err_name(ret), errmsg);
88a7d121 308 }
a7ca2199
RW
309
310 /* Map northbound return code to sysrepo return code. */
311 switch (ret) {
312 case NB_OK:
88a7d121 313 return SR_ERR_OK;
a7ca2199 314 case NB_ERR_NO_CHANGES:
88a7d121 315 nb_config_free(candidate);
a7ca2199
RW
316 return SR_ERR_OK;
317 case NB_ERR_LOCKED:
318 return SR_ERR_LOCKED;
319 case NB_ERR_RESOURCE:
320 return SR_ERR_NOMEM;
321 default:
322 return SR_ERR_VALIDATION_FAILED;
323 }
324}
325
88a7d121
RW
326static int frr_sr_config_change_cb_apply(sr_session_ctx_t *session,
327 const char *module_name)
328{
329 /* Apply the transaction. */
330 if (transaction) {
331 struct nb_config *candidate = transaction->config;
332
333 nb_candidate_commit_apply(transaction, true, NULL);
334 nb_config_free(candidate);
335 }
336
337 return SR_ERR_OK;
338}
339
340static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session,
341 const char *module_name)
342{
343 /* Abort the transaction. */
344 if (transaction) {
345 struct nb_config *candidate = transaction->config;
346
347 nb_candidate_commit_abort(transaction);
348 nb_config_free(candidate);
349 }
350
351 return SR_ERR_OK;
352}
353
354/* Callback for changes in the running configuration. */
355static 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)
358{
359 switch (sr_ev) {
360 case SR_EV_ENABLED:
361 return frr_sr_config_change_cb_verify(session, module_name,
362 true);
363 case SR_EV_VERIFY:
364 return frr_sr_config_change_cb_verify(session, module_name,
365 false);
366 case SR_EV_APPLY:
367 return frr_sr_config_change_cb_apply(session, module_name);
368 case SR_EV_ABORT:
369 return frr_sr_config_change_cb_abort(session, module_name);
370 default:
371 flog_err(EC_LIB_LIBSYSREPO, "%s: unknown sysrepo event: %u",
372 __func__, sr_ev);
373 return SR_ERR_INTERNAL;
374 }
375}
376
1a4bc045
RW
377static int frr_sr_state_data_iter_cb(const struct lys_node *snode,
378 struct yang_translator *translator,
379 struct yang_data *data, void *arg)
a7ca2199 380{
1a4bc045 381 struct list *elements = arg;
a7ca2199 382
1a4bc045 383 listnode_add(elements, data);
a7ca2199 384
1a4bc045 385 return NB_OK;
a7ca2199
RW
386}
387
388/* Callback for state retrieval. */
389static int frr_sr_state_cb(const char *xpath, sr_val_t **values,
390 size_t *values_cnt, uint64_t request_id,
de4eaa3c 391 const char *original_xpath, void *private_ctx)
a7ca2199
RW
392{
393 struct list *elements;
394 struct yang_data *data;
a7ca2199
RW
395 struct listnode *node;
396 sr_val_t *v;
397 int ret, count, i = 0;
398
a7ca2199 399 elements = yang_data_list_new();
1a4bc045
RW
400 if (nb_oper_data_iterate(xpath, NULL, NB_OPER_DATA_ITER_NORECURSE,
401 frr_sr_state_data_iter_cb, elements)
402 != NB_OK) {
403 flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
404 "%s: failed to obtain operational data [xpath %s]",
405 __func__, xpath);
406 goto exit;
a7ca2199 407 }
1a4bc045 408
a7ca2199
RW
409 if (list_isempty(elements))
410 goto exit;
411
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__,
416 sr_strerror(ret));
417 goto exit;
418 }
419
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",
424 __func__);
425 }
426 }
427
428 *values = v;
429 *values_cnt = count;
430
431 list_delete(&elements);
432
433 return SR_ERR_OK;
434
435exit:
436 list_delete(&elements);
437 *values = NULL;
dd9a956e 438 *values_cnt = 0;
a7ca2199
RW
439
440 return SR_ERR_OK;
441}
442
443static 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)
446{
447 struct nb_node *nb_node;
448 struct list *input;
449 struct list *output;
450 struct yang_data *data;
451 size_t cb_output_cnt;
452 int ret = SR_ERR_OK;
453
454 nb_node = nb_node_find(xpath);
455 if (!nb_node) {
456 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
457 "%s: unknown data path: %s", __func__, xpath);
458 return SR_ERR_INTERNAL;
459 }
460
461 input = yang_data_list_new();
462 output = yang_data_list_new();
463
464 /* Process input. */
465 for (size_t i = 0; i < input_cnt; i++) {
466 char value_str[YANG_VALUE_MAXLEN];
467
468 sr_val_to_buff(&sr_input[i], value_str, sizeof(value_str));
469
470 data = yang_data_new(xpath, value_str);
471 listnode_add(input, data);
472 }
473
474 /* Execute callback registered for this XPath. */
9eb2c0a1 475 if (nb_callback_rpc(nb_node, xpath, input, output) != NB_OK) {
a7ca2199
RW
476 flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
477 __func__, xpath);
478 ret = SR_ERR_OPERATION_FAILED;
479 goto exit;
480 }
481
482 /* Process output. */
483 if (listcount(output) > 0) {
484 sr_val_t *values = NULL;
485 struct listnode *node;
486 int i = 0;
487
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));
493 goto exit;
494 }
495
496 for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
497 if (yang_data_frr2sr(data, &values[i++]) != 0) {
498 flog_err(
499 EC_LIB_SYSREPO_DATA_CONVERT,
500 "%s: failed to convert data to Sysrepo format",
501 __func__);
502 ret = SR_ERR_INTERNAL;
503 sr_free_values(values, cb_output_cnt);
504 goto exit;
505 }
506 }
507
508 *sr_output = values;
509 *sr_output_cnt = cb_output_cnt;
510 }
511
512exit:
513 /* Release memory. */
514 list_delete(&input);
515 list_delete(&output);
516
517 return ret;
518}
519
520static int frr_sr_notification_send(const char *xpath, struct list *arguments)
521{
522 sr_val_t *values = NULL;
523 size_t values_cnt = 0;
524 int ret;
525
526 if (arguments && listcount(arguments) > 0) {
527 struct yang_data *data;
528 struct listnode *node;
529 int i = 0;
530
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));
536 return NB_ERR;
537 }
538
539 for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) {
540 if (yang_data_frr2sr(data, &values[i++]) != 0) {
541 flog_err(
542 EC_LIB_SYSREPO_DATA_CONVERT,
543 "%s: failed to convert data to sysrepo format",
544 __func__);
545 sr_free_values(values, values_cnt);
546 return NB_ERR;
547 }
548 }
549 }
550
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",
556 __func__, xpath);
557 return NB_ERR;
558 }
559
560 return NB_OK;
561}
562
563/* Code to integrate the sysrepo client into FRR main event loop. */
564struct sysrepo_thread {
565 struct thread *thread;
566 sr_fd_event_t event;
567 int fd;
568};
569
570static struct sysrepo_thread *frr_sr_fd_lookup(sr_fd_event_t event, int fd)
571{
572 struct sysrepo_thread *sr_thread;
573 struct listnode *node;
574
575 for (ALL_LIST_ELEMENTS_RO(sysrepo_threads, node, sr_thread)) {
576 if (sr_thread->event == event && sr_thread->fd == fd)
577 return sr_thread;
578 }
579
580 return NULL;
581}
582
583static void frr_sr_fd_add(int event, int fd)
584{
585 struct sysrepo_thread *sr_thread;
586
587 if (frr_sr_fd_lookup(event, fd) != NULL)
588 return;
589
590 sr_thread = XCALLOC(MTYPE_SYSREPO, sizeof(*sr_thread));
591 sr_thread->event = event;
592 sr_thread->fd = fd;
593 listnode_add(sysrepo_threads, sr_thread);
594
595 switch (event) {
596 case SR_FD_INPUT_READY:
597 thread_add_read(master, frr_sr_read_cb, NULL, fd,
598 &sr_thread->thread);
599 break;
600 case SR_FD_OUTPUT_READY:
601 thread_add_write(master, frr_sr_write_cb, NULL, fd,
602 &sr_thread->thread);
603 break;
604 default:
605 return;
606 }
607}
608
609static void frr_sr_fd_free(struct sysrepo_thread *sr_thread)
610{
611 THREAD_OFF(sr_thread->thread);
612 XFREE(MTYPE_SYSREPO, sr_thread);
613}
614
615static void frr_sr_fd_del(int event, int fd)
616{
617 struct sysrepo_thread *sr_thread;
618
619 sr_thread = frr_sr_fd_lookup(event, fd);
620 if (!sr_thread)
621 return;
622
623 listnode_delete(sysrepo_threads, sr_thread);
624 frr_sr_fd_free(sr_thread);
625}
626
627static void frr_sr_fd_update(sr_fd_change_t *fd_change_set,
628 size_t fd_change_set_cnt)
629{
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;
633
634 if (event != SR_FD_INPUT_READY && event != SR_FD_OUTPUT_READY)
635 continue;
636
637 switch (fd_change_set[i].action) {
638 case SR_FD_START_WATCHING:
639 frr_sr_fd_add(event, fd);
640 break;
641 case SR_FD_STOP_WATCHING:
642 frr_sr_fd_del(event, fd);
643 break;
644 default:
645 break;
646 }
647 }
648}
649
650static int frr_sr_read_cb(struct thread *thread)
651{
652 int fd = THREAD_FD(thread);
653 sr_fd_change_t *fd_change_set = NULL;
654 size_t fd_change_set_cnt = 0;
655 int ret;
656
657 ret = sr_fd_event_process(fd, SR_FD_INPUT_READY, &fd_change_set,
658 &fd_change_set_cnt);
659 if (ret != SR_ERR_OK) {
660 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s",
661 __func__, sr_strerror(ret));
662 return -1;
663 }
664
665 thread = NULL;
666 thread_add_read(master, frr_sr_read_cb, NULL, fd, &thread);
667
668 frr_sr_fd_update(fd_change_set, fd_change_set_cnt);
669 free(fd_change_set);
670
671 return 0;
672}
673
674static int frr_sr_write_cb(struct thread *thread)
675{
676 int fd = THREAD_FD(thread);
677 sr_fd_change_t *fd_change_set = NULL;
678 size_t fd_change_set_cnt = 0;
679 int ret;
680
681 ret = sr_fd_event_process(fd, SR_FD_OUTPUT_READY, &fd_change_set,
682 &fd_change_set_cnt);
683 if (ret != SR_ERR_OK) {
684 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s",
685 __func__, sr_strerror(ret));
686 return -1;
687 }
688
689 frr_sr_fd_update(fd_change_set, fd_change_set_cnt);
690 free(fd_change_set);
691
692 return 0;
693}
694
695static void frr_sr_subscribe_config(struct yang_module *module)
696{
697 int ret;
698
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",
705 sr_strerror(ret));
706}
707
e0ccfad2 708static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg)
a7ca2199 709{
e0ccfad2 710 struct yang_module *module = arg;
a7ca2199
RW
711 struct nb_node *nb_node;
712 int ret;
713
db452508 714 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
e0ccfad2 715 return YANG_ITER_CONTINUE;
a7ca2199 716 /* We only need to subscribe to the root of the state subtrees. */
db452508 717 if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
e0ccfad2 718 return YANG_ITER_CONTINUE;
a7ca2199
RW
719
720 nb_node = snode->priv;
9eb2c0a1
RW
721
722 DEBUGD(&nb_dbg_client_sysrepo, "%s: providing data to '%s'", __func__,
723 nb_node->xpath);
a7ca2199
RW
724
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",
730 sr_strerror(ret));
e0ccfad2
RW
731
732 return YANG_ITER_CONTINUE;
a7ca2199
RW
733}
734
e0ccfad2 735static int frr_sr_subscribe_rpc(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
741 if (snode->nodetype != LYS_RPC)
e0ccfad2 742 return YANG_ITER_CONTINUE;
a7ca2199
RW
743
744 nb_node = snode->priv;
9eb2c0a1
RW
745
746 DEBUGD(&nb_dbg_client_sysrepo, "%s: providing RPC to '%s'", __func__,
747 nb_node->xpath);
a7ca2199
RW
748
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",
754 sr_strerror(ret));
e0ccfad2
RW
755
756 return YANG_ITER_CONTINUE;
a7ca2199
RW
757}
758
e0ccfad2 759static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg)
a7ca2199 760{
e0ccfad2 761 struct yang_module *module = arg;
a7ca2199
RW
762 struct nb_node *nb_node;
763 int ret;
764
765 if (snode->nodetype != LYS_ACTION)
e0ccfad2 766 return YANG_ITER_CONTINUE;
a7ca2199
RW
767
768 nb_node = snode->priv;
9eb2c0a1
RW
769
770 DEBUGD(&nb_dbg_client_sysrepo, "%s: providing action to '%s'", __func__,
771 nb_node->xpath);
a7ca2199
RW
772
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",
778 sr_strerror(ret));
e0ccfad2
RW
779
780 return YANG_ITER_CONTINUE;
a7ca2199
RW
781}
782
9eb2c0a1
RW
783/* CLI commands. */
784DEFUN (debug_nb_sr,
785 debug_nb_sr_cmd,
786 "[no] debug northbound client sysrepo",
787 NO_STR
788 DEBUG_STR
789 "Northbound debugging\n"
790 "Northbound client\n"
791 "Sysrepo\n")
792{
793 uint32_t mode = DEBUG_NODE2MODE(vty->node);
794 bool no = strmatch(argv[0]->text, "no");
795
796 DEBUG_MODE_SET(&nb_dbg_client_sysrepo, mode, !no);
797
798 return CMD_SUCCESS;
799}
800
801static int frr_sr_debug_config_write(struct vty *vty)
802{
803 if (DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo, DEBUG_MODE_CONF))
804 vty_out(vty, "debug northbound client sysrepo\n");
805
806 return 0;
807}
808
809static int frr_sr_debug_set_all(uint32_t flags, bool set)
810{
811 DEBUG_FLAGS_SET(&nb_dbg_client_sysrepo, flags, set);
812
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);
816
817 return 0;
818}
819
820static void frr_sr_cli_init(void)
821{
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);
824
825 install_element(ENABLE_NODE, &debug_nb_sr_cmd);
826 install_element(CONFIG_NODE, &debug_nb_sr_cmd);
827}
828
a7ca2199
RW
829/* FRR's Sysrepo initialization. */
830static int frr_sr_init(const char *program_name)
831{
832 struct yang_module *module;
833 int sysrepo_fd, ret;
834
835 sysrepo_threads = list_new();
836
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));
841 goto cleanup;
842 }
843
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__,
848 sr_strerror(ret));
849 goto cleanup;
850 }
851
852 /* Start session. */
853 ret = sr_session_start(connection, SR_DS_RUNNING, SR_SESS_DEFAULT,
854 &session);
855 if (ret != SR_ERR_OK) {
856 flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_session_start(): %s",
857 __func__, sr_strerror(ret));
858 goto cleanup;
859 }
860
861 /* Perform subscriptions. */
862 RB_FOREACH (module, yang_modules, &yang_modules) {
863 frr_sr_subscribe_config(module);
e0ccfad2
RW
864 yang_snodes_iterate_module(module->info, frr_sr_subscribe_state,
865 0, module);
866 yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc,
867 0, module);
868 yang_snodes_iterate_module(module->info,
869 frr_sr_subscribe_action, 0, module);
a7ca2199
RW
870 }
871
872 hook_register(nb_notification_send, frr_sr_notification_send);
873
874 frr_sr_fd_add(SR_FD_INPUT_READY, sysrepo_fd);
875
876 return 0;
877
878cleanup:
879 frr_sr_finish();
880
881 return -1;
882}
883
884static int frr_sr_finish(void)
885{
886 struct yang_module *module;
887
888 RB_FOREACH (module, yang_modules, &yang_modules) {
889 if (!module->sr_subscription)
890 continue;
891 sr_unsubscribe(session, module->sr_subscription);
892 }
893
894 if (session)
895 sr_session_stop(session);
896 if (connection)
897 sr_disconnect(connection);
898
899 sysrepo_threads->del = (void (*)(void *))frr_sr_fd_free;
900 list_delete(&sysrepo_threads);
901 sr_fd_watcher_cleanup();
902
903 return 0;
904}
905
906static int frr_sr_module_late_init(struct thread_master *tm)
907{
908 master = tm;
909
910 if (frr_sr_init(frr_get_progname()) < 0) {
911 flog_err(EC_LIB_SYSREPO_INIT,
912 "failed to initialize the Sysrepo module");
913 return -1;
914 }
915
916 hook_register(frr_fini, frr_sr_finish);
9eb2c0a1 917 frr_sr_cli_init();
a7ca2199
RW
918
919 return 0;
920}
921
922static int frr_sr_module_init(void)
923{
924 hook_register(frr_late_init, frr_sr_module_late_init);
925
926 return 0;
927}
928
929FRR_MODULE_SETUP(.name = "frr_sysrepo", .version = FRR_VERSION,
930 .description = "FRR sysrepo integration module",
931 .init = frr_sr_module_init, )