diff --git a/doc/Extensions/NFSen.md b/doc/Extensions/NFSen.md index acf6f44618..f2698e746b 100644 --- a/doc/Extensions/NFSen.md +++ b/doc/Extensions/NFSen.md @@ -1,10 +1,11 @@ source: Extensions/NFSen.md path: blob/master/doc/ + # NFSen > The installation of NFSen is out of scope for this document / LibreNMS -#### Configuration +## Configuration The following is the configuration that can be used: @@ -13,43 +14,184 @@ $config['nfsen_enable'] = 1; $config['nfsen_split_char'] = '_'; $config['nfsen_rrds'][] = '/var/nfsen/profiles-stat/live/'; $config['nfsen_rrds'][] = '/var/nfsen/profiles-stat'; +$config['nfsen_base'][] = '/var/nfsen/'; $config['nfsen_suffix'] = "_yourdomain_com"; ``` Set `$config['nfsen_enable'] = 1;` to enable NFSen support. -`$config['nfsen_rrds']` This value tells us where your NFSen rrd files live. This can also be an array to -specify more directories like: +`$config['nfsen_rrds']` This value tells us where your NFSen rrd files +live. This can also be an array to specify more directories like: ```php $config['nfsen_rrds'][] = '/var/nfsen/profiles-stat/sitea/'; $config['nfsen_rrds'][] = '/var/nfsen/profiles-stat/siteb/'; ``` -Although for most setups, it will look like below, with the profiles-stat/live directory being where it stores the general RRDs for data sources. +Although for most setups, it will look like below, with the +profiles-stat/live directory being where it stores the general RRDs +for data sources. ```php $config['nfsen_rrds'][] = '/var/nfsen/profiles-stat/live'; ``` -`$config['nfsen_split_char']` This value tells us what to replace the full stops `.` in the devices hostname with. - -`$config['nfsen_suffix']` This value will be removed from the domain name and can be useful if your rrd files are -something like `host1.rrd` but your device hostname is `domain.host1.rrd`. You can then set $config['nfsen_suffix'] = 'domain'; - -If you wish to render info for configure channels for a device, you need add the various profile-stat directories your system uses, which for most systems will be as below. +If you wish to render info for configure channels for a device, you +need add the various profile-stat directories your system uses, which +for most systems will be as below. ```php $config['nfsen_rrds'][] = '/var/nfsen/profiles-stat'; ``` -When adding sources to nfsen.conf, it is important to use the hostname that matches what is configured in LibreNMS, because the rrd files NfSen creates is named after the source name (ident), and it doesn't allow you to use an IP address instead. However, in LibreNMS, if your device is added by an IP address, add your source with any name of your choice, and create a symbolic link to the rrd file. +When adding sources to nfsen.conf, it is important to use the hostname +that matches what is configured in LibreNMS, because the rrd files +NfSen creates is named after the source name (ident), and it doesn't +allow you to use an IP address instead. However, in LibreNMS, if your +device is added by an IP address, add your source with any name of +your choice, and create a symbolic link to the rrd file. + ```sh cd /var/nfsen/profiles-stat/sitea/ ln -s mychannel.rrd librenmsdeviceIP.rrd ``` -When creating profiles under nfsen, be sure to use the hostname so it matches the name in LibreNMS. That is where channel data will be pulled from. +```php +$config['nfsen_split_char'] = '_'; +``` -You should see a new tab for the device called Nfsen. +This value tells us what to replace the full stops `.` in the devices +hostname with. + +```php +$config['nfsen_suffix'] = "_yourdomain_com"; +``` + +The above is a very important bit as device names in NfSen are limited +to 21 characters. This means full domain names for devices can be very +problematic to squeeze in, so there for this chunk is usually removed. + +On a similar note, NfSen profiles for channels should be created with +the same name. + +## Stats Defaults and Settings + +Below are the default settings used with nfdump for stats. + +For more defailted information on that, please see nfdump(1). + +```php +$config['nfsen_last_max'] = 153600; +$config['nfsen_top_max'] = 500; +$config['nfsen_top_N']=array( 10, 20, 50, 100, 200, 500 ); +$config['nfsen_top_default']=20; +$config['nfsen_stat_default']='srcip'; +$config['nfsen_order_default']='packets'; +$config['nfsen_last_default']=900; +$config['nfsen_lasts']=array( + '300'=>'5 minutes', + '600'=>'10 minutes', + '900'=>'15 minutes', + '1800'=>'30 minutes', + '3600'=>'1 hour', + '9600'=>'3 hours', + '38400'=>'12 hours', + '76800'=>'24 hours', + '115200'=>'36 hours', + '153600'=>'48 hours', + ); +``` + +``` +$config['nfsen_last_max'] = 153600; +``` + +The above is the max value in seconds one may pull stats for. The +higher this is, the more CPU and disk intensive the search will be. + +Numbers larger than this will be set to this. + +``` +$config['nfsen_top_max'] = 500; +``` + +The above is max number of items to be displayed. + +Numbers larger than this will be set to this. + +``` +$config['nfsen_top_N']=array( 10, 20, 50, 100, 200, 500 ); +``` + +The above is a array containing a list for the drop down menu how many +top items should be returned. + +```php +$config['nfsen_top_default']=20; +``` + +The above sets default top number to use from the drop down. + +```php +$config['nfsen_stat_default']='srcip'; +``` + +The above sets default stat type to use from the drop down. + +``` +record Flow Records +ip Any IP Address +srcip SRC IP Address +dstip DST IP Address +port Any Port +srcport SRC Port +dstport DST Port +srctos SRC TOS +dsttos DST TOS +tos TOS +as AS +srcas SRC AS +dstas DST AS +``` + +```php +$config['nfsen_order_default']='packets'; +``` + +The above sets default order type to use from the drop down. Any of +the following below are currently supported. + +``` +flows Number of total flows for the time period. +packet Number of total packets for the time period. +bytes Number of total bytes for the time period. +pps Packets Per Second +bps Bytes Per Second +bpp Bytes Per Packet +``` + +``` +$config['nfsen_last_default']=900; +``` + +The above is the last default to use from the drop down. + +``` +$config['nfsen_lasts']=array( + '300'=>'5 minutes', + '600'=>'10 minutes', + '900'=>'15 minutes', + '1800'=>'30 minutes', + '3600'=>'1 hour', + '9600'=>'3 hours', + '38400'=>'12 hours', + '76800'=>'24 hours', + '115200'=>'36 hours', + '153600'=>'48 hours', + ); +``` + +The above associative array contains time intervals for how +far back to go. The keys are the length in seconds and the +value is just a description to display. diff --git a/includes/defaults.inc.php b/includes/defaults.inc.php index 899d501cdf..9a8e40527c 100644 --- a/includes/defaults.inc.php +++ b/includes/defaults.inc.php @@ -62,6 +62,7 @@ $config['ipmitool'] = '/usr/bin/ipmitool'; $config['virsh'] = '/usr/bin/virsh'; $config['dot'] = '/usr/bin/dot'; $config['sfdp'] = '/usr/bin/sfdp'; +$config['nfdump'] = '/usr/bin/nfdump'; $config['slow_statistics'] = true; // THIS WILL CHANGE TO FALSE IN FUTURE @@ -541,6 +542,26 @@ $config['nfsen_enable'] = 0; // $config['nfsen_split_char'] = "_"; // $config['nfsen_rrds'] = "/var/nfsen/profiles-stat/live/"; // $config['nfsen_suffix'] = "_yourdomain_com"; +$config['nfsen_subdirlayout'] = 1; +$config['nfsen_last_max'] = 153600; // 48 hours ago in seconds +$config['nfsen_top_max'] = 500; // max topN value for stats +$config['nfsen_top_N']=array( 10, 20, 50, 100, 200, 500 ); +$config['nfsen_top_default']=20; +$config['nfsen_stat_default']='srcip'; +$config['nfsen_order_default']='packets'; +$config['nfsen_last_default']=900; +$config['nfsen_lasts']=array( + '300'=>'5 minutes', + '600'=>'10 minutes', + '900'=>'15 minutes', + '1800'=>'30 minutes', + '3600'=>'1 hour', + '9600'=>'3 hours', + '38400'=>'12 hours', + '76800'=>'24 hours', + '115200'=>'36 hours', + '153600'=>'48 hours', + ); // Location Mapping // Use this feature to map ugly locations to pretty locations // config['location_map']['Under the Sink'] = "Under The Sink, The Office, London, UK"; diff --git a/includes/html/functions.inc.php b/includes/html/functions.inc.php index 9fb1bdb2bd..50ab03342a 100644 --- a/includes/html/functions.inc.php +++ b/includes/html/functions.inc.php @@ -571,7 +571,7 @@ function generate_sensor_url($sensor, $vars = array()) return generate_url(array('page' => 'graphs', 'id' => $sensor['sensor_id'], 'type' => $sensor['graph_type'], 'from' => Config::get('time.day')), $vars); }//end generate_sensor_url() - + function generate_port_url($port, $vars = array()) { return generate_url(array('page' => 'device', 'device' => $port['device_id'], 'tab' => 'port', 'port' => $port['port_id']), $vars); @@ -1640,3 +1640,105 @@ function get_sensor_label_color($sensor, $type = 'sensors') $unit = __("$type.{$sensor['sensor_class']}.unit"); return "".trim(format_si($sensor['sensor_current']).$unit).""; } + +/** + * @params int unix time + * @params int seconds + * @return int + * + * Rounds down to the nearest interval. + * + * The first argument is required and it is the unix time being + * rounded down. + * + * The second value is the time interval. If not specified, it + * defaults to 300, or 5 minutes. + */ +function lowest_time($time, $seconds = 300) +{ + return $time - ($time % $seconds); +} + +/** + * @params int + * @return string + * + * This returns the subpath for working with nfdump. + * + * 1 value is taken and that is a unix time stamp. It will be then be rounded + * off to the lowest five minutes earlier. + * + * The return string will be a path partial you can use with nfdump to tell it what + * file or range of files to use. + * + * Below ie a explanation of the layouts as taken from the NfSen config file. + * 0 no hierachy levels - flat layout - compatible with pre NfSen version + * 1 %Y/%m/%d year/month/day + * 2 %Y/%m/%d/%H year/month/day/hour + * 3 %Y/%W/%u year/week_of_year/day_of_week + * 4 %Y/%W/%u/%H year/week_of_year/day_of_week/hour + * 5 %Y/%j year/day-of-year + * 6 %Y/%j/%H year/day-of-year/hour + * 7 %Y-%m-%d year-month-day + * 8 %Y-%m-%d/%H year-month-day/hour + */ +function time_to_nfsen_subpath($time) +{ + $time=lowest_time($time); + $layout=Config::get('nfsen_subdirlayout'); + + if ($layout == 0) { + return 'nfcapd.'.date('YmdHi', $time); + } elseif ($layout == 1) { + return date('Y\/m\/d\/\n\f\c\a\p\d\.YmdHi', $time); + } elseif ($layout == 2) { + return date('Y\/m\/d\/H\/\n\f\c\a\p\d\.YmdHi', $time); + } elseif ($layout == 3) { + return date('Y\/W\/w\/\n\f\c\a\p\d\.YmdHi', $time); + } elseif ($layout == 4) { + return date('Y\/W\/w\/H\/\n\f\c\a\p\d\.YmdHi', $time); + } elseif ($layout == 5) { + return date('Y\/z\/\n\f\c\a\p\d\.YmdHi', $time); + } elseif ($layout == 6) { + return date('Y\/z\/H\/\n\f\c\a\p\d\.YmdHi', $time); + } elseif ($layout == 7) { + return date('Y\-m\-d\/\n\f\c\a\p\d\.YmdHi', $time); + } elseif ($layout == 8) { + return date('Y\-m\-d\/H\/\n\f\c\a\p\d\.YmdHi', $time); + } +} + +/** + * @params string hostname + * @return string + * + * Takes a hostname and transforms it to the name + * used by nfsen. +*/ +function nfsen_hostname($hostname) +{ + $nfsen_hostname=str_replace('.', Config::get('nfsen_split_char'), $hostname); + + if (!is_null(Config::get('nfsen_suffix'))) { + $nfsen_hostname=str_replace(Config::get('nfsen_suffix'), '', $nfsen_hostname); + } + return $nfsen_hostname; +} + +/** + * @params string hostname + * @return string + * + * Takes a hostname and returns the path to the nfsen + * live dir. +*/ +function nfsen_live_dir($hostname) +{ + $hostname=nfsen_hostname($hostname); + + foreach (Config::get('nfsen_base') as $base_dir) { + if (file_exists($base_dir) && is_dir($base_dir)) { + return $base_dir.'/profiles-data/live/'.$hostname; + } + } +} diff --git a/includes/html/graphs/device/nfsen_channel_common.inc.php b/includes/html/graphs/device/nfsen_channel_common.inc.php index be58edc51b..a3216486d7 100644 --- a/includes/html/graphs/device/nfsen_channel_common.inc.php +++ b/includes/html/graphs/device/nfsen_channel_common.inc.php @@ -3,37 +3,41 @@ $simple_rrd = true; foreach ((array)\LibreNMS\Config::get('nfsen_rrds', []) as $nfsenrrds) { - $nfsenrrds = rtrim($nfsenrrds, '/') . '/'; - - if (\LibreNMS\Config::get('nfsen_split_char')) { - $nfsenHostname = str_replace('.', \LibreNMS\Config::get('nfsen_split_char'), $device['hostname']); - } else { - $nfsenHostname=$device['hostname']; + if ($nfsenrrds[(strlen($nfsenrrds) - 1)] != '/') { + $nfsenrrds .= '/'; } - $rrd_filename=$nfsenrrds.$nfsenHostname.'/'.$vars['channel'].'.rrd'; - if (is_file($rrd_filename)) { - $colours = 'blues'; - $nototal = 0; - $units = ''; - $scale_min = '0'; - $unit_text = $dsdescr; + $nfsen_filename=nfsen_hostname($device['hostname']); - // set a multiplier which in turn will create a CDEF if this var is set - if ($dsprefix == 'traffic_') { - $multiplier = '8'; - } + if (is_file($nfsenrrds.$nfsen_filename.'/'.$vars['channel'].'.rrd')) { + $rrd_filename = $nfsenrrds.$nfsen_filename.'/'.$vars['channel'].'.rrd'; $flowtypes = array('tcp', 'udp', 'icmp', 'other'); + $rrd_list = array(); + $nfsen_iter = 1; foreach ($flowtypes as $flowtype) { - $rrd_list[] = array( - 'filename' => $rrd_filename, - 'descr' => $flowtype, - 'ds' => $dsprefix.$flowtype - ); + $rrd_list[$nfsen_iter]['filename'] = $rrd_filename; + $rrd_list[$nfsen_iter]['descr'] = $flowtype; + $rrd_list[$nfsen_iter]['ds'] = $dsprefix.$flowtype; + + // set a multiplier which in turn will create a CDEF if this var is set + if ($dsprefix == 'traffic_') { + $multiplier = '8'; + } + + $colours = 'blues'; + $nototal = 0; + $units = ''; + $unit_text = $dsdescr; + $scale_min = '0'; + + if ($_GET['debug']) { + print_r($rrd_list); + } + + $nfsen_iter++; } - d_echo($rrd_list); } } diff --git a/includes/html/graphs/device/nfsen_common.inc.php b/includes/html/graphs/device/nfsen_common.inc.php index 374181e67a..7d02224af6 100644 --- a/includes/html/graphs/device/nfsen_common.inc.php +++ b/includes/html/graphs/device/nfsen_common.inc.php @@ -7,15 +7,7 @@ foreach ((array)\LibreNMS\Config::get('nfsen_rrds', []) as $nfsenrrds) { $nfsenrrds .= '/'; } - // convert dots in filename to underscores - $nfsensuffix = \LibreNMS\Config::get('nfsen_suffix', ''); - - if (!empty(\LibreNMS\Config::get('nfsen_split_char'))) { - $basefilename_underscored = preg_replace('/\./', \LibreNMS\Config::get('nfsen_split_char'), $device['hostname']); - } else { - $basefilename_underscored = $device['hostname']; - } - $nfsen_filename = preg_replace('/'.$nfsensuffix.'/', '', $basefilename_underscored); + $nfsen_filename=nfsen_hostname($device['hostname']); if (is_file($nfsenrrds.$nfsen_filename.'.rrd')) { $rrd_filename = $nfsenrrds.$nfsen_filename.'.rrd'; diff --git a/includes/html/pages/device/nfsen.inc.php b/includes/html/pages/device/nfsen.inc.php index d6f6785035..580817f36f 100644 --- a/includes/html/pages/device/nfsen.inc.php +++ b/includes/html/pages/device/nfsen.inc.php @@ -9,25 +9,27 @@ $link_array = array( ); echo generate_link('General', $link_array, array('nfsen' => 'general')); +echo '|'; +echo generate_link('Stats', $link_array, array('nfsen' => 'stats')); -$printedChannel=false; -$nfsenHostname = str_replace('.', \LibreNMS\Config::get('nfsen_split_char'), $device['hostname']); -foreach ((array)\LibreNMS\Config::get('nfsen_rrds', []) as $nfsenDir) { - $hostDir=$nfsenDir.'/'.$nfsenHostname.'/'; +$printedChannel = false; +$nfsen_hostname = nfsen_hostname($device['hostname']); +foreach (\LibreNMS\Config::get('nfsen_rrds') as $nfsenDir) { + $hostDir = $nfsenDir.'/'.$nfsen_hostname.'/'; if (is_dir($hostDir)) { - $nfsenRRDchannelGlob=$hostDir.'*.rrd'; + $nfsenRRDchannelGlob = $hostDir.'*.rrd'; foreach (glob($nfsenRRDchannelGlob) as $nfsenRRD) { $channel = str_replace(array($hostDir, '.rrd'), '', $nfsenRRD); if (!$printedChannel) { echo '|Channels:'; - $printedChannel=true; + $printedChannel = true; } else { echo ','; } if ($vars['channel'] == $channel) { - $channelFilter=$hostDir.$channel.'-filter.txt'; + $channelFilter = $hostDir.$channel.'-filter.txt'; } echo generate_link($channel, $link_array, array('nfsen' => 'channel', 'channel' => $channel)); @@ -37,9 +39,12 @@ foreach ((array)\LibreNMS\Config::get('nfsen_rrds', []) as $nfsenDir) { print_optionbar_end(); -$nfsen_type = basename($vars['nfsen'] ?? 'general'); -if (is_file("includes/html/pages/device/nfsen/$nfsen_type.inc.php")) { - include "includes/html/pages/device/nfsen/$nfsen_type.inc.php"; +if (!$vars['nfsen']) { + $vars['nfsen'] = 'general'; +} + +if (is_file('includes/html/pages/device/nfsen/'.mres($vars['nfsen']).'.inc.php')) { + include 'includes/html/pages/device/nfsen/'.mres($vars['nfsen']).'.inc.php'; } else { include 'includes/html/pages/device/nfsen/general.inc.php'; } diff --git a/includes/html/pages/device/nfsen/stats.inc.php b/includes/html/pages/device/nfsen/stats.inc.php new file mode 100644 index 0000000000..f7e77e04db --- /dev/null +++ b/includes/html/pages/device/nfsen/stats.inc.php @@ -0,0 +1,153 @@ + 'stats')).'" iOCd = "FlowStats" method = "SUBMIT">'; + +echo 'Top N: + +During the last: + +, Stat Type: + +, Order By: + + +'; +echo ''; + +print_optionbar_end(); + +// process stuff now if we the button was clicked on +if (isset($vars['process'])) { + // Make sure we have a sane value for lastN + $lastN = 900; + if (isset($vars['lastN']) && + is_numeric($vars['lastN']) && + ($vars['lastN'] <= \LibreNMS\Config::get('nfsen_last_max')) + ) { + $lastN = $vars['lastN']; + } + + // Make sure we have a sane value for lastN + $topN = 20; // The default if not set or something invalid is set + if (isset($vars['topN']) && + is_numeric($vars['topN']) && + ($vars['topN'] <= \LibreNMS\Config::get('nfsen_top_max')) + ) { + $topN = $vars['topN']; + } + + // Handle the stat order. + $stat_order = 'pps'; // The default if not set or something invalid is set + if (isset($vars['statorder']) && isset($order_types[$vars['statorder']])) { + $stat_order = $vars['statorder']; + } + + // Handle the stat type. + $stat_type = 'srcip'; // The default if not set or something invalid is set + if (isset($vars['stattype']) && isset($stat_types[$vars['stattype']])) { + $stat_type = $vars['stattype']; + } + + $current_time = lowest_time(time() - 300); + $last_time = lowest_time($current_time - $lastN - 300); + + $command = \LibreNMS\Config::get('nfdump').' -M '.nfsen_live_dir($device['hostname']).' -T -R '. + time_to_nfsen_subpath($last_time).':'.time_to_nfsen_subpath($current_time). + ' -n '.$topN.' -s '.$stat_type.'/'.$stat_order; + + echo '
';
+    system($command);
+    echo '
'; +}