]>
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" | |
09781197 | 28 | #include "lib/version.h" |
a7ca2199 RW |
29 | #include "northbound.h" |
30 | ||
31 | #include <sysrepo.h> | |
32 | #include <sysrepo/values.h> | |
33 | #include <sysrepo/xpath.h> | |
34 | ||
bf8d3d6a | 35 | DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module"); |
a7ca2199 | 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 | 43 | |
cc9f21da | 44 | static void 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; |
3bb513c3 CH |
51 | const struct lysc_node *snode; |
52 | struct lysc_node_container *scontainer; | |
53 | struct lysc_node_leaf *sleaf; | |
54 | struct lysc_node_leaflist *sleaflist; | |
a7ca2199 RW |
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: | |
3bb513c3 CH |
70 | scontainer = (struct lysc_node_container *)snode; |
71 | if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE)) | |
a7ca2199 RW |
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: | |
3bb513c3 | 79 | sleaf = (struct lysc_node_leaf *)snode; |
7b73f3db | 80 | type = sleaf->type->basetype; |
a7ca2199 RW |
81 | break; |
82 | case LYS_LEAFLIST: | |
3bb513c3 | 83 | sleaflist = (struct lysc_node_leaflist *)snode; |
7b73f3db | 84 | type = sleaflist->type->basetype; |
a7ca2199 RW |
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: | |
7b73f3db | 304 | return SR_ERR_NO_MEMORY; |
a7ca2199 RW |
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. */ | |
7b73f3db | 342 | static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id, |
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 | ||
3bb513c3 | 362 | static int frr_sr_state_data_iter_cb(const struct lysc_node *snode, |
1a4bc045 RW |
363 | struct yang_translator *translator, |
364 | struct yang_data *data, void *arg) | |
a7ca2199 | 365 | { |
24ed137c | 366 | struct lyd_node *dnode = arg; |
7b73f3db | 367 | LY_ERR ly_errno; |
24ed137c RW |
368 | |
369 | ly_errno = 0; | |
7b73f3db FA |
370 | ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value, |
371 | 0, &dnode); | |
24ed137c RW |
372 | if (!dnode && ly_errno) { |
373 | flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", | |
374 | __func__); | |
375 | yang_data_free(data); | |
376 | return NB_ERR; | |
377 | } | |
a7ca2199 | 378 | |
24ed137c | 379 | yang_data_free(data); |
1a4bc045 | 380 | return NB_OK; |
a7ca2199 RW |
381 | } |
382 | ||
383 | /* Callback for state retrieval. */ | |
7b73f3db FA |
384 | static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id, |
385 | const char *module_name, const char *xpath, | |
386 | const char *request_xpath, uint32_t request_id, | |
387 | struct lyd_node **parent, void *private_ctx) | |
a7ca2199 | 388 | { |
24ed137c | 389 | struct lyd_node *dnode; |
a7ca2199 | 390 | |
24ed137c RW |
391 | dnode = *parent; |
392 | if (nb_oper_data_iterate(request_xpath, NULL, 0, | |
393 | frr_sr_state_data_iter_cb, dnode) | |
1a4bc045 RW |
394 | != NB_OK) { |
395 | flog_warn(EC_LIB_NB_OPERATIONAL_DATA, | |
396 | "%s: failed to obtain operational data [xpath %s]", | |
397 | __func__, xpath); | |
24ed137c | 398 | return SR_ERR_INTERNAL; |
a7ca2199 RW |
399 | } |
400 | ||
24ed137c | 401 | *parent = dnode; |
a7ca2199 RW |
402 | |
403 | return SR_ERR_OK; | |
404 | } | |
7b73f3db FA |
405 | static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id, |
406 | const char *xpath, const sr_val_t *sr_input, | |
24ed137c RW |
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 | ||
1dd5ce9b | 518 | ret = sr_notif_send(session, xpath, values, values_cnt, 0, 0); |
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 | ||
cc9f21da | 529 | static void frr_sr_read_cb(struct thread *thread) |
a7ca2199 | 530 | { |
7640e3c6 | 531 | struct yang_module *module = THREAD_ARG(thread); |
a7ca2199 | 532 | int fd = THREAD_FD(thread); |
a7ca2199 RW |
533 | int ret; |
534 | ||
1dd5ce9b DA |
535 | ret = sr_subscription_process_events(module->sr_subscription, session, |
536 | NULL); | |
a7ca2199 RW |
537 | if (ret != SR_ERR_OK) { |
538 | flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", | |
539 | __func__, sr_strerror(ret)); | |
cc9f21da | 540 | return; |
a7ca2199 RW |
541 | } |
542 | ||
7640e3c6 | 543 | thread_add_read(master, frr_sr_read_cb, module, fd, &module->sr_thread); |
a7ca2199 RW |
544 | } |
545 | ||
546 | static void frr_sr_subscribe_config(struct yang_module *module) | |
547 | { | |
548 | int ret; | |
549 | ||
bbeaa033 RW |
550 | DEBUGD(&nb_dbg_client_sysrepo, |
551 | "sysrepo: subscribing for configuration changes made in the '%s' module", | |
552 | module->name); | |
553 | ||
a7ca2199 | 554 | ret = sr_module_change_subscribe( |
24ed137c RW |
555 | session, module->name, NULL, frr_sr_config_change_cb, NULL, 0, |
556 | SR_SUBSCR_DEFAULT | SR_SUBSCR_ENABLED | SR_SUBSCR_NO_THREAD, | |
a7ca2199 RW |
557 | &module->sr_subscription); |
558 | if (ret != SR_ERR_OK) | |
559 | flog_err(EC_LIB_LIBSYSREPO, "sr_module_change_subscribe(): %s", | |
560 | sr_strerror(ret)); | |
561 | } | |
562 | ||
3bb513c3 | 563 | static int frr_sr_subscribe_state(const struct lysc_node *snode, void *arg) |
a7ca2199 | 564 | { |
e0ccfad2 | 565 | struct yang_module *module = arg; |
a7ca2199 RW |
566 | struct nb_node *nb_node; |
567 | int ret; | |
568 | ||
db452508 | 569 | if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R)) |
e0ccfad2 | 570 | return YANG_ITER_CONTINUE; |
a7ca2199 | 571 | /* We only need to subscribe to the root of the state subtrees. */ |
db452508 | 572 | if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R)) |
e0ccfad2 | 573 | return YANG_ITER_CONTINUE; |
a7ca2199 RW |
574 | |
575 | nb_node = snode->priv; | |
9bde0b25 RW |
576 | if (!nb_node) |
577 | return YANG_ITER_CONTINUE; | |
9eb2c0a1 | 578 | |
bbeaa033 | 579 | DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing data to '%s'", |
9eb2c0a1 | 580 | nb_node->xpath); |
a7ca2199 | 581 | |
1dd5ce9b DA |
582 | ret = sr_oper_get_subscribe(session, snode->module->name, |
583 | nb_node->xpath, frr_sr_state_cb, NULL, 0, | |
584 | &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 | ||
3bb513c3 | 592 | static int frr_sr_subscribe_rpc(const struct lysc_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; | |
9bde0b25 RW |
602 | if (!nb_node) |
603 | return YANG_ITER_CONTINUE; | |
9eb2c0a1 | 604 | |
bbeaa033 | 605 | DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'", |
9eb2c0a1 | 606 | nb_node->xpath); |
a7ca2199 RW |
607 | |
608 | ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, | |
1dd5ce9b | 609 | NULL, 0, 0, &module->sr_subscription); |
a7ca2199 RW |
610 | if (ret != SR_ERR_OK) |
611 | flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s", | |
612 | sr_strerror(ret)); | |
e0ccfad2 RW |
613 | |
614 | return YANG_ITER_CONTINUE; | |
a7ca2199 RW |
615 | } |
616 | ||
9eb2c0a1 RW |
617 | /* CLI commands. */ |
618 | DEFUN (debug_nb_sr, | |
619 | debug_nb_sr_cmd, | |
620 | "[no] debug northbound client sysrepo", | |
621 | NO_STR | |
622 | DEBUG_STR | |
623 | "Northbound debugging\n" | |
624 | "Northbound client\n" | |
625 | "Sysrepo\n") | |
626 | { | |
627 | uint32_t mode = DEBUG_NODE2MODE(vty->node); | |
628 | bool no = strmatch(argv[0]->text, "no"); | |
629 | ||
630 | DEBUG_MODE_SET(&nb_dbg_client_sysrepo, mode, !no); | |
631 | ||
632 | return CMD_SUCCESS; | |
633 | } | |
634 | ||
635 | static int frr_sr_debug_config_write(struct vty *vty) | |
636 | { | |
637 | if (DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo, DEBUG_MODE_CONF)) | |
638 | vty_out(vty, "debug northbound client sysrepo\n"); | |
639 | ||
640 | return 0; | |
641 | } | |
642 | ||
643 | static int frr_sr_debug_set_all(uint32_t flags, bool set) | |
644 | { | |
645 | DEBUG_FLAGS_SET(&nb_dbg_client_sysrepo, flags, set); | |
646 | ||
647 | /* If all modes have been turned off, don't preserve options. */ | |
648 | if (!DEBUG_MODE_CHECK(&nb_dbg_client_sysrepo, DEBUG_MODE_ALL)) | |
649 | DEBUG_CLEAR(&nb_dbg_client_sysrepo); | |
650 | ||
651 | return 0; | |
652 | } | |
653 | ||
654 | static void frr_sr_cli_init(void) | |
655 | { | |
656 | hook_register(nb_client_debug_config_write, frr_sr_debug_config_write); | |
657 | hook_register(nb_client_debug_set_all, frr_sr_debug_set_all); | |
658 | ||
659 | install_element(ENABLE_NODE, &debug_nb_sr_cmd); | |
660 | install_element(CONFIG_NODE, &debug_nb_sr_cmd); | |
661 | } | |
662 | ||
a7ca2199 | 663 | /* FRR's Sysrepo initialization. */ |
24ed137c | 664 | static int frr_sr_init(void) |
a7ca2199 RW |
665 | { |
666 | struct yang_module *module; | |
24ed137c | 667 | int ret; |
a7ca2199 RW |
668 | |
669 | /* Connect to Sysrepo. */ | |
24ed137c | 670 | ret = sr_connect(SR_CONN_DEFAULT, &connection); |
a7ca2199 RW |
671 | if (ret != SR_ERR_OK) { |
672 | flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_connect(): %s", __func__, | |
673 | sr_strerror(ret)); | |
674 | goto cleanup; | |
675 | } | |
676 | ||
677 | /* Start session. */ | |
24ed137c | 678 | ret = sr_session_start(connection, SR_DS_RUNNING, &session); |
a7ca2199 RW |
679 | if (ret != SR_ERR_OK) { |
680 | flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_session_start(): %s", | |
681 | __func__, sr_strerror(ret)); | |
682 | goto cleanup; | |
683 | } | |
684 | ||
685 | /* Perform subscriptions. */ | |
686 | RB_FOREACH (module, yang_modules, &yang_modules) { | |
24ed137c RW |
687 | int event_pipe; |
688 | ||
a7ca2199 | 689 | frr_sr_subscribe_config(module); |
8d869d37 RW |
690 | yang_snodes_iterate(module->info, frr_sr_subscribe_state, 0, |
691 | module); | |
692 | yang_snodes_iterate(module->info, frr_sr_subscribe_rpc, 0, | |
693 | module); | |
24ed137c RW |
694 | |
695 | /* Watch subscriptions. */ | |
696 | ret = sr_get_event_pipe(module->sr_subscription, &event_pipe); | |
697 | if (ret != SR_ERR_OK) { | |
698 | flog_err(EC_LIB_SYSREPO_INIT, | |
699 | "%s: sr_get_event_pipe(): %s", __func__, | |
700 | sr_strerror(ret)); | |
701 | goto cleanup; | |
702 | } | |
7640e3c6 | 703 | thread_add_read(master, frr_sr_read_cb, module, |
24ed137c | 704 | event_pipe, &module->sr_thread); |
a7ca2199 RW |
705 | } |
706 | ||
707 | hook_register(nb_notification_send, frr_sr_notification_send); | |
708 | ||
a7ca2199 RW |
709 | return 0; |
710 | ||
711 | cleanup: | |
712 | frr_sr_finish(); | |
713 | ||
714 | return -1; | |
715 | } | |
716 | ||
717 | static int frr_sr_finish(void) | |
718 | { | |
719 | struct yang_module *module; | |
720 | ||
721 | RB_FOREACH (module, yang_modules, &yang_modules) { | |
722 | if (!module->sr_subscription) | |
723 | continue; | |
24ed137c RW |
724 | sr_unsubscribe(module->sr_subscription); |
725 | THREAD_OFF(module->sr_thread); | |
a7ca2199 RW |
726 | } |
727 | ||
728 | if (session) | |
729 | sr_session_stop(session); | |
730 | if (connection) | |
731 | sr_disconnect(connection); | |
732 | ||
a7ca2199 RW |
733 | return 0; |
734 | } | |
735 | ||
2bafda27 | 736 | static int frr_sr_module_config_loaded(struct thread_master *tm) |
a7ca2199 RW |
737 | { |
738 | master = tm; | |
739 | ||
24ed137c | 740 | if (frr_sr_init() < 0) { |
a7ca2199 RW |
741 | flog_err(EC_LIB_SYSREPO_INIT, |
742 | "failed to initialize the Sysrepo module"); | |
743 | return -1; | |
744 | } | |
745 | ||
746 | hook_register(frr_fini, frr_sr_finish); | |
88e635ee RW |
747 | |
748 | return 0; | |
749 | } | |
750 | ||
751 | static int frr_sr_module_late_init(struct thread_master *tm) | |
752 | { | |
9eb2c0a1 | 753 | frr_sr_cli_init(); |
a7ca2199 RW |
754 | |
755 | return 0; | |
756 | } | |
757 | ||
758 | static int frr_sr_module_init(void) | |
759 | { | |
760 | hook_register(frr_late_init, frr_sr_module_late_init); | |
2bafda27 | 761 | hook_register(frr_config_post, frr_sr_module_config_loaded); |
a7ca2199 RW |
762 | |
763 | return 0; | |
764 | } | |
765 | ||
766 | FRR_MODULE_SETUP(.name = "frr_sysrepo", .version = FRR_VERSION, | |
767 | .description = "FRR sysrepo integration module", | |
80413c20 DL |
768 | .init = frr_sr_module_init, |
769 | ); |