parent
ac73affcb4
commit
722499ea76
|
@ -64,6 +64,41 @@ class ImportRowModifierForm extends DirectorObjectForm
|
|||
$error = $e->getMessage();
|
||||
$mods = $this->optionalEnum([]);
|
||||
}
|
||||
$this->addElement('YesNo', 'use_filter', [
|
||||
'label' => $this->translate('Set based on filter'),
|
||||
'ignore' => true,
|
||||
'class' => 'autosubmit',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
if ($this->hasBeenSent()) {
|
||||
$useFilter = $this->getSentValue('use_filter');
|
||||
if ($useFilter === null) {
|
||||
$this->setElementValue('use_filter', $useFilter = 'n');
|
||||
}
|
||||
} elseif ($object = $this->getObject()) {
|
||||
$expression = $object->get('filter_expression');
|
||||
$useFilter = ($expression === null || strlen($expression) === 0) ? 'n' : 'y';
|
||||
$this->setElementValue('use_filter', $useFilter);
|
||||
} else {
|
||||
$this->setElementValue('use_filter', $useFilter = 'n');
|
||||
}
|
||||
|
||||
if ($useFilter === 'y') {
|
||||
$this->addElement('text', 'filter_expression', [
|
||||
'label' => $this->translate('Filter Expression'),
|
||||
'description' => $this->translate(
|
||||
'This allows to filter for specific parts within the given source expression.'
|
||||
. ' You are allowed to refer all imported columns. Examples: host=www* would'
|
||||
. ' set this property only for rows imported with a host property starting'
|
||||
. ' with "www". Complex example: host=www*&!(address=127.*|address6=::1).'
|
||||
. ' Please note, that CIDR notation based matches are also supported: '
|
||||
. ' address=192.0.2.128/25|address=2001:db8::/32|address=::ffff:192.0.2.0/96'
|
||||
),
|
||||
'required' => true,
|
||||
// TODO: validate filter
|
||||
]);
|
||||
}
|
||||
|
||||
$this->addElement('select', 'provider_class', [
|
||||
'label' => $this->translate('Modifier'),
|
||||
|
|
|
@ -23,6 +23,8 @@ This version hasn't been released yet
|
|||
|
||||
### Import and Sync
|
||||
* FEATURE: regular expression based modifier allows explicit NULL on no match (#2705)
|
||||
* FEATURE: property modifiers can now be applied based on filters (#2756)
|
||||
* FEATURE: CIDR notation (network ranges) is supported in such filters (#2757)
|
||||
* FIX: synchronizing Service (and -Set) Templates has been fixed (#2745, #2217)
|
||||
|
||||
### Permissions and Restrictions
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Filter;
|
||||
|
||||
use Icinga\Data\Filter\FilterExpression;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function array_map;
|
||||
use function filter_var;
|
||||
use function inet_pton;
|
||||
use function pack;
|
||||
use function preg_match;
|
||||
use function str_pad;
|
||||
use function str_split;
|
||||
|
||||
class CidrExpression extends FilterExpression
|
||||
{
|
||||
protected $networkAddress;
|
||||
protected $broadcastAddress;
|
||||
|
||||
public function __construct($column, $sign, $expression)
|
||||
{
|
||||
if ($parts = static::splitOptionalCidrString($expression)) {
|
||||
list($this->networkAddress, $this->broadcastAddress) = $parts;
|
||||
|
||||
} else {
|
||||
throw new InvalidArgumentException("'$expression' isn't valid CIDR notation");
|
||||
}
|
||||
|
||||
parent::__construct($column, $sign, $expression);
|
||||
}
|
||||
|
||||
public static function isCidrFormat(string $string): bool
|
||||
{
|
||||
return static::splitOptionalCidrString($string) !== null;
|
||||
}
|
||||
|
||||
protected static function splitOptionalCidrString(string $string): ?array
|
||||
{
|
||||
if (preg_match('#^(.+?)/(\d{1,3})$#', $string, $match)) {
|
||||
$address = $match[1];
|
||||
$mask = (int) $match[2];
|
||||
|
||||
if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && $mask <= 32) {
|
||||
$bits = 32;
|
||||
} elseif (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && $mask <= 128) {
|
||||
$bits = 128;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$binaryAddress = inet_pton($address);
|
||||
$broadcast = $binaryAddress | static::bitmaskToInverseBinaryMask($mask, $bits);
|
||||
|
||||
return [$binaryAddress, $broadcast];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function matches($row): bool
|
||||
{
|
||||
if (! isset($row->{$this->column})) {
|
||||
return false;
|
||||
}
|
||||
$value = inet_pton((string) $row->{$this->column});
|
||||
|
||||
return $value >= $this->networkAddress && $value <= $this->broadcastAddress;
|
||||
}
|
||||
|
||||
public static function fromExpression(FilterExpression $filter): CidrExpression
|
||||
{
|
||||
$sign = $filter->getSign();
|
||||
if ($sign !== '=') {
|
||||
throw new InvalidArgumentException("'$sign' cannot be applied to CIDR notation");
|
||||
}
|
||||
return new CidrExpression($filter->getColumn(), $sign, $filter->getExpression());
|
||||
}
|
||||
|
||||
protected static function bitmaskToInverseBinaryMask($mask, $maxLen): string
|
||||
{
|
||||
$binary = str_pad(str_pad('', $mask, '0'), $maxLen, '1');
|
||||
$address = '';
|
||||
foreach (array_map('bindec', str_split($binary, 8)) as $char) {
|
||||
$address .= pack('C*', $char);
|
||||
}
|
||||
|
||||
return $address;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Filter;
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Data\Filter\FilterChain;
|
||||
use Icinga\Data\Filter\FilterExpression;
|
||||
|
||||
class FilterEnrichment
|
||||
{
|
||||
public static function enrichFilter(Filter $filter): Filter
|
||||
{
|
||||
if ($filter instanceof FilterExpression) {
|
||||
if (CidrExpression::isCidrFormat($filter->getExpression())) {
|
||||
return CidrExpression::fromExpression($filter);
|
||||
}
|
||||
} elseif ($filter instanceof FilterChain) {
|
||||
foreach ($filter->filters() as $subFilter) {
|
||||
if ($subFilter instanceof FilterExpression
|
||||
&& CidrExpression::isCidrFormat($subFilter->getExpression())
|
||||
) {
|
||||
$filter->replaceById($subFilter->getId(), CidrExpression::fromExpression($subFilter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $filter;
|
||||
}
|
||||
}
|
|
@ -18,13 +18,14 @@ class ImportRowModifier extends DbObjectWithSettings implements InstantiatedViaH
|
|||
protected $autoincKeyName = 'id';
|
||||
|
||||
protected $defaultProperties = [
|
||||
'id' => null,
|
||||
'source_id' => null,
|
||||
'property_name' => null,
|
||||
'provider_class' => null,
|
||||
'target_property' => null,
|
||||
'priority' => null,
|
||||
'description' => null,
|
||||
'id' => null,
|
||||
'source_id' => null,
|
||||
'property_name' => null,
|
||||
'provider_class' => null,
|
||||
'target_property' => null,
|
||||
'filter_expression' => null,
|
||||
'priority' => null,
|
||||
'description' => null,
|
||||
];
|
||||
|
||||
protected $settingsTable = 'import_row_modifier_setting';
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Director\Application\MemoryLimit;
|
||||
use Icinga\Module\Director\Data\Db\DbObjectWithSettings;
|
||||
use Icinga\Module\Director\Db;
|
||||
use Icinga\Module\Director\DirectorObject\Automation\ExportInterface;
|
||||
use Icinga\Module\Director\Exception\DuplicateKeyException;
|
||||
use Icinga\Module\Director\Filter\FilterEnrichment;
|
||||
use Icinga\Module\Director\Hook\PropertyModifierHook;
|
||||
use Icinga\Module\Director\Import\Import;
|
||||
use Icinga\Module\Director\Import\SyncUtils;
|
||||
|
@ -252,10 +254,14 @@ class ImportSource extends DbObjectWithSettings implements ExportInterface
|
|||
|
||||
foreach ($modifiers as $modPair) {
|
||||
/** @var PropertyModifierHook $modifier */
|
||||
list($property, $modifier) = $modPair;
|
||||
/** @var ?Filter $filter */
|
||||
list($property, $modifier, $filter) = $modPair;
|
||||
$rejected = [];
|
||||
$newRows = [];
|
||||
foreach ($data as $key => $row) {
|
||||
if ($filter && ! $filter->matches($row)) {
|
||||
continue;
|
||||
}
|
||||
$this->applyPropertyModifierToRow($modifier, $property, $row);
|
||||
if ($modifier->rejectsRow()) {
|
||||
$rejected[] = $key;
|
||||
|
@ -372,7 +378,12 @@ class ImportSource extends DbObjectWithSettings implements ExportInterface
|
|||
{
|
||||
$mods = [];
|
||||
foreach ($this->fetchRowModifiers() as $mod) {
|
||||
$mods[] = [$mod->get('property_name'), $mod->getInstance()];
|
||||
if ($filterExpression = $mod->get('filter_expression')) {
|
||||
$filter = FilterEnrichment::enrichFilter(Filter::fromQueryString($filterExpression));
|
||||
} else {
|
||||
$filter = null;
|
||||
}
|
||||
$mods[] = [$mod->get('property_name'), $mod->getInstance(), $filter];
|
||||
}
|
||||
|
||||
return $mods;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE import_row_modifier ADD COLUMN filter_expression TEXT DEFAULT NULL AFTER priority;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (187, NOW());
|
|
@ -1459,6 +1459,7 @@ CREATE TABLE import_row_modifier (
|
|||
target_property VARCHAR(255) DEFAULT NULL,
|
||||
provider_class VARCHAR(128) NOT NULL,
|
||||
priority SMALLINT UNSIGNED NOT NULL,
|
||||
filter_expression TEXT DEFAULT NULL,
|
||||
description TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY search_idx (property_name),
|
||||
|
@ -2445,4 +2446,4 @@ CREATE TABLE branched_icinga_dependency (
|
|||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (186, NOW());
|
||||
VALUES (187, NOW());
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE import_row_modifier ADD COLUMN filter_expression text DEFAULT NULL,
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (187, NOW());
|
|
@ -1608,6 +1608,7 @@ CREATE TABLE import_row_modifier (
|
|||
target_property character varying(255) DEFAULT NULL,
|
||||
provider_class character varying(128) NOT NULL,
|
||||
priority integer NOT NULL,
|
||||
filter_expression text DEFAULT NULL,
|
||||
description text DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT row_modifier_import_source
|
||||
|
@ -2784,4 +2785,4 @@ CREATE INDEX branched_dependency_search_object_name ON branched_icinga_dependenc
|
|||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (186, NOW());
|
||||
VALUES (187, NOW());
|
||||
|
|
Loading…
Reference in New Issue