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