[rrd-developers] [PATCH 5 of 7] * Add initioal OO style data fetching support

Peter Stamfest peter at stamfest.at
Mon Mar 7 20:28:07 CET 2011


# HG changeset patch
# User Peter Stamfest <peter at stamfest.at>
# Date 1299524671 -3600
# Node ID 66ab2898ff2c8fdcdb03f1da63514ac55e616496
# Parent  5c2915ea1c62b764a97bdfa035371ce90ef2bc6e
* Add initioal OO style data fetching support
* This uses URLs to specify RRD files, possibly using rrdcached connections
  - currently only file:// urls supported, but they allow for rrdcached
    connections to allow for better backwards compatibility
  - the framework should allow for other schemes as well

diff --git a/Makefile.am b/Makefile.am
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,7 +5,7 @@
 
 # build the following subdirectories
 
-SUBDIRS = po src examples doc bindings uriparser/lib
+SUBDIRS = po uriparser/lib src examples doc bindings
 
  # the following files are not mentioned in any other Makefile
 EXTRA_DIST = COPYRIGHT CHANGES WIN32-BUILD-TIPS.txt TODO CONTRIBUTORS THREADS \
diff --git a/src/Makefile.am b/src/Makefile.am
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,7 +12,7 @@
 INCLUDES = -DLOCALEDIR="\"$(datadir)/locale\""
 RRD_DEFAULT_FONT=@RRD_DEFAULT_FONT@
 AM_CPPFLAGS = -DRRD_DEFAULT_FONT=\"$(RRD_DEFAULT_FONT)\" -DNUMVERS=@NUMVERS@
-AM_CFLAGS = @CFLAGS@
+AM_CFLAGS = @CFLAGS@ -I$(top_srcdir)/uriparser/include
 ## no including this by default @WERROR@
 
 UPD_C_FILES =		\
@@ -43,6 +43,7 @@
 	rrd_flushcached.c \
 	rrd_fetch.c	\
 	rrd_resize.c \
+	rrd_source.c \
 	rrd_tune.c
 
 if BUILD_RRDGRAPH
@@ -61,6 +62,7 @@
 	rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_rpncalc.h \
 	rrd_hw.h rrd_hw_math.h rrd_hw_update.h \
 	fnv.h rrd_graph.h \
+	rrd_source.h \
 	rrd_is_thread_safe.h
 
 if BUILD_LIBDBI
@@ -84,7 +86,7 @@
 
 librrd_la_SOURCES         = $(RRD_C_FILES)
 librrd_la_DEPENDENCIES    = librrdupd.la librrd.sym
-librrd_la_LIBADD          = librrdupd.la $(ALL_LIBS)
+librrd_la_LIBADD          = librrdupd.la $(ALL_LIBS) -L$(top_srcdir)/uriparser/lib -luriparser
 librrd_la_LDFLAGS         = -version-info @LIBVERS@
 librrd_la_LDFLAGS         += -export-symbols librrd.sym
 
@@ -93,7 +95,7 @@
 librrd_th_la_CFLAGS          = $(AM_CFLAGS) $(MULTITHREAD_CFLAGS)
 librrd_th_la_LDFLAGS         = $(MULTITHREAD_LDFLAGS) -version-info @LIBVERS@
 librrd_th_la_LDFLAGS         += -export-symbols librrd.sym
-librrd_th_la_LIBADD          = $(ALL_LIBS)
+librrd_th_la_LIBADD          = $(ALL_LIBS) -L$(top_srcdir)/uriparser/lib -luriparser
 
 include_HEADERS	= rrd.h rrd_format.h rrd_client.h
 
