diff --git a/library/Icinga/Application/Logger.php b/library/Icinga/Application/Logger.php index aa03b49fc..9203a2f75 100644 --- a/library/Icinga/Application/Logger.php +++ b/library/Icinga/Application/Logger.php @@ -239,7 +239,9 @@ class Logger array_shift($arguments), array_map( 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 ) diff --git a/library/Icinga/Data/Filter/FilterExpression.php b/library/Icinga/Data/Filter/FilterExpression.php index ca5866a6a..86b36e81c 100644 --- a/library/Icinga/Data/Filter/FilterExpression.php +++ b/library/Icinga/Data/Filter/FilterExpression.php @@ -3,6 +3,8 @@ namespace Icinga\Data\Filter; +use Exception; + class FilterExpression extends Filter { protected $column; @@ -97,22 +99,24 @@ class FilterExpression extends Filter public function matches($row) { - if (! isset($row->{$this->column})) { + try { + $rowValue = $row->{$this->column}; + } catch (Exception $e) { // TODO: REALLY? Exception? return false; } if (is_array($this->expression)) { - return in_array($row->{$this->column}, $this->expression); + return in_array($rowValue, $this->expression); } $expression = (string) $this->expression; if (strpos($expression, '*') === false) { - if (is_array($row->{$this->column})) { - return in_array($expression, $row->{$this->column}); + if (is_array($rowValue)) { + return in_array($expression, $rowValue); } - return (string) $row->{$this->column} === $expression; + return (string) $rowValue === $expression; } $parts = array(); @@ -121,8 +125,8 @@ class FilterExpression extends Filter } $pattern = '/^' . implode('.*', $parts) . '$/'; - if (is_array($row->{$this->column})) { - foreach ($row->{$this->column} as $candidate) { + if (is_array($rowValue)) { + foreach ($rowValue as $candidate) { if (preg_match($pattern, $candidate)) { return true; } @@ -131,7 +135,7 @@ class FilterExpression extends Filter return false; } - return (bool) preg_match($pattern, $row->{$this->column}); + return (bool) preg_match($pattern, $rowValue); } public function andFilter(Filter $filter) diff --git a/modules/monitoring/application/forms/Navigation/ActionForm.php b/modules/monitoring/application/forms/Navigation/ActionForm.php index e712b254d..fbd0a51f2 100644 --- a/modules/monitoring/application/forms/Navigation/ActionForm.php +++ b/modules/monitoring/application/forms/Navigation/ActionForm.php @@ -47,6 +47,8 @@ class ActionForm extends NavigationItemForm 'instance_name', 'service_description', 'servicegroup_name', + 'contact_name', + 'contactgroup_name', function ($c) { return preg_match('/^_(?:host|service)_/', $c); } @@ -63,6 +65,8 @@ class ActionForm extends NavigationItemForm 'hostgroup_name', 'service_description', 'servicegroup_name', + 'contact_name', + 'contactgroup_name', '_(host|service)_' )) )); diff --git a/modules/monitoring/library/Monitoring/Object/Macro.php b/modules/monitoring/library/Monitoring/Object/Macro.php index 73e982b15..b311f95b4 100644 --- a/modules/monitoring/library/Monitoring/Object/Macro.php +++ b/modules/monitoring/library/Monitoring/Object/Macro.php @@ -3,6 +3,9 @@ namespace Icinga\Module\Monitoring\Object; +use Exception; +use Icinga\Application\Logger; + /** * 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]})) { return $object->{self::$icingaMacros[$macro]}; } - $customVar = strtolower($macro); - if (isset($object->customvars[$customVar])) { - return $object->customvars[$customVar]; + + try { + $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; } } diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index 866e57c60..80a545784 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -5,7 +5,6 @@ namespace Icinga\Module\Monitoring\Object; use InvalidArgumentException; use Icinga\Application\Config; -use Icinga\Application\Logger; use Icinga\Data\Filter\Filter; use Icinga\Data\Filterable; use Icinga\Exception\InvalidPropertyException; @@ -50,12 +49,26 @@ abstract class MonitoredObject implements Filterable protected $comments; /** - * Custom variables + * This object's obfuscated custom variables * * @var array */ protected $customvars; + /** + * The host custom variables + * + * @var array + */ + protected $hostVariables; + + /** + * The service custom variables + * + * @var array + */ + protected $serviceVariables; + /** * Contact groups * @@ -218,6 +231,8 @@ abstract class MonitoredObject implements Filterable * @return bool * * @throws ProgrammingError In case the object cannot be found + * + * @deprecated Use $filter->matches($object) instead */ public function matches(Filter $filter) { @@ -229,38 +244,7 @@ abstract class MonitoredObject implements Filterable ); } - $queryString = $filter->toQueryString(); - $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); + return $filter->matches($this); } /** @@ -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() { @@ -463,28 +447,81 @@ abstract class MonitoredObject implements Filterable $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( 'varname', 'varvalue', 'is_json' )) - ->where('object_type', $this->type) + ->where('object_type', static::TYPE_HOST) ->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) { - $name = strtolower($cv->varname); - if ($blacklistPattern && preg_match($blacklistPattern, $cv->varname)) { - $this->customvars[$name] = '***'; - } elseif ($cv->is_json) { - $this->customvars[$name] = json_decode($cv->varvalue); + /** + * Fetch the service custom variables related to this object + * + * @return $this + * + * @throws ProgrammingError In case this object is not a service + */ + 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 { - $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)) { return $this->properties->$name; - } elseif (property_exists($this, $name) && $this->$name !== null) { - return $this->$name; } elseif (property_exists($this, $name)) { - $fetchMethod = 'fetch' . ucfirst($name); - $this->$fetchMethod(); + if ($this->$name === null) { + $fetchMethod = 'fetch' . ucfirst($name); + $this->$fetchMethod(); + } + return $this->$name; - } - if (substr($name, 0, strlen($this->prefix)) !== $this->prefix) { - $prefixedName = $this->prefix . strtolower($name); + } elseif (preg_match('/^_(host|service)_(.+)/i', $name, $matches)) { + if (strtolower($matches[1]) === static::TYPE_HOST) { + 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)) { 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); } diff --git a/modules/monitoring/library/Monitoring/Web/Navigation/Action.php b/modules/monitoring/library/Monitoring/Web/Navigation/Action.php index 505229abb..1b17106ee 100644 --- a/modules/monitoring/library/Monitoring/Web/Navigation/Action.php +++ b/modules/monitoring/library/Monitoring/Web/Navigation/Action.php @@ -101,7 +101,7 @@ class Action extends NavigationItem { if ($this->render === null) { $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;