backend = $backend; } /** * Get the object's data view * * @return \Icinga\Module\Monitoring\DataView\DataView */ abstract protected function getDataView(); /** * Get all note urls configured for this monitored object * * @return array All note urls as a string */ abstract public function getNotesUrls(); /** * {@inheritdoc} */ public function addFilter(Filter $filter) { // Left out on purpose. Interface is deprecated. } /** * {@inheritdoc} */ public function applyFilter(Filter $filter) { $this->getFilter()->addFilter($filter); return $this; } /** * {@inheritdoc} */ public function getFilter() { if ($this->filter === null) { $this->filter = Filter::matchAll(); } return $this->filter; } /** * {@inheritdoc} */ public function setFilter(Filter $filter) { // Left out on purpose. Interface is deprecated. } /** * {@inheritdoc} */ public function where($condition, $value = null) { // Left out on purpose. Interface is deprecated. } /** * Require the object's type to be one of the given types * * @param array $oneOf * * @return bool * @throws InvalidArgumentException If the object's type is not one of the given types. */ public function assertOneOf(array $oneOf) { if (! in_array($this->type, $oneOf)) { throw new InvalidArgumentException; } return true; } /** * Fetch the object's properties * * @return bool */ public function fetch() { $properties = $this->getDataView()->applyFilter($this->getFilter())->getQuery()->fetchRow(); if ($properties === false) { return false; } if (isset($properties->host_contacts)) { $this->contacts = array(); foreach (preg_split('~,~', $properties->host_contacts) as $contact) { $this->contacts[] = (object) array( 'contact_name' => $contact, 'contact_alias' => $contact, 'contact_email' => null, 'contact_pager' => null, ); } } $this->properties = $properties; return true; } /** * Fetch the object's acknowledgement */ public function fetchAcknowledgement() { if ($this->comments === null) { $this->fetchComments(); } return $this; } /** * Fetch the object's comments * * @return $this */ public function fetchComments() { $commentsView = $this->backend->select()->from('comment', array( 'author' => 'comment_author_name', 'comment' => 'comment_data', 'expiration' => 'comment_expiration', 'id' => 'comment_internal_id', 'name' => 'comment_name', 'persistent' => 'comment_is_persistent', 'timestamp' => 'comment_timestamp', 'type' => 'comment_type' )); if ($this->type === self::TYPE_SERVICE) { $commentsView ->whereEx(new FilterEqual('service_host_name', '=', $this->host_name)) ->whereEx(new FilterEqual('service_description', '=', $this->service_description)); } else { $commentsView->whereEx(new FilterEqual('host_name', '=', $this->host_name)); } $commentsView ->whereEx(new FilterEqual('comment_type', '=', ['ack', 'comment'])) ->whereEx(new FilterEqual('object_type', '=', $this->type)); $comments = $commentsView->fetchAll(); if ((bool) $this->properties->{$this->prefix . 'acknowledged'}) { $ackCommentIdx = null; foreach ($comments as $i => $comment) { if ($comment->type === 'ack') { $this->acknowledgement = new Acknowledgement(array( 'author' => $comment->author, 'comment' => $comment->comment, 'entry_time' => $comment->timestamp, 'expiration_time' => $comment->expiration, 'sticky' => (int) $this->properties->{$this->prefix . 'acknowledgement_type'} === 2 )); $ackCommentIdx = $i; break; } } if ($ackCommentIdx !== null) { unset($comments[$ackCommentIdx]); } } $this->comments = $comments; return $this; } /** * Fetch the object's contact groups * * @return $this */ public function fetchContactgroups() { $contactsGroups = $this->backend->select()->from('contactgroup', array( 'contactgroup_name', 'contactgroup_alias' )); if ($this->type === self::TYPE_SERVICE) { $contactsGroups ->whereEx(new FilterEqual('service_host_name', '=', $this->host_name)) ->whereEx(new FilterEqual('service_description', '=', $this->service_description)); } else { $contactsGroups->whereEx(new FilterEqual('host_name', '=', $this->host_name)); } $this->contactgroups = $contactsGroups; return $this; } /** * Fetch the object's contacts * * @return $this */ public function fetchContacts() { $contacts = $this->backend->select()->from("{$this->type}contact", array( 'contact_name', 'contact_alias', 'contact_email', 'contact_pager', )); if ($this->type === self::TYPE_SERVICE) { $contacts ->whereEx(new FilterEqual('service_host_name', '=', $this->host_name)) ->whereEx(new FilterEqual('service_description', '=', $this->service_description)); } else { $contacts->whereEx(new FilterEqual('host_name', '=', $this->host_name)); } $this->contacts = $contacts; return $this; } /** * Fetch this object's obfuscated custom variables * * @return $this */ public function fetchCustomvars() { if ($this->type === self::TYPE_SERVICE) { $this->fetchServiceVariables(); $customvars = $this->serviceVariables; } else { $this->fetchHostVariables(); $customvars = $this->hostVariables; } $this->customvars = $customvars; $this->hideBlacklistedProperties(); $this->customvars = $this->obfuscateCustomVars($this->customvars, null); $this->customvarsWithOriginalNames = $this->obfuscateCustomVars($this->customvarsWithOriginalNames, null); return $this; } /** * Obfuscate custom variables recursively * * @param stdClass|array $customvars The custom variables to obfuscate * * @return stdClass|array The obfuscated custom variables */ protected function obfuscateCustomVars($customvars, $_) { return self::protectCustomVars($customvars); } public static function protectCustomVars($customvars) { $blacklist = []; $blacklistPattern = ''; if (($blacklistConfig = Config::module('monitoring')->get('security', 'protected_customvars', '')) !== '') { foreach (explode(',', $blacklistConfig) as $customvar) { $nonWildcards = array(); foreach (explode('*', $customvar) as $nonWildcard) { $nonWildcards[] = preg_quote($nonWildcard, '/'); } $blacklist[] = implode('.*', $nonWildcards); } $blacklistPattern = '/^(' . implode('|', $blacklist) . ')$/i'; } if (! $blacklistPattern) { return $customvars; } $obfuscator = function ($vars) use ($blacklistPattern, &$obfuscator) { $result = []; foreach ($vars as $name => $value) { if ($blacklistPattern && preg_match($blacklistPattern, $name)) { $result[$name] = '***'; } elseif ($value instanceof stdClass || is_array($value)) { $obfuscated = $obfuscator($value); $result[$name] = $value instanceof stdClass ? (object) $obfuscated : $obfuscated; } else { $result[$name] = $value; } } return $result; }; $obfuscatedCustomVars = $obfuscator($customvars); return $customvars instanceof stdClass ? (object) $obfuscatedCustomVars : $obfuscatedCustomVars; } /** * Hide all blacklisted properties from the user as restricted by monitoring/blacklist/properties * * Currently this only affects the custom variables */ protected function hideBlacklistedProperties() { if ($this->blacklistedProperties === null) { $this->blacklistedProperties = new GlobFilter( Auth::getInstance()->getRestrictions('monitoring/blacklist/properties') ); } $allProperties = $this->blacklistedProperties->removeMatching( [$this->type => ['vars' => $this->customvars]] ); $this->customvars = isset($allProperties[$this->type]['vars']) ? $allProperties[$this->type]['vars'] : []; $allProperties = $this->blacklistedProperties->removeMatching( [$this->type => ['vars' => $this->customvarsWithOriginalNames]] ); $this->customvarsWithOriginalNames = isset($allProperties[$this->type]['vars']) ? $allProperties[$this->type]['vars'] : []; } /** * 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' )) ->whereEx(new FilterEqual('object_type', '=', static::TYPE_HOST)) ->whereEx(new FilterEqual('host_name', '=', $this->host_name)); $this->hostVariables = []; if ($this->type === static::TYPE_HOST) { $this->customvarsWithOriginalNames = []; } 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; } if ($this->type === static::TYPE_HOST) { $this->customvarsWithOriginalNames[$row->varname] = $this->hostVariables[strtolower($row->varname)]; } } return $this; } /** * 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' )) ->whereEx(new FilterEqual('object_type', '=', static::TYPE_SERVICE)) ->whereEx(new FilterEqual('host_name', '=', $this->host_name)) ->whereEx(new FilterEqual('service_description', '=', $this->service_description)); $this->serviceVariables = []; $this->customvarsWithOriginalNames = []; foreach ($query as $row) { if ($row->is_json) { $this->customvarsWithOriginalNames[$row->varname] = json_decode($row->varvalue); $this->serviceVariables[strtolower($row->varname)] = $this->customvarsWithOriginalNames[$row->varname]; } else { $this->serviceVariables[strtolower($row->varname)] = $row->varvalue; $this->customvarsWithOriginalNames[$row->varname] = $row->varvalue; } } return $this; } /** * Fetch the object's downtimes * * @return $this */ public function fetchDowntimes() { $downtimes = $this->backend->select()->from('downtime', array( 'author_name' => 'downtime_author_name', 'comment' => 'downtime_comment', 'duration' => 'downtime_duration', 'end' => 'downtime_end', 'entry_time' => 'downtime_entry_time', 'id' => 'downtime_internal_id', 'is_fixed' => 'downtime_is_fixed', 'is_flexible' => 'downtime_is_flexible', 'is_in_effect' => 'downtime_is_in_effect', 'name' => 'downtime_name', 'objecttype' => 'object_type', 'scheduled_end' => 'downtime_scheduled_end', 'scheduled_start' => 'downtime_scheduled_start', 'start' => 'downtime_start' )) ->whereEx(new FilterEqual('object_type', '=', $this->type)) ->order('downtime_is_in_effect', 'DESC') ->order('downtime_scheduled_start', 'ASC'); if ($this->type === self::TYPE_SERVICE) { $downtimes ->whereEx(new FilterEqual('service_host_name', '=', $this->host_name)) ->whereEx(new FilterEqual('service_description', '=', $this->service_description)); } else { $downtimes ->whereEx(new FilterEqual('host_name', '=', $this->host_name)); } $this->downtimes = $downtimes->getQuery()->fetchAll(); return $this; } /** * Fetch the object's event history * * @return $this */ public function fetchEventhistory() { $eventHistory = $this->backend ->select() ->from( 'eventhistory', array( 'id', 'object_type', 'host_name', 'host_display_name', 'service_description', 'service_display_name', 'timestamp', 'state', 'output', 'type' ) ) ->whereEx(new FilterEqual('object_type', '=', $this->type)) ->whereEx(new FilterEqual('host_name', '=', $this->host_name)); if ($this->type === self::TYPE_SERVICE) { $eventHistory->whereEx( new FilterEqual('service_description', '=', $this->service_description) ); } $this->eventhistory = $eventHistory; return $this; } /** * Fetch the object's host groups * * @return $this */ public function fetchHostgroups() { $this->hostgroups = $this->backend->select() ->from('hostgroup', array('hostgroup_name', 'hostgroup_alias')) ->whereEx(new FilterEqual('host_name', '=', $this->host_name)) ->applyFilter($this->getFilter()) ->fetchPairs(); return $this; } /** * Fetch the object's service groups * * @return $this */ public function fetchServicegroups() { $query = $this->backend->select() ->from('servicegroup', array('servicegroup_name', 'servicegroup_alias')) ->whereEx(new FilterEqual('host_name', '=', $this->host_name)); if ($this->type === self::TYPE_SERVICE) { $query->whereEx( new FilterEqual('service_description', '=', $this->service_description) ); } $this->servicegroups = $query->applyFilter($this->getFilter())->fetchPairs(); return $this; } /** * Fetch stats * * @return $this */ public function fetchStats() { $this->stats = $this->backend->select()->from('servicestatussummary', array( 'services_total', 'services_ok', 'services_critical', 'services_critical_unhandled', 'services_critical_handled', 'services_warning', 'services_warning_unhandled', 'services_warning_handled', 'services_unknown', 'services_unknown_unhandled', 'services_unknown_handled', 'services_pending', )) ->whereEx(new FilterEqual('service_host_name', '=', $this->host_name)) ->applyFilter($this->getFilter()) ->fetchRow(); return $this; } /** * Get all action urls configured for this monitored object * * @return array All note urls as a string */ public function getActionUrls() { return $this->resolveAllStrings( MonitoredObject::parseAttributeUrls($this->action_url) ); } /** * Get the type of the object * * @param bool $translate * * @return string */ public function getType($translate = false) { if ($translate !== false) { switch ($this->type) { case self::TYPE_HOST: $type = mt('montiroing', 'host'); break; case self::TYPE_SERVICE: $type = mt('monitoring', 'service'); break; default: throw new InvalidArgumentException('Invalid type ' . $this->type); } } else { $type = $this->type; } return $type; } /** * Parse the content of the action_url or notes_url attributes * * Find all occurences of http links, separated by whitespaces and quoted * by single or double-ticks. * * @link http://docs.icinga.com/latest/de/objectdefinitions.html * * @param string $urlString A string containing one or more urls * @return array Array of urls as strings */ public static function parseAttributeUrls($urlString) { if (empty($urlString)) { return array(); } $links = array(); if (strpos($urlString, "' ") === false) { $links[] = $urlString; } else { // parse notes-url format foreach (explode("' ", $urlString) as $url) { $url = strpos($url, "'") === 0 ? substr($url, 1) : $url; $url = strrpos($url, "'") === strlen($url) - 1 ? substr($url, 0, strlen($url) - 1) : $url; $links[] = $url; } } return $links; } /** * Fetch all available data of the object * * @return $this */ public function populate() { $this ->fetchComments() ->fetchContactgroups() ->fetchContacts() ->fetchCustomvars() ->fetchDowntimes(); // Call fetchHostgroups or fetchServicegroups depending on the object's type $fetchGroups = 'fetch' . ucfirst($this->type) . 'groups'; $this->$fetchGroups(); return $this; } /** * Resolve macros in all given strings in the current object context * * @param array $strs An array of urls as string * * @return array */ protected function resolveAllStrings(array $strs) { foreach ($strs as $i => $str) { $strs[$i] = Macro::resolveMacros($str, $this); } return $strs; } /** * Set the object's properties * * @param object $properties * * @return $this */ public function setProperties($properties) { $this->properties = (object) $properties; return $this; } public function __isset($name) { if (property_exists($this->properties, $name)) { return isset($this->properties->$name); } elseif (property_exists($this, $name)) { return isset($this->$name); } return false; } public function __get($name) { if (property_exists($this->properties, $name)) { return $this->properties->$name; } elseif (property_exists($this, $name)) { if ($this->$name === null) { $fetchMethod = 'fetch' . ucfirst($name); $this->$fetchMethod(); } return $this->$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); } }