0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-23 10:11:53 +00:00
bird/proto/stats/stats.c
Vojtech Vilimek d648c6b602 Protocol stats: a WIP implementation of conditional routes
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!
2023-09-26 10:42:28 +02:00

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);
}