mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Improved Modern Modules (#14315)
* Improved Modern Modules Modules now report module dependencies and can dump data for testing This should serve to the process of building a module more obvious. cleanup now only requires a device, not an os wrapped around a device Helper to create a modern module (including the legacy adapter) from a name. * return false correctly for dump and handle it. * make sure test data is in the right format. * wrong isis table name * sort * Fix style and lint issues
This commit is contained in:
@@ -28,24 +28,20 @@ namespace LibreNMS\Util;
|
||||
use App\Actions\Device\ValidateDeviceAndCreate;
|
||||
use App\Models\Device;
|
||||
use DeviceCache;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use LibreNMS\Component;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Data\Source\SnmpResponse;
|
||||
use LibreNMS\Exceptions\FileNotFoundException;
|
||||
use LibreNMS\Exceptions\InvalidModuleException;
|
||||
use LibreNMS\Poller;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ModuleTestHelper
|
||||
{
|
||||
private static $module_tables;
|
||||
|
||||
private $quiet = false;
|
||||
private $modules;
|
||||
private $variant;
|
||||
private $os;
|
||||
private $snmprec_file;
|
||||
private $json_file;
|
||||
private $snmprec_dir;
|
||||
@@ -59,18 +55,6 @@ class ModuleTestHelper
|
||||
// Definitions
|
||||
// ignore these when dumping all modules
|
||||
private $exclude_from_all = ['arp-table', 'fdb-table'];
|
||||
private static $module_deps = [
|
||||
'arp-table' => ['ports', 'arp-table'],
|
||||
'cisco-mac-accounting' => ['ports', 'cisco-mac-accounting'],
|
||||
'fdb-table' => ['ports', 'vlans', 'fdb-table'],
|
||||
'isis' => ['ports', 'isis'],
|
||||
'mpls' => ['ports', 'vrf', 'mpls'],
|
||||
'nac' => ['ports', 'nac'],
|
||||
'ospf' => ['ports', 'ospf'],
|
||||
'stp' => ['ports', 'vlans', 'stp'],
|
||||
'vlans' => ['ports', 'vlans'],
|
||||
'vrf' => ['ports', 'vrf'],
|
||||
];
|
||||
|
||||
/**
|
||||
* ModuleTester constructor.
|
||||
@@ -84,7 +68,6 @@ class ModuleTestHelper
|
||||
public function __construct($modules, $os, $variant = '')
|
||||
{
|
||||
$this->modules = self::resolveModuleDependencies((array) $modules);
|
||||
$this->os = strtolower($os);
|
||||
$this->variant = strtolower($variant);
|
||||
|
||||
// preset the file names
|
||||
@@ -104,11 +87,6 @@ class ModuleTestHelper
|
||||
Config::set('influxdb.enable', false);
|
||||
Config::set('graphite.enable', false);
|
||||
Config::set('prometheus.enable', false);
|
||||
|
||||
if (is_null(self::$module_tables)) {
|
||||
// only load the yaml once, then keep it in memory
|
||||
self::$module_tables = Yaml::parse(file_get_contents($install_dir . '/tests/module_tables.yaml'));
|
||||
}
|
||||
}
|
||||
|
||||
private static function compareOid($a, $b)
|
||||
@@ -217,10 +195,12 @@ class ModuleTestHelper
|
||||
preg_match_all($snmp_query_regex, $collection_output, $snmp_matches);
|
||||
|
||||
// extract mibs and group with oids
|
||||
$snmp_oids = [null => [
|
||||
'sysDescr.0_get' => ['oid' => 'sysDescr.0', 'mib' => 'SNMPv2-MIB', 'method' => 'get'],
|
||||
'sysObjectID.0_get' => ['oid' => 'sysObjectID.0', 'mib' => 'SNMPv2-MIB', 'method' => 'get'],
|
||||
]];
|
||||
$snmp_oids = [
|
||||
null => [
|
||||
'sysDescr.0_get' => ['oid' => 'sysDescr.0', 'mib' => 'SNMPv2-MIB', 'method' => 'get'],
|
||||
'sysObjectID.0_get' => ['oid' => 'sysObjectID.0', 'mib' => 'SNMPv2-MIB', 'method' => 'get'],
|
||||
],
|
||||
];
|
||||
foreach ($snmp_matches[0] as $index => $line) {
|
||||
preg_match("/'-m' '\+?([a-zA-Z0-9:\-]+)'/", $line, $mib_matches);
|
||||
$mib = $mib_matches[1];
|
||||
@@ -335,7 +315,7 @@ class ModuleTestHelper
|
||||
*
|
||||
* @throws InvalidModuleException
|
||||
*/
|
||||
private static function resolveModuleDependencies($modules)
|
||||
private static function resolveModuleDependencies(array $modules): array
|
||||
{
|
||||
// generate a full list of modules
|
||||
$full_list = [];
|
||||
@@ -345,11 +325,8 @@ class ModuleTestHelper
|
||||
throw new InvalidModuleException("Invalid module name: $module");
|
||||
}
|
||||
|
||||
if (isset(self::$module_deps[$module])) {
|
||||
$full_list = array_merge($full_list, self::$module_deps[$module]);
|
||||
} else {
|
||||
$full_list[] = $module;
|
||||
}
|
||||
$full_list = array_merge($full_list, Module::fromName($module)->dependencies());
|
||||
$full_list[] = $module;
|
||||
}
|
||||
|
||||
return array_unique($full_list);
|
||||
@@ -720,76 +697,17 @@ class ModuleTestHelper
|
||||
public function dumpDb($device_id, $modules, $type)
|
||||
{
|
||||
$data = [];
|
||||
$module_dump_info = $this->getTableData();
|
||||
|
||||
// don't dump some modules by default unless they are manually listed
|
||||
if (empty($this->modules)) {
|
||||
$modules = array_diff($modules, $this->exclude_from_all);
|
||||
}
|
||||
|
||||
// only dump data for the given modules
|
||||
// only dump data for the given modules (and modules that support dumping)
|
||||
foreach ($modules as $module) {
|
||||
foreach ($module_dump_info[$module] ?? [] as $table => $info) {
|
||||
if ($table == 'component') {
|
||||
$components = $this->collectComponents($device_id);
|
||||
if (! empty($components)) {
|
||||
$data[$module][$type][$table] = $components;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for custom where
|
||||
$where = isset($info['custom_where']) ? $info['custom_where'] : "WHERE `$table`.`device_id`=?";
|
||||
$params = [$device_id];
|
||||
|
||||
// build joins
|
||||
$join = '';
|
||||
$select = ["`$table`.*"];
|
||||
foreach ($info['joins'] ?? [] as $join_info) {
|
||||
if (isset($join_info['custom'])) {
|
||||
$join .= ' ' . $join_info['custom'];
|
||||
|
||||
$default_select = [];
|
||||
} else {
|
||||
[$left, $lkey] = explode('.', $join_info['left']);
|
||||
[$right, $rkey] = explode('.', $join_info['right']);
|
||||
$join .= " LEFT JOIN `$right` ON (`$left`.`$lkey` = `$right`.`$rkey`)";
|
||||
|
||||
$default_select = ["`$right`.*"];
|
||||
}
|
||||
|
||||
// build selects
|
||||
$select = array_merge($select, isset($join_info['select']) ? (array) $join_info['select'] : $default_select);
|
||||
}
|
||||
|
||||
if (isset($info['order_by'])) {
|
||||
$order_by = " ORDER BY {$info['order_by']}";
|
||||
} else {
|
||||
$order_by = '';
|
||||
}
|
||||
|
||||
$fields = implode(', ', $select);
|
||||
$rows = dbFetchRows("SELECT $fields FROM `$table` $join $where $order_by", $params);
|
||||
|
||||
// don't include empty tables
|
||||
if (empty($rows)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove unwanted fields
|
||||
if (isset($info['included_fields'])) {
|
||||
$keys = array_flip($info['included_fields']);
|
||||
$rows = array_map(function ($row) use ($keys) {
|
||||
return array_intersect_key($row, $keys);
|
||||
}, $rows);
|
||||
} elseif (isset($info['excluded_fields'])) {
|
||||
$keys = array_flip($info['excluded_fields']);
|
||||
$rows = array_map(function ($row) use ($keys) {
|
||||
return array_diff_key($row, $keys);
|
||||
}, $rows);
|
||||
}
|
||||
|
||||
$data[$module][$type][$table] = $rows;
|
||||
$module_data = Module::fromName($module)->dump(DeviceCache::get($device_id));
|
||||
if ($module_data !== false) {
|
||||
$data[$module][$type] = $this->dumpToArray($module_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,14 +715,22 @@ class ModuleTestHelper
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of tables used by a module
|
||||
* Includes a list of fields that will not be considered for testing
|
||||
*
|
||||
* @param array|\Illuminate\Support\Collection|\stdClass $data
|
||||
* @return array
|
||||
*/
|
||||
public function getTableData()
|
||||
private function dumpToArray($data): array
|
||||
{
|
||||
return array_intersect_key(self::$module_tables, array_flip($this->getModules()));
|
||||
$output = [];
|
||||
|
||||
foreach ($data as $table => $table_data) {
|
||||
foreach ($table_data as $item) {
|
||||
$output[$table][] = is_a($item, Model::class)
|
||||
? Arr::except($item->getAttributes(), $item->getHidden()) // don't apply accessors
|
||||
: (array) $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -847,27 +773,6 @@ class ModuleTestHelper
|
||||
return $this->poller_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all modules that support capturing data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedModules()
|
||||
{
|
||||
return array_keys(self::$module_tables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of modules to capture data for
|
||||
* If modules is empty, returns all supported modules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getModules()
|
||||
{
|
||||
return empty($this->modules) ? $this->getSupportedModules() : $this->modules;
|
||||
}
|
||||
|
||||
public function getTestData()
|
||||
{
|
||||
return json_decode(file_get_contents($this->json_file), true);
|
||||
@@ -881,14 +786,4 @@ class ModuleTestHelper
|
||||
|
||||
return $this->json_file;
|
||||
}
|
||||
|
||||
private function collectComponents(int $device_id): array
|
||||
{
|
||||
$components = (new Component())->getComponents($device_id)[$device_id] ?? [];
|
||||
$components = Arr::sort($components, function ($item) {
|
||||
return $item['type'] . $item['label'];
|
||||
});
|
||||
|
||||
return array_values($components);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user