diff options
Diffstat (limited to 'src/vty')
-rw-r--r-- | src/vty/Makefile.am | 2 | ||||
-rw-r--r-- | src/vty/stats_vty.c | 430 | ||||
-rw-r--r-- | src/vty/utils.c | 125 |
3 files changed, 545 insertions, 12 deletions
diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index 4225d27d..7c549d91 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -9,7 +9,7 @@ if ENABLE_VTY lib_LTLIBRARIES = libosmovty.la libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \ - telnet_interface.c logging_vty.c + telnet_interface.c logging_vty.c stats_vty.c libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la endif diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c new file mode 100644 index 00000000..98253fff --- /dev/null +++ b/src/vty/stats_vty.c @@ -0,0 +1,430 @@ +/* OpenBSC stats helper for the VTY */ +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2014 by Holger Hans Peter Freyther + * (C) 2015 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdlib.h> +#include <string.h> + +#include "../../config.h" + +#include <osmocom/vty/command.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/core/stats.h> + +#define CFG_STATS_STR "Configure stats sub-system\n" +#define CFG_REPORTER_STR "Configure a stats reporter\n" + +#define SHOW_STATS_STR "Show statistical values\n" + +struct cmd_node cfg_stats_node = { + CFG_STATS_NODE, + "%s(config-stats)# ", + 1 +}; + +static const struct value_string stats_class_strs[] = { + { OSMO_STATS_CLASS_GLOBAL, "global" }, + { OSMO_STATS_CLASS_PEER, "peer" }, + { OSMO_STATS_CLASS_SUBSCRIBER, "subscriber" }, + { 0, NULL } +}; + +static struct osmo_stats_reporter *osmo_stats_vty2srep(struct vty *vty) +{ + if (vty->node == CFG_STATS_NODE) + return vty->index; + + return NULL; +} + +static int set_srep_parameter_str(struct vty *vty, + int (*fun)(struct osmo_stats_reporter *, const char *), + const char *val, const char *param_name) +{ + int rc; + struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); + OSMO_ASSERT(srep); + + rc = fun(srep, val); + if (rc < 0) { + vty_out(vty, "%% Unable to set %s: %s%s", + param_name, strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static int set_srep_parameter_int(struct vty *vty, + int (*fun)(struct osmo_stats_reporter *, int), + const char *val, const char *param_name) +{ + int rc; + int int_val; + struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); + OSMO_ASSERT(srep); + + int_val = atoi(val); + + rc = fun(srep, int_val); + if (rc < 0) { + vty_out(vty, "%% Unable to set %s: %s%s", + param_name, strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_stats_reporter_local_ip, cfg_stats_reporter_local_ip_cmd, + "local-ip ADDR", + "Set the IP address to which we bind locally\n" + "IP Address\n") +{ + return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr, + argv[0], "local address"); +} + +DEFUN(cfg_no_stats_reporter_local_ip, cfg_no_stats_reporter_local_ip_cmd, + "no local-ip", + NO_STR + "Set the IP address to which we bind locally\n") +{ + return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr, + NULL, "local address"); +} + +DEFUN(cfg_stats_reporter_remote_ip, cfg_stats_reporter_remote_ip_cmd, + "remote-ip ADDR", + "Set the remote IP address to which we connect\n" + "IP Address\n") +{ + return set_srep_parameter_str(vty, osmo_stats_reporter_set_remote_addr, + argv[0], "remote address"); +} + +DEFUN(cfg_stats_reporter_remote_port, cfg_stats_reporter_remote_port_cmd, + "remote-port <1-65535>", + "Set the remote port to which we connect\n" + "Remote port number\n") +{ + return set_srep_parameter_int(vty, osmo_stats_reporter_set_remote_port, + argv[0], "remote port"); +} + +DEFUN(cfg_stats_reporter_mtu, cfg_stats_reporter_mtu_cmd, + "mtu <100-65535>", + "Set the maximum packet size\n" + "Size in byte\n") +{ + return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu, + argv[0], "mtu"); +} + +DEFUN(cfg_no_stats_reporter_mtu, cfg_no_stats_reporter_mtu_cmd, + "no mtu", + NO_STR "Set the maximum packet size\n") +{ + return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu, + "0", "mtu"); +} + +DEFUN(cfg_stats_reporter_prefix, cfg_stats_reporter_prefix_cmd, + "prefix PREFIX", + "Set the item name prefix\n" + "The prefix string\n") +{ + return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix, + argv[0], "prefix string"); +} + +DEFUN(cfg_no_stats_reporter_prefix, cfg_no_stats_reporter_prefix_cmd, + "no prefix", + NO_STR + "Set the item name prefix\n") +{ + return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix, + "", "prefix string"); +} + +DEFUN(cfg_stats_reporter_level, cfg_stats_reporter_level_cmd, + "level (global|peer|subscriber)", + "Set the maximum group level\n" + "Report global groups only\n" + "Report global and network peer related groups\n" + "Report global, peer, and subscriber groups\n") +{ + int level = get_string_value(stats_class_strs, argv[0]); + int rc; + struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); + + OSMO_ASSERT(srep); + rc = osmo_stats_reporter_set_max_class(srep, level); + if (rc < 0) { + vty_out(vty, "%% Unable to set level: %s%s", + strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + + return 0; +} + +DEFUN(cfg_stats_reporter_enable, cfg_stats_reporter_enable_cmd, + "enable", + "Enable the reporter\n") +{ + int rc; + struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); + OSMO_ASSERT(srep); + + rc = osmo_stats_reporter_enable(srep); + if (rc < 0) { + vty_out(vty, "%% Unable to enable the reporter: %s%s", + strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_stats_reporter_disable, cfg_stats_reporter_disable_cmd, + "disable", + "Disable the reporter\n") +{ + int rc; + struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); + OSMO_ASSERT(srep); + + rc = osmo_stats_reporter_disable(srep); + if (rc < 0) { + vty_out(vty, "%% Unable to disable the reporter: %s%s", + strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd, + "stats reporter statsd", + CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n") +{ + struct osmo_stats_reporter *srep; + + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); + if (!srep) { + srep = osmo_stats_reporter_create_statsd(NULL); + if (!srep) { + vty_out(vty, "%% Unable to create statsd reporter%s", + VTY_NEWLINE); + return CMD_WARNING; + } + srep->max_class = OSMO_STATS_CLASS_GLOBAL; + /* TODO: if needed, add osmo_stats_add_reporter(srep); */ + } + + vty->index = srep; + vty->node = CFG_STATS_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_stats_interval, cfg_stats_interval_cmd, + "stats interval <1-65535>", + CFG_STATS_STR "Set the reporting interval\n" + "Interval in seconds\n") +{ + int rc; + int interval = atoi(argv[0]); + rc = osmo_stats_set_interval(interval); + if (rc < 0) { + vty_out(vty, "%% Unable to set interval: %s%s", + strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + + +DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd, + "no stats reporter statsd", + NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n") +{ + struct osmo_stats_reporter *srep; + + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); + if (!srep) { + vty_out(vty, "%% No statsd logging active%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + osmo_stats_reporter_free(srep); + + return CMD_SUCCESS; +} + +DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd, + "stats reporter log", + CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n") +{ + struct osmo_stats_reporter *srep; + + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); + if (!srep) { + srep = osmo_stats_reporter_create_log(NULL); + if (!srep) { + vty_out(vty, "%% Unable to create log reporter%s", + VTY_NEWLINE); + return CMD_WARNING; + } + srep->max_class = OSMO_STATS_CLASS_GLOBAL; + /* TODO: if needed, add osmo_stats_add_reporter(srep); */ + } + + vty->index = srep; + vty->node = CFG_STATS_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd, + "no stats reporter log", + NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n") +{ + struct osmo_stats_reporter *srep; + + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); + if (!srep) { + vty_out(vty, "%% No log reporting active%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + osmo_stats_reporter_free(srep); + + return CMD_SUCCESS; +} + +DEFUN(show_stats, + show_stats_cmd, + "show stats", + SHOW_STR SHOW_STATS_STR) +{ + vty_out_statistics_full(vty, ""); + + return CMD_SUCCESS; +} + +static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_reporter *srep) +{ + if (srep == NULL) + return 0; + + switch (srep->type) { + case OSMO_STATS_REPORTER_STATSD: + vty_out(vty, "stats reporter statsd%s", VTY_NEWLINE); + break; + case OSMO_STATS_REPORTER_LOG: + vty_out(vty, "stats reporter log%s", VTY_NEWLINE); + break; + } + + vty_out(vty, " disable%s", VTY_NEWLINE); + + if (srep->have_net_config) { + if (srep->dest_addr_str) + vty_out(vty, " remote-ip %s%s", + srep->dest_addr_str, VTY_NEWLINE); + if (srep->dest_port) + vty_out(vty, " remote-port %d%s", + srep->dest_port, VTY_NEWLINE); + if (srep->bind_addr_str) + vty_out(vty, " local-ip %s%s", + srep->bind_addr_str, VTY_NEWLINE); + if (srep->mtu) + vty_out(vty, " mtu %d%s", + srep->mtu, VTY_NEWLINE); + } + + if (srep->max_class) + vty_out(vty, " level %s%s", + get_value_string(stats_class_strs, srep->max_class), + VTY_NEWLINE); + + if (srep->name_prefix && *srep->name_prefix) + vty_out(vty, " prefix %s%s", + srep->name_prefix, VTY_NEWLINE); + else + vty_out(vty, " no prefix%s", VTY_NEWLINE); + + if (srep->enabled) + vty_out(vty, " enable%s", VTY_NEWLINE); + + return 1; +} + +static int config_write_stats(struct vty *vty) +{ + struct osmo_stats_reporter *srep; + + /* TODO: loop through all reporters */ + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); + config_write_stats_reporter(vty, srep); + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); + config_write_stats_reporter(vty, srep); + + vty_out(vty, "stats interval %d%s", osmo_stats_config->interval, VTY_NEWLINE); + + return 1; +} + +void osmo_stats_vty_add_cmds() +{ + install_element_ve(&show_stats_cmd); + + install_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd); + install_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd); + install_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd); + install_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd); + install_element(CONFIG_NODE, &cfg_stats_interval_cmd); + + install_node(&cfg_stats_node, config_write_stats); + vty_install_default(CFG_STATS_NODE); + + install_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd); + install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd); + install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd); + install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd); + install_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd); + install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd); + install_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd); + install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd); + install_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd); + install_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd); + install_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd); +} diff --git a/src/vty/utils.c b/src/vty/utils.c index d0ad431d..8df44ae1 100644 --- a/src/vty/utils.c +++ b/src/vty/utils.c @@ -29,7 +29,9 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/timer.h> #include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stat_item.h> #include <osmocom/core/utils.h> +#include <osmocom/core/statistics.h> #include <osmocom/vty/vty.h> @@ -39,6 +41,30 @@ * @{ */ +struct vty_out_context { + struct vty *vty; + const char *prefix; +}; + +static int rate_ctr_handler( + struct rate_ctr_group *ctrg, struct rate_ctr *ctr, + const struct rate_ctr_desc *desc, void *vctx_) +{ + struct vty_out_context *vctx = vctx_; + struct vty *vty = vctx->vty; + + vty_out(vty, " %s%s: %8" PRIu64 " " + "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s", + vctx->prefix, desc->description, ctr->current, + ctr->intv[RATE_CTR_INTV_SEC].rate, + ctr->intv[RATE_CTR_INTV_MIN].rate, + ctr->intv[RATE_CTR_INTV_HOUR].rate, + ctr->intv[RATE_CTR_INTV_DAY].rate, + VTY_NEWLINE); + + return 0; +} + /*! \brief print a rate counter group to given VTY * \param[in] vty The VTY to which it should be printed * \param[in] prefix Any additional log prefix ahead of each line @@ -47,20 +73,97 @@ void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, struct rate_ctr_group *ctrg) { - unsigned int i; + struct vty_out_context vctx = {vty, prefix}; vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE); - for (i = 0; i < ctrg->desc->num_ctr; i++) { - struct rate_ctr *ctr = &ctrg->ctr[i]; - vty_out(vty, " %s%s: %8" PRIu64 " " - "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s", - prefix, ctrg->desc->ctr_desc[i].description, ctr->current, - ctr->intv[RATE_CTR_INTV_SEC].rate, - ctr->intv[RATE_CTR_INTV_MIN].rate, - ctr->intv[RATE_CTR_INTV_HOUR].rate, - ctr->intv[RATE_CTR_INTV_DAY].rate, + + rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx); +} + +static int osmo_stat_item_handler( + struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *vctx_) +{ + struct vty_out_context *vctx = vctx_; + struct vty *vty = vctx->vty; + + vty_out(vty, " %s%s: %8" PRIi32 " %s%s", + vctx->prefix, item->desc->description, + osmo_stat_item_get_last(item), + item->desc->unit, VTY_NEWLINE); + + return 0; +} + +/*! \brief print a stat item group to given VTY + * \param[in] vty The VTY to which it should be printed + * \param[in] prefix Any additional log prefix ahead of each line + * \param[in] statg Stat item group to be printed + */ +void vty_out_stat_item_group(struct vty *vty, const char *prefix, + struct osmo_stat_item_group *statg) +{ + struct vty_out_context vctx = {vty, prefix}; + + vty_out(vty, "%s%s:%s", prefix, statg->desc->group_description, + VTY_NEWLINE); + osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, &vctx); +} + +static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_) +{ + struct vty_out_context *vctx = vctx_; + struct vty *vty = vctx->vty; + + if (statg->idx) + vty_out(vty, "%s%s (%d):%s", vctx->prefix, + statg->desc->group_description, statg->idx, VTY_NEWLINE); - }; + else + vty_out(vty, "%s%s:%s", vctx->prefix, + statg->desc->group_description, VTY_NEWLINE); + + osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx); + + return 0; +} + +static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_) +{ + struct vty_out_context *vctx = vctx_; + struct vty *vty = vctx->vty; + + if (ctrg->idx) + vty_out(vty, "%s%s (%d):%s", vctx->prefix, + ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE); + else + vty_out(vty, "%s%s:%s", vctx->prefix, + ctrg->desc->group_description, VTY_NEWLINE); + + rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx); + + return 0; +} + +static int handle_counter(struct osmo_counter *counter, void *vctx_) +{ + struct vty_out_context *vctx = vctx_; + struct vty *vty = vctx->vty; + + vty_out(vty, " %s%s: %8lu%s", + vctx->prefix, counter->description, + osmo_counter_get(counter), VTY_NEWLINE); + + return 0; +} + +void vty_out_statistics_full(struct vty *vty, const char *prefix) +{ + struct vty_out_context vctx = {vty, prefix}; + + vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE); + osmo_counters_for_each(handle_counter, &vctx); + rate_ctr_for_each_group(rate_ctr_group_handler, &vctx); + osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx); } /*! \brief Generate a VTY command string from value_string */ |