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