]> git.proxmox.com Git - mirror_frr.git/blob - lib/northbound_sysrepo.c
Merge pull request #13060 from opensourcerouting/feature/allow_peering_with_127.0.0.1
[mirror_frr.git] / lib / northbound_sysrepo.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2018 NetDEF, Inc.
4 * Renato Westphal
5 */
6
7 #include <zebra.h>
8
9 #include "log.h"
10 #include "lib_errors.h"
11 #include "command.h"
12 #include "debug.h"
13 #include "memory.h"
14 #include "libfrr.h"
15 #include "lib/version.h"
16 #include "northbound.h"
17
18 #include <sysrepo.h>
19 #include <sysrepo/values.h>
20 #include <sysrepo/xpath.h>
21
22 DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module");
23
24 static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"};
25
26 static struct event_loop *master;
27 static sr_session_ctx_t *session;
28 static sr_conn_ctx_t *connection;
29 static struct nb_transaction *transaction;
30
31 static void frr_sr_read_cb(struct event *thread);
32 static int frr_sr_finish(void);
33
34 /* Convert FRR YANG data value to sysrepo YANG data value. */
35 static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
36 {
37 struct nb_node *nb_node;
38 const struct lysc_node *snode;
39 struct lysc_node_container *scontainer;
40 struct lysc_node_leaf *sleaf;
41 struct lysc_node_leaflist *sleaflist;
42 LY_DATA_TYPE type;
43
44 sr_val_set_xpath(sr_data, frr_data->xpath);
45
46 nb_node = nb_node_find(frr_data->xpath);
47 if (!nb_node) {
48 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
49 "%s: unknown data path: %s", __func__,
50 frr_data->xpath);
51 return -1;
52 }
53
54 snode = nb_node->snode;
55 switch (snode->nodetype) {
56 case LYS_CONTAINER:
57 scontainer = (struct lysc_node_container *)snode;
58 if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
59 return -1;
60 sr_data->type = SR_CONTAINER_PRESENCE_T;
61 return 0;
62 case LYS_LIST:
63 sr_data->type = SR_LIST_T;
64 return 0;
65 case LYS_LEAF:
66 sleaf = (struct lysc_node_leaf *)snode;
67 type = sleaf->type->basetype;
68 break;
69 case LYS_LEAFLIST:
70 sleaflist = (struct lysc_node_leaflist *)snode;
71 type = sleaflist->type->basetype;
72 break;
73 default:
74 return -1;
75 }
76
77 switch (type) {
78 case LY_TYPE_BINARY:
79 sr_val_set_str_data(sr_data, SR_BINARY_T, frr_data->value);
80 break;
81 case LY_TYPE_BITS:
82 sr_val_set_str_data(sr_data, SR_BITS_T, frr_data->value);
83 break;
84 case LY_TYPE_BOOL:
85 sr_data->type = SR_BOOL_T;
86 sr_data->data.bool_val = yang_str2bool(frr_data->value);
87 break;
88 case LY_TYPE_DEC64:
89 sr_data->type = SR_DECIMAL64_T;
90 sr_data->data.decimal64_val =
91 yang_str2dec64(frr_data->xpath, frr_data->value);
92 break;
93 case LY_TYPE_EMPTY:
94 sr_data->type = SR_LEAF_EMPTY_T;
95 break;
96 case LY_TYPE_ENUM:
97 sr_val_set_str_data(sr_data, SR_ENUM_T, frr_data->value);
98 break;
99 case LY_TYPE_IDENT:
100 sr_val_set_str_data(sr_data, SR_IDENTITYREF_T, frr_data->value);
101 break;
102 case LY_TYPE_INST:
103 sr_val_set_str_data(sr_data, SR_INSTANCEID_T, frr_data->value);
104 break;
105 case LY_TYPE_INT8:
106 sr_data->type = SR_INT8_T;
107 sr_data->data.int8_val = yang_str2int8(frr_data->value);
108 break;
109 case LY_TYPE_INT16:
110 sr_data->type = SR_INT16_T;
111 sr_data->data.int16_val = yang_str2int16(frr_data->value);
112 break;
113 case LY_TYPE_INT32:
114 sr_data->type = SR_INT32_T;
115 sr_data->data.int32_val = yang_str2int32(frr_data->value);
116 break;
117 case LY_TYPE_INT64:
118 sr_data->type = SR_INT64_T;
119 sr_data->data.int64_val = yang_str2int64(frr_data->value);
120 break;
121 case LY_TYPE_STRING:
122 sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
123 break;
124 case LY_TYPE_UINT8:
125 sr_data->type = SR_UINT8_T;
126 sr_data->data.uint8_val = yang_str2uint8(frr_data->value);
127 break;
128 case LY_TYPE_UINT16:
129 sr_data->type = SR_UINT16_T;
130 sr_data->data.uint16_val = yang_str2uint16(frr_data->value);
131 break;
132 case LY_TYPE_UINT32:
133 sr_data->type = SR_UINT32_T;
134 sr_data->data.uint32_val = yang_str2uint32(frr_data->value);
135 break;
136 case LY_TYPE_UINT64:
137 sr_data->type = SR_UINT64_T;
138 sr_data->data.uint64_val = yang_str2uint64(frr_data->value);
139 break;
140 default:
141 return -1;
142 }
143
144 return 0;
145 }
146
147 static int frr_sr_process_change(struct nb_config *candidate,
148 sr_change_oper_t sr_op, sr_val_t *sr_old_val,
149 sr_val_t *sr_new_val)
150 {
151 struct nb_node *nb_node;
152 enum nb_operation nb_op;
153 sr_val_t *sr_data;
154 const char *xpath;
155 char value_str[YANG_VALUE_MAXLEN];
156 struct yang_data *data;
157 int ret;
158
159 sr_data = sr_new_val ? sr_new_val : sr_old_val;
160 assert(sr_data);
161
162 xpath = sr_data->xpath;
163
164 DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: processing change [xpath %s]",
165 xpath);
166
167 /* Non-presence container - nothing to do. */
168 if (sr_data->type == SR_CONTAINER_T)
169 return NB_OK;
170
171 nb_node = nb_node_find(xpath);
172 if (!nb_node) {
173 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
174 "%s: unknown data path: %s", __func__, xpath);
175 return NB_ERR;
176 }
177
178 /* Map operation values. */
179 switch (sr_op) {
180 case SR_OP_CREATED:
181 case SR_OP_MODIFIED:
182 if (nb_operation_is_valid(NB_OP_CREATE, nb_node->snode))
183 nb_op = NB_OP_CREATE;
184 else if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode)) {
185 nb_op = NB_OP_MODIFY;
186 } else
187 /* Ignore list keys modifications. */
188 return NB_OK;
189 break;
190 case SR_OP_DELETED:
191 /*
192 * When a list is deleted or one of its keys is changed, we are
193 * notified about the removal of all of its leafs, even the ones
194 * that are non-optional. We need to ignore these notifications.
195 */
196 if (!nb_operation_is_valid(NB_OP_DESTROY, nb_node->snode))
197 return NB_OK;
198
199 nb_op = NB_OP_DESTROY;
200 break;
201 case SR_OP_MOVED:
202 nb_op = NB_OP_MOVE;
203 break;
204 default:
205 flog_err(EC_LIB_DEVELOPMENT,
206 "%s: unexpected operation %u [xpath %s]", __func__,
207 sr_op, xpath);
208 return NB_ERR;
209 }
210
211 sr_val_to_buff(sr_data, value_str, sizeof(value_str));
212 data = yang_data_new(xpath, value_str);
213
214 ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data);
215 yang_data_free(data);
216 if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
217 flog_warn(
218 EC_LIB_NB_CANDIDATE_EDIT_ERROR,
219 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
220 __func__, nb_operation_name(nb_op), xpath);
221 return NB_ERR;
222 }
223
224 return NB_OK;
225 }
226
227 static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session,
228 const char *module_name)
229 {
230 sr_change_iter_t *it;
231 int ret;
232 sr_change_oper_t sr_op;
233 sr_val_t *sr_old_val, *sr_new_val;
234 struct nb_context context = {};
235 struct nb_config *candidate;
236 char errmsg[BUFSIZ] = {0};
237
238 ret = sr_get_changes_iter(session, "//*", &it);
239 if (ret != SR_ERR_OK) {
240 flog_err(EC_LIB_LIBSYSREPO,
241 "%s: sr_get_changes_iter() failed for \"%s\"",
242 __func__, module_name);
243 return ret;
244 }
245
246 candidate = nb_config_dup(running_config);
247
248 while ((ret = sr_get_change_next(session, it, &sr_op, &sr_old_val,
249 &sr_new_val))
250 == SR_ERR_OK) {
251 ret = frr_sr_process_change(candidate, sr_op, sr_old_val,
252 sr_new_val);
253 sr_free_val(sr_old_val);
254 sr_free_val(sr_new_val);
255 if (ret != NB_OK)
256 break;
257 }
258
259 sr_free_change_iter(it);
260 if (ret != NB_OK && ret != SR_ERR_NOT_FOUND) {
261 nb_config_free(candidate);
262 return SR_ERR_INTERNAL;
263 }
264
265 transaction = NULL;
266 context.client = NB_CLIENT_SYSREPO;
267 /*
268 * Validate the configuration changes and allocate all resources
269 * required to apply them.
270 */
271 ret = nb_candidate_commit_prepare(context, candidate, NULL,
272 &transaction, false, false, errmsg,
273 sizeof(errmsg));
274 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
275 flog_warn(
276 EC_LIB_LIBSYSREPO,
277 "%s: failed to prepare configuration transaction: %s (%s)",
278 __func__, nb_err_name(ret), errmsg);
279
280 if (!transaction)
281 nb_config_free(candidate);
282
283 /* Map northbound return code to sysrepo return code. */
284 switch (ret) {
285 case NB_OK:
286 return SR_ERR_OK;
287 case NB_ERR_NO_CHANGES:
288 return SR_ERR_OK;
289 case NB_ERR_LOCKED:
290 return SR_ERR_LOCKED;
291 case NB_ERR_RESOURCE:
292 return SR_ERR_NO_MEMORY;
293 default:
294 return SR_ERR_VALIDATION_FAILED;
295 }
296 }
297
298 static int frr_sr_config_change_cb_apply(sr_session_ctx_t *session,
299 const char *module_name)
300 {
301 /* Apply the transaction. */
302 if (transaction) {
303 struct nb_config *candidate = transaction->config;
304 char errmsg[BUFSIZ] = {0};
305
306 nb_candidate_commit_apply(transaction, true, NULL, errmsg,
307 sizeof(errmsg));
308 nb_config_free(candidate);
309 }
310
311 return SR_ERR_OK;
312 }
313
314 static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session,
315 const char *module_name)
316 {
317 /* Abort the transaction. */
318 if (transaction) {
319 struct nb_config *candidate = transaction->config;
320 char errmsg[BUFSIZ] = {0};
321
322 nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg));
323 nb_config_free(candidate);
324 }
325
326 return SR_ERR_OK;
327 }
328
329 /* Callback for changes in the running configuration. */
330 static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id,
331 const char *module_name, const char *xpath,
332 sr_event_t sr_ev, uint32_t request_id,
333 void *private_data)
334 {
335 switch (sr_ev) {
336 case SR_EV_ENABLED:
337 case SR_EV_CHANGE:
338 return frr_sr_config_change_cb_prepare(session, module_name);
339 case SR_EV_DONE:
340 return frr_sr_config_change_cb_apply(session, module_name);
341 case SR_EV_ABORT:
342 return frr_sr_config_change_cb_abort(session, module_name);
343 default:
344 flog_err(EC_LIB_LIBSYSREPO, "%s: unexpected sysrepo event: %u",
345 __func__, sr_ev);
346 return SR_ERR_INTERNAL;
347 }
348 }
349
350 static int frr_sr_state_data_iter_cb(const struct lysc_node *snode,
351 struct yang_translator *translator,
352 struct yang_data *data, void *arg)
353 {
354 struct lyd_node *dnode = arg;
355 LY_ERR ly_errno;
356
357 ly_errno = 0;
358 ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value,
359 0, &dnode);
360 if (!dnode && ly_errno) {
361 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
362 __func__);
363 yang_data_free(data);
364 return NB_ERR;
365 }
366
367 yang_data_free(data);
368 return NB_OK;
369 }
370
371 /* Callback for state retrieval. */
372 static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id,
373 const char *module_name, const char *xpath,
374 const char *request_xpath, uint32_t request_id,
375 struct lyd_node **parent, void *private_ctx)
376 {
377 struct lyd_node *dnode;
378
379 dnode = *parent;
380 if (nb_oper_data_iterate(request_xpath, NULL, 0,
381 frr_sr_state_data_iter_cb, dnode)
382 != NB_OK) {
383 flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
384 "%s: failed to obtain operational data [xpath %s]",
385 __func__, xpath);
386 return SR_ERR_INTERNAL;
387 }
388
389 *parent = dnode;
390
391 return SR_ERR_OK;
392 }
393 static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id,
394 const char *xpath, const sr_val_t *sr_input,
395 const size_t input_cnt, sr_event_t sr_ev,
396 uint32_t request_id, sr_val_t **sr_output,
397 size_t *sr_output_cnt, void *private_ctx)
398 {
399 struct nb_node *nb_node;
400 struct list *input;
401 struct list *output;
402 struct yang_data *data;
403 size_t cb_output_cnt;
404 int ret = SR_ERR_OK;
405 char errmsg[BUFSIZ] = {0};
406
407 nb_node = nb_node_find(xpath);
408 if (!nb_node) {
409 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
410 "%s: unknown data path: %s", __func__, xpath);
411 return SR_ERR_INTERNAL;
412 }
413
414 input = yang_data_list_new();
415 output = yang_data_list_new();
416
417 /* Process input. */
418 for (size_t i = 0; i < input_cnt; i++) {
419 char value_str[YANG_VALUE_MAXLEN];
420
421 sr_val_to_buff(&sr_input[i], value_str, sizeof(value_str));
422
423 data = yang_data_new(xpath, value_str);
424 listnode_add(input, data);
425 }
426
427 /* Execute callback registered for this XPath. */
428 if (nb_callback_rpc(nb_node, xpath, input, output, errmsg,
429 sizeof(errmsg))
430 != NB_OK) {
431 flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
432 __func__, xpath);
433 ret = SR_ERR_OPERATION_FAILED;
434 goto exit;
435 }
436
437 /* Process output. */
438 if (listcount(output) > 0) {
439 sr_val_t *values = NULL;
440 struct listnode *node;
441 int i = 0;
442
443 cb_output_cnt = listcount(output);
444 ret = sr_new_values(cb_output_cnt, &values);
445 if (ret != SR_ERR_OK) {
446 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s",
447 __func__, sr_strerror(ret));
448 goto exit;
449 }
450
451 for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
452 if (yang_data_frr2sr(data, &values[i++]) != 0) {
453 flog_err(
454 EC_LIB_SYSREPO_DATA_CONVERT,
455 "%s: failed to convert data to Sysrepo format",
456 __func__);
457 ret = SR_ERR_INTERNAL;
458 sr_free_values(values, cb_output_cnt);
459 goto exit;
460 }
461 }
462
463 *sr_output = values;
464 *sr_output_cnt = cb_output_cnt;
465 }
466
467 exit:
468 /* Release memory. */
469 list_delete(&input);
470 list_delete(&output);
471
472 return ret;
473 }
474
475 static int frr_sr_notification_send(const char *xpath, struct list *arguments)
476 {
477 sr_val_t *values = NULL;
478 size_t values_cnt = 0;
479 int ret;
480
481 if (arguments && listcount(arguments) > 0) {
482 struct yang_data *data;
483 struct listnode *node;
484 int i = 0;
485
486 values_cnt = listcount(arguments);
487 ret = sr_new_values(values_cnt, &values);
488 if (ret != SR_ERR_OK) {
489 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s",
490 __func__, sr_strerror(ret));
491 return NB_ERR;
492 }
493
494 for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) {
495 if (yang_data_frr2sr(data, &values[i++]) != 0) {
496 flog_err(
497 EC_LIB_SYSREPO_DATA_CONVERT,
498 "%s: failed to convert data to sysrepo format",
499 __func__);
500 sr_free_values(values, values_cnt);
501 return NB_ERR;
502 }
503 }
504 }
505
506 ret = sr_notif_send(session, xpath, values, values_cnt, 0, 0);
507 if (ret != SR_ERR_OK) {
508 flog_err(EC_LIB_LIBSYSREPO,
509 "%s: sr_event_notif_send() failed for xpath %s",
510 __func__, xpath);
511 return NB_ERR;
512 }
513
514 return NB_OK;
515 }
516
517 static void frr_sr_read_cb(struct event *thread)
518 {
519 struct yang_module *module = EVENT_ARG(thread);
520 int fd = EVENT_FD(thread);
521 int ret;
522
523 ret = sr_subscription_process_events(module->sr_subscription, session,
524 NULL);
525 if (ret != SR_ERR_OK) {
526 flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s",
527 __func__, sr_strerror(ret));
528 return;
529 }
530
531 event_add_read(master, frr_sr_read_cb, module, fd, &module->sr_thread);
532 }
533
534 static void frr_sr_subscribe_config(struct yang_module *module)
535 {
536 int ret;
537
538 DEBUGD(&nb_dbg_client_sysrepo,
539 "sysrepo: subscribing for configuration changes made in the '%s' module",
540 module->name);
541
542 ret = sr_module_change_subscribe(
543 session, module->name, NULL, frr_sr_config_change_cb, NULL, 0,
544 SR_SUBSCR_DEFAULT | SR_SUBSCR_ENABLED | SR_SUBSCR_NO_THREAD,
545 &module->sr_subscription);
546 if (ret != SR_ERR_OK)
547 flog_err(EC_LIB_LIBSYSREPO, "sr_module_change_subscribe(): %s",
548 sr_strerror(ret));
549 }
550
551 static int frr_sr_subscribe_state(const struct lysc_node *snode, void *arg)
552 {
553 struct yang_module *module = arg;
554 struct nb_node *nb_node;
555 int ret;
556
557 if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
558 return YANG_ITER_CONTINUE;
559 /* We only need to subscribe to the root of the state subtrees. */
560 if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
561 return YANG_ITER_CONTINUE;
562
563 nb_node = snode->priv;
564 if (!nb_node)
565 return YANG_ITER_CONTINUE;
566
567 DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing data to '%s'",
568 nb_node->xpath);
569
570 ret = sr_oper_get_subscribe(session, snode->module->name,
571 nb_node->xpath, frr_sr_state_cb, NULL, 0,
572 &module->sr_subscription);
573 if (ret != SR_ERR_OK)
574 flog_err(EC_LIB_LIBSYSREPO, "sr_oper_get_items_subscribe(): %s",
575 sr_strerror(ret));
576
577 return YANG_ITER_CONTINUE;
578 }
579
580 static int frr_sr_subscribe_rpc(const struct lysc_node *snode, void *arg)
581 {
582 struct yang_module *module = arg;
583 struct nb_node *nb_node;
584 int ret;
585
586 if (snode->nodetype != LYS_RPC)
587 return YANG_ITER_CONTINUE;
588
589 nb_node = snode->priv;
590 if (!nb_node)
591 return YANG_ITER_CONTINUE;
592
593 DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'",
594 nb_node->xpath);
595
596 ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb,
597 NULL, 0, 0, &module->sr_subscription);
598 if (ret != SR_ERR_OK)
599 flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
600 sr_strerror(ret));
601
602 return YANG_ITER_CONTINUE;
603 }
604
605 /* CLI commands. */
606 DEFUN (debug_nb_sr,
607 debug_nb_sr_cmd,
608 "[no] debug northbound client sysrepo",
609 NO_STR
610 DEBUG_STR
611 "Northbound debugging\n"
612 "Northbound client\n"
613 "Sysrepo\n")
614 {
615 uint32_t mode = DEBUG_NODE2MODE(vty->node);
616 bool no = strmatch(argv[0]->text, "no");
617
618 DEBUG_MODE_SET(&nb_dbg_client_sysrepo, mode, !no);
619
620 return CMD_SUCCESS;
621 }
622
623 static int frr_sr_debug_config_write(struct vty *vty)
624 {
625 if (DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo, DEBUG_MODE_CONF))
626 vty_out(vty, "debug northbound client sysrepo\n");
627
628 return 0;
629 }
630
631 static int frr_sr_debug_set_all(uint32_t flags, bool set)
632 {
633 DEBUG_FLAGS_SET(&nb_dbg_client_sysrepo, flags, set);
634
635 /* If all modes have been turned off, don't preserve options. */
636 if (!DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo, DEBUG_MODE_ALL))
637 DEBUG_CLEAR(&nb_dbg_client_sysrepo);
638
639 return 0;
640 }
641
642 static void frr_sr_cli_init(void)
643 {
644 hook_register(nb_client_debug_config_write, frr_sr_debug_config_write);
645 hook_register(nb_client_debug_set_all, frr_sr_debug_set_all);
646
647 install_element(ENABLE_NODE, &debug_nb_sr_cmd);
648 install_element(CONFIG_NODE, &debug_nb_sr_cmd);
649 }
650
651 /* FRR's Sysrepo initialization. */
652 static int frr_sr_init(void)
653 {
654 struct yang_module *module;
655 int ret;
656
657 /* Connect to Sysrepo. */
658 ret = sr_connect(SR_CONN_DEFAULT, &connection);
659 if (ret != SR_ERR_OK) {
660 flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_connect(): %s", __func__,
661 sr_strerror(ret));
662 goto cleanup;
663 }
664
665 /* Start session. */
666 ret = sr_session_start(connection, SR_DS_RUNNING, &session);
667 if (ret != SR_ERR_OK) {
668 flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_session_start(): %s",
669 __func__, sr_strerror(ret));
670 goto cleanup;
671 }
672
673 /* Perform subscriptions. */
674 RB_FOREACH (module, yang_modules, &yang_modules) {
675 int event_pipe;
676
677 frr_sr_subscribe_config(module);
678 yang_snodes_iterate(module->info, frr_sr_subscribe_state, 0,
679 module);
680 yang_snodes_iterate(module->info, frr_sr_subscribe_rpc, 0,
681 module);
682
683 /* Watch subscriptions. */
684 ret = sr_get_event_pipe(module->sr_subscription, &event_pipe);
685 if (ret != SR_ERR_OK) {
686 flog_err(EC_LIB_SYSREPO_INIT,
687 "%s: sr_get_event_pipe(): %s", __func__,
688 sr_strerror(ret));
689 goto cleanup;
690 }
691 event_add_read(master, frr_sr_read_cb, module, event_pipe,
692 &module->sr_thread);
693 }
694
695 hook_register(nb_notification_send, frr_sr_notification_send);
696
697 return 0;
698
699 cleanup:
700 frr_sr_finish();
701
702 return -1;
703 }
704
705 static int frr_sr_finish(void)
706 {
707 struct yang_module *module;
708
709 RB_FOREACH (module, yang_modules, &yang_modules) {
710 if (!module->sr_subscription)
711 continue;
712 sr_unsubscribe(module->sr_subscription);
713 EVENT_OFF(module->sr_thread);
714 }
715
716 if (session)
717 sr_session_stop(session);
718 if (connection)
719 sr_disconnect(connection);
720
721 return 0;
722 }
723
724 static int frr_sr_module_config_loaded(struct event_loop *tm)
725 {
726 master = tm;
727
728 if (frr_sr_init() < 0) {
729 flog_err(EC_LIB_SYSREPO_INIT,
730 "failed to initialize the Sysrepo module");
731 return -1;
732 }
733
734 hook_register(frr_fini, frr_sr_finish);
735
736 return 0;
737 }
738
739 static int frr_sr_module_late_init(struct event_loop *tm)
740 {
741 frr_sr_cli_init();
742
743 return 0;
744 }
745
746 static int frr_sr_module_init(void)
747 {
748 hook_register(frr_late_init, frr_sr_module_late_init);
749 hook_register(frr_config_post, frr_sr_module_config_loaded);
750
751 return 0;
752 }
753
754 FRR_MODULE_SETUP(.name = "frr_sysrepo", .version = FRR_VERSION,
755 .description = "FRR sysrepo integration module",
756 .init = frr_sr_module_init,
757 );