]>
Commit | Line | Data |
---|---|---|
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 | ||
35 | DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module") | |
36 | ||
9eb2c0a1 RW |
37 | static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"}; |
38 | ||
a7ca2199 | 39 | static struct thread_master *master; |
a7ca2199 RW |
40 | static sr_session_ctx_t *session; |
41 | static sr_conn_ctx_t *connection; | |
88a7d121 | 42 | static struct nb_transaction *transaction; |
a7ca2199 RW |
43 | |
44 | static int frr_sr_read_cb(struct thread *thread); | |
a7ca2199 RW |
45 | static int frr_sr_finish(void); |
46 | ||
47 | /* Convert FRR YANG data value to sysrepo YANG data value. */ | |
48 | static 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 | ||
160 | static 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 | 240 | static 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 |
310 | static 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 | ||
326 | static 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. */ | |
342 | static 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 |
362 | static 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 |
383 | static 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 |
405 | static 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 | ||
479 | exit: | |
480 | /* Release memory. */ | |
481 | list_delete(&input); | |
482 | list_delete(&output); | |
483 | ||
484 | return ret; | |
485 | } | |
486 | ||
487 | static 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 |
529 | static 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 | ||
548 | static 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 | 565 | static 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 | 592 | static 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. */ |
617 | DEFUN (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 | ||
634 | static 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 | ||
642 | static 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 | ||
653 | static 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 | 663 | static 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 | ||
710 | cleanup: | |
711 | frr_sr_finish(); | |
712 | ||
713 | return -1; | |
714 | } | |
715 | ||
716 | static 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 | 735 | static 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 | ||
750 | static 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 | ||
757 | static 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 | ||
765 | FRR_MODULE_SETUP(.name = "frr_sysrepo", .version = FRR_VERSION, | |
766 | .description = "FRR sysrepo integration module", | |
767 | .init = frr_sr_module_init, ) |