e9297021a34a30175c17479815ecf8bc5d371fc5
[dpdk.git] / lib / librte_telemetry / rte_telemetry_parser.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <jansson.h>
10
11 #include <rte_metrics.h>
12 #include <rte_common.h>
13 #include <rte_ethdev.h>
14
15 #include "rte_telemetry_internal.h"
16
17 typedef int (*command_func)(struct telemetry_impl *, int, json_t *);
18
19 struct rte_telemetry_command {
20         char *text;
21         command_func fn;
22 } command;
23
24 static int32_t
25 rte_telemetry_command_clients(struct telemetry_impl *telemetry, int action,
26         json_t *data)
27 {
28         int ret;
29
30         if (telemetry == NULL) {
31                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
32                 return -1;
33         }
34
35         if (action != ACTION_DELETE) {
36                 TELEMETRY_LOG_WARN("Invalid action for this command");
37                 goto einval_fail;
38         }
39
40         if (!json_is_object(data)) {
41                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
42                 goto einval_fail;
43         }
44
45         json_t *client_path = json_object_get(data, "client_path");
46         if (!json_is_string(client_path)) {
47                 TELEMETRY_LOG_WARN("Command value is not a string");
48                 goto einval_fail;
49         }
50
51         ret = rte_telemetry_unregister_client(telemetry,
52                         json_string_value(client_path));
53         if (ret < 0) {
54                 TELEMETRY_LOG_ERR("Could not unregister client");
55                 goto einval_fail;
56         }
57
58         return 0;
59
60 einval_fail:
61         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
62         if (ret < 0)
63                 TELEMETRY_LOG_ERR("Could not send error");
64         return -1;
65 }
66
67 static int32_t
68 rte_telemetry_command_ports(struct telemetry_impl *telemetry, int action,
69         json_t *data)
70 {
71         int ret;
72
73         if (telemetry == NULL) {
74                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
75                 return -1;
76         }
77
78         if (!json_is_null(data)) {
79                 TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'ports' command");
80                 goto einval_fail;
81         }
82
83         if (action != ACTION_GET) {
84                 TELEMETRY_LOG_WARN("Invalid action for this command");
85                 goto einval_fail;
86         }
87
88         return 0;
89
90 einval_fail:
91         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
92         if (ret < 0)
93                 TELEMETRY_LOG_ERR("Could not send error");
94         return -1;
95 }
96
97 static int32_t
98 rte_telemetry_command_ports_details(struct telemetry_impl *telemetry,
99         int action, json_t *data)
100 {
101         json_t *value, *port_ids_json = json_object_get(data, "ports");
102         uint64_t num_port_ids = json_array_size(port_ids_json);
103         int ret, port_ids[num_port_ids];
104         RTE_SET_USED(port_ids);
105         size_t index;
106
107         if (telemetry == NULL) {
108                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
109                 return -1;
110         }
111
112         if (action != ACTION_GET) {
113                 TELEMETRY_LOG_WARN("Invalid action for this command");
114                 goto einval_fail;
115         }
116
117         if (!json_is_object(data)) {
118                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
119                 goto einval_fail;
120         }
121
122         if (!json_is_array(port_ids_json)) {
123                 TELEMETRY_LOG_WARN("Invalid Port ID array");
124                 goto einval_fail;
125         }
126
127         json_array_foreach(port_ids_json, index, value) {
128                 if (!json_is_integer(value)) {
129                         TELEMETRY_LOG_WARN("Port ID given is invalid");
130                         goto einval_fail;
131                 }
132                 port_ids[index] = json_integer_value(value);
133         }
134
135         return 0;
136
137 einval_fail:
138         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
139         if (ret < 0)
140                 TELEMETRY_LOG_ERR("Could not send error");
141         return -1;
142 }
143
144 static int32_t
145 rte_telemetry_command_port_stats(struct telemetry_impl *telemetry, int action,
146         json_t *data)
147 {
148         int ret;
149
150         if (telemetry == NULL) {
151                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
152                 return -1;
153         }
154
155         if (!json_is_null(data)) {
156                 TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'port_stats' command");
157                 goto einval_fail;
158         }
159
160         if (action != ACTION_GET) {
161                 TELEMETRY_LOG_WARN("Invalid action for this command");
162                 goto einval_fail;
163         }
164
165         return 0;
166
167 einval_fail:
168         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
169         if (ret < 0)
170                 TELEMETRY_LOG_ERR("Could not send error");
171         return -1;
172 }
173
174 static int32_t
175 rte_telemetry_stat_names_to_ids(struct telemetry_impl *telemetry,
176         const char * const *stat_names, uint32_t *stat_ids,
177         uint64_t num_stat_names)
178 {
179         struct rte_metric_name *names;
180         int ret, num_metrics;
181         uint32_t i, k;
182
183         if (stat_names == NULL) {
184                 TELEMETRY_LOG_WARN("Invalid stat_names argument");
185                 goto einval_fail;
186         }
187
188         if (num_stat_names <= 0) {
189                 TELEMETRY_LOG_WARN("Invalid num_stat_names argument");
190                 goto einval_fail;
191         }
192
193         num_metrics = rte_metrics_get_names(NULL, 0);
194         if (num_metrics < 0) {
195                 TELEMETRY_LOG_ERR("Cannot get metrics count");
196                 goto eperm_fail;
197         } else if (num_metrics == 0) {
198                 TELEMETRY_LOG_WARN("No metrics have been registered");
199                 goto eperm_fail;
200         }
201
202         names = malloc(sizeof(struct rte_metric_name) * num_metrics);
203         if (names == NULL) {
204                 TELEMETRY_LOG_ERR("Cannot allocate memory for names");
205
206                 ret = rte_telemetry_send_error_response(telemetry, -ENOMEM);
207                 if (ret < 0)
208                         TELEMETRY_LOG_ERR("Could not send error");
209
210                 return -1;
211         }
212
213         ret = rte_metrics_get_names(names, num_metrics);
214         if (ret < 0 || ret > num_metrics) {
215                 TELEMETRY_LOG_ERR("Cannot get metrics names");
216                 free(names);
217                 goto eperm_fail;
218         }
219
220         k = 0;
221         for (i = 0; i < (uint32_t)num_stat_names; i++) {
222                 uint32_t j;
223                 for (j = 0; j < (uint32_t)num_metrics; j++) {
224                         if (strcmp(stat_names[i], names[j].name) == 0) {
225                                 stat_ids[k] = j;
226                                 k++;
227                                 break;
228                         }
229                 }
230         }
231
232         if (k != num_stat_names) {
233                 TELEMETRY_LOG_WARN("Invalid stat names provided");
234                 free(names);
235                 goto einval_fail;
236         }
237
238         free(names);
239         return 0;
240
241 einval_fail:
242         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
243         if (ret < 0)
244                 TELEMETRY_LOG_ERR("Could not send error");
245         return -1;
246
247 eperm_fail:
248         ret = rte_telemetry_send_error_response(telemetry, -EPERM);
249         if (ret < 0)
250                 TELEMETRY_LOG_ERR("Could not send error");
251         return -1;
252 }
253
254 int32_t
255 rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry,
256          int action, json_t *data)
257 {
258         int ret, num_metrics, i, p;
259         struct rte_metric_value *values;
260         uint64_t num_port_ids = 0;
261         uint32_t port_ids[RTE_MAX_ETHPORTS];
262
263         if (telemetry == NULL) {
264                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
265                 return -1;
266         }
267
268         if (action != ACTION_GET) {
269                 TELEMETRY_LOG_WARN("Invalid action for this command");
270                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
271                 if (ret < 0)
272                         TELEMETRY_LOG_ERR("Could not send error");
273                 return -1;
274         }
275
276         if (json_is_object(data)) {
277                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
278                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
279                 if (ret < 0)
280                         TELEMETRY_LOG_ERR("Could not send error");
281                 return -1;
282         }
283
284         num_metrics = rte_metrics_get_values(0, NULL, 0);
285         if (num_metrics < 0) {
286                 TELEMETRY_LOG_ERR("Cannot get metrics count");
287
288                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
289                 if (ret < 0)
290                         TELEMETRY_LOG_ERR("Could not send error");
291
292                 return -1;
293         } else if (num_metrics == 0) {
294                 TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
295
296                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
297                 if (ret < 0)
298                         TELEMETRY_LOG_ERR("Could not send error");
299
300                 return -1;
301         }
302
303         values = malloc(sizeof(struct rte_metric_value) * num_metrics);
304         if (values == NULL) {
305                 TELEMETRY_LOG_ERR("Cannot allocate memory");
306                 ret = rte_telemetry_send_error_response(telemetry,
307                          -ENOMEM);
308                 if (ret < 0)
309                         TELEMETRY_LOG_ERR("Could not send error");
310                 return -1;
311         }
312
313         uint32_t stat_ids[num_metrics];
314
315         RTE_ETH_FOREACH_DEV(p) {
316                 port_ids[num_port_ids] = p;
317                 num_port_ids++;
318         }
319
320         if (!num_port_ids) {
321                 TELEMETRY_LOG_WARN("No active ports");
322
323                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
324                 if (ret < 0)
325                         TELEMETRY_LOG_ERR("Could not send error");
326
327                 goto fail;
328         }
329
330         ret = rte_metrics_get_values(port_ids[0], values, num_metrics);
331         if (ret < 0) {
332                 TELEMETRY_LOG_ERR("Could not get stat values");
333                 goto fail;
334         }
335         for (i = 0; i < num_metrics; i++)
336                 stat_ids[i] = values[i].key;
337
338         ret = rte_telemetry_send_ports_stats_values(stat_ids, num_metrics,
339                 port_ids, num_port_ids, telemetry);
340         if (ret < 0) {
341                 TELEMETRY_LOG_ERR("Sending ports stats values failed");
342                 goto fail;
343         }
344
345         free(values);
346         return 0;
347
348 fail:
349         free(values);
350         return -1;
351 }
352
353 int32_t
354 rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
355         *telemetry, int action, json_t *data)
356 {
357         int ret;
358         json_t *port_ids_json = json_object_get(data, "ports");
359         json_t *stat_names_json = json_object_get(data, "stats");
360         uint64_t num_port_ids = json_array_size(port_ids_json);
361         uint64_t num_stat_names = json_array_size(stat_names_json);
362         const char *stat_names[num_stat_names];
363         uint32_t port_ids[num_port_ids], stat_ids[num_stat_names];
364         size_t index;
365         json_t *value;
366
367         if (telemetry == NULL) {
368                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
369                 return -1;
370         }
371
372         if (action != ACTION_GET) {
373                 TELEMETRY_LOG_WARN("Invalid action for this command");
374                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
375                 if (ret < 0)
376                         TELEMETRY_LOG_ERR("Could not send error");
377                 return -1;
378         }
379
380         if (!json_is_object(data)) {
381                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
382                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
383                 if (ret < 0)
384                         TELEMETRY_LOG_ERR("Could not send error");
385                 return -1;
386         }
387
388         if (!json_is_array(port_ids_json) ||
389                  !json_is_array(stat_names_json)) {
390                 TELEMETRY_LOG_WARN("Invalid input data array(s)");
391                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
392                 if (ret < 0)
393                         TELEMETRY_LOG_ERR("Could not send error");
394                 return -1;
395         }
396
397         json_array_foreach(port_ids_json, index, value) {
398                 if (!json_is_integer(value)) {
399                         TELEMETRY_LOG_WARN("Port ID given is not valid");
400                         ret = rte_telemetry_send_error_response(telemetry,
401                                 -EINVAL);
402                         if (ret < 0)
403                                 TELEMETRY_LOG_ERR("Could not send error");
404                         return -1;
405                 }
406                 port_ids[index] = json_integer_value(value);
407                 ret = rte_telemetry_is_port_active(port_ids[index]);
408                 if (ret < 1) {
409                         ret = rte_telemetry_send_error_response(telemetry,
410                                 -EINVAL);
411                         if (ret < 0)
412                                 TELEMETRY_LOG_ERR("Could not send error");
413                         return -1;
414                 }
415         }
416
417         json_array_foreach(stat_names_json, index, value) {
418                 if (!json_is_string(value)) {
419                         TELEMETRY_LOG_WARN("Stat Name given is not a string");
420
421                         ret = rte_telemetry_send_error_response(telemetry,
422                                         -EINVAL);
423                         if (ret < 0)
424                                 TELEMETRY_LOG_ERR("Could not send error");
425
426                         return -1;
427                 }
428                 stat_names[index] = json_string_value(value);
429         }
430
431         ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
432                 num_stat_names);
433         if (ret < 0) {
434                 TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
435                 return -1;
436         }
437
438         ret = rte_telemetry_send_ports_stats_values(stat_ids, num_stat_names,
439                 port_ids, num_port_ids, telemetry);
440         if (ret < 0) {
441                 TELEMETRY_LOG_ERR("Sending ports stats values failed");
442                 return -1;
443         }
444
445         return 0;
446 }
447
448 static int32_t
449 rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
450         const char *command, json_t *data)
451 {
452         int ret;
453         uint32_t i;
454
455         if (telemetry == NULL) {
456                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
457                 return -1;
458         }
459
460         struct rte_telemetry_command commands[] = {
461                 {
462                         .text = "clients",
463                         .fn = &rte_telemetry_command_clients
464                 },
465                 {
466                         .text = "ports",
467                         .fn = &rte_telemetry_command_ports
468                 },
469                 {
470                         .text = "ports_details",
471                         .fn = &rte_telemetry_command_ports_details
472                 },
473                 {
474                         .text = "port_stats",
475                         .fn = &rte_telemetry_command_port_stats
476                 },
477                 {
478                         .text = "ports_stats_values_by_name",
479                         .fn = &rte_telemetry_command_ports_stats_values_by_name
480                 },
481                 {
482                         .text = "ports_all_stat_values",
483                         .fn = &rte_telemetry_command_ports_all_stat_values
484                 }
485         };
486
487         const uint32_t num_commands = RTE_DIM(commands);
488
489         for (i = 0; i < num_commands; i++) {
490                 if (strcmp(command, commands[i].text) == 0) {
491                         ret = commands[i].fn(telemetry, action, data);
492                         if (ret < 0) {
493                                 TELEMETRY_LOG_ERR("Command Function for %s failed",
494                                         commands[i].text);
495                                 return -1;
496                         }
497                         return 0;
498                 }
499         }
500
501         TELEMETRY_LOG_WARN("\"%s\" command not found", command);
502
503         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
504         if (ret < 0)
505                 TELEMETRY_LOG_ERR("Could not send error");
506
507         return -1;
508 }
509
510 int32_t __rte_experimental
511 rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
512 {
513         int ret, action_int;
514         json_error_t error;
515         json_t *root, *action, *command, *data;
516
517         if (telemetry == NULL) {
518                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
519                 return -1;
520         }
521
522         root = json_loads(socket_rx_data, 0, &error);
523         if (root == NULL) {
524                 TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
525                                 error.text);
526                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
527                 if (ret < 0)
528                         TELEMETRY_LOG_ERR("Could not send error");
529                 return -EPERM;
530         } else if (!json_is_object(root)) {
531                 TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
532                 json_decref(root);
533                 goto einval_fail;
534         }
535
536         action = json_object_get(root, "action");
537         if (action == NULL) {
538                 TELEMETRY_LOG_WARN("Request does not have action field");
539                 goto einval_fail;
540         } else if (!json_is_integer(action)) {
541                 TELEMETRY_LOG_WARN("Action value is not an integer");
542                 goto einval_fail;
543         }
544
545         command = json_object_get(root, "command");
546         if (command == NULL) {
547                 TELEMETRY_LOG_WARN("Request does not have command field");
548                 goto einval_fail;
549         } else if (!json_is_string(command)) {
550                 TELEMETRY_LOG_WARN("Command value is not a string");
551                 goto einval_fail;
552         }
553
554         action_int = json_integer_value(action);
555         if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
556                 TELEMETRY_LOG_WARN("Invalid action code");
557                 goto einval_fail;
558         }
559
560         const char *command_string = json_string_value(command);
561         data = json_object_get(root, "data");
562         if (data == NULL) {
563                 TELEMETRY_LOG_WARN("Request does not have data field");
564                 goto einval_fail;
565         }
566
567         ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
568                 data);
569         if (ret < 0) {
570                 TELEMETRY_LOG_WARN("Could not parse command");
571                 return -EINVAL;
572         }
573
574         return 0;
575
576 einval_fail:
577         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
578         if (ret < 0) {
579                 TELEMETRY_LOG_ERR("Could not send error");
580                 return -EPERM;
581         }
582         return -EINVAL;
583 }