aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-04-24 11:02:35 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-04-24 11:02:35 +0300
commitc36ee7102c3095f70e8db52aa55046c1393ff4cd (patch)
treee21b479c6e1d9c4b1c2fc8e6f0eed515b0588fe7
parent0d24087bca70de572cda0e35b91678816c5d9b40 (diff)
downloadtagr-c36ee7102c3095f70e8db52aa55046c1393ff4cd.tar.gz
tagr-c36ee7102c3095f70e8db52aa55046c1393ff4cd.tar.bz2
Fix statistics gathering algorithm. Start implementing drawing functions.
* src/output.c: New file. * src/Makefile.am (tagr_SOURCES): Add output.c * src/graph.c (draw_graph): New function. * src/main.c (mkfilename): Allow for suffix=NULL * src/report.c (tr_init): Set queue sizes to exactly the number of the corresponding samples. (update_output): New function. (rebuild): Implement * src/stat.c (interpolate): Avoid interpolation if time span is less than the step. Use queue_put to store the data and always call the overflow procedure, if supplied. (overflow): Fix condition. (compute_avg): New function. (ovf_monthly, ovf_weekly, ovf_daily): Add verbose diagnostics. (_convert): Additional argument: overflow function. (convert_yearly, convert_monthly, convert_weekly): Compute the average. Add verbose diagnostics. (convert_daily): Add verbose diagnostics. * src/tagr.h (update_output, draw_graph): New protos.
-rw-r--r--src/Makefile.am1
-rw-r--r--src/graph.c94
-rw-r--r--src/main.c23
-rw-r--r--src/output.c123
-rw-r--r--src/report.c53
-rw-r--r--src/stat.c78
-rw-r--r--src/tagr.h11
7 files changed, 351 insertions, 32 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 7f067d5..4ef8905 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,6 +24,7 @@ tagr_SOURCES=\
html.lex.l\
log.c\
main.c\
+ output.c\
queue.c\
readconfig.c\
report.c\
diff --git a/src/graph.c b/src/graph.c
index b88a5e8..1026f35 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -1,5 +1,5 @@
/* This file is part of tagr.
- Copyright (C) 2006, Sergey Poznyakoff
+ Copyright (C) 2006, 2009 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -50,8 +50,98 @@ int color_in_max[3] = { 0,166,33 };
int color_out_max[3] = { 255,0,255 };
int color_percent[3] = { 239,159,79 };
+int graph_xsize = 460;
+int graph_ysize = 100;
+
static char *short_suffix[] = {"", "k", "M", "G", "T"};
char **number_suffix = short_suffix;
size_t number_suffix_count = sizeof (short_suffix) / sizeof (short_suffix[0]);
-/* No functions so far */
+#define make_color_index(g, ar) \
+ gdImageColorAllocate (g, (ar)[0], (ar)[1], (ar)[2])
+
+int
+draw_graph (FILE *fp,
+ queue_t *dataq, const struct avg_acc *avg, time_t now,
+ int xstep, unsigned long xmax, unsigned long ymax,
+ int growright)
+{
+ int x;
+ int i, n;
+ gdImagePtr graph, brush_out, brush_outm, brush_outp;
+ int i_background, i_light, i_dark;
+ int i_major, i_in, i_out, i_grid, i_inm, i_outm;
+ int i_outp, i_outpg;
+ double xscale, yscale;
+
+ yscale = ((double) graph_ysize - 35) / ymax;
+ xscale = ((double) graph_xsize - 100 - percent_option * 30) / xmax;
+
+#define ytr(y) \
+ (unsigned long) ((ymax - (y)) * yscale + 14)
+#define xtr(x) \
+ (unsigned long) (growright ? ((graph_xsize - (x))) : (x))
+
+ graph = gdImageCreate (graph_xsize, graph_ysize);
+ brush_out = gdImageCreate (1, 2);
+ brush_outm = gdImageCreate (1, 2);
+ brush_outp = gdImageCreate (1, 2);
+
+ i_background = make_color_index (graph, color_background);
+ i_light = make_color_index (graph, color_light);
+ i_dark = make_color_index (graph, color_dark);
+
+ if (transparent_option)
+ gdImageColorTransparent (graph, i_background);
+
+ gdImageInterlace (graph, 1);
+
+ i_major = make_color_index (graph, color_major);
+ i_in = make_color_index (graph, color_in);
+ i_out = make_color_index (brush_out, color_out);
+ i_grid = make_color_index (graph, color_grid);
+ i_inm = make_color_index (graph, color_in_max);
+ i_outm = make_color_index (brush_outm, color_out_max);
+ i_outp = make_color_index (brush_outp, color_percent);
+ i_outpg = make_color_index (graph, color_percent);
+
+ /* Draw the image border */
+ gdImageLine (graph, 0, 0, graph_xsize - 1, 0, i_light);
+ gdImageLine (graph, 1, 1, graph_xsize - 2, 1, i_light);
+ gdImageLine (graph, 0, 0, 0, graph_ysize - 1, i_light);
+ gdImageLine (graph, 1, 1, 1, graph_ysize - 2, i_light);
+ gdImageLine (graph, graph_xsize - 1, 0, graph_xsize - 1,
+ graph_ysize - 1, i_dark);
+ gdImageLine (graph, 0, graph_ysize - 1, graph_xsize - 1,
+ graph_ysize - 1, i_dark);
+ gdImageLine (graph, graph_xsize - 2, 1, graph_xsize - 2,
+ graph_ysize - 2, i_dark);
+ gdImageLine (graph, 1, graph_ysize - 2, graph_xsize - 2,
+ graph_ysize - 2, i_dark);
+
+ n = queue_count (dataq);
+ for (i = n - 1, x = 0; i > 0; i--, x++)
+ {
+ struct traffic_history *th = queue_get_ptr (dataq, i);
+ struct traffic_history *tnext = queue_get_ptr (dataq, i - 1);
+ if (fill_incoming_option)
+ gdImageLine (graph,
+ xtr (x), ytr (0),
+ xtr (x), ytr (tnext->inrate), i_in);
+ gdImageLine (graph, xtr (x), ytr (th->inrate),
+ xtr (x+1), ytr (tnext->inrate), i_in);
+#if 0
+ gdImageLine (graph, x + 0.5, ytr (0),
+ x + 0.5, ytr (th->inrate), i_in);
+#endif
+ }
+
+ /* FIXME: Drow outgoing traffic, grids and legends */
+ gdImagePng (graph, fp);
+
+ gdImageDestroy (graph);
+ gdImageDestroy (brush_out);
+ gdImageDestroy (brush_outm);
+ gdImageDestroy (brush_outp);
+ return 0;
+}
diff --git a/src/main.c b/src/main.c
index e512502..5d26970 100644
--- a/src/main.c
+++ b/src/main.c
@@ -636,22 +636,23 @@ char *
mkfilename (char *dir, char *name, char *suffix)
{
int ret;
- int len = strlen (name) + strlen (suffix);
char *buf;
+ int len = strlen (name) + (suffix ? strlen (suffix) : 0);
+
if (dir)
len += strlen (dir) + 1;
- buf = malloc (len + 1);
- if (buf)
+
+ buf = xmalloc (len + 1);
+ buf[0] = 0;
+ if (dir)
{
- buf[0] = 0;
- if (dir)
- {
- strcat (buf, dir);
- strcat (buf, "/");
- }
- strcat (buf, name);
- strcat (buf, suffix);
+ strcat (buf, dir);
+ strcat (buf, "/");
}
+ strcat (buf, name);
+ if (suffix)
+ strcat (buf, suffix);
+
return buf;
}
diff --git a/src/output.c b/src/output.c
new file mode 100644
index 0000000..b205a4e
--- /dev/null
+++ b/src/output.c
@@ -0,0 +1,123 @@
+/* This file is part of tagr.
+ Copyright (C) 2009 Sergey Poznyakoff
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <tagr.h>
+
+int
+create_hierarchy (char *dir, int perm)
+{
+ int rc;
+ struct stat st;
+ char *p;
+
+ if (stat (dir, &st) == 0)
+ {
+ if (!S_ISDIR (st.st_mode))
+ {
+ logmsg (L_ERR, _("component %s is not a directory"), dir);
+ return 1;
+ }
+ return 0;
+ }
+ else if (errno != ENOENT)
+ {
+ logmsg (L_ERR, _("cannot stat file %s: %s"), dir, strerror (errno));
+ return 1;
+ }
+
+ p = strrchr (dir, '/');
+ if (p)
+ *p = 0;
+ rc = create_hierarchy (dir, perm);
+ if (rc == 0)
+ {
+ if (p)
+ *p = '/';
+ if (mkdir (dir, perm))
+ {
+ logmsg (L_ERR, _("cannot create directory %s: %s"),
+ dir, strerror (errno));
+ rc = 1;
+ }
+ }
+ return rc;
+}
+
+static int
+do_update_output (SD *host,
+ queue_t *queue, const struct avg_acc *avg,
+ time_t timestamp,
+ size_t step, size_t count,
+ char *dirname, char *img_suffix)
+{
+ char *fname;
+ FILE *fp;
+ int rc;
+
+ if (create_hierarchy (dirname, 0755))
+ {
+ logmsg (L_ERR, _("cannot create directory %s"),
+ dirname);
+ return 1;
+ }
+
+ fname = mkfilename (dirname, host->name, img_suffix);
+ fp = fopen (fname, "w");
+ if (!fp)
+ {
+ logmsg (L_ERR, _("cannot create file %s: %s"), fname, strerror (errno));
+ rc = 1;
+ }
+ else
+ {
+ rc = draw_graph (fp, queue, avg, timestamp, step, count,
+ host->max, 0);
+ fclose (fp);
+ }
+ free (fname);
+ return rc;
+}
+
+int
+update_output (SD *host, struct traffic_record *tr, time_t timestamp)
+{
+ char *dirname = mkfilename (basedir, host->dir, NULL);
+ int rc;
+
+ rc = do_update_output (host, &tr->day_hist, NULL,
+ DAY_SAMPLE, DAY_COUNT, timestamp,
+ dirname, "-day.png");
+ rc += do_update_output (host, &tr->week_hist, &tr->week_avg,
+ WEEK_SAMPLE, WEEK_COUNT, timestamp,
+ dirname, "-week.png");
+ rc += do_update_output (host, &tr->month_hist, &tr->month_avg,
+ MONTH_SAMPLE, MONTH_COUNT, timestamp,
+ dirname, "-month.png");
+ rc += do_update_output (host, &tr->year_hist, &tr->year_avg,
+ YEAR_SAMPLE, YEAR_COUNT, timestamp,
+ dirname, "-year.png");
+ free (dirname);
+ return rc;
+}
diff --git a/src/report.c b/src/report.c
index 08989ec..3a7fc1c 100644
--- a/src/report.c
+++ b/src/report.c
@@ -72,16 +72,16 @@ void
tr_init (struct traffic_record *tr)
{
tr->day_hist.queue = tr->history;
- tr->day_hist.size = DAY_COUNT + 2;
+ tr->day_hist.size = DAY_COUNT;
tr->week_hist.queue = tr->day_hist.queue + tr->day_hist.size;
- tr->week_hist.size = WEEK_COUNT + 2;
+ tr->week_hist.size = WEEK_COUNT;
tr->month_hist.queue = tr->week_hist.queue + tr->week_hist.size;
- tr->month_hist.size = MONTH_COUNT + 2;
+ tr->month_hist.size = MONTH_COUNT;
tr->year_hist.queue = tr->month_hist.queue + tr->month_hist.size;
- tr->year_hist.size = YEAR_COUNT + 2;
+ tr->year_hist.size = YEAR_COUNT;
}
static void
@@ -234,8 +234,51 @@ report (Stat *stat, time_t timestamp)
logmsg (L_WARNING, "%s not found in config", stat->name);
}
+int
+update_router (datum key, time_t timestamp)
+{
+ char id[MAX_NAME_LENGTH+1];
+ struct traffic_record *tr = NULL;
+ SD *sd;
+
+ if (key.dsize > MAX_NAME_LENGTH)
+ {
+ logmsg (L_ERR, _("tag name too long (%u)"), key.dsize);
+ return 1;
+ }
+ memcpy (id, key.dptr, key.dsize);
+ id[key.dsize] = 0;
+ sd = find_router_id (id);
+ if (!sd)
+ {
+ logmsg (L_ERR, _("%s: no such router"), id);
+ return 1;
+ }
+ _read_db (key, &tr);
+ if (tr)
+ {
+ update_output (sd, tr, timestamp);
+ free (tr);
+ }
+
+ return 0;
+}
+
void
rebuild ()
{
- die (EX_SOFTWARE, "Rebuild is not yet implemented");
+ datum key;
+ datum content;
+ time_t now = time (NULL);
+
+ open_db ();
+ key = gdbm_firstkey (dbf);
+ while (key.dptr)
+ {
+ datum nextkey = gdbm_nextkey ( dbf, key );
+ update_router (key, now);
+ free (key.dptr);
+ key = nextkey;
+ }
+ close_db ();
}
diff --git a/src/stat.c b/src/stat.c
index 4c1d434..a7bd2c1 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -41,17 +41,28 @@ interpolate (queue_t *q,
struct traffic_record *tr)
{
time_t next;
+ struct traffic_history th;
+ if (now - last_time <= step)
+ {
+ th.inrate = inrate;
+ th.outrate = outrate;
+ verbose (3, "Insert %lu %g %g", next, th.inrate, th.outrate);
+ queue_put (q, &th);
+ if (ovf)
+ ovf (&th, tr, now);
+ return;
+ }
+
for (next = last_time + step; next <= now; next += step)
{
- struct traffic_history th;
-
th.inrate = (inrate - last_rates->inrate) * (next - last_time)
/ interval + last_rates->inrate;
th.outrate = (outrate - last_rates->outrate) * (next - last_time)
/ interval + last_rates->outrate;
verbose (3, "Insert %lu %g %g", next, th.inrate, th.outrate);
- if (queue_xchg (q, &th) && ovf)
+ queue_put (q, &th);
+ if (ovf)
ovf (&th, tr, now);
}
}
@@ -66,7 +77,7 @@ overflow (struct traffic_history *th,
int maxcount,
int step)
{
- if (now - avg->time > step)
+ if (now - avg->time >= step)
{
struct traffic_history *lastp = queue_get_tail (q);
if (lastp)
@@ -99,22 +110,28 @@ overflow (struct traffic_history *th,
int
ovf_monthly (struct traffic_history *th, struct traffic_record *tr, time_t now)
{
+ verbose (2, "begin overflow_monthly %lu %g %g", now, th->inrate, th->outrate);
overflow (th, tr, now, NULL, &tr->year_avg, &tr->year_hist,
YEAR_COUNT, YEAR_SAMPLE);
+ verbose (2, "end overflow_monthly");
}
int
ovf_weekly (struct traffic_history *th, struct traffic_record *tr, time_t now)
{
+ verbose (2, "begin overflow_weekly %lu %g %g", now, th->inrate, th->outrate);
overflow (th, tr, now, ovf_monthly, &tr->month_avg, &tr->month_hist,
MONTH_COUNT, MONTH_SAMPLE);
+ verbose (2, "end overflow_daily");
}
int
ovf_daily (struct traffic_history *th, struct traffic_record *tr, time_t now)
{
+ verbose (2, "begin overflow_daily %lu %g %g", now, th->inrate, th->outrate);
overflow (th, tr, now, ovf_weekly, &tr->week_avg, &tr->week_hist,
WEEK_COUNT, WEEK_SAMPLE);
+ verbose (2, "end overflow_daily");
}
void
@@ -161,16 +178,35 @@ update_stats (SD *sd, struct traffic_record *tr)
+
+static void
+compute_avg (struct avg_acc *avg, queue_t *q, struct last_sample *last)
+{
+ int i, n = queue_count (q);
+ avg->inrate = avg->outrate = 0;
+ avg->count = n;
+
+ for (i = 0; i < n; i++)
+ {
+ struct traffic_history *th = queue_get_ptr (q, i);
+ avg->inrate += th->inrate;
+ avg->outrate += th->outrate;
+ }
+ avg->inrate /= n;
+ avg->outrate /= n;
+ avg->time = last->time;
+}
+
#define hist_rec last_sample
static void
-_convert (queue_t *q,
+_convert (queue_t *q, ovf_t ovf,
struct traffic_record *tr, struct hist_rec *hist, size_t count,
time_t sample_interval)
{
size_t i;
struct hist_rec *hp;
-
+
tr->last.time = hist[count-1].time - sample_interval;
for (hp = hist + count - 1; hp >= hist; hp--)
{
@@ -181,7 +217,7 @@ _convert (queue_t *q,
interval = hp->time - tr->last.time;
inrate = (double) hp->in;
outrate = (double) hp->out;
-
+
if (interval == 0)
{
logmsg (L_ERR, "Ignoring zero interval");
@@ -196,7 +232,7 @@ _convert (queue_t *q,
&tr->last_rates,
interval,
inrate, outrate,
- NULL, tr);
+ ovf, tr);
}
else
{
@@ -212,37 +248,51 @@ _convert (queue_t *q,
tr->last.out = hp->out;
tr->last_rates.inrate = inrate;
tr->last_rates.outrate = outrate;
- }
+ }
}
static void
convert_yearly (struct traffic_record *tr, struct hist_rec *hp, size_t count)
{
- _convert (&tr->year_hist, tr, hp, YEAR_COUNT + 1, YEAR_SAMPLE);
+ verbose (2, "begin convert_yearly");
+ _convert (&tr->year_hist, NULL, tr,
+ hp, count, YEAR_SAMPLE);
+ compute_avg (&tr->year_avg, &tr->year_hist, &tr->last);
+ verbose (2, "end convert_yearly");
}
static void
convert_monthly (struct traffic_record *tr, struct hist_rec *hp, size_t count)
{
+ verbose (2, "begin convert_monthly");
if (count > MONTH_COUNT+1)
convert_yearly (tr, hp + MONTH_COUNT + 1, count - (MONTH_COUNT + 1));
- _convert (&tr->month_hist, tr, hp, MONTH_COUNT + 1, MONTH_SAMPLE);
+ _convert (&tr->month_hist, ovf_monthly, tr,
+ hp, MONTH_COUNT + 1, MONTH_SAMPLE);
+ compute_avg (&tr->month_avg, &tr->month_hist, &tr->last);
+ verbose (2, "end convert_monthly");
}
static void
convert_weekly (struct traffic_record *tr, struct hist_rec *hp, size_t count)
{
+ verbose (2, "begin convert_weekly");
if (count > WEEK_COUNT+1)
convert_monthly (tr, hp + WEEK_COUNT + 1, count - (WEEK_COUNT + 1));
- _convert (&tr->week_hist, tr, hp, WEEK_COUNT + 1, WEEK_SAMPLE);
+ _convert (&tr->week_hist, ovf_weekly,
+ tr, hp, WEEK_COUNT + 1, WEEK_SAMPLE);
+ compute_avg (&tr->week_avg, &tr->week_hist, &tr->last);
+ verbose (2, "end convert_weekly");
}
static void
convert_daily (struct traffic_record *tr, struct hist_rec *hp, size_t count)
{
+ verbose (2, "begin convert_daily");
if (count > DAY_COUNT+1)
convert_weekly (tr, hp + DAY_COUNT + 1, count - (DAY_COUNT + 1));
- _convert (&tr->day_hist, tr, hp, DAY_COUNT + 1, DAY_SAMPLE);
+ _convert (&tr->day_hist, ovf_daily, tr, hp, DAY_COUNT + 1, DAY_SAMPLE);
+ verbose (2, "end convert_daily");
}
static void
@@ -255,7 +305,7 @@ convert_stats (SD *sd, struct hist_rec *last,
tr_init (&tr);
convert_daily (&tr, hp, count);
-
+
read_db (sd, &trp);
*trp = tr;
trp->last = *last;
diff --git a/src/tagr.h b/src/tagr.h
index 2766e40..a88ba9e 100644
--- a/src/tagr.h
+++ b/src/tagr.h
@@ -211,3 +211,14 @@ void queue_put (queue_t *q, struct traffic_history *elt);
int queue_xchg (queue_t *q, struct traffic_history *elt);
char *mkfilename (char *dir, char *name, char *suffix);
+
+
+/* output.c */
+int update_output (SD *host, struct traffic_record *tr, time_t timestamp);
+
+
+/* graph.c */
+int draw_graph (FILE *fp,
+ queue_t *dataq, const struct avg_acc *avg, time_t now,
+ int xstep, unsigned long xmax, unsigned long ymax,
+ int growright);

Return to:

Send suggestions and report system problems to the System administrator.