Adding New Metrics ################## Scripts allow users to create their own scripts and `custom pages`_ as long as long as defining their own `timeseries schemas`_. This is often enough to add custom metrics and visualize them. Some user however need to export internal metrics from the ntopng core. The information that follow should give a good starting point to do this. .. _`custom pages`: ../../scripts/custom_pages.html .. _`timeseries schemas`: ../../scripts/timeseries_schemas.html General Overview ================ Traffic elements (such as local hosts and interfaces) are iterated periodically and by some Lua scripts and their statistics are dumped in the form of timeseries. Traffic elements are handled in some standard ways: 1. Most traffic elements are implemented in C, and their statistics are passed to Lua via the :code:`::lua` method. For example, :code:`AutonomousSystem::lua` dumps the autonomous system statistics to Lua. *Important* if the element has a `::tsLua` method check out the case 2 below. 2. Some other traffic elements are implemented in C, but their statistics are hold on a :code:`TimeseriesPoint` rather then the element itself. For example, the local hosts data is stored into the :code:`HostTimeseriesPoint` class. In order to add new timeseries for a local host, the :code:`HostTimeseriesPoint` is the class to modify (and related :code:`::lua` method). 3. Some traffic elements are implemented in Lua. Their state is stored in Redis usually in JSON form. This includes, for example, the SNMP devices. In order to add a custom timeseries it's necessary to identify the correct case above. It's also important to note that not all the traffic elements can be exported. Remote Hosts and Flows timeseries, for example, cannot be exported anyway due to the design of ntopng. Case 1: Adding metrics via the `::lua` method --------------------------------------------- The new metric should be exposed into the :code:`::lua` method. Then, you can simply add the metric to the `custom timeseries scripts`_. For example, the autonomous systems `num_hosts` field, exposed into the :code:`AutonomousSystem::lua` method, can be written as a timeseries in this way. Please note that if the metric is already exposed into the :code:`::lua` method, you can keep compatibility with the standard ntopng and update it normally (no ntopng fork needed). Case 2: Adding metrics inside a `TimeseriesPoint` ------------------------------------------------- In order to add a new metric to a LocalHost or NetworkInterface, the corresponding :code:`TimeseriesPoint` should be modified instead: - For LocalHost, modify the :code:`HostTimeseriesPoint` - For NetworkInterface, modify the :code:`NetworkInterfaceTsPoint` Things to keep in mind: - The new metric should be added to the header file (e.g. `HostTimeseriesPoint.h`). - The metric should be written to the :code:`TimeseriesPoint`, (e.g. in :code:`LocalHostStats::makeTsPoint`) - The metric should be exposed to Lua in the :code:`TimeseriesPoint:lua` method (e.g. in :code:`HostTimeseriesPoint::lua`) After this, the metric should now be available in Lua. Use the `custom timeseries scripts`_ to export it as a timeseries. Since this requires modifications of the C source code, compatibility with the standard ntopng cannot be preserved. Case 3: Adding metrics for Lua only objects ------------------------------------------- This really depends on the specific element to be added. Compatibility may or may not be assured. .. _`custom timeseries scripts`: #custom-timeseries-scripts Custom Timeseries Scripts ========================= Once the new metrics are available in Lua via one of the methods discussed above, it's necessary to export such metrics as timeseries. In order to do so, two actions are required: - The metric format should be declared in a timeseries schema - The metric should be written to the timeseries driver Both actions can be implemented inside the custom timeseries scripts. ntopng handles custom timeseries with updates every: - 1 minute for interfaces - 5 minutes for local hosts This means that custom timeseries with a point every minute and a point every 5 minutes can be generated for interfaces and local hosts, respectively. ntopng looks for custom timeseries in the following Lua files under :code:`scripts/lua/modules/timeseries/custom/`: - :code:`ts_minute_custom.lua` for local hosts timeseries with 5-minute updates - :code:`ts_5min_custom.lua` for interface timeseries with 1-minute updates If file :code:`ts_5min_custom.lua` does not exist, ntopng will skip the creation of custom timeseries with 5-minute updates. Similarly, if file :code:`ts_minute_custom.lua` does not exist, ntopng will skip the creation of custom timeseries with 1-minute updates. Sample files :code:`ts_5min_custom.lua.sample` and :code:`ts_minute_custom.lua.sample` are created automatically upon ntopng installation with some example contents. Those files are ignored by ntopng. However, it is safe to copy them to :code:`ts_5min_custom.lua` and :code:`ts_minute_custom.lua` and modify the copies when it is necessary to add custom timeseries. Structure of Custom Timeseries Scripts -------------------------------------- Every custom file must contain a method :code:`setup` which defines one or more schemas. Every custom timeseries *needs* a schema to function. A schema defines the timeseries in terms of tags and metrics. The documentation describes what is a schema in detail. Later in this section an example schema will be shown. File :code:`ts_5min_custom.lua` must contain a callback :code:`ts_custom.host_update_stats` which is called by ntopng every 5 minutes for every *active* local host. This callback accepts the following arguments: - :code:`when` The time (expressed as a Unix Epoch) of the call - :code:`hostname` The IP address of the host, possibly followed by a VLAN tag - :code:`host` The host metrics in a Lua table - :code:`ifstats` The interface stats of the host interface - :code:`verbose` and extra flag passed when ntopng is working in verbose mode File :code:`ts_minute_custom.lua` must contain a callback :code:`ts_custom.iface_update_stats` which is called by ntopng every minute for every monitored interface. This callback accepts the following arguments: - :code:`when` The time (expressed as a Unix Epoch) of the call - :code:`_ifname` The name of the monitored interface - :code:`ifstats` The interface stats of the monitored interface - :code:`verbose` and extra flag passed when ntopng is working in verbose mode Callbacks can be used to append points to the timeseries. Indeed, once the schema is defined, it is necessary to :code:`append` points to the timeseries. The function used to append points to the timeseries is the :code:`ts_utils.append` documented later in this section of the documentation. Example ------- Let's see how to add an interface timeseries that counts the number of issues detected when analyzing sequence numbers. The total issues detected when analyzing sequence numbers is considered as the sum of TCP retransmitted, out-of-order and lost packets. The first thing to do is to add a schema to the :code:`setup` function of :code:`ts_minute_custom.lua`. The schema is created as as follows: .. code-block:: lua schema = ts_utils.newSchema("iface:tcp_seq_errors", {step = 60}) schema:addTag("ifid") schema:addMetric("packets") The first argument of :code:`newSchema` specifies the timeseries name :code:`"iface:tcp_seq_errors"`. Timeseries interfaces *must* start with prefix :code:`iface:`. The second argument is a table that *must* contain argument :code:`step` which tells how frequently the timeseries will be updated. As we are in the 1-minute local hosts timeseries Lua script, a value of :code:`60` must be specified here. Then, function :code:`addTag` is used to indicate an interface id :code:`ifid` that will be used to uniquely identify the timeseries when multiple interfaces are monitored. Finally, :code:`addMetric` is called with an argument :code:`packets` to indicate the metric name. Note that both the :code:`ifid` and :code:`packets` are just plain strings here, their actual values will be set in the :code:`ts_custom.iface_update_stats` when updating the timeseries with new points. The number of issues detected when analyzing sequence numbers is a *counter*, that is, is an always-increasing function of time. By default, schemas consider metrics as counters so there is no need to specify this type upon schema addition. For *gauges*, one has to indicate an extra :code:`metrics_type` in the table containing the :code:`step`. So for example, to create a 1-minute timeseries for the number of active flows of a given host, one can use the following syntax :code:`ts_utils.newSchema("host:flows", {step=60, metrics_type=ts_utils.metrics.gauge})`. To update the timeseries with new points, callback :code:`ts_custom.iface_update_stats` is extended with a :code:`ts_utils.append` call as follows. .. code-block:: lua ts_utils.append("iface:tcp_seq_errors", {ifid = ifstats.id, packets = ifstats.tcpPacketStats.retransmissions + ifstats.tcpPacketStats.out_of_order + ifstats.tcpPacketStats.lost}, when, verbose) The first argument of :code:`ts_utils.append` is the timeseries name and *must* be equal to the one specified when defining the schema. The second argument is a table which *must* contain the tag (:code:`ifid`) and the metric (:code:`packets`) which must be set to their actual values. As it can be seen from the example above, the field :code:`id` of table :code:`ifstats` is used to set tag :code:`ifid`, whereas the sum of :code:`ifstats.tcpPacketStats` table fields :code:`retransmissions`, :code:`out_of_order` and :code:`lost` are used as value for the metric :code:`packets`. Finally, the third argument :code:`when` is the time of the call, and the latest argument :code:`verbose` indicates whether ntopng is operating in verbose mode. From that point on, the timeseries will be consistently updated by ntopng. Multiple schemas and multiple :code:`ts_utils.append` can be added in the same file. The full example can be seen at: https://github.com/ntop/ntopng/blob/dev/scripts/lua/modules/timeseries/custom/ts_minute_custom.lua.sample Another example that creates 5-minute timeseries of local hosts total bytes can be seen at https://github.com/ntop/ntopng/blob/dev/scripts/lua/modules/timeseries/custom/ts_5min_custom.lua.sample Charting New Metrics ==================== After exporting the new metrics to the timeseries driver (e.g. InfluxDB), the generated timeseries can be charted inside the traffic element page. The particular script to modify depends on the specific traffic element, here are some examples: - For local hosts, modify `host_details.lua` - For network interfaces, modify `if_stats.lua` The script should contain a call to :code:`graph_utils.drawGraphs` with a :code:`timeseries` field. The new timeseries should be added to it. Here is for example a modified host_stats.lua with a new `host:low_goodput_flows` metric: .. code:: lua graph_utils.drawGraphs(ifId, schema, tags, _GET["zoom"], url, selected_epoch, { top_protocols = "top:host:ndpi", ... timeseries = { {schema="host:traffic", label=i18n("traffic")}, {schema="host:flows", label=i18n("graphs.active_flows")}, {schema="host:misbehaving_flows", label=i18n("graphs.total_misbehaving_flows")}, -- The new metric is added here in order to be shown into the charts {schema="host:low_goodput_flows", label="Low Goodput Flows"}, ... } }) The metric will appear with the "Low Goodput Flows" into the timeseries dropdown after the timeseries points are available.