]>
Commit | Line | Data |
---|---|---|
5bce33b3 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" | |
25 | #include "libfrr.h" | |
26 | #include "version.h" | |
27 | #include "northbound.h" | |
28 | ||
29 | #include <confd_lib.h> | |
30 | #include <confd_cdb.h> | |
31 | #include <confd_dp.h> | |
32 | #include <confd_maapi.h> | |
33 | ||
34 | DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module") | |
35 | ||
36 | static struct thread_master *master; | |
37 | static struct sockaddr confd_addr; | |
38 | static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock; | |
39 | static struct thread *t_cdb_sub, *t_dp_ctl, *t_dp_worker; | |
40 | static struct confd_daemon_ctx *dctx; | |
41 | static struct confd_notification_ctx *live_ctx; | |
42 | static bool confd_connected; | |
43 | static struct list *confd_spoints; | |
44 | ||
45 | static void frr_confd_finish_cdb(void); | |
46 | static void frr_confd_finish_dp(void); | |
47 | static int frr_confd_finish(void); | |
48 | ||
49 | #define flog_err_confd(funcname) \ | |
50 | flog_err(EC_LIB_LIBCONFD, "%s: %s() failed: %s (%d): %s", __func__, \ | |
51 | (funcname), confd_strerror(confd_errno), confd_errno, \ | |
52 | confd_lasterr()) | |
53 | ||
54 | ||
55 | /* ------------ Utils ------------ */ | |
56 | ||
57 | /* Get XPath string from ConfD hashed keypath. */ | |
58 | static void frr_confd_get_xpath(const confd_hkeypath_t *kp, char *xpath, | |
59 | size_t len) | |
60 | { | |
61 | char *p; | |
62 | ||
63 | confd_xpath_pp_kpath(xpath, len, 0, kp); | |
64 | ||
65 | /* | |
66 | * Replace double quotes by single quotes (the format accepted by the | |
67 | * northbound API). | |
68 | */ | |
69 | p = xpath; | |
70 | while ((p = strchr(p, '"')) != NULL) | |
71 | *p++ = '\''; | |
72 | } | |
73 | ||
74 | /* Convert ConfD binary value to a string. */ | |
75 | static int frr_confd_val2str(const char *xpath, const confd_value_t *value, | |
76 | char *string, size_t string_size) | |
77 | { | |
78 | struct confd_cs_node *csp; | |
79 | ||
80 | csp = confd_cs_node_cd(NULL, xpath); | |
81 | if (!csp) { | |
82 | flog_err_confd("confd_cs_node_cd"); | |
83 | return -1; | |
84 | } | |
85 | if (confd_val2str(csp->info.type, value, string, string_size) | |
86 | == CONFD_ERR) { | |
87 | flog_err_confd("confd_val2str"); | |
88 | return -1; | |
89 | } | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
94 | /* Obtain list keys from ConfD hashed keypath. */ | |
95 | static void frr_confd_hkeypath_get_keys(const confd_hkeypath_t *kp, | |
96 | struct yang_list_keys *keys) | |
97 | { | |
98 | memset(keys, 0, sizeof(*keys)); | |
99 | for (int i = 0; i < kp->len; i++) { | |
100 | if (kp->v[i][0].type != C_BUF) | |
101 | continue; | |
102 | ||
103 | for (int j = 0; kp->v[i][j].type != C_NOEXISTS; j++) { | |
80243aef | 104 | strlcpy(keys->key[keys->num], |
5bce33b3 | 105 | (char *)kp->v[i][j].val.buf.ptr, |
80243aef | 106 | sizeof(keys->key[keys->num])); |
5bce33b3 RW |
107 | keys->num++; |
108 | } | |
109 | } | |
110 | } | |
111 | ||
112 | /* Fill the current date and time into a confd_datetime structure. */ | |
113 | static void getdatetime(struct confd_datetime *datetime) | |
114 | { | |
115 | struct tm tm; | |
116 | struct timeval tv; | |
117 | ||
118 | gettimeofday(&tv, NULL); | |
119 | gmtime_r(&tv.tv_sec, &tm); | |
120 | ||
121 | memset(datetime, 0, sizeof(*datetime)); | |
122 | datetime->year = 1900 + tm.tm_year; | |
123 | datetime->month = tm.tm_mon + 1; | |
124 | datetime->day = tm.tm_mday; | |
125 | datetime->sec = tm.tm_sec; | |
126 | datetime->micro = tv.tv_usec; | |
127 | datetime->timezone = 0; | |
128 | datetime->timezone_minutes = 0; | |
129 | datetime->hour = tm.tm_hour; | |
130 | datetime->min = tm.tm_min; | |
131 | } | |
132 | ||
133 | /* ------------ CDB code ------------ */ | |
134 | ||
135 | struct cdb_iter_args { | |
136 | struct nb_config *candidate; | |
137 | bool error; | |
138 | }; | |
139 | ||
140 | static enum cdb_iter_ret | |
141 | frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op, | |
142 | confd_value_t *oldv, confd_value_t *newv, void *args) | |
143 | { | |
144 | char xpath[XPATH_MAXLEN]; | |
145 | struct nb_node *nb_node; | |
146 | enum nb_operation nb_op; | |
147 | struct cdb_iter_args *iter_args = args; | |
148 | char value_str[YANG_VALUE_MAXLEN]; | |
149 | struct yang_data *data; | |
150 | char *sb1, *sb2; | |
151 | int ret; | |
152 | ||
153 | frr_confd_get_xpath(kp, xpath, sizeof(xpath)); | |
154 | ||
155 | /* | |
156 | * HACK: obtain value of leaf-list elements from the XPath due to | |
157 | * a bug in the ConfD API. | |
158 | */ | |
159 | value_str[0] = '\0'; | |
160 | sb1 = strrchr(xpath, '['); | |
161 | sb2 = strrchr(xpath, ']'); | |
162 | if (sb1 && sb2 && !strchr(sb1, '=')) { | |
163 | *sb2 = '\0'; | |
164 | strlcpy(value_str, sb1 + 1, sizeof(value_str)); | |
165 | *sb1 = '\0'; | |
166 | } | |
167 | ||
168 | nb_node = nb_node_find(xpath); | |
169 | if (!nb_node) { | |
170 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
171 | "%s: unknown data path: %s", __func__, xpath); | |
172 | iter_args->error = true; | |
173 | return ITER_STOP; | |
174 | } | |
175 | ||
176 | /* Map operation values. */ | |
177 | switch (cdb_op) { | |
178 | case MOP_CREATED: | |
179 | nb_op = NB_OP_CREATE; | |
180 | break; | |
181 | case MOP_DELETED: | |
182 | nb_op = NB_OP_DELETE; | |
183 | break; | |
184 | case MOP_VALUE_SET: | |
185 | if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode)) | |
186 | nb_op = NB_OP_MODIFY; | |
187 | else | |
188 | /* Ignore list keys modifications. */ | |
189 | return ITER_RECURSE; | |
190 | break; | |
191 | case MOP_MOVED_AFTER: | |
192 | nb_op = NB_OP_MOVE; | |
193 | break; | |
194 | case MOP_MODIFIED: | |
195 | /* We're not interested on this. */ | |
196 | return ITER_RECURSE; | |
197 | default: | |
198 | flog_err(EC_LIB_DEVELOPMENT, | |
199 | "%s: unexpected operation %u [xpath %s]", __func__, | |
200 | cdb_op, xpath); | |
201 | iter_args->error = true; | |
202 | return ITER_STOP; | |
203 | } | |
204 | ||
205 | /* Convert ConfD value to a string. */ | |
206 | if (nb_node->snode->nodetype != LYS_LEAFLIST && newv | |
207 | && frr_confd_val2str(nb_node->xpath, newv, value_str, | |
208 | sizeof(value_str)) | |
209 | != 0) { | |
210 | flog_err(EC_LIB_CONFD_DATA_CONVERT, | |
211 | "%s: failed to convert ConfD value to a string", | |
212 | __func__); | |
213 | iter_args->error = true; | |
214 | return ITER_STOP; | |
215 | } | |
216 | ||
217 | /* Edit the candidate configuration. */ | |
218 | data = yang_data_new(xpath, value_str); | |
219 | ret = nb_candidate_edit(iter_args->candidate, nb_node, nb_op, xpath, | |
220 | NULL, data); | |
221 | yang_data_free(data); | |
222 | if (ret != NB_OK) { | |
223 | flog_warn( | |
224 | EC_LIB_NB_CANDIDATE_EDIT_ERROR, | |
225 | "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", | |
226 | __func__, nb_operation_name(nb_op), xpath); | |
227 | iter_args->error = true; | |
228 | return ITER_STOP; | |
229 | } | |
230 | ||
231 | return ITER_RECURSE; | |
232 | } | |
233 | ||
234 | static int frr_confd_cdb_read_cb(struct thread *thread) | |
235 | { | |
236 | int fd = THREAD_FD(thread); | |
237 | int *subp = NULL; | |
238 | enum cdb_sub_notification cdb_ev; | |
239 | int flags; | |
240 | int reslen = 0; | |
241 | struct nb_config *candidate; | |
242 | struct cdb_iter_args iter_args; | |
243 | int ret; | |
244 | ||
245 | thread = NULL; | |
246 | thread_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &thread); | |
247 | ||
248 | if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen) | |
249 | != CONFD_OK) { | |
250 | flog_err_confd("cdb_read_subscription_socket2"); | |
251 | return -1; | |
252 | } | |
253 | ||
254 | /* | |
255 | * Ignore CDB_SUB_ABORT and CDB_SUB_COMMIT. We'll leverage the | |
256 | * northbound layer itself to abort or apply the configuration changes | |
257 | * when a transaction is created. | |
258 | */ | |
259 | if (cdb_ev != CDB_SUB_PREPARE) { | |
260 | free(subp); | |
261 | if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) | |
262 | != CONFD_OK) { | |
263 | flog_err_confd("cdb_sync_subscription_socket"); | |
264 | return -1; | |
265 | } | |
266 | return 0; | |
267 | } | |
268 | ||
269 | candidate = nb_config_dup(running_config); | |
270 | ||
271 | /* Iterate over all configuration changes. */ | |
272 | iter_args.candidate = candidate; | |
273 | iter_args.error = false; | |
274 | for (int i = 0; i < reslen; i++) { | |
275 | if (cdb_diff_iterate(fd, subp[i], frr_confd_cdb_diff_iter, | |
276 | ITER_WANT_PREV, &iter_args) | |
277 | != CONFD_OK) { | |
278 | flog_err_confd("cdb_diff_iterate"); | |
279 | } | |
280 | } | |
281 | free(subp); | |
282 | ||
283 | if (iter_args.error) { | |
284 | nb_config_free(candidate); | |
285 | ||
286 | if (cdb_sub_abort_trans( | |
287 | cdb_sub_sock, CONFD_ERRCODE_APPLICATION_INTERNAL, 0, | |
288 | 0, "Couldn't apply configuration changes") | |
289 | != CONFD_OK) { | |
290 | flog_err_confd("cdb_sub_abort_trans"); | |
291 | return -1; | |
292 | } | |
293 | return 0; | |
294 | } | |
295 | ||
296 | ret = nb_candidate_commit(candidate, NB_CLIENT_CONFD, true, NULL, NULL); | |
297 | nb_config_free(candidate); | |
298 | if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { | |
299 | enum confd_errcode errcode; | |
300 | const char *errmsg; | |
301 | ||
302 | switch (ret) { | |
303 | case NB_ERR_LOCKED: | |
304 | errcode = CONFD_ERRCODE_IN_USE; | |
305 | errmsg = "Configuration is locked by another process"; | |
306 | break; | |
307 | case NB_ERR_RESOURCE: | |
308 | errcode = CONFD_ERRCODE_RESOURCE_DENIED; | |
309 | errmsg = "Failed do allocate resources"; | |
310 | break; | |
311 | default: | |
312 | errcode = CONFD_ERRCODE_INTERNAL; | |
313 | errmsg = "Internal error"; | |
314 | break; | |
315 | } | |
316 | ||
317 | if (cdb_sub_abort_trans(cdb_sub_sock, errcode, 0, 0, "%s", | |
318 | errmsg) | |
319 | != CONFD_OK) { | |
320 | flog_err_confd("cdb_sub_abort_trans"); | |
321 | return -1; | |
322 | } | |
323 | } else { | |
324 | if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) | |
325 | != CONFD_OK) { | |
326 | flog_err_confd("cdb_sync_subscription_socket"); | |
327 | return -1; | |
328 | } | |
329 | } | |
330 | ||
331 | return 0; | |
332 | } | |
333 | ||
334 | /* Trigger CDB subscriptions to read the startup configuration. */ | |
335 | static void *thread_cdb_trigger_subscriptions(void *data) | |
336 | { | |
337 | int sock; | |
338 | int *sub_points = NULL, len = 0; | |
339 | struct listnode *node; | |
340 | int *spoint; | |
341 | int i = 0; | |
342 | ||
343 | /* Create CDB data socket. */ | |
344 | sock = socket(PF_INET, SOCK_STREAM, 0); | |
345 | if (sock < 0) { | |
346 | flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", | |
347 | __func__, safe_strerror(errno)); | |
348 | return NULL; | |
349 | } | |
350 | ||
351 | if (cdb_connect(sock, CDB_DATA_SOCKET, &confd_addr, | |
352 | sizeof(struct sockaddr_in)) | |
353 | != CONFD_OK) { | |
354 | flog_err_confd("cdb_connect"); | |
355 | return NULL; | |
356 | } | |
357 | ||
358 | /* | |
359 | * Fill array containing the subscription point of all loaded YANG | |
360 | * modules. | |
361 | */ | |
362 | len = listcount(confd_spoints); | |
363 | sub_points = XCALLOC(MTYPE_CONFD, len * sizeof(int)); | |
364 | for (ALL_LIST_ELEMENTS_RO(confd_spoints, node, spoint)) | |
365 | sub_points[i++] = *spoint; | |
366 | ||
367 | if (cdb_trigger_subscriptions(sock, sub_points, len) != CONFD_OK) { | |
368 | flog_err_confd("cdb_trigger_subscriptions"); | |
369 | return NULL; | |
370 | } | |
371 | ||
372 | /* Cleanup and exit thread. */ | |
373 | XFREE(MTYPE_CONFD, sub_points); | |
374 | cdb_close(sock); | |
375 | ||
376 | return NULL; | |
377 | } | |
378 | ||
379 | static int frr_confd_init_cdb(void) | |
380 | { | |
381 | struct yang_module *module; | |
382 | pthread_t cdb_trigger_thread; | |
383 | ||
384 | /* Create CDB subscription socket. */ | |
385 | cdb_sub_sock = socket(PF_INET, SOCK_STREAM, 0); | |
386 | if (cdb_sub_sock < 0) { | |
387 | flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", | |
388 | __func__, safe_strerror(errno)); | |
389 | return -1; | |
390 | } | |
391 | ||
392 | if (cdb_connect(cdb_sub_sock, CDB_SUBSCRIPTION_SOCKET, &confd_addr, | |
393 | sizeof(struct sockaddr_in)) | |
394 | != CONFD_OK) { | |
395 | flog_err_confd("cdb_connect"); | |
396 | goto error; | |
397 | } | |
398 | ||
399 | /* Subscribe to all loaded YANG data modules. */ | |
400 | confd_spoints = list_new(); | |
401 | RB_FOREACH (module, yang_modules, &yang_modules) { | |
402 | struct lys_node *snode; | |
403 | ||
404 | module->confd_hash = confd_str2hash(module->info->ns); | |
405 | if (module->confd_hash == 0) { | |
406 | flog_err( | |
407 | EC_LIB_LIBCONFD, | |
408 | "%s: failed to find hash value for namespace %s", | |
409 | __func__, module->info->ns); | |
410 | goto error; | |
411 | } | |
412 | ||
413 | /* | |
414 | * The CDB API doesn't provide a mechanism to subscribe to an | |
415 | * entire YANG module. So we have to find the top level | |
416 | * nodes ourselves and subscribe to their paths. | |
417 | */ | |
418 | LY_TREE_FOR (module->info->data, snode) { | |
419 | struct nb_node *nb_node; | |
420 | int *spoint; | |
421 | int ret; | |
422 | ||
423 | switch (snode->nodetype) { | |
424 | case LYS_CONTAINER: | |
425 | case LYS_LEAF: | |
426 | case LYS_LEAFLIST: | |
427 | case LYS_LIST: | |
428 | break; | |
429 | default: | |
430 | continue; | |
431 | } | |
432 | ||
433 | nb_node = snode->priv; | |
434 | if (debug_northbound) | |
435 | zlog_debug("%s: subscribing to '%s'", __func__, | |
436 | nb_node->xpath); | |
437 | ||
438 | spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint)); | |
439 | ret = cdb_subscribe2( | |
440 | cdb_sub_sock, CDB_SUB_RUNNING_TWOPHASE, | |
441 | CDB_SUB_WANT_ABORT_ON_ABORT, 3, spoint, | |
442 | module->confd_hash, nb_node->xpath); | |
443 | if (ret != CONFD_OK) { | |
444 | flog_err_confd("cdb_subscribe2"); | |
445 | XFREE(MTYPE_CONFD, spoint); | |
446 | } | |
447 | listnode_add(confd_spoints, spoint); | |
448 | } | |
449 | } | |
450 | ||
451 | if (cdb_subscribe_done(cdb_sub_sock) != CONFD_OK) { | |
452 | flog_err_confd("cdb_subscribe_done"); | |
453 | goto error; | |
454 | } | |
455 | ||
456 | /* Create short lived pthread to trigger the CDB subscriptions. */ | |
457 | if (pthread_create(&cdb_trigger_thread, NULL, | |
458 | thread_cdb_trigger_subscriptions, NULL)) { | |
459 | flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s", | |
460 | __func__, safe_strerror(errno)); | |
461 | goto error; | |
462 | } | |
463 | pthread_detach(cdb_trigger_thread); | |
464 | ||
465 | thread_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock, | |
466 | &t_cdb_sub); | |
467 | ||
468 | return 0; | |
469 | ||
470 | error: | |
471 | frr_confd_finish_cdb(); | |
472 | ||
473 | return -1; | |
474 | } | |
475 | ||
476 | static void frr_confd_finish_cdb(void) | |
477 | { | |
478 | if (cdb_sub_sock > 0) { | |
479 | THREAD_OFF(t_cdb_sub); | |
480 | cdb_close(cdb_sub_sock); | |
481 | } | |
482 | } | |
483 | ||
484 | /* ------------ DP code ------------ */ | |
485 | ||
486 | static int frr_confd_transaction_init(struct confd_trans_ctx *tctx) | |
487 | { | |
488 | confd_trans_set_fd(tctx, dp_worker_sock); | |
489 | ||
490 | return CONFD_OK; | |
491 | } | |
492 | ||
493 | static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx, | |
494 | confd_hkeypath_t *kp) | |
495 | { | |
496 | struct nb_node *nb_node, *parent_list; | |
497 | char xpath[BUFSIZ]; | |
498 | struct yang_list_keys keys; | |
499 | struct yang_data *data; | |
500 | confd_value_t v; | |
501 | const void *list_entry = NULL; | |
502 | ||
503 | frr_confd_get_xpath(kp, xpath, sizeof(xpath)); | |
504 | ||
505 | nb_node = nb_node_find(xpath); | |
506 | if (!nb_node) { | |
507 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
508 | "%s: unknown data path: %s", __func__, xpath); | |
509 | confd_data_reply_not_found(tctx); | |
510 | return CONFD_OK; | |
511 | } | |
512 | ||
513 | parent_list = nb_node->parent_list; | |
514 | if (parent_list) { | |
515 | frr_confd_hkeypath_get_keys(kp, &keys); | |
516 | list_entry = parent_list->cbs.lookup_entry(&keys); | |
517 | if (!list_entry) { | |
518 | flog_warn(EC_LIB_NB_CB_STATE, | |
519 | "%s: list entry not found: %s", __func__, | |
520 | xpath); | |
521 | confd_data_reply_not_found(tctx); | |
522 | return CONFD_OK; | |
523 | } | |
524 | } | |
525 | ||
526 | data = nb_node->cbs.get_elem(xpath, list_entry); | |
527 | if (data) { | |
528 | if (data->value) { | |
529 | CONFD_SET_STR(&v, data->value); | |
530 | confd_data_reply_value(tctx, &v); | |
531 | } else | |
532 | confd_data_reply_found(tctx); | |
533 | yang_data_free(data); | |
534 | } else | |
535 | confd_data_reply_not_found(tctx); | |
536 | ||
537 | return CONFD_OK; | |
538 | } | |
539 | ||
540 | static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, | |
541 | confd_hkeypath_t *kp, long next) | |
542 | { | |
543 | struct nb_node *nb_node; | |
544 | char xpath[BUFSIZ]; | |
545 | struct yang_list_keys keys; | |
546 | const void *nb_next; | |
547 | confd_value_t v[LIST_MAXKEYS]; | |
548 | ||
549 | frr_confd_get_xpath(kp, xpath, sizeof(xpath)); | |
550 | ||
551 | nb_node = nb_node_find(xpath); | |
552 | if (!nb_node) { | |
553 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
554 | "%s: unknown data path: %s", __func__, xpath); | |
555 | confd_data_reply_next_key(tctx, NULL, -1, -1); | |
556 | return CONFD_OK; | |
557 | } | |
558 | ||
559 | nb_next = nb_node->cbs.get_next(xpath, | |
560 | (next == -1) ? NULL : (void *)next); | |
561 | if (!nb_next) { | |
562 | /* End of the list. */ | |
563 | confd_data_reply_next_key(tctx, NULL, -1, -1); | |
564 | return CONFD_OK; | |
565 | } | |
566 | if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) { | |
567 | flog_warn(EC_LIB_NB_CB_STATE, "%s: failed to get list keys", | |
568 | __func__); | |
569 | confd_data_reply_next_key(tctx, NULL, -1, -1); | |
570 | return CONFD_OK; | |
571 | } | |
572 | ||
573 | /* Feed keys to ConfD. */ | |
574 | for (size_t i = 0; i < keys.num; i++) | |
80243aef | 575 | CONFD_SET_STR(&v[i], keys.key[i]); |
5bce33b3 RW |
576 | confd_data_reply_next_key(tctx, v, keys.num, (long)nb_next); |
577 | ||
578 | return CONFD_OK; | |
579 | } | |
580 | ||
581 | /* | |
582 | * Optional callback - implemented for performance reasons. | |
583 | */ | |
584 | static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, | |
585 | confd_hkeypath_t *kp) | |
586 | { | |
587 | struct nb_node *nb_node; | |
588 | char xpath[BUFSIZ]; | |
589 | char xpath_children[XPATH_MAXLEN]; | |
590 | char xpath_child[XPATH_MAXLEN]; | |
591 | struct yang_list_keys keys; | |
592 | struct list *elements; | |
593 | struct yang_data *data; | |
594 | const void *list_entry; | |
595 | struct ly_set *set; | |
596 | confd_value_t *values; | |
597 | ||
598 | frr_confd_get_xpath(kp, xpath, sizeof(xpath)); | |
599 | ||
600 | nb_node = nb_node_find(xpath); | |
601 | if (!nb_node) { | |
602 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
603 | "%s: unknown data path: %s", __func__, xpath); | |
604 | confd_data_reply_not_found(tctx); | |
605 | return CONFD_OK; | |
606 | } | |
607 | ||
608 | frr_confd_hkeypath_get_keys(kp, &keys); | |
609 | list_entry = nb_node->cbs.lookup_entry(&keys); | |
610 | if (!list_entry) { | |
611 | flog_warn(EC_LIB_NB_CB_STATE, "%s: list entry not found: %s", | |
612 | __func__, xpath); | |
613 | confd_data_reply_not_found(tctx); | |
614 | return CONFD_OK; | |
615 | } | |
616 | ||
617 | /* Find list child nodes. */ | |
618 | snprintf(xpath_children, sizeof(xpath_children), "%s/*", xpath); | |
619 | set = lys_find_path(nb_node->snode->module, NULL, xpath_children); | |
620 | if (!set) { | |
621 | flog_warn(EC_LIB_LIBYANG, "%s: lys_find_path() failed", | |
622 | __func__); | |
623 | return CONFD_ERR; | |
624 | } | |
625 | ||
626 | elements = yang_data_list_new(); | |
627 | values = XMALLOC(MTYPE_CONFD, set->number * sizeof(*values)); | |
628 | ||
629 | /* Loop through list child nodes. */ | |
630 | for (size_t i = 0; i < set->number; i++) { | |
631 | struct lys_node *child; | |
632 | struct nb_node *nb_node_child; | |
633 | ||
634 | child = set->set.s[i]; | |
635 | nb_node_child = child->priv; | |
636 | ||
637 | snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath, | |
638 | child->name); | |
639 | ||
640 | data = nb_node_child->cbs.get_elem(xpath_child, list_entry); | |
641 | if (data) { | |
642 | if (data->value) | |
643 | CONFD_SET_STR(&values[i], data->value); | |
644 | else | |
645 | CONFD_SET_NOEXISTS(&values[i]); | |
646 | listnode_add(elements, data); | |
647 | } else | |
648 | CONFD_SET_NOEXISTS(&values[i]); | |
649 | } | |
650 | ||
651 | confd_data_reply_value_array(tctx, values, set->number); | |
652 | ||
653 | /* Release memory. */ | |
654 | ly_set_free(set); | |
655 | XFREE(MTYPE_CONFD, values); | |
656 | list_delete(&elements); | |
657 | ||
658 | return CONFD_OK; | |
659 | } | |
660 | ||
661 | /* | |
662 | * Optional callback - implemented for performance reasons. | |
663 | */ | |
664 | static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, | |
665 | confd_hkeypath_t *kp, long next) | |
666 | { | |
667 | char xpath[BUFSIZ]; | |
668 | char xpath_children[XPATH_MAXLEN]; | |
669 | struct nb_node *nb_node; | |
670 | struct ly_set *set; | |
671 | struct list *elements; | |
672 | const void *nb_next; | |
673 | #define CONFD_OBJECTS_PER_TIME 100 | |
674 | struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1]; | |
675 | int nobjects = 0; | |
676 | ||
677 | frr_confd_get_xpath(kp, xpath, sizeof(xpath)); | |
678 | ||
679 | nb_node = nb_node_find(xpath); | |
680 | if (!nb_node) { | |
681 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
682 | "%s: unknown data path: %s", __func__, xpath); | |
683 | confd_data_reply_next_object_array(tctx, NULL, 0, 0); | |
684 | return CONFD_OK; | |
685 | } | |
686 | ||
687 | /* Find list child nodes. */ | |
688 | snprintf(xpath_children, sizeof(xpath_children), "%s/*", xpath); | |
689 | set = lys_find_path(nb_node->snode->module, NULL, xpath_children); | |
690 | if (!set) { | |
691 | flog_warn(EC_LIB_LIBYANG, "%s: lys_find_path() failed", | |
692 | __func__); | |
693 | return CONFD_ERR; | |
694 | } | |
695 | ||
696 | elements = yang_data_list_new(); | |
697 | nb_next = (next == -1) ? NULL : (void *)next; | |
698 | ||
699 | memset(objects, 0, sizeof(objects)); | |
700 | for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) { | |
701 | struct confd_next_object *object; | |
702 | struct yang_list_keys keys; | |
703 | struct yang_data *data; | |
704 | const void *list_entry; | |
705 | ||
706 | object = &objects[j]; | |
707 | ||
708 | nb_next = nb_node->cbs.get_next(xpath, nb_next); | |
709 | if (!nb_next) | |
710 | /* End of the list. */ | |
711 | break; | |
712 | ||
713 | if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) { | |
714 | flog_warn(EC_LIB_NB_CB_STATE, | |
715 | "%s: failed to get list keys", __func__); | |
716 | continue; | |
717 | } | |
718 | object->next = (long)nb_next; | |
719 | ||
720 | list_entry = nb_node->cbs.lookup_entry(&keys); | |
721 | if (!list_entry) { | |
722 | flog_warn(EC_LIB_NB_CB_STATE, | |
723 | "%s: failed to lookup list entry", __func__); | |
724 | continue; | |
725 | } | |
726 | ||
727 | object->v = XMALLOC(MTYPE_CONFD, | |
728 | set->number * sizeof(confd_value_t)); | |
729 | ||
730 | /* Loop through list child nodes. */ | |
731 | for (unsigned int i = 0; i < set->number; i++) { | |
732 | struct lys_node *child; | |
733 | struct nb_node *nb_node_child; | |
734 | char xpath_child[XPATH_MAXLEN]; | |
735 | confd_value_t *v = &object->v[i]; | |
736 | ||
737 | child = set->set.s[i]; | |
738 | nb_node_child = child->priv; | |
739 | ||
740 | snprintf(xpath_child, sizeof(xpath_child), "%s/%s", | |
741 | xpath, child->name); | |
742 | ||
743 | data = nb_node_child->cbs.get_elem(xpath_child, | |
744 | list_entry); | |
745 | if (data) { | |
746 | if (data->value) | |
747 | CONFD_SET_STR(v, data->value); | |
748 | else | |
749 | CONFD_SET_NOEXISTS(v); | |
750 | listnode_add(elements, data); | |
751 | } else | |
752 | CONFD_SET_NOEXISTS(v); | |
753 | } | |
754 | object->n = set->number; | |
755 | nobjects++; | |
756 | } | |
757 | ly_set_free(set); | |
758 | ||
759 | if (nobjects == 0) { | |
760 | confd_data_reply_next_object_array(tctx, NULL, 0, 0); | |
761 | list_delete(&elements); | |
762 | return CONFD_OK; | |
763 | } | |
764 | ||
765 | /* Detect end of the list. */ | |
766 | if (!nb_next) { | |
767 | nobjects++; | |
768 | objects[nobjects].v = NULL; | |
769 | } | |
770 | ||
771 | /* Reply to ConfD. */ | |
772 | confd_data_reply_next_object_arrays(tctx, objects, nobjects, 0); | |
773 | if (!nb_next) | |
774 | nobjects--; | |
775 | ||
776 | /* Release memory. */ | |
777 | list_delete(&elements); | |
778 | for (int j = 0; j < nobjects; j++) { | |
779 | struct confd_next_object *object; | |
780 | ||
781 | object = &objects[j]; | |
782 | XFREE(MTYPE_CONFD, object->v); | |
783 | } | |
784 | ||
785 | return CONFD_OK; | |
786 | } | |
787 | ||
788 | static int frr_confd_notification_send(const char *xpath, | |
789 | struct list *arguments) | |
790 | { | |
791 | struct nb_node *nb_node; | |
792 | struct yang_module *module; | |
793 | struct confd_datetime now; | |
794 | confd_tag_value_t *values; | |
795 | int nvalues; | |
796 | int i = 0; | |
797 | struct yang_data *data; | |
798 | struct listnode *node; | |
799 | int ret; | |
800 | ||
801 | nb_node = nb_node_find(xpath); | |
802 | if (!nb_node) { | |
803 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
804 | "%s: unknown data path: %s", __func__, xpath); | |
805 | return -1; | |
806 | } | |
807 | module = yang_module_find(nb_node->snode->module->name); | |
808 | assert(module); | |
809 | ||
810 | nvalues = 2; | |
811 | if (arguments) | |
812 | nvalues += listcount(arguments); | |
813 | ||
814 | values = XMALLOC(MTYPE_CONFD, nvalues * sizeof(*values)); | |
815 | ||
816 | CONFD_SET_TAG_XMLBEGIN(&values[i++], nb_node->confd_hash, | |
817 | module->confd_hash); | |
818 | for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) { | |
80243aef RW |
819 | struct nb_node *nb_node_arg; |
820 | ||
821 | nb_node_arg = nb_node_find(data->xpath); | |
822 | if (!nb_node_arg) { | |
823 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
824 | "%s: unknown data path: %s", __func__, | |
825 | data->xpath); | |
826 | XFREE(MTYPE_CONFD, values); | |
827 | return NB_ERR; | |
828 | } | |
5bce33b3 | 829 | |
80243aef | 830 | CONFD_SET_TAG_STR(&values[i++], nb_node_arg->confd_hash, |
5bce33b3 RW |
831 | data->value); |
832 | } | |
833 | CONFD_SET_TAG_XMLEND(&values[i++], nb_node->confd_hash, | |
834 | module->confd_hash); | |
835 | ||
836 | getdatetime(&now); | |
837 | ret = confd_notification_send(live_ctx, &now, values, nvalues); | |
838 | ||
839 | /* Release memory. */ | |
840 | XFREE(MTYPE_CONFD, values); | |
841 | ||
842 | /* Map ConfD return code to northbound return code. */ | |
843 | switch (ret) { | |
844 | case CONFD_OK: | |
845 | return NB_OK; | |
846 | default: | |
847 | return NB_ERR; | |
848 | } | |
849 | } | |
850 | ||
851 | static int frr_confd_action_init(struct confd_user_info *uinfo) | |
852 | { | |
853 | confd_action_set_fd(uinfo, dp_worker_sock); | |
854 | ||
855 | return CONFD_OK; | |
856 | } | |
857 | ||
858 | static int frr_confd_action_execute(struct confd_user_info *uinfo, | |
859 | struct xml_tag *name, confd_hkeypath_t *kp, | |
860 | confd_tag_value_t *params, int nparams) | |
861 | { | |
862 | char xpath[BUFSIZ]; | |
863 | struct nb_node *nb_node; | |
864 | struct list *input; | |
865 | struct list *output; | |
866 | struct yang_data *data; | |
867 | confd_tag_value_t *reply; | |
868 | int ret = CONFD_OK; | |
869 | ||
870 | /* Getting the XPath is tricky. */ | |
871 | if (kp) { | |
872 | /* This is a YANG RPC. */ | |
873 | frr_confd_get_xpath(kp, xpath, sizeof(xpath)); | |
874 | strlcat(xpath, "/", sizeof(xpath)); | |
875 | strlcat(xpath, confd_hash2str(name->tag), sizeof(xpath)); | |
876 | } else { | |
877 | /* This is a YANG action. */ | |
878 | snprintf(xpath, sizeof(xpath), "/%s:%s", | |
879 | confd_ns2prefix(name->ns), confd_hash2str(name->tag)); | |
880 | } | |
881 | ||
882 | nb_node = nb_node_find(xpath); | |
883 | if (!nb_node) { | |
884 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
885 | "%s: unknown data path: %s", __func__, xpath); | |
886 | return CONFD_ERR; | |
887 | } | |
888 | ||
889 | input = yang_data_list_new(); | |
890 | output = yang_data_list_new(); | |
891 | ||
892 | /* Process input nodes. */ | |
893 | for (int i = 0; i < nparams; i++) { | |
894 | char xpath_input[BUFSIZ]; | |
895 | char value_str[YANG_VALUE_MAXLEN]; | |
896 | ||
897 | snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath, | |
898 | confd_hash2str(params[i].tag.tag)); | |
899 | ||
900 | if (frr_confd_val2str(xpath_input, ¶ms[i].v, value_str, | |
901 | sizeof(value_str)) | |
902 | != 0) { | |
903 | flog_err( | |
904 | EC_LIB_CONFD_DATA_CONVERT, | |
905 | "%s: failed to convert ConfD value to a string", | |
906 | __func__); | |
907 | ret = CONFD_ERR; | |
908 | goto exit; | |
909 | } | |
910 | ||
911 | data = yang_data_new(xpath_input, value_str); | |
912 | listnode_add(input, data); | |
913 | } | |
914 | ||
915 | /* Execute callback registered for this XPath. */ | |
916 | if (nb_node->cbs.rpc(xpath, input, output) != NB_OK) { | |
917 | flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s", | |
918 | __func__, xpath); | |
919 | ret = CONFD_ERR; | |
920 | goto exit; | |
921 | } | |
922 | ||
923 | /* Process output nodes. */ | |
924 | if (listcount(output) > 0) { | |
925 | struct listnode *node; | |
926 | int i = 0; | |
927 | ||
928 | reply = XMALLOC(MTYPE_CONFD, | |
929 | listcount(output) * sizeof(*reply)); | |
930 | ||
931 | for (ALL_LIST_ELEMENTS_RO(output, node, data)) { | |
80243aef | 932 | struct nb_node *nb_node_output; |
5bce33b3 RW |
933 | int hash; |
934 | ||
80243aef RW |
935 | nb_node_output = nb_node_find(data->xpath); |
936 | if (!nb_node_output) { | |
937 | flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, | |
938 | "%s: unknown data path: %s", __func__, | |
939 | data->xpath); | |
940 | goto exit; | |
941 | } | |
942 | ||
943 | hash = confd_str2hash(nb_node_output->snode->name); | |
5bce33b3 RW |
944 | CONFD_SET_TAG_STR(&reply[i++], hash, data->value); |
945 | } | |
946 | confd_action_reply_values(uinfo, reply, listcount(output)); | |
947 | XFREE(MTYPE_CONFD, reply); | |
948 | } | |
949 | ||
950 | exit: | |
951 | /* Release memory. */ | |
952 | list_delete(&input); | |
953 | list_delete(&output); | |
954 | ||
955 | return ret; | |
956 | } | |
957 | ||
958 | ||
959 | static int frr_confd_dp_read(struct thread *thread) | |
960 | { | |
961 | struct confd_daemon_ctx *dctx = THREAD_ARG(thread); | |
962 | int fd = THREAD_FD(thread); | |
963 | int ret; | |
964 | ||
965 | thread = NULL; | |
966 | thread_add_read(master, frr_confd_dp_read, dctx, fd, &thread); | |
967 | ||
968 | ret = confd_fd_ready(dctx, fd); | |
969 | if (ret == CONFD_EOF) { | |
970 | flog_err_confd("confd_fd_ready"); | |
971 | return -1; | |
972 | } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) { | |
973 | flog_err_confd("confd_fd_ready"); | |
974 | return -1; | |
975 | } | |
976 | ||
977 | return 0; | |
978 | } | |
979 | ||
e0ccfad2 | 980 | static int frr_confd_subscribe_state(const struct lys_node *snode, void *arg) |
5bce33b3 RW |
981 | { |
982 | struct nb_node *nb_node = snode->priv; | |
e0ccfad2 | 983 | struct confd_data_cbs *data_cbs = arg; |
5bce33b3 | 984 | |
db452508 | 985 | if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R)) |
e0ccfad2 | 986 | return YANG_ITER_CONTINUE; |
5bce33b3 | 987 | /* We only need to subscribe to the root of the state subtrees. */ |
db452508 | 988 | if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R)) |
e0ccfad2 | 989 | return YANG_ITER_CONTINUE; |
5bce33b3 RW |
990 | |
991 | if (debug_northbound) | |
992 | zlog_debug("%s: providing data to '%s' (callpoint %s)", | |
993 | __func__, nb_node->xpath, snode->name); | |
994 | ||
995 | strlcpy(data_cbs->callpoint, snode->name, sizeof(data_cbs->callpoint)); | |
996 | if (confd_register_data_cb(dctx, data_cbs) != CONFD_OK) | |
997 | flog_err_confd("confd_register_data_cb"); | |
e0ccfad2 RW |
998 | |
999 | return YANG_ITER_CONTINUE; | |
5bce33b3 RW |
1000 | } |
1001 | ||
1002 | static int frr_confd_init_dp(const char *program_name) | |
1003 | { | |
1004 | struct confd_trans_cbs trans_cbs; | |
1005 | struct confd_data_cbs data_cbs; | |
1006 | struct confd_notification_stream_cbs ncbs; | |
1007 | struct confd_action_cbs acbs; | |
1008 | ||
1009 | /* Initialize daemon context. */ | |
1010 | dctx = confd_init_daemon(program_name); | |
1011 | if (!dctx) { | |
1012 | flog_err_confd("confd_init_daemon"); | |
1013 | goto error; | |
1014 | } | |
1015 | ||
1016 | /* | |
1017 | * Inform we want to receive YANG values as raw strings, and that we | |
1018 | * want to provide only strings in the reply functions, regardless of | |
1019 | * the YANG type. | |
1020 | */ | |
1021 | confd_set_daemon_flags(dctx, CONFD_DAEMON_FLAG_STRINGSONLY); | |
1022 | ||
1023 | /* Establish a control socket. */ | |
1024 | dp_ctl_sock = socket(PF_INET, SOCK_STREAM, 0); | |
1025 | if (dp_ctl_sock < 0) { | |
1026 | flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", | |
1027 | __func__, safe_strerror(errno)); | |
1028 | goto error; | |
1029 | } | |
1030 | ||
1031 | if (confd_connect(dctx, dp_ctl_sock, CONTROL_SOCKET, &confd_addr, | |
1032 | sizeof(struct sockaddr_in)) | |
1033 | != CONFD_OK) { | |
1034 | flog_err_confd("confd_connect"); | |
1035 | goto error; | |
1036 | } | |
1037 | ||
1038 | /* | |
1039 | * Establish a worker socket (only one since this plugin runs on a | |
1040 | * single thread). | |
1041 | */ | |
1042 | dp_worker_sock = socket(PF_INET, SOCK_STREAM, 0); | |
1043 | if (dp_worker_sock < 0) { | |
1044 | flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s", | |
1045 | __func__, safe_strerror(errno)); | |
1046 | goto error; | |
1047 | } | |
1048 | if (confd_connect(dctx, dp_worker_sock, WORKER_SOCKET, &confd_addr, | |
1049 | sizeof(struct sockaddr_in)) | |
1050 | != CONFD_OK) { | |
1051 | flog_err_confd("confd_connect"); | |
1052 | goto error; | |
1053 | } | |
1054 | ||
1055 | /* Register transaction callback functions. */ | |
1056 | memset(&trans_cbs, 0, sizeof(trans_cbs)); | |
1057 | trans_cbs.init = frr_confd_transaction_init; | |
1058 | confd_register_trans_cb(dctx, &trans_cbs); | |
1059 | ||
1060 | /* Register our read/write callbacks. */ | |
1061 | memset(&data_cbs, 0, sizeof(data_cbs)); | |
1062 | data_cbs.get_elem = frr_confd_data_get_elem; | |
1063 | data_cbs.exists_optional = frr_confd_data_get_elem; | |
1064 | data_cbs.get_next = frr_confd_data_get_next; | |
1065 | data_cbs.get_object = frr_confd_data_get_object; | |
1066 | data_cbs.get_next_object = frr_confd_data_get_next_object; | |
1067 | ||
1068 | /* | |
1069 | * Iterate over all loaded YANG modules and subscribe to the paths | |
1070 | * referent to state data. | |
1071 | */ | |
e0ccfad2 | 1072 | yang_snodes_iterate_all(frr_confd_subscribe_state, 0, &data_cbs); |
5bce33b3 RW |
1073 | |
1074 | /* Register notification stream. */ | |
1075 | memset(&ncbs, 0, sizeof(ncbs)); | |
1076 | ncbs.fd = dp_worker_sock; | |
1077 | /* | |
1078 | * RFC 5277 - Section 3.2.3: | |
1079 | * A NETCONF server implementation supporting the notification | |
1080 | * capability MUST support the "NETCONF" notification event | |
1081 | * stream. This stream contains all NETCONF XML event notifications | |
1082 | * supported by the NETCONF server. | |
1083 | */ | |
1084 | strlcpy(ncbs.streamname, "NETCONF", sizeof(ncbs.streamname)); | |
1085 | if (confd_register_notification_stream(dctx, &ncbs, &live_ctx) | |
1086 | != CONFD_OK) { | |
1087 | flog_err_confd("confd_register_notification_stream"); | |
1088 | goto error; | |
1089 | } | |
1090 | ||
1091 | /* Register the action handler callback. */ | |
1092 | memset(&acbs, 0, sizeof(acbs)); | |
1093 | strlcpy(acbs.actionpoint, "actionpoint", sizeof(acbs.actionpoint)); | |
1094 | acbs.init = frr_confd_action_init; | |
1095 | acbs.action = frr_confd_action_execute; | |
1096 | if (confd_register_action_cbs(dctx, &acbs) != CONFD_OK) { | |
1097 | flog_err_confd("confd_register_action_cbs"); | |
1098 | goto error; | |
1099 | } | |
1100 | ||
1101 | /* Notify we registered all callbacks we wanted. */ | |
1102 | if (confd_register_done(dctx) != CONFD_OK) { | |
1103 | flog_err_confd("confd_register_done"); | |
1104 | goto error; | |
1105 | } | |
1106 | ||
1107 | thread_add_read(master, frr_confd_dp_read, dctx, dp_ctl_sock, | |
1108 | &t_dp_ctl); | |
1109 | thread_add_read(master, frr_confd_dp_read, dctx, dp_worker_sock, | |
1110 | &t_dp_worker); | |
1111 | ||
1112 | return 0; | |
1113 | ||
1114 | error: | |
1115 | frr_confd_finish_dp(); | |
1116 | ||
1117 | return -1; | |
1118 | } | |
1119 | ||
1120 | static void frr_confd_finish_dp(void) | |
1121 | { | |
1122 | if (dp_worker_sock > 0) { | |
1123 | THREAD_OFF(t_dp_worker); | |
1124 | close(dp_worker_sock); | |
1125 | } | |
1126 | if (dp_ctl_sock > 0) { | |
1127 | THREAD_OFF(t_dp_ctl); | |
1128 | close(dp_ctl_sock); | |
1129 | } | |
1130 | if (dctx != NULL) | |
1131 | confd_release_daemon(dctx); | |
1132 | } | |
1133 | ||
1134 | /* ------------ Main ------------ */ | |
1135 | ||
e0ccfad2 RW |
1136 | static int frr_confd_calculate_snode_hash(const struct lys_node *snode, |
1137 | void *arg) | |
5bce33b3 RW |
1138 | { |
1139 | struct nb_node *nb_node = snode->priv; | |
1140 | ||
1141 | nb_node->confd_hash = confd_str2hash(snode->name); | |
e0ccfad2 RW |
1142 | |
1143 | return YANG_ITER_CONTINUE; | |
5bce33b3 RW |
1144 | } |
1145 | ||
1146 | static int frr_confd_init(const char *program_name) | |
1147 | { | |
1148 | struct sockaddr_in *confd_addr4 = (struct sockaddr_in *)&confd_addr; | |
1149 | int debuglevel = CONFD_SILENT; | |
1150 | int ret = -1; | |
1151 | ||
1152 | /* Initialize ConfD library. */ | |
1153 | confd_init(program_name, stderr, debuglevel); | |
1154 | ||
1155 | confd_addr4->sin_family = AF_INET; | |
1156 | confd_addr4->sin_addr.s_addr = inet_addr("127.0.0.1"); | |
1157 | confd_addr4->sin_port = htons(CONFD_PORT); | |
1158 | if (confd_load_schemas(&confd_addr, sizeof(struct sockaddr_in)) | |
1159 | != CONFD_OK) { | |
1160 | flog_err_confd("confd_load_schemas"); | |
1161 | return -1; | |
1162 | } | |
1163 | ||
1164 | ret = frr_confd_init_cdb(); | |
1165 | if (ret != 0) | |
1166 | goto error; | |
1167 | ||
1168 | ret = frr_confd_init_dp(program_name); | |
1169 | if (ret != 0) { | |
1170 | frr_confd_finish_cdb(); | |
1171 | goto error; | |
1172 | } | |
1173 | ||
e0ccfad2 | 1174 | yang_snodes_iterate_all(frr_confd_calculate_snode_hash, 0, NULL); |
5bce33b3 RW |
1175 | |
1176 | hook_register(nb_notification_send, frr_confd_notification_send); | |
1177 | ||
1178 | confd_connected = true; | |
1179 | return 0; | |
1180 | ||
1181 | error: | |
1182 | confd_free_schemas(); | |
1183 | ||
1184 | return ret; | |
1185 | } | |
1186 | ||
1187 | static int frr_confd_finish(void) | |
1188 | { | |
1189 | if (confd_connected == false) | |
1190 | return 0; | |
1191 | ||
1192 | frr_confd_finish_cdb(); | |
1193 | frr_confd_finish_dp(); | |
1194 | ||
1195 | confd_free_schemas(); | |
1196 | ||
1197 | confd_connected = false; | |
1198 | ||
1199 | return 0; | |
1200 | } | |
1201 | ||
1202 | static int frr_confd_module_late_init(struct thread_master *tm) | |
1203 | { | |
1204 | master = tm; | |
1205 | ||
1206 | if (frr_confd_init(frr_get_progname()) < 0) { | |
1207 | flog_err(EC_LIB_CONFD_INIT, | |
1208 | "failed to initialize the ConfD module"); | |
1209 | return -1; | |
1210 | } | |
1211 | ||
1212 | hook_register(frr_fini, frr_confd_finish); | |
1213 | ||
1214 | return 0; | |
1215 | } | |
1216 | ||
1217 | static int frr_confd_module_init(void) | |
1218 | { | |
1219 | hook_register(frr_late_init, frr_confd_module_late_init); | |
1220 | ||
1221 | return 0; | |
1222 | } | |
1223 | ||
1224 | FRR_MODULE_SETUP(.name = "frr_confd", .version = FRR_VERSION, | |
1225 | .description = "FRR ConfD integration module", | |
1226 | .init = frr_confd_module_init, ) |