]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
New redfish plugin
authorMozejko, MarcinX <marcinx.mozejko@intel.com>
Mon, 16 Jul 2018 12:33:41 +0000 (13:33 +0100)
committerMozejko, MarcinX <marcinx.mozejko@intel.com>
Thu, 13 Sep 2018 12:35:32 +0000 (13:35 +0100)
- Collect data from Redfish interface
- Use libredfish C library

Change-Id: I95e0ee23ccb11f617d41c9edb0b57c651e5cbdb5
Signed-off-by: Mozejko, MarcinX <marcinx.mozejko@intel.com>
Makefile.am
configure.ac
src/collectd.conf.in
src/collectd.conf.pod
src/redfish.c [new file with mode: 0644]
src/redfish_test.c [new file with mode: 0644]

index 48a7cb3d05d351949b5a09e015c23c43bc60bfcb..7867770b8b69cf3100f91eb6991a78b8af5808f3 100644 (file)
@@ -1547,6 +1547,26 @@ protocols_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 protocols_la_LIBADD = libignorelist.la
 endif
 
+if BUILD_PLUGIN_REDFISH
+pkglib_LTLIBRARIES += redfish.la
+redfish_la_SOURCES = src/redfish.c
+redfish_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBREDFISH_CPPFLAGS)
+redfish_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBREDFISH_LDFLAGS)
+redfish_la_LIBADD = $(BUILD_WITH_LIBREDFISH_LIBS) -lredfish
+
+test_plugin_redfish_SOURCES = src/redfish_test.c \
+                              src/daemon/utils_avltree.c \
+                              src/daemon/utils_llist.c \
+                              src/daemon/configfile.c \
+                              src/daemon/types_list.c
+test_plugin_redfish_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBREDFISH_CPPFLAGS)
+test_plugin_redfish_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBREDFISH_LDFLAGS)
+test_plugin_redfish_LDADD = liboconfig.la libplugin_mock.la \
+                            $(BUILD_WITH_LIBREDFISH_LIBS) -lredfish -ljansson
+check_PROGRAMS += test_plugin_redfish
+TESTS += test_plugin_redfish
+endif
+
 if BUILD_PLUGIN_REDIS
 pkglib_LTLIBRARIES += redis.la
 redis_la_SOURCES = src/redis.c
index 95caefff584c728fdbc7268988c9203e0c1e2d8a..bcfa6d3fb96b1637fd5a01ebee03263014363ea0 100644 (file)
@@ -2181,6 +2181,61 @@ AC_SUBST([BUILD_WITH_LIBHIREDIS_CPPFLAGS])
 AC_SUBST([BUILD_WITH_LIBHIREDIS_LDFLAGS])
 # }}}
 
+# --with-libredfish {{{
+AC_ARG_WITH([libredfish],
+  [AS_HELP_STRING([--with-libredfish@<:@=PREFIX@:>@], [Path to libredfish.])],
+  [
+    if test "x$withval" = "xyes"; then
+      with_libredfish="yes"
+    else if test "x$withval" = "xno"; then
+      with_libredfish="no"
+    else
+      with_libredfish="yes"
+      LIBREDFISH_CPPFLAGS="$LIBREDFISH_CPPFLAGS -I$withval/include"
+      LIBREDFISH_LDFLAGS="$LIBREDFISh_LDFLAGS -L$withval/lib"
+    fi; fi
+  ],
+  [with_libredfish="yes"]
+)
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $LIBREDFISH_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBHIREDIS_LDFLAGS"
+
+if test "x$with_libredfish" = "xyes"; then
+  if test "x$LIBREDFISH_CPPFLAGS" != "x"; then
+    AC_MSG_NOTICE([libredfish CPPFLAGS: $LIBHIREDFISH_CPPFLAGS])
+  fi
+  AC_CHECK_HEADERS([redfish.h],
+    [with_libredfish="yes"],
+    [with_libredfish="no (redfish.h not found)"]
+  )
+fi
+
+if test "x$with_libredfish" = "xyes"; then
+  if test "x$LIBREDFISH_LDFLAGS" != "x"; then
+    AC_MSG_NOTICE([libredfish LDFLAGS: $LIBREDFISH_LDFLAGS])
+  fi
+  AC_CHECK_LIB([redfish], [createServiceEnumerator],
+    [with_libredfish="yes"],
+    [with_libredfish="no (symbol 'createServiceEnumerator' not found)"]
+  )
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libredfish" = "xyes"; then
+  BUILD_WITH_LIBREDFISH_CPPFLAGS="$LIBREDFISH_CPPFLAGS"
+  BUILD_WITH_LIBREDFISH_LDFLAGS="$LIBREDFISH_LDFLAGS"
+fi
+
+AC_SUBST([BUILD_WITH_LIBREDFISH_CPPFLAGS])
+AC_SUBST([BUILD_WITH_LIBREDFISH_LDFLAGS])
+
+# }}}
+
 # --with-libcurl {{{
 with_curl_config="curl-config"
 with_curl_cflags=""
