}
</code>
+<sect>Stats
+<label id="stats">
+
+<sect1>
+<label id="stats-intro">
+
+<p>The Statistics protocol allows you to measure number of exported routes from
+a table. You can also narrow the view by applying export filter inside stats
+channel. One instance of stats protocol can have multiple channels attached.
+Stats protocol could be particullary useful when making conditional route advertising.
+
+<p>Statistics are accessed in filters by same name as channel connected to desired
+table. Value of this expresion is evaluated to the sum of all routes with
+generation smaller than max generation (see below).
+
+<sect1>Configuration
+<label id="stats-config">
+
+<p><descrtip>
+ <tag><label id="stats-max-generation">max generation <m/expr/</tag>
+ Statistics counter contains sum of all routes with generation less
+ than or equal to max generation. This copies behavior of pipe's
+ <ref id="pipe-max-generation" name="max generetion">. Must be in range
+ from 0 to 254. Default: 16.
+
+ <tag><label id="stats-update-after">settle time <m/time/ <m/number/</tag>
+ Wait given amount of time before notifying and recalculting filters
+ which reference this counter in seconds. If counter changes during this period,
+ current value is used. If set to zero, the notification is done instantly.
+ Default: 5 s.
+</descrip>
+
+<p>Example
+<code>
+protocol stats {
+ ipv4 stats1 { table bgp_tab1; };
+}
+
+protocol static {
+ # note that the stats1 is unrelated
+ ipv4 { import where stats1 > 200; };
+ route 0.0.0.0:0;
+}
+</code>
+
+<p>Beware that configuration with cyclic references (even logical ones) are
+considered invalid and the behaviour is not defined! You <em>should</em> avoid
+them. No detection is implemented.
<chapt>Conclusions
<label id="conclusion">
CF_HDR
-/* old: #include "proto/pipe/pipe.h" */
#include "proto/stats/stats.h"
CF_DEFINES
-/* old: #define PIPE_CFG ((struct pipe_config *) this_proto) */
#define STATS_CC ((struct stats_channel_config *) this_channel)
CF_DECLS
-/* TODO here add more keywords */
-CF_KEYWORDS(STATS, TABLE, MAX, GENERATION)
+CF_KEYWORDS(STATS, TABLE, MAX, GENERATION, SETTLE, TIME)
%type <cc> stats_channel_start
}
;
+stats_settle:
+ SETTLE TIME time {
+ STATS_CC->settle = $3 S_;
+ }
+
stats_opts:
/* empty */
| stats_opts channel_item ';'
| stats_opts channel_max_gen ';'
+ | stats_opts stats_settle ';'
;
stats_proto_channel: stats_channel_start stats_channel_opt_list channel_end ;
-/* stats_proto_channel: stats_channel_start stats_max_gen channel_opt_list channel_end ; */
stats_channel_start: net_type symbol
{
this_channel = channel_config_get(&channel_stats, $2->name, $1, this_proto);
STATS_CC->max_generation = 16;
- /* from filter/config.Y:
- cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++) */
+ STATS_CC->settle = 5 S_;
$2 = cf_define_symbol($2, SYM_COUNTER, ch_config, this_channel);
}
#include "conf/conf.h"
#include "filter/filter.h"
#include "lib/string.h"
+#include "lib/timer.h"
#include "stats.h"
-#ifdef CONFIG_BGP
-#include "proto/bgp/bgp.h"
-#endif
+#define COUNTER 255
+
+static void stats_kick_timer(struct stats_channel *c);
static void
stats_rt_notify(struct proto *P UNUSED, struct channel *src_ch, const net_addr *n UNUSED, rte *new, const rte *old)
{
struct stats_channel *ch = (void *) src_ch;
+ int changed = 0;
if (old)
{
ch->counters[old->generation]--;
if (old->generation < ch->max_generation)
- ch->sum--;
+ {
+ changed = 1;
+ ch->counters[COUNTER]--;
+ }
}
if (new)
{
ch->counters[new->generation]++;
if (new->generation < ch->max_generation)
- ch->sum++;
+ {
+ changed = 1;
+ ch->counters[COUNTER]++;
+ }
}
+
+ if (changed)
+ {
+ log(L_INFO "stats: timer kicked with time %u", ch->settle);
+ stats_kick_timer((struct stats_channel *) ch);
+ }
}
static void
struct stats_channel_config *scc = (void *) cc;
sc->max_generation = scc->max_generation;
+ sc->settle = scc->settle;
}
}
struct stats_channel_config *scc = (void *) cc;
sc->max_generation = scc->max_generation;
+ sc->settle = scc->settle;
/* recalculate sum */
- sc->sum = 0;
+ sc->counters[COUNTER] = 0;
for (u8 i = 0; i < sc->max_generation; i++)
- sc->sum += sc->counters[i];
+ sc->counters[COUNTER] += sc->counters[i];
+
+ sc->sum = sc->counters[COUNTER];
+
+ /* notify all hooked filters */
+ // TODO here
c->stale = 0;
}
cli_msg(-1006, " Channel %s", sc->c.name);
cli_msg(-1006, " Max generation: %3u", sc->max_generation);
- cli_msg(-1006, " Exports: %10u", sc->sum);
+ // FIXME : actual or visible to filters ? AND TIME below in the comment
+ cli_msg(-1006, " Exports: %10u (currently: %10u)",
+ sc->sum,
+ sc->counters[COUNTER]);
+ cli_msg(-1006, " Settle time: %7u s", sc->settle / 1000000 );
cli_msg(-1006, " Counter exported");
for (u8 i = 0; i < len; i++)
}
}
+static void
+stats_timer(timer *t)
+{
+ log(L_INFO "timer executing update");
+ struct stats_channel *c = (struct stats_channel *) t->data;
+
+ /* update the sum correct counter data */
+ c->sum = c->counters[COUNTER];
+
+ /* notify all filters to reevaluate them */
+ // TODO here
+
+}
+
+static void
+stats_kick_timer(struct stats_channel *c)
+{
+
+ /* if set to zero execute immediately */
+ if (!c->settle)
+ stats_timer(c->timer);
+
+ if (!tm_active(c->timer))
+ tm_start(c->timer, c->settle);
+}
+
static int
stats_channel_start(struct channel *C)
{
c->pool = p->p.pool;
+ c->timer = tm_new_init(c->pool, stats_timer, (void *) c, 0, 0);
+
c->counters = mb_allocz(c->pool, 256 * sizeof(u32));
c->sum = 0;
struct stats_channel *c = (void *) C;
mb_free(c->counters);
+
+ /* FIXME freed automatically by the resource pool ?
+ rfree(c->timer);
+ */
c->max_generation = 0;
c->counters = NULL;