telemetry: fix mapping of statistics
[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         return 0;
346
347 fail:
348         free(values);
349         return -1;
350 }
351
352 int32_t
353 rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
354         *telemetry, int action, json_t *data)
355 {
356         int ret;
357         json_t *port_ids_json = json_object_get(data, "ports");
358         json_t *stat_names_json = json_object_get(data, "stats");
359         uint64_t num_port_ids = json_array_size(port_ids_json);
360         uint64_t num_stat_names = json_array_size(stat_names_json);
361         const char *stat_names[num_stat_names];
362         uint32_t port_ids[num_port_ids], stat_ids[num_stat_names];
363         size_t index;
364         json_t *value;
365
366         if (telemetry == NULL) {
367                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
368                 return -1;
369         }
370
371         if (action != ACTION_GET) {
372                 TELEMETRY_LOG_WARN("Invalid action for this command");
373                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
374                 if (ret < 0)
375                         TELEMETRY_LOG_ERR("Could not send error");
376                 return -1;
377         }
378
379         if (!json_is_object(data)) {
380                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
381                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
382                 if (ret < 0)
383                         TELEMETRY_LOG_ERR("Could not send error");
384                 return -1;
385         }
386
387         if (!json_is_array(port_ids_json) ||
388                  !json_is_array(stat_names_json)) {
389                 TELEMETRY_LOG_WARN("Invalid input data array(s)");
390                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
391                 if (ret < 0)
392                         TELEMETRY_LOG_ERR("Could not send error");
393                 return -1;
394         }
395
396         json_array_foreach(port_ids_json, index, value) {
397                 if (!json_is_integer(value)) {
398                         TELEMETRY_LOG_WARN("Port ID given is not valid");
399                         ret = rte_telemetry_send_error_response(telemetry,
400                                 -EINVAL);
401                         if (ret < 0)
402                                 TELEMETRY_LOG_ERR("Could not send error");
403                         return -1;
404                 }
405                 port_ids[index] = json_integer_value(value);
406                 ret = rte_telemetry_is_port_active(port_ids[index]);
407                 if (ret < 1) {
408                         ret = rte_telemetry_send_error_response(telemetry,
409                                 -EINVAL);
410                         if (ret < 0)
411                                 TELEMETRY_LOG_ERR("Could not send error");
412                         return -1;
413                 }
414         }
415
416         json_array_foreach(stat_names_json, index, value) {
417                 if (!json_is_string(value)) {
418                         TELEMETRY_LOG_WARN("Stat Name given is not a string");
419
420                         ret = rte_telemetry_send_error_response(telemetry,
421                                         -EINVAL);
422                         if (ret < 0)
423                                 TELEMETRY_LOG_ERR("Could not send error");
424
425                         return -1;
426                 }
427                 stat_names[index] = json_string_value(value);
428         }
429
430         ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
431                 num_stat_names);
432         if (ret < 0) {
433                 TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
434                 return -1;
435         }
436
437         ret = rte_telemetry_send_ports_stats_values(stat_ids, num_stat_names,
438                 port_ids, num_port_ids, telemetry);
439         if (ret < 0) {
440                 TELEMETRY_LOG_ERR("Sending ports stats values failed");
441                 return -1;
442         }
443
444         return 0;
445 }
446
447 static int32_t
448 rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
449         const char *command, json_t *data)
450 {
451         int ret;
452         uint32_t i;
453
454         if (telemetry == NULL) {
455                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
456                 return -1;
457         }
458
459         struct rte_telemetry_command commands[] = {
460                 {
461                         .text = "clients",
462                         .fn = &rte_telemetry_command_clients
463                 },
464                 {
465                         .text = "ports",
466                         .fn = &rte_telemetry_command_ports
467                 },
468                 {
469                         .text = "ports_details",
470                         .fn = &rte_telemetry_command_ports_details
471                 },
472                 {
473                         .text = "port_stats",
474                         .fn = &rte_telemetry_command_port_stats
475                 },
476                 {
477                         .text = "ports_stats_values_by_name",
478                         .fn = &rte_telemetry_command_ports_stats_values_by_name
479                 },
480                 {
481                         .text = "ports_all_stat_values",
482                         .fn = &rte_telemetry_command_ports_all_stat_values
483                 }
484         };
485
486         const uint32_t num_commands = RTE_DIM(commands);
487
488         for (i = 0; i < num_commands; i++) {
489                 if (strcmp(command, commands[i].text) == 0) {
490                         ret = commands[i].fn(telemetry, action, data);
491                         if (ret < 0) {
492                                 TELEMETRY_LOG_ERR("Command Function for %s failed",
493                                         commands[i].text);
494                                 return -1;
495                         }
496                         return 0;
497                 }
498         }
499
500         TELEMETRY_LOG_WARN("\"%s\" command not found", command);
501
502         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
503         if (ret < 0)
504                 TELEMETRY_LOG_ERR("Could not send error");
505
506         return -1;
507 }
508
509 int32_t __rte_experimental
510 rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
511 {
512         int ret, action_int;
513         json_error_t error;
514         json_t *root, *action, *command, *data;
515
516         if (telemetry == NULL) {
517                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
518                 return -1;
519         }
520
521         root = json_loads(socket_rx_data, 0, &error);
522         if (root == NULL) {
523                 TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
524                                 error.text);
525                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
526                 if (ret < 0)
527                         TELEMETRY_LOG_ERR("Could not send error");
528                 return -EPERM;
529         } else if (!json_is_object(root)) {
530                 TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
531                 json_decref(root);
532                 goto einval_fail;
533         }
534
535         action = json_object_get(root, "action");
536         if (action == NULL) {
537                 TELEMETRY_LOG_WARN("Request does not have action field");
538                 goto einval_fail;
539         } else if (!json_is_integer(action)) {
540                 TELEMETRY_LOG_WARN("Action value is not an integer");
541                 goto einval_fail;
542         }
543
544         command = json_object_get(root, "command");
545         if (command == NULL) {
546                 TELEMETRY_LOG_WARN("Request does not have command field");
547                 goto einval_fail;
548         } else if (!json_is_string(command)) {
549                 TELEMETRY_LOG_WARN("Command value is not a string");
550                 goto einval_fail;
551         }
552
553         action_int = json_integer_value(action);
554         if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
555                 TELEMETRY_LOG_WARN("Invalid action code");
556                 goto einval_fail;
557         }
558
559         const char *command_string = json_string_value(command);
560         data = json_object_get(root, "data");
561         if (data == NULL) {
562                 TELEMETRY_LOG_WARN("Request does not have data field");
563                 goto einval_fail;
564         }
565
566         ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
567                 data);
568         if (ret < 0) {
569                 TELEMETRY_LOG_WARN("Could not parse command");
570                 return -EINVAL;
571         }
572
573         return 0;
574
575 einval_fail:
576         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
577         if (ret < 0) {
578                 TELEMETRY_LOG_ERR("Could not send error");
579                 return -EPERM;
580         }
581         return -EINVAL;
582 }