diff --git a/library/Icinga/Application/Cli.php b/library/Icinga/Application/Cli.php index c6e64905b8..9f14b6c206 100644 --- a/library/Icinga/Application/Cli.php +++ b/library/Icinga/Application/Cli.php @@ -53,8 +53,7 @@ protected function setupLogging() new Config( array( 'level' => Logger::INFO, - 'log' => 'file', - 'file' => 'php://stderr' + 'log' => 'stdout', ) ) ); diff --git a/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php index aa3765d86e..6a08eb93eb 100644 --- a/library/Icinga/Application/Config.php +++ b/library/Icinga/Application/Config.php @@ -378,7 +378,7 @@ public function fromSection($section, $key, $default = null) ); } - if ($default !== null) { + if ($value === null && $default !== null) { $value = $default; } diff --git a/library/Icinga/Application/Logger/LogWriter.php b/library/Icinga/Application/Logger/LogWriter.php index c18e512578..831c96fe8a 100644 --- a/library/Icinga/Application/Logger/LogWriter.php +++ b/library/Icinga/Application/Logger/LogWriter.php @@ -11,10 +11,18 @@ */ abstract class LogWriter { + /** + * @var Zend_Config + */ + protected $config; + /** * Create a new log writer initialized with the given configuration */ - abstract public function __construct(Config $config); + public function __construct(Config $config) + { + $this->config = $config; + } /** * Log a message with the given severity diff --git a/library/Icinga/Application/Logger/Writer/StdoutWriter.php b/library/Icinga/Application/Logger/Writer/StdoutWriter.php new file mode 100644 index 0000000000..1fe929d92d --- /dev/null +++ b/library/Icinga/Application/Logger/Writer/StdoutWriter.php @@ -0,0 +1,50 @@ +screen === null) { + $this->screen = Screen::instance(); + } + return $this->screen; + } + + /** + * Log a message with the given severity + * + * @param int $severity The severity to use + * @param string $message The message to log + */ + public function log($severity, $message) + { + $color = 'black'; + switch ($severity) { + case Logger::$ERROR: + $color = 'red'; + break; + case Logger::$WARNING: + $color = 'orange'; + break; + case Logger::$INFO: + $color = 'green'; + break; + case Logger::$DEBUG: + $color = 'blue'; + break; + } + file_put_contents('php://stderr', $this->screen()->colorize($message, $color) . "\n"); + } +} diff --git a/library/Icinga/Data/Db/DbConnection.php b/library/Icinga/Data/Db/DbConnection.php index 5fb08730e2..82686893da 100644 --- a/library/Icinga/Data/Db/DbConnection.php +++ b/library/Icinga/Data/Db/DbConnection.php @@ -216,7 +216,10 @@ public function fetchAll(DbQuery $query) */ public function fetchRow(DbQuery $query) { - return $this->dbAdapter->fetchRow($query->getSelectQuery()); + Benchmark::measure('DB is fetching row'); + $result = $this->dbAdapter->fetchRow($query->getSelectQuery()); + Benchmark::measure('DB row done'); + return $result; } /** diff --git a/library/Icinga/File/Ini/IniEditor.php b/library/Icinga/File/Ini/IniEditor.php index 6ee19fa749..e1a7984d80 100644 --- a/library/Icinga/File/Ini/IniEditor.php +++ b/library/Icinga/File/Ini/IniEditor.php @@ -433,6 +433,9 @@ private function formatKeyValuePair(array $key, $value) */ private function formatKey(array $key) { + foreach ($key as $i => $separator) { + $key[$i] = $this->sanitize($separator); + } return implode($this->nestSeparator, $key); } @@ -599,7 +602,7 @@ private function removeFromArray($array, $pos) } /** - * Prepare a value for INe + * Prepare a value for INI * * @param $value The value of the string * @@ -613,10 +616,12 @@ private function formatValue($value) return $value; } elseif (is_bool($value)) { return ($value ? 'true' : 'false'); - } elseif (strpos($value, '"') === false) { - return '"' . $value . '"'; - } else { - return '"' . str_replace('"', '\"', $value) . '"'; } + return '"' . str_replace('"', '\"', $this->sanitize($value)) . '"'; + } + + private function sanitize($value) + { + return str_replace('\n', '', $value); } } diff --git a/library/Icinga/File/Ini/IniWriter.php b/library/Icinga/File/Ini/IniWriter.php index af11742b91..cf9355b56e 100644 --- a/library/Icinga/File/Ini/IniWriter.php +++ b/library/Icinga/File/Ini/IniWriter.php @@ -52,45 +52,6 @@ public function __construct(array $options = null) parent::__construct($options); } - /** - * Find all keys containing dots and convert it to a nested configuration - * - * Ensure that configurations with the same ini representation the have - * similarly nested Zend_Config objects. The configuration may be altered - * during that process. - * - * @param Zend_Config $config The configuration to normalize - * @return Zend_Config The normalized config - */ - private function normalizeKeys(Zend_Config $config) - { - foreach ($config as $key => $value) { - if (preg_match('/\./', $key) > 0) { - // remove old key - unset ($config->$key); - - // insert new key - $nests = explode('.', $key); - $current = $config; - $i = 0; - for (; $i < count($nests) - 1; $i++) { - if (! isset($current->{$nests[$i]})) { - // configuration key doesn't exist, create a new nesting level - $current->{$nests[$i]} = new Zend_Config (array(), true); - } - // move to next nesting level - $current = $current->{$nests[$i]}; - } - // reached last nesting level, insert value - $current->{$nests[$i]} = $value; - } - if ($value instanceof Zend_Config) { - $config->$key = $this->normalizeKeys ($value); - } - } - return $config; - } - /** * Render the Zend_Config into a config file string * @@ -104,16 +65,6 @@ public function render() $oldconfig = new Zend_Config(array()); } - // create an internal copy of the given configuration, since the user of this class - // won't expect that a configuration will ever be altered during - // the rendering process. - $extends = $this->_config->getExtends(); - $this->_config = new Zend_Config ($this->_config->toArray(), true); - foreach ($extends as $extending => $extended) { - $this->_config->setExtend($extending, $extended); - } - $this->_config = $this->normalizeKeys($this->_config); - $newconfig = $this->_config; $editor = new IniEditor(@file_get_contents($this->_filename), $this->options); $this->diffConfigs($oldconfig, $newconfig, $editor); diff --git a/library/Icinga/Util/Translator.php b/library/Icinga/Util/Translator.php index 7552034744..52a84bd629 100644 --- a/library/Icinga/Util/Translator.php +++ b/library/Icinga/Util/Translator.php @@ -224,6 +224,7 @@ public static function getAvailableLocaleCodes() } } } + sort($codes); return $codes; } diff --git a/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php b/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php new file mode 100644 index 0000000000..63e819d1b9 --- /dev/null +++ b/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php @@ -0,0 +1,90 @@ +select()->from( + 'statusSummary', + array( + 'hosts_down_unhandled', + 'services_critical_unhandled' + ) + )->getQuery()->fetchRow(); + } + + if ($column === null) { + return self::$summary; + } elseif (isset(self::$summary->$column)) { + return self::$summary->$column; + } else { + return null; + } + } + + protected function getBadgeTitle() + { + $translations = array( + 'hosts_down_unhandled' => mt('monitoring', '%d unhandled hosts down'), + 'services_critical_unhandled' => mt('monitoring', '%d unhandled services critical') + ); + + $titles = array(); + $sum = $this->summary(); + + foreach ($this->columns as $col) { + if (isset($sum->$col) && $sum->$col > 0) { + $titles[] = sprintf($translations[$col], $sum->$col); + } + } + + return implode(', ', $titles); + } + + protected function countItems() + { + $sum = self::summary(); + $count = 0; + + foreach ($this->columns as $col) { + if (isset($sum->$col)) { + $count += $sum->$col; + } + } + + return $count; + } + + public function render(Menu $menu) + { + $count = $this->countItems(); + $badge = ''; + if ($count) { + $badge = sprintf( + '
%s
', + $this->getBadgeTitle(), + $count + ); + } + return sprintf( + '%s%s%s', + $menu->getUrl() ?: '#', + $menu->getIcon() ? ' ' : '', + htmlspecialchars($menu->getTitle()), + $badge + ); + } +} diff --git a/library/Icinga/Web/Menu/ProblemMenuItemRenderer.php b/library/Icinga/Web/Menu/ProblemMenuItemRenderer.php index 1254033ae0..58689547fd 100644 --- a/library/Icinga/Web/Menu/ProblemMenuItemRenderer.php +++ b/library/Icinga/Web/Menu/ProblemMenuItemRenderer.php @@ -1,39 +1,11 @@ select()->from( - 'statusSummary', - array( - 'hosts_down_unhandled', - 'services_critical_unhandled' - ) - )->getQuery()->fetchRow(); - $unhandled = $statusSummary->hosts_down_unhandled + $statusSummary->services_critical_unhandled; - $badge = ''; - if ($unhandled) { - $badge = sprintf( - '
%s
', - $unhandled - ); - } - return sprintf( - '%s%s %s', - $menu->getUrl() ?: '#', - $menu->getIcon() ? ' ' : '', - htmlspecialchars($menu->getTitle()), - $badge - ); - } +class ProblemMenuItemRenderer extends MonitoringMenuItemRenderer +{ + protected $columns = array( + 'hosts_down_unhandled', + 'services_critical_unhandled' + ); } diff --git a/library/Icinga/Web/Menu/UnhandledHostMenuItemRenderer.php b/library/Icinga/Web/Menu/UnhandledHostMenuItemRenderer.php index c7e6036fb3..10a75c00f5 100644 --- a/library/Icinga/Web/Menu/UnhandledHostMenuItemRenderer.php +++ b/library/Icinga/Web/Menu/UnhandledHostMenuItemRenderer.php @@ -1,38 +1,12 @@ select()->from( - 'statusSummary', - array( - 'hosts_down_unhandled' - ) - )->getQuery()->fetchRow(); - $badge = ''; - if ($statusSummary->hosts_down_unhandled) { - $badge = sprintf( - '
%s
', - t(sprintf('%d unhandled host problems', $statusSummary->hosts_down_unhandled)), - $statusSummary->hosts_down_unhandled - ); - } - return sprintf( - '%s%s %s', - $menu->getUrl() ?: '#', - $menu->getIcon() ? ' ' : '', - htmlspecialchars($menu->getTitle()), - $badge - ); - } +class UnhandledHostMenuItemRenderer extends MonitoringMenuItemRenderer +{ + protected $columns = array( + 'hosts_down_unhandled', + ); } diff --git a/library/Icinga/Web/Menu/UnhandledServiceMenuItemRenderer.php b/library/Icinga/Web/Menu/UnhandledServiceMenuItemRenderer.php index b677a49352..8a59e9baac 100644 --- a/library/Icinga/Web/Menu/UnhandledServiceMenuItemRenderer.php +++ b/library/Icinga/Web/Menu/UnhandledServiceMenuItemRenderer.php @@ -1,38 +1,12 @@ select()->from( - 'statusSummary', - array( - 'services_critical_unhandled' - ) - )->getQuery()->fetchRow(); - $badge = ''; - if ($statusSummary->services_critical_unhandled) { - $badge = sprintf( - '
%s
', - t(sprintf('%d unhandled service problems', $statusSummary->services_critical_unhandled)), - $statusSummary->services_critical_unhandled - ); - } - return sprintf( - '%s%s %s', - $menu->getUrl() ?: '#', - $menu->getIcon() ? ' ' : '', - htmlspecialchars($menu->getTitle()), - $badge - ); - } +class UnhandledServiceMenuItemRenderer extends MonitoringMenuItemRenderer +{ + protected $columns = array( + 'services_critical_unhandled' + ); } diff --git a/modules/monitoring/application/forms/Command/CommandForm.php b/modules/monitoring/application/forms/Command/CommandForm.php index 0a7cd19f9e..da797e60cf 100644 --- a/modules/monitoring/application/forms/Command/CommandForm.php +++ b/modules/monitoring/application/forms/Command/CommandForm.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Monitoring\Form\Command; -use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; use Icinga\Module\Monitoring\Command\Transport\CommandTransport; use Icinga\Web\Form; use Icinga\Web\Request; @@ -24,11 +24,11 @@ abstract class CommandForm extends Form /** * Set the monitoring backend * - * @param Backend $backend + * @param MonitoringBackend $backend * * @return $this */ - public function setBackend(Backend $backend) + public function setBackend(MonitoringBackend $backend) { $this->backend = $backend; return $this; diff --git a/modules/monitoring/application/views/helpers/MonitoringState.php b/modules/monitoring/application/views/helpers/MonitoringState.php index 871e8ad08f..4db1a7a91d 100644 --- a/modules/monitoring/application/views/helpers/MonitoringState.php +++ b/modules/monitoring/application/views/helpers/MonitoringState.php @@ -95,8 +95,11 @@ public function getStateFlags($object, $type = 'service') */ public function getStateTitle($object, $type) { - return strtoupper($this->monitoringState($object, $type)) - . ' since ' - . date('Y-m-d H:i:s', $object->{$type.'_last_state_change'}); + return sprintf( + '%s %s %s', + $this->view->translate(strtoupper($this->monitoringState($object, $type))), + $this->view->translate('since'), + date('Y-m-d H:i:s', $object->{$type.'_last_state_change'}) + ); } } diff --git a/modules/monitoring/bin/action/list.inc.php b/modules/monitoring/bin/action/list.inc.php deleted file mode 100644 index 01fd351316..0000000000 --- a/modules/monitoring/bin/action/list.inc.php +++ /dev/null @@ -1,123 +0,0 @@ -shift('backend')); - -$query = $backend->select()->from('status', array( - 'host_name', - 'host_state', - 'host_output', - 'host_acknowledged', - 'host_in_downtime', - 'service_description', - 'service_state', - 'service_acknowledged', - 'service_in_downtime', - 'service_handled', - 'service_output', - 'service_last_state_change' -))->order('service_last_state_change ASC'); - -$endless = $params->shift('endless'); -$query->applyFilters($params->getParams()); -$host_colors = array( - 0 => '2', // UP - 1 => '1', // DOWN - 2 => '3', // UNREACH (brown) - 99 => '0', // PEND -); -$host_states = array( - 0 => 'UP', // UP - 1 => 'DOWN', // DOWN - 2 => 'UNREACHABLE', // UNREACH (brown) - 99 => 'PENDING', // PEND -); -$service_colors = array( - 0 => '2', // OK - 1 => '3', // WARN - 2 => '1', // CRIT - 3 => '5', // UNKN - 99 => '0', // PEND -); -$service_states = array( - 0 => 'OK', // OK - 1 => 'WARNING', // WARN - 2 => 'CRITICAL', // CRIT - 3 => 'UNKNOWN', // UNKN - 99 => 'PENDING', // PEND -); - -$finished = false; -while (! $finished) { -$out = ''; -$last_host = null; - -foreach ($query->fetchAll() as $key => $row) { - $host_extra = array(); - if ($row->host_in_downtime) { - $host_extra[] = 'DOWNTIME'; - } - if ($row->host_acknowledged) { - $host_extra[] = 'ACK'; - } - if (empty($host_extra)) { - $host_extra = ''; - } else { - $host_extra = " \033[34;1m[" . implode(',', $host_extra) . "]\033[0m"; - } - - $service_extra = array(); - if ($row->service_in_downtime) { - $service_extra[] = 'DOWNTIME'; - } - if ($row->service_acknowledged) { - $service_extra[] = 'ACK'; - } - if (empty($service_extra)) { - $service_extra = ''; - } else { - $service_extra = " \033[34;52;1m[" . implode(',', $service_extra) . "]\033[0m"; - } - - if ($row->host_name !== $last_host) { - $out .= sprintf( - "\n\033[01;37;4%dm %-5s \033[0m \033[30;1m%s\033[0m%s: %s\n", - $host_colors[$row->host_state], - substr($host_states[$row->host_state], 0, 5), - $row->host_name, - $host_extra, - $row->host_output - ); - } - $last_host = $row->host_name; - $out .= sprintf( - "\033[01;37;4%dm \033[01;37;4%dm %4s \033[0m %s%s since %s: %s\n", - $host_colors[$row->host_state], - $service_colors[$row->service_state], - substr($service_states[$row->service_state] . ' ', 0, 4), - $row->service_description, - $service_extra, - Format::timeSince($row->service_last_state_change), - preg_replace('/\n/', sprintf( - "\n\033[01;37;4%dm \033[01;37;4%dm \033[0m ", - $host_colors[$row->host_state], - $service_colors[$row->service_state] - - ), substr(wordwrap(str_repeat(' ', 30) . preg_replace('~\@{3,}~', '@@@', $row->service_output), 72), 30)) - ); -} - -$out .= "\n"; - - if ($endless) { - echo "\033[2J\033[1;1H\033[1S" . $out; - sleep(3); - } else { - echo $out; - $finished = true; - } -} diff --git a/modules/monitoring/library/Monitoring/Backend.php b/modules/monitoring/library/Monitoring/Backend.php index 3af728860d..e3672dcd50 100644 --- a/modules/monitoring/library/Monitoring/Backend.php +++ b/modules/monitoring/library/Monitoring/Backend.php @@ -1,186 +1,11 @@ resource = $resource; - $this->type = $type; - } +// TODO: obsolete, remove once MonitoringBackend is in use everywhere - // Temporary workaround, we have no way to know our name - protected function setName($name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } +namespace Icinga\Module\Monitoring; - /** - * 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; - } +use Icinga\Module\Monitoring\Backend\MonitoringBackend; -public function getResource() +class Backend extends MonitoringBackend { - 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; - } } 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 0000000000..17bfb9f6ad --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/IdoBackend.php @@ -0,0 +1,9 @@ +name = $name; + $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)) { + + list($foundName, $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($foundName, $config); + if ($name === null) { + self::$instances[$foundName] = self::$instances[$name]; + } + } + + 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 + * + * @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 array($name, $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 array($name, $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); + } + + /** + * Get this backend's internal resource + * + * @return mixed + */ + 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($view) + ); + } + 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); + } +} diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index 2c4ec53800..1fdd8b4f94 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -15,7 +15,7 @@ use Icinga\Exception\QueryException; use Icinga\Web\Request; use Icinga\Web\Url; -use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; /** * A read-only view of an underlying query @@ -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(); } @@ -105,7 +104,7 @@ abstract public function getColumns(); */ public static function fromRequest($request, array $columns = null) { - $view = new static(Backend::createBackend($request->getParam('backend')), $columns); + $view = new static(MonitoringBackend::instance($request->getParam('backend')), $columns); $view->applyUrlFilter($request); return $view; @@ -141,7 +140,7 @@ protected function applyUrlFilter($request = null) */ public static function fromParams(array $params, array $columns = null) { - $view = new static(Backend::createBackend($params['backend']), $columns); + $view = new static(MonitoringBackend::instance($params['backend']), $columns); foreach ($params as $key => $value) { if ($view->isValidFilterTarget($key)) { diff --git a/modules/monitoring/library/Monitoring/Object/Host.php b/modules/monitoring/library/Monitoring/Object/Host.php index 483cb2baed..831579a571 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 ecd1a9cf6a..8e88b861be 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -7,7 +7,7 @@ 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 @@ public function __isset($name) 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::instance(), $params->get('host'), $params->get('service')); } elseif ($params->has('host')) { - return new Host(Backend::createBackend(), $params->get('host')); + return new Host(MonitoringBackend::instance(), $params->get('host')); } return null; } diff --git a/modules/monitoring/library/Monitoring/Object/ObjectList.php b/modules/monitoring/library/Monitoring/Object/ObjectList.php index 1e5595d587..6eb1e4db3d 100644 --- a/modules/monitoring/library/Monitoring/Object/ObjectList.php +++ b/modules/monitoring/library/Monitoring/Object/ObjectList.php @@ -5,7 +5,7 @@ use ArrayIterator; use Countable; use IteratorAggregate; -use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; abstract class ObjectList implements Countable, IteratorAggregate { @@ -21,7 +21,7 @@ abstract class ObjectList implements Countable, IteratorAggregate protected $count; - public function __construct(Backend $backend) + public function __construct(MonitoringBackend $backend) { $this->backend = $backend; } diff --git a/modules/monitoring/library/Monitoring/Object/Service.php b/modules/monitoring/library/Monitoring/Object/Service.php index 28d0338f20..6d5245f0d1 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); diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index 0138e0573f..6e0124ccd3 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -135,34 +135,9 @@ form.instance-features span.description, form.object-features span.description { display: inline; } -.alertsummary-flex-container { - display: -ms-Flexbox; - -ms-box-orient: horizontal; - - display: -webkit-flex; - display: -moz-flex; - display: -ms-flex; - display: flex; - - -webkit-flex-flow: row wrap; - -moz-flex-flow: row wrap; - -ms-flex-flow: row wrap; - flex-flow: row wrap; -} - -.alertsummary-flex { - flex: 1 1 auto; - overflow: auto; - border: 1px #333 solid; - padding: 5px; - margin: 0 10px 0 0; - border-spacing: 10px 10px; -} - table.avp .customvar ul { list-style-type: none; margin: 0; padding: 0; padding-left: 1.5em; } - diff --git a/modules/test/application/clicommands/PhpCommand.php b/modules/test/application/clicommands/PhpCommand.php index 126e0c4626..dbcb58f05e 100644 --- a/modules/test/application/clicommands/PhpCommand.php +++ b/modules/test/application/clicommands/PhpCommand.php @@ -56,7 +56,7 @@ public function unitAction() $options = array(); if ($this->isVerbose) { - $options[] = '--verbose'; + $options[] = '--verbose --testdox'; } if ($build) { $reportPath = $this->setupAndReturnReportDirectory(); @@ -71,7 +71,23 @@ public function unitAction() } chdir(realpath(__DIR__ . '/../..')); - passthru($phpUnit . ' ' . join(' ', array_merge($options, $this->params->getAllStandalone()))); + $command = $phpUnit . ' ' . join(' ', array_merge($options, $this->params->getAllStandalone())); + if ($this->isVerbose) { + $res = `$command`; + foreach (preg_split('/\n/', $res) as $line) { + if (preg_match('~\s+\[([x\s])\]\s~', $line, $m)) { + if ($m[1] === 'x') { + echo $this->screen->colorize($line, 'green') . "\n"; + } else { + echo $this->screen->colorize($line, 'red') . "\n"; + } + } else { + echo $line . "\n"; + } + } + } else { + passthru($command); + } } /** diff --git a/packages/debian/control b/packages/debian/control index ab3162d469..48383839a0 100644 --- a/packages/debian/control +++ b/packages/debian/control @@ -1,5 +1,5 @@ Source: icingaweb -Section: upstream +Section: admin Maintainer: Icinga Development Team Priority: optional Build-Depends: debhelper (>=9) @@ -28,7 +28,7 @@ Description: Icinga PHP common libraries Package: icingacli Architecture: any -Depends: libicinga-common-php (>= 2.0.0~alpha1) +Depends: libicinga-common-php (>= 2.0.0~alpha1), php5-cli (>= 5.3.2) Description: Icinga CLI tool The Icinga CLI allows one to access it's Icinga monitoring system from a terminal. diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 10ef3aa45d..e3cf1f6856 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -189,22 +189,3 @@ textarea { input, select, textarea { display: inline; } - -.control-group { - display: -webkit-flex; - display: flex; - -webkit-flex-flow: row wrap; - flex-flow: row wrap; -} - -.control-group > div { - width: 100%; - height: 100%; - overflow: auto; -} - -@media (min-width: 480px) { - .control-group > div { - width: auto; - } -} diff --git a/test/php/library/Icinga/Application/ConfigTest.php b/test/php/library/Icinga/Application/ConfigTest.php index e74eb06a50..4f4f772199 100644 --- a/test/php/library/Icinga/Application/ConfigTest.php +++ b/test/php/library/Icinga/Application/ConfigTest.php @@ -266,6 +266,11 @@ public function testWhetherItIsPossibleToDirectlyRetrieveASectionProperty() $config->fromSection('a', 'c', 'test'), 'Config::fromSection does not return the given default value for non-existent section properties' ); + $this->assertEquals( + 'c', + $config->fromSection('a', 'b', 'test'), + 'Config::fromSection does not return the actual value of a section\'s property in case a default is given' + ); } /** diff --git a/test/php/library/Icinga/File/Ini/IniWriterTest.php b/test/php/library/Icinga/File/Ini/IniWriterTest.php index 53bd750d80..1b9580fb39 100644 --- a/test/php/library/Icinga/File/Ini/IniWriterTest.php +++ b/test/php/library/Icinga/File/Ini/IniWriterTest.php @@ -29,6 +29,30 @@ public function tearDown() unlink($this->tempFile2); } + public function testWhetherPointInSectionIsNotNormalized() + { + $writer = new IniWriter( + array( + 'config' => new Config( + array( + 'section' => array( + 'foo.bar' => 1337 + ), + 'section.with.multiple.dots' => array( + 'some more' => array( + 'nested stuff' => 'With more values' + ) + ) + ) + ), + 'filename' => $this->tempFile + ) + ); + $writer->write(); + $config = Config::fromIni($this->tempFile)->toArray(); + $this->assertTrue(array_key_exists('section.with.multiple.dots', $config), 'Section names not normalized'); + } + public function testWhetherSimplePropertiesAreInsertedInEmptyFiles() { $this->markTestSkipped('Implementation has changed. Section-less properties are not supported anymore'); @@ -702,44 +726,6 @@ public function testWhetherCommentsOnSectionPropertyLinesArePreserved() ); } - public function testKeyNormalization() - { - $normalKeys = new IniWriter( - array ( - 'config' => new Config(array ( - 'foo' => 'bar', - 'nest' => array ( - 'nested' => array ( - 'stuff' => 'nested configuration element' - ) - ), - 'preserving' => array ( - 'ini' => array( - 'writer' => 'n' - ), - 'foo' => 'this should not be overwritten' - ) - )), - 'filename' => $this->tempFile - ) - - ); - - $nestedKeys = new IniWriter( - array ( - 'config' => new Config(array ( - 'foo' => 'bar', - 'nest.nested.stuff' => 'nested configuration element', - 'preserving.ini.writer' => 'n', - 'preserving.foo' => 'this should not be overwritten' - )), - 'filename' => $this->tempFile2 - ) - - ); - $this->assertEquals($normalKeys->render(), $nestedKeys->render()); - } - /** * Write a INI-configuration string to a temporary file and return it's path *