From 19ab8237d4e3ab5ba17f69cd76bd6c8981ef7019 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 15:40:38 +0100 Subject: [PATCH 01/11] Backend\MonitoringBackend: base class for backends refs #7635 --- .../Monitoring/Backend/MonitoringBackend.php | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php diff --git a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php new file mode 100644 index 000000000..18ae5a2ad --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php @@ -0,0 +1,304 @@ +name = $name; + $this->config = $config; + } + + public static function instance($name = null) + { + if (! array_key_exists($name, self::$instances)) { + + $config = static::loadConfig($name); + $type = $config->get('type'); + $class = implode( + '\\', + array( + __NAMESPACE__, + ucfirst($type), + ucfirst($type) . 'Backend' + ) + ); + + if (! class_exists($class)) { + throw new ConfigurationError( + mt('monitoring', 'There is no "%s" monitoring backend'), + $class + ); + } + + self::$instances[$name] = new $class($name, $config); + } + + return self::$instances[$name]; + } + + /** + * Whether this backend is of a specific type + * + * @param string $type Backend type + * + * @return boolean + */ + public function is($type) + { + return $this->getType() === $type; + } + + /** + * Get the configured name of this backend + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the backend type name + * + * @return string + */ + public function getType() + { + if ($this->type === null) { + $parts = preg_split('~\\\~', get_class($this)); + $class = array_pop($parts); + if (substr($class, -7) === 'Backend') { + $this->type = lcfirst(substr($class, 0, -7)); + } else { + throw new ProgrammingError( + '%s is not a valid monitoring backend class name', + $class + ); + } + } + return $this->type; + } + + /** + * Return the configuration for the first enabled or the given backend + */ + protected static function loadConfig($name = null) + { + $backends = Config::module('monitoring', 'backends'); + + if ($name === null) { + + $count = 0; + + foreach ($backends as $name => $config) { + $count++; + if ((bool) $config->get('disabled', false) === false) { + return $config; + } + } + + if ($count === 0) { + $message = mt('monitoring', 'No backend has been configured'); + } else { + $message = mt('monitoring', 'All backends are disabled'); + } + + throw new ConfigurationError($message); + + } else { + + $config = $backends->get($name); + + if ($config === null) { + throw new ConfigurationError( + mt('monitoring', 'No configuration for backend %s'), + $name + ); + } + + if ((bool) $config->get('disabled', false) === true) { + throw new ConfigurationError( + mt('monitoring', 'Configuration for backend %s is disabled'), + $name + ); + } + + return $config; + } + } + + /** + * Create a backend + * + * @deprecated + * + * @param string $backendName Name of the backend or null for creating the default backend which is the first INI + * configuration entry not being disabled + * + * @return Backend + * @throws ConfigurationError When no backend has been configured or all backends are disabled or the + * configuration for the requested backend does either not exist or it's disabled + */ + public static function createBackend($name = null) + { + return self::instance($name); + } + + public function getResource() + { + if ($this->resource === null) { + $this->resource = ResourceFactory::create($this->config->get('resource')); + if ($this->is('ido') && $this->resource->getDbType() !== 'oracle') { + // TODO(el): The resource should set the table prefix + $this->resource->setTablePrefix('icinga_'); + } + } + return $this->resource; + } + + /** + * Backend entry point + * + * @return self + */ + public function select() + { + return $this; + } + + /** + * Create a data view to fetch data from + * + * @param string $name + * @param array $columns + * + * @return DataView + */ + public function from($name, array $columns = null) + { + $class = $this->buildViewClassName($name); + return new $class($this, $columns); + } + + /** + * View name to class name resolution + * + * @param string $viewName + * + * @return string + * @throws ProgrammingError When the view does not exist + */ + protected function buildViewClassName($view) + { + $class = '\\Icinga\\Module\\Monitoring\\DataView\\' . ucfirst($view); + if (!class_exists($class)) { + throw new ProgrammingError( + 'DataView %s does not exist', + ucfirst($viewName) + ); + } + return $class; + } + + /** + * Get a specific query class instance + * + * @param string $name Query name + * @param array $columns Optional column list + * + * @return Icinga\Data\QueryInterface + */ + public function query($name, $columns = null) + { + $class = $this->buildQueryClassName($name); + + if (!class_exists($class)) { + throw new ProgrammingError( + 'Query "%s" does not exist for backend %s', + $name, + $this->getType() + ); + } + + return new $class($this->getResource(), $columns); + } + + /** + * Whether this backend supports the given query + * + * @param string $name Query name to check for + * + * @return bool + */ + public function hasQuery($name) + { + return class_exists($this->buildQueryClassName($name)); + } + + /** + * Query name to class name resolution + * + * @param string $query + * + * @return string + * @throws ProgrammingError When the query does not exist for this backend + */ + protected function buildQueryClassName($query) + { + $parts = preg_split('~\\\~', get_class($this)); + array_pop($parts); + array_push($parts, 'Query', ucfirst($query) . 'Query'); + return implode('\\', $parts); + } +} From 50b0ef45ff407cb917b292a39ac94b5c83f01047 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 15:44:39 +0100 Subject: [PATCH 02/11] IdoBackend: concrete backend implementation Nothing special to be found here. Not yet. refs #7635 --- .../library/Monitoring/Backend/Ido/IdoBackend.php | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/IdoBackend.php diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/IdoBackend.php b/modules/monitoring/library/Monitoring/Backend/Ido/IdoBackend.php new file mode 100644 index 000000000..17bfb9f6a --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/IdoBackend.php @@ -0,0 +1,9 @@ + Date: Tue, 11 Nov 2014 15:46:17 +0100 Subject: [PATCH 03/11] Monitoring\Backend: reduce to compat facade This is a facade for Monitoring\Backend right now. To be removed as soon as it got replaced everywhere. refs #7635 --- .../monitoring/library/Monitoring/Backend.php | 183 +----------------- 1 file changed, 4 insertions(+), 179 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Backend.php b/modules/monitoring/library/Monitoring/Backend.php index 3af728860..e3672dcd5 100644 --- a/modules/monitoring/library/Monitoring/Backend.php +++ b/modules/monitoring/library/Monitoring/Backend.php @@ -1,186 +1,11 @@ resource = $resource; - $this->type = $type; - } - - // Temporary workaround, we have no way to know our name - protected function setName($name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } - - /** - * Create a backend - * - * @param string $backendName Name of the backend or null for creating the default backend which is the first INI - * configuration entry not being disabled - * - * @return Backend - * @throws ConfigurationError When no backend has been configured or all backends are disabled or the - * configuration for the requested backend does either not exist or it's disabled - */ - public static function createBackend($backendName = null) - { - $config = Config::module('monitoring', 'backends'); - if ($config->count() === 0) { - throw new ConfigurationError(mt('monitoring', 'No backend has been configured')); - } - if ($backendName !== null) { - $backendConfig = $config->get($backendName); - if ($backendConfig === null) { - throw new ConfigurationError('No configuration for backend %s', $backendName); - } - if ((bool) $backendConfig->get('disabled', false) === true) { - throw new ConfigurationError( - mt('monitoring', 'Configuration for backend %s available but backend is disabled'), - $backendName - ); - } - } else { - foreach ($config as $name => $backendConfig) { - if ((bool) $backendConfig->get('disabled', false) === false) { - $backendName = $name; - break; - } - } - if ($backendName === null) { - throw new ConfigurationError(mt('monitoring', 'All backends are disabled')); - } - } - $resource = ResourceFactory::create($backendConfig->resource); - if ($backendConfig->type === 'ido' && $resource->getDbType() !== 'oracle') { - // TODO(el): The resource should set the table prefix - $resource->setTablePrefix('icinga_'); - } - $backend = new Backend($resource, $backendConfig->type); - $backend->setName($backendName); - return $backend; - } - -public function getResource() -{ - return $this->resource; -} - - /** - * Backend entry point - * - * @return self - */ - public function select() - { - return $this; - } - - /** - * Create a data view to fetch data from - * - * @param string $viewName - * @param array $columns - * - * @return DataView - */ - public function from($viewName, array $columns = null) - { - $viewClass = $this->resolveDataViewName($viewName); - return new $viewClass($this, $columns); - } - - /** - * View name to class name resolution - * - * @param string $viewName - * - * @return string - * @throws ProgrammingError When the view does not exist - */ - protected function resolveDataViewName($viewName) - { - $viewClass = '\\Icinga\\Module\\Monitoring\\DataView\\' . ucfirst($viewName); - if (!class_exists($viewClass)) { - throw new ProgrammingError( - 'DataView %s does not exist', - ucfirst($viewName) - ); - } - return $viewClass; - } - - public function getQueryClass($name) - { - return $this->resolveQueryName($name); - } - - /** - * Query name to class name resolution - * - * @param string $queryName - * - * @return string - * @throws ProgrammingError When the query does not exist for this backend - */ - protected function resolveQueryName($queryName) - { - $queryClass = '\\Icinga\\Module\\Monitoring\\Backend\\' - . ucfirst($this->type) - . '\\Query\\' - . ucfirst($queryName) - . 'Query'; - if (!class_exists($queryClass)) { - throw new ProgrammingError( - 'Query "%s" does not exist for backend %s', - ucfirst($queryName), - ucfirst($this->type) - ); - } - return $queryClass; - } } From 1557410b72dbec994c46589079a67bf33bc996a8 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 15:49:27 +0100 Subject: [PATCH 04/11] DataView: query instantiation is up to the backend refs #7635 --- modules/monitoring/library/Monitoring/DataView/DataView.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index 2c4ec5380..f5d7dafe6 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -44,8 +44,7 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable public function __construct(ConnectionInterface $connection, array $columns = null) { $this->connection = $connection; - $queryClass = $connection->getQueryClass($this->getQueryName()); - $this->query = new $queryClass($this->connection->getResource(), $columns); + $this->query = $connection->query($this->getQueryName(), $columns); $this->filter = Filter::matchAll(); $this->init(); } From 6627b5ae6ef39132dafe73a613815d8fc60cb50a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 16:29:07 +0100 Subject: [PATCH 05/11] Monitoring\Object: use MonitoringBackend refs #7635 --- .../monitoring/library/Monitoring/Object/Host.php | 6 +++--- .../library/Monitoring/Object/MonitoredObject.php | 12 ++++++------ .../monitoring/library/Monitoring/Object/Service.php | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Object/Host.php b/modules/monitoring/library/Monitoring/Object/Host.php index 483cb2bae..831579a57 100644 --- a/modules/monitoring/library/Monitoring/Object/Host.php +++ b/modules/monitoring/library/Monitoring/Object/Host.php @@ -5,7 +5,7 @@ namespace Icinga\Module\Monitoring\Object; use InvalidArgumentException; -use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; /** * A Icinga host @@ -63,10 +63,10 @@ class Host extends MonitoredObject /** * Create a new host * - * @param Backend $backend Backend to fetch host information from + * @param MonitoringBackend $backend Backend to fetch host information from * @param string $host Host name */ - public function __construct(Backend $backend, $host) + public function __construct(MonitoringBackend $backend, $host) { parent::__construct($backend); $this->host = $host; diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index ecd1a9cf6..abab3c0e3 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -7,7 +7,7 @@ namespace Icinga\Module\Monitoring\Object; use InvalidArgumentException; use Icinga\Application\Config; use Icinga\Exception\InvalidPropertyException; -use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; use Icinga\Web\UrlParams; /** @@ -28,7 +28,7 @@ abstract class MonitoredObject /** * Backend to fetch object information from * - * @var Backend + * @var MonitoringBackend */ protected $backend; @@ -119,9 +119,9 @@ abstract class MonitoredObject /** * Create a monitored object, i.e. host or service * - * @param Backend $backend Backend to fetch object information from + * @param MonitoringBackend $backend Backend to fetch object information from */ - public function __construct(Backend $backend) + public function __construct(MonitoringBackend $backend) { $this->backend = $backend; } @@ -480,9 +480,9 @@ abstract class MonitoredObject public static function fromParams(UrlParams $params) { if ($params->has('service') && $params->has('host')) { - return new Service(Backend::createBackend(), $params->get('host'), $params->get('service')); + return new Service(MonitoringBackend::createBackend(), $params->get('host'), $params->get('service')); } elseif ($params->has('host')) { - return new Host(Backend::createBackend(), $params->get('host')); + return new Host(MonitoringBackend::createBackend(), $params->get('host')); } return null; } diff --git a/modules/monitoring/library/Monitoring/Object/Service.php b/modules/monitoring/library/Monitoring/Object/Service.php index 28d0338f2..6d5245f0d 100644 --- a/modules/monitoring/library/Monitoring/Object/Service.php +++ b/modules/monitoring/library/Monitoring/Object/Service.php @@ -5,7 +5,7 @@ namespace Icinga\Module\Monitoring\Object; use InvalidArgumentException; -use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; /** * A Icinga service @@ -68,11 +68,11 @@ class Service extends MonitoredObject /** * Create a new service * - * @param Backend $backend Backend to fetch service information from + * @param MonitoringBackend $backend Backend to fetch service information from * @param string $host Host name the service is running on * @param string $service Service name */ - public function __construct(Backend $backend, $host, $service) + public function __construct(MonitoringBackend $backend, $host, $service) { parent::__construct($backend); $this->host = new Host($backend, $host); From 3f4110d33d4a0c3246223ca991cbbff4d9a94a96 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 16:43:51 +0100 Subject: [PATCH 06/11] MonitoredObject: use MonitoringBackend::instance refs #7635 --- .../monitoring/library/Monitoring/Object/MonitoredObject.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index abab3c0e3..8e88b861b 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -480,9 +480,9 @@ abstract class MonitoredObject public static function fromParams(UrlParams $params) { if ($params->has('service') && $params->has('host')) { - return new Service(MonitoringBackend::createBackend(), $params->get('host'), $params->get('service')); + return new Service(MonitoringBackend::instance(), $params->get('host'), $params->get('service')); } elseif ($params->has('host')) { - return new Host(MonitoringBackend::createBackend(), $params->get('host')); + return new Host(MonitoringBackend::instance(), $params->get('host')); } return null; } From 6c0aa8dcc5df3b1b42804200d34205061d2cc9c2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 16:49:56 +0100 Subject: [PATCH 07/11] MonitoringBackend: handle null name in a nice way When we get null as a backend name, we load the default one. While we want to cache that null backend, it should still know about it's real name. --- .../library/Monitoring/Backend/MonitoringBackend.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php index 18ae5a2ad..e18bd39a4 100644 --- a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php +++ b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php @@ -62,7 +62,7 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface { if (! array_key_exists($name, self::$instances)) { - $config = static::loadConfig($name); + list($foundName, $config) = static::loadConfig($name); $type = $config->get('type'); $class = implode( '\\', @@ -80,7 +80,10 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface ); } - self::$instances[$name] = new $class($name, $config); + self::$instances[$name] = new $class($foundName, $config); + if ($name === null) { + self::$instances[$foundName] = self::$instances[$name]; + } } return self::$instances[$name]; @@ -144,7 +147,7 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface foreach ($backends as $name => $config) { $count++; if ((bool) $config->get('disabled', false) === false) { - return $config; + return array($name, $config); } } @@ -174,7 +177,7 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface ); } - return $config; + return array($name, $config); } } From 8faf81dad5ee105d8f2a59b50ed42b42a0c2c8fd Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 16:57:12 +0100 Subject: [PATCH 08/11] MonitoringBackend: add clearInstances() refs #7635 --- .../library/Monitoring/Backend/MonitoringBackend.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php index e18bd39a4..77f12dd8d 100644 --- a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php +++ b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php @@ -89,6 +89,14 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface return self::$instances[$name]; } + /** + * Clear all cached instances. Mostly for testing purposes. + */ + public static function clearInstances() + { + self::$instances = array(); + } + /** * Whether this backend is of a specific type * From 9431a3432fadc713089832b7fdbcf3fbe70be8f2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 17:21:32 +0100 Subject: [PATCH 09/11] MonitoringBackend: remove comment for SOLID fanboys --- .../monitoring/library/Monitoring/Backend/MonitoringBackend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php index 77f12dd8d..697ff195c 100644 --- a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php +++ b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php @@ -40,7 +40,7 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface protected $name; /** - * Already created instances. YES, static. + * Already created instances * * @var array */ From 78fd5109ac2fe4ed2664e8b6ca6abe4afaa06e7a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 17:23:49 +0100 Subject: [PATCH 10/11] MonitoringBackend: fix variable name in exception refs #7635 --- .../library/Monitoring/Backend/MonitoringBackend.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php index 697ff195c..4de5f1d31 100644 --- a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php +++ b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php @@ -73,7 +73,7 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface ) ); - if (! class_exists($class)) { + if (!class_exists($class)) { throw new ConfigurationError( mt('monitoring', 'There is no "%s" monitoring backend'), $class @@ -256,7 +256,7 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface if (!class_exists($class)) { throw new ProgrammingError( 'DataView %s does not exist', - ucfirst($viewName) + ucfirst($view) ); } return $class; From 1ca83c001fe7df8e2e7e1f0964ed95fe5cf5d7a4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 17:33:31 +0100 Subject: [PATCH 11/11] MonitoringBackend: just a few comments fixes #7635 --- .../Monitoring/Backend/MonitoringBackend.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php index 4de5f1d31..11a8fb699 100644 --- a/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php +++ b/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php @@ -14,8 +14,10 @@ use Icinga\Exception\ProgrammingError; class MonitoringBackend implements Selectable, Queryable, ConnectionInterface { /** - * @var Config + * Backend configuration * + * @var Config + */ protected $config; /** @@ -58,6 +60,15 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface $this->config = $config; } + /** + * Get a backend instance + * + * You may ask for a specific backend name or get the default one otherwise + * + * @param string $name Backend name + * + * @return MonitoringBackend + */ public static function instance($name = null) { if (! array_key_exists($name, self::$instances)) { @@ -206,6 +217,11 @@ class MonitoringBackend implements Selectable, Queryable, ConnectionInterface return self::instance($name); } + /** + * Get this backend's internal resource + * + * @return mixed + */ public function getResource() { if ($this->resource === null) {