Support device group definitions from v2 (#4591)

* Support device group definitions from v2
Disable editing v2 groups.

V2 Device groups are defined as follows:
pattern = WHERE query with ? placeholders for values
params = json encoded array of values

* Can't array_unshift something that isn't an array...
This commit is contained in:
Tony Murray
2016-09-27 11:50:52 -05:00
committed by Neil Lathwood
parent 4f90d389c6
commit 02b84cf3f7
4 changed files with 77 additions and 24 deletions

View File

@@ -17,8 +17,7 @@ if (is_admin() === false) {
}
$rule = implode(' ', $_POST['rules']);
$rule = rtrim($rule, '&&');
$rule = rtrim($rule, '||');
$rule = rtrim($rule, '&|');
$alert_id = $_POST['alert_id'];
$count = mres($_POST['count']);
$delay = mres($_POST['delay']);

View File

@@ -14,9 +14,13 @@ if (!empty($group_count_check)) {
echo '<tr id="row_'.$group['id'].'">';
echo '<td>'.$group['name'].'</td>';
echo '<td>'.$group['desc'].'</td>';
echo '<td>'.$group['pattern'].'</td>';
echo '<td>'.formatDeviceGroupPattern($group['pattern'], json_decode($group['params'])).'</td>';
echo '<td>';
echo "<button type='button' class='btn btn-primary btn-sm' aria-label='Edit' data-toggle='modal' data-target='#create-group' data-group_id='".$group['id']."' name='edit-device-group'><i class='fa fa-pencil' aria-hidden='true'></i></button> ";
echo "<button type='button' class='btn btn-primary btn-sm' aria-label='Edit' data-toggle='modal' data-target='#create-group' data-group_id='".$group['id']."' name='edit-device-group'";
if (is_null($group['params'])) {
echo " disabled title='LibreNMS V2 device groups cannot be edited in LibreNMS V1'";
}
echo "><i class='fa fa-pencil' aria-hidden='true'></i></button> ";
echo "<button type='button' class='btn btn-danger btn-sm' aria-label='Delete' data-toggle='modal' data-target='#confirm-delete' data-group_id='".$group['id']."' name='delete-device-group'><i class='fa fa-trash' aria-hidden='true'></i></button>";
echo '</td>';
echo '</tr>';

View File

@@ -73,7 +73,8 @@ function EditDeviceGroup($group_id, $name = null, $desc = null, $pattern = null)
/**
* Generate SQL from Group-Pattern
* @param string $pattern Pattern to generate SQL for
* @param string $search What to searchid for
* @param string $search What to searchid for
* @param int $extra
* @return string sql to perform the search
*/
function GenGroupSQL($pattern, $search = '', $extra = 0)
@@ -82,23 +83,29 @@ function GenGroupSQL($pattern, $search = '', $extra = 0)
if ($pattern === false) {
return false;
}
$tmp = explode(' ', $pattern);
$tables = array();
foreach ($tmp as $opt) {
if (strstr($opt, '%') && strstr($opt, '.')) {
$tmpp = explode('.', $opt, 2);
$tmpp[0] = str_replace('%', '', $tmpp[0]);
$tables[] = mres(str_replace('(', '', $tmpp[0]));
$pattern = str_replace($opt, $tmpp[0].'.'.$tmpp[1], $pattern);
if (starts_with($pattern, '%')) {
// v1 pattern
$tables = array();
$words = explode(' ', $pattern);
foreach ($words as $word) {
if (starts_with($word, '%') && str_contains($word, '.')) {
list($table, $column) = explode('.', $word, 2);
$table = str_replace('%', '', $table);
$tables[] = mres(str_replace('(', '', $table));
$pattern = str_replace($word, $table . '.' . $column, $pattern);
}
}
$tables = array_keys(array_flip($tables));
} else {
// v2 pattern
$tables = getTablesFromPattern($pattern);
}
$pattern = rtrim($pattern, '&&');
$pattern = rtrim($pattern, '||');
$pattern = rtrim($pattern, '&|');
$tables = array_keys(array_flip($tables));
if (dbFetchCell('SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = ? && COLUMN_NAME = ?', array($tables[0],'device_id')) != 1) {
//Our first table has no valid glue, append the 'devices' table to it!
if ($tables[0] != 'devices' && dbFetchCell('SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = ? && COLUMN_NAME = ?', array($tables[0],'device_id')) != 1) {
//Our first table has no valid glue, prepend the 'devices' table to it!
array_unshift($tables, 'devices');
}
$x = sizeof($tables)-1;
@@ -137,13 +144,28 @@ function GenGroupSQL($pattern, $search = '', $extra = 0)
$search = str_replace("(", "", $tables[0]).'.'.$search. ' AND';
}
if (!empty($join)) {
$join = '('.rtrim($join, ' && ').') &&';
$join = '('.rtrim($join, '& ').') &&';
}
$sql = 'SELECT DISTINCT('.str_replace('(', '', $tables[0]).'.device_id)'.$sql_extra.' FROM '.implode(',', $tables).' WHERE '.$join.' '.$search.' ('.str_replace(array('%', '@', '!~', '~'), array('', '.*', 'NOT REGEXP', 'REGEXP'), $pattern).')';
return $sql;
}//end GenGroupSQL()
/**
* Extract an array of tables in a pattern
*
* @param string $pattern
* @return array
*/
function getTablesFromPattern($pattern)
{
preg_match_all('/[A-Za-z_]+(?=\.[A-Za-z_]+ )/', $pattern, $tables);
if (is_null($tables)) {
return array();
}
return array_keys(array_flip($tables[0])); // unique tables only
}
/**
* Run the group queries again to get fresh list of devices for this group
* @param integer $group_id Group-ID
@@ -151,11 +173,12 @@ function GenGroupSQL($pattern, $search = '', $extra = 0)
*/
function QueryDevicesFromGroup($group_id)
{
$pattern = dbFetchCell('SELECT pattern FROM device_groups WHERE id = ?', array($group_id));
$pattern = rtrim($pattern, '&&');
$pattern = rtrim($pattern, '||');
$group = dbFetchRow('SELECT pattern,params FROM device_groups WHERE id = ?', array($group_id));
$pattern = rtrim($group['pattern'], '&|');
$params = (array)json_decode($group['params']);
if (!empty($pattern)) {
return dbFetchColumn(GenGroupSQL($pattern));
$result = dbFetchColumn(GenGroupSQL($pattern), $params);
return $result;
}
return false;
@@ -196,7 +219,9 @@ function QueryGroupsFromDevice($device_id, $extra = 0)
{
$ret = array();
foreach (GetDeviceGroups() as $group) {
if (dbFetchCell(GenGroupSQL($group['pattern'], 'device_id=?', $extra).' LIMIT 1', array($device_id)) == $device_id) {
$params = (array)json_decode($group['params']);
array_unshift($params, $device_id);
if (dbFetchCell(GenGroupSQL($group['pattern'], 'device_id=?', $extra).' LIMIT 1', $params) == $device_id) {
if ($extra === 0) {
$ret[] = $group['id'];
} else {
@@ -257,6 +282,7 @@ function RunGroupMacros($rule, $x = 1)
*/
function UpdateGroupsForDevice($device_id)
{
d_echo("### Start Device Groups ###\n");
$queried_groups = QueryGroupsFromDevice($device_id);
$db_groups = GetGroupsFromDevice($device_id);
@@ -264,6 +290,9 @@ function UpdateGroupsForDevice($device_id)
$added_groups = array_diff($queried_groups, $db_groups);
$removed_groups = array_diff($db_groups, $queried_groups);
d_echo("Groups Added: ".implode(',', $added_groups).PHP_EOL);
d_echo("Groups Removed: ".implode(',', $removed_groups).PHP_EOL);
// insert new groups
$insert = array();
foreach ($added_groups as $group_id) {
@@ -277,6 +306,7 @@ function UpdateGroupsForDevice($device_id)
if (!empty($removed_groups)) {
dbDelete('device_group_device', '`device_id`=? AND `device_group_id` IN (?)', array($device_id, array(implode(',', $removed_groups))));
}
d_echo("### End Device Groups ###\n");
}
/**
@@ -306,3 +336,22 @@ function UpdateDeviceGroup($group_id)
dbDelete('device_group_device', '`device_group_id`=? AND `device_id` IN (?)', array($group_id, array(implode(',', $removed_devices))));
}
}
/**
* Fill in params into the pattern, replacing placeholders (?)
* If $params is empty or null, just returns $pattern
*
* @return string
*/
function formatDeviceGroupPattern($pattern, $params)
{
// fill in parameters
foreach ((array)$params as $value) {
if (!is_numeric($value) && !starts_with($value, "'")) {
$value = "'".$value."'";
}
$pattern = preg_replace('/\?/', $value, $pattern, 1);
}
return $pattern;
}

1
sql-schema/139.sql Normal file
View File

@@ -0,0 +1 @@
ALTER TABLE `device_groups` ADD `params` TEXT NULL DEFAULT NULL AFTER `pattern`;