]>
Commit | Line | Data |
---|---|---|
7d65b7b7 CH |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * MGMTD Backend Client Library api interfaces | |
4 | * Copyright (C) 2021 Vmware, Inc. | |
5 | * Pushpasis Sarkar <spushpasis@vmware.com> | |
6 | */ | |
7 | ||
8 | #include <zebra.h> | |
cfa0facb | 9 | #include "debug.h" |
070c5e7a | 10 | #include "compiler.h" |
7d65b7b7 CH |
11 | #include "libfrr.h" |
12 | #include "mgmtd/mgmt.h" | |
13 | #include "mgmt_be_client.h" | |
f82370b4 | 14 | #include "mgmt_msg.h" |
7d65b7b7 CH |
15 | #include "mgmt_pb.h" |
16 | #include "network.h" | |
cfa0facb | 17 | #include "northbound.h" |
7d65b7b7 CH |
18 | #include "stream.h" |
19 | #include "sockopt.h" | |
20 | ||
cfa0facb CH |
21 | #include "lib/mgmt_be_client_clippy.c" |
22 | ||
83b78f43 | 23 | #define MGMTD_BE_CLIENT_DBG(fmt, ...) \ |
08e8019c CH |
24 | DEBUGD(&mgmt_dbg_be_client, "BE-CLIENT: %s:" fmt, __func__, \ |
25 | ##__VA_ARGS__) | |
83b78f43 | 26 | #define MGMTD_BE_CLIENT_ERR(fmt, ...) \ |
08e8019c | 27 | zlog_err("BE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) |
cfa0facb CH |
28 | #define MGMTD_DBG_BE_CLIENT_CHECK() \ |
29 | DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_ALL) | |
7d65b7b7 | 30 | |
df6eb0bd DS |
31 | DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH, "backend transaction batch data"); |
32 | DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "backend transaction data"); | |
7d65b7b7 CH |
33 | |
34 | enum mgmt_be_txn_event { | |
35 | MGMTD_BE_TXN_PROC_SETCFG = 1, | |
36 | MGMTD_BE_TXN_PROC_GETCFG, | |
37 | MGMTD_BE_TXN_PROC_GETDATA | |
38 | }; | |
39 | ||
40 | struct mgmt_be_set_cfg_req { | |
41 | struct nb_cfg_change cfg_changes[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; | |
42 | uint16_t num_cfg_changes; | |
43 | }; | |
44 | ||
45 | struct mgmt_be_get_data_req { | |
46 | char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH]; | |
47 | uint16_t num_xpaths; | |
48 | }; | |
49 | ||
50 | struct mgmt_be_txn_req { | |
51 | enum mgmt_be_txn_event event; | |
52 | union { | |
53 | struct mgmt_be_set_cfg_req set_cfg; | |
54 | struct mgmt_be_get_data_req get_data; | |
55 | } req; | |
56 | }; | |
57 | ||
58 | PREDECL_LIST(mgmt_be_batches); | |
59 | struct mgmt_be_batch_ctx { | |
60 | /* Batch-Id as assigned by MGMTD */ | |
61 | uint64_t batch_id; | |
62 | ||
63 | struct mgmt_be_txn_req txn_req; | |
64 | ||
65 | uint32_t flags; | |
66 | ||
67 | struct mgmt_be_batches_item list_linkage; | |
68 | }; | |
69 | #define MGMTD_BE_BATCH_FLAGS_CFG_PREPARED (1U << 0) | |
70 | #define MGMTD_BE_TXN_FLAGS_CFG_APPLIED (1U << 1) | |
71 | DECLARE_LIST(mgmt_be_batches, struct mgmt_be_batch_ctx, list_linkage); | |
72 | ||
73 | struct mgmt_be_client_ctx; | |
74 | ||
75 | PREDECL_LIST(mgmt_be_txns); | |
76 | struct mgmt_be_txn_ctx { | |
77 | /* Txn-Id as assigned by MGMTD */ | |
78 | uint64_t txn_id; | |
79 | uint32_t flags; | |
80 | ||
81 | struct mgmt_be_client_txn_ctx client_data; | |
82 | struct mgmt_be_client_ctx *client_ctx; | |
83 | ||
84 | /* List of batches belonging to this transaction */ | |
85 | struct mgmt_be_batches_head cfg_batches; | |
86 | struct mgmt_be_batches_head apply_cfgs; | |
87 | ||
88 | struct mgmt_be_txns_item list_linkage; | |
89 | ||
90 | struct nb_transaction *nb_txn; | |
91 | uint32_t nb_txn_id; | |
92 | }; | |
93 | #define MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED (1U << 1) | |
94 | ||
95 | DECLARE_LIST(mgmt_be_txns, struct mgmt_be_txn_ctx, list_linkage); | |
96 | ||
97 | #define FOREACH_BE_TXN_BATCH_IN_LIST(txn, batch) \ | |
98 | frr_each_safe (mgmt_be_batches, &(txn)->cfg_batches, (batch)) | |
99 | ||
100 | #define FOREACH_BE_APPLY_BATCH_IN_LIST(txn, batch) \ | |
101 | frr_each_safe (mgmt_be_batches, &(txn)->apply_cfgs, (batch)) | |
102 | ||
103 | struct mgmt_be_client_ctx { | |
070c5e7a | 104 | struct msg_client client; |
7d65b7b7 CH |
105 | |
106 | struct nb_config *candidate_config; | |
107 | struct nb_config *running_config; | |
108 | ||
7d65b7b7 CH |
109 | unsigned long num_edit_nb_cfg; |
110 | unsigned long avg_edit_nb_cfg_tm; | |
111 | unsigned long num_prep_nb_cfg; | |
112 | unsigned long avg_prep_nb_cfg_tm; | |
113 | unsigned long num_apply_nb_cfg; | |
114 | unsigned long avg_apply_nb_cfg_tm; | |
115 | ||
116 | struct mgmt_be_txns_head txn_head; | |
117 | struct mgmt_be_client_params client_params; | |
118 | }; | |
119 | ||
7d65b7b7 CH |
120 | #define FOREACH_BE_TXN_IN_LIST(client_ctx, txn) \ |
121 | frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn)) | |
122 | ||
cfa0facb | 123 | struct debug mgmt_dbg_be_client = {0, "Management backend client operations"}; |
7d65b7b7 | 124 | |
f82370b4 | 125 | static struct mgmt_be_client_ctx mgmt_be_client_ctx = { |
070c5e7a | 126 | .client = {.conn = {.fd = -1}}}; |
7d65b7b7 CH |
127 | |
128 | const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = { | |
7d65b7b7 CH |
129 | #ifdef HAVE_STATICD |
130 | [MGMTD_BE_CLIENT_ID_STATICD] = "staticd", | |
7d65b7b7 CH |
131 | #endif |
132 | [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid", | |
133 | }; | |
134 | ||
7d65b7b7 | 135 | static int mgmt_be_client_send_msg(struct mgmt_be_client_ctx *client_ctx, |
070c5e7a | 136 | Mgmtd__BeMessage *be_msg) |
7d65b7b7 | 137 | { |
070c5e7a CH |
138 | return msg_conn_send_msg( |
139 | &client_ctx->client.conn, MGMT_MSG_VERSION_PROTOBUF, be_msg, | |
140 | mgmtd__be_message__get_packed_size(be_msg), | |
5f05ff58 | 141 | (size_t(*)(void *, void *))mgmtd__be_message__pack, false); |
7d65b7b7 CH |
142 | } |
143 | ||
144 | static struct mgmt_be_batch_ctx * | |
145 | mgmt_be_find_batch_by_id(struct mgmt_be_txn_ctx *txn, | |
146 | uint64_t batch_id) | |
147 | { | |
148 | struct mgmt_be_batch_ctx *batch = NULL; | |
149 | ||
150 | FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { | |
151 | if (batch->batch_id == batch_id) | |
152 | return batch; | |
153 | } | |
154 | ||
155 | return NULL; | |
156 | } | |
157 | ||
158 | static struct mgmt_be_batch_ctx * | |
159 | mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn, uint64_t batch_id) | |
160 | { | |
161 | struct mgmt_be_batch_ctx *batch = NULL; | |
162 | ||
163 | batch = mgmt_be_find_batch_by_id(txn, batch_id); | |
164 | if (!batch) { | |
165 | batch = XCALLOC(MTYPE_MGMTD_BE_BATCH, | |
166 | sizeof(struct mgmt_be_batch_ctx)); | |
167 | assert(batch); | |
168 | ||
169 | batch->batch_id = batch_id; | |
170 | mgmt_be_batches_add_tail(&txn->cfg_batches, batch); | |
171 | ||
218625aa CH |
172 | MGMTD_BE_CLIENT_DBG("Added new batch-id: %" PRIu64 |
173 | " to transaction", | |
174 | batch_id); | |
7d65b7b7 CH |
175 | } |
176 | ||
177 | return batch; | |
178 | } | |
179 | ||
180 | static void mgmt_be_batch_delete(struct mgmt_be_txn_ctx *txn, | |
181 | struct mgmt_be_batch_ctx **batch) | |
182 | { | |
183 | uint16_t indx; | |
184 | ||
185 | if (!batch) | |
186 | return; | |
187 | ||
188 | mgmt_be_batches_del(&txn->cfg_batches, *batch); | |
189 | if ((*batch)->txn_req.event == MGMTD_BE_TXN_PROC_SETCFG) { | |
190 | for (indx = 0; indx < MGMTD_MAX_CFG_CHANGES_IN_BATCH; indx++) { | |
191 | if ((*batch)->txn_req.req.set_cfg.cfg_changes[indx] | |
192 | .value) { | |
193 | free((char *)(*batch) | |
194 | ->txn_req.req.set_cfg | |
195 | .cfg_changes[indx] | |
196 | .value); | |
197 | } | |
198 | } | |
199 | } | |
200 | ||
201 | XFREE(MTYPE_MGMTD_BE_BATCH, *batch); | |
202 | *batch = NULL; | |
203 | } | |
204 | ||
205 | static void mgmt_be_cleanup_all_batches(struct mgmt_be_txn_ctx *txn) | |
206 | { | |
207 | struct mgmt_be_batch_ctx *batch = NULL; | |
208 | ||
209 | FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { | |
210 | mgmt_be_batch_delete(txn, &batch); | |
211 | } | |
212 | ||
213 | FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) { | |
214 | mgmt_be_batch_delete(txn, &batch); | |
215 | } | |
216 | } | |
217 | ||
218 | static struct mgmt_be_txn_ctx * | |
219 | mgmt_be_find_txn_by_id(struct mgmt_be_client_ctx *client_ctx, | |
220 | uint64_t txn_id) | |
221 | { | |
222 | struct mgmt_be_txn_ctx *txn = NULL; | |
223 | ||
224 | FOREACH_BE_TXN_IN_LIST (client_ctx, txn) { | |
225 | if (txn->txn_id == txn_id) | |
226 | return txn; | |
227 | } | |
228 | ||
229 | return NULL; | |
230 | } | |
231 | ||
232 | static struct mgmt_be_txn_ctx * | |
233 | mgmt_be_txn_create(struct mgmt_be_client_ctx *client_ctx, | |
234 | uint64_t txn_id) | |
235 | { | |
236 | struct mgmt_be_txn_ctx *txn = NULL; | |
237 | ||
238 | txn = mgmt_be_find_txn_by_id(client_ctx, txn_id); | |
239 | if (!txn) { | |
240 | txn = XCALLOC(MTYPE_MGMTD_BE_TXN, | |
241 | sizeof(struct mgmt_be_txn_ctx)); | |
242 | assert(txn); | |
243 | ||
244 | txn->txn_id = txn_id; | |
245 | txn->client_ctx = client_ctx; | |
246 | mgmt_be_batches_init(&txn->cfg_batches); | |
247 | mgmt_be_batches_init(&txn->apply_cfgs); | |
248 | mgmt_be_txns_add_tail(&client_ctx->txn_head, txn); | |
249 | ||
218625aa | 250 | MGMTD_BE_CLIENT_DBG("Added new txn-id: %" PRIu64, txn_id); |
7d65b7b7 CH |
251 | } |
252 | ||
253 | return txn; | |
254 | } | |
255 | ||
256 | static void mgmt_be_txn_delete(struct mgmt_be_client_ctx *client_ctx, | |
257 | struct mgmt_be_txn_ctx **txn) | |
258 | { | |
259 | char err_msg[] = "MGMT Transaction Delete"; | |
260 | ||
261 | if (!txn) | |
262 | return; | |
263 | ||
264 | /* | |
265 | * Remove the transaction from the list of transactions | |
266 | * so that future lookups with the same transaction id | |
267 | * does not return this one. | |
268 | */ | |
269 | mgmt_be_txns_del(&client_ctx->txn_head, *txn); | |
270 | ||
271 | /* | |
272 | * Time to delete the transaction which should also | |
273 | * take care of cleaning up all batches created via | |
274 | * CFGDATA_CREATE_REQs. But first notify the client | |
275 | * about the transaction delete. | |
276 | */ | |
277 | if (client_ctx->client_params.txn_notify) | |
278 | (void)(*client_ctx->client_params | |
279 | .txn_notify)( | |
280 | (uintptr_t)client_ctx, | |
281 | client_ctx->client_params.user_data, | |
282 | &(*txn)->client_data, true); | |
283 | ||
284 | mgmt_be_cleanup_all_batches(*txn); | |
285 | if ((*txn)->nb_txn) | |
286 | nb_candidate_commit_abort((*txn)->nb_txn, err_msg, | |
287 | sizeof(err_msg)); | |
288 | XFREE(MTYPE_MGMTD_BE_TXN, *txn); | |
289 | ||
290 | *txn = NULL; | |
291 | } | |
292 | ||
293 | static void | |
294 | mgmt_be_cleanup_all_txns(struct mgmt_be_client_ctx *client_ctx) | |
295 | { | |
296 | struct mgmt_be_txn_ctx *txn = NULL; | |
297 | ||
298 | FOREACH_BE_TXN_IN_LIST (client_ctx, txn) { | |
299 | mgmt_be_txn_delete(client_ctx, &txn); | |
300 | } | |
301 | } | |
302 | ||
303 | static int mgmt_be_send_txn_reply(struct mgmt_be_client_ctx *client_ctx, | |
304 | uint64_t txn_id, bool create, | |
305 | bool success) | |
306 | { | |
307 | Mgmtd__BeMessage be_msg; | |
308 | Mgmtd__BeTxnReply txn_reply; | |
309 | ||
310 | mgmtd__be_txn_reply__init(&txn_reply); | |
311 | txn_reply.create = create; | |
312 | txn_reply.txn_id = txn_id; | |
313 | txn_reply.success = success; | |
314 | ||
315 | mgmtd__be_message__init(&be_msg); | |
316 | be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY; | |
317 | be_msg.txn_reply = &txn_reply; | |
318 | ||
218625aa | 319 | MGMTD_BE_CLIENT_DBG("Sending TXN_REPLY txn-id %" PRIu64, txn_id); |
7d65b7b7 CH |
320 | |
321 | return mgmt_be_client_send_msg(client_ctx, &be_msg); | |
322 | } | |
323 | ||
324 | static int mgmt_be_process_txn_req(struct mgmt_be_client_ctx *client_ctx, | |
325 | uint64_t txn_id, bool create) | |
326 | { | |
327 | struct mgmt_be_txn_ctx *txn; | |
328 | ||
329 | txn = mgmt_be_find_txn_by_id(client_ctx, txn_id); | |
330 | if (create) { | |
331 | if (txn) { | |
332 | /* | |
333 | * Transaction with same txn-id already exists. | |
334 | * Should not happen under any circumstances. | |
335 | */ | |
336 | MGMTD_BE_CLIENT_ERR( | |
218625aa | 337 | "txn-id: %" PRIu64 " already exists", txn_id); |
7d65b7b7 CH |
338 | mgmt_be_send_txn_reply(client_ctx, txn_id, create, |
339 | false); | |
340 | } | |
341 | ||
218625aa | 342 | MGMTD_BE_CLIENT_DBG("Created new txn-id %" PRIu64, txn_id); |
7d65b7b7 CH |
343 | txn = mgmt_be_txn_create(client_ctx, txn_id); |
344 | ||
345 | if (client_ctx->client_params.txn_notify) | |
346 | (void)(*client_ctx->client_params | |
347 | .txn_notify)( | |
348 | (uintptr_t)client_ctx, | |
349 | client_ctx->client_params.user_data, | |
350 | &txn->client_data, false); | |
351 | } else { | |
352 | if (!txn) { | |
353 | /* | |
354 | * Transaction with same txn-id does not exists. | |
355 | * Return sucess anyways. | |
356 | */ | |
218625aa CH |
357 | MGMTD_BE_CLIENT_DBG("txn-id: %" PRIu64 |
358 | " for delete does NOT exists", | |
359 | txn_id); | |
7d65b7b7 | 360 | } else { |
218625aa | 361 | MGMTD_BE_CLIENT_DBG("Delete txn-id: %" PRIu64, txn_id); |
7d65b7b7 CH |
362 | mgmt_be_txn_delete(client_ctx, &txn); |
363 | } | |
364 | } | |
365 | ||
366 | mgmt_be_send_txn_reply(client_ctx, txn_id, create, true); | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
371 | static int | |
372 | mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client_ctx *client_ctx, | |
373 | uint64_t txn_id, uint64_t batch_id, | |
374 | bool success, const char *error_if_any) | |
375 | { | |
376 | Mgmtd__BeMessage be_msg; | |
377 | Mgmtd__BeCfgDataCreateReply cfgdata_reply; | |
378 | ||
379 | mgmtd__be_cfg_data_create_reply__init(&cfgdata_reply); | |
380 | cfgdata_reply.txn_id = (uint64_t)txn_id; | |
381 | cfgdata_reply.batch_id = (uint64_t)batch_id; | |
382 | cfgdata_reply.success = success; | |
383 | if (error_if_any) | |
384 | cfgdata_reply.error_if_any = (char *)error_if_any; | |
385 | ||
386 | mgmtd__be_message__init(&be_msg); | |
387 | be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY; | |
388 | be_msg.cfg_data_reply = &cfgdata_reply; | |
389 | ||
218625aa CH |
390 | MGMTD_BE_CLIENT_DBG("Sending CFGDATA_CREATE_REPLY txn-id: %" PRIu64 |
391 | " batch-id: %" PRIu64, | |
392 | txn_id, batch_id); | |
7d65b7b7 CH |
393 | |
394 | return mgmt_be_client_send_msg(client_ctx, &be_msg); | |
395 | } | |
396 | ||
397 | static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn) | |
398 | { | |
399 | char errmsg[BUFSIZ] = {0}; | |
400 | ||
401 | assert(txn && txn->client_ctx); | |
402 | if (txn->nb_txn) { | |
403 | MGMTD_BE_CLIENT_ERR( | |
218625aa CH |
404 | "Aborting configs after prep for txn-id: %" PRIu64, |
405 | txn->txn_id); | |
7d65b7b7 CH |
406 | nb_candidate_commit_abort(txn->nb_txn, errmsg, sizeof(errmsg)); |
407 | txn->nb_txn = 0; | |
408 | } | |
409 | ||
410 | /* | |
411 | * revert candidate back to running | |
412 | * | |
413 | * This is one txn ctx but the candidate_config is per client ctx, how | |
414 | * does that work? | |
415 | */ | |
416 | MGMTD_BE_CLIENT_DBG( | |
218625aa CH |
417 | "Reset candidate configurations after abort of txn-id: %" PRIu64, |
418 | txn->txn_id); | |
7d65b7b7 CH |
419 | nb_config_replace(txn->client_ctx->candidate_config, |
420 | txn->client_ctx->running_config, true); | |
421 | } | |
422 | ||
423 | static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) | |
424 | { | |
425 | struct mgmt_be_client_ctx *client_ctx; | |
426 | struct mgmt_be_txn_req *txn_req = NULL; | |
427 | struct nb_context nb_ctx = {0}; | |
428 | struct timeval edit_nb_cfg_start; | |
429 | struct timeval edit_nb_cfg_end; | |
430 | unsigned long edit_nb_cfg_tm; | |
431 | struct timeval prep_nb_cfg_start; | |
432 | struct timeval prep_nb_cfg_end; | |
433 | unsigned long prep_nb_cfg_tm; | |
434 | struct mgmt_be_batch_ctx *batch; | |
435 | bool error; | |
436 | char err_buf[BUFSIZ]; | |
437 | size_t num_processed; | |
7d65b7b7 CH |
438 | int err; |
439 | ||
440 | assert(txn && txn->client_ctx); | |
441 | client_ctx = txn->client_ctx; | |
442 | ||
443 | num_processed = 0; | |
444 | FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { | |
445 | txn_req = &batch->txn_req; | |
446 | error = false; | |
447 | nb_ctx.client = NB_CLIENT_CLI; | |
448 | nb_ctx.user = (void *)client_ctx->client_params.user_data; | |
449 | ||
450 | if (!txn->nb_txn) { | |
451 | /* | |
452 | * This happens when the current backend client is only | |
453 | * interested in consuming the config items but is not | |
454 | * interested in validating it. | |
455 | */ | |
456 | error = false; | |
cfa0facb CH |
457 | |
458 | gettimeofday(&edit_nb_cfg_start, NULL); | |
7d65b7b7 CH |
459 | nb_candidate_edit_config_changes( |
460 | client_ctx->candidate_config, | |
461 | txn_req->req.set_cfg.cfg_changes, | |
462 | (size_t)txn_req->req.set_cfg.num_cfg_changes, | |
463 | NULL, NULL, 0, err_buf, sizeof(err_buf), | |
464 | &error); | |
465 | if (error) { | |
466 | err_buf[sizeof(err_buf) - 1] = 0; | |
467 | MGMTD_BE_CLIENT_ERR( | |
218625aa CH |
468 | "Failed to update configs for txn-id: %" PRIu64 |
469 | " batch-id: %" PRIu64 | |
470 | " to candidate, err: '%s'", | |
471 | txn->txn_id, batch->batch_id, err_buf); | |
7d65b7b7 CH |
472 | return -1; |
473 | } | |
cfa0facb CH |
474 | gettimeofday(&edit_nb_cfg_end, NULL); |
475 | edit_nb_cfg_tm = timeval_elapsed(edit_nb_cfg_end, | |
476 | edit_nb_cfg_start); | |
477 | client_ctx->avg_edit_nb_cfg_tm = | |
478 | ((client_ctx->avg_edit_nb_cfg_tm * | |
479 | client_ctx->num_edit_nb_cfg) + | |
480 | edit_nb_cfg_tm) / | |
481 | (client_ctx->num_edit_nb_cfg + 1); | |
7d65b7b7 CH |
482 | client_ctx->num_edit_nb_cfg++; |
483 | } | |
484 | ||
485 | num_processed++; | |
486 | } | |
487 | ||
488 | if (!num_processed) | |
489 | return 0; | |
490 | ||
491 | /* | |
492 | * Now prepare all the batches we have applied in one go. | |
493 | */ | |
494 | nb_ctx.client = NB_CLIENT_CLI; | |
495 | nb_ctx.user = (void *)client_ctx->client_params.user_data; | |
cfa0facb CH |
496 | |
497 | gettimeofday(&prep_nb_cfg_start, NULL); | |
7d65b7b7 CH |
498 | err = nb_candidate_commit_prepare(nb_ctx, client_ctx->candidate_config, |
499 | "MGMTD Backend Txn", &txn->nb_txn, | |
500 | #ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED | |
501 | true, true, | |
502 | #else | |
503 | false, true, | |
504 | #endif | |
505 | err_buf, sizeof(err_buf) - 1); | |
506 | if (err != NB_OK) { | |
507 | err_buf[sizeof(err_buf) - 1] = 0; | |
508 | if (err == NB_ERR_VALIDATION) | |
509 | MGMTD_BE_CLIENT_ERR( | |
218625aa CH |
510 | "Failed to validate configs txn-id: %" PRIu64 |
511 | " %zu batches, err: '%s'", | |
512 | txn->txn_id, num_processed, err_buf); | |
7d65b7b7 CH |
513 | else |
514 | MGMTD_BE_CLIENT_ERR( | |
218625aa CH |
515 | "Failed to prepare configs for txn-id: %" PRIu64 |
516 | " %zu batches, err: '%s'", | |
517 | txn->txn_id, num_processed, err_buf); | |
7d65b7b7 CH |
518 | error = true; |
519 | SET_FLAG(txn->flags, MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED); | |
520 | } else | |
218625aa CH |
521 | MGMTD_BE_CLIENT_DBG("Prepared configs for txn-id: %" PRIu64 |
522 | " %zu batches", | |
523 | txn->txn_id, num_processed); | |
cfa0facb CH |
524 | |
525 | gettimeofday(&prep_nb_cfg_end, NULL); | |
526 | prep_nb_cfg_tm = timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start); | |
527 | client_ctx->avg_prep_nb_cfg_tm = ((client_ctx->avg_prep_nb_cfg_tm * | |
528 | client_ctx->num_prep_nb_cfg) + | |
529 | prep_nb_cfg_tm) / | |
530 | (client_ctx->num_prep_nb_cfg + 1); | |
7d65b7b7 CH |
531 | client_ctx->num_prep_nb_cfg++; |
532 | ||
533 | FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { | |
534 | mgmt_be_send_cfgdata_create_reply( | |
535 | client_ctx, txn->txn_id, batch->batch_id, | |
536 | error ? false : true, error ? err_buf : NULL); | |
537 | if (!error) { | |
538 | SET_FLAG(batch->flags, | |
539 | MGMTD_BE_BATCH_FLAGS_CFG_PREPARED); | |
540 | mgmt_be_batches_del(&txn->cfg_batches, batch); | |
541 | mgmt_be_batches_add_tail(&txn->apply_cfgs, batch); | |
542 | } | |
543 | } | |
544 | ||
cfa0facb CH |
545 | MGMTD_BE_CLIENT_DBG( |
546 | "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u", | |
547 | client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm, | |
548 | client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed); | |
7d65b7b7 CH |
549 | |
550 | if (error) | |
551 | mgmt_be_txn_cfg_abort(txn); | |
552 | ||
553 | return 0; | |
554 | } | |
555 | ||
556 | /* | |
557 | * Process all CFG_DATA_REQs received so far and prepare them all in one go. | |
558 | */ | |
559 | static int | |
560 | mgmt_be_update_setcfg_in_batch(struct mgmt_be_client_ctx *client_ctx, | |
561 | struct mgmt_be_txn_ctx *txn, | |
562 | uint64_t batch_id, | |
563 | Mgmtd__YangCfgDataReq * cfg_req[], | |
564 | int num_req) | |
565 | { | |
566 | struct mgmt_be_batch_ctx *batch = NULL; | |
567 | struct mgmt_be_txn_req *txn_req = NULL; | |
568 | int index; | |
569 | struct nb_cfg_change *cfg_chg; | |
570 | ||
571 | batch = mgmt_be_batch_create(txn, batch_id); | |
572 | if (!batch) { | |
573 | MGMTD_BE_CLIENT_ERR("Batch create failed!"); | |
574 | return -1; | |
575 | } | |
576 | ||
577 | txn_req = &batch->txn_req; | |
578 | txn_req->event = MGMTD_BE_TXN_PROC_SETCFG; | |
218625aa CH |
579 | MGMTD_BE_CLIENT_DBG("Created SETCFG request for batch-id: %" PRIu64 |
580 | " txn-id: %" PRIu64 " cfg-items:%d", | |
581 | batch_id, txn->txn_id, num_req); | |
7d65b7b7 CH |
582 | |
583 | txn_req->req.set_cfg.num_cfg_changes = num_req; | |
584 | for (index = 0; index < num_req; index++) { | |
585 | cfg_chg = &txn_req->req.set_cfg.cfg_changes[index]; | |
586 | ||
587 | if (cfg_req[index]->req_type | |
588 | == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA) | |
589 | cfg_chg->operation = NB_OP_DESTROY; | |
590 | else | |
591 | cfg_chg->operation = NB_OP_CREATE; | |
592 | ||
593 | strlcpy(cfg_chg->xpath, cfg_req[index]->data->xpath, | |
594 | sizeof(cfg_chg->xpath)); | |
595 | cfg_chg->value = (cfg_req[index]->data->value | |
596 | && cfg_req[index] | |
597 | ->data->value | |
598 | ->encoded_str_val | |
599 | ? strdup(cfg_req[index] | |
600 | ->data->value | |
601 | ->encoded_str_val) | |
602 | : NULL); | |
603 | if (cfg_chg->value | |
604 | && !strncmp(cfg_chg->value, MGMTD_BE_CONTAINER_NODE_VAL, | |
605 | strlen(MGMTD_BE_CONTAINER_NODE_VAL))) { | |
606 | free((char *)cfg_chg->value); | |
607 | cfg_chg->value = NULL; | |
608 | } | |
609 | } | |
610 | ||
611 | return 0; | |
612 | } | |
613 | ||
614 | static int | |
615 | mgmt_be_process_cfgdata_req(struct mgmt_be_client_ctx *client_ctx, | |
616 | uint64_t txn_id, uint64_t batch_id, | |
617 | Mgmtd__YangCfgDataReq * cfg_req[], int num_req, | |
618 | bool end_of_data) | |
619 | { | |
620 | struct mgmt_be_txn_ctx *txn; | |
621 | ||
622 | txn = mgmt_be_find_txn_by_id(client_ctx, txn_id); | |
623 | if (!txn) { | |
218625aa CH |
624 | MGMTD_BE_CLIENT_ERR("Invalid txn-id: %" PRIu64 |
625 | " from MGMTD server", | |
626 | txn_id); | |
7d65b7b7 CH |
627 | mgmt_be_send_cfgdata_create_reply( |
628 | client_ctx, txn_id, batch_id, false, | |
629 | "Transaction context not created yet"); | |
630 | } else { | |
631 | mgmt_be_update_setcfg_in_batch(client_ctx, txn, batch_id, | |
632 | cfg_req, num_req); | |
633 | } | |
634 | ||
635 | if (txn && end_of_data) { | |
636 | MGMTD_BE_CLIENT_DBG("Triggering CFG_PREPARE_REQ processing"); | |
637 | mgmt_be_txn_cfg_prepare(txn); | |
638 | } | |
639 | ||
640 | return 0; | |
641 | } | |
642 | ||
643 | static int mgmt_be_send_apply_reply(struct mgmt_be_client_ctx *client_ctx, | |
644 | uint64_t txn_id, uint64_t batch_ids[], | |
645 | size_t num_batch_ids, bool success, | |
646 | const char *error_if_any) | |
647 | { | |
648 | Mgmtd__BeMessage be_msg; | |
649 | Mgmtd__BeCfgDataApplyReply apply_reply; | |
650 | ||
651 | mgmtd__be_cfg_data_apply_reply__init(&apply_reply); | |
652 | apply_reply.success = success; | |
653 | apply_reply.txn_id = txn_id; | |
654 | apply_reply.batch_ids = (uint64_t *)batch_ids; | |
655 | apply_reply.n_batch_ids = num_batch_ids; | |
656 | ||
657 | if (error_if_any) | |
658 | apply_reply.error_if_any = (char *)error_if_any; | |
659 | ||
660 | mgmtd__be_message__init(&be_msg); | |
661 | be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY; | |
662 | be_msg.cfg_apply_reply = &apply_reply; | |
663 | ||
664 | MGMTD_BE_CLIENT_DBG( | |
218625aa CH |
665 | "Sending CFG_APPLY_REPLY txn-id %" PRIu64 |
666 | " %zu batch ids %" PRIu64 " - %" PRIu64, | |
667 | txn_id, num_batch_ids, | |
668 | success && num_batch_ids ? batch_ids[0] : 0, | |
669 | success && num_batch_ids ? batch_ids[num_batch_ids - 1] : 0); | |
7d65b7b7 CH |
670 | |
671 | return mgmt_be_client_send_msg(client_ctx, &be_msg); | |
672 | } | |
673 | ||
674 | static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn) | |
675 | { | |
676 | struct mgmt_be_client_ctx *client_ctx; | |
677 | struct timeval apply_nb_cfg_start; | |
678 | struct timeval apply_nb_cfg_end; | |
679 | unsigned long apply_nb_cfg_tm; | |
680 | struct mgmt_be_batch_ctx *batch; | |
681 | char err_buf[BUFSIZ]; | |
682 | size_t num_processed; | |
683 | static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ]; | |
7d65b7b7 CH |
684 | |
685 | assert(txn && txn->client_ctx); | |
686 | client_ctx = txn->client_ctx; | |
687 | ||
688 | assert(txn->nb_txn); | |
689 | num_processed = 0; | |
690 | ||
691 | /* | |
692 | * Now apply all the batches we have applied in one go. | |
693 | */ | |
cfa0facb | 694 | gettimeofday(&apply_nb_cfg_start, NULL); |
7d65b7b7 CH |
695 | (void)nb_candidate_commit_apply(txn->nb_txn, true, &txn->nb_txn_id, |
696 | err_buf, sizeof(err_buf) - 1); | |
cfa0facb CH |
697 | gettimeofday(&apply_nb_cfg_end, NULL); |
698 | ||
699 | apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start); | |
700 | client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm * | |
701 | client_ctx->num_apply_nb_cfg) + | |
702 | apply_nb_cfg_tm) / | |
703 | (client_ctx->num_apply_nb_cfg + 1); | |
7d65b7b7 CH |
704 | client_ctx->num_apply_nb_cfg++; |
705 | txn->nb_txn = NULL; | |
706 | ||
707 | /* | |
708 | * Send back CFG_APPLY_REPLY for all batches applied. | |
709 | */ | |
710 | FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) { | |
711 | /* | |
712 | * No need to delete the batch yet. Will be deleted during | |
713 | * transaction cleanup on receiving TXN_DELETE_REQ. | |
714 | */ | |
715 | SET_FLAG(batch->flags, MGMTD_BE_TXN_FLAGS_CFG_APPLIED); | |
716 | mgmt_be_batches_del(&txn->apply_cfgs, batch); | |
717 | mgmt_be_batches_add_tail(&txn->cfg_batches, batch); | |
718 | ||
719 | batch_ids[num_processed] = batch->batch_id; | |
720 | num_processed++; | |
721 | if (num_processed == MGMTD_BE_MAX_BATCH_IDS_IN_REQ) { | |
722 | mgmt_be_send_apply_reply(client_ctx, txn->txn_id, | |
723 | batch_ids, num_processed, | |
724 | true, NULL); | |
725 | num_processed = 0; | |
726 | } | |
727 | } | |
728 | ||
729 | mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids, | |
730 | num_processed, true, NULL); | |
731 | ||
cfa0facb CH |
732 | MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec", |
733 | apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm); | |
7d65b7b7 CH |
734 | |
735 | return 0; | |
736 | } | |
737 | ||
738 | static int | |
739 | mgmt_be_process_cfg_apply(struct mgmt_be_client_ctx *client_ctx, | |
740 | uint64_t txn_id) | |
741 | { | |
742 | struct mgmt_be_txn_ctx *txn; | |
743 | ||
744 | txn = mgmt_be_find_txn_by_id(client_ctx, txn_id); | |
745 | if (!txn) { | |
746 | mgmt_be_send_apply_reply(client_ctx, txn_id, NULL, 0, false, | |
747 | "Transaction not created yet!"); | |
748 | return -1; | |
749 | } | |
750 | ||
751 | MGMTD_BE_CLIENT_DBG("Trigger CFG_APPLY_REQ processing"); | |
752 | mgmt_be_txn_proc_cfgapply(txn); | |
753 | ||
754 | return 0; | |
755 | } | |
756 | ||
757 | static int | |
758 | mgmt_be_client_handle_msg(struct mgmt_be_client_ctx *client_ctx, | |
759 | Mgmtd__BeMessage *be_msg) | |
760 | { | |
0b645fd2 CH |
761 | /* |
762 | * protobuf-c adds a max size enum with an internal, and changing by | |
763 | * version, name; cast to an int to avoid unhandled enum warnings | |
764 | */ | |
765 | switch ((int)be_msg->message_case) { | |
7d65b7b7 | 766 | case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY: |
218625aa CH |
767 | MGMTD_BE_CLIENT_DBG("Got SUBSCR_REPLY success %u", |
768 | be_msg->subscr_reply->success); | |
7d65b7b7 CH |
769 | break; |
770 | case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ: | |
218625aa CH |
771 | MGMTD_BE_CLIENT_DBG("Got TXN_REQ %s txn-id: %" PRIu64, |
772 | be_msg->txn_req->create ? "Create" | |
773 | : "Delete", | |
774 | be_msg->txn_req->txn_id); | |
7d65b7b7 CH |
775 | mgmt_be_process_txn_req(client_ctx, |
776 | be_msg->txn_req->txn_id, | |
777 | be_msg->txn_req->create); | |
778 | break; | |
779 | case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ: | |
218625aa CH |
780 | MGMTD_BE_CLIENT_DBG("Got CFG_DATA_REQ txn-id: %" PRIu64 |
781 | " batch-id: %" PRIu64 " end-of-data %u", | |
782 | be_msg->cfg_data_req->txn_id, | |
783 | be_msg->cfg_data_req->batch_id, | |
784 | be_msg->cfg_data_req->end_of_data); | |
7d65b7b7 CH |
785 | mgmt_be_process_cfgdata_req( |
786 | client_ctx, be_msg->cfg_data_req->txn_id, | |
787 | be_msg->cfg_data_req->batch_id, | |
788 | be_msg->cfg_data_req->data_req, | |
789 | be_msg->cfg_data_req->n_data_req, | |
790 | be_msg->cfg_data_req->end_of_data); | |
791 | break; | |
792 | case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ: | |
218625aa CH |
793 | MGMTD_BE_CLIENT_DBG("Got CFG_APPLY_REQ txn-id: %" PRIu64, |
794 | be_msg->cfg_data_req->txn_id); | |
7d65b7b7 CH |
795 | mgmt_be_process_cfg_apply( |
796 | client_ctx, (uint64_t)be_msg->cfg_apply_req->txn_id); | |
797 | break; | |
798 | case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ: | |
799 | case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ: | |
800 | case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REQ: | |
801 | case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REQ: | |
218625aa CH |
802 | MGMTD_BE_CLIENT_ERR("Got unhandled message type %u", |
803 | be_msg->message_case); | |
7d65b7b7 CH |
804 | /* |
805 | * TODO: Add handling code in future. | |
806 | */ | |
807 | break; | |
808 | /* | |
809 | * NOTE: The following messages are always sent from Backend | |
810 | * clients to MGMTd only and/or need not be handled here. | |
811 | */ | |
812 | case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY: | |
813 | case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY: | |
814 | case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY: | |
815 | case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY: | |
816 | case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REPLY: | |
817 | case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REPLY: | |
818 | case MGMTD__BE_MESSAGE__MESSAGE_NOTIFY_DATA: | |
819 | case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET: | |
7d65b7b7 CH |
820 | default: |
821 | /* | |
822 | * A 'default' case is being added contrary to the | |
823 | * FRR code guidelines to take care of build | |
824 | * failures on certain build systems (courtesy of | |
825 | * the proto-c package). | |
826 | */ | |
827 | break; | |
828 | } | |
829 | ||
830 | return 0; | |
831 | } | |
832 | ||
070c5e7a CH |
833 | static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data, |
834 | size_t len, struct msg_conn *conn) | |
7d65b7b7 | 835 | { |
070c5e7a CH |
836 | struct mgmt_be_client_ctx *client_ctx; |
837 | struct msg_client *client; | |
7d65b7b7 | 838 | Mgmtd__BeMessage *be_msg; |
7d65b7b7 | 839 | |
070c5e7a CH |
840 | client = container_of(conn, struct msg_client, conn); |
841 | client_ctx = container_of(client, struct mgmt_be_client_ctx, client); | |
842 | ||
f82370b4 CH |
843 | be_msg = mgmtd__be_message__unpack(NULL, len, data); |
844 | if (!be_msg) { | |
845 | MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server", | |
846 | len); | |
847 | return; | |
7d65b7b7 | 848 | } |
f82370b4 CH |
849 | MGMTD_BE_CLIENT_DBG( |
850 | "Decoded %zu bytes of message(msg: %u/%u) from server", len, | |
851 | be_msg->message_case, be_msg->message_case); | |
852 | (void)mgmt_be_client_handle_msg(client_ctx, be_msg); | |
853 | mgmtd__be_message__free_unpacked(be_msg, NULL); | |
7d65b7b7 CH |
854 | } |
855 | ||
7d65b7b7 | 856 | static int mgmt_be_send_subscr_req(struct mgmt_be_client_ctx *client_ctx, |
f82370b4 CH |
857 | bool subscr_xpaths, uint16_t num_reg_xpaths, |
858 | char **reg_xpaths) | |
7d65b7b7 CH |
859 | { |
860 | Mgmtd__BeMessage be_msg; | |
861 | Mgmtd__BeSubscribeReq subscr_req; | |
862 | ||
863 | mgmtd__be_subscribe_req__init(&subscr_req); | |
864 | subscr_req.client_name = client_ctx->client_params.name; | |
865 | subscr_req.n_xpath_reg = num_reg_xpaths; | |
866 | if (num_reg_xpaths) | |
867 | subscr_req.xpath_reg = reg_xpaths; | |
868 | else | |
869 | subscr_req.xpath_reg = NULL; | |
870 | subscr_req.subscribe_xpaths = subscr_xpaths; | |
871 | ||
872 | mgmtd__be_message__init(&be_msg); | |
873 | be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ; | |
874 | be_msg.subscr_req = &subscr_req; | |
875 | ||
218625aa CH |
876 | MGMTD_FE_CLIENT_DBG( |
877 | "Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu", | |
878 | subscr_req.client_name, subscr_req.subscribe_xpaths, | |
879 | subscr_req.n_xpath_reg); | |
880 | ||
7d65b7b7 CH |
881 | return mgmt_be_client_send_msg(client_ctx, &be_msg); |
882 | } | |
883 | ||
070c5e7a | 884 | static int _notify_conenct_disconnect(struct msg_client *client, bool connected) |
7d65b7b7 | 885 | { |
070c5e7a CH |
886 | struct mgmt_be_client_ctx *client_ctx = |
887 | container_of(client, struct mgmt_be_client_ctx, client); | |
888 | int ret; | |
889 | ||
890 | if (connected) { | |
891 | assert(client->conn.fd != -1); | |
892 | ret = mgmt_be_send_subscr_req(client_ctx, false, 0, NULL); | |
893 | if (ret) | |
894 | return ret; | |
7d65b7b7 CH |
895 | } |
896 | ||
070c5e7a | 897 | /* Notify BE client through registered callback (if any) */ |
7d65b7b7 | 898 | if (client_ctx->client_params.client_connect_notify) |
f82370b4 | 899 | (void)(*client_ctx->client_params.client_connect_notify)( |
7d65b7b7 | 900 | (uintptr_t)client_ctx, |
070c5e7a CH |
901 | client_ctx->client_params.user_data, connected); |
902 | return 0; | |
7d65b7b7 CH |
903 | } |
904 | ||
070c5e7a | 905 | static int mgmt_be_client_notify_conenct(struct msg_client *client) |
7d65b7b7 | 906 | { |
070c5e7a | 907 | return _notify_conenct_disconnect(client, true); |
7d65b7b7 CH |
908 | } |
909 | ||
070c5e7a | 910 | static int mgmt_be_client_notify_disconenct(struct msg_conn *conn) |
7d65b7b7 | 911 | { |
070c5e7a | 912 | struct msg_client *client = container_of(conn, struct msg_client, conn); |
7d65b7b7 | 913 | |
070c5e7a | 914 | return _notify_conenct_disconnect(client, false); |
7d65b7b7 CH |
915 | } |
916 | ||
cfa0facb CH |
917 | DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd, |
918 | "[no] debug mgmt client backend", | |
919 | NO_STR DEBUG_STR MGMTD_STR | |
920 | "client\n" | |
921 | "backend\n") | |
922 | { | |
923 | uint32_t mode = DEBUG_NODE2MODE(vty->node); | |
924 | ||
925 | DEBUG_MODE_SET(&mgmt_dbg_be_client, mode, !no); | |
926 | ||
927 | return CMD_SUCCESS; | |
928 | } | |
929 | ||
930 | static void mgmt_debug_client_be_set_all(uint32_t flags, bool set) | |
931 | { | |
932 | DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set); | |
933 | } | |
934 | ||
935 | static int mgmt_debug_be_client_config_write(struct vty *vty) | |
936 | { | |
937 | if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF)) | |
938 | vty_out(vty, "debug mgmt client frontend\n"); | |
939 | ||
940 | return 1; | |
941 | } | |
942 | ||
943 | void mgmt_debug_be_client_show_debug(struct vty *vty) | |
944 | { | |
945 | if (MGMTD_DBG_BE_CLIENT_CHECK()) | |
946 | vty_out(vty, "debug mgmt client backend\n"); | |
947 | } | |
948 | ||
949 | static struct debug_callbacks mgmt_dbg_be_client_cbs = { | |
950 | .debug_set_all = mgmt_debug_client_be_set_all}; | |
951 | ||
952 | static struct cmd_node mgmt_dbg_node = { | |
953 | .name = "mgmt backend client", | |
954 | .node = DEBUG_NODE, | |
955 | .prompt = "", | |
956 | .config_write = mgmt_debug_be_client_config_write, | |
957 | }; | |
7d65b7b7 CH |
958 | |
959 | /* | |
960 | * Initialize library and try connecting with MGMTD. | |
961 | */ | |
962 | uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params, | |
cd9d0537 | 963 | struct event_loop *master_thread) |
7d65b7b7 | 964 | { |
070c5e7a CH |
965 | /* Don't call twice */ |
966 | assert(!mgmt_be_client_ctx.client.conn.loop); | |
7d65b7b7 | 967 | |
070c5e7a CH |
968 | /* Only call after frr_init() */ |
969 | assert(running_config); | |
7d65b7b7 | 970 | |
7d65b7b7 CH |
971 | mgmt_be_client_ctx.running_config = running_config; |
972 | mgmt_be_client_ctx.candidate_config = nb_config_new(NULL); | |
070c5e7a | 973 | mgmt_be_client_ctx.client_params = *params; |
7d65b7b7 | 974 | mgmt_be_txns_init(&mgmt_be_client_ctx.txn_head); |
070c5e7a CH |
975 | msg_client_init(&mgmt_be_client_ctx.client, master_thread, |
976 | MGMTD_BE_SERVER_PATH, mgmt_be_client_notify_conenct, | |
977 | mgmt_be_client_notify_disconenct, | |
978 | mgmt_be_client_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC, | |
5f05ff58 | 979 | MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, false, |
070c5e7a | 980 | "BE-client", MGMTD_DBG_BE_CLIENT_CHECK()); |
7d65b7b7 CH |
981 | |
982 | MGMTD_BE_CLIENT_DBG("Initialized client '%s'", params->name); | |
983 | ||
984 | return (uintptr_t)&mgmt_be_client_ctx; | |
985 | } | |
986 | ||
cfa0facb CH |
987 | |
988 | void mgmt_be_client_lib_vty_init(void) | |
989 | { | |
990 | debug_init(&mgmt_dbg_be_client_cbs); | |
991 | install_node(&mgmt_dbg_node); | |
992 | install_element(ENABLE_NODE, &debug_mgmt_client_be_cmd); | |
993 | install_element(CONFIG_NODE, &debug_mgmt_client_be_cmd); | |
994 | } | |
995 | ||
996 | ||
7d65b7b7 CH |
997 | /* |
998 | * Subscribe with MGMTD for one or more YANG subtree(s). | |
999 | */ | |
1000 | enum mgmt_result mgmt_be_subscribe_yang_data(uintptr_t lib_hndl, | |
1001 | char *reg_yang_xpaths[], | |
1002 | int num_reg_xpaths) | |
1003 | { | |
1004 | struct mgmt_be_client_ctx *client_ctx; | |
1005 | ||
42f4bb2b CH |
1006 | if (!num_reg_xpaths) |
1007 | return MGMTD_SUCCESS; | |
1008 | ||
7d65b7b7 CH |
1009 | client_ctx = (struct mgmt_be_client_ctx *)lib_hndl; |
1010 | if (!client_ctx) | |
1011 | return MGMTD_INVALID_PARAM; | |
1012 | ||
1013 | if (mgmt_be_send_subscr_req(client_ctx, true, num_reg_xpaths, | |
1014 | reg_yang_xpaths) | |
1015 | != 0) | |
1016 | return MGMTD_INTERNAL_ERROR; | |
1017 | ||
1018 | return MGMTD_SUCCESS; | |
1019 | } | |
1020 | ||
1021 | /* | |
1022 | * Unsubscribe with MGMTD for one or more YANG subtree(s). | |
1023 | */ | |
1024 | enum mgmt_result mgmt_be_unsubscribe_yang_data(uintptr_t lib_hndl, | |
1025 | char *reg_yang_xpaths[], | |
1026 | int num_reg_xpaths) | |
1027 | { | |
1028 | struct mgmt_be_client_ctx *client_ctx; | |
1029 | ||
42f4bb2b CH |
1030 | if (!num_reg_xpaths) |
1031 | return MGMTD_SUCCESS; | |
1032 | ||
7d65b7b7 CH |
1033 | client_ctx = (struct mgmt_be_client_ctx *)lib_hndl; |
1034 | if (!client_ctx) | |
1035 | return MGMTD_INVALID_PARAM; | |
1036 | ||
1037 | ||
1038 | if (mgmt_be_send_subscr_req(client_ctx, false, num_reg_xpaths, | |
1039 | reg_yang_xpaths) | |
1040 | < 0) | |
1041 | return MGMTD_INTERNAL_ERROR; | |
1042 | ||
1043 | return MGMTD_SUCCESS; | |
1044 | } | |
1045 | ||
1046 | /* | |
1047 | * Send one or more YANG notifications to MGMTD daemon. | |
1048 | */ | |
1049 | enum mgmt_result mgmt_be_send_yang_notify(uintptr_t lib_hndl, | |
1050 | Mgmtd__YangData * data_elems[], | |
1051 | int num_elems) | |
1052 | { | |
1053 | struct mgmt_be_client_ctx *client_ctx; | |
1054 | ||
1055 | client_ctx = (struct mgmt_be_client_ctx *)lib_hndl; | |
1056 | if (!client_ctx) | |
1057 | return MGMTD_INVALID_PARAM; | |
1058 | ||
1059 | return MGMTD_SUCCESS; | |
1060 | } | |
1061 | ||
1062 | /* | |
1063 | * Destroy library and cleanup everything. | |
1064 | */ | |
070c5e7a | 1065 | void mgmt_be_client_lib_destroy(void) |
7d65b7b7 | 1066 | { |
070c5e7a | 1067 | struct mgmt_be_client_ctx *client_ctx = &mgmt_be_client_ctx; |
7d65b7b7 CH |
1068 | |
1069 | MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'", | |
f82370b4 | 1070 | client_ctx->client_params.name); |
7d65b7b7 | 1071 | |
070c5e7a | 1072 | msg_client_cleanup(&client_ctx->client); |
7d65b7b7 CH |
1073 | mgmt_be_cleanup_all_txns(client_ctx); |
1074 | mgmt_be_txns_fini(&client_ctx->txn_head); | |
52a50ca1 | 1075 | nb_config_free(client_ctx->candidate_config); |
070c5e7a CH |
1076 | |
1077 | memset(client_ctx, 0, sizeof(*client_ctx)); | |
7d65b7b7 | 1078 | } |