diff --git a/src/rrd_source.c b/src/rrd_source.c
new file mode 100644
--- /dev/null
+++ b/src/rrd_source.c
@@ -0,0 +1,316 @@
+/****************************************************************************
+ * rrd_source.c  - OO style rrd data fetch support
+ * (c) 2011 Peter Stamfest
+ *
+ * Part of rrdtool - redistributable and usable as set out in the COPYRIGHT
+ * file
+ ****************************************************************************/
+#include <stdlib.h>
+
+#include "rrd.h"
+#include "rrd_tool.h"
+#include "rrd_client.h"
+#include "rrd_source.h"
+
+
+int rrd_fetch_obj(rrd_base_source_t *obj,
+    const char *cf,         /* which consolidation function ? */
+    time_t *start,
+    time_t *end,            /* which time frame do you want ?
+                             * will be changed to represent reality */
+    unsigned long *step,    /* which stepsize do you want?
+                             * will be changed to represent reality */
+    unsigned long *ds_cnt,  /* number of data sources in file */
+    char ***ds_namv,        /* names of data_sources */
+    rrd_value_t **data)
+{
+    if (obj == NULL) return -1;
+    if (obj->vtab->fetch_m == NULL) return -1;
+
+    return obj->vtab->fetch_m(obj, cf, start, end, step, ds_cnt, ds_namv, data);
+}
+
+/* convenience method - it is important that nothing is done besides calling
+ * the destructor. */
+int delete_source_obj(rrd_base_source_t *obj) {
+    if (obj == NULL) return 0;
+    if (obj->vtab->delete_m != NULL) {
+        return obj->vtab->delete_m(obj);
+    }
+    return 0;
+}
+
+/* base destructor. Will also free() the obj.  */
+static int delete_base_source(rrd_base_source_t *obj) {
+    if (obj == NULL) return 0;
+    if (obj->uri != NULL) {
+        uriFreeUriMembersA(obj->uri);
+        free(obj->uri);
+        obj->uri = NULL;
+    }
+    free(obj);
+    return 0;
+}
+
+static int delete_file_source(rrd_file_source_t *obj) {
+    if (obj == NULL) return 0;
+    if (obj->filename != NULL) free(obj->filename);
+    obj->filename = NULL;
+
+    /* super destructor */
+    delete_base_source((rrd_base_source_t *)obj);
+    return 0;
+}
+
+static int fetch_file_source(rrd_file_source_t *obj,
+                             const char *cf,         /* which consolidation function ? */
+                             time_t *start,
+                             time_t *end,            /* which time frame do you want ?
+                                                      * will be changed to represent reality */
+                             unsigned long *step,    /* which stepsize do you want?
+                                                      * will be changed to represent reality */
+                             unsigned long *ds_cnt,  /* number of data sources in file */
+                             char ***ds_namv,        /* names of data_sources */
+                             rrd_value_t **data)
+{
+    rrdc_connection_t *conn = NULL;
+    int status = 0;
+
+    if (obj->cache == NULL) {
+        return rrd_fetch_r(obj->filename, cf, start, end, step, ds_cnt, ds_namv, data);
+    }
+
+    conn = new_rrdc_connection(obj->cache);
+    if (conn == NULL) {
+        rrd_set_error("Problem connecting to the cache daemon");
+        return -1;
+    }
+    status = rrdc_fetch (conn, obj->filename, cf, start, end, step,
+                         ds_cnt, ds_namv, data);
+    delete_rrdc_connection(conn);
+    return status;
+}
+
+static int compare_file_source(rrd_file_source_t *obj,
+                               rrd_base_source_t *other) {
+    rrd_file_source_t *other_file;
+    int rc = 0;
+
+    if (obj == NULL || other == NULL) return -1;
+    if (obj->super.vtab != other->vtab) return -1;
+
+    /* OK, both are file sources */
+    other_file = (rrd_file_source_t *) other;
+
+    if (obj->filename == NULL || other_file->filename == NULL)
+        return -1;
+    rc = strcmp(obj->filename, other_file->filename);
+    if (rc != 0) return rc;
+
+    if (obj->cache == NULL && other_file->cache == NULL) return 0;
+    if (obj->cache == NULL || other_file->cache == NULL) return -1;
+
+    rc = strcmp(obj->cache, other_file->cache);
+    if (rc != 0) return rc;
+
+    return 0;
+}
+
+static char * to_string_file_source(rrd_file_source_t *obj) {
+    char *buf = NULL;
+    int len = 8; /*  file:/// */
+
+    if (obj == NULL) return NULL;
+
+    len += strlen(obj->filename);
+    if (obj->cache != NULL) {
+        len += 7; /* ?cache=  */
+        len += strlen(obj->cache);
+    }
+    len++;  /* NUL */
+
+    buf = malloc(len);
+    if (buf == NULL) return buf;
+
+    buf[0] = 0;
+    strcat(buf, "file:///");
+    strcat(buf, obj->filename);
+
+    if (obj->cache != NULL) {
+        strcat(buf, "?cache=");
+        strcat(buf, obj->cache);
+    }
+    return buf;
+}
+
+static const struct rrd_source_base_vtab rrd_source_file_vtab = {
+    .delete_m    = (delete_op_ptr)    delete_file_source,
+    .fetch_m     = (fetch_op_ptr)     fetch_file_source,
+    .compare_m   = (compare_op_ptr)   compare_file_source,
+    .to_string_m = (to_string_op_ptr) to_string_file_source,
+};
+
+rrd_file_source_t *new_file_source(const char *filename, const char *cache) {
+    if (filename == NULL) {
+        rrd_set_error("non NULL filename required");
+        return NULL;
+    }
+
+    rrd_file_source_t *obj = calloc(1, sizeof(rrd_file_source_t));
+    if (obj == NULL) {
+        rrd_set_error("Out of memory");
+        return NULL;
+    }
+
+    /* methods */
+    obj->super.vtab = &rrd_source_file_vtab;
+    /* data */
+    obj->filename = strdup(filename);
+    obj->cache = cache != NULL && strlen(cache) > 0 ? strdup(cache) : NULL;
+
+    return obj;
+}
+
+static char *partDup(UriTextRangeA *s) {
+    if (s == NULL) return NULL;
+    if (s->first == NULL) return NULL;
+
+    return strndup(s->first, s->afterLast - s->first);
+}
+
+/* Take parsed URI and reconstruct file from the list of path segments.
+ *
+ * Returned path must be free'd */
+static char *get_uri_path(UriUriA *uri) {
+    UriPathSegmentA *seg;
+    char *p = NULL, *pp;
+    int count = 0;
+
+    if (uri == NULL) return NULL;
+
+    int n = 0;
+    if (uri->absolutePath) {
+        n += 1;
+    }
+    /* two passes: first to calculate length, second to build path. */
+    for(seg = uri->pathHead ; seg != uri->pathTail->next ; seg = seg->next) {
+        n += (seg->text.afterLast - seg->text.first) + 1;
+    }
+    n += 1; /* NUL byte */
+
+    p = pp = malloc(n);
+    if (p == NULL) {
+        /* OOM */
+        /* should we call rrd_set_error ? */
+        return NULL;
+    }
+
+    if (uri->absolutePath) {
+        *(pp++) = '/';
+    }
+
+    for(seg = uri->pathHead ; seg != uri->pathTail->next ; seg = seg->next, count++) {
+        int l = seg->text.afterLast - seg->text.first;
+        if (count > 0) *(pp++) = '/';
+        strncpy(pp, seg->text.first, l);
+        pp += l;
+    }
+    *pp = 0;
+    return p;
+}
+
+rrd_base_source_t *parse_source_string(const char *spec) {
+    rrd_base_source_t *result = NULL;
+    UriParserStateA state;
+    UriUriA *uri = (UriUriA *) malloc(sizeof(UriUriA));
+
+    if (uri == NULL) {
+        /* OOM */
+        rrd_set_error("Out of memory");
+        goto out;
+    }
+
+    state.uri = uri;
+    if (uriParseUriA(&state, spec) != URI_SUCCESS) {
+        rrd_set_error("Cannot parse '%s' as URL", spec);
+        goto out;
+    }
+
+    result = parse_source_uri(uri);
+
+out:
+    if (result == NULL && uri != NULL) {
+        uriFreeUriMembersA(uri);
+        free(uri);
+    }
+    return result;
+}
+
+
+/** turn a parsed URI into a rrd source.
+ *
+ * The passed uri becomes part of the returned object,
+ * and will be free'd by the destructor. */
+rrd_base_source_t *parse_source_uri(UriUriA *uri) {
+    rrd_base_source_t *result = NULL;
+    char *scheme = NULL;
+    char *host = NULL, *port = NULL;
+
+    if (uri == NULL) return NULL;
+
+    scheme = partDup(&(uri->scheme));
+    if (scheme == NULL) {
+        /* default is ordinary file with optional rrdcached connection... */
+        return NULL;
+    } else if (strcmp(scheme, "file") == 0) {
+        /* file, if host/port given -> use that as the corresponding
+         * rrdcached. This only works for TCP connected rrdcached */
+
+        /* an optional query parameter "cache" also denotes the associated
+         * cache... this works for local sockets as well */
+
+        char *cache = NULL;
+        char *f = get_uri_path(uri);
+        UriQueryListA *queryList, *p;
+        int itemCount;
+
+        if (f == NULL) {
+            goto out;
+        }
+
+        host = partDup(&(uri->hostText));
+        port = partDup(&(uri->portText));
+        if (host != NULL && strlen(host) > 0) {
+            cache = malloc(strlen(host) + 1 + strlen(port ? port : "") + 1);
+            if (cache == NULL) goto out;
+            sprintf(cache, (port ? "%s:%s" : "%s"), host, port);
+        }
+
+        if (uriDissectQueryMallocA(&queryList, &itemCount, uri->query.first,
+                                   uri->query.afterLast) == URI_SUCCESS) {
+            for (p = queryList ; p ; p = p->next) {
+                if (strcmp(p->key, "cache") == 0) {
+                    if (cache != NULL) free(cache); /* in case we have set it before */
+                    cache = strdup(p->value);
+                    if (cache == NULL) goto out;
+                }
+            }
+
+            uriFreeQueryListA(queryList);
+        }
+
+        result = (rrd_base_source_t*) new_file_source(f, cache);
+        result->uri = uri;
+        free(f);
+        if (cache != NULL) free(cache);
+    } else if (strcmp(scheme, "rrdcache") == 0) {
+
+    }
+
+out:
+    if (scheme == NULL) free(scheme);
+    if (host   == NULL) free(host);
+    if (port   == NULL) free(port);
+
+    return result;
+}
diff --git a/src/rrd_source.h b/src/rrd_source.h
new file mode 100644
--- /dev/null
+++ b/src/rrd_source.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+ * rrd_source.h  - OO style rrd data fetch support
+ * (c) 2011 Peter Stamfest
+ *
+ * Part of rrdtool - redistributable and usable as set out in the COPYRIGHT
+ * file
+ ****************************************************************************/
+#ifndef _RRD_SOURCE_H
+#define _RRD_SOURCE_H
+
+#include "uriparser/Uri.h"
+
+struct rrd_source_base_s;
+struct rrd_file_source_s;
+
+typedef int (*delete_op_ptr)(struct rrd_source_base_s *obj);
+
+typedef int (*fetch_op_ptr)(
+    struct rrd_source_base_s *obj,
+    const char *cf,         /* which consolidation function ? */
+    time_t *start,
+    time_t *end,            /* which time frame do you want ?
+                             * will be changed to represent reality */
+    unsigned long *step,    /* which stepsize do you want?
+                             * will be changed to represent reality */
+    unsigned long *ds_cnt,  /* number of data sources in file */
+    char ***ds_namv,        /* names of data_sources */
+    rrd_value_t **data);
+
+typedef int (*compare_op_ptr)(struct rrd_source_base_s *obj,
+                              struct rrd_source_base_s *other);
+
+/* turn object into a human readable string representation.
+ * Returned string must be free'd. */
+typedef char * (*to_string_op_ptr)(struct rrd_source_base_s *obj);
+
+
+/*
+ * A very simple OO system. It uses distinct virtual function
+ * tables (at least not now). This allows for runtime type identification
+ * (which should NOT be used lightly, but simplyfies comparing of objects)
+ */
+struct rrd_source_base_vtab {
+    delete_op_ptr       delete_m;     /* destructor. NOTE: Destructors MUST be
+                                       * chained, with the call to a base class
+                                       * destructor being the last thing done
+                                       * in the destructor of a derived class */
+    fetch_op_ptr        fetch_m;
+    compare_op_ptr      compare_m;
+    to_string_op_ptr    to_string_m;
+};
+
+struct rrd_source_base_s {
+    const struct rrd_source_base_vtab *vtab;
+
+    UriUriA *uri;
+};
+
+/* effectively derived from rrd_source_base_s */
+struct rrd_file_source_s {
+    struct rrd_source_base_s super;
+
+    char *filename;
+    char *cache;
+};
+
+
+typedef struct rrd_file_source_s rrd_file_source_t;
+typedef struct rrd_source_base_s rrd_base_source_t;
+
+
+#define call0(obj, method) ((obj)->vtab->method((obj)))
+#define call(obj, method, ...) ((obj)->vtab->method((obj), __VA_ARGS__))
+
+int rrd_fetch_obj(rrd_base_source_t *obj,
+                  const char *cf,         /* which consolidation function ? */
+                  time_t *start,
+                  time_t *end,            /* which time frame do you want ?
+                                           * will be changed to represent reality */
+                  unsigned long *step,    /* which stepsize do you want?
+                                 * will be changed to represent reality */
+                  unsigned long *ds_cnt,  /* number of data sources in file */
+                  char ***ds_namv,        /* names of data_sources */
+                  rrd_value_t **data);
+
+int delete_source_obj(rrd_base_source_t *obj);
+rrd_file_source_t *new_file_source(const char *filename, const char *cache);
+
+rrd_base_source_t *parse_source_string(const char *uri);
+rrd_base_source_t *parse_source_uri(UriUriA *uri);
+
+
+#endif



More information about the rrd-developers mailing list