Merge branch 'bugfix/make-host-customvars-accessible-from-service-objects-10304'

fixes #10304
This commit is contained in:
Johannes Meyer 2015-10-05 14:09:26 +02:00
commit 883608b09b
6 changed files with 189 additions and 69 deletions

View File

@ -239,7 +239,9 @@ class Logger
array_shift($arguments), array_shift($arguments),
array_map( array_map(
function ($a) { function ($a) {
return is_string($a) ? $a : ($a instanceof Exception ? $a->getMessage() : json_encode($a)); return is_string($a) ? $a : ($a instanceof Exception
? IcingaException::describe($a)
: json_encode($a));
}, },
$arguments $arguments
) )

View File

@ -3,6 +3,8 @@
namespace Icinga\Data\Filter; namespace Icinga\Data\Filter;
use Exception;
class FilterExpression extends Filter class FilterExpression extends Filter
{ {
protected $column; protected $column;
@ -97,22 +99,24 @@ class FilterExpression extends Filter
public function matches($row) public function matches($row)
{ {
if (! isset($row->{$this->column})) { try {
$rowValue = $row->{$this->column};
} catch (Exception $e) {
// TODO: REALLY? Exception? // TODO: REALLY? Exception?
return false; return false;
} }
if (is_array($this->expression)) { if (is_array($this->expression)) {
return in_array($row->{$this->column}, $this->expression); return in_array($rowValue, $this->expression);
} }
$expression = (string) $this->expression; $expression = (string) $this->expression;
if (strpos($expression, '*') === false) { if (strpos($expression, '*') === false) {
if (is_array($row->{$this->column})) { if (is_array($rowValue)) {
return in_array($expression, $row->{$this->column}); return in_array($expression, $rowValue);
} }
return (string) $row->{$this->column} === $expression; return (string) $rowValue === $expression;
} }
$parts = array(); $parts = array();
@ -121,8 +125,8 @@ class FilterExpression extends Filter
} }
$pattern = '/^' . implode('.*', $parts) . '$/'; $pattern = '/^' . implode('.*', $parts) . '$/';
if (is_array($row->{$this->column})) { if (is_array($rowValue)) {
foreach ($row->{$this->column} as $candidate) { foreach ($rowValue as $candidate) {
if (preg_match($pattern, $candidate)) { if (preg_match($pattern, $candidate)) {
return true; return true;
} }
@ -131,7 +135,7 @@ class FilterExpression extends Filter
return false; return false;
} }
return (bool) preg_match($pattern, $row->{$this->column}); return (bool) preg_match($pattern, $rowValue);
} }
public function andFilter(Filter $filter) public function andFilter(Filter $filter)

View File

@ -47,6 +47,8 @@ class ActionForm extends NavigationItemForm
'instance_name', 'instance_name',
'service_description', 'service_description',
'servicegroup_name', 'servicegroup_name',
'contact_name',
'contactgroup_name',
function ($c) { function ($c) {
return preg_match('/^_(?:host|service)_/', $c); return preg_match('/^_(?:host|service)_/', $c);
} }
@ -63,6 +65,8 @@ class ActionForm extends NavigationItemForm
'hostgroup_name', 'hostgroup_name',
'service_description', 'service_description',
'servicegroup_name', 'servicegroup_name',
'contact_name',
'contactgroup_name',
'_(host|service)_<customvar-name>' '_(host|service)_<customvar-name>'
)) ))
)); ));

View File

@ -3,6 +3,9 @@
namespace Icinga\Module\Monitoring\Object; namespace Icinga\Module\Monitoring\Object;
use Exception;
use Icinga\Application\Logger;
/** /**
* Expand macros in string in the context of MonitoredObjects * Expand macros in string in the context of MonitoredObjects
*/ */
@ -60,11 +63,14 @@ class Macro
if (isset(self::$icingaMacros[$macro]) && isset($object->{self::$icingaMacros[$macro]})) { if (isset(self::$icingaMacros[$macro]) && isset($object->{self::$icingaMacros[$macro]})) {
return $object->{self::$icingaMacros[$macro]}; return $object->{self::$icingaMacros[$macro]};
} }
$customVar = strtolower($macro);
if (isset($object->customvars[$customVar])) { try {
return $object->customvars[$customVar]; $value = $object->$macro;
} catch (Exception $e) {
$value = null;
Logger::debug('Unable to resolve macro "%s". An error occured: %s', $macro, $e);
} }
return $macro; return $value !== null ? $value : $macro;
} }
} }

