1 /**
2 * collectd - src/redfish.c
3 *
4 * Copyright(c) 2018 Intel Corporation. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * Authors:
25 * Marcin Mozejko <marcinx.mozejko@intel.com>
26 * Martin Kennelly <martin.kennelly@intel.com>
27 * Adrian Boczkowski <adrianx.boczkowski@intel.com>
28 **/
29
30 #include "collectd.h"
31
32 #include "utils/avltree/avltree.h"
33 #include "utils/common/common.h"
34 #include "utils/deq/deq.h"
35 #include "utils_llist.h"
36
37 #include <redfish.h>
38 #define PLUGIN_NAME "redfish"
39 #define MAX_STR_LEN 128
40
41 struct redfish_property_s {
42 char *name;
43 char *plugin_inst;
44 char *type;
45 char *type_inst;
46 };
47 typedef struct redfish_property_s redfish_property_t;
48
49 struct redfish_resource_s {
50 char *name;
51 llist_t *properties;
52 };
53 typedef struct redfish_resource_s redfish_resource_t;
54
55 struct redfish_query_s {
56 char *name;
57 char *endpoint;
58 llist_t *resources;
59 };
60 typedef struct redfish_query_s redfish_query_t;
61
62 struct redfish_service_s {
63 char *name;
64 char *host;
65 char *user;
66 char *passwd;
67 char *token;
68 unsigned int flags;
69 char **queries; /* List of queries */
70 llist_t *query_ptrs; /* Pointers to query structs */
71 size_t queries_num;
72 enumeratorAuthentication auth;
73 redfishService *redfish;
74 };
75 typedef struct redfish_service_s redfish_service_t;
76
77 struct redfish_payload_ctx_s {
78 redfish_service_t *service;
79 redfish_query_t *query;
80 };
81 typedef struct redfish_payload_ctx_s redfish_payload_ctx_t;
82
83 enum redfish_value_type_e { VAL_TYPE_STR = 0, VAL_TYPE_INT, VAL_TYPE_REAL };
84 typedef enum redfish_value_type_e redfish_value_type_t;
85
86 union redfish_value_u {
87 double real;
88 int integer;
89 char *string;
90 };
91 typedef union redfish_value_u redfish_value_t;
92
93 typedef struct redfish_job_s {
94 DEQ_LINKS(struct redfish_job_s);
95 redfish_payload_ctx_t *service_query;
96 } redfish_job_t;
97
98 DEQ_DECLARE(redfish_job_t, redfish_job_list_t);
99
100 struct redfish_ctx_s {
101 llist_t *services;
102 c_avl_tree_t *queries;
103 pthread_t worker_thread;
104 redfish_job_list_t jobs;
105 };
106 typedef struct redfish_ctx_s redfish_ctx_t;
107
108 /* Globals */
109 static redfish_ctx_t ctx;
110
111 static int redfish_cleanup(void);
112 static int redfish_validate_config(void);
113 static void *redfish_worker_thread(void __attribute__((unused)) * args);
114
115 #if COLLECT_DEBUG
redfish_print_config(void)116 static void redfish_print_config(void) {
117 DEBUG(PLUGIN_NAME ": ====================CONFIGURATION====================");
118 DEBUG(PLUGIN_NAME ": SERVICES: %d", llist_size(ctx.services));
119 for (llentry_t *le = llist_head(ctx.services); le != NULL; le = le->next) {
120 redfish_service_t *s = (redfish_service_t *)le->value;
121 char queries_str[MAX_STR_LEN];
122
123 strjoin(queries_str, MAX_STR_LEN, s->queries, s->queries_num, ", ");
124
125 DEBUG(PLUGIN_NAME ": --------------------");
126 DEBUG(PLUGIN_NAME ": Service: %s", s->name);
127 DEBUG(PLUGIN_NAME ": Host: %s", s->host);
128
129 if (s->user && s->passwd) {
130 DEBUG(PLUGIN_NAME ": User: %s", s->user);
131 DEBUG(PLUGIN_NAME ": Passwd: %s", s->passwd);
132 } else if (s->token)
133 DEBUG(PLUGIN_NAME ": Token: %s", s->token);
134
135 DEBUG(PLUGIN_NAME ": Queries[%" PRIsz "]: (%s)", s->queries_num,
136 queries_str);
137 }
138
139 DEBUG(PLUGIN_NAME ": =====================================================");
140
141 c_avl_iterator_t *i = c_avl_get_iterator(ctx.queries);
142 char *key;
143 redfish_query_t *q;
144
145 DEBUG(PLUGIN_NAME ": QUERIES: %d", c_avl_size(ctx.queries));
146
147 while (c_avl_iterator_next(i, (void *)&key, (void *)&q) == 0) {
148 DEBUG(PLUGIN_NAME ": --------------------");
149 DEBUG(PLUGIN_NAME ": Query: %s", q->name);
150 DEBUG(PLUGIN_NAME ": Endpoint: %s", q->endpoint);
151 for (llentry_t *le = llist_head(q->resources); le != NULL; le = le->next) {
152 redfish_resource_t *r = (redfish_resource_t *)le->value;
153 DEBUG(PLUGIN_NAME ": Resource: %s", r->name);
154 for (llentry_t *le = llist_head(r->properties); le != NULL;
155 le = le->next) {
156 redfish_property_t *p = (redfish_property_t *)le->value;
157 DEBUG(PLUGIN_NAME ": Property: %s", p->name);
158 DEBUG(PLUGIN_NAME ": PluginInstance: %s", p->plugin_inst);
159 DEBUG(PLUGIN_NAME ": Type: %s", p->type);
160 DEBUG(PLUGIN_NAME ": TypeInstance: %s", p->type_inst);
161 }
162 }
163 }
164
165 c_avl_iterator_destroy(i);
166 DEBUG(PLUGIN_NAME ": =====================================================");
167 }
168 #endif
169
redfish_service_destroy(redfish_service_t * service)170 static void redfish_service_destroy(redfish_service_t *service) {
171 /* This is checked internally by cleanupServiceEnumerator() also,
172 * but as long as it's a third-party library let's be a little 'defensive' */
173 if (service->redfish != NULL)
174 cleanupServiceEnumerator(service->redfish);
175
176 /* Destroy all service members, sfree() as well as strarray_free()
177 * and llist_destroy() are safe to call on NULL argument */
178 sfree(service->name);
179 sfree(service->host);
180 sfree(service->user);
181 sfree(service->passwd);
182 sfree(service->token);
183 strarray_free(service->queries, (size_t)service->queries_num);
184 llist_destroy(service->query_ptrs);
185
186 sfree(service);
187 }
188
redfish_job_destroy(redfish_job_t * job)189 static void redfish_job_destroy(redfish_job_t *job) {
190 sfree(job->service_query);
191 sfree(job);
192 }
193
redfish_init(void)194 static int redfish_init(void) {
195 #if COLLECT_DEBUG
196 redfish_print_config();
197 #endif
198 int ret = redfish_validate_config();
199
200 if (ret != 0) {
201 ERROR(PLUGIN_NAME ": Validation of configuration file failed");
202 return ret;
203 }
204
205 DEQ_INIT(ctx.jobs);
206 ret = pthread_create(&ctx.worker_thread, NULL, redfish_worker_thread, NULL);
207
208 if (ret != 0) {
209 ERROR(PLUGIN_NAME ": Creation of thread failed");
210 return ret;
211 }
212
213 for (llentry_t *le = llist_head(ctx.services); le != NULL; le = le->next) {
214 redfish_service_t *service = (redfish_service_t *)le->value;
215 /* Ignore redfish version */
216 service->flags |= REDFISH_FLAG_SERVICE_NO_VERSION_DOC;
217
218 /* Preparing struct for authentication */
219 if (service->user && service->passwd) {
220 service->auth.authCodes.userPass.username = service->user;
221 service->auth.authCodes.userPass.password = service->passwd;
222 service->redfish = createServiceEnumerator(
223 service->host, NULL, &service->auth, service->flags);
224 } else if (service->token) {
225 service->auth.authCodes.authToken.token = service->token;
226 service->auth.authType = REDFISH_AUTH_BEARER_TOKEN;
227 service->redfish = createServiceEnumerator(
228 service->host, NULL, &service->auth, service->flags);
229 } else {
230 service->redfish =
231 createServiceEnumerator(service->host, NULL, NULL, service->flags);
232 }
233
234 service->query_ptrs = llist_create();
235 if (service->query_ptrs == NULL) {
236 ERROR(PLUGIN_NAME ": Failed to allocate memory for service query list");
237 goto error;
238 }
239
240 /* Preparing query pointers list for every service */
241 for (size_t i = 0; i < service->queries_num; i++) {
242 redfish_query_t *ptr;
243 if (c_avl_get(ctx.queries, (void *)service->queries[i], (void *)&ptr) !=
244 0) {
245 ERROR(PLUGIN_NAME ": Cannot find a service query in a context");
246 goto error;
247 }
248
249 llentry_t *entry = llentry_create(ptr->name, ptr);
250 if (entry != NULL)
251 llist_append(service->query_ptrs, entry);
252 else {
253 ERROR(PLUGIN_NAME ": Failed to allocate memory for a query list entry");
254 goto error;
255 }
256 }
257 }
258
259 return 0;
260
261 error:
262 /* Freeing libredfish resources & llists */
263 for (llentry_t *le = llist_head(ctx.services); le != NULL; le = le->next) {
264 redfish_service_t *service = (redfish_service_t *)le->value;
265
266 redfish_service_destroy(service);
267 }
268 return -ENOMEM;
269 }
270
redfish_preconfig(void)271 static int redfish_preconfig(void) {
272 /* Creating placeholder for services */
273 ctx.services = llist_create();
274 if (ctx.services == NULL)
275 goto error;
276
277 /* Creating placeholder for queries */
278 ctx.queries = c_avl_create((void *)strcmp);
279 if (ctx.services == NULL)
280 goto free_services;
281
282 return 0;
283
284 free_services:
285 llist_destroy(ctx.services);
286 error:
287 ERROR(PLUGIN_NAME ": Failed to allocate memory for plugin context");
288 return -ENOMEM;
289 }
290
redfish_config_property(redfish_resource_t * resource,oconfig_item_t * cfg_item)291 static int redfish_config_property(redfish_resource_t *resource,
292 oconfig_item_t *cfg_item) {
293 assert(resource != NULL);
294 assert(cfg_item != NULL);
295
296 redfish_property_t *property = calloc(1, sizeof(*property));
297
298 if (property == NULL) {
299 ERROR(PLUGIN_NAME ": Failed to allocate memory for property");
300 return -ENOMEM;
301 }
302
303 int ret = cf_util_get_string(cfg_item, &property->name);
304 if (ret != 0) {
305 ERROR(PLUGIN_NAME ": Could not get property argument in resource section "
306 "named \"%s\"",
307 resource->name);
308 ret = -EINVAL;
309 goto free_all;
310 }
311
312 for (int i = 0; i < cfg_item->children_num; i++) {
313 oconfig_item_t *opt = cfg_item->children + i;
314 if (strcasecmp("PluginInstance", opt->key) == 0)
315 ret = cf_util_get_string(opt, &property->plugin_inst);
316 else if (strcasecmp("Type", opt->key) == 0)
317 ret = cf_util_get_string(opt, &property->type);
318 else if (strcasecmp("TypeInstance", opt->key) == 0)
319 ret = cf_util_get_string(opt, &property->type_inst);
320 else {
321 ERROR(PLUGIN_NAME ": Invalid option \"%s\" in property \"%s\" "
322 "in resource \"%s\"",
323 opt->key, property->name, resource->name);
324 ret = -EINVAL;
325 goto free_all;
326 }
327
328 if (ret != 0) {
329 ERROR(PLUGIN_NAME ": Something went wrong going through attributes in "
330 "property named \"%s\" in resource named \"%s\"",
331 property->name, resource->name);
332 goto free_all;
333 }
334 }
335
336 llentry_t *entry = llentry_create(property->name, property);
337 if (entry == NULL) {
338 ERROR(PLUGIN_NAME ": Failed to allocate memory for property");
339 ret = -ENOMEM;
340 goto free_all;
341 }
342 llist_append(resource->properties, entry);
343
344 return 0;
345
346 free_all:
347 sfree(property->name);
348 sfree(property->plugin_inst);
349 sfree(property->type);
350 sfree(property->type_inst);
351 sfree(property);
352 return ret;
353 }
354
redfish_config_resource(redfish_query_t * query,oconfig_item_t * cfg_item)355 static int redfish_config_resource(redfish_query_t *query,
356 oconfig_item_t *cfg_item) {
357 assert(query != NULL);
358 assert(cfg_item != NULL);
359
360 redfish_resource_t *resource = calloc(1, sizeof(*resource));
361
362 if (resource == NULL) {
363 ERROR(PLUGIN_NAME ": Failed to allocate memory for resource");
364 return -ENOMEM;
365 }
366
367 resource->properties = llist_create();
368
369 if (resource->properties == NULL)
370 goto free_memory;
371
372 int ret = cf_util_get_string(cfg_item, &resource->name);
373 if (ret != 0) {
374 ERROR(PLUGIN_NAME ": Could not get resource name for query named \"%s\"",
375 query->name);
376 goto free_memory;
377 }
378 for (int i = 0; i < cfg_item->children_num; i++) {
379 oconfig_item_t *opt = cfg_item->children + i;
380 if (strcasecmp("Property", opt->key) != 0) {
381 WARNING(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
382 continue;
383 }
384
385 ret = redfish_config_property(resource, opt);
386
387 if (ret != 0) {
388 goto free_memory;
389 }
390 }
391
392 llentry_t *entry = llentry_create(resource->name, resource);
393 if (entry == NULL) {
394 ERROR(PLUGIN_NAME ": Failed to allocate memory for resource list entry");
395 goto free_memory;
396 }
397 llist_append(query->resources, entry);
398
399 return 0;
400
401 free_memory:
402 sfree(resource->name);
403 llist_destroy(resource->properties);
404 sfree(resource);
405 return -1;
406 }
407
redfish_config_query(oconfig_item_t * cfg_item,c_avl_tree_t * queries)408 static int redfish_config_query(oconfig_item_t *cfg_item,
409 c_avl_tree_t *queries) {
410 redfish_query_t *query = calloc(1, sizeof(*query));
411
412 if (query == NULL) {
413 ERROR(PLUGIN_NAME ": Failed to allocate memory for query");
414 return -ENOMEM;
415 }
416
417 query->resources = llist_create();
418
419 int ret;
420 if (query->resources == NULL) {
421 ret = -ENOMEM;
422 goto free_all;
423 }
424
425 ret = cf_util_get_string(cfg_item, &query->name);
426 if (ret != 0) {
427 ERROR(PLUGIN_NAME ": Unable to get query name. Query ignored");
428 ret = -EINVAL;
429 goto free_all;
430 }
431
432 for (int i = 0; i < cfg_item->children_num; i++) {
433 oconfig_item_t *opt = cfg_item->children + i;
434
435 if (strcasecmp("Endpoint", opt->key) == 0)
436 ret = cf_util_get_string(opt, &query->endpoint);
437 else if (strcasecmp("Resource", opt->key) == 0)
438 ret = redfish_config_resource(query, opt);
439 else {
440 ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
441 ret = -EINVAL;
442 goto free_all;
443 }
444
445 if (ret != 0) {
446 ERROR(PLUGIN_NAME ": Something went wrong processing query \"%s\"",
447 query->name);
448 ret = -EINVAL;
449 goto free_all;
450 }
451 }
452
453 ret = c_avl_insert(queries, query->name, query);
454
455 if (ret != 0)
456 goto free_all;
457
458 return 0;
459
460 free_all:
461 sfree(query->name);
462 sfree(query->endpoint);
463 llist_destroy(query->resources);
464 sfree(query);
465 return ret;
466 }
467
redfish_read_queries(oconfig_item_t * cfg_item,char *** queries_ptr)468 static int redfish_read_queries(oconfig_item_t *cfg_item, char ***queries_ptr) {
469 char **queries = NULL;
470 size_t queries_num = 0;
471
472 for (int i = 0; i < cfg_item->values_num; ++i) {
473 strarray_add(&queries, &queries_num, cfg_item->values[i].value.string);
474 }
475
476 if (queries_num != (size_t)cfg_item->values_num) {
477 ERROR(PLUGIN_NAME ": Failed to allocate memory for query list");
478 strarray_free(queries, queries_num);
479 return -ENOMEM;
480 }
481
482 *queries_ptr = queries;
483 return 0;
484 }
485
redfish_config_service(oconfig_item_t * cfg_item)486 static int redfish_config_service(oconfig_item_t *cfg_item) {
487 redfish_service_t *service = calloc(1, sizeof(*service));
488
489 if (service == NULL) {
490 ERROR(PLUGIN_NAME ": Failed to allocate memory for service");
491 return -ENOMEM;
492 }
493
494 int ret = cf_util_get_string(cfg_item, &service->name);
495 if (ret != 0) {
496 ERROR(PLUGIN_NAME ": A service was defined without an argument");
497 goto free_service;
498 }
499
500 for (int i = 0; i < cfg_item->children_num; i++) {
501 oconfig_item_t *opt = cfg_item->children + i;
502
503 if (strcasecmp("Host", opt->key) == 0)
504 ret = cf_util_get_string(opt, &service->host);
505 else if (strcasecmp("User", opt->key) == 0)
506 ret = cf_util_get_string(opt, &service->user);
507 else if (strcasecmp("Passwd", opt->key) == 0)
508 ret = cf_util_get_string(opt, &service->passwd);
509 else if (strcasecmp("Token", opt->key) == 0)
510 ret = cf_util_get_string(opt, &service->token);
511 else if (strcasecmp("Queries", opt->key) == 0) {
512 ret = redfish_read_queries(opt, &service->queries);
513 service->queries_num = opt->values_num;
514 } else {
515 ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
516 }
517
518 if (ret != 0) {
519 ERROR(PLUGIN_NAME ": Something went wrong processing the service named \
520 \"%s\"",
521 service->name);
522 goto free_service;
523 }
524 }
525
526 llentry_t *entry = llentry_create(service->name, service);
527
528 if (entry != NULL)
529 llist_append(ctx.services, entry);
530 else {
531 ERROR(PLUGIN_NAME ": Failed to create list for service name \"%s\"",
532 service->name);
533 goto free_service;
534 }
535
536 return 0;
537
538 free_service:
539 redfish_service_destroy(service);
540 return -1;
541 }
542
redfish_config(oconfig_item_t * cfg_item)543 static int redfish_config(oconfig_item_t *cfg_item) {
544 int ret = redfish_preconfig();
545
546 if (ret != 0)
547 return ret;
548
549 for (int i = 0; i < cfg_item->children_num; i++) {
550 oconfig_item_t *child = cfg_item->children + i;
551
552 if (strcasecmp("Query", child->key) == 0)
553 ret = redfish_config_query(child, ctx.queries);
554 else if (strcasecmp("Service", child->key) == 0)
555 ret = redfish_config_service(child);
556 else {
557 ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", child->key);
558 }
559
560 if (ret != 0) {
561 redfish_cleanup();
562 return ret;
563 }
564 }
565
566 return 0;
567 }
568
redfish_validate_config(void)569 static int redfish_validate_config(void) {
570 /* Service validation */
571 for (llentry_t *llserv = llist_head(ctx.services); llserv != NULL;
572 llserv = llserv->next) {
573 redfish_service_t *service = llserv->value;
574 if (service->name == NULL) {
575 ERROR(PLUGIN_NAME ": A service has no name");
576 return -EINVAL;
577 }
578 if (service->host == NULL) {
579 ERROR(PLUGIN_NAME ": Service \"%s\" has no host attribute",
580 service->name);
581 return -EINVAL;
582 }
583 if ((service->user == NULL) ^ (service->passwd == NULL)) {
584 ERROR(PLUGIN_NAME ": Service \"%s\" does not have user and/or password "
585 "defined",
586 service->name);
587 return -EINVAL;
588 }
589 if (service->user == NULL && service->token == NULL) {
590 ERROR(PLUGIN_NAME ": Service \"%s\" does not have an user/pass or "
591 "token defined",
592 service->name);
593 return -EINVAL;
594 }
595 if (service->queries_num == 0)
596 WARNING(PLUGIN_NAME ": Service \"%s\" does not have queries",
597 service->name);
598
599 for (int i = 0; i < service->queries_num; i++) {
600 redfish_query_t *query_query;
601 bool found = false;
602 char *key;
603 c_avl_iterator_t *query_iter = c_avl_get_iterator(ctx.queries);
604 while (c_avl_iterator_next(query_iter, (void *)&key,
605 (void *)&query_query) == 0 &&
606 !found) {
607 if (query_query->name != NULL && service->queries[i] != NULL &&
608 strcmp(query_query->name, service->queries[i]) == 0) {
609 found = true;
610 }
611 }
612
613 if (!found) {
614 ERROR(PLUGIN_NAME ": Query named \"%s\" in service \"%s\" not found",
615 service->queries[i], service->name);
616 c_avl_iterator_destroy(query_iter);
617 return -EINVAL;
618 }
619
620 c_avl_iterator_destroy(query_iter);
621 }
622 }
623
624 c_avl_iterator_t *queries_iter = c_avl_get_iterator(ctx.queries);
625 char *key;
626 redfish_query_t *query;
627
628 /* Query validation */
629 while (c_avl_iterator_next(queries_iter, (void *)&key, (void *)&query) == 0) {
630 if (query->name == NULL) {
631 ERROR(PLUGIN_NAME ": A query does not have a name");
632 goto error;
633 }
634 if (query->endpoint == NULL) {
635 ERROR(PLUGIN_NAME ": Query \"%s\" does not have a valid endpoint",
636 query->name);
637 goto error;
638 }
639 for (llentry_t *llres = llist_head(query->resources); llres != NULL;
640 llres = llres->next) {
641 redfish_resource_t *resource = (redfish_resource_t *)llres->value;
642 /* Resource validation */
643 if (resource->name == NULL) {
644 WARNING(PLUGIN_NAME ": A resource in query \"%s\" is not named",
645 query->name);
646 }
647 /* Property validation */
648 for (llentry_t *llprop = llist_head(resource->properties); llprop != NULL;
649 llprop = llprop->next) {
650 redfish_property_t *prop = (redfish_property_t *)llprop->value;
651 if (prop->name == NULL) {
652 ERROR(PLUGIN_NAME ": A property has no name in query \"%s\"",
653 query->name);
654 goto error;
655 }
656 if (prop->plugin_inst == NULL) {
657 ERROR(PLUGIN_NAME ": A plugin instance is not defined in property "
658 "\"%s\" in query \"%s\"",
659 prop->name, query->name);
660 goto error;
661 }
662 if (prop->type == NULL) {
663 ERROR(PLUGIN_NAME ": Type is not defined in property \"%s\" in "
664 "query \"%s\"",
665 prop->name, query->name);
666 goto error;
667 }
668 }
669 }
670 }
671
672 c_avl_iterator_destroy(queries_iter);
673
674 return 0;
675
676 error:
677 c_avl_iterator_destroy(queries_iter);
678 return -EINVAL;
679 }
680
redfish_convert_val(redfish_value_t * value,redfish_value_type_t src_type,value_t * vl,int dst_type)681 static int redfish_convert_val(redfish_value_t *value,
682 redfish_value_type_t src_type, value_t *vl,
683 int dst_type) {
684 switch (dst_type) {
685 case DS_TYPE_GAUGE:
686 if (src_type == VAL_TYPE_STR)
687 vl->gauge = strtod(value->string, NULL);
688 else if (src_type == VAL_TYPE_INT)
689 vl->gauge = (gauge_t)value->integer;
690 else if (src_type == VAL_TYPE_REAL)
691 vl->gauge = value->real;
692 break;
693 case DS_TYPE_DERIVE:
694 if (src_type == VAL_TYPE_STR)
695 vl->derive = strtoll(value->string, NULL, 0);
696 else if (src_type == VAL_TYPE_INT)
697 vl->derive = (derive_t)value->integer;
698 else if (src_type == VAL_TYPE_REAL)
699 vl->derive = (derive_t)value->real;
700 break;
701 case DS_TYPE_COUNTER:
702 if (src_type == VAL_TYPE_STR)
703 vl->derive = strtoull(value->string, NULL, 0);
704 else if (src_type == VAL_TYPE_INT)
705 vl->derive = (derive_t)value->integer;
706 else if (src_type == VAL_TYPE_REAL)
707 vl->derive = (derive_t)value->real;
708 break;
709 case DS_TYPE_ABSOLUTE:
710 if (src_type == VAL_TYPE_STR)
711 vl->absolute = strtoull(value->string, NULL, 0);
712 else if (src_type == VAL_TYPE_INT)
713 vl->absolute = (absolute_t)value->integer;
714 else if (src_type == VAL_TYPE_REAL)
715 vl->absolute = (absolute_t)value->real;
716 break;
717 default:
718 ERROR(PLUGIN_NAME ": Invalid data set type. Cannot convert value");
719 return -EINVAL;
720 }
721
722 return 0;
723 }
724
redfish_json_get_string(char * const value,const size_t value_len,const json_t * const json)725 static int redfish_json_get_string(char *const value, const size_t value_len,
726 const json_t *const json) {
727 if (json_is_string(json)) {
728 const char *str_val = json_string_value(json);
729 sstrncpy(value, str_val, value_len);
730 return 0;
731 } else if (json_is_integer(json)) {
732 snprintf(value, value_len, "%d", (int)json_integer_value(json));
733 return 0;
734 }
735
736 ERROR(PLUGIN_NAME ": Expected JSON value to be a string or an integer");
737 return -EINVAL;
738 }
739
redfish_process_payload_property(const redfish_property_t * prop,const json_t * json_array,const redfish_resource_t * res,const redfish_service_t * serv)740 static void redfish_process_payload_property(const redfish_property_t *prop,
741 const json_t *json_array,
742 const redfish_resource_t *res,
743 const redfish_service_t *serv) {
744 /* Iterating through array of sensor(s) */
745 for (int i = 0; i < json_array_size(json_array); i++) {
746 json_t *item = json_array_get(json_array, i);
747 if (item == NULL) {
748 ERROR(PLUGIN_NAME ": Failure retrieving array member for resource \"%s\"",
749 res->name);
750 continue;
751 }
752 json_t *object = json_object_get(item, prop->name);
753 if (object == NULL) {
754 ERROR(PLUGIN_NAME
755 ": Failure retreiving property \"%s\" from resource \"%s\"",
756 prop->name, res->name);
757 continue;
758 }
759 value_list_t v1 = VALUE_LIST_INIT;
760 v1.values_len = 1;
761 if (prop->plugin_inst != NULL)
762 sstrncpy(v1.plugin_instance, prop->plugin_inst,
763 sizeof(v1.plugin_instance));
764 if (prop->type_inst != NULL)
765 sstrncpy(v1.type_instance, prop->type_inst, sizeof(v1.type_instance));
766 else {
767 /* Retrieving MemberId of sensor */
768 json_t *member_id = json_object_get(item, "MemberId");
769 if (member_id == NULL) {
770 ERROR(PLUGIN_NAME
771 ": Failed to get MemberId for property \"%s\" in resource "
772 "\"%s\"",
773 prop->name, res->name);
774 continue;
775 }
776
777 int ret = redfish_json_get_string(v1.type_instance,
778 sizeof(v1.type_instance), member_id);
779
780 if (ret != 0) {
781 ERROR(PLUGIN_NAME ": Cannot convert \"%s\" to a type instance",
782 prop->type_inst);
783 continue;
784 }
785 }
786
787 /* Checking whether real or integer value */
788 redfish_value_t value;
789 redfish_value_type_t type = VAL_TYPE_STR;
790 if (json_is_string(object)) {
791 value.string = (char *)json_string_value(object);
792 } else if (json_is_integer(object)) {
793 type = VAL_TYPE_INT;
794 value.integer = json_integer_value(object);
795 } else if (json_is_real(object)) {
796 type = VAL_TYPE_REAL;
797 value.real = json_real_value(object);
798 }
799 const data_set_t *ds = plugin_get_ds(prop->type);
800
801 /* Check if data set found */
802 if (ds == NULL)
803 continue;
804
805 value_t tmp = {0};
806 v1.values = &tmp;
807 redfish_convert_val(&value, type, v1.values, ds->ds[0].type);
808
809 sstrncpy(v1.host, serv->name, sizeof(v1.host));
810 sstrncpy(v1.plugin, PLUGIN_NAME, sizeof(v1.plugin));
811 sstrncpy(v1.type, prop->type, sizeof(v1.type));
812 plugin_dispatch_values(&v1);
813 /* Clear values assigned in case of leakage */
814 v1.values = NULL;
815 v1.values_len = 0;
816 }
817 }
818
redfish_process_payload(bool success,unsigned short http_code,redfishPayload * payload,void * context)819 static void redfish_process_payload(bool success, unsigned short http_code,
820 redfishPayload *payload, void *context) {
821 redfish_job_t *job = (redfish_job_t *)context;
822
823 if (!success) {
824 WARNING(PLUGIN_NAME ": Query has failed, HTTP code = %u\n", http_code);
825 goto free_job;
826 }
827
828 redfish_service_t *serv = job->service_query->service;
829
830 if (!payload) {
831 WARNING(PLUGIN_NAME ": Failed to get payload for service name \"%s\"",
832 serv->name);
833 goto free_job;
834 }
835
836 for (llentry_t *llres = llist_head(job->service_query->query->resources);
837 llres != NULL; llres = llres->next) {
838 redfish_resource_t *res = (redfish_resource_t *)llres->value;
839 json_t *json_array = json_object_get(payload->json, res->name);
840
841 if (json_array == NULL) {
842 WARNING(PLUGIN_NAME ": Could not find resource \"%s\"", res->name);
843 continue;
844 }
845
846 for (llentry_t *llprop = llist_head(res->properties); llprop != NULL;
847 llprop = llprop->next) {
848 redfish_property_t *prop = (redfish_property_t *)llprop->value;
849
850 redfish_process_payload_property(prop, json_array, res, serv);
851 }
852 }
853
854 free_job:
855 cleanupPayload(payload);
856 redfish_job_destroy(job);
857 }
858
redfish_worker_thread(void * args)859 static void *redfish_worker_thread(void __attribute__((unused)) * args) {
860 INFO(PLUGIN_NAME ": Worker is running");
861
862 for (;;) {
863 usleep(10);
864
865 if (DEQ_IS_EMPTY(ctx.jobs))
866 continue;
867
868 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
869
870 redfish_job_t *job = DEQ_HEAD(ctx.jobs);
871 getPayloadByPathAsync(job->service_query->service->redfish,
872 job->service_query->query->endpoint, NULL,
873 redfish_process_payload, job);
874 DEQ_REMOVE_HEAD(ctx.jobs);
875
876 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
877 }
878
879 pthread_exit(NULL);
880 return NULL;
881 }
882
redfish_read(user_data_t * ud)883 static int redfish_read(__attribute__((unused)) user_data_t *ud) {
884 for (llentry_t *le = llist_head(ctx.services); le != NULL; le = le->next) {
885 redfish_service_t *service = (redfish_service_t *)le->value;
886
887 for (llentry_t *le = llist_head(service->query_ptrs); le != NULL;
888 le = le->next) {
889 redfish_query_t *query = (redfish_query_t *)le->value;
890 redfish_job_t *job = calloc(1, sizeof(*job));
891
892 if (job == NULL) {
893 WARNING(PLUGIN_NAME ": Failed to allocate memory for task");
894 continue;
895 }
896
897 DEQ_ITEM_INIT(job);
898
899 redfish_payload_ctx_t *serv_res = calloc(1, sizeof(*serv_res));
900
901 if (serv_res == NULL) {
902 WARNING(PLUGIN_NAME ": Failed to allocate memory for task's context");
903 sfree(job);
904 continue;
905 }
906
907 serv_res->query = query;
908 serv_res->service = service;
909 job->service_query = serv_res;
910
911 DEQ_INSERT_TAIL(ctx.jobs, job);
912 }
913 }
914 return 0;
915 }
916
redfish_cleanup(void)917 static int redfish_cleanup(void) {
918 INFO(PLUGIN_NAME ": Cleaning up");
919 /* Shutting down a worker thread */
920 if (pthread_cancel(ctx.worker_thread) != 0)
921 ERROR(PLUGIN_NAME ": Failed to cancel the worker thread");
922
923 if (pthread_join(ctx.worker_thread, NULL) != 0)
924 ERROR(PLUGIN_NAME ": Failed to join the worker thread");
925
926 /* Cleaning worker's queue */
927 while (!DEQ_IS_EMPTY(ctx.jobs)) {
928 redfish_job_t *job = DEQ_HEAD(ctx.jobs);
929 DEQ_REMOVE_HEAD(ctx.jobs);
930 redfish_job_destroy(job);
931 }
932
933 for (llentry_t *le = llist_head(ctx.services); le; le = le->next) {
934 redfish_service_t *service = (redfish_service_t *)le->value;
935
936 redfish_service_destroy(service);
937 }
938 llist_destroy(ctx.services);
939
940 c_avl_iterator_t *i = c_avl_get_iterator(ctx.queries);
941
942 char *key;
943 redfish_query_t *query;
944
945 while (c_avl_iterator_next(i, (void *)&key, (void *)&query) == 0) {
946 for (llentry_t *le = llist_head(query->resources); le != NULL;
947 le = le->next) {
948 redfish_resource_t *resource = (redfish_resource_t *)le->value;
949 for (llentry_t *le = llist_head(resource->properties); le != NULL;
950 le = le->next) {
951 redfish_property_t *property = (redfish_property_t *)le->value;
952 sfree(property->name);
953 sfree(property->plugin_inst);
954 sfree(property->type);
955 sfree(property->type_inst);
956 sfree(property);
957 }
958 sfree(resource->name);
959 llist_destroy(resource->properties);
960 sfree(resource);
961 }
962 sfree(query->name);
963 sfree(query->endpoint);
964 llist_destroy(query->resources);
965 sfree(query);
966 }
967
968 c_avl_iterator_destroy(i);
969 c_avl_destroy(ctx.queries);
970
971 return 0;
972 }
973
module_register(void)974 void module_register(void) {
975 plugin_register_init(PLUGIN_NAME, redfish_init);
976 plugin_register_complex_config(PLUGIN_NAME, redfish_config);
977 plugin_register_complex_read(NULL, PLUGIN_NAME, redfish_read, 0, NULL);
978 plugin_register_shutdown(PLUGIN_NAME, redfish_cleanup);
979 }
980