/* * BIRD -- Statistics Protocol * * (c) 2022 Vojtech Vilimek * (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); }