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

Peter Stamfest <[email protected]>
Newsgroups gmane.comp.db.rrdtool.devel
Message-ID <[email protected]>
# HG changeset patch
# User Peter Stamfest <[email protected]>
# 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.