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