Add application powermon (#12500)

* adding powermon application

* working consumption graph

* updating powermon application

* powermon app style fixes

* added powermon app test data

* debug code

* revert

* fix tests

* fix tests

Co-authored-by: Tony Murray <murraytony@gmail.com>
Co-authored-by: Jellyfrog <Jellyfrog@users.noreply.github.com>
This commit is contained in:
yrebrac
2021-03-08 06:28:09 +11:00
committed by GitHub
parent 49868ee1a6
commit 7d65cf1f05
9 changed files with 2006 additions and 2 deletions

View File

@@ -69,6 +69,7 @@ class StringHelpers
'powerdns' => 'PowerDNS',
'powerdns-dnsdist' => 'PowerDNS dnsdist',
'powerdns-recursor' => 'PowerDNS Recursor',
'powermon' => 'PowerMon',
'pureftpd' => 'PureFTPd',
'rrdcached' => 'RRDCached',
'sdfsinfo' => 'SDFS info',

View File

@@ -124,6 +124,7 @@ by following the steps under the `SNMP Extend` heading.
1. [PowerDNS](#powerdns) - Agent
1. [PowerDNS Recursor](#powerdns-recursor) - Direct, SNMP extend, Agent
1. [PowerDNS dnsdist](#powerdns-dnsdist) - SNMP extend
1. [PowerMon](#powermon) - SNMP extend
1. [Proxmox](#proxmox) - SNMP extend
1. [Puppet Agent](#puppet_agent) - SNMP extend
1. [PureFTPd](#pureftpd) - SNMP extend
@@ -819,7 +820,7 @@ Verify it is working by running `/usr/lib/check_mk_agent/local/gpsd`
Shell script that reports load average/memory/open-files stats of Icecast
## SNMP Extend
1. Copy the shell script, icecast-stats.sh, to the desired host (the host must be added to LibreNMS devices)
1. Copy the shell script, icecast-stats.sh, to the desired host (the host must be added to LibreNMS devices)
```
wget https://github.com/librenms/librenms-agent/raw/master/snmp/icecast-stats.sh -O /etc/snmp/icecast-stats.sh
```
@@ -845,7 +846,7 @@ wget https://raw.githubusercontent.com/librenms/librenms-agent/master/snmp/mailc
2: Run `chmod +x /etc/snmp/mailcow-dockerized-postfix`
> Maybe you will be neeed to install `pflogsumm` on debian based OS. Please check if you have package installed.
> Maybe you will be neeed to install `pflogsumm` on debian based OS. Please check if you have package installed.
3: Edit your snmpd.conf file (usually /etc/snmp/snmpd.conf) and add:
@@ -1531,6 +1532,151 @@ The application should be auto-discovered as described at the top of
the page. If it is not, please follow the steps set out under `SNMP
Extend` heading top of page.
# PowerMon
PowerMon tracks the power usage on your host and can report on both consumption
and cost, using a python script installed on the host.
[PowerMon consumption graph](../img/example-app-powermon-consumption-02.png)
Currently the script uses one of two methods to determine current power usage:
* ACPI via libsensors
* HP-Health (HP Proliant servers only)
The ACPI method is quite unreliable as it is usually only implemented by
battery-powered devices, e.g. laptops. YMMV. However, it's possible to support
any method as long as it can return a power value, usually in Watts.
> TIP: You can achieve this by adding a method and a function for that method to
> the script. It should be called by getData() and return a dictionary.
Because the methods are unreliable for all hardware, you need to declare to the
script which method to use. The are several options to assist with testing, see
`--help`.
## SNMP Extend
### Initial setup
1. Download the python script onto the host:
```
wget https://raw.githubusercontent.com/librenms/librenms-agent/master/snmp/powermon-snmp.py -O /usr/local/bin/powermon-snmp.py
```
2. Make the script executable:
```
chmod +x /usr/local/bin/powermon-snmp.py
```
3. Edit the script and set the cost per kWh for your supply. You must uncomment
this line for the script to work:
```
vi /usr/local/bin/powermon-snmp.py
#costPerkWh = 0.15
```
4. Choose you method below:
=== "Method 1: sensors"
* Install dependencies:
```
dnf install lm_sensors
pip install PySensors
```
* Test the script from the command-line. For example:
```
$ /usr/local/bin/powermon-snmp.py -m sensors -n -p
{
"meter": {
"0": {
"reading": 0.0
}
},
"psu": {},
"supply": {
"rate": 0.15
},
"reading": "0.0"
}
```
If you see a reading of `0.0` it is likely this method is not supported for
your system. If not, continue.
=== "Method 2: hpasmcli"
* Obtain the hp-health package for your system. Generally there are
three options:
* Standalone package from [HPE Support](https://support.hpe.com/hpsc/swd/public/detail?swItemId=MTX-c0104db95f574ae6be873e2064#tab2)
* From the HP Management Component Pack (MCP).
* Included in the [HP Service Pack for Proliant (SPP)](https://support.hpe.com/hpesc/public/docDisplay?docId=emr_na-a00026884en_us)
* If you've downloaded the standalone package, install it. For example:
```
rpm -ivh hp-health-10.91-1878.11.rhel8.x86_64.rpm
```
* Check the service is running:
```
systemctl status hp-health
```
* Test the script from the command-line. For example:
```
$ /usr/local/bin/powermon-snmp.py -m hpasmcli -n -p
{
"meter": {
"1": {
"reading": 338.0
}
},
"psu": {
"1": {
"present": "Yes",
"redundant": "No",
"condition": "Ok",
"hotplug": "Supported",
"reading": 315.0
},
"2": {
"present": "Yes",
"redundant": "No",
"condition": "FAILED",
"hotplug": "Supported"
}
},
"supply": {
"rate": 0.224931
},
"reading": 338.0
}
```
If you see a reading of `0.0` it is likely this method is not supported for
your system. If not, continue.
### Finishing Up
5. Edit your snmpd.conf file (usually `/etc/snmp/snmpd.conf`) and add the following:
```
extend powermon /usr/local/bin/powermon-snmp.py -m hpasmcli
```
> NOTE: Avoid using other script options in the snmpd config as the results may not be
> interpreted correctly by LibreNMS.
6. Reload your snmpd service:
```
systemctl reload snmpd
```
7. You're now ready to enable the application in LibreNMS.
# Proxmox
1: For Proxmox 4.4+ install the libpve-apiclient-perl package `apt

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,176 @@
<?php
/*
LibreNMS Application for monitoring power consumption
@link https://www.upaya.net.au/
@copyright 2021 Ben Carbery
@author Ben Carbery <yrebrac@upaya.net.au>
LICENSE - GPLv3
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3. See https://www.gnu.org/licenses/gpl-3.0.txt
DESCRIPTION
Displays the crrent watts being consumed by a host and uses that data series
to estimate total power consumption in kWh over the graph. Finally, displays
the cost of the kWh consumed based on the cost defined below.
Watts (power) is an instantaneous value. Hence this graph will be more accurate
when these is a fairly consistent load on the server between polling cycles.
Watts is easily converted to kilowatt hours which most electricity providers
will charge in terms of. The graph will display the total kWh hours consumed at
the bottom. You can update the default cost_per_kWh below and currency symbol
below under LOCAL OPTIONS
*/
$name = 'powermon';
$app_id = $app['app_id'];
$rrd_filename = rrd_name($device['hostname'], ['app', $name, $app_id]);
$ds_list[0]['vname'] = 'watts';
$ds_list[0]['ds'] = 'watts-gauge';
$ds_list[0]['filename'] = $rrd_filename;
$ds_list[0]['descr'] = 'Average Power';
$ds_list[0]['units_text'] = ' W';
$ds_list[0]['colour'] = 'FF6600'; // orange
$ds_list[0]['colour'] = 'F9F900'; // yellow
$ds_list[0]['areacolour'] = 'FAFAB2'; // yellow
$ds_list[1]['vname'] = 'rate';
$ds_list[1]['ds'] = 'rate';
$ds_list[1]['filename'] = $rrd_filename;
$ds_list[1]['descr'] = ' Total Cost';
$ds_list[1]['units_text'] = '$';
$ds_list[1]['colour'] = '006600'; // money green
if ($_GET['debug']) {
print_r($ds_list);
}
// COMMON OPTIONS
//$from = ;
//$to = ;
//$width = 200;
//$height = 100;
//$inverse = false;
//$nototal = true;
//$nodetails = false;
//$noagg = true;
//$title = '';
$scale_min = 0;
//$scale_max = 0;
//$scale_rigid = anything;
//$norigid = false;
$float_precision = 0;
// LOCAL OPTIONS
$line_width = 2.5;
$pad_to = 18; // padding for left-hand column in legend
$currency_symbol = '$'; // update this if required
require 'includes/html/graphs/common.inc.php';
//if ($nototal) {
// $pad_to += '2';
//}
$rrd_options .= ' COMMENT:\s'; // spacer in legend
$i = 0;
foreach ($ds_list as $ds_item) {
$vname = $ds_item['vname'];
$ds = $ds_item['ds'];
$filename = $ds_item['filename'];
$descr = rrdtool_escape($ds_item['descr'], $pad_to);
// CF to use
$use_cf_last = ['nothing', 'nothing'];
if (in_array($vname, $use_cf_last)) {
$cf = 'LAST';
} else {
$cf = 'AVERAGE';
}
$rrd_options .= ' DEF:' . "$vname=$filename:$ds:" . $cf;
// Units
if (isset($ds_item['units_text'])) {
$units_text = $ds_item['units_text'];
} else {
$units_text = '';
}
// Line Width
if (isset($ds_item['line_width'])) {
$ds_line_width = $ds_item['line_width'];
} else {
$ds_line_wdith = $line_width;
}
// Line Colour
if (isset($ds_item['colour'])) {
$colour = $ds_item['colour'];
} else {
if (! \LibreNMS\Config::get("graph_colours.$colours.$i")) {
$i = 0;
}
$colour = \LibreNMS\Config::get("graph_colours.$colours.$i");
$i++;
}
// Area Colour
if (isset($ds_item['areacolour'])) {
$areacolour = $ds_item['areacolour'];
} else {
$areacolour = $colour . '20';
}
// Graph command
if ($vname == 'watts') {
$rrd_options .= ' AREA:' . $vname . '#' . $areacolour;
$rrd_options .= " LINE{$ds_line_width}:{$vname}#{$colour}:'{$descr}'";
$rrd_options .= " GPRINT:{$vname}:AVERAGE:%12.{$float_precision}lf'{$units_text}'\l";
$rrd_options .= ' COMMENT:\s'; // spacer in legend
/*
// Watt Seconds
$descr = ' Total Consumed';
$units_text = ' Ws';
$descr = rrdtool_escape($descr, $pad_to + 2);
$rrd_options .= " COMMENT:'{$descr}'";
$rrd_options .= ' VDEF:wattsecs=watts,TOTAL';
$rrd_options .= " GPRINT:wattsecs:%12.{$float_precision}lf'{$units_text}'\l";
*/
// Kilowatt Hours
$units_text = ' kWh';
$float_precision = 2;
$descr = ' Total Consumed';
$descr = rrdtool_escape($descr, $pad_to + 2);
$rrd_options .= " COMMENT:'{$descr}'";
$rrd_options .= ' CDEF:series_a=watts,3600000,/';
$rrd_options .= ' VDEF:kilowatthours=series_a,TOTAL';
$rrd_options .= " GPRINT:kilowatthours:%12.{$float_precision}lf'{$units_text}'\l";
} elseif ($vname == 'rate') {
// Consumption Charge
$float_precision = 2;
$descr = rrdtool_escape($descr, $pad_to + 7);
$rrd_options .= " COMMENT:'{$descr}{$currency_symbol}'";
$rrd_options .= " CDEF:series_b=watts,{$vname},*,3600000,/";
$rrd_options .= ' VDEF:total_cost=series_b,TOTAL';
$rrd_options .= " GPRINT:total_cost:%6.{$float_precision}lf' @ average rate of'";
$rrd_options .= ' VDEF:average_rate=rate,AVERAGE';
$rrd_options .= " GPRINT:average_rate:%0.6lf' per kWh\l'";
}
}

View File

@@ -58,6 +58,9 @@ $graphs['powerdns-recursor'] = [
'cache_performance',
'outqueries',
];
$graphs['powermon'] = [
'consumption',
];
$graphs['pureftpd'] = [
'bitrate',
'connections',

View File

@@ -0,0 +1,41 @@
<?php
/*
LibreNMS Application for monitoring power consumption and cost
@link https://www.upaya.net.au/
@copyright 2021 Ben Carbery
@author Ben Carbery <yrebrac@upaya.net.au>
LICENSE - GPLv3
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3. See https://www.gnu.org/licenses/gpl-3.0.txt
*/
$graphs = [
'powermon_consumption' => 'PowerMon - Consumption',
];
foreach ($graphs as $key => $text) {
$graph_type = $key;
$graph_array['height'] = '100';
$graph_array['width'] = '215';
$graph_array['to'] = \LibreNMS\Config::get('time.now');
$graph_array['id'] = $app['app_id'];
$graph_array['type'] = 'application_' . $key;
echo '<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">' . $text . '</h3>
</div>
<div class="panel-body">
<div class="row">';
include 'includes/html/print-graphrow.inc.php';
echo '</div>';
echo '</div>';
echo '</div>';
}

View File

@@ -0,0 +1,61 @@
<?php
/*
LibreNMS Application for monitoring power consumption and cost
@link https://www.upaya.net.au/
@copyright 2021 Ben Carbery
@author Ben Carbery <yrebrac@upaya.net.au>
LICENSE - GPLv3
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3. See https://www.gnu.org/licenses/gpl-3.0.txt
*/
use LibreNMS\Exceptions\JsonAppException;
use LibreNMS\RRD\RrdDefinition;
$name = 'powermon';
$app_id = $app['app_id'];
echo $name;
try {
$result = json_app_get($device, $name);
} catch (JsonAppException $e) {
echo PHP_EOL . $name . ':' . $e->getCode() . ':' . $e->getMessage() . PHP_EOL;
update_application($app, $e->getCode() . ':' . $e->getMessage(), []);
// Set empty metrics and error message
log_event('application ' . $name . ' caught JsonAppException');
return;
}
// should be doing something with error codes/messages returned in the snmp
// result or will they be caught above?
$rrd_name = ['app', $name, $app_id];
$rrd_def = RrdDefinition::make()
->addDataset('watts-gauge', 'GAUGE', 0)
->addDataset('watts-abs', 'ABSOLUTE', 0)
->addDataset('rate', 'GAUGE', 0);
$fields = [
'watts-gauge' => $result['data']['reading'],
'watts-abs' => $result['data']['reading'],
'rate' => $result['data']['supply']['rate'],
];
/*
log_event(
"watts-gauage: " . $result['data']['reading']
. ", watts-abs: " . $result['data']['reading']
);
*/
$tags = compact('name', 'app_id', 'rrd_name', 'rrd_def');
data_update($device, 'app', $tags, $fields);
update_application($app, 'OK', $fields);

View File

@@ -0,0 +1,49 @@
{
"applications": {
"discovery": {
"applications": [
{
"app_type": "powermon",
"app_state": "UNKNOWN",
"discovered": 1,
"app_state_prev": null,
"app_status": "",
"app_instance": ""
}
],
"application_metrics": []
},
"poller": {
"applications": [
{
"app_type": "powermon",
"app_state": "OK",
"discovered": 1,
"app_state_prev": "UNKNOWN",
"app_status": "",
"app_instance": ""
}
],
"application_metrics": [
{
"metric": "rate",
"value": 0.224931,
"value_prev": null,
"app_type": "powermon"
},
{
"metric": "watts-abs",
"value": 340,
"value_prev": null,
"app_type": "powermon"
},
{
"metric": "watts-gauge",
"value": 340,
"value_prev": null,
"app_type": "powermon"
}
]
}
}
}

View File

File diff suppressed because it is too large Load Diff