View File

@ -5,7 +5,6 @@ namespace Icinga\Module\Monitoring\Object;
use InvalidArgumentException; use InvalidArgumentException;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Data\Filterable; use Icinga\Data\Filterable;
use Icinga\Exception\InvalidPropertyException; use Icinga\Exception\InvalidPropertyException;
@ -50,12 +49,26 @@ abstract class MonitoredObject implements Filterable
protected $comments; protected $comments;
/** /**
* Custom variables * This object's obfuscated custom variables
* *
* @var array * @var array
*/ */
protected $customvars; protected $customvars;
/**
* The host custom variables
*
* @var array
*/
protected $hostVariables;
/**
* The service custom variables
*
* @var array
*/
protected $serviceVariables;
/** /**
* Contact groups * Contact groups
* *
@ -218,6 +231,8 @@ abstract class MonitoredObject implements Filterable
* @return bool * @return bool
* *
* @throws ProgrammingError In case the object cannot be found * @throws ProgrammingError In case the object cannot be found
*
* @deprecated Use $filter->matches($object) instead
*/ */
public function matches(Filter $filter) public function matches(Filter $filter)
{ {
@ -229,38 +244,7 @@ abstract class MonitoredObject implements Filterable
); );
} }
$queryString = $filter->toQueryString(); return $filter->matches($this);
$row = clone $this->properties;
if (strpos($queryString, '_host_') !== false || strpos($queryString, '_service_') !== false) {
if ($this->customvars === null) {
$this->fetchCustomvars();
}
foreach ($this->customvars as $name => $value) {
if (! is_object($value)) {
$row->{'_' . $this->getType() . '_' . $name} = $value;
}
}
}
if (strpos($queryString, 'hostgroup_name') !== false) {
if ($this->hostgroups === null) {
$this->fetchHostgroups();
}
$row->hostgroup_name = array_keys($this->hostgroups);
}
if (strpos($queryString, 'servicegroup_name') !== false) {
if ($this->servicegroups === null) {
$this->fetchServicegroups();
}
$row->servicegroup_name = array_keys($this->servicegroups);
}
return $filter->matches($row);
} }
/** /**
@ -438,9 +422,9 @@ abstract class MonitoredObject implements Filterable
} }
/** /**
* Fetch the object's custom variables * Fetch this object's obfuscated custom variables
* *
* @return $this * @return $this
*/ */
public function fetchCustomvars() public function fetchCustomvars()
{ {
@ -463,28 +447,81 @@ abstract class MonitoredObject implements Filterable
$blacklistPattern = '/^(' . implode('|', $blacklist) . ')$/i'; $blacklistPattern = '/^(' . implode('|', $blacklist) . ')$/i';
} }
if ($this->type === self::TYPE_SERVICE) {
$this->fetchServiceVariables();
$customvars = $this->serviceVariables;
} else {
$this->fetchHostVariables();
$customvars = $this->hostVariables;
}
$this->customvars = array();
foreach ($customvars as $name => $value) {
if ($blacklistPattern && preg_match($blacklistPattern, $name)) {
$this->customvars[$name] = '***';
} else {
$this->customvars[$name] = $value;
}
}
return $this;
}
/**
* Fetch the host custom variables related to this object
*
* @return $this
*/
public function fetchHostVariables()
{
$query = $this->backend->select()->from('customvar', array( $query = $this->backend->select()->from('customvar', array(
'varname', 'varname',
'varvalue', 'varvalue',
'is_json' 'is_json'
)) ))
->where('object_type', $this->type) ->where('object_type', static::TYPE_HOST)
->where('host_name', $this->host_name); ->where('host_name', $this->host_name);
if ($this->type === self::TYPE_SERVICE) {
$query->where('service_description', $this->service_description); $this->hostVariables = array();
foreach ($query as $row) {
if ($row->is_json) {
$this->hostVariables[strtolower($row->varname)] = json_decode($row->varvalue);
} else {
$this->hostVariables[strtolower($row->varname)] = $row->varvalue;
}
} }
$this->customvars = array(); return $this;
}
$customvars = $query->getQuery()->fetchAll(); /**
foreach ($customvars as $cv) { * Fetch the service custom variables related to this object
$name = strtolower($cv->varname); *
if ($blacklistPattern && preg_match($blacklistPattern, $cv->varname)) { * @return $this
$this->customvars[$name] = '***'; *
} elseif ($cv->is_json) { * @throws ProgrammingError In case this object is not a service
$this->customvars[$name] = json_decode($cv->varvalue); */
public function fetchServiceVariables()
{
if ($this->type !== static::TYPE_SERVICE) {
throw new ProgrammingError('Cannot fetch service custom variables for non-service objects');
}
$query = $this->backend->select()->from('customvar', array(
'varname',
'varvalue',
'is_json'
))
->where('object_type', static::TYPE_SERVICE)
->where('host_name', $this->host_name)
->where('service_description', $this->service_description);
$this->serviceVariables = array();
foreach ($query as $row) {
if ($row->is_json) {
$this->serviceVariables[strtolower($row->varname)] = json_decode($row->varvalue);
} else { } else {
$this->customvars[$name] = $cv->varvalue; $this->serviceVariables[strtolower($row->varname)] = $row->varvalue;
} }
} }
@ -754,19 +791,86 @@ abstract class MonitoredObject implements Filterable
{ {
if (property_exists($this->properties, $name)) { if (property_exists($this->properties, $name)) {
return $this->properties->$name; return $this->properties->$name;
} elseif (property_exists($this, $name) && $this->$name !== null) {
return $this->$name;
} elseif (property_exists($this, $name)) { } elseif (property_exists($this, $name)) {
$fetchMethod = 'fetch' . ucfirst($name); if ($this->$name === null) {
$this->$fetchMethod(); $fetchMethod = 'fetch' . ucfirst($name);
$this->$fetchMethod();
}
return $this->$name; return $this->$name;
} } elseif (preg_match('/^_(host|service)_(.+)/i', $name, $matches)) {
if (substr($name, 0, strlen($this->prefix)) !== $this->prefix) { if (strtolower($matches[1]) === static::TYPE_HOST) {
$prefixedName = $this->prefix . strtolower($name); if ($this->hostVariables === null) {
$this->fetchHostVariables();
}
$customvars = $this->hostVariables;
} else {
if ($this->serviceVariables === null) {
$this->fetchServiceVariables();
}
$customvars = $this->serviceVariables;
}
$variableName = strtolower($matches[2]);
if (isset($customvars[$variableName])) {
return $customvars[$variableName];
}
return null; // Unknown custom variables MUST NOT throw an error
} elseif (in_array($name, array('contact_name', 'contactgroup_name', 'hostgroup_name', 'servicegroup_name'))) {
if ($name === 'contact_name') {
if ($this->contacts === null) {
$this->fetchContacts();
}
return array_map(function ($el) { return $el->contact_name; }, $this->contacts);
} elseif ($name === 'contactgroup_name') {
if ($this->contactgroups === null) {
$this->fetchContactgroups();
}
return array_map(function ($el) { return $el->contactgroup_name; }, $this->contactgroups);
} elseif ($name === 'hostgroup_name') {
if ($this->hostgroups === null) {
$this->fetchHostgroups();
}
return array_keys($this->hostgroups);
} else { // $name === 'servicegroup_name'
if ($this->servicegroups === null) {
$this->fetchServicegroups();
}
return array_keys($this->servicegroups);
}
} elseif (strpos($name, $this->prefix) !== 0) {
$propertyName = strtolower($name);
$prefixedName = $this->prefix . $propertyName;
if (property_exists($this->properties, $prefixedName)) { if (property_exists($this->properties, $prefixedName)) {
return $this->properties->$prefixedName; return $this->properties->$prefixedName;
} }
if ($this->type === static::TYPE_HOST) {
if ($this->hostVariables === null) {
$this->fetchHostVariables();
}
$customvars = $this->hostVariables;
} else { // $this->type === static::TYPE_SERVICE
if ($this->serviceVariables === null) {
$this->fetchServiceVariables();
}
$customvars = $this->serviceVariables;
}
if (isset($customvars[$propertyName])) {
return $customvars[$propertyName];
}
} }
throw new InvalidPropertyException('Can\'t access property \'%s\'. Property does not exist.', $name); throw new InvalidPropertyException('Can\'t access property \'%s\'. Property does not exist.', $name);
} }

View File

@ -101,7 +101,7 @@ class Action extends NavigationItem
{ {
if ($this->render === null) { if ($this->render === null) {
$filter = $this->getFilter(); $filter = $this->getFilter();
$this->render = $filter ? $this->getObject()->matches(Filter::fromQueryString($filter)) : true; $this->render = $filter ? Filter::fromQueryString($filter)->matches($this->getObject()) : true;
} }
return $this->render; return $this->render;