telemetry: support global metrics
[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         struct telemetry_encode_param ep;
262
263         memset(&ep, 0, sizeof(ep));
264         if (telemetry == NULL) {
265                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
266                 return -1;
267         }
268
269         if (action != ACTION_GET) {
270                 TELEMETRY_LOG_WARN("Invalid action for this command");
271                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
272                 if (ret < 0)
273                         TELEMETRY_LOG_ERR("Could not send error");
274                 return -1;
275         }
276
277         if (json_is_object(data)) {
278                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
279                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
280                 if (ret < 0)
281                         TELEMETRY_LOG_ERR("Could not send error");
282                 return -1;
283         }
284
285         num_metrics = rte_metrics_get_values(0, NULL, 0);
286         if (num_metrics < 0) {
287                 TELEMETRY_LOG_ERR("Cannot get metrics count");
288
289                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
290                 if (ret < 0)
291                         TELEMETRY_LOG_ERR("Could not send error");
292
293                 return -1;
294         } else if (num_metrics == 0) {
295                 TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
296
297                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
298                 if (ret < 0)
299                         TELEMETRY_LOG_ERR("Could not send error");
300
301                 return -1;
302         }
303
304         values = malloc(sizeof(struct rte_metric_value) * num_metrics);
305         if (values == NULL) {
306                 TELEMETRY_LOG_ERR("Cannot allocate memory");
307                 ret = rte_telemetry_send_error_response(telemetry,
308                          -ENOMEM);
309                 if (ret < 0)
310                         TELEMETRY_LOG_ERR("Could not send error");
311                 return -1;
312         }
313
314         RTE_ETH_FOREACH_DEV(p) {
315                 ep.pp.port_ids[num_port_ids] = p;
316                 num_port_ids++;
317         }
318
319         if (!num_port_ids) {
320                 TELEMETRY_LOG_WARN("No active ports");
321
322                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
323                 if (ret < 0)
324                         TELEMETRY_LOG_ERR("Could not send error");
325
326                 goto fail;
327         }
328
329         ret = rte_metrics_get_values(ep.pp.port_ids[0], values, num_metrics);
330         if (ret < 0) {
331                 TELEMETRY_LOG_ERR("Could not get stat values");
332                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
333                 if (ret < 0)
334                         TELEMETRY_LOG_ERR("Could not send error");
335                 goto fail;
336         }
337         for (i = 0; i < num_metrics; i++)
338                 ep.pp.metric_ids[i] = values[i].key;
339
340         ep.pp.num_port_ids = num_port_ids;
341         ep.pp.num_metric_ids = num_metrics;
342         ep.type = PORT_STATS;
343
344         ret = rte_telemetry_send_ports_stats_values(&ep, telemetry);
345         if (ret < 0) {
346                 TELEMETRY_LOG_ERR("Sending ports stats values failed");
347                 goto fail;
348         }
349
350         free(values);
351         return 0;
352
353 fail:
354         free(values);
355         return -1;
356 }
357
358 int32_t
359 rte_telemetry_command_global_stat_values(struct telemetry_impl *telemetry,
360          int action, json_t *data)
361 {
362         int ret, num_metrics, i;
363         struct rte_metric_value *values;
364         struct telemetry_encode_param ep;
365
366         memset(&ep, 0, sizeof(ep));
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         num_metrics = rte_metrics_get_values(RTE_METRICS_GLOBAL, NULL, 0);
389         if (num_metrics < 0) {
390                 TELEMETRY_LOG_ERR("Cannot get metrics count");
391
392                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
393                 if (ret < 0)
394                         TELEMETRY_LOG_ERR("Could not send error");
395
396                 return -1;
397         } else if (num_metrics == 0) {
398                 TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
399
400                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
401                 if (ret < 0)
402                         TELEMETRY_LOG_ERR("Could not send error");
403
404                 return -1;
405         }
406
407         values = malloc(sizeof(struct rte_metric_value) * num_metrics);
408         if (values == NULL) {
409                 TELEMETRY_LOG_ERR("Cannot allocate memory");
410                 ret = rte_telemetry_send_error_response(telemetry,
411                          -ENOMEM);
412                 if (ret < 0)
413                         TELEMETRY_LOG_ERR("Could not send error");
414                 return -1;
415         }
416
417         ret = rte_metrics_get_values(RTE_METRICS_GLOBAL, values, num_metrics);
418         if (ret < 0) {
419                 TELEMETRY_LOG_ERR("Could not get stat values");
420                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
421                 if (ret < 0)
422                         TELEMETRY_LOG_ERR("Could not send error");
423                 goto fail;
424         }
425         for (i = 0; i < num_metrics; i++)
426                 ep.gp.metric_ids[i] = values[i].key;
427
428         ep.gp.num_metric_ids = num_metrics;
429         ep.type = GLOBAL_STATS;
430
431         ret = rte_telemetry_send_global_stats_values(&ep, telemetry);
432         if (ret < 0) {
433                 TELEMETRY_LOG_ERR("Sending global stats values failed");
434                 goto fail;
435         }
436
437         free(values);
438         return 0;
439
440 fail:
441         free(values);
442         return -1;
443 }
444
445 int32_t
446 rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
447         *telemetry, int action, json_t *data)
448 {
449         int ret;
450         json_t *port_ids_json = json_object_get(data, "ports");
451         json_t *stat_names_json = json_object_get(data, "stats");
452         uint64_t num_stat_names = json_array_size(stat_names_json);
453         const char *stat_names[num_stat_names];
454         struct telemetry_encode_param ep;
455         size_t index;
456         json_t *value;
457
458         ep.pp.num_port_ids = json_array_size(port_ids_json);
459         ep.pp.num_metric_ids = num_stat_names;
460         memset(&ep, 0, sizeof(ep));
461         if (telemetry == NULL) {
462                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
463                 return -1;
464         }
465
466         if (action != ACTION_GET) {
467                 TELEMETRY_LOG_WARN("Invalid action for this command");
468                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
469                 if (ret < 0)
470                         TELEMETRY_LOG_ERR("Could not send error");
471                 return -1;
472         }
473
474         if (!json_is_object(data)) {
475                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
476                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
477                 if (ret < 0)
478                         TELEMETRY_LOG_ERR("Could not send error");
479                 return -1;
480         }
481
482         if (!json_is_array(port_ids_json) ||
483                  !json_is_array(stat_names_json)) {
484                 TELEMETRY_LOG_WARN("Invalid input data array(s)");
485                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
486                 if (ret < 0)
487                         TELEMETRY_LOG_ERR("Could not send error");
488                 return -1;
489         }
490
491         json_array_foreach(port_ids_json, index, value) {
492                 if (!json_is_integer(value)) {
493                         TELEMETRY_LOG_WARN("Port ID given is not valid");
494                         ret = rte_telemetry_send_error_response(telemetry,
495                                 -EINVAL);
496                         if (ret < 0)
497                                 TELEMETRY_LOG_ERR("Could not send error");
498                         return -1;
499                 }
500                 ep.pp.port_ids[index] = json_integer_value(value);
501                 ret = rte_telemetry_is_port_active(ep.pp.port_ids[index]);
502                 if (ret < 1) {
503                         ret = rte_telemetry_send_error_response(telemetry,
504                                 -EINVAL);
505                         if (ret < 0)
506                                 TELEMETRY_LOG_ERR("Could not send error");
507                         return -1;
508                 }
509         }
510
511         json_array_foreach(stat_names_json, index, value) {
512                 if (!json_is_string(value)) {
513                         TELEMETRY_LOG_WARN("Stat Name given is not a string");
514
515                         ret = rte_telemetry_send_error_response(telemetry,
516                                         -EINVAL);
517                         if (ret < 0)
518                                 TELEMETRY_LOG_ERR("Could not send error");
519
520                         return -1;
521                 }
522                 stat_names[index] = json_string_value(value);
523         }
524
525         ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names,
526                 ep.pp.metric_ids, num_stat_names);
527         if (ret < 0) {
528                 TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
529                 return -1;
530         }
531
532         ep.type = PORT_STATS;
533         ret = rte_telemetry_send_ports_stats_values(&ep, telemetry);
534         if (ret < 0) {
535                 TELEMETRY_LOG_ERR("Sending ports stats values failed");
536                 return -1;
537         }
538
539         return 0;
540 }
541
542 static int32_t
543 rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
544         const char *command, json_t *data)
545 {
546         int ret;
547         uint32_t i;
548
549         if (telemetry == NULL) {
550                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
551                 return -1;
552         }
553
554         struct rte_telemetry_command commands[] = {
555                 {
556                         .text = "clients",
557                         .fn = &rte_telemetry_command_clients
558                 },
559                 {
560                         .text = "ports",
561                         .fn = &rte_telemetry_command_ports
562                 },
563                 {
564                         .text = "ports_details",
565                         .fn = &rte_telemetry_command_ports_details
566                 },
567                 {
568                         .text = "port_stats",
569                         .fn = &rte_telemetry_command_port_stats
570                 },
571                 {
572                         .text = "ports_stats_values_by_name",
573                         .fn = &rte_telemetry_command_ports_stats_values_by_name
574                 },
575                 {
576                         .text = "ports_all_stat_values",
577                         .fn = &rte_telemetry_command_ports_all_stat_values
578                 },
579                 {
580                         .text = "global_stat_values",
581                         .fn = &rte_telemetry_command_global_stat_values
582                 }
583         };
584
585         const uint32_t num_commands = RTE_DIM(commands);
586
587         for (i = 0; i < num_commands; i++) {
588                 if (strcmp(command, commands[i].text) == 0) {
589                         ret = commands[i].fn(telemetry, action, data);
590                         if (ret < 0) {
591                                 TELEMETRY_LOG_ERR("Command Function for %s failed",
592                                         commands[i].text);
593                                 return -1;
594                         }
595                         return 0;
596                 }
597         }
598
599         TELEMETRY_LOG_WARN("\"%s\" command not found", command);
600
601         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
602         if (ret < 0)
603                 TELEMETRY_LOG_ERR("Could not send error");
604
605         return -1;
606 }
607
608 int32_t __rte_experimental
609 rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
610 {
611         int ret, action_int;
612         json_error_t error;
613         json_t *root, *action, *command, *data;
614
615         if (telemetry == NULL) {
616                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
617                 return -1;
618         }
619
620         root = json_loads(socket_rx_data, 0, &error);
621         if (root == NULL) {
622                 TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
623                                 error.text);
624                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
625                 if (ret < 0)
626                         TELEMETRY_LOG_ERR("Could not send error");
627                 return -EPERM;
628         } else if (!json_is_object(root)) {
629                 TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
630                 json_decref(root);
631                 goto einval_fail;
632         }
633
634         action = json_object_get(root, "action");
635         if (action == NULL) {
636                 TELEMETRY_LOG_WARN("Request does not have action field");
637                 goto einval_fail;
638         } else if (!json_is_integer(action)) {
639                 TELEMETRY_LOG_WARN("Action value is not an integer");
640                 goto einval_fail;
641         }
642
643         command = json_object_get(root, "command");
644         if (command == NULL) {
645                 TELEMETRY_LOG_WARN("Request does not have command field");
646                 goto einval_fail;
647         } else if (!json_is_string(command)) {
648                 TELEMETRY_LOG_WARN("Command value is not a string");
649                 goto einval_fail;
650         }
651
652         action_int = json_integer_value(action);
653         if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
654                 TELEMETRY_LOG_WARN("Invalid action code");
655                 goto einval_fail;
656         }
657
658         const char *command_string = json_string_value(command);
659         data = json_object_get(root, "data");
660         if (data == NULL) {
661                 TELEMETRY_LOG_WARN("Request does not have data field");
662                 goto einval_fail;
663         }
664
665         ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
666                 data);
667         if (ret < 0) {
668                 TELEMETRY_LOG_WARN("Could not parse command");
669                 return -EINVAL;
670         }
671
672         return 0;
673
674 einval_fail:
675         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
676         if (ret < 0) {
677                 TELEMETRY_LOG_ERR("Could not send error");
678                 return -EPERM;
679         }
680         return -EINVAL;
681 }