@@ -6799,6 +6854,7 @@ AC_PLUGIN([powerdns],            [yes],                     [PowerDNS statistics
 AC_PLUGIN([processes],           [$plugin_processes],       [Process statistics])
 AC_PLUGIN([protocols],           [$plugin_protocols],       [Protocol (IP, TCP, ...) statistics])
 AC_PLUGIN([python],              [$plugin_python],          [Embed a Python interpreter])
+AC_PLUGIN([redfish],             [$with_libredfish],        [Redfish plugin])
 AC_PLUGIN([redis],               [$with_libhiredis],        [Redis plugin])
 AC_PLUGIN([routeros],            [$with_librouteros],       [RouterOS plugin])
 AC_PLUGIN([rrdcached],           [$librrd_rrdc_update],     [RRDTool output plugin])
@@ -7220,6 +7276,7 @@ AC_MSG_RESULT([    powerdns  . . . . . . $enable_powerdns])
 AC_MSG_RESULT([    processes . . . . . . $enable_processes])
 AC_MSG_RESULT([    protocols . . . . . . $enable_protocols])
 AC_MSG_RESULT([    python  . . . . . . . $enable_python])
+AC_MSG_RESULT([    redfish . . . . . . . $enable_redfish])
 AC_MSG_RESULT([    redis . . . . . . . . $enable_redis])
 AC_MSG_RESULT([    routeros  . . . . . . $enable_routeros])
 AC_MSG_RESULT([    rrdcached . . . . . . $enable_rrdcached])
index af65214511ad303bcc450943001acb78660bf5f6..6d8c8838c8185abee89b1efec0f382de462cc4aa 100644 (file)
 #@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
 #@BUILD_PLUGIN_PROTOCOLS_TRUE@LoadPlugin protocols
 #@BUILD_PLUGIN_PYTHON_TRUE@LoadPlugin python
+#@BUILD_PLUGIN_REDFISH_TRUE@LoadPlugin redfish
 #@BUILD_PLUGIN_REDIS_TRUE@LoadPlugin redis
 #@BUILD_PLUGIN_ROUTEROS_TRUE@LoadPlugin routeros
 #@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached
 #   </Node>
 #</Plugin>
 
+#<Plugin redfish>
+#  <Query "fans">
+#    Endpoint "/redfish/v1/Chassis/Chassis-1/Thermal"
+#    <Resource "Fans">
+#      <Property "ReadingRPM">
+#        PluginInstance "chassis-1"
+#        Type "rpm"
+#      </Property>
+#    </Resource>
+#  </Query>
+#  <Query "temperatures">
+#    Endpoint "/redfish/v1/Chassis/Chassis-1/Thermal"
+#    <Resource "Temperatures">
+#      <Property "ReadingCelsius">
+#        PluginInstance "chassis-1"
+#        Type "degrees"
+#      </Property>
+#    </Resource>
+#  </Query>
+#  <Query "voltages">
+#    Endpoint "/redfish/v1/Chassis/Chassis-1/Power"
+#    <Resource "Voltages">
+#      <Property "ReadingVolts">
+#        PluginInstance "chassis-1"
+#        Type "volts"
+#      </Property>
+#    </Resource>
+#  </Query>
+#  <Service "local">
+#    Host "127.0.0.1:5000"
+#    User "user"
+#    Passwd "passwd"
+#    Queries "fans" "voltages" "temperatures"
+#  </Service>
+#</Plugin>
+#
+
 #<Plugin routeros>
 #      <Router>
 #              Host "router.example.com"
index 6e6d6eaf9845cf4d1c17f57f4017b6c2d7cd86d7..5e13dec93d28b8cd692823cb4dae0d90fe521dd2 100644 (file)
@@ -7254,6 +7254,97 @@ matching values will be ignored.
 This plugin embeds a Python-interpreter into collectd and provides an interface
 to collectd's plugin system. See L<collectd-python(5)> for its documentation.
 
+=head2 Plugin C<redfish>
+
+The C<redfish> plugin collects sensor data using REST protocol called
+Redfish.
+
+B<Sample configuration:>
+
+  <Plugin redfish>
+    <Query "fans">
+      Endpoint "/redfish/v1/Chassis/Chassis-1/Thermal"
+      <Resource "Fans">
+        <Property "ReadingRPM">
+          PluginInstance "chassis-1"
+          Type "rpm"
+        </Property>
+      </Resource>
+    </Query>
+    <Query "temperatures">
+      Endpoint "/redfish/v1/Chassis/Chassis-1/Thermal"
+      <Resource "Temperatures">
+        <Property "ReadingCelsius">
+          PluginInstance "chassis-1"
+          Type "degrees"
+        </Property>
+      </Resource>
+    </Query>
+    <Query "voltages">
+      Endpoint "/redfish/v1/Chassis/Chassis-1/Power"
+      <Resource "Voltages">
+        <Property "ReadingVolts">
+          PluginInstance "chassis-1"
+          Type "volts"
+        </Property>
+      </Resource>
+    </Query>
+    <Service "local">
+      Host "127.0.0.1:5000"
+      User "user"
+      Passwd "passwd"
+      Queries "fans" "voltages" "temperatures"
+    </Service>
+  </Plugin>
+
+=over 4
+
+=item B<Query>
+
+Section defining a query performed on Redfish interface
+
+=item B<Endpoint>
+
+URI of the REST API Endpoint for accessing the BMC
+
+=item B<Resource>
+
+Selects single resource or array to collect data.
+
+=item B<Property>
+
+Selects property from which data is gathered
+
+=item B<PluginInstance>
+
+Plugin instance of dispatched collectd metric
+
+=item B<Type>
+
+Type of dispatched collectd metric
+
+=item B<TypeInstance>
+
+Type instance of collectd metric
+
+=item B<Service>
+
+Section defining service to be sent requests
+
+=item B<Username>
+
+BMC username
+
+=item B<Password>
+
+BMC password
+
+=item B<Queries>
+
+Queries to run
+
+=back
+
 =head2 Plugin C<routeros>
 
 The C<routeros> plugin connects to a device running I<RouterOS>, the
diff --git a/src/redfish.c b/src/redfish.c
new file mode 100644 (file)
index 0000000..e815e2b
--- /dev/null
@@ -0,0 +1,891 @@
+/**
+ * collectd - src/redfish.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Marcin Mozejko <marcinx.mozejko@intel.com>
+ *   Martin Kennelly <martin.kennelly@intel.com>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "utils_avltree.h"
+#include "utils_llist.h"
+
+#include <redfish.h>
+#define PLUGIN_NAME "redfish"
+#define MAX_STR_LEN 128
+
+struct redfish_property_s {
+  char *name;
+  char *plugin_inst;
+  char *type;
+  char *type_inst;
+};
+typedef struct redfish_property_s redfish_property_t;
+
+struct redfish_resource_s {
+  char *name;
+  llist_t *properties;
+};
+typedef struct redfish_resource_s redfish_resource_t;
+
+struct redfish_query_s {
+  char *name;
+  char *endpoint;
+  llist_t *resources;
+};
+typedef struct redfish_query_s redfish_query_t;
+
+struct redfish_service_s {
+  char *name;
+  char *host;
+  char *user;
+  char *passwd;
+  char *token;
+  unsigned int flags;
+  char **queries;      /* List of queries */
+  llist_t *query_ptrs; /* Pointers to query structs */
+  size_t queries_num;
+  enumeratorAuthentication auth;
+  redfishService *redfish;
+};
+typedef struct redfish_service_s redfish_service_t;
+
+struct redfish_ctx_s {
+  llist_t *services;
+  c_avl_tree_t *queries;
+};
+typedef struct redfish_ctx_s redfish_ctx_t;
+
+struct redfish_payload_ctx_s {
+  redfish_service_t *service;
+  llist_t *resources;
+};
+typedef struct redfish_payload_ctx_s redfish_payload_ctx_t;
+
+enum redfish_value_type_e { VAL_TYPE_STR = 0, VAL_TYPE_INT, VAL_TYPE_REAL };
+typedef enum redfish_value_type_e redfish_value_type_t;
+
+union redfish_value_u {
+  double real;
+  int integer;
+  char *string;
+};
+typedef union redfish_value_u redfish_value_t;
+
+redfish_ctx_t *ctx;
+
+static int redfish_cleanup(void);
+static int redfish_validate_config(void);
+
+#if COLLECT_DEBUG
+static void redfish_print_config(void) {
+  DEBUG(PLUGIN_NAME ": ====================CONFIGURATION====================");
+  DEBUG(PLUGIN_NAME ": SERVICES: %d", llist_size(ctx->services));
+  for (llentry_t *le = llist_head(ctx->services); le != NULL; le = le->next) {
+    redfish_service_t *s = (redfish_service_t *)le->value;
+    char queries_str[MAX_STR_LEN];
+
+    strjoin(queries_str, MAX_STR_LEN, s->queries, s->queries_num, ", ");
+
+    DEBUG(PLUGIN_NAME ": --------------------");
+    DEBUG(PLUGIN_NAME ": Service: %s", s->name);
+    DEBUG(PLUGIN_NAME ":   Host: %s", s->host);
+
+    if (s->user && s->passwd) {
+      DEBUG(PLUGIN_NAME ":   User: %s", s->user);
+      DEBUG(PLUGIN_NAME ":   Passwd: %s", s->passwd);
+    } else if (s->token)
+      DEBUG(PLUGIN_NAME ":   Token: %s", s->token);
+
+    DEBUG(PLUGIN_NAME ":   Queries[%" PRIsz "]: (%s)", s->queries_num,
+          queries_str);
+  }
+
+  DEBUG(PLUGIN_NAME ": =====================================================");
+
+  c_avl_iterator_t *i = c_avl_get_iterator(ctx->queries);
+  char *key;
+  redfish_query_t *q;
+
+  DEBUG(PLUGIN_NAME ": QUERIES: %d", c_avl_size(ctx->queries));
+
+  while (c_avl_iterator_next(i, (void **)&key, (void **)&q) == 0) {
+    DEBUG(PLUGIN_NAME ": --------------------");
+    DEBUG(PLUGIN_NAME ": Query: %s", q->name);
+    DEBUG(PLUGIN_NAME ":   Endpoint: %s", q->endpoint);
+    for (llentry_t *le = llist_head(q->resources); le != NULL; le = le->next) {
+      redfish_resource_t *r = (redfish_resource_t *)le->value;
+      DEBUG(PLUGIN_NAME ":   Resource: %s", r->name);
+      for (llentry_t *le = llist_head(r->properties); le != NULL;
+           le = le->next) {
+        redfish_property_t *p = (redfish_property_t *)le->value;
+        DEBUG(PLUGIN_NAME ":     Property: %s", p->name);
+        DEBUG(PLUGIN_NAME ":       PluginInstance: %s", p->plugin_inst);
+        DEBUG(PLUGIN_NAME ":       Type: %s", p->type);
+        DEBUG(PLUGIN_NAME ":       TypeInstance: %s", p->type_inst);
+      }
+    }
+  }
+
+  c_avl_iterator_destroy(i);
+  DEBUG(PLUGIN_NAME ": =====================================================");
+}
+#endif
+
+static int redfish_init(void) {
+#if COLLECT_DEBUG
+  redfish_print_config();
+#endif
+  int ret = redfish_validate_config();
+
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": Validation of configuration file failed");
+    return ret;
+  }
+
+  for (llentry_t *le = llist_head(ctx->services); le != NULL; le = le->next) {
+    redfish_service_t *service = (redfish_service_t *)le->value;
+    /* Ignore redfish version */
+    service->flags |= 0x00000001;
+
+    /* Preparing struct for authentication */
+    if (service->user && service->passwd) {
+      service->auth.authCodes.userPass.username = service->user;
+      service->auth.authCodes.userPass.password = service->passwd;
+      service->redfish = createServiceEnumerator(
+          service->host, NULL, &service->auth, service->flags);
+    } else if (service->token) {
+      service->auth.authCodes.authToken.token = service->token;
+      service->auth.authType = REDFISH_AUTH_BEARER_TOKEN;
+      service->redfish = createServiceEnumerator(
+          service->host, NULL, &service->auth, service->flags);
+    } else {
+      service->redfish =
+          createServiceEnumerator(service->host, NULL, NULL, service->flags);
+    }
+
+    service->query_ptrs = llist_create();
+    if (service->query_ptrs == NULL)
+      goto error;
+
+    /* Preparing query pointers list for every service */
+    for (size_t i = 0; i < service->queries_num; i++) {
+      redfish_query_t *ptr;
+      if (c_avl_get(ctx->queries, service->queries[i], (void **)&ptr) != 0)
+        goto error;
+
+      llentry_t *entry = llentry_create(ptr->name, ptr);
+      if (entry != NULL)
+        llist_append(service->query_ptrs, entry);
+      else
+        goto error;
+    }
+  }
+
+  return 0;
+
+error:
+  ERROR(PLUGIN_NAME ": Failed to allocate memory for service queries list");
+  /* Freeing libredfish resources & llists */
+  for (llentry_t *le = llist_head(ctx->services); le != NULL; le = le->next) {
+    redfish_service_t *service = (redfish_service_t *)le->value;
+    cleanupServiceEnumerator(service->redfish);
+    llist_destroy(service->query_ptrs);
+  }
+  return -ENOMEM;
+}
+
+static int redfish_preconfig(void) {
+  /* Allocating plugin context */
+  ctx = calloc(1, sizeof(*ctx));
+  if (ctx == NULL)
+    goto error;
+
+  /* Creating placeholder for services */
+  ctx->services = llist_create();
+  if (ctx->services == NULL)
+    goto free_ctx;
+
+  /* Creating placeholder for queries */
+  ctx->queries = c_avl_create((int (*)(const void *, const void *))strcmp);
+  if (ctx->services == NULL)
+    goto free_services;
+
+  return 0;
+
+free_services:
+  llist_destroy(ctx->services);
+free_ctx:
+  sfree(ctx);
+error:
+  ERROR(PLUGIN_NAME ": Failed to allocate memory for plugin context");
+  return -ENOMEM;
+}
+
+static int redfish_config_property(redfish_resource_t *resource,
+                                   oconfig_item_t *cfg_item) {
+  assert(resource != NULL);
+  assert(cfg_item != NULL);
+
+  redfish_property_t *property = calloc(1, sizeof(*property));
+  int ret = 0;
+
+  if (property == NULL) {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for property");
+    return -ENOMEM;
+  }
+
+  ret = cf_util_get_string(cfg_item, &property->name);
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": Could not get property argument in resource section "
+                      "named \"%s\"",
+          resource->name);
+    ret = -EINVAL;
+    goto free_property;
+  }
+
+  for (int i = 0; i < cfg_item->children_num; i++) {
+    oconfig_item_t *opt = cfg_item->children + i;
+    if (strcasecmp("PluginInstance", opt->key) == 0)
+      ret = cf_util_get_string(opt, &property->plugin_inst);
+    else if (strcasecmp("Type", opt->key) == 0)
+      ret = cf_util_get_string(opt, &property->type);
+    else if (strcasecmp("TypeInstance", opt->key) == 0)
+      ret = cf_util_get_string(opt, &property->type_inst);
+    else {
+      ERROR(PLUGIN_NAME ": Invalid option \"%s\" in property \"%s\" "
+                        "in resource \"%s\"",
+            opt->key, property->name, resource->name);
+      ret = -EINVAL;
+      goto free_all;
+    }
+
+    if (ret != 0) {
+      ERROR(PLUGIN_NAME ": Something went wrong going through attributes in "
+                        "property named \"%s\" in resource named \"%s\"",
+            property->name, resource->name);
+      goto free_all;
+    }
+  }
+
+  llentry_t *entry = llentry_create(property->name, property);
+  if (entry != NULL)
+    llist_append(resource->properties, entry);
+  else {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for property");
+    ret = -ENOMEM;
+    goto free_all;
+  }
+
+  return 0;
+
+free_all:
+  sfree(property->name);
+  sfree(property->plugin_inst);
+  sfree(property->type);
+  sfree(property->type_inst);
+free_property:
+  sfree(property);
+  return ret;
+}
+
+static int redfish_config_resource(redfish_query_t *query,
+                                   oconfig_item_t *cfg_item) {
+  assert(query != NULL);
+  assert(cfg_item != NULL);
+
+  redfish_resource_t *resource = calloc(1, sizeof(*resource));
+
+  if (resource == NULL)
+    goto error;
+
+  resource->properties = llist_create();
+
+  if (resource->properties == NULL)
+    goto free_resource;
+
+  int ret = cf_util_get_string(cfg_item, &resource->name);
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": Could not get resource name for query named \"%s\"",
+          query->name);
+    goto free_list;
+  }
+  for (int i = 0; i < cfg_item->children_num; i++) {
+    oconfig_item_t *opt = cfg_item->children + i;
+    if (strcasecmp("Property", opt->key) == 0)
+      ret = redfish_config_property(resource, opt);
+    else {
+      ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
+    }
+
+    if (ret != 0) {
+      goto free_name;
+    }
+  }
+
+  llentry_t *entry = llentry_create(resource->name, resource);
+  if (entry != NULL)
+    llist_append(query->resources, entry);
+  else
+    goto free_name;
+
+  return 0;
+
+free_name:
+  sfree(resource->name);
+free_list:
+  llist_destroy(resource->properties);
+free_resource:
+  sfree(resource);
+  return -1;
+
+error:
+  ERROR(PLUGIN_NAME ": Failed to allocate memory for resource");
+  return -ENOMEM;
+}
+
+static int redfish_config_query(oconfig_item_t *cfg_item,
+                                c_avl_tree_t *queries) {
+  redfish_query_t *query = calloc(1, sizeof(*query));
+
+  if (query == NULL) {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for query");
+    return -ENOMEM;
+  }
+
+  int ret = 0;
+
+  query->resources = llist_create();
+
+  if (query->resources == NULL) {
+    ret = -ENOMEM;
+    goto free_query;
+  }
+
+  ret = cf_util_get_string(cfg_item, &query->name);
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": Unable to get query name. Query ignored");
+    ret = -EINVAL;
+    goto free_list;
+  }
+
+  for (int i = 0; i < cfg_item->children_num; i++) {
+    oconfig_item_t *opt = cfg_item->children + i;
+
+    if (strcasecmp("Endpoint", opt->key) == 0)
+      ret = cf_util_get_string(opt, &query->endpoint);
+    else if (strcasecmp("Resource", opt->key) == 0)
+      ret = redfish_config_resource(query, opt);
+    else {
+      ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
+      ret = -EINVAL;
+      goto free_all;
+    }
+
+    if (ret != 0) {
+      ERROR(PLUGIN_NAME ": Something went wrong processing query \"%s\"",
+            query->name);
+      ret = -EINVAL;
+      goto free_all;
+    }
+  }
+
+  ret = c_avl_insert(queries, query->name, query);
+
+  if (ret != 0)
+    goto free_all;
+
+  return 0;
+
+free_all:
+  sfree(query->name);
+  sfree(query->endpoint);
+free_list:
+  llist_destroy(query->resources);
+free_query:
+  sfree(query);
+  return ret;
+}
+
+static int redfish_read_queries(oconfig_item_t *cfg_item, char ***queries_ptr) {
+  size_t q_num = cfg_item->values_num;
+
+  *queries_ptr = calloc(1, sizeof(**queries_ptr) * q_num);
+  if (*queries_ptr == NULL) {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for queries list");
+    goto error;
+  }
+
+  char **queries = *queries_ptr;
+  size_t i;
+  for (i = 0; i < q_num; i++) {
+    if (cfg_item->values[i].type != OCONFIG_TYPE_STRING) {
+      ERROR(PLUGIN_NAME ": 'Queries' requires string arguments");
+      goto free_array;
+    }
+    queries[i] = strdup(cfg_item->values[i].value.string);
+
+    if (queries[i] == NULL)
+      goto free_array;
+  }
+
+  return 0;
+
+free_array:
+  for (int j = 0; j <= i; j++)
+    sfree(queries[j]);
+  sfree(queries);
+error:
+  sfree(queries_ptr);
+  return -1;
+}
+
+static int redfish_config_service(oconfig_item_t *cfg_item) {
+  redfish_service_t *service = calloc(1, sizeof(*service));
+
+  if (service == NULL)
+    goto error;
+
+  int ret = cf_util_get_string(cfg_item, &service->name);
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": A service was defined without an argument");
+    goto free_service;
+  }
+
+  for (int i = 0; i < cfg_item->children_num; i++) {
+    oconfig_item_t *opt = cfg_item->children + i;
+
+    if (strcasecmp("Host", opt->key) == 0)
+      ret = cf_util_get_string(opt, &service->host);
+    else if (strcasecmp("User", opt->key) == 0)
+      ret = cf_util_get_string(opt, &service->user);
+    else if (strcasecmp("Passwd", opt->key) == 0)
+      ret = cf_util_get_string(opt, &service->passwd);
+    else if (strcasecmp("Token", opt->key) == 0)
+      ret = cf_util_get_string(opt, &service->token);
+    else if (strcasecmp("Queries", opt->key) == 0) {
+      ret = redfish_read_queries(opt, &service->queries);
+      service->queries_num = opt->values_num;
+    } else {
+      ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
+    }
+
+    if (ret != 0) {
+      ERROR(PLUGIN_NAME ": Something went wrong processing the service named \
+            \"%s\"",
+            service->name);
+      goto free_string_query;
+    }
+  }
+
+  llentry_t *entry = llentry_create(service->name, service);
+  if (entry != NULL)
+    llist_append(ctx->services, entry);
+  else {
+    ERROR(PLUGIN_NAME ": Failed to create list for service name \"%s\"",
+          service->name);
+    goto free_string_query;
+  }
+
+  return 0;
+
+free_string_query:
+  sfree(service->name);
+  sfree(service->host);
+  sfree(service->user);
+  sfree(service->passwd);
+  sfree(service->token);
+  for (int j = 0; j < service->queries_num; j++)
+    sfree(service->queries[j]);
+
+free_service:
+  sfree(service);
+  return -1;
+
+error:
+  ERROR(PLUGIN_NAME ": Failed to allocate memory for service");
+  return -ENOMEM;
+}
+
+static int redfish_config(oconfig_item_t *cfg_item) {
+  int ret = redfish_preconfig();
+
+  if (ret != 0)
+    return ret;
+
+  for (int i = 0; i < cfg_item->children_num; i++) {
+    oconfig_item_t *child = cfg_item->children + i;
+
+    if (strcasecmp("Query", child->key) == 0)
+      ret = redfish_config_query(child, ctx->queries);
+    else if (strcasecmp("Service", child->key) == 0)
+      ret = redfish_config_service(child);
+    else {
+      ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", child->key);
+    }
+
+    if (ret != 0) {
+      redfish_cleanup();
+      return ret;
+    }
+  }
+
+  return 0;
+}
+
+static int redfish_validate_config(void) {
+  /* Service validation */
+  for (llentry_t *llserv = llist_head(ctx->services); llserv != NULL;
+       llserv = llserv->next) {
+    redfish_service_t *service = llserv->value;
+    if (service->name == NULL) {
+      ERROR(PLUGIN_NAME ": A service has no name");
+      return -EINVAL;
+    }
+    if (service->host == NULL) {
+      ERROR(PLUGIN_NAME ": Service \"%s\" has no host attribute",
+            service->name);
+      return -EINVAL;
+    }
+    if ((service->user == NULL) ^ (service->passwd == NULL)) {
+      ERROR(PLUGIN_NAME ": Service \"%s\" does not have user and/or password "
+                        "defined",
+            service->name);
+      return -EINVAL;
+    }
+    if (service->user == NULL && service->token == NULL) {
+      ERROR(PLUGIN_NAME ": Service \"%s\" does not have an user/pass or "
+                        "token defined",
+            service->name);
+      return -EINVAL;
+    }
+    if (service->queries_num == 0)
+      WARNING(PLUGIN_NAME ": Service \"%s\" does not have queries",
+              service->name);
+
+    for (int i = 0; i < service->queries_num; i++) {
+      redfish_query_t *query_query;
+      bool found = false;
+      char *key;
+      c_avl_iterator_t *query_iter = c_avl_get_iterator(ctx->queries);
+      while (c_avl_iterator_next(query_iter, (void **)&key,
+                                 (void **)&query_query) == 0 &&
+             !found) {
+        if (query_query->name != NULL && service->queries[i] != NULL &&
+            strcmp(query_query->name, service->queries[i]) == 0) {
+          found = true;
+        }
+      }
+
+      if (!found) {
+        ERROR(PLUGIN_NAME ": Query named \"%s\" in service \"%s\" not found",
+              service->queries[i], service->name);
+        c_avl_iterator_destroy(query_iter);
+        return -EINVAL;
+      }
+
+      c_avl_iterator_destroy(query_iter);
+    }
+  }
+
+  c_avl_iterator_t *queries_iter = c_avl_get_iterator(ctx->queries);
+  char *key;
+  redfish_query_t *query;
+
+  /* Query validation */
+  while (c_avl_iterator_next(queries_iter, (void **)&key, (void **)&query) ==
+         0) {
+    if (query->name == NULL) {
+      ERROR(PLUGIN_NAME ": A query does not have a name");
+      goto error;
+    }
+    if (query->endpoint == NULL) {
+      ERROR(PLUGIN_NAME ": Query \"%s\" does not have a valid endpoint",
+            query->name);
+      goto error;
+    }
+    for (llentry_t *llres = llist_head(query->resources); llres != NULL;
+         llres = llres->next) {
+      redfish_resource_t *resource = (redfish_resource_t *)llres->value;
+      /* Resource validation */
+      if (resource->name == NULL) {
+        WARNING(PLUGIN_NAME ": A resource in query \"%s\" is not named",
+                query->name);
+      }
+      /* Property validation */
+      for (llentry_t *llprop = llist_head(resource->properties); llprop != NULL;
+           llprop = llprop->next) {
+        redfish_property_t *prop = (redfish_property_t *)llprop->value;
+        if (prop->name == NULL) {
+          ERROR(PLUGIN_NAME ": A property has no name in query \"%s\"",
+                query->name);
+          goto error;
+        }
+        if (prop->plugin_inst == NULL) {
+          ERROR(PLUGIN_NAME ": A plugin instance is not defined in property "
+                            "\"%s\" in query \"%s\"",
+                prop->name, query->name);
+          goto error;
+        }
+        if (prop->type == NULL) {
+          ERROR(PLUGIN_NAME ": Type is not defined in property \"%s\" in "
+                            "query \"%s\"",
+                prop->name, query->name);
+          goto error;
+        }
+      }
+    }
+  }
+
+  c_avl_iterator_destroy(queries_iter);
+
+  return 0;
+
+error:
+  c_avl_iterator_destroy(queries_iter);
+  return -EINVAL;
+}
+
+static int redfish_convert_val(redfish_value_t *value,
+                               redfish_value_type_t src_type, value_t *vl,
+                               int dst_type) {
+  switch (dst_type) {
+  case DS_TYPE_GAUGE:
+    if (src_type == VAL_TYPE_STR)
+      vl->gauge = strtod(value->string, NULL);
+    else if (src_type == VAL_TYPE_INT)
+      vl->gauge = (gauge_t)value->integer;
+    else if (src_type == VAL_TYPE_REAL)
+      vl->gauge = value->real;
+    break;
+  case DS_TYPE_DERIVE:
+    if (src_type == VAL_TYPE_STR)
+      vl->derive = strtoll(value->string, NULL, 0);
+    else if (src_type == VAL_TYPE_INT)
+      vl->derive = (derive_t)value->integer;
+    else if (src_type == VAL_TYPE_REAL)
+      vl->derive = (derive_t)value->real;
+    break;
+  case DS_TYPE_COUNTER:
+    if (src_type == VAL_TYPE_STR)
+      vl->derive = strtoull(value->string, NULL, 0);
+    else if (src_type == VAL_TYPE_INT)
+      vl->derive = (derive_t)value->integer;
+    else if (src_type == VAL_TYPE_REAL)
+      vl->derive = (derive_t)value->real;
+    break;
+  case DS_TYPE_ABSOLUTE:
+    if (src_type == VAL_TYPE_STR)
+      vl->absolute = strtoull(value->string, NULL, 0);
+    else if (src_type == VAL_TYPE_INT)
+      vl->absolute = (absolute_t)value->integer;
+    else if (src_type == VAL_TYPE_REAL)
+      vl->absolute = (absolute_t)value->real;
+    break;
+  default:
+    ERROR(PLUGIN_NAME ": Invalid data set type. Cannot convert value");
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+static void redfish_process_payload(bool success, unsigned short http_code,
+                                    redfishPayload *payload, void *context) {
+  if (success == false) {
+    WARNING(PLUGIN_NAME ": Query has failed, HTTP code = %u\n", http_code);
+    return;
+  }
+  redfish_payload_ctx_t *res_serv = (redfish_payload_ctx_t *)context;
+  redfish_service_t *serv = res_serv->service;
+  if (payload) {
+    json_t *json_array;
+    for (llentry_t *llres = llist_head(res_serv->resources); llres != NULL;
+         llres = llres->next) {
+      redfish_resource_t *res = (redfish_resource_t *)llres->value;
+      json_array = json_object_get(payload->json, res->name);
+      for (llentry_t *llprop = llist_head(res->properties); llprop != NULL;
+           llprop = llprop->next) {
+        redfish_property_t *prop = (redfish_property_t *)llprop->value;
+        if (json_array == NULL) {
+          ERROR("Could not find resource \"%s\"", res->name);
+          continue;
+        }
+
+        /* Iterating through array of sensor(s) */
+        for (int i = 0; i < json_array_size(json_array); i++) {
+          json_t *member_id;
+          json_t *object;
+          json_t *item = json_array_get(json_array, i);
+          if (item == NULL) {
+            ERROR("Failure retrieving array member for resource \"%s\"",
+                  res->name);
+            continue;
+          }
+          object = json_object_get(item, prop->name);
+          if (object == NULL) {
+            ERROR("Failure retreiving property \"%s\" from resource \"%s\"",
+                  prop->name, res->name);
+            continue;
+          }
+          value_list_t v1 = VALUE_LIST_INIT;
+          v1.values_len = 1;
+          if (prop->plugin_inst != NULL)
+            sstrncpy(v1.plugin_instance, prop->plugin_inst,
+                     sizeof(v1.plugin_instance));
+          if (prop->type_inst != NULL)
+            sstrncpy(v1.type_instance, prop->type_inst,
+                     sizeof(v1.type_instance));
+          else {
+            /* Retrieving MemberId of sensor */
+            member_id = json_object_get(item, "MemberId");
+            if (member_id == NULL) {
+              ERROR("Failed to get MemberId for property \"%s\" in resource "
+                    "\"%s\"",
+                    prop->name, res->name);
+              continue;
+            }
+            char type_instance[sizeof(v1.type_instance)];
+            int ch_count = snprintf(type_instance, sizeof(type_instance), "%d",
+                                    (int)json_integer_value(member_id));
+            if (ch_count != 0)
+              sstrncpy(v1.type_instance, type_instance,
+                       sizeof(v1.type_instance));
+            else {
+              ERROR("Failed to convert MemberId to a character");
+              continue;
+            }
+          }
+          /* Checking whether real or integer value */
+          redfish_value_t value;
+          redfish_value_type_t type = VAL_TYPE_STR;
+          if (json_is_string(object)) {
+            value.string = (char *)json_string_value(object);
+          } else if (json_is_integer(object)) {
+            type = VAL_TYPE_INT;
+            value.integer = json_integer_value(object);
+          } else if (json_is_real(object)) {
+            type = VAL_TYPE_REAL;
+            value.real = json_real_value(object);
+          }
+          const data_set_t *ds = plugin_get_ds(prop->type);
+
+          /* Check if data set found */
+          if (ds == NULL)
+            continue;
+
+          v1.values = &(value_t){0};
+          redfish_convert_val(&value, type, v1.values, ds->ds[0].type);
+
+          sstrncpy(v1.host, serv->host, sizeof(v1.host));
+          sstrncpy(v1.plugin, PLUGIN_NAME, sizeof(v1.plugin));
+          sstrncpy(v1.type, prop->type, sizeof(v1.type));
+          plugin_dispatch_values(&v1);
+          /* Clear values assigned in case of leakage */
+          v1.values = NULL;
+          v1.values_len = 0;
+        }
+      }
+      json_decref(json_array);
+    }
+  } else {
+    WARNING("Failed to get payload for service name \"%s\"", serv->name);
+  }
+}
+
+static int redfish_read(__attribute__((unused)) user_data_t *ud) {
+  for (llentry_t *le = llist_head(ctx->services); le != NULL; le = le->next) {
+    redfish_service_t *service = (redfish_service_t *)le->value;
+    for (llentry_t *le = llist_head(service->query_ptrs); le != NULL;
+         le = le->next) {
+      redfish_query_t *query = (redfish_query_t *)le->value;
+      redfish_payload_ctx_t rs = {.service = service,
+                                  .resources = query->resources};
+      getPayloadByPathAsync(service->redfish, query->endpoint, NULL,
+                            redfish_process_payload, &rs);
+      /* TODO: Work around for race condition. Needs permanent fix. */
+      sleep(10);
+      // serviceDecRefAndWait(service->redfish);
+    }
+  }
+  return 0;
+}
+
+static int redfish_cleanup(void) {
+  for (llentry_t *le = llist_head(ctx->services); le; le = le->next) {
+    redfish_service_t *service = (redfish_service_t *)le->value;
+
+    cleanupServiceEnumerator(service->redfish);
+    for (size_t i = 0; i < service->queries_num; i++)
+      sfree(service->queries[i]);
+
+    llist_destroy(service->query_ptrs);
+
+    sfree(service->name);
+    sfree(service->host);
+    sfree(service->user);
+    sfree(service->passwd);
+    sfree(service->token);
+    sfree(service->queries);
+    sfree(service);
+  }
+  llist_destroy(ctx->services);
+
+  c_avl_iterator_t *i = c_avl_get_iterator(ctx->queries);
+
+  char *key;
+  redfish_query_t *query;
+
+  while (c_avl_iterator_next(i, (void **)&key, (void **)&query) == 0) {
+    for (llentry_t *le = llist_head(query->resources); le != NULL;
+         le = le->next) {
+      redfish_resource_t *resource = (redfish_resource_t *)le->value;
+      for (llentry_t *le = llist_head(resource->properties); le != NULL;
+           le = le->next) {
+        redfish_property_t *property = (redfish_property_t *)le->value;
+        sfree(property->name);
+        sfree(property->plugin_inst);
+        sfree(property->type);
+        sfree(property->type_inst);
+      }
+      sfree(resource->name);
+    }
+    sfree(query->name);
+    sfree(query->endpoint);
+    sfree(query);
+  }
+
+  c_avl_iterator_destroy(i);
+  c_avl_destroy(ctx->queries);
+  sfree(ctx);
+  return 0;
+}
+
+void module_register(void) {
+  plugin_register_init(PLUGIN_NAME, redfish_init);
+  plugin_register_complex_config(PLUGIN_NAME, redfish_config);
+  plugin_register_complex_read(NULL, PLUGIN_NAME, redfish_read, 0, NULL);
+  plugin_register_shutdown(PLUGIN_NAME, redfish_cleanup);
+}
diff --git a/src/redfish_test.c b/src/redfish_test.c
new file mode 100644 (file)
index 0000000..9488f74
--- /dev/null
@@ -0,0 +1,481 @@
+/**
+ * collectd - src/redfish_test.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Martin Kennelly <martin.kennelly@intel.com>
+ *   Marcin Mozejko <marcinx.mozejko@intel.com>
+ **/
+
+#include "redfish.c"
+#include "testing.h"
+
+DEF_TEST(read_queries) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+
+  assert(ci != NULL);
+  ci->values = calloc(3, sizeof(*ci->values));
+  assert(ci->values != NULL);
+  ci->values_num = 3;
+  ci->values[0].value.string = "temperatures";
+  ci->values[0].type = OCONFIG_TYPE_STRING;
+  ci->values[1].value.string = "fans";
+  ci->values[1].type = OCONFIG_TYPE_STRING;
+  ci->values[2].value.string = "power";
+  ci->values[2].type = OCONFIG_TYPE_STRING;
+
+  char **queries;
+  int ret = redfish_read_queries(ci, &queries);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("temperatures", queries[0]);
+  EXPECT_EQ_STR("fans", queries[1]);
+  EXPECT_EQ_STR("power", queries[2]);
+
+  sfree(ci->values);
+  sfree(ci);
+
+  for (int j = 0; j < 3; j++)
+    sfree(queries[j]);
+  sfree(queries);
+  return 0;
+}
+
+DEF_TEST(convert_val) {
+  redfish_value_t val = {.string = "1"};
+  redfish_value_type_t src_type = VAL_TYPE_STR;
+  int dst_type = DS_TYPE_GAUGE;
+  value_t vl = {0};
+  int ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.gauge == 1.0);
+
+  val.integer = 1;
+  src_type = VAL_TYPE_INT;
+  dst_type = DS_TYPE_GAUGE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.gauge == 1.0);
+
+  val.real = 1.0;
+  src_type = VAL_TYPE_REAL;
+  dst_type = DS_TYPE_GAUGE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.gauge == 1.0);
+
+  val.string = "-1";
+  src_type = VAL_TYPE_STR;
+  dst_type = DS_TYPE_DERIVE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.derive == -1);
+
+  val.integer = -1;
+  src_type = VAL_TYPE_INT;
+  dst_type = DS_TYPE_DERIVE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.derive == -1);
+
+  val.real = -1.0;
+  src_type = VAL_TYPE_REAL;
+  dst_type = DS_TYPE_DERIVE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.derive == -1);
+
+  val.string = "1";
+  src_type = VAL_TYPE_STR;
+  dst_type = DS_TYPE_COUNTER;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.counter == 1);
+
+  val.integer = 1;
+  src_type = VAL_TYPE_INT;
+  dst_type = DS_TYPE_COUNTER;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.counter == 1);
+
+  val.real = 1.0;
+  src_type = VAL_TYPE_REAL;
+  dst_type = DS_TYPE_COUNTER;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.counter == 1);
+
+  val.string = "1";
+  src_type = VAL_TYPE_STR;
+  dst_type = DS_TYPE_ABSOLUTE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.absolute == 1);
+
+  val.integer = 1;
+  src_type = VAL_TYPE_INT;
+  dst_type = DS_TYPE_ABSOLUTE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.absolute == 1);
+
+  val.real = 1.0;
+  src_type = VAL_TYPE_REAL;
+  dst_type = DS_TYPE_ABSOLUTE;
+  ret = redfish_convert_val(&val, src_type, &vl, dst_type);
+  EXPECT_EQ_INT(0, ret);
+  OK(vl.absolute == 1);
+
+  return 0;
+}
+
+/* Testing allocation of memory for ctx struct. Creation of services list
+ * & queries avl tree */
+DEF_TEST(redfish_preconfig) {
+  int ret = redfish_preconfig();
+
+  EXPECT_EQ_INT(0, ret);
+  CHECK_NOT_NULL(ctx);
+  CHECK_NOT_NULL(ctx->queries);
+  CHECK_NOT_NULL(ctx->services);
+
+  llist_destroy(ctx->services);
+  c_avl_destroy(ctx->queries);
+  free(ctx);
+
+  return 0;
+}
+
+/* Testing correct input of properties from conf file */
+DEF_TEST(config_property) {
+  redfish_resource_t *resource = calloc(1, sizeof(*resource));
+  assert(resource != NULL);
+  resource->name = "test property";
+  resource->properties = llist_create();
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+
+  assert(ci != NULL);
+  ci->values_num = 1;
+  ci->values = calloc(1, sizeof(*ci->values));
+  ci->values[0].type = OCONFIG_TYPE_STRING;
+  ci->values[0].value.string = "ReadingRPM";
+
+  ci->children_num = 3;
+  ci->children = calloc(1, sizeof(*ci->children) * ci->children_num);
+
+  ci->children[0].key = "PluginInstance";
+  ci->children[0].parent = ci;
+  ci->children[0].values = calloc(1, sizeof(*ci->children[0].values));
+  assert(ci->children[0].values != NULL);
+  ci->children[0].values_num = 1;
+  ci->children[0].values->value.string = "chassis1";
+  ci->children[0].values->type = OCONFIG_TYPE_STRING;
+
+  ci->children[1].key = "Type";
+  ci->children[1].parent = ci;
+  ci->children[1].values = calloc(1, sizeof(*ci->children[1].values));
+  assert(ci->children[1].values != NULL);
+  ci->children[1].values_num = 1;
+  ci->children[1].values->value.string = "degrees";
+  ci->children[1].values->type = OCONFIG_TYPE_STRING;
+
+  ci->children[2].key = "TypeInstance";
+  ci->children[2].parent = ci;
+  ci->children[2].values = calloc(1, sizeof(*ci->children[2].values));
+  assert(ci->children[2].values != NULL);
+  ci->children[2].values_num = 1;
+  ci->children[2].values->value.string = "0";
+  ci->children[2].values->type = OCONFIG_TYPE_STRING;
+
+  int ret = redfish_config_property(resource, ci);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, llist_size(resource->properties));
+
+  sfree(ci->children[0].values);
+  sfree(ci->children[1].values);
+  sfree(ci->children[2].values);
+
+  sfree(ci->children);
+  sfree(ci->values);
+  sfree(ci);
+
+  for (llentry_t *llprop = llist_head(resource->properties); llprop != NULL;
+       llprop = llprop->next) {
+    redfish_property_t *property = (redfish_property_t *)llprop->value;
+    sfree(property->name);
+    sfree(property->plugin_inst);
+    sfree(property->type);
+    sfree(property->type_inst);
+    sfree(property);
+  }
+  llist_destroy(resource->properties);
+  free(resource);
+  return 0;
+}
+
+DEF_TEST(config_resource) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  assert(ci != NULL);
+
+  ci->values_num = 1;
+  ci->values = calloc(1, sizeof(*ci->values));
+  assert(ci->values != NULL);
+  ci->values[0].value.string = "Temperatures";
+
+  ci->children_num = 1;
+  ci->children = calloc(1, sizeof(*ci->children));
+  assert(ci->children != NULL);
+  ci->children[0].children_num = 1;
+  ci->children[0].parent = ci;
+  ci->children[0].key = "Property";
+  ci->children[0].values_num = 1;
+  ci->children[0].values = calloc(1, sizeof(*ci->children[0].values));
+  ci->children[0].values->value.string = "ReadingRPM";
+  assert(ci->children[0].values != NULL);
+
+  oconfig_item_t *ci_prop = calloc(1, sizeof(*ci_prop));
+  assert(ci_prop != NULL);
+
+  ci->children[0].children = ci_prop;
+  ci->children_num = 1;
+
+  ci_prop->key = "PluginInstance";
+  ci_prop->parent = ci;
+  ci_prop->values_num = 1;
+  ci_prop->values = calloc(1, sizeof(*ci_prop->values));
+  assert(ci_prop->values != NULL);
+  ci_prop->values[0].type = OCONFIG_TYPE_STRING;
+  ci_prop->values[0].value.string = "chassis-1";
+
+  redfish_query_t *query = calloc(1, sizeof(*query));
+  query->endpoint = "/redfish/v1/Chassis/Chassis-1/Thermal";
+  query->name = "fans";
+  query->resources = llist_create();
+
+  int ret = redfish_config_resource(query, ci);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, llist_size(query->resources));
+
+  sfree(ci_prop->values);
+  sfree(ci_prop);
+
+  sfree(ci->values);
+  sfree(ci->children[0].values);
+  sfree(ci->children);
+  sfree(ci);
+
+  for (llentry_t *llres = llist_head(query->resources); llres != NULL;
+       llres = llres->next) {
+    redfish_resource_t *resource = (redfish_resource_t *)llres->value;
+    for (llentry_t *llprop = llist_head(resource->properties); llprop != NULL;
+         llprop = llprop->next) {
+      redfish_property_t *property = (redfish_property_t *)llprop->value;
+      sfree(property->name);
+      sfree(property->plugin_inst);
+      sfree(property->type);
+      sfree(property->type_inst);
+      free(property);
+    }
+    llist_destroy(resource->properties);
+    free(resource->name);
+    free(resource);
+  }
+  llist_destroy(query->resources);
+  sfree(query);
+  return 0;
+}
+
+DEF_TEST(config_query) {
+  oconfig_item_t *qci = calloc(1, sizeof(*qci));
+  assert(qci != NULL);
+  qci->key = "Query";
+  qci->values_num = 1;
+  qci->values = calloc(1, sizeof(*qci->values));
+  assert(qci->values != NULL);
+  qci->values->type = OCONFIG_TYPE_STRING;
+  qci->values->value.string = "fans";
+
+  qci->children_num = 2;
+  qci->children = calloc(1, sizeof(*qci->children) * qci->children_num);
+  assert(qci->children != NULL);
+
+  qci->children[0].key = "Endpoint";
+  qci->children[0].values = calloc(1, sizeof(*qci->children[0].values));
+  assert(qci->children[0].values != NULL);
+  qci->children[0].values->type = OCONFIG_TYPE_STRING;
+  qci->children[0].values_num = 1;
+  qci->children[0].values->value.string =
+      "/redfish/v1/Chassis/Chassis-1/Thermal";
+
+  qci->children[1].key = "Resource";
+  qci->children[1].values_num = 1;
+  qci->children[1].values = calloc(1, sizeof(*qci->children[1].values));
+  assert(qci->children[1].values != NULL);
+  qci->children[1].values->type = OCONFIG_TYPE_STRING;
+  qci->children[1].values->value.string = "Temperature";
+  qci->children[1].children_num = 1;
+
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  assert(ci != NULL);
+
+  qci->children[1].children = ci;
+
+  ci->key = "Property";
+  ci->values_num = 1;
+  ci->values = calloc(1, sizeof(*ci->values));
+  assert(ci->values != NULL);
+  ci->values->type = OCONFIG_TYPE_STRING;
+  ci->values->value.string = "ReadingRPM";
+
+  oconfig_item_t *ci_prop = calloc(1, sizeof(*ci_prop));
+  assert(ci_prop != NULL);
+
+  ci->children = ci_prop;
+  ci->children_num = 1;
+
+  ci_prop->key = "PluginInstance";
+  ci_prop->parent = ci;
+  ci_prop->values_num = 1;
+  ci_prop->values = calloc(1, sizeof(*ci_prop->values));
+  assert(ci_prop->values != NULL);
+  ci_prop->values[0].type = OCONFIG_TYPE_STRING;
+  ci_prop->values[0].value.string = "chassis-1";
+
+  c_avl_tree_t *queries =
+      c_avl_create((int (*)(const void *, const void *))strcmp);
+
+  int ret = redfish_config_query(qci, queries);
+  EXPECT_EQ_INT(0, ret);
+
+  sfree(ci_prop->values);
+  sfree(ci_prop);
+
+  sfree(ci->values);
+  sfree(ci);
+  sfree(qci->children[0].values);
+  sfree(qci->children[1].values);
+  sfree(qci->children);
+  sfree(qci->values);
+  sfree(qci);
+
+  redfish_query_t *query;
+  char *key;
+  c_avl_iterator_t *query_iter = c_avl_get_iterator(queries);
+  while (c_avl_iterator_next(query_iter, (void **)&key, (void **)&query) == 0) {
+    for (llentry_t *llres = llist_head(query->resources); llres != NULL;
+         llres = llres->next) {
+      redfish_resource_t *resource = (redfish_resource_t *)llres->value;
+      for (llentry_t *llprop = llist_head(resource->properties); llprop != NULL;
+           llprop = llprop->next) {
+        redfish_property_t *property = (redfish_property_t *)llprop->value;
+        sfree(property->name);
+        sfree(property->plugin_inst);
+        sfree(property->type);
+        sfree(property->type_inst);
+        sfree(property);
+      }
+      llist_destroy(resource->properties);
+      sfree(resource->name);
+      sfree(resource);
+    }
+    llist_destroy(query->resources);
+    sfree(query->name);
+    sfree(query->endpoint);
+  }
+  sfree(query_iter);
+  c_avl_destroy(queries);
+  return 0;
+}
+
+DEF_TEST(config_service) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  assert(ci != NULL);
+  ci->key = "Service";
+  ci->values_num = 1;
+  ci->values = calloc(1, sizeof(*ci->values));
+  ci->values->type = OCONFIG_TYPE_STRING;
+  ci->values->value.string = "Server 5";
+  ci->children_num = 4;
+  ci->children = calloc(1, sizeof(*ci->children) * ci->children_num);
+  ci->children[0].key = "Host";
+  ci->children[0].values_num = 1;
+  ci->children[0].values = calloc(1, sizeof(*ci->children[0].values));
+  ci->children[0].values->type = OCONFIG_TYPE_STRING;
+  ci->children[0].values->value.string = "127.0.0.1:5000";
+  ci->children[1].key = "User";
+  ci->children[1].values_num = 1;
+  ci->children[1].values = calloc(1, sizeof(*ci->children[1].values));
+  ci->children[1].values->type = OCONFIG_TYPE_STRING;
+  ci->children[1].values->value.string = "user";
+  ci->children[2].key = "Passwd";
+  ci->children[2].values_num = 1;
+  ci->children[2].values = calloc(1, sizeof(*ci->children[2].values));
+  ci->children[2].values->type = OCONFIG_TYPE_STRING;
+  ci->children[2].values->value.string = "passwd";
+  ci->children[3].key = "Queries";
+  ci->children[3].values_num = 1;
+  ci->children[3].values = calloc(1, sizeof(*ci->children[2].values));
+  ci->children[3].values->type = OCONFIG_TYPE_STRING;
+  ci->children[3].values->value.string = "fans";
+
+  ctx = calloc(1, sizeof(*ctx));
+  ctx->services = llist_create();
+
+  int ret = redfish_config_service(ci);
+
+  EXPECT_EQ_INT(0, ret);
+
+  for (llentry_t *llserv = llist_head(ctx->services); llserv != NULL;
+       llserv = llserv->next) {
+    redfish_service_t *serv = (redfish_service_t *)llserv->value;
+    sfree(serv->name);
+    sfree(serv->host);
+    sfree(serv->user);
+    sfree(serv->passwd);
+    for (int i = 0; i < serv->queries_num; i++)
+      sfree(serv->queries[i]);
+  }
+  llist_destroy(ctx->services);
+  sfree(ctx);
+
+  sfree(ci->children[3].values);
+  sfree(ci->children[2].values);
+  sfree(ci->children[1].values);
+  sfree(ci->children[0].values);
+  sfree(ci->children);
+  sfree(ci->values);
+  sfree(ci);
+  return 0;
+}
+
+int main(void) {
+  RUN_TEST(read_queries);
+  RUN_TEST(convert_val);
+  RUN_TEST(redfish_preconfig);
+  RUN_TEST(config_property);
+  RUN_TEST(config_resource);
+  RUN_TEST(config_query);
+  RUN_TEST(config_service);
+  END_TEST;
+}