telemetry: add parser for client socket messages
[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_name *names;
260         uint64_t num_port_ids = 0;
261
262         if (telemetry == NULL) {
263                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
264                 return -1;
265         }
266
267         if (action != ACTION_GET) {
268                 TELEMETRY_LOG_WARN("Invalid action for this command");
269                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
270                 if (ret < 0)
271                         TELEMETRY_LOG_ERR("Could not send error");
272                 return -1;
273         }
274
275         if (json_is_object(data)) {
276                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
277                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
278                 if (ret < 0)
279                         TELEMETRY_LOG_ERR("Could not send error");
280                 return -1;
281         }
282
283         num_metrics = rte_metrics_get_names(NULL, 0);
284         if (num_metrics < 0) {
285                 TELEMETRY_LOG_ERR("Cannot get metrics count");
286
287                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
288                 if (ret < 0)
289                         TELEMETRY_LOG_ERR("Could not send error");
290
291                 return -1;
292         } else if (num_metrics == 0) {
293                 TELEMETRY_LOG_ERR("No metrics to display (none have been registered)");
294
295                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
296                 if (ret < 0)
297                         TELEMETRY_LOG_ERR("Could not send error");
298
299                 return -1;
300         }
301
302         names = malloc(sizeof(struct rte_metric_name) * num_metrics);
303         if (names == NULL) {
304                 TELEMETRY_LOG_ERR("Cannot allocate memory");
305                 ret = rte_telemetry_send_error_response(telemetry,
306                          -ENOMEM);
307                 if (ret < 0)
308                         TELEMETRY_LOG_ERR("Could not send error");
309                 return -1;
310         }
311
312         const char *stat_names[num_metrics];
313         uint32_t stat_ids[num_metrics];
314
315         RTE_ETH_FOREACH_DEV(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_names(names, num_metrics);
330         for (i = 0; i < num_metrics; i++)
331                 stat_names[i] = names[i].name;
332
333         ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
334                 num_metrics);
335         if (ret < 0) {
336                 TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
337                 goto fail;
338         }
339
340         return 0;
341
342 fail:
343         free(names);
344         return -1;
345 }
346
347 int32_t
348 rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl
349         *telemetry, int action, json_t *data)
350 {
351         int ret;
352         json_t *port_ids_json = json_object_get(data, "ports");
353         json_t *stat_names_json = json_object_get(data, "stats");
354         uint64_t num_port_ids = json_array_size(port_ids_json);
355         uint64_t num_stat_names = json_array_size(stat_names_json);
356         const char *stat_names[num_stat_names];
357         uint32_t port_ids[num_port_ids], stat_ids[num_stat_names];
358         size_t index;
359         json_t *value;
360
361         if (telemetry == NULL) {
362                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
363                 return -1;
364         }
365
366         if (action != ACTION_GET) {
367                 TELEMETRY_LOG_WARN("Invalid action for this command");
368                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
369                 if (ret < 0)
370                         TELEMETRY_LOG_ERR("Could not send error");
371                 return -1;
372         }
373
374         if (!json_is_object(data)) {
375                 TELEMETRY_LOG_WARN("Invalid data provided for this command");
376                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
377                 if (ret < 0)
378                         TELEMETRY_LOG_ERR("Could not send error");
379                 return -1;
380         }
381
382         if (!json_is_array(port_ids_json) ||
383                  !json_is_array(stat_names_json)) {
384                 TELEMETRY_LOG_WARN("Invalid input data array(s)");
385                 ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
386                 if (ret < 0)
387                         TELEMETRY_LOG_ERR("Could not send error");
388                 return -1;
389         }
390
391         json_array_foreach(port_ids_json, index, value) {
392                 if (!json_is_integer(value)) {
393                         TELEMETRY_LOG_WARN("Port ID given is not valid");
394                         ret = rte_telemetry_send_error_response(telemetry,
395                                 -EINVAL);
396                         if (ret < 0)
397                                 TELEMETRY_LOG_ERR("Could not send error");
398                         return -1;
399                 }
400                 port_ids[index] = json_integer_value(value);
401                 ret = rte_telemetry_is_port_active(port_ids[index]);
402                 if (ret < 1) {
403                         ret = rte_telemetry_send_error_response(telemetry,
404                                 -EINVAL);
405                         if (ret < 0)
406                                 TELEMETRY_LOG_ERR("Could not send error");
407                         return -1;
408                 }
409         }
410
411         json_array_foreach(stat_names_json, index, value) {
412                 if (!json_is_string(value)) {
413                         TELEMETRY_LOG_WARN("Stat Name given is not a string");
414
415                         ret = rte_telemetry_send_error_response(telemetry,
416                                         -EINVAL);
417                         if (ret < 0)
418                                 TELEMETRY_LOG_ERR("Could not send error");
419
420                         return -1;
421                 }
422                 stat_names[index] = json_string_value(value);
423         }
424
425         ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids,
426                 num_stat_names);
427         if (ret < 0) {
428                 TELEMETRY_LOG_ERR("Could not convert stat names to IDs");
429                 return -1;
430         }
431         return 0;
432 }
433
434 static int32_t
435 rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action,
436         const char *command, json_t *data)
437 {
438         int ret;
439         uint32_t i;
440
441         if (telemetry == NULL) {
442                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
443                 return -1;
444         }
445
446         struct rte_telemetry_command commands[] = {
447                 {
448                         .text = "clients",
449                         .fn = &rte_telemetry_command_clients
450                 },
451                 {
452                         .text = "ports",
453                         .fn = &rte_telemetry_command_ports
454                 },
455                 {
456                         .text = "ports_details",
457                         .fn = &rte_telemetry_command_ports_details
458                 },
459                 {
460                         .text = "port_stats",
461                         .fn = &rte_telemetry_command_port_stats
462                 },
463                 {
464                         .text = "ports_stats_values_by_name",
465                         .fn = &rte_telemetry_command_ports_stats_values_by_name
466                 },
467                 {
468                         .text = "ports_all_stat_values",
469                         .fn = &rte_telemetry_command_ports_all_stat_values
470                 }
471         };
472
473         const uint32_t num_commands = RTE_DIM(commands);
474
475         for (i = 0; i < num_commands; i++) {
476                 if (strcmp(command, commands[i].text) == 0) {
477                         ret = commands[i].fn(telemetry, action, data);
478                         if (ret < 0) {
479                                 TELEMETRY_LOG_ERR("Command Function for %s failed",
480                                         commands[i].text);
481                                 return -1;
482                         }
483                         return 0;
484                 }
485         }
486
487         TELEMETRY_LOG_WARN("\"%s\" command not found", command);
488
489         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
490         if (ret < 0)
491                 TELEMETRY_LOG_ERR("Could not send error");
492
493         return -1;
494 }
495
496 int32_t __rte_experimental
497 rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data)
498 {
499         int ret, action_int;
500         json_error_t error;
501         json_t *root, *action, *command, *data;
502
503         if (telemetry == NULL) {
504                 TELEMETRY_LOG_ERR("Invalid telemetry argument");
505                 return -1;
506         }
507
508         root = json_loads(socket_rx_data, 0, &error);
509         if (root == NULL) {
510                 TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s",
511                                 error.text);
512                 ret = rte_telemetry_send_error_response(telemetry, -EPERM);
513                 if (ret < 0)
514                         TELEMETRY_LOG_ERR("Could not send error");
515                 return -EPERM;
516         } else if (!json_is_object(root)) {
517                 TELEMETRY_LOG_WARN("JSON Request is not a JSON object");
518                 json_decref(root);
519                 goto einval_fail;
520         }
521
522         action = json_object_get(root, "action");
523         if (action == NULL) {
524                 TELEMETRY_LOG_WARN("Request does not have action field");
525                 goto einval_fail;
526         } else if (!json_is_integer(action)) {
527                 TELEMETRY_LOG_WARN("Action value is not an integer");
528                 goto einval_fail;
529         }
530
531         command = json_object_get(root, "command");
532         if (command == NULL) {
533                 TELEMETRY_LOG_WARN("Request does not have command field");
534                 goto einval_fail;
535         } else if (!json_is_string(command)) {
536                 TELEMETRY_LOG_WARN("Command value is not a string");
537                 goto einval_fail;
538         }
539
540         action_int = json_integer_value(action);
541         if (action_int != ACTION_GET && action_int != ACTION_DELETE) {
542                 TELEMETRY_LOG_WARN("Invalid action code");
543                 goto einval_fail;
544         }
545
546         const char *command_string = json_string_value(command);
547         data = json_object_get(root, "data");
548         if (data == NULL) {
549                 TELEMETRY_LOG_WARN("Request does not have data field");
550                 goto einval_fail;
551         }
552
553         ret = rte_telemetry_parse_command(telemetry, action_int, command_string,
554                 data);
555         if (ret < 0) {
556                 TELEMETRY_LOG_WARN("Could not parse command");
557                 return -EINVAL;
558         }
559
560         return 0;
561
562 einval_fail:
563         ret = rte_telemetry_send_error_response(telemetry, -EINVAL);
564         if (ret < 0) {
565                 TELEMETRY_LOG_ERR("Could not send error");
566                 return -EPERM;
567         }
568         return -EINVAL;
569 }