From 02b84cf3f70cca45ba5f53f785054ab258c009d2 Mon Sep 17 00:00:00 2001 From: Tony Murray Date: Tue, 27 Sep 2016 11:50:52 -0500 Subject: [PATCH] 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... --- html/includes/forms/create-alert-item.inc.php | 3 +- html/pages/device-groups.inc.php | 8 +- includes/device-groups.inc.php | 89 ++++++++++++++----- sql-schema/139.sql | 1 + 4 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 sql-schema/139.sql diff --git a/html/includes/forms/create-alert-item.inc.php b/html/includes/forms/create-alert-item.inc.php index 41c0a847fe..01ae19e728 100644 --- a/html/includes/forms/create-alert-item.inc.php +++ b/html/includes/forms/create-alert-item.inc.php @@ -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']); diff --git a/html/pages/device-groups.inc.php b/html/pages/device-groups.inc.php index 6d84020aeb..e869accf75 100644 --- a/html/pages/device-groups.inc.php +++ b/html/pages/device-groups.inc.php @@ -14,9 +14,13 @@ if (!empty($group_count_check)) { echo ''; echo ''.$group['name'].''; echo ''.$group['desc'].''; - echo ''.$group['pattern'].''; + echo ''.formatDeviceGroupPattern($group['pattern'], json_decode($group['params'])).''; echo ''; - echo " "; + echo " "; echo ""; echo ''; echo ''; diff --git a/includes/device-groups.inc.php b/includes/device-groups.inc.php index 058456de3c..f47a691e2a 100644 --- a/includes/device-groups.inc.php +++ b/includes/device-groups.inc.php @@ -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; +} diff --git a/sql-schema/139.sql b/sql-schema/139.sql new file mode 100644 index 0000000000..6ac428bcb6 --- /dev/null +++ b/sql-schema/139.sql @@ -0,0 +1 @@ +ALTER TABLE `device_groups` ADD `params` TEXT NULL DEFAULT NULL AFTER `pattern`;