mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-08 01:51:54 +00:00
326 lines
7.0 KiB
C
326 lines
7.0 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 */
|
|
log(L_INFO "nothing happen - no change of counter");
|
|
else if (!old)
|
|
{
|
|
log(L_INFO "increasing _counter");
|
|
ch->_counter++;
|
|
changed = 1;
|
|
if (ch->_counter > 100)
|
|
log(L_INFO "underflow?");
|
|
}
|
|
else if (!new)
|
|
{
|
|
log(L_INFO "decreasing _counter");
|
|
ch->_counter--;
|
|
changed = 1;
|
|
if (ch->_counter > 100)
|
|
log(L_INFO "underflow? - ");
|
|
}
|
|
else /* shouldn't happen */
|
|
{
|
|
log(L_INFO "BUG is here !!!");
|
|
bug("Both pointers *new and *old in rt_notify are NULL");
|
|
}
|
|
|
|
log(L_INFO "stats channel %s: preparing to kick the timer %d", src_ch->name,
|
|
changed);
|
|
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)
|
|
{
|
|
log(L_INFO "stats_init() ");
|
|
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 struct settle_timer_class stats_settle_class = {
|
|
.action = stats_settle_timer,
|
|
.kick = NULL,
|
|
};
|
|
|
|
static void
|
|
stats_configure_channels(struct proto *P, struct proto_config *CF)
|
|
{
|
|
log(L_INFO "stats_configure_channels()");
|
|
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)
|
|
{
|
|
log(L_INFO "stats_start() ");
|
|
stats_configure_channels(P, P->cf);
|
|
|
|
struct stats_term_config *tc;
|
|
WALK_LIST(tc, ((struct stats_config *) P->cf)->terms)
|
|
{
|
|
log(L_INFO "term %s", tc->name);
|
|
f_eval(tc->code, &tc->val);
|
|
log(L_INFO "content: %s, matches %s", val_dump(&tc->val),
|
|
tc->type == tc->val.type ? "yes" : "no");
|
|
}
|
|
|
|
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:");
|
|
cli_msg(-1006, "terms list: %p", ((struct stats_config *) p->c)->terms);
|
|
}
|
|
|
|
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)
|
|
{
|
|
timer *t = (void *) st;
|
|
struct stats_channel *c = t->data;
|
|
log(L_INFO "stats_settle_timer() _counter: %u, counter: %u",
|
|
c->_counter, c->counter);
|
|
|
|
/* 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;
|
|
|
|
if (!c->settle_timer)
|
|
c->settle_timer = stm_new_timer(
|
|
c->pool, (void *) c, &stats_settle_class);
|
|
|
|
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)
|
|
{
|
|
log(L_INFO "stats_channel_shutdown()");
|
|
struct stats_channel *c = (void *) C;
|
|
|
|
tm_stop((timer *) c->settle_timer);
|
|
|
|
c->settle_timer->min_settle_time = NULL;
|
|
c->settle_timer->max_settle_time = NULL;
|
|
|
|
mb_free(c->settle_timer);
|
|
c->settle_timer = NULL;
|
|
|
|
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;
|
|
}
|
|
|
|
struct f_val
|
|
stats_eval_term(struct stats_term_config *tc)
|
|
{
|
|
log(L_INFO "stats_eval_term() evaluating value of %s",
|
|
tc->name);
|
|
enum filter_return fret = f_eval(tc->code, &tc->val);
|
|
|
|
if (fret > F_RETURN)
|
|
tc->val.type = T_VOID;
|
|
|
|
if (tc->type != tc->val.type)
|
|
tc->val.type = T_VOID;
|
|
|
|
log(L_INFO " stats_eval_term() returning %s", val_dump(&tc->val));
|
|
return tc->val;
|
|
}
|
|
|
|
int
|
|
stats_get_type(struct stats_term_config *tc)
|
|
{
|
|
log(L_INFO "stats_get_type()");
|
|
return tc->type;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|