[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