mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
docs: Updated Support-New-OS doc to provide clearer information (#6492)
* docs: Updated Support-New-OS doc to provide clearer information * Update Custom-Graphs.md * Update Custom-Graphs.md * Update Health-Information.md * Update Initial-Detection.md
This commit is contained in:
committed by
Tony Murray
parent
dcc6c96ec4
commit
2361541e33
@@ -1,541 +1,10 @@
|
||||
source: Developing/Support-New-OS.md
|
||||
This document will explain how to add basic and full support for a new OS. **Some knowledge in PHP is needed for the full support.**
|
||||
|
||||
This document is broken down into the relevant sections depending on what support you are adding.
|
||||
During all of these examples we will be using the OS of `pulse` as the example OS we will add.
|
||||
|
||||
#### BASIC SUPPORT FOR A NEW OS
|
||||
|
||||
### MIB
|
||||
|
||||
If we have the MIB, we can copy the file into the default directory:
|
||||
|
||||
```bash
|
||||
/opt/librenms/mibs
|
||||
```
|
||||
|
||||
#### New OS definition
|
||||
Let's begin to declare the new OS in LibreNMS. At first we create a new definition file located here:
|
||||
|
||||
```bash
|
||||
includes/definitions/$os.yaml
|
||||
```
|
||||
|
||||
This is a [Yaml file](https://en.wikipedia.org/wiki/YAML). Please be careful of the formatting of this file.
|
||||
|
||||
```yaml
|
||||
os: pulse
|
||||
text: 'Pulse Secure'
|
||||
type: firewall
|
||||
icon: pulse
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
- sysDescr:
|
||||
- Pulse Connect Secure
|
||||
- Pulse Secure
|
||||
- Juniper Networks,Inc,VA-DTE
|
||||
- VA-SPE
|
||||
```
|
||||
|
||||
#### Icon and Logo
|
||||
|
||||
Create an SVG image of the icon and logo. Legacy PNG bitmaps are also supported but look bad on HiDPI.
|
||||
- A vector image should not contain padding.
|
||||
- The file should not be larger than 20 Kb. Simplify paths to reduce large files.
|
||||
- Use plain SVG without gzip compression.
|
||||
|
||||
##### Icon
|
||||
- Save the icon SVG to **html/images/os/$os.svg**.
|
||||
- Icons should look good when viewed at 32x32 px.
|
||||
- Square icons are preferred to full logos with text.
|
||||
- Remove small ornaments that are almost not visible when displayed with 32px width (e.g. ® or ™).
|
||||
|
||||
##### Logo
|
||||
- Save the logo SVG to **html/images/logos/$os.svg**.
|
||||
- Logos can be any dimension, but often are wide and contain the company name.
|
||||
- If a logo is not present, the icon will be used.
|
||||
|
||||
##### Hints
|
||||
|
||||
Hints for [Inkscape](https://inkscape.org/):
|
||||
|
||||
- You can open a PDF to extract the logo.
|
||||
- Ungroup elements to isolate the logo.
|
||||
- Use `Path -> Simplify` to simplify paths of large files.
|
||||
- Use `File -> Document Properties… -> Resize page to content…` to remove padding.
|
||||
- Use `File -> Clean up document` to remove unused gradients, patterns, or markers.
|
||||
- Use `File -> Save As -> Plain SVG` to save the final image.
|
||||
|
||||
By optimizing the SVG you can shrink the file size in some cases to less than 20 %.
|
||||
[SVG Optimizer](https://github.com/svg/svgo) does a great job. There is also an [online version](https://jakearchibald.github.io/svgomg/).
|
||||
|
||||
#### OS Discovery
|
||||
The discovery section of the OS yaml file contains information needed to detect this OS.
|
||||
|
||||
##### Discovery Operators
|
||||
- `sysObjectId` The preferred operator. Checks if the sysObjectID starts with one of the strings under this item
|
||||
- `sysDescr` Use this in addition to sysObjectId if required. Check that the sysDescr contains one of the strings under this item
|
||||
- `sysDescr_regex` Please avoid use of this. Checks if the sysDescr matches one of the regex statements under this item
|
||||
|
||||
##### Discoery Logic
|
||||
YAML is converted to an array in PHP. Consider the following YAML:
|
||||
```yaml
|
||||
discovery:
|
||||
- sysObjectId: foo
|
||||
-
|
||||
sysDescr: [ snafu, exodar ]
|
||||
sysObjectId: bar
|
||||
|
||||
```
|
||||
This is how the discovery array would look in PHP:
|
||||
```php
|
||||
[
|
||||
[
|
||||
"sysObjectId" => "foo",
|
||||
],
|
||||
[
|
||||
"sysDescr" => [
|
||||
"snafu",
|
||||
"exodar",
|
||||
],
|
||||
"sysObjectId" => "bar",
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
The logic for the discovery is as follows:
|
||||
1. One of the first level items must match
|
||||
2. ALL of the second level items must match (sysObjectId, sysDescr)
|
||||
3. One of the third level items (foo, [snafu,exodar], bar) must match
|
||||
|
||||
So, considering the example:
|
||||
- `sysObjectId: foo, sysDescr: ANYTHING` matches
|
||||
- `sysObjectId: bar, sysDescr: ANYTHING` does not match
|
||||
- `sysObjectId: bar, sysDescr: exodar` matches
|
||||
- `sysObjectId: bar, sysDescr: snafu` matches
|
||||
|
||||
#### Basic OS information polling
|
||||
|
||||
Here is the file location for polling the new OS within a vendor MIB or a standard one:
|
||||
|
||||
```bash
|
||||
includes/polling/os/pulse.inc.php
|
||||
```
|
||||
This file will usually set the variables for $version, $hardware and $hostname retrieved from an snmp lookup.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$version = preg_replace('/[\r\n\"]+/', ' ', snmp_get($device, "productVersion.0", "-OQv", "PULSESECURE-PSG-MIB"));
|
||||
$hardware = "Juniper " . preg_replace('/[\r\n\"]+/', ' ', snmp_get($device, "productName.0", "-OQv", "PULSESECURE-PSG-MIB"));
|
||||
$hostname = trim($poll_device['sysName'], '"');
|
||||
```
|
||||
|
||||
Quick explanation and examples :
|
||||
|
||||
```bash
|
||||
snmpwalk -v2c -c public -m SNMPv2-MIB -M mibs
|
||||
//will give the overall OIDs that can be retrieve with this standard MIB. OID on the left side and the result on the right side
|
||||
//Then we have just to pick the wanted OID and do a check
|
||||
|
||||
snmpget -v2c -c public -OUsb -m SNMPv2-MIB -M /opt/librenms/mibs -t 30 HOSTNAME SNMPv2-SMI::mib-2.1.1.0
|
||||
//sysDescr.0 = STRING: Juniper Networks,Inc,Pulse Connect Secure,VA-DTE,8.1R1 (build 33493)
|
||||
|
||||
snmpget -v2c -c public -OUsb -m SNMPv2-MIB -M /opt/librenms/mibs -t 30 HOSTNAME SNMPv2-SMI::mib-2.1.5.0
|
||||
//sysName.0 = STRING: pulse-secure
|
||||
|
||||
//Here the same with the vendor MIB and the specific OID
|
||||
snmpget -v2c -c public -OUsb -m PULSESECURE-PSG-MIB -M /opt/librenms_old/mibs -t 30 HOSTNAME productName.0
|
||||
//productName.0 = STRING: "Pulse Connect Secure,VA-DTE"
|
||||
|
||||
snmpget -v2c -c public -OUsb -m PULSESECURE-PSG-MIB -M /opt/librenms/mibs -t 30 HOSTNAME productVersion.0
|
||||
//productVersion.0 = STRING: "8.1R1 (build 33493)"
|
||||
```
|
||||
|
||||
#### The final check
|
||||
|
||||
Discovery
|
||||
```bash
|
||||
./discovery.php -h HOSTNAME
|
||||
```
|
||||
|
||||
Polling
|
||||
```bash
|
||||
./poller.php -h HOSTNAME
|
||||
```
|
||||
|
||||
At this step we should see all the values retrieved in LibreNMS.
|
||||
|
||||
### Full support for a new OS
|
||||
|
||||
#### MIB
|
||||
|
||||
At first we copy the MIB file into the default directory:
|
||||
|
||||
```bash
|
||||
/opt/librenms/mibs
|
||||
```
|
||||
|
||||
We are now ready to look at inside the file and find the OID we want to use. _For this documentation we'll use Pulse Secure devices._
|
||||
|
||||
Then we can test it with the snmpget command (hostname must be reachable):
|
||||
|
||||
```bash
|
||||
//for example the OID iveCpuUtil.0:
|
||||
snmpget -v2c -c public -OUsb -m PULSESECURE-PSG-MIB -M /opt/librenms/mibs -t 30 HOSTNAME iveCpuUtil.0
|
||||
//quick explanation : snmpget -v2c -c COMMUNITY -OUsb -m MIBFILE -M MIB DIRECTORY HOSTNAME OID
|
||||
|
||||
//Result here:
|
||||
iveCpuUtil.0 = Gauge32: 28
|
||||
```
|
||||
|
||||
#### New OS definition
|
||||
Let's begin to declare the new OS in LibreNMS. At first we create a new definition file located here:
|
||||
|
||||
```bash
|
||||
includes/definitions/$os.yaml
|
||||
```
|
||||
|
||||
This is a [Yaml file](https://en.wikipedia.org/wiki/YAML). Please be careful of the formatting of this file.
|
||||
|
||||
```yaml
|
||||
os: pulse
|
||||
text: 'Pulse Secure'
|
||||
type: firewall
|
||||
icon: pulse
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
```
|
||||
|
||||
If you are adding custom graphs, please add the following to `includes/definitions.inc.php`:
|
||||
```php
|
||||
//Don't forget to declare the specific graphs if needed. It will be located near the end of the file.
|
||||
|
||||
//Pulse Secure Graphs
|
||||
$config['graph_types']['device']['pulse_users']['section'] = 'firewall';
|
||||
$config['graph_types']['device']['pulse_users']['order'] = '0';
|
||||
$config['graph_types']['device']['pulse_users']['descr'] = 'Active Users';
|
||||
$config['graph_types']['device']['pulse_sessions']['section'] = 'firewall';
|
||||
$config['graph_types']['device']['pulse_sessions']['order'] = '0';
|
||||
$config['graph_types']['device']['pulse_sessions']['descr'] = 'Active Sessions';
|
||||
```
|
||||
|
||||
#### Discovery OS
|
||||
|
||||
We create a new file named as our OS definition and in this directory:
|
||||
|
||||
```bash
|
||||
includes/discovery/os/pulse.inc.php
|
||||
```
|
||||
|
||||
Look at other files to get help in the code structure. For this example, it can be like this :
|
||||
|
||||
```php
|
||||
// Pulse Secure OS definition
|
||||
if (str_contains($sysDescr, array('Pulse Connect Secure', 'Pulse Secure', 'Juniper Networks,Inc,VA-DTE', 'VA-SPE'))) {
|
||||
$os = 'pulse';
|
||||
}
|
||||
```
|
||||
|
||||
As we declared Memory and CPU graphs before, we declare the OID in a PHP file :
|
||||
|
||||
|
||||
**Memory**
|
||||
|
||||
```bash
|
||||
includes/discovery/mempools/pulse.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
//
|
||||
// Hardcoded discovery of Memory usage on Pulse Secure devices.
|
||||
//
|
||||
if ($device['os'] == 'pulse') {
|
||||
echo 'PULSE-MEMORY-POOL: ';
|
||||
|
||||
$usage = str_replace('"', "", snmp_get($device, 'PULSESECURE-PSG-MIB::iveMemoryUtil.0', '-OvQ'));
|
||||
|
||||
if (is_numeric($usage)) {
|
||||
discover_mempool($valid_mempool, $device, 0, 'pulse-mem', 'Main Memory', '100', null, null);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**CPU**
|
||||
|
||||
```bash
|
||||
includes/discovery/processors/pulse.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
//
|
||||
// Hardcoded discovery of CPU usage on Pulse Secure devices.
|
||||
//
|
||||
if ($device['os'] == 'pulse') {
|
||||
echo 'Pulse Secure : ';
|
||||
|
||||
$descr = 'Processor';
|
||||
$usage = str_replace('"', "", snmp_get($device, 'PULSESECURE-PSG-MIB::iveCpuUtil.0', '-OvQ'));
|
||||
|
||||
if (is_numeric($usage)) {
|
||||
discover_processor($valid['processor'], $device, 'PULSESECURE-PSG-MIB::iveCpuUtil.0', '0', 'pulse-cpu', $descr,
|
||||
'100', $usage, null, null);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
_Please keep in mind that the PHP code is often different for the needs of the devices and the information we retrieve._
|
||||
|
||||
#### Polling OS
|
||||
|
||||
We will now do the same for the polling process:
|
||||
|
||||
**Memory**
|
||||
|
||||
```bash
|
||||
includes/polling/mempools/pulse-mem.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
// Simple hard-coded poller for Pulse Secure
|
||||
echo 'Pulse Secure MemPool'.'\n';
|
||||
|
||||
if ($device['os'] == 'pulse') {
|
||||
$perc = str_replace('"', "", snmp_get($device, "PULSESECURE-PSG-MIB::iveMemoryUtil.0", '-OvQ'));
|
||||
$memory_available = str_replace('"', "", snmp_get($device, "UCD-SNMP-MIB::memTotalReal.0", '-OvQ'));
|
||||
$mempool['total'] = $memory_available;
|
||||
|
||||
if (is_numeric($perc)) {
|
||||
$mempool['used'] = ($memory_available / 100 * $perc);
|
||||
$mempool['free'] = ($memory_available - $mempool['used']);
|
||||
}
|
||||
|
||||
echo "PERC " .$perc."%\n";
|
||||
echo "Avail " .$mempool['total']."\n";
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**CPU**
|
||||
|
||||
```bash
|
||||
includes/polling/processors/pulse-cpu.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
// Simple hard-coded poller for Pulse Secure
|
||||
echo 'Pulse Secure CPU Usage';
|
||||
|
||||
if ($device['os'] == 'pulse') {
|
||||
$usage = str_replace('"', "", snmp_get($device, 'PULSESECURE-PSG-MIB::iveCpuUtil.0', '-OvQ'));
|
||||
|
||||
if (is_numeric($usage)) {
|
||||
$proc = ($usage * 100);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is the file location for the specific graphs based on the OID in the vendor MIB:
|
||||
|
||||
```bash
|
||||
includes/polling/os/pulse.inc.php
|
||||
```
|
||||
We declare two specific graphs for users and sessions numbers. Theses two graphs will be displayed on the firewall section of the graphs tab as it was written in the definition include file.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$version = preg_replace('/[\r\n\"]+/', ' ', snmp_get($device, "productVersion.0", "-OQv", "PULSESECURE-PSG-MIB"));
|
||||
$hardware = "Juniper " . preg_replace('/[\r\n\"]+/', ' ', snmp_get($device, "productName.0", "-OQv", "PULSESECURE-PSG-MIB"));
|
||||
$hostname = trim($poll_device['sysName'], '"');
|
||||
|
||||
$users = snmp_get($device, 'iveConcurrentUsers.0', '-OQv', 'PULSESECURE-PSG-MIB');
|
||||
|
||||
if (is_numeric($users)) {
|
||||
$rrd_def = 'DS:users:GAUGE:600:0:U';
|
||||
|
||||
$fields = array(
|
||||
'users' => $users,
|
||||
);
|
||||
|
||||
$tags = compact('rrd_def');
|
||||
data_update($device, 'pulse_users', $tags, $fields);
|
||||
$graphs['pulse_users'] = true;
|
||||
}
|
||||
|
||||
$sessions = snmp_get($device, 'iveConcurrentUsers.0', '-OQv', 'PULSESECURE-PSG-MIB');
|
||||
|
||||
if (is_numeric($sessions)) {
|
||||
$rrd_def = 'DS:sessions:GAUGE:600:0:U';
|
||||
|
||||
$fields = array(
|
||||
'sessions' => $sessions,
|
||||
);
|
||||
|
||||
$tags = compact('rrd_def');
|
||||
data_update($device, 'pulse_sessions', $tags, $fields);
|
||||
$graphs['pulse_sessions'] = true;
|
||||
}
|
||||
```
|
||||
We finish in the declaration of the two graph types in the database:
|
||||
|
||||
We can do that within a file to share our work and contribute in the development of LibreNMS. :-)
|
||||
|
||||
```bash
|
||||
sql-schema/xxx.sql
|
||||
//check the file number in GitHub
|
||||
|
||||
php includes/sql-schema/update.php
|
||||
```
|
||||
|
||||
Or put the SQL commands directly in Mysql or PhpMyadmin for our tests:
|
||||
|
||||
```php
|
||||
INSERT INTO `graph_types`(`graph_type`, `graph_subtype`, `graph_section`, `graph_descr`, `graph_order`) VALUES ('device', 'pulse_users', 'firewall', 'Active Users', '');
|
||||
INSERT INTO `graph_types`(`graph_type`, `graph_subtype`, `graph_section`, `graph_descr`, `graph_order`) VALUES ('device', 'pulse_sessions', 'firewall', 'Active Sessions', '');
|
||||
```
|
||||
|
||||
#### Displaying
|
||||
|
||||
The specific graphs are not displayed automatically so we need to write the following PHP code:
|
||||
|
||||
**Pulse Sessions**
|
||||
|
||||
```bash
|
||||
html/includes/graphs/device/pulse_sessions.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$rrd_filename = rrd_name($device['hostname'], 'pulse_sessions');
|
||||
|
||||
require 'includes/graphs/common.inc.php';
|
||||
|
||||
$ds = 'sessions';
|
||||
|
||||
$colour_area = '9999cc';
|
||||
$colour_line = '0000cc';
|
||||
|
||||
$colour_area_max = '9999cc';
|
||||
|
||||
$graph_max = 1;
|
||||
$graph_min = 0;
|
||||
|
||||
$unit_text = 'Sessions';
|
||||
|
||||
require 'includes/graphs/generic_simplex.inc.php';
|
||||
```
|
||||
|
||||
**Pulse Users**
|
||||
|
||||
```bash
|
||||
html/includes/graphs/device/pulse_users.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$rrd_filename = rrd_name($device['hostname'], 'pulse_users');
|
||||
|
||||
require 'includes/graphs/common.inc.php';
|
||||
|
||||
$ds = 'users';
|
||||
|
||||
$colour_area = '9999cc';
|
||||
$colour_line = '0000cc';
|
||||
|
||||
$colour_area_max = '9999cc';
|
||||
|
||||
$graph_max = 1;
|
||||
|
||||
$unit_text = 'Users';
|
||||
|
||||
require 'includes/graphs/generic_simplex.inc.php';
|
||||
```
|
||||
|
||||
|
||||
#### The final check
|
||||
|
||||
Discovery
|
||||
```bash
|
||||
./discovery.php -h HOSTNAME
|
||||
```
|
||||
|
||||
Polling
|
||||
```bash
|
||||
./poller.php -h HOSTNAME
|
||||
```
|
||||
|
||||
At this step we should see all the values retrieved in LibreNMS.
|
||||
|
||||
### OS Test units
|
||||
We have a testing unit for new OS', please ensure you add a test for any new OS' or updates to existing OS discovery.
|
||||
|
||||
The OS test unit file is located `tests/OSDiscoveryTest.php`. An example of this is as follows:
|
||||
|
||||
```php
|
||||
public function testNios()
|
||||
{
|
||||
$this->checkOS('nios');
|
||||
$this->checkOS('nios', 'nios-ipam');
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
We utilise [snmpsim](http://snmpsim.sourceforge.net/) to do unit testing for OS discovery. For this to work you need
|
||||
to supply an snmprec file. This is pretty simple and using nios as the example again this would look like:
|
||||
```
|
||||
1.3.6.1.2.1.1.1.0|4|Linux 3.14.25 #1 SMP Thu Jun 16 18:19:37 EDT 2016 x86_64
|
||||
1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.7779.1.1402
|
||||
```
|
||||
|
||||
During testing LibreNMS will use any info in the snmprec file for snmp calls. This one provides
|
||||
sysDescr (`.1.3.6.1.2.1.1.1.0`, 4 = Octet String) and sysObjectID (`.1.3.6.1.2.1.1.2.0`, 6 = Object Identifier),
|
||||
which is the minimum that should be provided for new snmprec files.
|
||||
|
||||
To look up the numeric OID and type of an string OID with snmptranslate:
|
||||
```bash
|
||||
snmptranslate -On -Td SNMPv2-MIB::sysDescr.0
|
||||
```
|
||||
|
||||
Common OIDs used in discovery:
|
||||
|
||||
| String OID | Numeric OID |
|
||||
| ----------------------------------- | --------------------------- |
|
||||
| SNMPv2-MIB::sysDescr.0 | 1.3.6.1.2.1.1.1.0 |
|
||||
| SNMPv2-MIB::sysObjectID.0 | 1.3.6.1.2.1.1.2.0 |
|
||||
| ENTITY-MIB::entPhysicalDescr.1 | 1.3.6.1.2.1.47.1.1.1.1.2.1 |
|
||||
| ENTITY-MIB::entPhysicalMfgName.1 | 1.3.6.1.2.1.47.1.1.1.1.12.1 |
|
||||
| SML-MIB::product-Name.0 | 1.3.6.1.4.1.2.6.182.3.3.1.0 |
|
||||
|
||||
List of SNMP data types:
|
||||
|
||||
| Type | Value |
|
||||
| ----------------- | ------------- |
|
||||
| OCTET STRING | 4 |
|
||||
| HEX STRING | 4x |
|
||||
| Integer32 | 2 |
|
||||
| NULL | 5 |
|
||||
| OBJECT IDENTIFIER | 6 |
|
||||
| IpAddress | 64 |
|
||||
| Counter32 | 65 |
|
||||
| Gauge32 | 66 |
|
||||
| TimeTicks | 67 |
|
||||
| Opaque | 68 |
|
||||
| Counter64 | 70 |
|
||||
|
||||
Hex encoded strings (4x) should be used for any strings that contain line returns
|
||||
|
||||
You can run `./scripts/pre-commit.php -u` to run the unit tests to check your code.
|
||||
If you would like to run tests locally against a full snmpsim instance, run `./scripts/pre-commit.php -u --snmpsim`.
|
||||
> - [Adding the initial detection.](os/Initial-Detection.md)
|
||||
> - [Adding Memory and CPU information.](os/Mem-CPU-Information.md)
|
||||
> - [Adding Health / Sensor information.](os/Health-Information.md)
|
||||
> - [Adding custom graphs.](os/Custom-Graphs.md)
|
||||
> - [Adding Unit tests (required).](os/Test-Units.md)
|
||||
134
doc/Developing/os/Custom-Graphs.md
Normal file
134
doc/Developing/os/Custom-Graphs.md
Normal file
@@ -0,0 +1,134 @@
|
||||
source: Developing/os/Custom-Graphs.md
|
||||
|
||||
If you are adding custom graphs, please add the following to `includes/definitions.inc.php`:
|
||||
```php
|
||||
//Don't forget to declare the specific graphs if needed. It will be located near the end of the file.
|
||||
|
||||
//Pulse Secure Graphs
|
||||
$config['graph_types']['device']['pulse_users']['section'] = 'firewall';
|
||||
$config['graph_types']['device']['pulse_users']['order'] = '0';
|
||||
$config['graph_types']['device']['pulse_users']['descr'] = 'Active Users';
|
||||
$config['graph_types']['device']['pulse_sessions']['section'] = 'firewall';
|
||||
$config['graph_types']['device']['pulse_sessions']['order'] = '0';
|
||||
$config['graph_types']['device']['pulse_sessions']['descr'] = 'Active Sessions';
|
||||
```
|
||||
|
||||
#### Polling OS
|
||||
|
||||
OS polling is not necessarily where custom polling should be done, please speak to one of the core devs in irc for guidance.
|
||||
|
||||
Let's update our example file to add additional polling:
|
||||
|
||||
```bash
|
||||
includes/polling/os/pulse.inc.php
|
||||
```
|
||||
We declare two specific graphs for users and sessions numbers. Theses two graphs will be displayed on the firewall section of the graphs tab as it was written in the definition include file.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$users = snmp_get($device, 'iveConcurrentUsers.0', '-OQv', 'PULSESECURE-PSG-MIB');
|
||||
|
||||
if (is_numeric($users)) {
|
||||
$rrd_def = 'DS:users:GAUGE:600:0:U';
|
||||
|
||||
$fields = array(
|
||||
'users' => $users,
|
||||
);
|
||||
|
||||
$tags = compact('rrd_def');
|
||||
data_update($device, 'pulse_users', $tags, $fields);
|
||||
$graphs['pulse_users'] = true;
|
||||
}
|
||||
|
||||
$sessions = snmp_get($device, 'iveConcurrentUsers.0', '-OQv', 'PULSESECURE-PSG-MIB');
|
||||
|
||||
if (is_numeric($sessions)) {
|
||||
$rrd_def = 'DS:sessions:GAUGE:600:0:U';
|
||||
|
||||
$fields = array(
|
||||
'sessions' => $sessions,
|
||||
);
|
||||
|
||||
$tags = compact('rrd_def');
|
||||
data_update($device, 'pulse_sessions', $tags, $fields);
|
||||
$graphs['pulse_sessions'] = true;
|
||||
}
|
||||
```
|
||||
We finish in the declaration of the two graph types in the database:
|
||||
|
||||
We can do that within a file to share our work and contribute in the development of LibreNMS. :-)
|
||||
|
||||
```bash
|
||||
sql-schema/xxx.sql
|
||||
php includes/sql-schema/update.php
|
||||
./scripts/build-schema.php
|
||||
```
|
||||
|
||||
Or put the SQL commands directly in Mysql or PhpMyadmin for our tests:
|
||||
|
||||
```php
|
||||
INSERT INTO `graph_types`(`graph_type`, `graph_subtype`, `graph_section`, `graph_descr`, `graph_order`) VALUES ('device', 'pulse_users', 'firewall', 'Active Users', '');
|
||||
INSERT INTO `graph_types`(`graph_type`, `graph_subtype`, `graph_section`, `graph_descr`, `graph_order`) VALUES ('device', 'pulse_sessions', 'firewall', 'Active Sessions', '');
|
||||
```
|
||||
|
||||
#### Displaying
|
||||
|
||||
The specific graphs are not displayed automatically so we need to write the following PHP code:
|
||||
|
||||
**Pulse Sessions**
|
||||
|
||||
```bash
|
||||
html/includes/graphs/device/pulse_sessions.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$rrd_filename = rrd_name($device['hostname'], 'pulse_sessions');
|
||||
|
||||
require 'includes/graphs/common.inc.php';
|
||||
|
||||
$ds = 'sessions';
|
||||
|
||||
$colour_area = '9999cc';
|
||||
$colour_line = '0000cc';
|
||||
|
||||
$colour_area_max = '9999cc';
|
||||
|
||||
$graph_max = 1;
|
||||
$graph_min = 0;
|
||||
|
||||
$unit_text = 'Sessions';
|
||||
|
||||
require 'includes/graphs/generic_simplex.inc.php';
|
||||
```
|
||||
|
||||
**Pulse Users**
|
||||
|
||||
```bash
|
||||
html/includes/graphs/device/pulse_users.inc.php
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$rrd_filename = rrd_name($device['hostname'], 'pulse_users');
|
||||
|
||||
require 'includes/graphs/common.inc.php';
|
||||
|
||||
$ds = 'users';
|
||||
|
||||
$colour_area = '9999cc';
|
||||
$colour_line = '0000cc';
|
||||
|
||||
$colour_area_max = '9999cc';
|
||||
|
||||
$graph_max = 1;
|
||||
|
||||
$unit_text = 'Users';
|
||||
|
||||
require 'includes/graphs/generic_simplex.inc.php';
|
||||
```
|
||||
|
||||
That should be it, after data has started to be collected graphs should appear in the WebUI.
|
||||
53
doc/Developing/os/Health-Information.md
Normal file
53
doc/Developing/os/Health-Information.md
Normal file
@@ -0,0 +1,53 @@
|
||||
source: Developing/os/Health-Information.md
|
||||
|
||||
This document will guide you through adding health / sensor information for your new device.
|
||||
|
||||
Currently we have support for the following health metrics along with the values we expect to see the data in:
|
||||
|
||||
| Class | Measurement |
|
||||
| ------------------------------- | --------------------------- |
|
||||
| airflow | cfm |
|
||||
| charge | % |
|
||||
| current | A |
|
||||
| dbm | dBm |
|
||||
| fanspeed | rpm |
|
||||
| frequency | Hz |
|
||||
| humidity | % |
|
||||
| load | % |
|
||||
| power | W |
|
||||
| runtime | Min |
|
||||
| signal | dBm |
|
||||
| state | # |
|
||||
| temperature | C |
|
||||
| voltage | V |
|
||||
|
||||
The directory structure for sensor information is `includes/discovery/sensors/$class/$os.inc.php`. The format of all
|
||||
of the sensors follows the same code format which is to call the `discover_sensor()` function - with the
|
||||
exception of state which requires additional code.
|
||||
|
||||
`discover_sensor()` Accepts the following arguments:
|
||||
|
||||
- &$valid = This is always $valid['sensor'], do not pass any other values.
|
||||
- $class = Required. This is the sensor class from the table above (i.e humidity).
|
||||
- $device = Required. This is the $device array.
|
||||
- $oid = Required. This must be the numerical OID for where the data can be found, i.e .1.2.3.4.5.6.7.0
|
||||
- $index = Required. This must be unique for this sensor class, device and type.
|
||||
Typically it's the index from the table being walked or it could be the name of the OID if it's a single value.
|
||||
- $type = Required. This should be the OS name, i.e pulse.
|
||||
- $descr = Required. This is a descriptive value for the sensor. Some devices will provide names to use.
|
||||
- $divisor = Defaults to 1. This is used to divied the returned value.
|
||||
- $multiplier = Defaults to 1. This is used to multiply the returned value.
|
||||
- $low_limit = Defaults to null. Sets the low threshold limit for the sensor, used in alerting to report out range sensors.
|
||||
- $low_warn_limit = Defaults to null. Sets the low warning limit for the sensor, used in alerting to report near out of range sensors.
|
||||
- $warn_limit = Defaults to null. Sets the high warning limit for the sensor, used in alerting to report near out of range sensors.
|
||||
- $high_limit = Defaults to null. Sets the high limit for the sensor, used in alerting to report out range sensors.
|
||||
- $current = Defaults to null. Can be used to set the current value on discovery. Poller will update this on the next poll cycle anyway.
|
||||
- $poller_type = Defaults to snmp. Things like the unix-agent can set different values but for the most part this should be left as snmp.
|
||||
- $entPhysicalIndex = Defaults to null. Sets the entPhysicalIndex to be used to look up further hardware if available.
|
||||
- $entPhysicalIndex_measured = Defaults to null. Sets the type of entPhysicalIndex used, i.e ports.
|
||||
|
||||
For the majority of devices, this is all that's required to add support for a sensor. Polling is done based on the data gathered using `discover_sensor()`.
|
||||
If custom polling is needed then the file format is similar to discovery: `includes/polling/sensors/$class/$os.inc.php`. Whilst it's possible to perform additional
|
||||
snmp queries within polling this should be avoided where possible. The value for the OID is already available as `$sensor_value`.
|
||||
|
||||
Graphing is performed automatically for sensors, no custom graphing is required or supported.
|
||||
197
doc/Developing/os/Initial-Detection.md
Normal file
197
doc/Developing/os/Initial-Detection.md
Normal file
@@ -0,0 +1,197 @@
|
||||
source: Developing/os/Initial-Detection.md
|
||||
|
||||
This document will provide the information you should need to add basic detection for a new OS.
|
||||
|
||||
### Discovery
|
||||
|
||||
Discovery is now all done by yaml files, you do not and should not create a php file for discovery.
|
||||
|
||||
Create the new OS file which should be called `includes/definitions/pulse.yaml`. Here is a working example:
|
||||
|
||||
```yaml
|
||||
os: pulse
|
||||
text: 'Pulse Secure'
|
||||
type: firewall
|
||||
icon: pulse
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
- sysObjectId:
|
||||
- .1.3.6.1.4.1.12532.
|
||||
```
|
||||
|
||||
`over`: This is a list of the graphs which will be shown within the device header bar (mini graphs top right).
|
||||
|
||||
`discovery`: Here we are detecting this new OS using sysObjectId, this is the preferred method for detection.
|
||||
Other options are available:
|
||||
|
||||
- `sysObjectId` The preferred operator. Checks if the sysObjectID starts with one of the strings under this item
|
||||
- `sysDescr` Use this in addition to sysObjectId if required. Check that the sysDescr contains one of the strings under this item
|
||||
- `sysDescr_regex` Please avoid use of this. Checks if the sysDescr matches one of the regex statements under this item
|
||||
- `_except` You can add this to any of the above to exclude that element. As an example:
|
||||
|
||||
```yaml
|
||||
discovery:
|
||||
-
|
||||
sysObjectId:
|
||||
- .1.3.6.1.4.1.12532.
|
||||
sysDescr_except:
|
||||
- 'Not some pulse'
|
||||
```
|
||||
|
||||
`group`: You can group certain OS' together by using group, for instance ios, nx-os, iosxr are all within a group
|
||||
called cisco.
|
||||
|
||||
`bad_ifXEntry`: This is a list of models for which to tell LibreNMS that the device doesn't support ifXEntry and to
|
||||
ignore it:
|
||||
|
||||
```yaml
|
||||
bad_ifXEntry:
|
||||
- cisco1941
|
||||
- cisco886Va
|
||||
- cisco2811
|
||||
```
|
||||
|
||||
`mib_dir`: You can use this to specify the additional directories to look in for MIBs:
|
||||
|
||||
```yaml
|
||||
mib_dir:
|
||||
- juniper
|
||||
- cisco
|
||||
```
|
||||
|
||||
`poller_modules`: This is a list of poller modules to either enable (1) or disable (0). Check `includes/defaults.inc.php` to see which modules are enabled/disabled by default.
|
||||
|
||||
```yaml
|
||||
poller_modules:
|
||||
wifi: 1
|
||||
cisco-ace-serverfarms: 0
|
||||
cisco-ace-loadbalancer: 0
|
||||
```
|
||||
|
||||
`discovery_modules`: This is the list of discovery modules to either enable (1) or disable (0). Check `includes/defaults.inc.php` to see which modules are enabled/disabled by default.
|
||||
|
||||
```yaml
|
||||
discovery_modules:
|
||||
cisco-cef: 1
|
||||
cisco-sla: 1
|
||||
cisco-mac-accounting: 0
|
||||
```
|
||||
|
||||
|
||||
##### Discovery Logic
|
||||
YAML is converted to an array in PHP. Consider the following YAML:
|
||||
```yaml
|
||||
discovery:
|
||||
- sysObjectId: foo
|
||||
-
|
||||
sysDescr: [ snafu, exodar ]
|
||||
sysObjectId: bar
|
||||
|
||||
```
|
||||
This is how the discovery array would look in PHP:
|
||||
```php
|
||||
[
|
||||
[
|
||||
"sysObjectId" => "foo",
|
||||
],
|
||||
[
|
||||
"sysDescr" => [
|
||||
"snafu",
|
||||
"exodar",
|
||||
],
|
||||
"sysObjectId" => "bar",
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
The logic for the discovery is as follows:
|
||||
1. One of the first level items must match
|
||||
2. ALL of the second level items must match (sysObjectId, sysDescr)
|
||||
3. One of the third level items (foo, [snafu,exodar], bar) must match
|
||||
|
||||
So, considering the example:
|
||||
- `sysObjectId: foo, sysDescr: ANYTHING` matches
|
||||
- `sysObjectId: bar, sysDescr: ANYTHING` does not match
|
||||
- `sysObjectId: bar, sysDescr: exodar` matches
|
||||
- `sysObjectId: bar, sysDescr: snafu` matches
|
||||
|
||||
|
||||
### Poller
|
||||
|
||||
OS polling is done within `includes/polling/os/$os.inc.php` and is where we detect certain values.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$version = preg_replace('/[\r\n\"]+/', ' ', snmp_get($device, "productVersion.0", "-OQv", "PULSESECURE-PSG-MIB"));
|
||||
$hardware = "Juniper " . preg_replace('/[\r\n\"]+/', ' ', snmp_get($device, "productName.0", "-OQv", "PULSESECURE-PSG-MIB"));
|
||||
|
||||
```
|
||||
|
||||
`$version`: The version of the OS running on the device.
|
||||
|
||||
`$hardware`: The hardware version for the device. For example: 'WS-C3560X-24T-S'
|
||||
|
||||
`$features`: Features for the device, for example a list of cards in the slots of a modular chassis.
|
||||
|
||||
`$serial`: The main serial number of the device.
|
||||
|
||||
### MIBs
|
||||
|
||||
If the device has MIBs available and you use it in the detection then you can add these in. It is highly
|
||||
recommended that you add mibs to a vendor specific directory. For instance HP mibs are in `mibs/hp`. Please
|
||||
ensure that these directories are specified in the yaml detection file, see `mib_dir` above.
|
||||
|
||||
### Icon and Logo
|
||||
|
||||
It is highly recommended to use SVG images where possible, these scale and provide a nice visual image for users
|
||||
with HiDPI screens. If you can't find SVG images then please use png.
|
||||
|
||||
Create an SVG image of the icon and logo. Legacy PNG bitmaps are also supported but look bad on HiDPI.
|
||||
- A vector image should not contain padding.
|
||||
- The file should not be larger than 20 Kb. Simplify paths to reduce large files.
|
||||
- Use plain SVG without gzip compression.
|
||||
- The SVG root element must not contain length and width attributes, only viewBox.
|
||||
|
||||
##### Icon
|
||||
- Save the icon SVG to `html/images/os/$os.svg`.
|
||||
- Icons should look good when viewed at 32x32 px.
|
||||
- Square icons are preferred to full logos with text.
|
||||
- Remove small ornaments that are almost not visible when displayed with 32px width (e.g. ® or ™).
|
||||
|
||||
##### Logo
|
||||
- Save the logo SVG to `html/images/logos/$os.svg`.
|
||||
- Logos can be any dimension, but often are wide and contain the company name.
|
||||
- If a logo is not present, the icon will be used.
|
||||
|
||||
##### Hints
|
||||
|
||||
Hints for [Inkscape](https://inkscape.org/):
|
||||
|
||||
- You can open a PDF or EPS to extract the logo.
|
||||
- Ungroup elements to isolate the logo.
|
||||
- Use `Path -> Simplify` to simplify paths of large files.
|
||||
- Use `File -> Document Properties… -> Resize page to content…` to remove padding.
|
||||
- Use `File -> Clean up document` to remove unused gradients, patterns, or markers.
|
||||
- Use `File -> Save As -> Plain SVG` to save the final image.
|
||||
|
||||
By optimizing the SVG you can shrink the file size in some cases to less than 20 %.
|
||||
[SVG Optimizer](https://github.com/svg/svgo) does a great job. There is also an [online version](https://jakearchibald.github.io/svgomg/).
|
||||
|
||||
#### The final check
|
||||
|
||||
Discovery
|
||||
```bash
|
||||
./discovery.php -h HOSTNAME
|
||||
```
|
||||
|
||||
Polling
|
||||
```bash
|
||||
./poller.php -h HOSTNAME
|
||||
```
|
||||
|
||||
At this step we should see all the values retrieved in LibreNMS.
|
||||
75
doc/Developing/os/Mem-CPU-Information.md
Normal file
75
doc/Developing/os/Mem-CPU-Information.md
Normal file
@@ -0,0 +1,75 @@
|
||||
source: Developing/os/Mem-CPU-Information.md
|
||||
|
||||
This document will guide you through adding detection for Memory / Processor for your new device.
|
||||
|
||||
#### Memory
|
||||
|
||||
Detection for memory is done via two php scripts, one for discovery and the other for polling:
|
||||
|
||||
`includes/discovery/mempools/pulse.inc.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
if ($device['os'] === 'pulse') {
|
||||
echo 'PULSE-MEMORY-POOL: ';
|
||||
|
||||
$usage = str_replace('"', "", snmp_get($device, 'iveMemoryUtil.0', '-OvQ', 'PULSESECURE-PSG-MIB'));
|
||||
|
||||
if (is_numeric($usage)) {
|
||||
discover_mempool($valid_mempool, $device, 0, 'pulse-mem', 'Main Memory', '100', null, null);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`includes/polling/mempools/pulse.inc.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
echo 'Pulse Secure MemPool\n';
|
||||
|
||||
$perc = str_replace('"', "", snmp_get($device, "iveMemoryUtil.0", '-OvQ', 'PULSESECURE-PSG-MIB'));
|
||||
|
||||
if (is_numeric($perc)) {
|
||||
$memory_available = str_replace('"', "", snmp_get($device, "memTotalReal.0", '-OvQ', 'UCD-SNMP-MIB'));
|
||||
$mempool['total'] = $memory_available;
|
||||
$mempool['used'] = ($memory_available / 100 * $perc);
|
||||
$mempool['free'] = ($memory_available - $mempool['used']);
|
||||
}
|
||||
```
|
||||
|
||||
#### Processor
|
||||
|
||||
Detection for processors is done via a single script unless custom processing of data is required (as in this example).
|
||||
|
||||
`includes/discovery/processors/pulse.inc.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
if ($device['os'] === 'pulse') {
|
||||
echo 'Pulse Secure : ';
|
||||
|
||||
$descr = 'Processor';
|
||||
$usage = str_replace('"', "", snmp_get($device, 'iveCpuUtil.0', '-OvQ', 'PULSESECURE-PSG-MIB'));
|
||||
|
||||
if (is_numeric($usage)) {
|
||||
discover_processor($valid['processor'], $device, 'iveCpuUtil.0', '0', 'pulse-cpu', $descr, '100', $usage, null, null);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`includes/polling/processors/pulse.inc.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
echo 'Pulse Secure CPU Usage';
|
||||
|
||||
$usage = str_replace('"', "", snmp_get($device, 'iveCpuUtil.0', '-OvQ', 'PULSESECURE-PSG-MIB'));
|
||||
|
||||
if (is_numeric($usage)) {
|
||||
$proc = ($usage * 100);
|
||||
}
|
||||
```
|
||||
71
doc/Developing/os/Test-Units.md
Normal file
71
doc/Developing/os/Test-Units.md
Normal file
@@ -0,0 +1,71 @@
|
||||
source: Developing/os/Test-Units.md
|
||||
|
||||
We have a testing unit for new OS', please ensure you add a test for any new OS' or updates to existing OS discovery.
|
||||
|
||||
The OS test unit file is located `tests/OSDiscoveryTest.php`. An example of this is as follows:
|
||||
|
||||
```php
|
||||
public function testPulse()
|
||||
{
|
||||
$this->checkOS('pulse');
|
||||
$this->checkOS('pulse', 'pulse-mag2600');
|
||||
$this->checkOS('pulse', 'pulse-sa2500');
|
||||
$this->checkOS('pulse', 'pulse-sa6500');
|
||||
$this->checkOS('pulse', 'pulse-vaspe');
|
||||
$this->checkOS('pulse', 'pulse-sa');
|
||||
}
|
||||
```
|
||||
|
||||
The above example has multiple test files, the first argument in the function call is the os name we expect the test
|
||||
to pass as, the second argument is the filename to use for the test. If not filename is passed then the os name is used
|
||||
for the file.
|
||||
|
||||
We utilise [snmpsim](http://snmpsim.sourceforge.net/) to do unit testing for OS discovery. For this to work you need
|
||||
to supply an snmprec file. This is pretty simple and using pulse as the example again this would look like:
|
||||
|
||||
`tests/snmpsim/pulse-mag2600.snmprec`
|
||||
```
|
||||
1.3.6.1.2.1.1.1.0|4|Pulse Secure,LLC,MAG-2600,8.0R14 (build 41869)
|
||||
1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.12532.254.1.1
|
||||
```
|
||||
|
||||
During testing LibreNMS will use any info in the snmprec file for snmp calls. This one provides
|
||||
sysDescr (`.1.3.6.1.2.1.1.1.0`, 4 = Octet String) and sysObjectID (`.1.3.6.1.2.1.1.2.0`, 6 = Object Identifier),
|
||||
which is the minimum that should be provided for new snmprec files.
|
||||
|
||||
To look up the numeric OID and type of an string OID with snmptranslate:
|
||||
```bash
|
||||
snmptranslate -On -Td SNMPv2-MIB::sysDescr.0
|
||||
```
|
||||
|
||||
Common OIDs used in discovery:
|
||||
|
||||
| String OID | Numeric OID |
|
||||
| ----------------------------------- | --------------------------- |
|
||||
| SNMPv2-MIB::sysDescr.0 | 1.3.6.1.2.1.1.1.0 |
|
||||
| SNMPv2-MIB::sysObjectID.0 | 1.3.6.1.2.1.1.2.0 |
|
||||
| ENTITY-MIB::entPhysicalDescr.1 | 1.3.6.1.2.1.47.1.1.1.1.2.1 |
|
||||
| ENTITY-MIB::entPhysicalMfgName.1 | 1.3.6.1.2.1.47.1.1.1.1.12.1 |
|
||||
| SML-MIB::product-Name.0 | 1.3.6.1.4.1.2.6.182.3.3.1.0 |
|
||||
|
||||
List of SNMP data types:
|
||||
|
||||
| Type | Value |
|
||||
| ----------------- | ------------- |
|
||||
| OCTET STRING | 4 |
|
||||
| HEX STRING | 4x |
|
||||
| Integer32 | 2 |
|
||||
| NULL | 5 |
|
||||
| OBJECT IDENTIFIER | 6 |
|
||||
| IpAddress | 64 |
|
||||
| Counter32 | 65 |
|
||||
| Gauge32 | 66 |
|
||||
| TimeTicks | 67 |
|
||||
| Opaque | 68 |
|
||||
| Counter64 | 70 |
|
||||
|
||||
Hex encoded strings (4x) should be used for any strings that contain line returns.
|
||||
|
||||
You can run `./scripts/pre-commit.php -u` to run the unit tests to check your code.
|
||||
|
||||
If you would like to run tests locally against a full snmpsim instance, run `./scripts/pre-commit.php -u --snmpsim`.
|
||||
@@ -83,7 +83,7 @@ pages:
|
||||
- Developing/Code-Structure.md
|
||||
|
||||
- Developing/Creating-Documentation.md
|
||||
- Developing/Support-New-OS.md
|
||||
- Adding support for a new OS: Developing/Support-New-OS.md
|
||||
- Developing/Dynamic-Config.md
|
||||
- Developing/Sensor-State-Support.md
|
||||
|
||||
@@ -112,3 +112,8 @@ pages:
|
||||
- Support/Support-New-OS.md
|
||||
- Extensions/Agent-Setup.md
|
||||
- Extensions/RRDCached-Security.md
|
||||
- Developing/os/Initial-Detection.md
|
||||
- Developing/os/Mem-CPU-Information.md
|
||||
- Developing/os/Test-Units.md
|
||||
- Developing/os/Health-Information.md
|
||||
- Developing/os/Custom-Graphs.md
|
||||
|
||||
Reference in New Issue
Block a user