mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-23 10:11:53 +00:00
d648c6b602
Add new global counter for stats channel New symbol is added for each stats protocol channel, the symbol is accessed by same name as the underlying channel. Symbols evaluate to the sum of all routes exported from connected table with generation less than max generation of particular channel. Default max generation is 16. Beware you shouldn't make cyclic references as the behavior of such configuration is not defined!
281 lines
5.6 KiB
C
281 lines
5.6 KiB
C
/*
|
|
* BIRD -- Statistics Protocol
|
|
*
|
|
* (c) 2022 Vojtech Vilimek <vojtech.vilimek@nic.cz>
|
|
* (c) 2022 CZ.NIC z.s.p.o.
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
/**
|
|
* DOC: Stats
|
|
*
|
|
*/
|
|
|
|
#undef LOCAL_DEBUG
|
|
|
|
#include "nest/bird.h"
|
|
#include "nest/iface.h"
|
|
#include "nest/protocol.h"
|
|
#include "nest/rt.h"
|
|
#include "nest/cli.h"
|
|
#include "conf/conf.h"
|
|
#include "filter/filter.h"
|
|
#include "lib/string.h"
|
|
#include "lib/timer.h"
|
|
|
|
#include "stats.h"
|
|
|
|
static void stats_settle_timer(struct settle_timer *st);
|
|
|
|
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 (new && old)
|
|
/* count of exported routes stays the same */
|
|
;
|
|
else if (!old)
|
|
{
|
|
ch->_counter++;
|
|
changed = 1;
|
|
}
|
|
else if (!new)
|
|
{
|
|
ch->_counter--;
|
|
changed = 1;
|
|
}
|
|
else /* shouldn't happen */
|
|
{
|
|
bug("Both pointers *new and *old in rt_notify are NULL");
|
|
}
|
|
|
|
if (changed)
|
|
{
|
|
settle_timer_changed(&ch->settle_timer);
|
|
kick_settle_timer(&ch->settle_timer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
stats_reload_routes(struct channel *C)
|
|
{
|
|
// TODO
|
|
struct stats_channel *c = (void *) C;
|
|
|
|
c->_counter = c->counter = 0;
|
|
channel_request_feeding(C);
|
|
}
|
|
|
|
static struct proto *
|
|
stats_init(struct proto_config *CF)
|
|
{
|
|
struct proto *P = proto_new(CF);
|
|
struct stats_proto *p = (void *) P;
|
|
|
|
P->rt_notify = stats_rt_notify;
|
|
P->reload_routes = stats_reload_routes;
|
|
|
|
p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
|
|
|
|
return P;
|
|
}
|
|
|
|
static void
|
|
stats_configure_channels(struct proto *P, struct proto_config *CF)
|
|
{
|
|
struct channel_config *cc;
|
|
WALK_LIST(cc, CF->channels)
|
|
{
|
|
struct channel *c = proto_find_channel_by_name(P, cc->name);
|
|
proto_configure_channel(P, &c, cc);
|
|
}
|
|
}
|
|
|
|
static int
|
|
stats_start(struct proto *P)
|
|
{
|
|
stats_configure_channels(P, P->cf);
|
|
|
|
/* evaluate terms on protocol start */
|
|
struct stats_term_config *tc;
|
|
WALK_LIST(tc, ((struct stats_config *) P->cf)->terms)
|
|
{
|
|
stats_eval_term(tc);
|
|
}
|
|
|
|
return PS_UP;
|
|
}
|
|
|
|
static int
|
|
stats_reconfigure(struct proto *P, struct proto_config *CF)
|
|
{
|
|
struct stats_proto *p = (void *) P;
|
|
struct stats_config *new = (void *) CF;
|
|
|
|
struct channel *c;
|
|
WALK_LIST(c, p->p.channels)
|
|
c->stale = 1;
|
|
|
|
struct channel_config *cc;
|
|
WALK_LIST(cc, new->c.channels)
|
|
{
|
|
c = proto_find_channel_by_name(P, cc->name);
|
|
if (!proto_configure_channel(P, &c, cc))
|
|
return 0;
|
|
|
|
if (c)
|
|
{
|
|
struct stats_channel *sc = (void *) c;
|
|
struct stats_channel_config *scc = (void *) cc;
|
|
|
|
sc->settle_timer.min_settle_time = scc->min_settle_time;
|
|
sc->settle_timer.max_settle_time = scc->max_settle_time;
|
|
|
|
if (sc->counter != sc->_counter)
|
|
{
|
|
sc->counter = sc->_counter;
|
|
|
|
/* notify all hooked filters */
|
|
// TODO here
|
|
}
|
|
|
|
c->stale = 0;
|
|
}
|
|
}
|
|
|
|
struct channel *c2;
|
|
WALK_LIST_DELSAFE(c, c2, p->p.channels)
|
|
if (c->stale && !proto_configure_channel(P, &c, NULL))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
stats_show_proto_info(struct proto *P)
|
|
{
|
|
struct stats_proto *p = (void *) P;
|
|
|
|
struct stats_channel *sc;
|
|
WALK_LIST(sc, p->p.channels)
|
|
{
|
|
cli_msg(-1006, " Channel %s", sc->c.name);
|
|
cli_msg(-1006, " Exports: %10u (currently: %10u)",
|
|
sc->counter,
|
|
sc->_counter);
|
|
if (!P->disabled)
|
|
{
|
|
cli_msg(-1006, " Settle time: %4u s", sc->settle_timer.min_settle_time TO_S);
|
|
cli_msg(-1006, " Settle time: %4u s", sc->settle_timer.max_settle_time TO_S);
|
|
}
|
|
}
|
|
|
|
cli_msg(-1006, " Terms:");
|
|
|
|
struct stats_term_config *tc;
|
|
WALK_LIST(tc, ((struct stats_config *) P->cf)->terms)
|
|
{
|
|
stats_eval_term(tc);
|
|
cli_msg(-1006, " %s = %s", tc->name, val_dump(tc->val));
|
|
}
|
|
}
|
|
|
|
void
|
|
stats_update_debug(struct proto *P)
|
|
{
|
|
struct channel *c;
|
|
WALK_LIST(c, P->channels)
|
|
{
|
|
c->debug = P->debug;
|
|
}
|
|
}
|
|
|
|
static void
|
|
stats_settle_timer(struct settle_timer *st)
|
|
{
|
|
struct stats_channel *c = st->settle_data;
|
|
|
|
/* update only if real change happen */
|
|
if (c->counter != c->_counter)
|
|
{
|
|
c->counter = c->_counter;
|
|
/* do update here */
|
|
// WALK_LIST(s, subscribers)
|
|
// { ... }
|
|
}
|
|
}
|
|
|
|
static int
|
|
stats_channel_start(struct channel *C)
|
|
{
|
|
struct stats_channel *c = (void *) C;
|
|
struct stats_channel_config *cc = (void *) C->config;
|
|
struct stats_proto *p = (void *) C->proto;
|
|
|
|
c->pool = p->p.pool;
|
|
|
|
stm_init(&c->settle_timer, c->pool, (void *)c, stats_settle_timer);
|
|
|
|
c->settle_timer.min_settle_time = cc->min_settle_time;
|
|
c->settle_timer.max_settle_time = cc->max_settle_time;
|
|
|
|
c->_counter = 0;
|
|
c->counter = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
stats_channel_shutdown(struct channel *C)
|
|
{
|
|
struct stats_channel *c = (void *) C;
|
|
|
|
tm_stop(c->settle_timer.t);
|
|
|
|
c->_counter = 0;
|
|
c->counter = 0;
|
|
c->pool = NULL;
|
|
}
|
|
|
|
int
|
|
stats_get_counter(struct symbol *sym)
|
|
{
|
|
if (sym->ch_config->channel)
|
|
return (int) ((struct stats_channel *) sym->ch_config->channel)->counter;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void stats_eval_term(struct stats_term_config *tc)
|
|
{
|
|
f_eval(tc->code, tc->val);
|
|
}
|
|
|
|
struct channel_class channel_stats = {
|
|
.channel_size = sizeof(struct stats_channel),
|
|
.config_size = sizeof(struct stats_channel_config),
|
|
.start = stats_channel_start,
|
|
.shutdown = stats_channel_shutdown,
|
|
};
|
|
|
|
struct protocol proto_stats = {
|
|
.name = "Stats",
|
|
.template = "stat%d",
|
|
.channel_mask = NB_ANY,
|
|
.proto_size = sizeof(struct stats_proto),
|
|
.config_size = sizeof(struct stats_config),
|
|
.init = stats_init,
|
|
.start = stats_start,
|
|
.reconfigure = stats_reconfigure,
|
|
.show_proto_info = stats_show_proto_info
|
|
};
|
|
|
|
void
|
|
stats_build(void)
|
|
{
|
|
proto_build(&proto_stats);
|
|
}
|